use std::{cell::UnsafeCell, ops::RangeInclusive};
use azalea_block::{
BlockState, BlockStates, block_state::BlockStateIntegerRepr, properties::Waterlogged,
};
use azalea_inventory::Menu;
use azalea_registry::builtin::BlockKind;
use nohash_hasher::IntMap;
use super::costs::BLOCK_BREAK_ADDITIONAL_PENALTY;
use crate::auto_tool::best_tool_in_hotbar_for_block;
pub struct MiningCache {
block_state_id_costs: UnsafeCell<IntMap<BlockStateIntegerRepr, f32>>,
inventory_menu: Option<Menu>,
water_block_state_range: RangeInclusive<BlockStateIntegerRepr>,
lava_block_state_range: RangeInclusive<BlockStateIntegerRepr>,
falling_blocks: Vec<BlockState>,
}
impl MiningCache {
pub fn new(inventory_menu: Option<Menu>) -> Self {
let water_block_states = BlockStates::from(BlockKind::Water);
let lava_block_states = BlockStates::from(BlockKind::Lava);
let mut water_block_state_range_min = BlockStateIntegerRepr::MAX;
let mut water_block_state_range_max = BlockStateIntegerRepr::MIN;
for state in water_block_states {
water_block_state_range_min = water_block_state_range_min.min(state.id());
water_block_state_range_max = water_block_state_range_max.max(state.id());
}
let water_block_state_range = water_block_state_range_min..=water_block_state_range_max;
let mut lava_block_state_range_min = BlockStateIntegerRepr::MAX;
let mut lava_block_state_range_max = BlockStateIntegerRepr::MIN;
for state in lava_block_states {
lava_block_state_range_min = lava_block_state_range_min.min(state.id());
lava_block_state_range_max = lava_block_state_range_max.max(state.id());
}
let lava_block_state_range = lava_block_state_range_min..=lava_block_state_range_max;
let mut falling_blocks: Vec<BlockState> = vec![
BlockKind::Sand.into(),
BlockKind::RedSand.into(),
BlockKind::Gravel.into(),
BlockKind::Anvil.into(),
BlockKind::ChippedAnvil.into(),
BlockKind::DamagedAnvil.into(),
BlockKind::WhiteConcretePowder.into(),
BlockKind::OrangeConcretePowder.into(),
BlockKind::MagentaConcretePowder.into(),
BlockKind::LightBlueConcretePowder.into(),
BlockKind::YellowConcretePowder.into(),
BlockKind::LimeConcretePowder.into(),
BlockKind::PinkConcretePowder.into(),
BlockKind::GrayConcretePowder.into(),
BlockKind::LightGrayConcretePowder.into(),
BlockKind::CyanConcretePowder.into(),
BlockKind::PurpleConcretePowder.into(),
BlockKind::BlueConcretePowder.into(),
BlockKind::BrownConcretePowder.into(),
BlockKind::GreenConcretePowder.into(),
BlockKind::RedConcretePowder.into(),
BlockKind::BlackConcretePowder.into(),
];
falling_blocks.sort_unstable_by_key(|block| block.id());
Self {
block_state_id_costs: UnsafeCell::new(IntMap::default()),
inventory_menu,
water_block_state_range,
lava_block_state_range,
falling_blocks,
}
}
pub fn cost_for(&self, block: BlockState) -> f32 {
let Some(inventory_menu) = &self.inventory_menu else {
return f32::INFINITY;
};
let block_state_id_costs = unsafe { &mut *self.block_state_id_costs.get() };
if let Some(cost) = block_state_id_costs.get(&block.id()) {
*cost
} else {
let best_tool_result = best_tool_in_hotbar_for_block(block, inventory_menu);
let mut cost = 1. / best_tool_result.percentage_per_tick;
cost += BLOCK_BREAK_ADDITIONAL_PENALTY;
block_state_id_costs.insert(block.id(), cost);
cost
}
}
pub fn is_liquid(&self, block: BlockState) -> bool {
self.water_block_state_range.contains(&block.id())
|| self.lava_block_state_range.contains(&block.id())
|| is_waterlogged(block)
}
pub fn is_falling_block(&self, block: BlockState) -> bool {
self.falling_blocks
.binary_search_by_key(&block.id(), |block| block.id())
.is_ok()
}
}
pub fn is_waterlogged(block: BlockState) -> bool {
block.property::<Waterlogged>().unwrap_or_default()
}