func NpcNearest(id, x, y int) *NPC {
	point := NewLocation(x, y)
	minDelta := 16
	var npc *NPC
	regionLock.RLock()
	defer regionLock.RUnlock()
	for x := 0; x < MaxX; x += RegionSize {
		for y := 0; y < MaxY; y += RegionSize {
			Region(x, y).NPCs.RangeNpcs(func(n *NPC) bool {
				if n.ID == id && n.LongestDelta(point) < minDelta {
					minDelta = n.LongestDelta(point)
					npc = n
				}
				return false
			})
		}
	}
	return npc
}
NpcNearest looks for the NPC with the given ID, that is the closest to the given coordinates
and then returns it.
Returns nil if it can not find an NPC to fit the given description.
package world
import (
	"math"
	"sync"
	"time"
	"github.com/spkaeros/rscgo/pkg/isaac"
	"github.com/spkaeros/rscgo/pkg/tasks"
	"github.com/spkaeros/rscgo/pkg/game/entity"
	"github.com/spkaeros/rscgo/pkg/log"
	rscRand "github.com/spkaeros/rscgo/pkg/rand"
)
const (
	TicksDay         = 135000
	TicksHour        = 5625
	TicksTwentyMin   = 1875
	TicksMinute      = 100
	TickMinute       = TicksMinute * TickMillis
	TickHour         = TicksHour * TickMillis
	TickDay          = TicksDay * TickMillis
	TickMillis       = 640 * time.Millisecond
	ClientTickMillis = TickMillis >> 5
	MaxX = 944
	MaxY = 944 * 4  
)
func CurrentTick() int {
	return int(tasks.Ticks.Load())
}
const (
	RegionSize = 48
	HorizontalPlanes = MaxX/RegionSize + 1
	VerticalPlanes = MaxY/RegionSize + 1
	LowerBound = RegionSize / 2
)
var UpdateTime time.Time
type indexQueue []int
func (q *indexQueue) Push(i int) {
	*q = append([]int{i}, *q...)
}
func (q *indexQueue) Pop() int {
	if len(*q) <= 0 {
		return -1
	}
	idx := (*q)[0]
	if len(*q) == 1 {
		*q = (*q)[:0]
	} else {
		*q = (*q)[1:]
	}
	return idx
}
type PlayersList map[*Player]struct{}
type PlayerList struct {
	sync.RWMutex
	PlayersList
	free   indexQueue
	curIdx int
}
var Players = &PlayerList{free: make(indexQueue, 0, 1250), PlayersList: make(PlayersList)}
func (m *PlayerList) FindHash(hash uint64) (*Player, bool) {
	m.RLock()
	defer m.RUnlock()
	for player := range m.PlayersList {
		if player.UsernameHash() == hash {
			return player, true
		}
	}
	return nil, false
}
func (m *PlayerList) FindIndex(index int) (*Player, bool) {
	if index < 0 {
		return nil, false
	}
	m.RLock()
	defer m.RUnlock()
	for player := range m.PlayersList {
		if player.ServerIndex() == index {
			return player, true
		}
	}
	return nil, false
}
func (m *PlayerList) Find(player *Player) int {
	return m.ForEach(func(p *Player) bool {
		return p == player
	})
}
func (m *PlayerList) Contains(player *Player) bool {
	return m.Find(player) > -1
}
func (m *PlayerList) ContainsHash(hash uint64) bool {
	_, ret := m.FindHash(hash)
	return ret
}
func (m *PlayerList) Put(player *Player) {
	if m.Contains(player) {
		log.Warn("Player list double-entry attempted!")
		return
	}
	player.SetServerIndex(m.nextSlot())
	m.Lock()
	defer m.Unlock()
	m.PlayersList[player] = struct{}{} 
}
func (m *PlayerList) Remove(player *Player) {
	slot := player.ServerIndex()
	if slot == -1 {
		log.Warn("Error: Player with non-valid index being removed!")
		return
	}
	player.SetServerIndex(-1)
	m.Lock()
	defer m.Unlock()
	delete(m.PlayersList, player)
	m.free.Push(slot)
}
func (m *PlayerList) Range(action func(*Player)) {
	m.RLock()
	defer m.RUnlock()
	for p := range m.PlayersList {
		if p != nil {
			action(p)
		}
	}
}
func (m *PlayerList) ForEach(action func(*Player) bool) int {
	m.RLock()
	defer m.RUnlock()
	for p := range m.PlayersList {
		if p != nil {
			if action(p) {
				return p.ServerIndex()
			}
		}
	}
	return -1
}
func (m *PlayerList) Size() int {
	m.RLock()
	defer m.RUnlock()
	return len(m.PlayersList)
}
func (m *PlayerList) nextSlot() int {
	m.Lock()
	defer m.Unlock()
	idx := m.free.Pop()
	if idx == -1 {
		defer func() { m.curIdx += 1 }()
		return m.curIdx
	}
	return idx
}
func (m *PlayerList) Set() []*Player {
	keys := make([]*Player, m.Size())
	i := 0
	for k := range m.PlayersList {
	    keys[i] = k
	    i++
	}
	return keys
}
func (m *PlayerList) AsyncRange(fn func(*Player)) {
	m.RLock()
	sz := len(m.PlayersList)
	done := make(chan struct{}, sz)
	for player := range m.PlayersList {
		go func(player *Player) {
			fn(player)
			done <- struct{}{}
		}(player)
	}
	m.RUnlock()
	defer close(done)
	for i := 0; i < sz; i += 1 {
		select {
		case _, ok := <-done:
			if !ok {
				log.Debug("premature AsyncRange done-signal")
				break
			}
			continue
		}
	}
}
type region struct {
	x       int
	y       int
	Players *MobList
	NPCs    *MobList
	Objects *entityList
	Items   *entityList
}
var regions [HorizontalPlanes][VerticalPlanes]*region
func WithinWorld(x, y int) bool {
	return x <= MaxX && x >= 0 && y >= 0 && y <= MaxY
}
func AddPlayer(p *Player) {
	Players.Put(p)
	Region(p.X(), p.Y()).Players.Add(p)
}
func RemovePlayer(p *Player) {
	p.SetRegionRemoved()
	Region(p.X(), p.Y()).Players.Remove(p)
	Players.Remove(p)
}
func AddNpc(n *NPC) {
	Region(n.X(), n.Y()).NPCs.Add(n)
}
func RemoveNpc(n *NPC) {
	Region(n.X(), n.Y()).NPCs.Remove(n)
}
func AddItem(i *GroundItem) {
	Region(i.X(), i.Y()).Items.Add(i)
}
func GetItem(x, y, id int) *GroundItem {
	region := Region(x, y)
	region.Items.RLock()
	defer region.Items.RUnlock()
	for _, i := range region.Items.set {
		if i, ok := i.(*GroundItem); ok {
			if i.ID == id && i.X() == x && i.Y() == y {
				return i
			}
		}
	}
	return nil
}
func RemoveItem(i *GroundItem) {
	Region(i.X(), i.Y()).Items.Remove(i)
}
type mapBarrier interface {
	Solid() bool
	Door() bool
	Passable() bool
	Width() int
	Height() int
	Defined() bool
}
func AddObject(o *Object) {
	Region(o.X(), o.Y()).Objects.Add(o)
	data := o.TypeData()
	if !data.Defined() {
		return
	}
	if data.Passable() {
		return
	}
	if o.Boundary {
		x,y := o.X(),o.Y()
		areaX := (2304+x) % RegionSize
		areaY := (1776+y - (944*((y+100)/944))) % RegionSize
		switch o.Direction {
		case 0:
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipNorth
			if areaX+areaY > 0 {
				sectorFromCoords(x, y-1).Tiles[areaX*RegionSize+areaY-1] |= ClipSouth
			}
		case 1:
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipEast
			if areaX > 0 || areaY >= 48 {
				sectorFromCoords(x-1, y).Tiles[(areaX-1)*RegionSize+areaY] |= ClipWest
			}
		case 2:
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipSwNe
		case 3:
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipSeNw
		}
		return
	}
	width, height := data.Width(), data.Height()
	if o.Direction&3 != 0 {
		width, height = height, width
	}
	for dx := 0; dx < width; dx++ {
		for dy := 0; dy < height; dy++ {
			x,y := o.X()+dx,o.Y()+dy
			areaX := (2304+x) % RegionSize
			areaY := (1776+y - (944*((y+100)/944))) % RegionSize
			if len(sectorFromCoords(x, y).Tiles) <= 0 {
				return
			}
			if data.Solid() {
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipFullBlock
				continue
			}
			switch int(o.Direction) {
			case 0:  
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipEast
				if len(sectorFromCoords(x-1, y).Tiles) > 0 && (areaX > 0 || areaY >= RegionSize) {
					sectorFromCoords(x-1, y).Tiles[(areaX-1)*RegionSize+areaY] |= ClipWest
				}
			case 2:  
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipSouth
				sectorFromCoords(x, y+1).Tiles[areaX*RegionSize+areaY+1] |= ClipNorth
			case 4:  
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipWest
				if areaX, areaY := (2304+x+1)%RegionSize, (1776+y-(944*((y+100)/944)))%RegionSize; (areaX+1)*RegionSize+areaY > 2304 {
					sectorFromCoords(x+1, y).Tiles[areaX*RegionSize+areaY] |= ClipEast
				}
			case 6:  
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipNorth
				if len(sectorFromCoords(x, y-1).Tiles) > 0 && areaX+areaY > 0 {
					sectorFromCoords(x, y-1).Tiles[areaX*RegionSize+areaY-1] |= ClipSouth
				}
			}
		}
	}
}
func RemoveObject(o *Object) {
	Region(o.X(), o.Y()).Objects.Remove(o)
	data := o.TypeData()
	if data.Passable() {
		return
	}
	if !o.Boundary {
		width, height := data.Width(), data.Height()
		if o.Direction&3 != 0 {
			width,height = height,width
		}
		for dx := 0; dx < width; dx++ {
			for dy := 0; dy < height; dy++ {
				x,y := o.X()+dx,o.Y()+dy
				areaX := (2304 + x) % RegionSize
				areaY := (1776 + y - (944 * ((y + 100) / 944))) % RegionSize
				if data.Solid() {
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipFullBlock
					continue
				}
				switch o.Direction {
				case 0:
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipEast
					if sectorFromCoords(x-1, y) != nil {
						sectorFromCoords(x-1, y).Tiles[(areaX-1)*RegionSize+areaY] &= ^ClipWest
					}
				case 2:
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipSouth
					if sectorFromCoords(x, y+1) != nil {
						sectorFromCoords(x, y+1).Tiles[areaX*RegionSize+areaY+1] &= ^ClipNorth
					}
				case 4:
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipWest
					if sectorFromCoords(x+1, y) != nil {
						sectorFromCoords(x+1, y).Tiles[(areaX+1)*RegionSize+areaY] &= ^ClipEast
					}
				case 6:
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipNorth
					if sectorFromCoords(x, y-1) != nil {
						sectorFromCoords(x, y-1).Tiles[areaX*RegionSize+areaY-1] &= ^ClipSouth
					}
				}
			}
		}
	} else {
		x, y := o.X(), o.Y()
		areaX := (2304 + x) % RegionSize
		areaY := (1776 + y - (944 * ((y + 100) / 944))) % RegionSize
		if o.Direction == 0 {  
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipNorth
			if areaX+areaY > 0 {
				sectorFromCoords(x, y-1).Tiles[areaX*RegionSize+areaY-1] &= ^ClipSouth
			}
		} else if o.Direction == 1 {  
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipEast
			if areaX > 0 || areaY >= 48 {
				sectorFromCoords(x-1, y).Tiles[(areaX-1)*RegionSize+areaY] &= ^ClipWest
			}
		} else if o.Direction == 2 {  
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipSwNe
		} else if o.Direction == 3 {  
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipSeNw
		}
	}
}
func ReplaceObject(old *Object, newID int) *Object {
	RemoveObject(old)
	object := NewObject(newID, int(old.Direction), old.X(), old.Y(), old.Boundary)
	AddObject(object)
	return object
}
func GetAllObjects() (list []entity.Entity) {
	regionLock.RLock()
	defer regionLock.RUnlock()
	for _, xR := range regions {
		for _, yR := range xR {
			if yR != nil {
				yR.Objects.Range(func(e entity.Entity) {
					list = append(list, e)
				})
			}
		}
	}
	return list
}
func GetObject(x, y int) *Object {
	r := Region(x, y)
	r.Objects.RLock()
	defer r.Objects.RUnlock()
	for _, o := range r.Objects.set {
		if o, ok := o.(*Object); ok {
			if o.X() == x && o.Y() == y {
				return o
			}
		}
	}
	return nil
}
func GetNpc(index int) *NPC {
	m := Npcs.Get(index)
	if m == nil {
		return nil
	}
	return m.(*NPC)
}