Do pathfinding from the hero to the mouse point
It works finally! And uses A*!
This commit is contained in:
parent
a8bbc47668
commit
2d82d9834f
4 changed files with 42 additions and 35 deletions
|
@ -28,7 +28,8 @@ class Interface:
|
||||||
|
|
||||||
self.map_window = MapWindow(
|
self.map_window = MapWindow(
|
||||||
Rect.from_raw_values(0, 0, size.width, size.height - 5),
|
Rect.from_raw_values(0, 0, size.width, size.height - 5),
|
||||||
engine.map)
|
engine.map,
|
||||||
|
engine.hero)
|
||||||
self.info_window = InfoWindow(
|
self.info_window = InfoWindow(
|
||||||
Rect.from_raw_values(0, size.height - 5, 28, 5))
|
Rect.from_raw_values(0, size.height - 5, 28, 5))
|
||||||
self.message_window = MessageLogWindow(
|
self.message_window = MessageLogWindow(
|
||||||
|
@ -43,7 +44,7 @@ class Interface:
|
||||||
|
|
||||||
hero = self.engine.hero
|
hero = self.engine.hero
|
||||||
self.info_window.update_hero(hero)
|
self.info_window.update_hero(hero)
|
||||||
self.map_window.update_drawable_map_bounds(hero)
|
self.map_window.update_drawable_map_bounds()
|
||||||
|
|
||||||
sorted_entities = sorted(self.engine.entities, key=lambda e: e.render_order.value)
|
sorted_entities = sorted(self.engine.entities, key=lambda e: e.render_order.value)
|
||||||
self.map_window.entities = sorted_entities
|
self.map_window.entities = sorted_entities
|
||||||
|
@ -64,6 +65,7 @@ class Interface:
|
||||||
context.present(self.console)
|
context.present(self.console)
|
||||||
|
|
||||||
for event in tev.wait():
|
for event in tev.wait():
|
||||||
|
context.convert_event(event)
|
||||||
did_handle = self.event_handler.dispatch(event)
|
did_handle = self.event_handler.dispatch(event)
|
||||||
if did_handle:
|
if did_handle:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
# Eryn Wells <eryn@erynwells.me>
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Generic, Optional, TypeVar
|
||||||
|
|
||||||
from tcod import event as tev
|
from tcod import event as tev
|
||||||
from tcod.console import Console
|
from tcod.console import Console
|
||||||
|
|
||||||
from ...geometry import Point, Rect, Vector
|
from ...geometry import Point, Rect, Vector
|
||||||
|
|
||||||
|
WindowT = TypeVar('WindowT', bound='Window')
|
||||||
|
|
||||||
|
|
||||||
class Window:
|
class Window:
|
||||||
'''A user interface window. It can be framed and it can handle events.'''
|
'''A user interface window. It can be framed and it can handle events.'''
|
||||||
|
|
||||||
class EventHandler(tev.EventDispatch[bool]):
|
class EventHandler(tev.EventDispatch[bool], Generic[WindowT]):
|
||||||
'''
|
'''
|
||||||
Handles events for a Window. Event dispatch methods return True if the event
|
Handles events for a Window. Event dispatch methods return True if the event
|
||||||
was handled and no further action is needed.
|
was handled and no further action is needed.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, window: 'Window'):
|
def __init__(self, window: WindowT):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.window = window
|
self.window = window
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Eryn Wells <eryn@erynwells.me>
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import tcod.event as tev
|
import tcod.event as tev
|
||||||
|
@ -16,7 +16,7 @@ from ...object import Entity, Hero
|
||||||
class MapWindow(Window):
|
class MapWindow(Window):
|
||||||
'''A Window that displays a game map'''
|
'''A Window that displays a game map'''
|
||||||
|
|
||||||
class EventHandler(Window.EventHandler):
|
class EventHandler(Window.EventHandler['MapWindow']):
|
||||||
'''An event handler for the MapWindow.'''
|
'''An event handler for the MapWindow.'''
|
||||||
|
|
||||||
def ev_mousemotion(self, event: tev.MouseMotion) -> bool:
|
def ev_mousemotion(self, event: tev.MouseMotion) -> bool:
|
||||||
|
@ -24,23 +24,40 @@ class MapWindow(Window):
|
||||||
if not mouse_point:
|
if not mouse_point:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# TODO: Convert window point to map point
|
log.UI.info('Mouse point in window %s', mouse_point)
|
||||||
# TODO: Perform a path finding operation from the hero to the mouse point
|
|
||||||
# TODO: Highlight those points on the map
|
hero = self.window.hero
|
||||||
|
if not hero:
|
||||||
|
return False
|
||||||
|
|
||||||
|
map_point = self.window.convert_window_point_to_map(mouse_point)
|
||||||
|
log.UI.info('Mouse point in map %s', map_point)
|
||||||
|
|
||||||
|
map_ = self.window.map
|
||||||
|
path = map_.find_walkable_path_from_point_to_point(hero.position, map_point)
|
||||||
|
map_.highlight_points(path)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# pylint: disable=redefined-builtin
|
# pylint: disable=redefined-builtin
|
||||||
def __init__(self, bounds: Rect, map: Map, **kwargs):
|
def __init__(self, bounds: Rect, map: Map, hero: Hero, **kwargs):
|
||||||
super().__init__(bounds, **kwargs)
|
super().__init__(bounds, event_handler=self.__class__.EventHandler(self), **kwargs)
|
||||||
self.map = map
|
self.map = map
|
||||||
|
|
||||||
self.drawable_map_bounds = map.bounds
|
self.drawable_map_bounds = map.bounds
|
||||||
|
self.hero = hero
|
||||||
self.entities: List[Entity] = []
|
self.entities: List[Entity] = []
|
||||||
|
|
||||||
self._draw_bounds = self.drawable_bounds
|
self._draw_bounds = self.drawable_bounds
|
||||||
|
|
||||||
def update_drawable_map_bounds(self, hero: Hero):
|
def convert_window_point_to_map(self, point: Point) -> Point:
|
||||||
|
'''
|
||||||
|
Convert a point in window coordinates to a point relative to the map's
|
||||||
|
origin point.
|
||||||
|
'''
|
||||||
|
return point - Vector.from_point(self._draw_bounds.origin)
|
||||||
|
|
||||||
|
def update_drawable_map_bounds(self):
|
||||||
'''
|
'''
|
||||||
Figure out what portion of the map is drawable and update
|
Figure out what portion of the map is drawable and update
|
||||||
`self.drawable_map_bounds`. This method attempts to keep the hero
|
`self.drawable_map_bounds`. This method attempts to keep the hero
|
||||||
|
@ -59,7 +76,7 @@ class MapWindow(Window):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Attempt to keep the player centered in the viewport.
|
# Attempt to keep the player centered in the viewport.
|
||||||
hero_point = hero.position
|
hero_point = self.hero.position
|
||||||
|
|
||||||
if viewport_is_wider_than_map:
|
if viewport_is_wider_than_map:
|
||||||
x = 0
|
x = 0
|
||||||
|
@ -115,8 +132,6 @@ class MapWindow(Window):
|
||||||
drawable_map_bounds = self.drawable_map_bounds
|
drawable_map_bounds = self.drawable_map_bounds
|
||||||
drawable_bounds = self.drawable_bounds
|
drawable_bounds = self.drawable_bounds
|
||||||
|
|
||||||
log.UI.info('Drawing map')
|
|
||||||
|
|
||||||
map_slice = np.s_[
|
map_slice = np.s_[
|
||||||
drawable_map_bounds.min_x: drawable_map_bounds.max_x + 1,
|
drawable_map_bounds.min_x: drawable_map_bounds.max_x + 1,
|
||||||
drawable_map_bounds.min_y: drawable_map_bounds.max_y + 1]
|
drawable_map_bounds.min_y: drawable_map_bounds.max_y + 1]
|
||||||
|
@ -126,19 +141,12 @@ class MapWindow(Window):
|
||||||
console_draw_bounds.min_x: console_draw_bounds.max_x + 1,
|
console_draw_bounds.min_x: console_draw_bounds.max_x + 1,
|
||||||
console_draw_bounds.min_y: console_draw_bounds.max_y + 1]
|
console_draw_bounds.min_y: console_draw_bounds.max_y + 1]
|
||||||
|
|
||||||
log.UI.debug('Map bounds=%s, slice=%s', drawable_map_bounds, map_slice)
|
|
||||||
log.UI.debug('Console bounds=%s, slice=%s', drawable_bounds, console_slice)
|
|
||||||
|
|
||||||
console.tiles_rgb[console_slice] = self.map.composited_tiles[map_slice]
|
console.tiles_rgb[console_slice] = self.map.composited_tiles[map_slice]
|
||||||
|
|
||||||
log.UI.info('Done drawing map')
|
|
||||||
|
|
||||||
def _draw_entities(self, console):
|
def _draw_entities(self, console):
|
||||||
map_bounds_vector = Vector.from_point(self.drawable_map_bounds.origin)
|
map_bounds_vector = Vector.from_point(self.drawable_map_bounds.origin)
|
||||||
draw_bounds_vector = Vector.from_point(self._draw_bounds.origin)
|
draw_bounds_vector = Vector.from_point(self._draw_bounds.origin)
|
||||||
|
|
||||||
log.UI.info('Drawing entities')
|
|
||||||
|
|
||||||
for ent in self.entities:
|
for ent in self.entities:
|
||||||
# Only draw entities that are in the field of view
|
# Only draw entities that are in the field of view
|
||||||
if not self.map.point_is_visible(ent.position):
|
if not self.map.point_is_visible(ent.position):
|
||||||
|
@ -161,5 +169,3 @@ class MapWindow(Window):
|
||||||
string=ent.symbol,
|
string=ent.symbol,
|
||||||
fg=ent.foreground,
|
fg=ent.foreground,
|
||||||
bg=tuple(map_tile_at_entity_position['bg'][:3]))
|
bg=tuple(map_tile_at_entity_position['bg'][:3]))
|
||||||
|
|
||||||
log.UI.info('Done drawing entities')
|
|
||||||
|
|
|
@ -111,16 +111,13 @@ class Map:
|
||||||
for pt in points:
|
for pt in points:
|
||||||
self.highlighted[pt.x, pt.y] = True
|
self.highlighted[pt.x, pt.y] = True
|
||||||
|
|
||||||
def print_to_console(self, console: tcod.Console, bounds: Rect) -> None:
|
def find_walkable_path_from_point_to_point(self, point_a: Point, point_b: Point) -> Iterable[Point]:
|
||||||
'''Render the map to the console.'''
|
'''
|
||||||
size = self.size
|
Find a path between point A and point B using tcod's A* implementation.
|
||||||
|
'''
|
||||||
# If a tile is in the visible array, draw it with the "light" color. If it's not, but it's in the explored
|
a_star = tcod.path.AStar(self.tiles['walkable'])
|
||||||
# array, draw it with the "dark" color. Otherwise, draw it as Empty.
|
path = a_star.get_path(point_a.x, point_a.y, point_b.x, point_b.y)
|
||||||
console.tiles_rgb[0:size.width, 0:size.height] = np.select(
|
return map(lambda t: Point(t[0], t[1]), path)
|
||||||
condlist=[self.highlighted, self.visible, self.explored],
|
|
||||||
choicelist=[self.tiles['highlighted'], self.tiles['light'], self.tiles['dark']],
|
|
||||||
default=Shroud)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
string = ''
|
string = ''
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue