parent
221d497204
commit
9874e23aa6
3 changed files with 316 additions and 307 deletions
@ -0,0 +1,314 @@ |
|||||||
|
import re |
||||||
|
import time |
||||||
|
import random |
||||||
|
from math import hypot |
||||||
|
from itertools import count |
||||||
|
from copy import copy |
||||||
|
|
||||||
|
from mosfet import utils |
||||||
|
from mosfet import path |
||||||
|
from mosfet import blocks |
||||||
|
from mosfet import mobs |
||||||
|
|
||||||
|
class World: |
||||||
|
def __init__(self, global_state): |
||||||
|
self.g = global_state |
||||||
|
|
||||||
|
def block_at(self, x, y, z): |
||||||
|
return self.g.chunks.get_block_at(x, y, z) |
||||||
|
|
||||||
|
def check_air_column(self, pos, distance): |
||||||
|
for i in range(distance): |
||||||
|
check = utils.padd(pos, (0, i, 0)) |
||||||
|
if self.block_at(*check) not in blocks.NON_SOLID_IDS: |
||||||
|
return False |
||||||
|
return True |
||||||
|
|
||||||
|
def find_blocks_3d(self, center, block_ids, distance=0, y_limit=0): |
||||||
|
for offset in utils.search_3d(distance, y_limit): |
||||||
|
check = utils.padd(center, offset) |
||||||
|
if self.block_at(*check) in block_ids: |
||||||
|
yield check |
||||||
|
|
||||||
|
def find_blocks_indexed(self, center, block_ids, distance=0): |
||||||
|
print('finding', block_ids) |
||||||
|
index = [] |
||||||
|
for bid in block_ids: |
||||||
|
index.extend(self.g.chunks.index.get(bid, [])) |
||||||
|
|
||||||
|
print('index', index) |
||||||
|
|
||||||
|
result = [] |
||||||
|
for block in index: |
||||||
|
if self.block_at(*block) not in block_ids: |
||||||
|
continue |
||||||
|
if distance and utils.phyp(center, block) > distance: |
||||||
|
continue |
||||||
|
if block not in result: |
||||||
|
result.append(block) |
||||||
|
|
||||||
|
result.sort(key=lambda x: utils.phyp(center, x)) |
||||||
|
return result |
||||||
|
|
||||||
|
def find_blocks(self, center, distance, block_ids, limit=0): |
||||||
|
# search in a spiral from center to all blocks with ID |
||||||
|
result = [] |
||||||
|
for n in count(): |
||||||
|
offset = utils.spiral(n) |
||||||
|
check = utils.padd(center, offset) |
||||||
|
if self.block_at(*check) in block_ids: |
||||||
|
if hypot(*offset) < distance: |
||||||
|
result.append(check) |
||||||
|
if limit and len(result) == limit: |
||||||
|
return result |
||||||
|
if offset[0] > distance: |
||||||
|
return result |
||||||
|
|
||||||
|
def find_trees(self, center, distance): |
||||||
|
found_trees = [] |
||||||
|
for log in self.find_blocks_3d(center, blocks.LOG_IDS, distance, 15): |
||||||
|
# crawl to the bottom log |
||||||
|
while self.block_at(*utils.padd(log, path.BLOCK_BELOW)) in blocks.LOG_IDS: |
||||||
|
log = utils.padd(log, path.BLOCK_BELOW) |
||||||
|
base = log |
||||||
|
|
||||||
|
if base in found_trees: |
||||||
|
continue |
||||||
|
|
||||||
|
# make sure we are on the ground |
||||||
|
if self.block_at(*utils.padd(base, path.BLOCK_BELOW)) in blocks.NON_SOLID_IDS: |
||||||
|
continue |
||||||
|
|
||||||
|
# crawl to the top log to count and check leaves |
||||||
|
log_count = 1 |
||||||
|
good_leaves = False |
||||||
|
while self.block_at(*utils.padd(log, path.BLOCK_ABOVE)) in blocks.LOG_IDS: |
||||||
|
log = utils.padd(log, path.BLOCK_ABOVE) |
||||||
|
log_count += 1 |
||||||
|
|
||||||
|
for offset in path.CHECK_DIRECTIONS: |
||||||
|
if self.block_at(*utils.padd(log, offset)) in blocks.LEAF_IDS: |
||||||
|
good_leaves = True |
||||||
|
|
||||||
|
# make sure it's a good tree |
||||||
|
if not good_leaves or log_count < 3: |
||||||
|
continue |
||||||
|
|
||||||
|
found_trees.append(base) |
||||||
|
|
||||||
|
yield base |
||||||
|
|
||||||
|
def find_tree_openings(self, tree): |
||||||
|
# returns coords in a cardinal direction where we can stand by tree |
||||||
|
maze_solver = path.Pathfinder(self.g) |
||||||
|
result = [] |
||||||
|
|
||||||
|
# TODO: make sure only non-solid and leaves between |
||||||
|
# make sure traversable too and non-avoid |
||||||
|
|
||||||
|
for distance in range(5): |
||||||
|
for direction in path.CHECK_DIRECTIONS: |
||||||
|
offset = utils.pmul(direction, distance+1) |
||||||
|
if maze_solver.check_traverse(tree, offset): |
||||||
|
result.append(utils.padd(tree, offset)) |
||||||
|
return result |
||||||
|
|
||||||
|
def path_to_place(self, start, place): |
||||||
|
maze_solver = path.Pathfinder(self.g) |
||||||
|
|
||||||
|
try: |
||||||
|
s = maze_solver.astar(start, place) |
||||||
|
return list(s) if s else None |
||||||
|
except path.AStarTimeout: |
||||||
|
return None |
||||||
|
|
||||||
|
def find_bed_areas(self, center, distance): |
||||||
|
bed_clearance = 9 # 5x5 area |
||||||
|
clear_distance = 2 |
||||||
|
|
||||||
|
for a in self.find_blocks_3d(center, [0], distance, 50): |
||||||
|
# check for air around the area |
||||||
|
if len(self.find_blocks(a, clear_distance, [0], bed_clearance)) < bed_clearance: |
||||||
|
continue |
||||||
|
|
||||||
|
# check for ground around the area |
||||||
|
if len(self.find_blocks(utils.padd(a, path.BLOCK_BELOW), clear_distance, blocks.NON_SOLID_IDS, bed_clearance)): |
||||||
|
continue |
||||||
|
|
||||||
|
# check for air above the area |
||||||
|
if len(self.find_blocks(utils.padd(a, path.BLOCK_ABOVE), clear_distance, [0], bed_clearance)) < bed_clearance: |
||||||
|
continue |
||||||
|
|
||||||
|
# ensure there's no monsters within 20 blocks |
||||||
|
# can't sleep if they are within 10, good to have a buffer |
||||||
|
if self.find_monsters(a, 20): |
||||||
|
continue |
||||||
|
|
||||||
|
yield a |
||||||
|
|
||||||
|
def find_cache_areas(self, center, distance): |
||||||
|
return self.find_bed_areas(center, distance) |
||||||
|
|
||||||
|
def sand_adjacent_safe(self, sand): |
||||||
|
for direction in path.CHECK_DIRECTIONS: |
||||||
|
if self.block_at(*utils.padd(sand, direction)) in blocks.AVOID_IDS: |
||||||
|
return False |
||||||
|
return True |
||||||
|
|
||||||
|
def find_sand(self, center, distance, player): |
||||||
|
sand = [] |
||||||
|
sand.extend(self.find_blocks(center, distance, [blocks.SAND], 25)) |
||||||
|
|
||||||
|
safe_sand = [] |
||||||
|
for s in sand: |
||||||
|
# make sure it has solid below |
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_BELOW)) in blocks.NON_SOLID_IDS: |
||||||
|
continue |
||||||
|
# make sure it has solid two below - prevent hanging sand |
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_BELOW2)) in blocks.NON_SOLID_IDS: |
||||||
|
continue |
||||||
|
|
||||||
|
# and walkable air above |
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_ABOVE)) not in blocks.NON_SOLID_IDS: |
||||||
|
continue |
||||||
|
|
||||||
|
if not self.sand_adjacent_safe(s): |
||||||
|
continue |
||||||
|
|
||||||
|
safe_sand.append(s) |
||||||
|
|
||||||
|
safe_sand.sort(key=lambda x: utils.phyp(player, x)) |
||||||
|
return safe_sand |
||||||
|
|
||||||
|
def check_sand_slice(self, center): |
||||||
|
# checks if a 5x5x1 slice has sand in it |
||||||
|
for i in range(9): |
||||||
|
s = utils.padd(center, utils.spiral(i)) |
||||||
|
if self.block_at(*s) != blocks.SAND: |
||||||
|
continue |
||||||
|
# make sure it has solid below |
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_BELOW)) in blocks.NON_SOLID_IDS: |
||||||
|
continue |
||||||
|
# make sure it has solid two below - prevent hanging sand |
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_BELOW2)) in blocks.NON_SOLID_IDS: |
||||||
|
continue |
||||||
|
# and walkable air above |
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_ABOVE)) not in blocks.NON_SOLID_IDS: |
||||||
|
continue |
||||||
|
if not self.sand_adjacent_safe(s): |
||||||
|
continue |
||||||
|
return True |
||||||
|
|
||||||
|
return False |
||||||
|
|
||||||
|
def find_sand_slice(self, center, distance, y_limit=0, bad_slices=[], prev_layer=0): |
||||||
|
# returns the centre coord of the next 5x5x1 slice that still has |
||||||
|
# diggable sand in it. lower slices are only valid if there's an |
||||||
|
# adjacent slice farther at the same level. this should ensure an |
||||||
|
# upside down pyramid gets excavated so the edges are still climbable |
||||||
|
for v in count(prev_layer): |
||||||
|
peak = utils.padd(center, (0, 10-v, 0)) |
||||||
|
|
||||||
|
slices = [] |
||||||
|
layer = 0 |
||||||
|
for step in count(): |
||||||
|
offset = utils.spiral(step) |
||||||
|
layer = max(layer, *offset) |
||||||
|
offset = utils.pmul(offset, 3) |
||||||
|
check = utils.padd(peak, offset) |
||||||
|
check = utils.padd(check, (0, layer, 0)) |
||||||
|
|
||||||
|
if y_limit and check[1] - center[1] > y_limit: |
||||||
|
break |
||||||
|
if utils.phyp_king(center, check) > distance: |
||||||
|
break |
||||||
|
|
||||||
|
if self.check_sand_slice(check) and check not in bad_slices: |
||||||
|
slices.append(check) |
||||||
|
|
||||||
|
if len(slices): |
||||||
|
return v, slices[-1] |
||||||
|
elif v > 40: |
||||||
|
return None, None |
||||||
|
|
||||||
|
|
||||||
|
def find_bed_openings(self, area): |
||||||
|
# returns coords in a cardinal direction where we can stand by bed |
||||||
|
result = [] |
||||||
|
|
||||||
|
for direction in path.CHECK_DIRECTIONS: |
||||||
|
result.append(utils.padd(area, direction)) |
||||||
|
return result |
||||||
|
|
||||||
|
def find_cache_openings(self, area): |
||||||
|
return self.find_bed_openings(area) |
||||||
|
|
||||||
|
def find_objects(self, object_ids): |
||||||
|
result = [] |
||||||
|
for eid, obj in copy(self.g.objects).items(): |
||||||
|
if obj.get('item_id', None) in object_ids: |
||||||
|
result.append(obj) |
||||||
|
return result |
||||||
|
|
||||||
|
def find_leaves(self, center, distance): |
||||||
|
for a in self.find_blocks_3d(center, blocks.LEAF_IDS, distance, 10): |
||||||
|
yield a |
||||||
|
|
||||||
|
def find_monsters(self, center, distance): |
||||||
|
# finds monsters within distance |
||||||
|
result = [] |
||||||
|
for eid, mob in copy(self.g.mobs).items(): |
||||||
|
if mob.type not in mobs.EVIL_IDS: |
||||||
|
continue |
||||||
|
pos = utils.pint((mob.x, mob.y, mob.z)) |
||||||
|
if utils.phyp(center, pos) > distance: |
||||||
|
continue |
||||||
|
result.append(mob) |
||||||
|
return result |
||||||
|
|
||||||
|
def find_threats(self, center, distance): |
||||||
|
# finds monsters on the surface within distance |
||||||
|
monsters = self.find_monsters(center, distance) |
||||||
|
result = [] |
||||||
|
for mob in monsters: |
||||||
|
pos = utils.pint((mob.x, mob.y, mob.z)) |
||||||
|
# check distance number of blocks above, close enough? |
||||||
|
if not self.check_air_column(pos, distance): |
||||||
|
continue |
||||||
|
result.append(mob) |
||||||
|
return result |
||||||
|
|
||||||
|
def find_villagers(self, center, distance): |
||||||
|
# finds villagers within distance |
||||||
|
result = [] |
||||||
|
for eid, mob in copy(self.g.mobs).items(): |
||||||
|
type_name = mobs.MOB_NAMES[mob.type] |
||||||
|
if type_name != 'villager' : continue |
||||||
|
pos = utils.pint((mob.x, mob.y, mob.z)) |
||||||
|
if utils.phyp(center, pos) > distance: |
||||||
|
continue |
||||||
|
result.append(mob) |
||||||
|
return result |
||||||
|
|
||||||
|
def find_villager_openings(self, villager): |
||||||
|
# returns coords in a cardinal direction where we can stand by a villager |
||||||
|
maze_solver = path.Pathfinder(self.g) |
||||||
|
result = [] |
||||||
|
|
||||||
|
for distance in range(3): |
||||||
|
for direction in path.CHECK_DIRECTIONS: |
||||||
|
offset = utils.pmul(direction, distance+1) |
||||||
|
|
||||||
|
if not maze_solver.check_traverse(villager, offset): |
||||||
|
continue |
||||||
|
|
||||||
|
# check for line of sight |
||||||
|
for check in range(distance+1): |
||||||
|
offset2 = utils.pmul(direction, check+1) |
||||||
|
offset2 = utils.padd(offset2, path.BLOCK_ABOVE) |
||||||
|
check = utils.padd(villager, offset2) |
||||||
|
if self.block_at(*check) not in blocks.NON_SOLID_IDS: |
||||||
|
break |
||||||
|
else: # for |
||||||
|
result.append(utils.padd(villager, offset)) |
||||||
|
return result |
Loading…
Reference in new issue