Skip to content

Commit

Permalink
Merge branch 'upstream'
Browse files Browse the repository at this point in the history
  • Loading branch information
ollybh committed Jul 15, 2024
2 parents 9fef4aa + c62a4af commit 67588f6
Show file tree
Hide file tree
Showing 35 changed files with 894 additions and 111 deletions.
2 changes: 2 additions & 0 deletions assets/app/view/game/part/city_slot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ def render_part
token_attrs[:stroke] = 'black'
token_attrs[:'stroke-width'] = '3px'
token_attrs[:'stroke-dasharray'] = '4'
elsif @city&.outline
token_attrs[:'fill-opacity'] = '0'
end

children = [h(:circle, attrs: token_attrs)]
Expand Down
4 changes: 1 addition & 3 deletions assets/app/view/welcome.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ def render

def render_notification
message = <<~MESSAGE
<p><a href="https://github.com/tobymao/18xx/wiki/18neb">18Neb</a>, <a href="https://github.com/tobymao/18xx/wiki/1844">1844</a>, and <a href="https://github.com/tobymao/18xx/wiki/1847-AE">1847 AE</a> are in production.</p>
<p><a href="https://github.com/tobymao/18xx/wiki/18RoyalGorge">18RoyalGorge</a> and <a href="https://github.com/tobymao/18xx/wiki/18Hiawatha">18Hiawatha</a> are in alpha</a>.</p>
<p>18Uruguay is in alpha.</p>
<p>Report bugs and make feature requests <a href='https://github.com/tobymao/18xx/issues'>on GitHub</a>.</p>
MESSAGE
Expand Down
58 changes: 30 additions & 28 deletions lib/engine/ability/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Abilities

This page documents the different ability types and the attributes which may be
This page documents the different ability types and the attributes which may be
set for each type.

Abilities are mainly used to describe private company powers, but may also apply
to other entities such as corporations. Examples of how their use can be seen in
Abilities are mainly used to describe private company powers, but may also apply
to other entities such as corporations. Examples of how their use can be seen in
the [game configuration directory](../config/game).

## Generic attributes
Expand Down Expand Up @@ -38,12 +38,12 @@ These attributes may be set for all ability types.
- `buying_train`: train buying step.
- `has_train`: when the owning corporation owns at least one train.
- `never`: use with `close` abilities to prevent a company from closing.
- `operated`: when the owning corporation has finished the dividend step
- `operated`: when the owning corporation has finished the dividend step
on their first turn.
- `or_between_turns`: usable at the start of any corporation's OR turn,
before that corporation has acted.
- `owning_corp_or_turn`: usable at any point during the owning corporation's OR turn.
- `owning_player_or_turn`: usable at any point during any of the owning player's
- `owning_player_or_turn`: usable at any point during any of the owning player's
OR turns.
- `owning_player_sr_turn`: usable at any point during any of the owning player's
SR turns.
Expand Down Expand Up @@ -78,8 +78,8 @@ When a company with this ability is sold to a corporation, the company is
automatically assigned to the new owning corporation.

- `closed_when_used_up`: This ability has a count that is decreased each time
it is used. If this attribute is true the private is closed when count reaches
zero, if false the private remains open but the discount can no longer be used.
it is used. If this attribute is true the private is closed when count reaches
zero, if false the private remains open but the discount can no longer be used.
Default false.

With this configuration, the automatic assignment will happen and the company
Expand Down Expand Up @@ -157,7 +157,7 @@ during a step that allows exchange.
- `corporations`: An array with corporation names, whose share may be exchanged.
Use a simple `"any"` (no array) to allow for any corporation. Use a simple
`"ipoed"` (no array) to allow from any company that has been IPOed.
- `from`: Where the share may be take from, either `"ipo"`, `"market"`, or an
- `from`: Where the share may be take from, either `"ipo"`, `"market"`, or an
array containing both.

## hex_bonus
Expand Down Expand Up @@ -189,7 +189,7 @@ Reserve a token slot.
Take a station token off the board and place back on the charter
in the most expensive open location.

- `reimburse`: If true, the corporation is reimbursed the token cost of the
- `reimburse`: If true, the corporation is reimbursed the token cost of the
location where the token is placed.

## revenue_change
Expand Down Expand Up @@ -239,15 +239,15 @@ Discount the cost for laying tiles in the specified terrain type.
- `exact_match`: Tile may only contain specified terrain type. Default true.
- `hexes`: If not specified, all applicable hexes qualifies for
the discount. If specified, only specified hexes qualify.
- `terrain`: If set, type of terrain for which discount is provided, otherwise
- `terrain`: If set, type of terrain for which discount is provided, otherwise
the discount is off the total cost.

## tile_income

Generate extra revenue when tiles are laid on specified terrain types.

- `income`: Extra income per tile lay.
- `owner_only`: Does this income apply to any tile lay (1882 Tresle Bridge) or
- `owner_only`: Does this income apply to any tile lay (1882 Tresle Bridge) or
just the owner (1817 Mountain Engineers).
- `terrain`: Terrain type for this ability.

Expand All @@ -256,19 +256,19 @@ Generate extra revenue when tiles are laid on specified terrain types.
Lay or upgrade one or more track tiles without connectivity, in addition to
normal tile lay actions.

- `blocks`: If true and `when` is `sold`, then the step `TrackLayWhenCompanySold`
- `blocks`: If true and `when` is `sold`, then the step `TrackLayWhenCompanySold`
will require a tile lay. Default false.
- `closed_when_used_up`: This ability has a count that is decreased each time it
is used. If this attribute is true the private is closed when count reaches
zero, if false the private remains open but the discount can no longer be used.
is used. If this attribute is true the private is closed when count reaches
zero, if false the private remains open but the discount can no longer be used.
Default false.
- `connect`: If true, and `count` is greater than 1, tiles laid must connect to
- `connect`: If true, and `count` is greater than 1, tiles laid must connect to
each other. Default true unless `count_per_or` is used, in which case `connect`
isn't checked by default.
- `consume_tile_lay`: If true, using this private counts as a corporations tile
lay and must follow lay/upgrade rules. Upgrade's also count towards the
- `consume_tile_lay`: If true, using this private counts as a corporations tile
lay and must follow lay/upgrade rules. Upgrade's also count towards the
corporations 'upgrade' lays. Default false.
- `count_per_or`: used if private ability limits the amount of tile lays that
- `count_per_or`: used if private ability limits the amount of tile lays that
you can use per OR.
- `cost`: Cost to use the ability.
- `discount`: Discount the cost of laying the tile by the given
Expand All @@ -277,8 +277,8 @@ normal tile lay actions.
- `hexes`: Array of hex coordinates where tiles may be laid.
- `lay_count` and `upgrade_count` - Use as an alternative to `count`. `lay_count`
is the number of yellow tile lays, and `upgrade_count` is the number of green
or higher tile upgrades. When these are set, the ability cannot be used for
both new tile lays and upgrades. With these set, you need to make sure the
or higher tile upgrades. When these are set, the ability cannot be used for
both new tile lays and upgrades. With these set, you need to make sure the
`ability.use!` call includes an `upgrade` kwarg.
- `must_lay_all`: If true and `count` is greater than 1 and `must_lay_together`
is true, all the tile lays must be used; if false, then some tile lays may be
Expand All @@ -289,7 +289,7 @@ normal tile lay actions.
- `reachable`: If true, when tile laid, a check is done if one of the
controlling corporation's station tokens are reachable; if not a game
error is triggered. Default false.
- `special`: If true, do not check that the tile upgrade preserves labels and
- `special`: If true, do not check that the tile upgrade preserves labels and
city count. Default true.
- `tiles`: Array of tile numbers which may be laid.

Expand All @@ -312,16 +312,16 @@ Modified station token placement.
- `city`: Index of the city on the hex where this ability may be used, if
multiple cities are there.
- `connected`: If true, when token placed, a check is done if the desired token
slot is connected by track with another city that has a token of the
slot is connected by track with another city that has a token of the
corporation; if not a game error is triggered. Default false.
- `discount`: ratio discount from the normal price, e.g., `0.25` takes 25% off
the token price.
- `extra_action`: If true, this ability may be used in addition to the turn's
normal token placement step. Default false.
- `extra_slot`: Simlar to `cheater` except this token does not take a slot -
When `cheater` is used, when the city gets an extra city slot the 'cheater'
When `cheater` is used, when the city gets an extra city slot the 'cheater'
token goes into the newly opened slot. If `extra_slot` is used, when the city
gets an extra token slot, the new token slot is open - the extra token does
gets an extra token slot, the new token slot is open - the extra token does
not consume it. This also means that an `extra_slot` token lay in an city with
an open slot does not use up the open slot.
- `from_owner`: If true, this ability uses a token from the owning corporation's
Expand All @@ -337,6 +337,8 @@ Modified station token placement.
placed in a location that the ability is allowed to use. Default false.
- `teleport_price`: If present, this ability may be used to place a
token without connectivity, for the given price.
- `same_hex_allowed`: If `true`, a token may be placed on a hex where the owning
corporation already has a token in a different city.

## train_buy

Expand All @@ -347,21 +349,21 @@ Modify train buy in some way.

## train_discount

Discount the train buy cost. The `count` attribute specify how many times the
Discount the train buy cost. The `count` attribute specify how many times the
discount can be used.

- `closed_when_used_up`: This ability has a count that is decreased each time it
is used. If this attribute is true the private is closed when count reaches
is used. If this attribute is true the private is closed when count reaches
zero, if false the private remains open but the discount can no longer be used.
Default false.
- `discount`: Discount amount. If > 1 this is an absolute amount.
- `discount`: Discount amount. If > 1 this is an absolute amount.
If 0 < amount < 1 it is the fraction, e.g. 0.75 is a 75% discount.
- `trains`: An array of all train names that the discount applies to.

## train_limit

Modify train limit in some way.
For performance reasons, the supporting code needs to be added directly to the
For performance reasons, the supporting code needs to be added directly to the
game class. See G18MEX#train_limit for an example.

- `increase`: If positive, this will increase the train limit with this
Expand Down
5 changes: 3 additions & 2 deletions lib/engine/ability/token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ module Ability
class Token < Base
attr_reader :hexes, :teleport_price, :extra_action, :from_owner, :discount, :city,
:neutral, :cheater, :special_only, :extra_slot, :check_tokenable,
:closed_when_used_up, :connected
:closed_when_used_up, :connected, :same_hex_allowed

def setup(hexes:, price: nil, teleport_price: nil, extra_action: nil,
from_owner: nil, discount: nil, city: nil, neutral: nil,
cheater: nil, extra_slot: nil, special_only: nil, check_tokenable: nil,
closed_when_used_up: nil, connected: nil)
closed_when_used_up: nil, connected: nil, same_hex_allowed: nil)
@hexes = hexes
@price = price
@teleport_price = teleport_price
Expand All @@ -27,6 +27,7 @@ def setup(hexes:, price: nil, teleport_price: nil, extra_action: nil,
@check_tokenable = check_tokenable.nil? ? true : check_tokenable
@closed_when_used_up = closed_when_used_up || false
@connected = connected || false
@same_hex_allowed = same_hex_allowed || false
end

def price(token = nil)
Expand Down
2 changes: 2 additions & 0 deletions lib/engine/game/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3227,6 +3227,8 @@ def ability_usable?(ability)

return false unless token_ability_from_owner_usable?(ability, corporation)

return true if ability.same_hex_allowed

tokened_hexes = []

corporation.tokens.each do |token|
Expand Down
15 changes: 12 additions & 3 deletions lib/engine/game/g_1822/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,13 @@ def place_home_token(corporation)
end

def player_value(player)
player.value - @player_debts[player] + tax_haven_value(player)
# tax_haven_company.value can sometimes be zero and sometimes the same
# as tax_haven_value() (issues #5200 and #11007) because it is only
# set in company_status_str, which is only called by some views, so
# substract that value and include only the correct calculation
tax_haven_val = tax_haven_value(player) - (tax_haven_company&.value || 0)

player.value - @player_debts[player] + tax_haven_val
end

def purchasable_companies(entity = nil)
Expand Down Expand Up @@ -1956,6 +1962,7 @@ def preprocess_action(action)

def hex_blocked_by_ability?(entity, ability, hex, tile)
return false if tile.name == 'BC'
return false unless hex.tile.color == :white
return false unless ability.player
return false if entity.player == ability.player
return false if ability.hexes.none? { |h| h.id == hex.id }
Expand Down Expand Up @@ -2015,10 +2022,12 @@ def pending_home_tokeners
self.class::PENDING_HOME_TOKENERS
end

def share_owning_players
def tax_haven_company
@tax_haven_company ||= company_by_id(self.class::COMPANY_OSTH)
end

if @tax_haven_company&.owned_by_player?
def share_owning_players
if tax_haven_company&.owned_by_player?
[*@players, @tax_haven]
else
@players
Expand Down
1 change: 1 addition & 0 deletions lib/engine/game/g_1822_ca/entities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ module Entities
price: 0,
special_only: true,
teleport_price: 0,
same_hex_allowed: true,
when: 'owning_corp_or_turn',
},
],
Expand Down
5 changes: 5 additions & 0 deletions lib/engine/game/g_1822_ca/step/special_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def available_tokens(entity)
def process_place_token(action)
entity = action.entity.owner

if (city = action.city).tokened_by?(entity)
raise GameError,
"#{entity.name} already has a token in #{city.hex.location_name} (#{city.hex.id}) city #{city.index}"
end

super

@game.remove_exchange_token(entity)
Expand Down
22 changes: 11 additions & 11 deletions lib/engine/game/g_1822_pnw/step/acquire_company.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ module Game
module G1822PNW
module Step
class AcquireCompany < Engine::Step::AcquireCompany
attr_reader :choices

def actions(entity)
return ['choose'] if @choices

Expand All @@ -31,8 +33,6 @@ def process_acquire_company(action)
end
end

attr_reader :choices

def choice_name
'Pick which associated minor to replace'
end
Expand Down Expand Up @@ -95,21 +95,21 @@ def process_choose(action)
end

def p20_targets
bidbox_corporations = @game.bidbox_minors.map { |c| @game.corporation_from_company(c) }
targets = @game.corporations.select { |c| @game.associated_minor?(c) && !c.owner && !bidbox_corporations.include?(c) }
bidbox_minors = @game.bidbox_minors

# include minors that fell through the bidboxes without bids
@game.minor_associations.each do |minor_id, major_id|
@game.minor_associations.each_with_object([]) do |(minor_id, major_id), targets|
company = @game.company_by_id("M#{minor_id}")
minor = @game.corporation_by_id(minor_id)
major = @game.corporation_by_id(major_id)

targets << minor if !company.owner && company.closed? &&
!minor.owner && minor.closed? &&
!major.owner && !major.floated?
end
already_discarded = (!company.owner && company.closed? && minor.closed?)

minor_available = !minor.owner &&
(already_discarded || !bidbox_minors.include?(company))
major_available = !major.owner && !major.floated?

targets
targets << minor if minor_available && major_available
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/engine/game/g_1822_pnw_short/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def setup_companies
@company_trains['P1'] = find_and_remove_train_by_id('5P-0')
@company_trains['P2'] = find_and_remove_train_by_id('2P-0', buyable: false)
@company_trains['P3'] = find_and_remove_train_by_id('LP-0', buyable: false)
@company_trains['P5'] = find_and_remove_train_by_id('P-0', buyable: false)
@company_trains['P5'] = find_and_remove_train_by_id('P+-0', buyable: false)
end

def stock_round
Expand Down
2 changes: 1 addition & 1 deletion lib/engine/game/g_1822_pnw_short/trains.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ module Trains
price: 500,
},
{
name: 'P',
name: 'P+',
distance: [
{
'nodes' => ['city'],
Expand Down
Loading

0 comments on commit 67588f6

Please sign in to comment.