Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
Download file

root / crates / common / src / stats.rs

use crate::item::{Item, ItemStack};
use crate::world::Block;

pub const ACHIEVEMENT: i32 = 5242880;
pub const MINE: i32 = 16777216;
pub const CRAFT: i32 = 16842752;
pub const USE: i32 = 16908288;
pub const BREAK: i32 = 16973824;

pub struct Achievement {
    pub name: &'static str,
    pub position: [i32; 2],
    pub icon: Item,
    pub parent: Option<usize>,
    pub special: bool,
}
macro_rules! achievements {
    ($($name:literal, $x:literal, $y:literal, $icon:expr, $parent:expr, $special:literal;)*) => {
        [$(Achievement {
            name: $name,
            position: [$x, $y],
            icon: $icon,
            parent: $parent,
            special: $special,
        }),*]
    };
}
pub const ACHIEVEMENTS: [Achievement; 16] = achievements![
    "openInventory", 0, 0, Item::BOOK, None, false;
    "mineWood", 2, 1, Item::from_block(Block::LOG), Some(0), false;
    "buildWorkBench", 4, -1, Item::from_block(Block::CRAFTING_TABLE), Some(1), false;
    "buildPickaxe", 4, 2, Item::WOODEN_PICKAXE, Some(2), false;
    "buildFurnace", 3, 4, Item::from_block(Block::LIT_FURNACE), Some(3), false;
    "acquireIron", 1, 4, Item::IRON_INGOT, Some(4), false;
    "buildHoe", 2, -3, Item::WOODEN_HOE, Some(2), false;
    "makeBread", -1, -3, Item::BREAD, Some(6), false;
    "bakeCake", 0, -5, Item::CAKE, Some(6), false;
    "buildBetterPickaxe", 6, 2, Item::STONE_PICKAXE, Some(3), false;
    "cookFish", 2, 6, Item::COOKED_FISH, Some(4), false;
    "onARail", 2, 3, Item::from_block(Block::RAIL), Some(5), true;
    "buildSword", 6, -1, Item::WOODEN_SWORD, Some(2), false;
    "killEnemy", 8, -1, Item::BONE, Some(12), false;
    "killCow", 7, -3, Item::LEATHER, Some(12), false;
    "flyPig", 8, -4, Item::SADDLE, Some(14), true;
];
pub const GENERAL: [(i32, &str); 23] = [
    (1000, "startGame"),
    (1001, "createWorld"),
    (1002, "loadWorld"),
    (1003, "joinMultiplayer"),
    (1004, "leaveGame"),
    (1100, "playOneMinute"),
    (2000, "walkOneCm"),
    (2001, "swimOneCm"),
    (2002, "fallOneCm"),
    (2003, "climbOneCm"),
    (2004, "flyOneCm"),
    (2005, "diveOneCm"),
    (2006, "minecartOneCm"),
    (2007, "boatOneCm"),
    (2008, "pigOneCm"),
    (2010, "jump"),
    (2011, "drop"),
    (2020, "damageDealt"),
    (2021, "damageTaken"),
    (2022, "deaths"),
    (2023, "mobKills"),
    (2024, "playerKills"),
    (2025, "fishCaught"),
];
pub fn independent(id: i32) -> bool {
    matches!(id,1000..=1004|1100|2000..=2008|2010|2011) || id == ACHIEVEMENT
}
pub fn block_id(block: Block) -> i32 {
    i32::from(
        match block {
            Block::WATER => Block::FLOWING_WATER,
            Block::JACK_O_LANTERN => Block::PUMPKIN,
            Block::LIT_FURNACE => Block::FURNACE,
            Block::LIT_REDSTONE_ORE => Block::REDSTONE_ORE,
            Block::POWERED_REPEATER => Block::REPEATER,
            Block::REDSTONE_TORCH => Block::UNLIT_REDSTONE_TORCH,
            Block::RED_MUSHROOM => Block::BROWN_MUSHROOM,
            Block::DOUBLE_SLAB => Block::SLAB,
            Block::GRASS | Block::FARMLAND => Block::DIRT,
            other => other,
        }
        .0,
    )
}
pub fn item_id(item: Item) -> i32 {
    item.block().map_or(i32::from(item.0), block_id)
}
pub fn crafted_achievement(stack: ItemStack) -> Option<usize> {
    match stack.item {
        Item::WOODEN_PICKAXE => Some(3),
        Item::WOODEN_HOE => Some(6),
        Item::BREAD => Some(7),
        Item::CAKE => Some(8),
        Item::STONE_PICKAXE | Item::IRON_PICKAXE | Item::DIAMOND_PICKAXE | Item::GOLDEN_PICKAXE => {
            Some(9)
        }
        Item::WOODEN_SWORD => Some(12),
        item if item == Item::from_block(Block::CRAFTING_TABLE) => Some(2),
        item if item == Item::from_block(Block::FURNACE) => Some(4),
        _ => None,
    }
}
/// Languages whose locale separates decimals by a comma.
const COMMA_LANGUAGES: [&str; 31] = [
    "bg", "ca", "cs", "da", "de", "el", "es", "et", "fi", "fr", "hr", "hu", "id", "it", "lt", "lv",
    "nb", "nl", "nn", "no", "pl", "pt", "ro", "ru", "sk", "sl", "sr", "sv", "tr", "uk", "vi",
];

/// `DecimalFormat` symbols of the platform locale: whether it separates decimals by a comma.
fn decimal_comma() -> bool {
    static COMMA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
    *COMMA.get_or_init(|| {
        let locale = ["LC_ALL", "LC_NUMERIC", "LANG"]
            .into_iter()
            .filter_map(|key| std::env::var(key).ok())
            .find(|value| !value.is_empty())
            .unwrap_or_default();
        let language = locale
            .split(['_', '.', '@', '-'])
            .next()
            .unwrap_or_default();
        COMMA_LANGUAGES.contains(&language)
    })
}

/// `new DecimalFormat("########0.00")` in the platform locale.
fn decimal(value: f64) -> String {
    let text = format!("{value:.2}");
    if decimal_comma() {
        text.replace('.', ",")
    } else {
        text
    }
}

pub fn format(id: i32, value: i32) -> String {
    if id == 1100 {
        let seconds = f64::from(value) / 20.0;
        for (divisor, unit) in [
            (31536000.0, "y"),
            (86400.0, "d"),
            (3600.0, "h"),
            (60.0, "m"),
        ] {
            if seconds / divisor > 0.5 {
                return format!("{} {unit}", decimal(seconds / divisor));
            }
        }
        return format!("{seconds:?} s");
    }
    if (2000..=2008).contains(&id) {
        if value > 50000 {
            return format!("{} km", decimal(f64::from(value) / 100000.0));
        }
        if value > 50 {
            return format!("{} m", decimal(f64::from(value) / 100.0));
        }
        return format!("{value} cm");
    }
    let digits = value.to_string();
    let mut grouped = String::new();
    for (index, ch) in digits.chars().enumerate() {
        if index > 0 && ch.is_ascii_digit() && (digits.len() - index).is_multiple_of(3) {
            grouped.push(',');
        }
        grouped.push(ch);
    }
    grouped
}