use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
use crate::coord::CoordGrid;
use crate::player::Player;
use crate::grid::ZoneMap;
use crate::npc::Npc;
#[derive(Eq, PartialEq, Clone)]
pub struct BuildArea {
pub players: Vec<i32>,
pub npcs: Vec<i32>,
appearances: [u32; 2048],
force_view_distance: bool,
pub view_distance: u8,
last_resize: u32,
}
impl BuildArea {
pub const INTERVAL: u8 = 10;
pub const PREFERRED_PLAYERS: u8 = 250;
pub const PREFERRED_NPCS: u8 = 255;
pub const PREFERRED_VIEW_DISTANCE: u8 = 15;
pub fn new() -> BuildArea {
return BuildArea {
players: Vec::with_capacity(BuildArea::PREFERRED_PLAYERS as usize),
npcs: Vec::with_capacity(BuildArea::PREFERRED_NPCS as usize),
appearances: [0; 2048],
force_view_distance: false,
view_distance: 15,
last_resize: 0,
}
}
pub fn cleanup(&mut self) {
self.players.clear();
self.npcs.clear();
self.appearances.fill(0);
}
#[inline]
pub fn resize(&mut self) {
if self.force_view_distance {
return;
}
if self.players.len() >= BuildArea::PREFERRED_PLAYERS as usize {
if self.view_distance > 1 {
self.view_distance -= 1;
}
self.last_resize = 0;
return;
}
self.last_resize += 1;
if self.last_resize >= BuildArea::INTERVAL as u32 {
if self.view_distance < BuildArea::PREFERRED_VIEW_DISTANCE {
self.view_distance += 1;
} else {
self.last_resize = 0;
}
}
}
#[inline]
pub fn rebuild_npcs(&mut self) {
self.npcs.clear();
}
#[inline]
pub fn rebuild_players(&mut self, players: &[Option<Player>], grid: &HashMap<u32, HashSet<i32>>, pid: i32, x: u16, y: u8, z: u16) {
self.players.clear();
self.last_resize = 0;
self.view_distance = BuildArea::PREFERRED_VIEW_DISTANCE;
let mut count: u8 = 0;
let mut decrement: bool = false;
for _ in self.get_nearby_players_nearest(players, grid, pid, x, y, z) {
count += 1;
if count >= BuildArea::PREFERRED_PLAYERS {
decrement = true;
break;
}
}
if decrement {
self.view_distance -= 1;
}
}
#[inline]
pub fn has_appearance(&self, pid: i32, tick: u32) -> bool {
return unsafe { *self.appearances.as_ptr().add(pid as usize) == tick }
}
#[inline]
pub fn save_appearance(&mut self, pid: i32, tick: u32) {
unsafe { *self.appearances.as_mut_ptr().add(pid as usize) = tick }
}
#[inline]
pub fn get_nearby_players(
&self,
players: &[Option<Player>],
grid: &HashMap<u32, HashSet<i32>>,
map: &mut ZoneMap,
pid: i32,
x: u16,
y: u8,
z: u16
) -> Vec<i32> {
return if self.view_distance < BuildArea::PREFERRED_VIEW_DISTANCE {
self.get_nearby_players_nearest(players, grid, pid, x, y, z)
} else {
self.get_nearby_players_zones(players, map, pid, x, y, z)
}
}
#[inline]
pub fn get_nearby_players_zones(
&self,
players: &[Option<Player>],
map: &mut ZoneMap,
pid: i32,
x: u16,
y: u8,
z: u16
) -> Vec<i32> {
let distance: u16 = self.view_distance as u16;
let start_x: u16 = (x.saturating_sub(distance)) >> 3;
let start_z: u16 = (z.saturating_sub(distance)) >> 3;
let end_x: u16 = (x.saturating_add(distance)) >> 3;
let end_z: u16 = (z.saturating_add(distance)) >> 3;
let count: usize = self.players.len();
let mut nearby: Vec<i32> = Vec::with_capacity(BuildArea::PREFERRED_PLAYERS as usize - count);
for zx in start_x..=end_x {
let zone_x: u16 = zx << 3;
for zz in start_z..=end_z {
if nearby.len() + count >= BuildArea::PREFERRED_PLAYERS as usize {
return nearby;
}
let zone_z: u16 = zz << 3;
nearby.extend(
map.zone(zone_x, y, zone_z).players
.iter()
.take(BuildArea::PREFERRED_PLAYERS as usize - nearby.len())
.filter(|&&player| self.filter_player(players, player, pid, x, y, z)),
);
}
}
return nearby
}
#[inline]
pub fn get_nearby_players_nearest(
&self,
players: &[Option<Player>],
grid: &HashMap<u32, HashSet<i32>>,
pid: i32,
x: u16,
y: u8,
z: u16
) -> Vec<i32> {
let radius: i32 = (self.view_distance as i32) * 2;
let min: i32 = -(radius >> 1);
let max: i32 = radius >> 1;
let length: i32 = radius.pow(2);
let (mut dx, mut dz): (i32, i32) = (0, 0);
let (mut ldx, mut ldz): (i32, i32) = (0, -1);
let count: usize = self.players.len();
let mut nearby: Vec<i32> = Vec::with_capacity(BuildArea::PREFERRED_PLAYERS as usize - count);
for _ in 1..=length {
if nearby.len() + count >= BuildArea::PREFERRED_PLAYERS as usize {
return nearby;
}
if (min < dx && dx <= max) && (min < dz && dz <= max) {
if let Some(set) = grid.get(&CoordGrid::from(((x as i32) + dx) as u16, y, ((z as i32) + dz) as u16).coord) {
nearby.extend(
set
.iter()
.take(BuildArea::PREFERRED_PLAYERS as usize - nearby.len())
.filter(|&&player| self.filter_player(players, player, pid, x, y, z)),
);
if nearby.len() + count >= BuildArea::PREFERRED_PLAYERS as usize {
return nearby;
}
}
}
if dx == dz || (dx < 0 && dx == -dz) || (dx > 0 && dx == 1 - dz) {
(ldx, ldz) = (-ldz, ldx);
}
dx += ldx;
dz += ldz;
}
return nearby
}
#[inline]
pub fn get_nearby_npcs(
&self,
tick: u32,
npcs: &[Option<Npc>],
map: &mut ZoneMap,
x: u16,
y: u8,
z: u16
) -> Vec<i32> {
let distance: u16 = BuildArea::PREFERRED_VIEW_DISTANCE as u16;
let start_x: u16 = (x.saturating_sub(distance)) >> 3;
let start_z: u16 = (z.saturating_sub(distance)) >> 3;
let end_x: u16 = (x.saturating_add(distance)) >> 3;
let end_z: u16 = (z.saturating_add(distance)) >> 3;
let count: usize = self.npcs.len();
let mut nearby: Vec<i32> = Vec::with_capacity(BuildArea::PREFERRED_NPCS as usize - count);
for zx in start_x..=end_x {
let zone_x: u16 = zx << 3;
for zz in start_z..=end_z {
if nearby.len() + count >= BuildArea::PREFERRED_NPCS as usize {
return nearby;
}
let zone_z: u16 = zz << 3;
nearby.extend(
map.zone(zone_x, y, zone_z).npcs
.iter()
.take(BuildArea::PREFERRED_NPCS as usize - nearby.len())
.filter(|&&npc| self.filter_npc(tick, npcs, npc, x, y, z)),
);
}
}
return nearby
}
#[inline]
fn filter_player(
&self,
players: &[Option<Player>],
player: i32,
pid: i32,
x: u16,
y: u8,
z: u16
) -> bool {
if let Some(other) = unsafe { &*players.as_ptr().add(player as usize) } {
return !(self.players.contains(&player) || !CoordGrid::within_distance_sw(&other.coord, &CoordGrid::from(x, y, z), self.view_distance) || other.pid == -1 || other.pid == pid || other.coord.y() != y);
}
return false;
}
#[inline]
fn filter_npc(
&self,
tick: u32,
npcs: &[Option<Npc>],
npc: i32,
x: u16,
y: u8,
z: u16
) -> bool {
if let Some(other) = unsafe { &*npcs.as_ptr().add(npc as usize) } {
return !(self.npcs.contains(&npc) || !CoordGrid::within_distance_sw(&other.coord, &CoordGrid::from(x, y, z), BuildArea::PREFERRED_VIEW_DISTANCE) || other.nid == -1 || other.coord.y() != y || !other.check_life_cycle(tick));
}
return false;
}
}
impl Hash for BuildArea {
fn hash<H: Hasher>(&self, state: &mut H) {
self.force_view_distance.hash(state);
self.view_distance.hash(state);
self.last_resize.hash(state);
}
}