use std::cmp::Ordering; use ::rltk::prelude::*; use ::specs::prelude::*; use super::{draw_tooltips, get_item_color, get_item_display_name}; use crate::components::{ Attribute, Attributes, Consumable, Duration, Equipped, HungerClock, HungerState, InBackpack, KnownSpells, Name, Pools, StatusEffect, Weapon, }; use crate::{colors, gamelog, Map}; fn draw_attribute(name: &str, attribute: &Attribute, y: i32, draw_batch: &mut DrawBatch) { draw_batch.print_color( Point::new(50, y), name, ColorPair::new(colors::ATTR_GRAY, colors::BLACK), ); let color = match attribute.modifiers.cmp(&0) { Ordering::Less => colors::RED, Ordering::Equal => colors::WHITE, Ordering::Greater => colors::GREEN, }; draw_batch.print_color( Point::new(67, y), &format!("{}", attribute.base + attribute.modifiers), ColorPair::new(color, colors::BLACK), ); draw_batch.print_color( Point::new(73, y), &format!("{}", attribute.bonus), ColorPair::new(color, colors::BLACK), ); if attribute.bonus > 0 { draw_batch.set( Point::new(72, y), ColorPair::new(color, colors::BLACK), rltk::to_cp437('+'), ); } } fn box_framework(draw_batch: &mut DrawBatch) { draw_batch.draw_hollow_box( Rect::with_size(0, 0, 79, 59), ColorPair::new(colors::BOX_GRAY, colors::BLACK), ); // Overall box draw_batch.draw_hollow_box( Rect::with_size(0, 0, 49, 45), ColorPair::new(colors::BOX_GRAY, colors::BLACK), ); // Map box draw_batch.draw_hollow_box( Rect::with_size(0, 45, 79, 14), ColorPair::new(colors::BOX_GRAY, colors::BLACK), ); // Log box draw_batch.draw_hollow_box( Rect::with_size(49, 0, 30, 8), ColorPair::new(colors::BOX_GRAY, colors::BLACK), ); // Top-right panel // Draw box connectors draw_batch.set( Point::new(0, 45), ColorPair::new(colors::BOX_GRAY, colors::BLACK), to_cp437('├'), ); draw_batch.set( Point::new(49, 8), ColorPair::new(colors::BOX_GRAY, colors::BLACK), to_cp437('├'), ); draw_batch.set( Point::new(49, 0), ColorPair::new(colors::BOX_GRAY, colors::BLACK), to_cp437('┬'), ); draw_batch.set( Point::new(49, 45), ColorPair::new(colors::BOX_GRAY, colors::BLACK), to_cp437('┴'), ); draw_batch.set( Point::new(79, 8), ColorPair::new(colors::BOX_GRAY, colors::BLACK), to_cp437('┤'), ); draw_batch.set( Point::new(79, 45), ColorPair::new(colors::BOX_GRAY, colors::BLACK), to_cp437('┤'), ); } pub fn map_label(ecs: &World, draw_batch: &mut DrawBatch) { let map = ecs.fetch::(); let name_length = map.name.len() + 2; let x_pos = (22 - (name_length / 2)) as i32; draw_batch.set( Point::new(x_pos, 0), ColorPair::new(colors::BOX_GRAY, colors::BLACK), to_cp437('┤'), ); draw_batch.set( Point::new(x_pos + name_length as i32 + 1, 0), ColorPair::new(colors::BOX_GRAY, colors::BLACK), to_cp437('├'), ); draw_batch.print_color( Point::new(x_pos + 1, 0), &format!(" {} ", map.name), ColorPair::new(colors::WHITE, colors::BLACK), ); } fn draw_stats(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) { let pools = ecs.read_storage::(); let player_pools = pools.get(*player_entity).unwrap(); let health = format!( "Health: {}/{}", player_pools.hit_points.current, player_pools.hit_points.max ); let mana = format!( "Mana: {}/{}", player_pools.mana.current, player_pools.mana.max ); let xp = format!("Level: {}", player_pools.level); draw_batch.print_color( Point::new(50, 1), &health, ColorPair::new(colors::WHITE, colors::BLACK), ); draw_batch.print_color( Point::new(50, 2), &mana, ColorPair::new(colors::WHITE, colors::BLACK), ); draw_batch.print_color( Point::new(50, 3), &xp, ColorPair::new(colors::WHITE, colors::BLACK), ); draw_batch.bar_horizontal( Point::new(64, 1), 14, player_pools.hit_points.current, player_pools.hit_points.max, ColorPair::new(colors::RED, colors::BLACK), ); draw_batch.bar_horizontal( Point::new(64, 2), 14, player_pools.mana.current, player_pools.mana.max, ColorPair::new(colors::BLUE, colors::BLACK), ); let xp_level_start = (player_pools.level - 1) * 1000; draw_batch.bar_horizontal( Point::new(64, 3), 14, player_pools.xp - xp_level_start, 1000, ColorPair::new(colors::GOLD, colors::BLACK), ); } fn draw_attributes(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) { let attributes = ecs.read_storage::(); let attr = attributes.get(*player_entity).unwrap(); draw_attribute("Might:", &attr.might, 4, draw_batch); draw_attribute("Quickness:", &attr.quickness, 5, draw_batch); draw_attribute("Fitness:", &attr.fitness, 6, draw_batch); draw_attribute("Intelligence:", &attr.intelligence, 7, draw_batch); } fn initiative_weight(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) { let attributes = ecs.read_storage::(); let attr = attributes.get(*player_entity).unwrap(); let pools = ecs.read_storage::(); let player_pools = pools.get(*player_entity).unwrap(); draw_batch.print_color( Point::new(50, 9), &format!( "{:.0} lbs ({} lbs max)", player_pools.total_weight, (attr.might.base + attr.might.modifiers) * 15 ), ColorPair::new(colors::WHITE, colors::BLACK), ); draw_batch.print_color( Point::new(50, 10), &format!( "Initiative Penalty: {:.0}", player_pools.total_initiative_penalty ), ColorPair::new(colors::WHITE, colors::BLACK), ); draw_batch.print_color( Point::new(50, 11), &format!("Gold: {:.1}", player_pools.gold), ColorPair::new(colors::GOLD, colors::BLACK), ); } fn equipped(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) -> i32 { let mut y = 13; let entities = ecs.entities(); let equipped = ecs.read_storage::(); let weapon = ecs.read_storage::(); for (entity, equipped_by) in (&entities, &equipped).join() { if equipped_by.owner == *player_entity { let name = get_item_display_name(ecs, entity); draw_batch.print_color( Point::new(50, y), &name, ColorPair::new(get_item_color(ecs, entity), colors::BLACK), ); y += 1; if let Some(weapon) = weapon.get(entity) { let mut weapon_info = match weapon.damage_bonus.cmp(&0) { Ordering::Less => { format!( "┤ {} ({}d{}{})", &name, weapon.damage_n_dice, weapon.damage_die_type, weapon.damage_bonus ) } Ordering::Equal => { format!( "┤ {} ({}d{})", &name, weapon.damage_n_dice, weapon.damage_die_type ) } Ordering::Greater => { format!( "┤ {} ({}d{}+{})", &name, weapon.damage_n_dice, weapon.damage_die_type, weapon.damage_bonus ) } }; if let Some(range) = weapon.range { weapon_info += &format!(" (range: {}, F to fire, V cycle targets)", range); } weapon_info += " ├"; draw_batch.print_color( Point::new(3, 45), &weapon_info, ColorPair::new(colors::YELLOW, colors::BLACK), ); } } } y } fn consumables(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity, mut y: i32) -> i32 { y += 1; let entities = ecs.entities(); let consumables = ecs.read_storage::(); let backpack = ecs.read_storage::(); let mut index = 1; for (entity, carried_by, _consumable) in (&entities, &backpack, &consumables).join() { if carried_by.owner == *player_entity && index < 10 { draw_batch.print_color( Point::new(50, y), &format!("↑{}", index), ColorPair::new(colors::YELLOW, colors::BLACK), ); draw_batch.print_color( Point::new(53, y), &get_item_display_name(ecs, entity), ColorPair::new(get_item_color(ecs, entity), colors::BLACK), ); y += 1; index += 1; } } y } fn spells(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity, mut y: i32) -> i32 { y += 1; let known_spells_storage = ecs.read_storage::(); let known_spells = &known_spells_storage.get(*player_entity).unwrap().spells; let mut index = 1; for spell in known_spells.iter() { draw_batch.print_color( Point::new(50, y), &format!("^{}", index), ColorPair::new(colors::CYAN, colors::BLACK), ); draw_batch.print_color( Point::new(53, y), &format!("{} ({})", spell.display_name, spell.mana_cost), ColorPair::new(colors::CYAN, colors::BLACK), ); index += 1; y += 1; } y } fn status(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) { let mut y = 44; let hunger = ecs.read_storage::(); let hc = hunger.get(*player_entity).unwrap(); match hc.state { HungerState::WellFed => { draw_batch.print_color( Point::new(50, y), "Well Fed", ColorPair::new(colors::GREEN, colors::BLACK), ); y -= 1; } HungerState::Normal => {} HungerState::Hungry => { draw_batch.print_color( Point::new(50, y), "Hungry", ColorPair::new(colors::ORANGE, colors::BLACK), ); y -= 1; } HungerState::Starving => { draw_batch.print_color( Point::new(50, y), "Starving", ColorPair::new(colors::RED, colors::BLACK), ); y -= 1; } } let statuses = ecs.read_storage::(); let durations = ecs.read_storage::(); let names = ecs.read_storage::(); for (status, duration, name) in (&statuses, &durations, &names).join() { if status.target == *player_entity { draw_batch.print_color( Point::new(50, y), &format!("{} ({})", name.name, duration.turns), ColorPair::new(colors::RED, colors::BLACK), ); y -= 1; } } } pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { let mut draw_batch = DrawBatch::new(); let player_entity = ecs.fetch::(); box_framework(&mut draw_batch); map_label(ecs, &mut draw_batch); draw_stats(ecs, &mut draw_batch, &player_entity); draw_attributes(ecs, &mut draw_batch, &player_entity); initiative_weight(ecs, &mut draw_batch, &player_entity); let mut y = equipped(ecs, &mut draw_batch, &player_entity); y += consumables(ecs, &mut draw_batch, &player_entity, y); spells(ecs, &mut draw_batch, &player_entity, y); status(ecs, &mut draw_batch, &player_entity); gamelog::print_log( &mut ::rltk::BACKEND_INTERNAL.lock().consoles[1].console, Point::new(1, 23), ); draw_tooltips(ecs, ctx); draw_batch .submit(5000) .expect("Failed to batch draw hud interface"); }