2024-06-11 13:55:39 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2024-07-08 14:08:17 +00:00
|
|
|
require "dice"
|
|
|
|
|
2024-06-11 13:55:39 +00:00
|
|
|
class DiceRoller
|
2024-06-20 07:26:33 +00:00
|
|
|
attr_reader :dice
|
|
|
|
attr_reader :result
|
|
|
|
|
2024-06-11 13:55:39 +00:00
|
|
|
def initialize(roll_command, stat: nil)
|
|
|
|
@roll_command = roll_command
|
|
|
|
@stat = stat&.value
|
2024-06-20 07:26:33 +00:00
|
|
|
@dice = []
|
2024-06-11 13:55:39 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def roll
|
2024-07-08 15:15:48 +00:00
|
|
|
roll_command = parse_roll_command_references(roll_command)
|
|
|
|
Dice.roll(roll_command)
|
2024-06-11 13:55:39 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def valid?
|
|
|
|
return if @roll_command.blank?
|
|
|
|
|
|
|
|
# No repeated math operators
|
|
|
|
return false if @roll_command.match?(/[+\-*\/]{2,}/)
|
|
|
|
|
|
|
|
# No leading or trailing math operators
|
|
|
|
return false if @roll_command.match?(/\A[+\-*\/]/) || @roll_command.match?(/[+\-*\/]\z/)
|
|
|
|
|
|
|
|
@roll_command.match?(
|
|
|
|
/
|
|
|
|
\A(
|
|
|
|
(\d*d\d*) |
|
|
|
|
([+\-*\/]) |
|
|
|
|
(\d+) |
|
|
|
|
(self)
|
|
|
|
)*\z/xi,
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2024-07-08 15:15:48 +00:00
|
|
|
def roll_delimiters
|
|
|
|
[ "+", "-", "*", "/" ]
|
|
|
|
end
|
|
|
|
|
2024-06-11 13:55:39 +00:00
|
|
|
def roll_dice(command)
|
|
|
|
parts = command.downcase.split("d").compact_blank
|
|
|
|
die_type = parts.last
|
|
|
|
dice_number = parts.length > 1 ? parts.first.to_i : 1
|
|
|
|
|
|
|
|
result = 0
|
|
|
|
dice_number.times do
|
2024-06-20 07:26:33 +00:00
|
|
|
roll = rand(1..die_type.to_i)
|
|
|
|
result += roll
|
|
|
|
dice << [ die_type, result ]
|
2024-06-11 13:55:39 +00:00
|
|
|
end
|
|
|
|
result
|
|
|
|
end
|
2024-07-08 15:15:48 +00:00
|
|
|
|
|
|
|
def parse_roll_command_references(roll_command)
|
|
|
|
roll_command = @roll_command.include?("self") ? @roll_command.gsub("self", @stat.to_s) : @roll_command
|
|
|
|
|
|
|
|
roll_command.split(Regexp.union(roll_delimiters)).each do |part|
|
|
|
|
next if part.match?(/\A\d*\z/)
|
|
|
|
next if part.match?(/\A\d*d\d*\z/)
|
|
|
|
|
|
|
|
value = CharacterSheetFeature.find_by(slug: part).featurable.value
|
|
|
|
raise "#{part} not found" if value.blank?
|
|
|
|
roll_command.gsub!(part, value.to_s)
|
|
|
|
end
|
|
|
|
roll_command
|
|
|
|
end
|
2024-06-11 13:55:39 +00:00
|
|
|
end
|