197 lines
5.2 KiB
Ruby

# The game class
class Game < ActiveRecord::Base
belongs_to :table, optional: true
has_many :seats
has_many :players, dependent: :destroy
has_many :users, through: :players
has_many :player_cards, through: :players
has_many :plays, through: :players
belongs_to :play_to_beat, optional: true
belongs_to :controlling_player, class_name: "Player", foreign_key: "controlling_player_id", optional: true
accepts_nested_attributes_for :players
#validates :title, presence: true
after_create :add_table
# Returns the newest joinable game or creates a new game if none are found
scope :play_now, lambda { |user|
where(status:nil).joins("LEFT OUTER JOIN players ON players.game_id = games.id").group("games.id").having("count(players) < 4").last || Game.create(title: "Game " + user.soft_token[0..4])
}
def start
validate_startable
return false unless startable?
# Start the game
self.status = 'Game Started'
# Create a deck
d = Deck.new
# Shuffle the deck
d.cards.shuffle!
# Deal the cards
players.each do |player|
13.times do
card = d.cards.shift
player.player_cards.new(card.instance_values)
end
player.save
end
# Set status to first turn
# Player with the lowest card
self.status = 'First Play at: ' + self.lowest_card.player.display_name
self.controlling_player_id = self.lowest_card.player.id
save
end
def winner
self.players.find_by_id(self.winner_player_id)
end
def validate_startable
@player_count = players.count
@user_count = players.where.not(soft_token: nil).count
errors.add(:players, 'Must be at least 2 players.') if @player_count < 2
errors.add(:players, 'Must be less than 5 players.') if @player_count > 4
errors.add(:status, 'Game already started...') if self.status.nil? == false
errors.add(:player_cards, 'Cards already dealt...') unless player_cards.empty?
fail StandardError, errors if errors.count > 0
return true
end
def current_hand
return nil
end
def startable?
begin
self.validate_startable
rescue
return false
end
end
def lowest_card
self.player_cards.order(:value).first
end
def add_player_from_user(user)
return false unless self.joinable?(user)
new_player = self.players.create(user: user, soft_token: user.soft_token)
end
def add_player_bot
# todo return if game is full
# return false unless self.joinable?(user)
new_player = self.players.create(is_bot: true)
end
def remove_player_from_user(user)
return false unless self.has_user? user
if self.players.exists?(user_id: user.id)
@player = self.players.find_by(user_id: user.id)
elsif self.players.exists?(soft_token: user.soft_token)
@player = self.players.find_by(soft_token: user.soft_token)
end
if @player
@player.user_id = nil
@player.soft_token = nil
end
end
def can_accomodate(user)
!(already_has?(user) || full?)
end
#TODO: Which has_user? method is better? Probably has_user?.
def already_has?(user)
self.players.pluck(:user_id, :soft_token).flatten.compact.to_set.intersect? ([user.id, user.soft_token].to_set)
end
def has_user? user
if user.id
return true if self.players.exists?(user_id: user.id)
end
if user.soft_token
return true if self.players.exists?(soft_token: user.soft_token)
end
return false
end
def full?
# players.count >= 4
end
def set_controlling_player(arg)
case arg
when 'next'
self.controlling_player_id = self.next_player_id
end
# Clear play to beat if play returns to owner
# if self.play_to_beat.try(:player).try(:id) == self.controlling_player_id
# self.play_to_beat_id = nil
# end
end
def joinable? user
# !self.has_user?(user) && !self.full?
!self.full?
end
def player_ids_by_seat_order
player_ids = self.seats.order(:position).select {|s| ( s.player.present? && s.player.inventory.count > 0) ? s.player_id : nil}.compact.map {|s| s.player_id}
end
def next_player_id
@controlling_player_index = self.player_ids_by_seat_order.index(controlling_player_id)
# TODO Fix bug
@controlling_player_index ||= 0
player_ids_by_seat_order[(@controlling_player_index + 1) % self.player_ids_by_seat_order.length]
end
# The user whose turn it is
def controlling_user
self.players.find(self.controlling_player_id).user
end
def hand_to_beat
self.play_to_beat.try(:play).try(:hand)
end
def player_to_beat
self.play_to_beat.try(:play).try(:player)
end
def started?
!self.status.nil?
end
def over?
return false unless self.started?
@inventory_counts = self.players.map {|p| p.player_cards.count > 0 ? p.inventory.count : nil}
@inventory_counts.include? 0
end
# private
def add_table
self.table ||= Table.create current_game_id: self.id
self.seats = self.table.seats
self.seats.each do |seat|
# if (seat.user_id || seat.user_soft_token)
if (seat.occupied?)
new_player = self.players.create(user_id: seat.user_id, soft_token: seat.user_soft_token, is_bot: seat.player.is_bot)
seat.player = new_player
seat.save
end
end
self.save
end
end