extern crate rand;

extern crate colored;
use colored::*;

use rand::Rng;
use std::{io, process};
use std::rc::Rc;
use std::cell::{RefCell, RefMut};
use std::cmp::{max, min};

//// UTIL

fn get_user_input(max: usize) -> usize {
    let mut input_text = String::new();
    io::stdin()
        .read_line(&mut input_text)
        .expect("failed to read from stdin");

    let trimmed = input_text.trim();
    match trimmed.parse::<usize>() {
        Ok(i) => {
            if i < max {
                return i;
            }
            println!("Введите число от 0 до {}", max-1);
            get_user_input(max)
        }
        Err(..) => {
            println!("Введите число");
            get_user_input(max)
        }
    }
}

fn get_random_number(max: usize) -> usize {
    if max == 0 {
        return 0;
    }
    rand::thread_rng().gen_range(0, max)
}


/// CHAR EFFECTS (battle)
///
#[derive(Clone)]
pub struct Effects
{
    // -x debuff(/2) for x rounds, +x buff(*2) for x rounds
    atk: i32, // x2
    def: i32, // x2
    eva: i32, // x3
    crit: i32, // x3
}

impl Default for Effects
{
    fn default() -> Effects {
        Effects { atk: 0, def: 0, crit: 0, eva: 0 }
    }
}

impl Effects
{
    fn on_turn(&mut self) {
        let progress = |x: &mut i32| -> () {
            if *x < 0 {
                *x += 1;
            }
            if *x > 0 {
                *x -= 1;
            }
        };
        progress(&mut self.atk);
        progress(&mut self.def);
        progress(&mut self.eva);
        progress(&mut self.crit);
    }
    fn effect(stat: usize, effect: i32, mul: usize) -> usize {
        if effect > 0 {
            return stat * mul;
        }
        if effect < 0 {
            return stat / mul;
        }
        stat
    }
    fn atk(&self, dmg: i32) -> i32 {
        if dmg > 0 {
            Self::effect(dmg as usize, self.atk, 2) as i32
        } else {
            dmg
        }
    }
    fn def(&self, dmg: i32) -> i32 {
        if dmg > 0 {
            Self::effect(dmg as usize, -self.def, 2) as i32
        } else {
            dmg
        }
    }
    fn eva(&self, eva: usize) -> usize {
        Self::effect(eva, self.eva, 3)
    }
    fn crit(&self, crit: usize) -> usize {
        Self::effect(crit, self.crit, 3)
    }

    fn apply(&mut self, e: Effects) {
        self.atk += e.atk;
        self.def += e.def;
        self.eva += e.eva;
        self.crit += e.crit;
    }
}

///TRAITS
#[derive(Clone, Copy, PartialEq)]
enum DamageType
{
    Phys,
    Shock,
    Gun,
}

trait CharacterTrait
{
    fn take_damage(&mut self, dt: DamageType, dmg: i32, source: &mut dyn CharacterTrait);
    fn level(&self) -> [i32; 3];
    fn effects(&mut self) -> &mut Effects;
    fn crit_rate(&self) -> usize;

    fn heal(&mut self, hp : i32);

    fn print_sheet(&self);
}

pub struct Usable(RefCell<i32>);

impl Usable
{
    fn useless(&self) -> bool {
        *(self.0.borrow()) == 0
    }
    fn use_item(&self) -> bool { // true if becomes useless
        if *self.0.borrow() > 0 {
            *self.0.borrow_mut() -= 1;
        }
        self.useless()
    }

    fn uses_left(&self) -> RefMut<i32> { self.0.borrow_mut() }

    fn new(uses: i32) -> Usable {
        Usable(RefCell::new(uses))
    }
}

trait Equipment
{
    fn name(&self) -> String;
    fn provides_action(&self) -> bool { false }
    fn on_action(&self, _source: &mut dyn CharacterTrait, _target: &mut dyn CharacterTrait) {}
    fn on_damage(&self, _t: DamageType, dmg: i32) -> i32 { dmg } // reduce dmg

    fn can_stack(&self) -> bool { false }
    fn usable(&self) -> Option<&Usable> { None }

    fn on_expiration(&self) { println!("{} ломается", self.name().yellow()); }
}


/// WEAPONS

pub struct Weapon
{
    u : Usable,
    level: i32,
    wtype: DamageType,
}

impl Equipment for Weapon
{
    fn name(&self) -> String
    {
        let mut wname: String =
            match self.wtype {
                DamageType::Phys => "Дубинка",
                DamageType::Shock => "Шокер",
                DamageType::Gun => "Пистолет",
                //_ => "???".to_string()
            }.to_string();
        if self.level > 0 {
            wname = format!("{} +{}", wname, self.level);
        }
        wname
    }

    fn provides_action(&self) -> bool { true }

    fn on_action(&self, source: &mut dyn CharacterTrait, target: &mut dyn CharacterTrait)
    {
        // calculate damage...
        let dmg = 10 + 5 * self.level + 2 * source.level()[self.wtype as usize];

        self.u.use_item();
        target.take_damage(self.wtype, dmg, source);
    }
    fn usable(&self) -> Option<&Usable> {
        Some(&self.u)
    }
    fn can_stack(&self) -> bool { self.wtype == DamageType::Gun } // only gun can stack ammo

}


pub struct Vest
{
    u: Usable
}

impl Equipment for Vest
{
    fn name(&self) -> String { "Бронежилет".to_string() }

    fn on_damage(&self, t: DamageType, dmg: i32) -> i32
    {
        if t == DamageType::Gun {
            self.u.use_item();
            println!("Пуля застревает в бронежилете!");
            return 0;
        }
        dmg
    }
    fn usable(&self) -> Option<&Usable> {
        Some(&self.u)
    }
}

pub struct Antishock
{
    u: Usable
}

impl Equipment for Antishock
{
    fn name(&self) -> String { "Свитер".to_string() }

    fn on_damage(&self, t: DamageType, dmg: i32) -> i32
    {
        if t == DamageType::Shock {
            self.u.use_item();
            println!("Свитер защищает от шокера!");
            return 0;
        }
        dmg
    }
    fn usable(&self) -> Option<&Usable> {
        Some(&self.u)
    }
}

pub struct IronArmor;

impl Equipment for IronArmor
{
    fn name(&self) -> String { "Железная броня".to_string() }

    fn on_damage(&self, t: DamageType, dmg: i32) -> i32
    {
        if t == DamageType::Shock {
            dmg * 3
        } else {
            println!("Железная броня блокирует {}", match t {
                DamageType::Gun => "пулю",
                _ => "удар"
            });
            1
        }
    }
}


pub struct TempBuff
{
    u: Usable,
    name: String,
    desc: String,
    self_effect: Effects,
    target_effect: Effects,
    heal : i32,
}

impl Default for TempBuff
{
    fn default() -> TempBuff {
        TempBuff { name : "Предмет".to_string(), desc : "".to_string(),
        self_effect : Effects::default(), target_effect : Effects::default(),
            heal : 0, u : Usable::new(1)
        }
    }
}

impl Equipment for TempBuff
{
    fn name(&self) -> String { self.name.clone() }

    fn usable(&self) -> Option<&Usable> { Some(&self.u) }

    fn provides_action(&self) -> bool { true }

    fn on_action(&self, source: &mut dyn CharacterTrait, target: &mut dyn CharacterTrait)
    {
        self.u.use_item();
        println!("{}", self.desc);
        source.effects().apply(self.self_effect.clone());
        target.effects().apply(self.target_effect.clone());
        source.heal(self.heal);
    }

    fn on_expiration(&self) { println!("{} кончается", self.name().yellow()); }
    fn can_stack(&self) -> bool { true }
}


pub struct Calculator
{
    u: Usable,
    div: i32,
}

impl Equipment for Calculator
{
    fn name(&self) -> String
    {
        if self.div == 1 {
            "Калькулятор для простых ЛВЛ".to_string()
        } else {
            format!("Калькулятор ЛВЛ÷{}", self.div)
        }
    }

    fn usable(&self) -> Option<&Usable> { Some(&self.u) }

    fn provides_action(&self) -> bool { true }

    fn on_action(&self, source: &mut dyn CharacterTrait, target: &mut dyn CharacterTrait)
    {
        self.u.use_item();
        let calculate = |lvl: i32| -> bool {
            let mut ok = false;
            if self.div == 1 {
                ok = true;
                for x in 2..(lvl - 1) {
                    if lvl % x == 0 {
                        ok = false;
                        break;
                    }
                }
            } else if lvl % self.div == 0 {
                ok = true;
            }
            if self.div == 1 {
                println!("{} - {}простое число", lvl, match ok {
                    true => "",
                    false => "не "
                });
            } else {
                println!("{} - {}делится на {}!", lvl, match ok {
                    true => "",
                    false => "не "
                }, self.div);
            }
            ok
        };
        let dmg = 150;
        if calculate(target.level()[0]) {
            target.take_damage(DamageType::Phys, dmg, source);
        }
        if calculate(target.level()[1]) {
            target.take_damage(DamageType::Shock, dmg, source);
        }
        if calculate(target.level()[2]) {
            target.take_damage(DamageType::Gun, dmg, source);
        }
        if calculate(target.level()[0] + target.level()[1] + target.level()[2]) {
            target.take_damage(DamageType::Phys, dmg, source);
        }
        if calculate(source.level()[0] + source.level()[1] + source.level()[2]) {
            source.take_damage(DamageType::Phys, dmg, target);
        }
    }
}

pub struct Glasses
{
    u: Usable
}

impl Equipment for Glasses
{
    fn name(&self) -> String { "AR очки".to_string() }

    fn usable(&self) -> Option<&Usable> { Some(&self.u) }

    fn provides_action(&self) -> bool { true }

    fn on_action(&self, _source: &mut dyn CharacterTrait, target: &mut dyn CharacterTrait)
    {
        self.u.use_item();
        println!("{}", "Вы надеваете очки дополненной реальности и видите всю информацию о противнике".bold());
        target.print_sheet();
    }
}



// CHAR


pub struct Character
{
    name: String,
    hp: i32,
    max_hp: i32,

    money: i32,

    level: [i32; 3], // (str, dex, cha)

    evasion: [usize; 3],
    crit_rate: usize,

    eff: Effects,

    npc: bool,
    equip: Vec<Rc<dyn Equipment>>,

}

impl Default for Character
{
    fn default() -> Character {
        Character {
            name: "Кто-то".to_string(),
            hp: 0,
            max_hp: 1,
            money: 0,
            level: [0, 0, 0],
            evasion: [0, 0, 0],
            crit_rate: 0,
            eff: Effects::default(),
            npc: true,
            equip: vec![],
        }
    }
}

impl CharacterTrait for Character
{
    fn take_damage(&mut self, dt: DamageType, dmg: i32, source: &mut dyn CharacterTrait)
    {
        let mut new_dmg = self.effects().def(source.effects().atk(dmg));

        let cr = source.crit_rate();
        let eva = self.evasion[dt as usize];
        let rng = get_random_number(100);
        if rng < source.effects().crit(cr) {
            println!("{}", "Критический удар!".red());
            new_dmg *= 2; // x2, ignores equips
        } else if rng >= (100 - self.effects().eva(eva)) {
            println!("{}", "Промах!".yellow());
            new_dmg = 0;
        } else {
            for eq in &mut self.equip
                {
                    new_dmg = eq.on_damage(dt, new_dmg);
                    if new_dmg == 0 {
                        break;
                    }
                }
        }

        println!("{} получает {} урона!", self.name.yellow(), new_dmg.to_string().red());
        self.hp = max(self.hp - new_dmg, 0);
        if !self.npc {
            println!("У вас {}/{} HP", self.hp, self.max_hp);
        }
        //...
    }

    fn heal(&mut self, hp : i32)
    {
        self.hp = max(0, min(self.hp + hp, self.max_hp));
    }

    fn level(&self) -> [i32; 3] { self.level }

    fn effects(&mut self) -> &mut Effects { &mut self.eff }

    fn crit_rate(&self) -> usize { self.crit_rate }

    fn print_sheet(&self)
    {
        println!("Имя: {}", self.name.yellow());
        println!("HP: {}/{}", self.hp, self.max_hp);
        println!("УРОВЕНЬ: {}: СИЛ {}, ЛОВ {}, ХАР {}", self.level[0] + self.level[1] + self.level[2], self.level[0], self.level[1], self.level[2]);
        println!("УКЛОН: ФИЗ {}% / ШОК {}% / СТР {}%", self.evasion[0], self.evasion[1], self.evasion[2]);
        println!("КРИТ: {}%", self.crit_rate);
        println!("Деньги: ${}", self.money);
        println!("Экипировка:");
        for eq in self.equip.iter() {
            if let Some(u) = eq.usable() {
                if *u.uses_left() > 0 {
                    println!(" - {} ({})", eq.name().blue(), *u.uses_left());
                    continue;
                }
            }
            println!(" - {}", eq.name().blue());
        }
        println!("Эффекты: АТК(урон*2): {} ЗАЩ(урон/2): {} УКЛОН*3: {} КРИТ*3: {}",
                 self.eff.atk, self.eff.def, self.eff.eva, self.eff.crit);
    }
}


impl Character
{
    fn take_action(&mut self, target: &mut Self)
    {
        let actions: Vec<&Rc<dyn Equipment>> =
            self.equip
                .iter()
                .filter(|&eq| eq.provides_action())
                .collect();
        let act_idx: usize;

        if actions.is_empty()
        {
            println!("{} ничего не делает", self.name);
            return;
        }

        if self.npc {
            act_idx = get_random_number(actions.len());
        } else {
            for (idx, &eq) in actions
                .iter()
                .enumerate()
                {
                    if let Some(u) = eq.usable() {
                        if *u.uses_left() > 0 {
                            println!("{}) {} ({})", idx, eq.name().blue(), *u.uses_left());
                            continue;
                        }
                    }
                    println!("{}) {}", idx, eq.name().blue());
                }
            act_idx = get_user_input(actions.len());
        }

        println!("{} использует {}!", self.name.as_str().yellow(), actions[act_idx].name().blue());
        actions[act_idx]
            .clone()
            .on_action(self, target);

        // remove useless (broken) equip
        self.equip.retain(|e| -> bool {
            if let Some(u) = e.usable() {
                if u.useless() {
                    e.on_expiration();
                    return false;
                }
            }
            true
        });
    }

    fn give(&mut self, equip: Rc<dyn Equipment>)
    {
        for eq in self.equip.iter() {
            if eq.name() == equip.name() {
                if let Some(u1) = eq.usable() {
                    if let Some(u2) = equip.usable() {
                        if *u1.uses_left() >= 0 {
                            if eq.can_stack() {
                                *u1.uses_left() += *u2.uses_left();
                            } else {
                                let before = *u1.uses_left();
                                *u1.uses_left() = max( before, *u2.uses_left());
                            }
                        }
                    }
                } else {
                    // already exists
                }
                return;
            }
        }
        self.equip.push(equip);
        self.equip.sort_by_key(|e| e.name());
    }
}

fn generate_character(tutorial : usize, atk_lvl : i32, def_lvl : i32) -> Character
{
    let mut chr  = Character {
       ..Default::default()
    };
    chr.level = [
        atk_lvl / 3 + get_random_number(atk_lvl as usize / 3) as i32,
        atk_lvl / 3 + get_random_number(atk_lvl as usize / 3) as i32,
        atk_lvl / 3 + get_random_number(atk_lvl as usize / 3) as i32
    ];

    let names = [ "коп", "полицейский", "детектив" ];
    chr.name = names[get_random_number(names.len())].to_string();

    let immunity = (tutorial & 2) != 0;
    let evasion = (tutorial & 4)  != 0;
    let buffs = (tutorial & 8)  != 0;
    let debuffs = (tutorial & 16)  != 0;
    let critter = (tutorial & 32)  != 0;

    chr.money = (atk_lvl + def_lvl) * 50 + get_random_number(200) as i32;
    chr.max_hp = def_lvl * 5 + get_random_number(5 * def_lvl as usize) as i32;

    let robot = match tutorial < 10 { // robots from 10+lvl
        true => false,
        false => get_random_number(5) == 0 // 20%
    };
    let weapons = min(atk_lvl as usize, get_random_number(7) + 1); // non empty subset of 3 weapons [1,7]
    //let give_weapons = |chr : &mut Character, weapons : usize| {
        if weapons & 1 != 0 {
            chr.give(Rc::new(Weapon{ wtype : DamageType::Phys, level : atk_lvl / 10, u : Usable::new(-1)}));
        }
        if weapons & 2 != 0 && !robot {
            chr.give(Rc::new(Weapon{ wtype : DamageType::Shock, level : atk_lvl / 10, u : Usable::new(-1)}));
        }
        if weapons & 4 != 0 {
            chr.give(Rc::new(Weapon{ wtype : DamageType::Gun, level : atk_lvl / 10, u : Usable::new(-1)}));
        }
    //};

    if evasion {
        if !immunity  && !robot {
            //chr.level[1] *= 2; // high dex
            chr.name = "Кунг-фу коп".to_string();
            chr.evasion = [min(2 * def_lvl as usize, 60), min(2 * def_lvl as usize, 50), min(2 * def_lvl as usize, 50)];

        } else {
            chr.evasion = [
                get_random_number((3 + def_lvl / 5) as usize),
                get_random_number((3 + def_lvl / 5) as usize),
                get_random_number((3 + def_lvl / 5) as usize)];
        }
    }
    if robot {
        chr.level[1] /= 2; // low dex
        chr.name = "Робокоп".to_string();
        chr.give(Rc::new(IronArmor{}));
    } else {
        if immunity {
            let uses = 3 * (def_lvl / 20 + 1);
            let armor = get_random_number(3) + 1;
            if armor & 1 != 0 {
                if get_random_number(3) == 0 {
                    chr.name = "спецназовец".to_string();
                    chr.give(Rc::new(TempBuff {
                        name : "Щит".to_string(),
                        desc : "↑ЗАЩ на 4 хода".to_string(),
                        self_effect : Effects { def : 4, ..Default::default() },
                        u : Usable::new(1),
                        ..Default::default()
                    }));

                }
                chr.give(Rc::new(Vest { u: Usable::new(uses) }));
            }
            if armor & 2 != 0 {
                chr.give(Rc::new(Antishock { u: Usable::new(uses) }));
            }
        }
        if buffs {
            chr.name = format!("{}-наркобарон", chr.name);
            let drugs = get_random_number(7) + 1;
            if drugs & 1 != 0 {
                chr.give(Rc::new(TempBuff {
                    name: "Кокаин".to_string(),
                    desc: "↑АТК ↓ЗАЩ на 6 ходов".to_string(),
                    self_effect: Effects { atk: 6, def: -6, ..Default::default() },
                    ..Default::default()
                }));
            }
            if drugs & 2 != 0 {
                chr.give(Rc::new(TempBuff {
                    name: "Морфий".to_string(),
                    desc: "↑10HP ↑ЗАЩ ↓УКЛОН на 6 ходов".to_string(),
                    heal: 10,
                    self_effect: Effects { eva: -6, def: 6, ..Default::default() },
                    ..Default::default()
                }));
            }
            if drugs & 4 != 0 {
                chr.give(Rc::new(TempBuff {
                    name: "Героин".to_string(),
                    desc: "↑АТК ↓УКЛОН на 6 ходов".to_string(),
                    heal: 10,
                    self_effect: Effects { eva: -6, atk: 6, ..Default::default() },
                    ..Default::default()
                }));
            }
        }
        if atk_lvl + def_lvl > 30 {
            chr.give(generate_loot(atk_lvl, def_lvl as usize));
        }
        if atk_lvl + def_lvl > 60 {
            chr.give(generate_loot(atk_lvl, def_lvl as usize));
        }
    }

    if debuffs {
        chr.name = format!("подлый {}", chr.name);
        //match
        let deb = 1 + get_random_number(3);
        if deb & 1 != 0 {
            chr.give(Rc::new(TempBuff {
                name: "Наручники".to_string(),
                desc: "Цель: ↓УКЛОН ↓АТК на 7 ходов".to_string(),
                target_effect: Effects { eva: -7, atk: -7, ..Default::default() },
                ..Default::default()
            }));
        }
        if deb & 2 != 0 {
            chr.give(Rc::new(TempBuff {
                name: "Перцовый баллончик".to_string(),
                desc: "Цель: ↓ЗАЩ ↓АТК на 5 ходов".to_string(),
                target_effect: Effects { def: -5, atk: -5, ..Default::default() },
                ..Default::default()
            }));
        }
    }

    if critter {
        chr.name = format!("стильный {}", chr.name);
        chr.crit_rate = min(35, 10+get_random_number(atk_lvl as usize));
    }

    //
    chr
}

fn generate_loot(loot_level : i32, variance : usize) -> Rc<dyn Equipment>
{
    let lvl = loot_level + get_random_number(variance) as i32;
    let rng = get_random_number(100);
    (match (lvl,rng)  {
        (_, 0..=19) => {let t = get_random_number(3); Rc::new(Weapon {
            level : lvl / 10,
            wtype : match t {
                0 => DamageType::Phys,
                1 => DamageType::Shock,
                _ => DamageType::Gun,
            },
            u : Usable::new(match t {0|1=>10, _=>7}),
        })},
        (_, 20..=24) => Rc::new(Vest {
            u : Usable::new(3),
        }),
        (_, 25..=29) => Rc::new(Antishock {
            u : Usable::new(3),
        }),
        (_, 30..=39) => Rc::new(TempBuff {
            name : "Аптечка".to_string(),
            desc : "↑130HP".to_string(),
            heal : 130,
            ..Default::default()
        }),
        (_, 40..=49) => Rc::new(TempBuff {
            name : "Бинт".to_string(),
            desc : "↑50HP".to_string(),
            heal : 50,
            ..Default::default()
        }),
        (10..=999, 50..=54) => Rc::new(Glasses {
            u : Usable::new(5),
        }),
        (20..=999, 55..=59) => Rc::new(TempBuff {
            name : "Жвачка".to_string(),
            desc : "Цель: ↓УКЛОН на 3 хода".to_string(),
            heal : 10,
            target_effect : Effects { eva : -3, ..Default::default() },
            u : Usable::new(3),
            ..Default::default()
        }),
        (20..=999, 60..=64) => Rc::new(TempBuff {
            name : "Кокаин".to_string(),
            desc : "↑АТК ↓ЗАЩ на 6 ходов".to_string(),
            self_effect : Effects { atk : 6, def : -6, ..Default::default() },
            ..Default::default()
        }),
        (20..=999, 65..=69) => Rc::new(TempBuff {
            name : "Морфий".to_string(),
            desc : "↑10HP ↑ЗАЩ ↓УКЛОН на 6 ходов".to_string(),
            heal : 10,
            self_effect : Effects { eva : -6, def : 6, ..Default::default() },
            ..Default::default()
        }),
        (30..=999, 70..=74) => Rc::new(TempBuff {
            name : "Наручники".to_string(),
            desc : "Цель: ↓УКЛОН ↓АТК на 5 ходов".to_string(),
            target_effect : Effects { eva : -5, atk : -5, ..Default::default() },
            ..Default::default()
        }),
        (30..=999, 75..=79) => Rc::new(TempBuff {
            name : "Перцовый баллончик".to_string(),
            desc : "↓30HP Цель: ↓ЗАЩ ↓АТК на 5 ходов".to_string(),
            target_effect : Effects { def : -5, atk : -5, ..Default::default() },
            heal : -30,
            ..Default::default()
        }),
        (20..=999, 80..=82) => Rc::new(TempBuff {
            name : "Сигарета".to_string(),
            desc : "↑УКЛОН ↓КРИТ на 3 хода".to_string(),
            self_effect : Effects { eva : 3, crit : -3, ..Default::default() },
            u : Usable::new(10),
            ..Default::default()
        }),
        //..
        (40..=999, 90..=99) => Rc::new( Calculator {
            div : match get_random_number(min(lvl,100) as usize) {
                0..=35 => 7,
                35..=50 => 5,
                50..=65 => 1,
                65..=80 => 4,
                85..=95 => 3,
                _ => 2,
            },
            u : Usable::new(3),
        }),
        (_, _) => generate_loot(lvl, variance),

    })
}

fn battle_scene(pc: &mut Character, mut npc: Character)
{
    pc.hp = pc.max_hp;
    pc.eff = Effects::default();
    npc.hp = npc.max_hp;
    npc.eff = Effects::default();
    loop {
        pc.eff.on_turn();
        pc.take_action(&mut npc);
        if npc.hp <= 0 {
            println!("{} побеждает!", pc.name.yellow());
            pc.money += npc.money;
            println!("Вы нашли ${}. Всего ${}", npc.money, pc.money);
            break;
        }

        npc.eff.on_turn();
        npc.take_action(pc);

            if pc.hp <= 0 {
                println!("{} побеждает", npc.name.red());
                println!("{}", "GAME OVER".red());
                println!("Score: {}", pc.money.to_string().red());
                println!("Продолжить?");
                println!("0) Да (↓ЛВЛ, $0)");
                println!("1) Нет");
                if get_user_input(2) == 1 {
                    process::exit(0);
                }
                for i in &mut pc.level {
                    *i -= 1;
                }
                pc.money = 0;
                break;
        }
    }
    pc.eff = Effects::default();
    pc.hp = pc.max_hp;
}

fn cafe_scene(pc: &mut Character)
{
    loop {
        let level = pc.level[0] + pc.level[1] + pc.level[2];
        let price = |lvl| max(50, lvl * 100);
        let take_money = |lvl, money: &mut i32| -> bool {
            if *money >= price(lvl) {
                *money -= price(lvl);
                true
            } else {
                println!("Не хватает денег ${}/${}", *money, price(lvl));
                false
            }
        };
        println!("0) Выход");
        println!("1) Пончик с джемом (${})", price(level));
        println!("2) Пончик с корицей (${})", price(level));
        println!("3) Пончик с кремом (${})", price(level));
        println!("4) Статус");
        println!("Деньги: ${}", pc.money);
        //println!("Уровень: {} STR {}, DEX {}, CHA {}",  pc.level[0] + pc.level[1] + pc.level[2], pc.level[0], pc.level[1], pc.level[2])

        match get_user_input(5) {
            0 => { break; }
            1 => {
                if take_money(level, &mut pc.money) {
                    // str
                    println!("Вы чувствуете себя сильнее! ↑HP ↑ФИЗАТК");
                    pc.level[0] += 1;
                    pc.max_hp += match pc.level[0] {
                        0..=4 => 10,
                        5..=9 => 9,
                        10..=14 => 8,
                        _ => 7
                    };
                    pc.hp = pc.max_hp;

                    if pc.level[0] == 10 {
                        println!("Вы теперь можете использовать {}", "Защита".blue());
                        pc.give(Rc::new(TempBuff {
                            name : "Защита".to_string(),
                            desc : "↑ЗАЩ на 3 хода".to_string(),
                            self_effect : Effects { def : 3, ..Default::default() },
                            u : Usable::new(-1),
                            ..Default::default()
                        }));
                    }
                    if pc.level[0] == 20 {
                        println!("Вы теперь можете использовать {}", "Атака".blue());
                        pc.give(Rc::new(TempBuff {
                            name : "Атака".to_string(),
                            desc : "↑АТК на 3 хода".to_string(),
                            self_effect : Effects { atk : 3, ..Default::default() },
                            u : Usable::new(-1),
                            ..Default::default()
                        }));
                    }
                }
            }
            2 => {
                if take_money(level, &mut pc.money) {
                    println!("Вы чувствуете себя более ловким! ↑УКЛОН ↑ШОКАТК");
                    // dex
                    pc.max_hp += 2;
                    pc.level[1] += 1;
                    if pc.evasion[0] < 33 {
                        for e in pc.evasion.iter_mut() {
                            *e += 1;
                        }
                    }
                }
            }
            3 => {
                if take_money(level, &mut pc.money) {
                    println!("Вы чувствуете себя более харизматичным! ↑КРИТ ↑СТРАТК");
                    // cha
                    pc.max_hp += 2;
                    pc.level[2] += 1;
                    if pc.crit_rate < 33 {
                        pc.crit_rate += 1;
                    }
                    if pc.level[2] == 10 {
                        println!("Вы теперь можете использовать {}", "Улыбка".blue());
                        pc.give(Rc::new(TempBuff {
                            name : "Улыбка".to_string(),
                            desc : "↑КРИТ на 3 хода".to_string(),
                            self_effect : Effects { crit : 3, ..Default::default() },
                            u : Usable::new(-1),
                            ..Default::default()
                        }));
                    }
                    if pc.level[2] == 20 {
                        println!("Вы теперь можете использовать {}", "Надсмешка".blue());
                        pc.give(Rc::new(TempBuff {
                            name : "Надсмешка".to_string(),
                            desc : "Цель: ↓КРИТ на 3 хода".to_string(),
                            target_effect : Effects { crit : -3, ..Default::default() },
                            u : Usable::new(-1),
                            ..Default::default()
                        }));
                    }
                }
            }
            4 => pc.print_sheet(),
            _ => panic!("bad input in cafeteria")
        }
    }
}


fn corridor_scene(pc: &mut Character, dungeon_level: i32)
{
    let mut cafe_found = false;
    let mut exit_found = false;

    fn loot(pc : &mut Character, loot_lvl : i32, var : usize) {
        let item = generate_loot(loot_lvl, var);
        println!("Вы нашли: {}", item.name().blue());
        pc.give(item);
        if (get_random_number(100) as i32) < min(25, pc.level[1]) {
            // more dex -> more loot
            return loot(pc, loot_lvl, var);
        }
    };

    loop {
        println!("0) Исследовать этаж");
        if cafe_found {
            println!("1) Идти в столовую");
        }
        if exit_found {
            println!("2) Идти на следующий этаж");
        }
        match get_user_input(3) {
            0 => {
                match get_random_number(100) {
                    0..=5 => loot(pc, dungeon_level, 30), // rare loot 5%
                    5..=30 => { // enemy 25%

                        let npc = generate_character(get_random_number((dungeon_level + dungeon_level / 2) as usize),
                                                     dungeon_level,
                                                     dungeon_level);
                        println!("Пока вы ходили по этажу, вас заметил {}!", npc.name.red());
                        battle_scene(pc, npc);
                        loot(pc, dungeon_level, 20)
                    },
                    30..=45 if !cafe_found => { // 15%
                        println!("{}", "Вы нашли столовую".bold());
                        cafe_found = true;
                    },
                    45..=60 if !exit_found => { // 15%
                        println!("{}", "Вы нашли проход на следующий этаж, но он охраняется!".bold());
                        exit_found = true;
                    },
                    61 => { // 1%
                        let money = 1000 * dungeon_level;
                        println!("Вы нашли ${}!", money);
                        pc.money += money;
                    }
                    _ => loot(pc, dungeon_level, 10), // common loot,
                }
            },
            1 if cafe_found => cafe_scene(pc),
            2 if exit_found => {
                let npc = generate_character(get_random_number((dungeon_level + dungeon_level / 3) as usize),
                                             dungeon_level + 1,
                                             dungeon_level * 2);
                println!("{} охраняет выход и замечает вас", npc.name.red());
                battle_scene(pc, npc);
                return;
            },
            _ => println!("Неверный выбор"),
        }
    }
}


fn intro_scene() -> Character
{
    println!("");
    println!(" {}", "Вы просыпаетесь в плохо освещенной комнате от того, что кто-то слепит вас фонариком.".bold());
    println!(" {}", "Человек в полицейской форме протягивает вам стопку бумаг и ручку.".bold());
    println!(" {}", r#""Подписывай. Это твое чистоседечное признание.""#.bold().green());
    println!(" {}", r#""...?""#.bold());
    println!(" {}", r#""Там ты признаешься в том, что ты... а, впрочем, это не важно. На тебя повесили часть нераскрытых преступлений. Тебе лишь осталось вписать свое имя.""#.bold().green());
    println!(" {}", "Человек достает пистолет и целится вам в ногу.".bold());
    println!(" {}", r#""Не волнуйся, никто не придет тебя спасать. В этом здании нет ни одного честного человека. Теперь подписывай.""#.bold().green());
    println!("Введите {}:", "имя".red());

    let mut input_text = String::new();
    io::stdin().read_line(&mut input_text).expect("stdin read fail");
    let name = input_text.trim().to_string();

    let mut pc = Character {
        npc: false,
        max_hp: 30,
        hp: 30,
        name,
        money: 0,
        evasion: [0, 0, 0],
        crit_rate: 0,
        equip: vec![
            Rc::new(Weapon {
                level: 0,
                wtype: DamageType::Gun,
                u : Usable::new(15),

            }),
        ],
        ..Default::default()
    };

    println!(" {}", "Выбрав подходящий момент, вы выхватываете пистолет и целитесь в полицейского".bold());
    println!(" {}", r#""Ха-ха. Думаешь, ты сможешь отсюда выбраться? Я включил сигнализацию, все выходы и лифт заблокированы. Твой единственный шанс - это вертолет на крыше, но в этом здании 100 этажей и тебе до туда никогда не добраться. Сдавайся, если не хочешь умереть.""#.bold().green());

    let fool = Character {
        max_hp: 10,
        name: "самодовольный коп".to_string(),
        money: 300,
        evasion: [0,0,5],
        ..Default::default()
    };

    battle_scene(&mut pc, fool);

    pc
}

fn main() {
    println!("Включить цветной вывод? Требуется ANSI терминал. 0 - да(рекомендуется), 1 - нет");
    if get_user_input(2) == 0 {
        colored::control::set_virtual_terminal(true).unwrap();
        colored::control::set_override(true);
    } else {
        colored::control::set_override(false);
    }

    let mut pc = intro_scene();
    let mut dungeon_level = 1;
    loop {
        println!("Вы на {} этаже", dungeon_level.to_string().red());
        corridor_scene(&mut pc, dungeon_level);
        dungeon_level += 1;
        if dungeon_level == 101 {
            println!("Наконец-то вы добрались до крыши!");
            println!("Вы садитесь в вертолет и улетаете в закат!");
            println!("Игра окончена, вы победили!");
            break;
        }
    }
}

