use rand::Rng;
use serde::Serialize;
use std::fmt;
#[derive(Copy, Clone, Debug, PartialEq, Serialize)]
pub struct Die {
pub rolled: i32,
pub die: i32,
pub value: Option<i32>,
}
impl fmt::Display for Die {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.value {
Some(n) => write!(f, "{}/{} ({})", self.rolled, self.die, n),
None => write!(f, "{}/{}", self.rolled, self.die),
}
}
}
impl Die {
pub fn roll<R: Rng + ?Sized>(range: i32, rng: &mut R) -> (Vec<Die>, i32) {
let mut dice = Vec::new();
let mut value = 0_i32;
match range {
2 => {
Die::roll_for_value(6, 6, &mut dice, rng);
value = (Die::value(&dice) as f64 / 3.0).ceil() as i32;
}
3 => {
Die::roll_for_value(6, 6, &mut dice, rng);
value = (Die::value(&dice) as f64 / 2.0).ceil() as i32;
}
4 => {
Die::roll_for_value(4, 4, &mut dice, rng);
value = Die::value(&dice);
}
5 => {
Die::roll_for_value(10, 10, &mut dice, rng);
value = Die::value(&dice);
if value == 0 {
value = 10;
}
value = (value as f64 / 2.0).ceil() as i32;
}
6 => {
Die::roll_for_value(6, 6, &mut dice, rng);
value = Die::value(&dice);
}
8 => {
Die::roll_for_value(8, 8, &mut dice, rng);
value = Die::value(&dice);
}
10 => {
Die::roll_for_value(10, 10, &mut dice, rng);
value = Die::value(&dice);
if value == 0 {
value = 10;
}
}
12 => {
Die::roll_for_value(12, 12, &mut dice, rng);
value = Die::value(&dice);
}
20 => {
Die::roll_for_value(20, 20, &mut dice, rng);
value = Die::value(&dice);
}
25 => {
Die::roll_for_value(100, 10, &mut dice, rng);
value = Die::value(&dice);
if value == 0 {
value = 100;
}
value = ((value as f64) / 4.0).ceil() as i32;
}
50 => {
Die::roll_for_value(100, 10, &mut dice, rng);
value = Die::value(&dice);
if value == 0 {
value = 100;
}
value = ((value as f64) / 2.0).ceil() as i32;
}
100 => {
Die::roll_for_value(100, 10, &mut dice, rng);
value = Die::value(&dice);
if value == 0 {
value = 100;
}
}
1000 => {
Die::roll_for_value(1000, 10, &mut dice, rng);
value = Die::value(&dice);
if value == 0 {
value = 1000;
}
}
10000 => {
Die::roll_for_value(10000, 10, &mut dice, rng);
value = Die::value(&dice);
if value == 0 {
value = 10000;
}
}
_ => {
if range < 10 {
Die::roll_for_value(range, 10, &mut dice, rng);
value = Die::value(&dice);
} else if range < 20 {
Die::roll_for_value(range, 20, &mut dice, rng);
value = Die::value(&dice);
}
}
}
(dice, value)
}
pub fn value(dice: &Vec<Die>) -> i32 {
let mut value = 0;
for die in dice.into_iter() {
match die.value {
Some(n) => value = value + n,
None => (),
}
}
value
}
fn roll_for_value<R: Rng + ?Sized>(range: i32, die: i32, dice: &mut Vec<Die>, rng: &mut R) {
if die == 10 && range % 10 == 0 {
let mut total = 0;
let mut n = range;
loop {
let rolled = rng.random_range(0..10);
n = n / 10;
total = total + (rolled * n);
dice.push(Die {
rolled,
die,
value: Some(rolled * n),
});
if n < 10 {
break;
}
}
} else if range == die {
let rolled = rng.random_range(0..die) + 1;
dice.push(Die {
rolled: rolled,
die,
value: Some(rolled),
});
} else if range < die {
loop {
let rolled = rng.random_range(0..die) + 1;
if rolled > range {
dice.push(Die {
rolled,
die,
value: None,
})
} else {
dice.insert(
0,
Die {
rolled,
die,
value: Some(rolled),
},
);
break;
}
}
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct Value {
pub value: i32,
pub range: i32,
pub dice: Vec<Die>,
add: i32,
mul: i32,
constant: bool,
bonus: bool,
keep: bool,
hit: bool,
sum: i32,
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.keep {
match self.bonus {
false => {
if self.value != self.sum {
write!(f, "{} ({})", self.value, self.sum)
} else {
write!(f, "{}", self.value)
}
}
true => {
if self.value != self.sum {
write!(f, "{}* ({})", self.value, self.sum)
} else {
write!(f, "{}*", self.value)
}
}
}
} else {
match self.bonus {
false => write!(f, "{}-", self.value + self.add),
true => write!(f, "{}*-", self.value + self.add),
}
}
}
}
impl Value {
pub fn constant(value: i32) -> Value {
Value {
value,
range: value,
dice: vec![],
add: 0,
mul: 1,
constant: true,
bonus: false,
keep: true,
hit: false,
sum: value,
}
}
pub fn d6(value: i32) -> Value {
Value {
value,
range: 6,
dice: vec![Die {
rolled: value,
value: Some(value),
die: 6,
}],
add: 0,
mul: 1,
constant: false,
bonus: false,
keep: true,
hit: false,
sum: value,
}
}
pub fn random<R: Rng + ?Sized>(range: i32, bonus: bool, rng: &mut R) -> Value {
let (dice, value) = Die::roll(range, rng);
Value {
value,
range,
dice: dice,
constant: false,
add: 0,
mul: 1,
bonus,
keep: true,
hit: true,
sum: value,
}
}
pub fn sum(&self) -> i32 {
self.sum
}
pub fn is_const(&self) -> bool {
self.constant
}
pub fn is_random(&self) -> bool {
!self.is_const()
}
pub fn is_hit(&self) -> bool {
self.keep && self.hit
}
pub fn is_bonus(&self) -> bool {
self.bonus
}
pub fn is_discarded(&self) -> bool {
!self.keep
}
pub fn modifier(&self) -> i32 {
self.add
}
pub fn set_modifier(&mut self, add: i32) {
self.add = add;
if self.keep {
self.sum = self.mul * (self.value + add);
}
}
pub fn mark_bonus(&mut self) {
self.bonus = true;
}
pub fn mark_penalty(&mut self) {
self.mul = -1;
self.sum = self.mul * (self.value + self.add);
}
pub fn mark_discarded(&mut self) {
self.keep = false;
self.sum = 0;
self.mul = 0;
}
pub fn set_hit(&mut self, hit: bool) {
self.hit = hit;
if self.keep {
if hit {
self.sum = self.mul;
} else {
self.sum = 0;
}
}
}
}
#[derive(Debug, Serialize)]
pub struct Pool {
pub values: Vec<Value>,
total: Option<i32>,
}
impl fmt::Display for Pool {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut first = true;
for v in self.values.iter() {
if first {
write!(f, "{}", v)?;
first = false;
} else {
write!(f, ", {}", v)?;
}
}
match self.total {
Some(v) => write!(f, " = {} {{{}}}", self.sum(), v),
None => write!(f, " = {}", self.sum()),
}
}
}
impl Default for Pool {
fn default() -> Self {
Self::new()
}
}
impl Pool {
pub fn new() -> Pool {
Pool {
values: vec![],
total: None,
}
}
pub fn new_with_values(values: Vec<Value>) -> Pool {
Pool {
values,
total: None,
}
}
pub fn range(&self) -> i32 {
if self.values.is_empty() {
0
} else {
self.values
.iter()
.filter(|&v| !v.constant)
.map(|v| v.range)
.max()
.unwrap()
}
}
pub fn count(&self) -> usize {
self.values.len()
}
pub fn sum(&self) -> i32 {
self.values.iter().map(|v| v.sum()).sum()
}
pub fn kept(&self) -> usize {
self.values.iter().filter(|v| !v.is_discarded()).count()
}
pub fn values(&self) -> Vec<Option<i32>> {
self.values
.iter()
.map(|v| {
if v.is_discarded() {
None
} else {
Some(v.sum())
}
})
.collect()
}
pub fn hits(&self) -> usize {
self.values.iter().filter(|v| v.is_hit()).count()
}
pub fn bonus(&self) -> usize {
self.values.iter().filter(|v| v.is_bonus()).count()
}
pub fn value(&self) -> i32 {
if let Some(v) = self.total {
v
} else {
self.sum()
}
}
pub fn last_value(&self) -> i32 {
self.values[self.count() - 1].value
}
pub fn last_range(&self) -> i32 {
self.values[self.count() - 1].range
}
pub fn set_total(&mut self, value: i32) {
self.total = Some(value)
}
}
#[derive(Serialize)]
pub struct Results {
pub lhs: Pool,
pub rhs: Option<Pool>,
pub value: i32,
}
impl fmt::Display for Results {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.lhs)?;
match &self.rhs {
Some(rhs) => write!(f, " <> {} = {}", rhs, self.sum())?,
None => (),
}
write!(f, "")
}
}
impl Results {
pub fn sum(&self) -> i32 {
match &self.rhs {
Some(_) => self.value,
None => self.lhs.value(),
}
}
}