Compare commits
4 Commits
433e35e79b
...
ae2b0f4875
Author | SHA1 | Date | |
---|---|---|---|
ae2b0f4875 | |||
23891066c0 | |||
9874e23aa6 | |||
221d497204 |
|
@ -24,16 +24,17 @@ from mosfet.protocol.managers import DataManager, ChunksManager, ChatManager, Ch
|
||||||
|
|
||||||
from munch import Munch
|
from munch import Munch
|
||||||
|
|
||||||
from mosfet import blocks
|
|
||||||
from mosfet import game
|
from mosfet import game
|
||||||
from mosfet import items
|
|
||||||
from mosfet import job
|
from mosfet import job
|
||||||
from mosfet import mcdata
|
|
||||||
from mosfet import mobs
|
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import print_help
|
from mosfet import print_help
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import vector
|
from mosfet import vector
|
||||||
|
from mosfet import world
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
for module in [
|
for module in [
|
||||||
blocks,
|
blocks,
|
||||||
|
@ -46,6 +47,7 @@ for module in [
|
||||||
print_help,
|
print_help,
|
||||||
utils,
|
utils,
|
||||||
vector,
|
vector,
|
||||||
|
world,
|
||||||
]:
|
]:
|
||||||
importlib.reload(module)
|
importlib.reload(module)
|
||||||
|
|
||||||
|
@ -264,7 +266,7 @@ def bot(global_state):
|
||||||
g.chat = ChatManager(g)
|
g.chat = ChatManager(g)
|
||||||
|
|
||||||
g.game = game.Game(g)
|
g.game = game.Game(g)
|
||||||
g.world = game.MCWorld(g)
|
g.world = world.World(g)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while not g.pos:
|
while not g.pos:
|
||||||
|
|
326
mosfet/game.py
326
mosfet/game.py
|
@ -2,11 +2,9 @@ import re
|
||||||
import time
|
import time
|
||||||
import importlib
|
import importlib
|
||||||
import random
|
import random
|
||||||
import functools
|
|
||||||
from math import hypot
|
from math import hypot
|
||||||
from itertools import count
|
from itertools import count
|
||||||
from munch import Munch
|
from munch import Munch
|
||||||
from copy import copy
|
|
||||||
|
|
||||||
from minecraft.networking.packets import Packet, clientbound, serverbound
|
from minecraft.networking.packets import Packet, clientbound, serverbound
|
||||||
from minecraft.networking.types import BlockFace
|
from minecraft.networking.types import BlockFace
|
||||||
|
@ -27,316 +25,12 @@ from mosfet.protocol.types import Slot
|
||||||
from mosfet import print_help
|
from mosfet import print_help
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
|
||||||
from mosfet import items
|
|
||||||
from mosfet import mcdata
|
|
||||||
from mosfet import mobs
|
|
||||||
from mosfet import bot
|
from mosfet import bot
|
||||||
from mosfet import vector
|
from mosfet import vector
|
||||||
|
from mosfet.info import blocks
|
||||||
class MCWorld:
|
from mosfet.info import items
|
||||||
def __init__(self, global_state):
|
from mosfet.info import mcdata
|
||||||
self.g = global_state
|
from mosfet.info import mobs
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
def __init__(self, global_state):
|
def __init__(self, global_state):
|
||||||
|
@ -377,8 +71,8 @@ class Game:
|
||||||
self.g.name = packet.Username
|
self.g.name = packet.Username
|
||||||
|
|
||||||
def handle_join_game(self, packet):
|
def handle_join_game(self, packet):
|
||||||
|
print('Received join game packet')
|
||||||
print('Connected.')
|
print('Connected.')
|
||||||
print(packet)
|
|
||||||
self.g.info = packet
|
self.g.info = packet
|
||||||
self.g.eid = packet.entity_id
|
self.g.eid = packet.entity_id
|
||||||
self.g.dimension = packet.world_name.replace('minecraft:', '')
|
self.g.dimension = packet.world_name.replace('minecraft:', '')
|
||||||
|
@ -947,8 +641,8 @@ class Game:
|
||||||
elif g.window:
|
elif g.window:
|
||||||
g.window.contents[packet.slot] = packet.slot_data
|
g.window.contents[packet.slot] = packet.slot_data
|
||||||
|
|
||||||
if packet.window_id >= 0 and not packet.slot_data.present:
|
if g.item_lock and packet.window_id >= 0 and not packet.slot_data.present:
|
||||||
print('unlocking item lock')
|
print('Unlocking item lock')
|
||||||
g.item_lock = False
|
g.item_lock = False
|
||||||
|
|
||||||
def break_block(self, location):
|
def break_block(self, location):
|
||||||
|
@ -1301,6 +995,12 @@ class Game:
|
||||||
self.g.health = packet.health
|
self.g.health = packet.health
|
||||||
self.g.food = packet.food
|
self.g.food = packet.food
|
||||||
|
|
||||||
|
if packet.health == 0:
|
||||||
|
print('Died, stopping')
|
||||||
|
print('Use 1respawn to respawn the bot')
|
||||||
|
self.close_window()
|
||||||
|
bot.init(self.g)
|
||||||
|
|
||||||
def use_item(self, hand):
|
def use_item(self, hand):
|
||||||
packet = serverbound.play.UseItemPacket()
|
packet = serverbound.play.UseItemPacket()
|
||||||
packet.hand = hand
|
packet.hand = hand
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
|
|
||||||
MCD_BLOCKS = {}
|
MCD_BLOCKS = {}
|
||||||
for d in mcdata.mcd.blocks.values():
|
for d in mcdata.mcd.blocks.values():
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
from mosfet.jobs import (
|
from mosfet.jobs import (
|
||||||
cache_items,
|
cache_items,
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class CacheItemsStates:
|
class CacheItemsStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class CheckThreatsStates:
|
class CheckThreatsStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class ClearLeavesStates:
|
class ClearLeavesStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class EatFoodStates:
|
class EatFoodStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class FillBlocksStates:
|
class FillBlocksStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class FindGappleStates:
|
class FindGappleStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class GatherCropStates:
|
class GatherCropStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
@ -35,8 +35,7 @@ class GatherCropStates:
|
||||||
blocks.MATURE_BEETROOT_ID,
|
blocks.MATURE_BEETROOT_ID,
|
||||||
]
|
]
|
||||||
|
|
||||||
for crop in w.find_blocks_3d(p, mature_crops, 50, 20):
|
for crop in w.find_blocks_3d(p, mature_crops, 50, 20, True):
|
||||||
print('Found crop:', crop)
|
|
||||||
if crop not in self.bad_crops:
|
if crop not in self.bad_crops:
|
||||||
break
|
break
|
||||||
else: # for
|
else: # for
|
||||||
|
@ -44,6 +43,7 @@ class GatherCropStates:
|
||||||
self.state = self.cleanup
|
self.state = self.cleanup
|
||||||
return
|
return
|
||||||
|
|
||||||
|
print('Found crop:', crop)
|
||||||
self.crop = crop
|
self.crop = crop
|
||||||
self.type_id = w.block_at(*crop)
|
self.type_id = w.block_at(*crop)
|
||||||
self.state = self.nav_to_crop
|
self.state = self.nav_to_crop
|
||||||
|
@ -55,10 +55,12 @@ class GatherCropStates:
|
||||||
navpath = w.path_to_place(p, self.crop)
|
navpath = w.path_to_place(p, self.crop)
|
||||||
|
|
||||||
if navpath:
|
if navpath:
|
||||||
|
print('Going to crop', self.crop)
|
||||||
self.g.path = navpath
|
self.g.path = navpath
|
||||||
self.g.look_at = utils.padd(self.crop, path.BLOCK_BELOW)
|
self.g.look_at = utils.padd(self.crop, path.BLOCK_BELOW)
|
||||||
self.state = self.going_to_crop
|
self.state = self.going_to_crop
|
||||||
else:
|
else:
|
||||||
|
print('Cant get to it, blacklisting')
|
||||||
self.bad_crops.append(self.crop)
|
self.bad_crops.append(self.crop)
|
||||||
self.wait_time = 0.5
|
self.wait_time = 0.5
|
||||||
self.state = self.wait_to_restart
|
self.state = self.wait_to_restart
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class GatherSandStates:
|
class GatherSandStates:
|
||||||
def bair(self, p):
|
def bair(self, p):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class GatherWartStates:
|
class GatherWartStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
@ -29,8 +29,7 @@ class GatherWartStates:
|
||||||
p = utils.pint(self.g.pos)
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
mature_wart = max(blocks.NETHERWART_IDS)
|
mature_wart = max(blocks.NETHERWART_IDS)
|
||||||
for wart in w.find_blocks_3d(p, [mature_wart], 50, 20):
|
for wart in w.find_blocks_3d(p, [mature_wart], 50, 20, True):
|
||||||
print('Found wart:', wart)
|
|
||||||
if wart not in self.bad_warts:
|
if wart not in self.bad_warts:
|
||||||
break
|
break
|
||||||
else: # for
|
else: # for
|
||||||
|
@ -38,6 +37,7 @@ class GatherWartStates:
|
||||||
self.state = self.cleanup
|
self.state = self.cleanup
|
||||||
return
|
return
|
||||||
|
|
||||||
|
print('Found wart:', wart)
|
||||||
self.wart = wart
|
self.wart = wart
|
||||||
self.state = self.nav_to_wart
|
self.state = self.nav_to_wart
|
||||||
|
|
||||||
|
@ -48,11 +48,21 @@ class GatherWartStates:
|
||||||
navpath = w.path_to_place(p, self.wart)
|
navpath = w.path_to_place(p, self.wart)
|
||||||
|
|
||||||
if navpath:
|
if navpath:
|
||||||
|
print('Going to wart', self.wart)
|
||||||
self.g.path = navpath
|
self.g.path = navpath
|
||||||
self.g.look_at = utils.padd(self.wart, path.BLOCK_BELOW)
|
self.g.look_at = utils.padd(self.wart, path.BLOCK_BELOW)
|
||||||
self.state = self.going_to_wart
|
self.state = self.going_to_wart
|
||||||
else:
|
else:
|
||||||
|
print('Cant get to it, blacklisting')
|
||||||
self.bad_warts.append(wart)
|
self.bad_warts.append(wart)
|
||||||
|
self.wait_time = 0.5
|
||||||
|
self.state = self.wait_to_restart
|
||||||
|
|
||||||
|
def wait_to_restart(self):
|
||||||
|
# prevent timeouts
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
self.state = self.find_new_wart
|
self.state = self.find_new_wart
|
||||||
|
|
||||||
def going_to_wart(self):
|
def going_to_wart(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class GatherWoodStates:
|
class GatherWoodStates:
|
||||||
def bair(self, p):
|
def bair(self, p):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class GrabSandStates:
|
class GrabSandStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class GrabSaplingStates:
|
class GrabSaplingStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class GrabSuppliesStates:
|
class GrabSuppliesStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class PlantTreeStates:
|
class PlantTreeStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class SellToVillagerStates:
|
class SellToVillagerStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import items
|
from mosfet.info import items
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
from mosfet import mobs
|
from mosfet.info import mobs
|
||||||
|
|
||||||
class SleepWithBedStates:
|
class SleepWithBedStates:
|
||||||
def idle(self):
|
def idle(self):
|
||||||
|
|
|
@ -5,7 +5,7 @@ from math import hypot, sqrt
|
||||||
|
|
||||||
from astar import AStar
|
from astar import AStar
|
||||||
|
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
|
|
||||||
class AStarTimeout(Exception):
|
class AStarTimeout(Exception):
|
||||||
|
|
|
@ -10,7 +10,7 @@ from minecraft.networking.types import (
|
||||||
|
|
||||||
from .types import Nbt, Slot, Entry, Trade
|
from .types import Nbt, Slot, Entry, Trade
|
||||||
|
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
|
|
||||||
|
|
||||||
class ChunkDataPacket(Packet):
|
class ChunkDataPacket(Packet):
|
||||||
|
|
|
@ -2,8 +2,8 @@ import importlib
|
||||||
import collections
|
import collections
|
||||||
from math import floor, ceil, sqrt, hypot
|
from math import floor, ceil, sqrt, hypot
|
||||||
|
|
||||||
from mosfet import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet import mcdata
|
from mosfet.info import mcdata
|
||||||
|
|
||||||
TICK = 0.05
|
TICK = 0.05
|
||||||
|
|
||||||
|
@ -133,49 +133,32 @@ def search_2d(distance=0):
|
||||||
visited.add(cur)
|
visited.add(cur)
|
||||||
yield cur
|
yield cur
|
||||||
|
|
||||||
def search_3d(distance=0, y_limit=0):
|
def get_neighbors_3d(x,y,z):
|
||||||
def get_neighbors(x,y,z):
|
return [
|
||||||
return [
|
#(x+1, y+1, z+0),
|
||||||
(x+1, y+1, z+0),
|
#(x+1, y-1, z+0),
|
||||||
(x+1, y-1, z+0),
|
#(x+1, y+1, z+1),
|
||||||
(x+1, y+1, z+1),
|
#(x+1, y+0, z+1),
|
||||||
(x+1, y+0, z+1),
|
#(x+1, y-1, z+1),
|
||||||
(x+1, y-1, z+1),
|
#(x+1, y+1, z-1),
|
||||||
(x+1, y+1, z-1),
|
#(x+1, y+0, z-1),
|
||||||
(x+1, y+0, z-1),
|
#(x+1, y-1, z-1),
|
||||||
(x+1, y-1, z-1),
|
(x+1, y+0, z+0),
|
||||||
(x+1, y+0, z+0),
|
(x+0, y+1, z+0),
|
||||||
(x+0, y+1, z+0),
|
(x+0, y-1, z+0),
|
||||||
(x+0, y-1, z+0),
|
#(x+0, y+1, z+1),
|
||||||
(x+0, y+1, z+1),
|
(x+0, y+0, z+1),
|
||||||
(x+0, y+0, z+1),
|
#(x+0, y-1, z+1),
|
||||||
(x+0, y-1, z+1),
|
#(x+0, y+1, z-1),
|
||||||
(x+0, y+1, z-1),
|
(x+0, y+0, z-1),
|
||||||
(x+0, y+0, z-1),
|
#(x+0, y-1, z-1),
|
||||||
(x+0, y-1, z-1),
|
#(x-1, y+1, z+0),
|
||||||
(x-1, y+1, z+0),
|
#(x-1, y-1, z+0),
|
||||||
(x-1, y-1, z+0),
|
#(x-1, y+1, z+1),
|
||||||
(x-1, y+1, z+1),
|
#(x-1, y+0, z+1),
|
||||||
(x-1, y+0, z+1),
|
#(x-1, y-1, z+1),
|
||||||
(x-1, y-1, z+1),
|
#(x-1, y+1, z-1),
|
||||||
(x-1, y+1, z-1),
|
#(x-1, y+0, z-1),
|
||||||
(x-1, y+0, z-1),
|
#(x-1, y-1, z-1),
|
||||||
(x-1, y-1, z-1),
|
(x-1, y+0, z+0),
|
||||||
(x-1, y+0, z+0),
|
]
|
||||||
]
|
|
||||||
|
|
||||||
to_visit = collections.deque([(0, 0, 0)])
|
|
||||||
visited = set()
|
|
||||||
|
|
||||||
while to_visit:
|
|
||||||
cur = to_visit.pop()
|
|
||||||
if cur in visited:
|
|
||||||
continue
|
|
||||||
if y_limit and abs(cur[1]) > y_limit:
|
|
||||||
continue
|
|
||||||
if distance and hypot(*cur) > distance:
|
|
||||||
continue
|
|
||||||
for neighbor in get_neighbors(*cur):
|
|
||||||
to_visit.appendleft(neighbor)
|
|
||||||
visited.add(cur)
|
|
||||||
yield cur
|
|
||||||
|
|
333
mosfet/world.py
Normal file
333
mosfet/world.py
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
import collections
|
||||||
|
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.info import blocks
|
||||||
|
from mosfet.info 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, thru_air=False):
|
||||||
|
to_visit = collections.deque([(0, 0, 0)])
|
||||||
|
visited = set()
|
||||||
|
|
||||||
|
while to_visit:
|
||||||
|
cur = to_visit.pop()
|
||||||
|
if cur in visited:
|
||||||
|
continue
|
||||||
|
if y_limit and abs(cur[1]) > y_limit:
|
||||||
|
continue
|
||||||
|
if distance and hypot(*cur) > distance:
|
||||||
|
continue
|
||||||
|
|
||||||
|
check = utils.padd(center, cur)
|
||||||
|
|
||||||
|
if not thru_air or self.block_at(*check) in blocks.NON_SOLID_IDS:
|
||||||
|
for neighbor in utils.get_neighbors_3d(*cur):
|
||||||
|
to_visit.appendleft(neighbor)
|
||||||
|
|
||||||
|
visited.add(cur)
|
||||||
|
|
||||||
|
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
Block a user