Update "Grid-Interaction-Patterns.-"
parent
cffd9d02de
commit
45cee16081
2 changed files with 303 additions and 429 deletions
303
Grid-Interaction-Patterns.-.md
Normal file
303
Grid-Interaction-Patterns.-.md
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
# Grid Interaction Patterns
|
||||
|
||||
Patterns for handling mouse and keyboard interaction with Grids, cells, and entities. These patterns build on the grid-specific event handlers.
|
||||
|
||||
**Related Pages:**
|
||||
- [[Grid-System]] - Grid architecture and layers
|
||||
- [[Entity-Management]] - Entity behavior patterns
|
||||
- [[Input-and-Events]] - General event handling
|
||||
|
||||
---
|
||||
|
||||
## Grid Cell Events
|
||||
|
||||
Grids provide cell-level mouse events in addition to standard UIDrawable events:
|
||||
|
||||
| Property | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `on_cell_click` | `(cell_pos: Vector, button: MouseButton, action: InputState) -> None` | Cell clicked |
|
||||
| `on_cell_enter` | `(cell_pos: Vector) -> None` | Mouse enters cell |
|
||||
| `on_cell_exit` | `(cell_pos: Vector) -> None` | Mouse leaves cell |
|
||||
| `hovered_cell` | `(x, y)` or `None` | Currently hovered cell (read-only) |
|
||||
|
||||
Cell positions arrive as `Vector` objects. Use `int(cell_pos.x)`, `int(cell_pos.y)` to get grid coordinates.
|
||||
|
||||
---
|
||||
|
||||
## Setup Template
|
||||
|
||||
Most grid interaction patterns fit into this structure:
|
||||
|
||||
```python
|
||||
import mcrfpy
|
||||
|
||||
# Scene setup
|
||||
scene = mcrfpy.Scene("game")
|
||||
ui = scene.children
|
||||
|
||||
# Load tileset texture
|
||||
texture = mcrfpy.Texture("assets/sprites/tileset.png", 16, 16)
|
||||
|
||||
# Create layers
|
||||
terrain = mcrfpy.TileLayer(name="terrain", z_index=-2, texture=texture)
|
||||
highlight = mcrfpy.ColorLayer(name="highlight", z_index=-1)
|
||||
overlay = mcrfpy.ColorLayer(name="overlay", z_index=1)
|
||||
|
||||
# Create grid with layers
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(20, 15),
|
||||
pos=(50, 50),
|
||||
size=(640, 480),
|
||||
layers=[terrain, highlight, overlay]
|
||||
)
|
||||
grid.fill_color = mcrfpy.Color(20, 20, 30)
|
||||
ui.append(grid)
|
||||
|
||||
# Create player entity
|
||||
player = mcrfpy.Entity(grid_pos=(10, 7), sprite_index=0)
|
||||
grid.entities.append(player)
|
||||
|
||||
# Wire up events (patterns below fill these in)
|
||||
# grid.on_cell_click = ...
|
||||
# grid.on_cell_enter = ...
|
||||
# grid.on_cell_exit = ...
|
||||
|
||||
mcrfpy.current_scene = scene
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cell Hover Highlighting
|
||||
|
||||
Show which cell the mouse is over using a ColorLayer.
|
||||
|
||||
```python
|
||||
current_highlight = [None]
|
||||
|
||||
def on_cell_enter(cell_pos):
|
||||
x, y = int(cell_pos.x), int(cell_pos.y)
|
||||
highlight.set((x, y), mcrfpy.Color(255, 255, 255, 40))
|
||||
current_highlight[0] = (x, y)
|
||||
|
||||
def on_cell_exit(cell_pos):
|
||||
x, y = int(cell_pos.x), int(cell_pos.y)
|
||||
highlight.set((x, y), mcrfpy.Color(0, 0, 0, 0))
|
||||
current_highlight[0] = None
|
||||
|
||||
grid.on_cell_enter = on_cell_enter
|
||||
grid.on_cell_exit = on_cell_exit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cell Click Actions
|
||||
|
||||
Respond to clicks on specific cells. Callbacks receive `(cell_pos, button, action)`.
|
||||
|
||||
```python
|
||||
def on_cell_click(cell_pos, button, action):
|
||||
x, y = int(cell_pos.x), int(cell_pos.y)
|
||||
point = grid.at(x, y)
|
||||
|
||||
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED:
|
||||
if point.walkable:
|
||||
player.grid_x = x
|
||||
player.grid_y = y
|
||||
|
||||
elif button == mcrfpy.MouseButton.RIGHT and action == mcrfpy.InputState.PRESSED:
|
||||
# Inspect cell
|
||||
show_cell_info(x, y, point)
|
||||
|
||||
elif button == mcrfpy.MouseButton.MIDDLE and action == mcrfpy.InputState.PRESSED:
|
||||
# Toggle walkability (level editor)
|
||||
point.walkable = not point.walkable
|
||||
|
||||
grid.on_cell_click = on_cell_click
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WASD Movement
|
||||
|
||||
Use `scene.on_key` with Key enums for movement.
|
||||
|
||||
```python
|
||||
move_map = {
|
||||
mcrfpy.Key.W: (0, -1),
|
||||
mcrfpy.Key.A: (-1, 0),
|
||||
mcrfpy.Key.S: (0, 1),
|
||||
mcrfpy.Key.D: (1, 0),
|
||||
}
|
||||
|
||||
def handle_key(key, action):
|
||||
if action != mcrfpy.InputState.PRESSED:
|
||||
return
|
||||
if key in move_map:
|
||||
dx, dy = move_map[key]
|
||||
new_x = int(player.grid_x + dx)
|
||||
new_y = int(player.grid_y + dy)
|
||||
point = player.grid.at(new_x, new_y)
|
||||
if point and point.walkable:
|
||||
player.grid_x = new_x
|
||||
player.grid_y = new_y
|
||||
|
||||
scene.on_key = handle_key
|
||||
```
|
||||
|
||||
### Smooth Animated Movement
|
||||
|
||||
For visual smoothness, animate the entity position:
|
||||
|
||||
```python
|
||||
def handle_key(key, action):
|
||||
if action != mcrfpy.InputState.PRESSED:
|
||||
return
|
||||
if key in move_map:
|
||||
dx, dy = move_map[key]
|
||||
new_x = int(player.grid_x + dx)
|
||||
new_y = int(player.grid_y + dy)
|
||||
point = player.grid.at(new_x, new_y)
|
||||
if point and point.walkable:
|
||||
player.animate("x", float(new_x), 0.15, mcrfpy.Easing.EASE_OUT_QUAD)
|
||||
player.animate("y", float(new_y), 0.15, mcrfpy.Easing.EASE_OUT_QUAD)
|
||||
player.grid_x = new_x
|
||||
player.grid_y = new_y
|
||||
|
||||
scene.on_key = handle_key
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entity Selection
|
||||
|
||||
Click to select entities using a ColorLayer overlay.
|
||||
|
||||
```python
|
||||
selected = [None]
|
||||
|
||||
def select_entity(entity):
|
||||
# Clear previous selection
|
||||
if selected[0]:
|
||||
ex, ey = int(selected[0].grid_x), int(selected[0].grid_y)
|
||||
overlay.set((ex, ey), mcrfpy.Color(0, 0, 0, 0))
|
||||
|
||||
selected[0] = entity
|
||||
|
||||
if entity:
|
||||
overlay.set((int(entity.grid_x), int(entity.grid_y)),
|
||||
mcrfpy.Color(255, 200, 0, 80))
|
||||
|
||||
def on_cell_click(cell_pos, button, action):
|
||||
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED:
|
||||
x, y = int(cell_pos.x), int(cell_pos.y)
|
||||
for entity in grid.entities:
|
||||
if int(entity.grid_x) == x and int(entity.grid_y) == y:
|
||||
select_entity(entity)
|
||||
return
|
||||
select_entity(None)
|
||||
|
||||
grid.on_cell_click = on_cell_click
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Path Preview
|
||||
|
||||
Show pathfinding path on hover using a ColorLayer.
|
||||
|
||||
```python
|
||||
current_path_cells = []
|
||||
|
||||
def show_path_to(target_x, target_y):
|
||||
global current_path_cells
|
||||
# Clear previous path
|
||||
for cx, cy in current_path_cells:
|
||||
highlight.set((cx, cy), mcrfpy.Color(0, 0, 0, 0))
|
||||
current_path_cells = []
|
||||
|
||||
# Calculate path
|
||||
path = grid.find_path(
|
||||
(int(player.grid_x), int(player.grid_y)),
|
||||
(target_x, target_y)
|
||||
)
|
||||
if not path:
|
||||
return
|
||||
|
||||
# Draw path cells
|
||||
i = 0
|
||||
while len(path) > 0:
|
||||
step = path.walk()
|
||||
if step:
|
||||
x, y = int(step.x), int(step.y)
|
||||
alpha = max(30, 100 - (i * 5))
|
||||
highlight.set((x, y), mcrfpy.Color(100, 200, 255, alpha))
|
||||
current_path_cells.append((x, y))
|
||||
i += 1
|
||||
|
||||
def on_cell_enter(cell_pos):
|
||||
show_path_to(int(cell_pos.x), int(cell_pos.y))
|
||||
|
||||
grid.on_cell_enter = on_cell_enter
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tile Inspector Panel
|
||||
|
||||
Click a cell to show information in a side panel.
|
||||
|
||||
```python
|
||||
# Create inspector UI
|
||||
inspector = mcrfpy.Frame(pos=(700, 50), size=(200, 150),
|
||||
fill_color=mcrfpy.Color(30, 30, 40, 230))
|
||||
inspector.outline = 1
|
||||
inspector.outline_color = mcrfpy.Color(80, 80, 100)
|
||||
inspector.visible = False
|
||||
ui.append(inspector)
|
||||
|
||||
title = mcrfpy.Caption(text="Cell Info", pos=(10, 8))
|
||||
title.fill_color = mcrfpy.Color(180, 180, 200)
|
||||
inspector.children.append(title)
|
||||
|
||||
info_lines = []
|
||||
for i in range(4):
|
||||
line = mcrfpy.Caption(text="", pos=(10, 30 + i * 20))
|
||||
line.fill_color = mcrfpy.Color(160, 160, 180)
|
||||
inspector.children.append(line)
|
||||
info_lines.append(line)
|
||||
|
||||
def on_cell_click(cell_pos, button, action):
|
||||
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED:
|
||||
x, y = int(cell_pos.x), int(cell_pos.y)
|
||||
point = grid.at(x, y)
|
||||
|
||||
title.text = f"Cell ({x}, {y})"
|
||||
info_lines[0].text = f"Walkable: {point.walkable}"
|
||||
info_lines[1].text = f"Transparent: {point.transparent}"
|
||||
|
||||
entities_here = [e for e in grid.entities
|
||||
if int(e.grid_x) == x and int(e.grid_y) == y]
|
||||
info_lines[2].text = f"Entities: {len(entities_here)}"
|
||||
|
||||
if entities_here:
|
||||
info_lines[3].text = f" {entities_here[0].name or 'unnamed'}"
|
||||
else:
|
||||
info_lines[3].text = ""
|
||||
|
||||
inspector.visible = True
|
||||
|
||||
grid.on_cell_click = on_cell_click
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [[Entity-Management]] - Entity behavior and pathfinding
|
||||
- [[Grid-System]] - Layer management and rendering
|
||||
- [[UI-Widget-Patterns]] - Non-grid UI patterns
|
||||
- [[Input-and-Events]] - Event API reference
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-02-07*
|
||||
|
|
@ -1,429 +0,0 @@
|
|||
# Grid Interaction Patterns
|
||||
|
||||
Patterns for handling mouse and keyboard interaction with Grids, cells, and entities. These patterns build on the grid-specific event handlers.
|
||||
|
||||
**Related Pages:**
|
||||
- [[Grid-System]] - Grid architecture and layers
|
||||
- [[Entity-Management]] - Entity behavior patterns
|
||||
- [[Input-and-Events]] - General event handling
|
||||
|
||||
---
|
||||
|
||||
## Grid Cell Events
|
||||
|
||||
Grids provide cell-level mouse events in addition to standard UIDrawable events:
|
||||
|
||||
| Property | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `on_cell_click` | `(grid_x, grid_y, button) -> None` | Cell clicked (0=left, 1=right, 2=middle) |
|
||||
| `on_cell_enter` | `(grid_x, grid_y) -> None` | Mouse enters cell |
|
||||
| `on_cell_exit` | `(grid_x, grid_y) -> None` | Mouse leaves cell |
|
||||
| `hovered_cell` | `(x, y)` or `None` | Currently hovered cell (read-only) |
|
||||
|
||||
These events use **grid coordinates** (cell indices), not pixel coordinates.
|
||||
|
||||
---
|
||||
|
||||
## Setup Template
|
||||
|
||||
Most grid interaction patterns fit into this structure:
|
||||
|
||||
```python
|
||||
import mcrfpy
|
||||
|
||||
# Scene setup
|
||||
mcrfpy.createScene("game")
|
||||
ui = mcrfpy.sceneUI("game")
|
||||
|
||||
# Create grid with layers
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(20, 15),
|
||||
pos=(50, 50),
|
||||
size=(640, 480),
|
||||
layers={}
|
||||
)
|
||||
grid.fill_color = mcrfpy.Color(20, 20, 30)
|
||||
ui.append(grid)
|
||||
|
||||
# Add terrain layer
|
||||
terrain = grid.add_layer("tile", z_index=-2)
|
||||
# terrain.fill(0) # Fill with default tile
|
||||
|
||||
# Add highlight layer (above terrain, below entities)
|
||||
highlight = grid.add_layer("color", z_index=-1)
|
||||
|
||||
# Add overlay layer (above entities, for fog/selection)
|
||||
overlay = grid.add_layer("color", z_index=1)
|
||||
|
||||
# Create player entity
|
||||
player = mcrfpy.Entity(pos=(10, 7), sprite_index=0)
|
||||
grid.entities.append(player)
|
||||
|
||||
# Wire up events (patterns below fill these in)
|
||||
# grid.on_cell_click = ...
|
||||
# grid.on_cell_enter = ...
|
||||
# grid.on_cell_exit = ...
|
||||
|
||||
mcrfpy.setScene("game")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cell Hover Highlighting
|
||||
|
||||
Show which cell the mouse is over.
|
||||
|
||||
```python
|
||||
# Track currently highlighted cell
|
||||
current_highlight = [None] # Use list for closure mutability
|
||||
|
||||
def on_cell_enter(x, y):
|
||||
# Highlight new cell
|
||||
highlight.set(x, y, mcrfpy.Color(255, 255, 255, 40))
|
||||
current_highlight[0] = (x, y)
|
||||
|
||||
def on_cell_exit(x, y):
|
||||
# Clear old highlight
|
||||
highlight.set(x, y, mcrfpy.Color(0, 0, 0, 0))
|
||||
current_highlight[0] = None
|
||||
|
||||
grid.on_cell_enter = on_cell_enter
|
||||
grid.on_cell_exit = on_cell_exit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cell Click Actions
|
||||
|
||||
Respond to clicks on specific cells.
|
||||
|
||||
```python
|
||||
def on_cell_click(x, y, button):
|
||||
point = grid.at(x, y)
|
||||
|
||||
if button == 0: # Left click
|
||||
if point.walkable:
|
||||
# Move player to clicked cell
|
||||
player.pos = (x, y)
|
||||
|
||||
elif button == 1: # Right click
|
||||
# Inspect cell
|
||||
show_cell_info(x, y, point)
|
||||
|
||||
elif button == 2: # Middle click
|
||||
# Toggle walkability (for level editor)
|
||||
point.walkable = not point.walkable
|
||||
|
||||
grid.on_cell_click = on_cell_click
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WASD Movement
|
||||
|
||||
Track key states for smooth entity movement.
|
||||
|
||||
```python
|
||||
class MovementController:
|
||||
def __init__(self, entity):
|
||||
self.entity = entity
|
||||
self.keys = {"W": False, "A": False, "S": False, "D": False}
|
||||
self.move_delay = 150 # ms between moves
|
||||
self.last_move = 0
|
||||
|
||||
def handle_key(self, key, pressed):
|
||||
if key in self.keys:
|
||||
self.keys[key] = pressed
|
||||
|
||||
def update(self, current_time):
|
||||
if current_time - self.last_move < self.move_delay:
|
||||
return
|
||||
|
||||
dx, dy = 0, 0
|
||||
if self.keys["W"]: dy -= 1
|
||||
if self.keys["S"]: dy += 1
|
||||
if self.keys["A"]: dx -= 1
|
||||
if self.keys["D"]: dx += 1
|
||||
|
||||
if dx == 0 and dy == 0:
|
||||
return
|
||||
|
||||
new_x = self.entity.x + dx
|
||||
new_y = self.entity.y + dy
|
||||
|
||||
# Check walkability
|
||||
point = self.entity.grid.at(new_x, new_y)
|
||||
if point and point.walkable:
|
||||
self.entity.pos = (new_x, new_y)
|
||||
self.last_move = current_time
|
||||
|
||||
# Setup
|
||||
controller = MovementController(player)
|
||||
mcrfpy.keypressScene(controller.handle_key)
|
||||
|
||||
def game_update(dt):
|
||||
import time
|
||||
controller.update(time.time() * 1000)
|
||||
|
||||
mcrfpy.setTimer("movement", game_update, 16) # ~60fps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entity Selection
|
||||
|
||||
Click to select entities, show selection indicator.
|
||||
|
||||
```python
|
||||
class SelectionManager:
|
||||
def __init__(self, grid, overlay_layer):
|
||||
self.grid = grid
|
||||
self.overlay = overlay_layer
|
||||
self.selected = None
|
||||
|
||||
def select(self, entity):
|
||||
# Clear previous selection
|
||||
if self.selected:
|
||||
self._clear_indicator(self.selected)
|
||||
|
||||
self.selected = entity
|
||||
|
||||
if entity:
|
||||
self._draw_indicator(entity)
|
||||
|
||||
def _draw_indicator(self, entity):
|
||||
x, y = entity.x, entity.y
|
||||
self.overlay.set(x, y, mcrfpy.Color(255, 200, 0, 80))
|
||||
|
||||
def _clear_indicator(self, entity):
|
||||
x, y = entity.x, entity.y
|
||||
self.overlay.set(x, y, mcrfpy.Color(0, 0, 0, 0))
|
||||
|
||||
def update_indicator(self):
|
||||
"""Call after selected entity moves."""
|
||||
if self.selected:
|
||||
# Clear all overlay first (simple approach)
|
||||
self.overlay.fill(mcrfpy.Color(0, 0, 0, 0))
|
||||
self._draw_indicator(self.selected)
|
||||
|
||||
selection = SelectionManager(grid, overlay)
|
||||
|
||||
def on_cell_click(x, y, button):
|
||||
if button == 0: # Left click
|
||||
# Find entity at position
|
||||
for entity in grid.entities:
|
||||
if entity.x == x and entity.y == y:
|
||||
selection.select(entity)
|
||||
return
|
||||
# Clicked empty cell - deselect
|
||||
selection.select(None)
|
||||
|
||||
grid.on_cell_click = on_cell_click
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Path Preview
|
||||
|
||||
Show pathfinding path on hover.
|
||||
|
||||
```python
|
||||
class PathPreview:
|
||||
def __init__(self, grid, highlight_layer, source_entity):
|
||||
self.grid = grid
|
||||
self.highlight = highlight_layer
|
||||
self.source = source_entity
|
||||
self.current_path = []
|
||||
|
||||
def show_path_to(self, target_x, target_y):
|
||||
# Clear previous path
|
||||
self.clear()
|
||||
|
||||
# Calculate path
|
||||
path = self.source.path_to((target_x, target_y))
|
||||
if not path:
|
||||
return
|
||||
|
||||
self.current_path = path
|
||||
|
||||
# Draw path cells
|
||||
for i, (x, y) in enumerate(path):
|
||||
if i == 0:
|
||||
continue # Skip source cell
|
||||
alpha = 100 - (i * 5) # Fade with distance
|
||||
alpha = max(30, alpha)
|
||||
self.highlight.set(x, y, mcrfpy.Color(100, 200, 255, alpha))
|
||||
|
||||
def clear(self):
|
||||
for x, y in self.current_path:
|
||||
self.highlight.set(x, y, mcrfpy.Color(0, 0, 0, 0))
|
||||
self.current_path = []
|
||||
|
||||
path_preview = PathPreview(grid, highlight, player)
|
||||
|
||||
def on_cell_enter(x, y):
|
||||
path_preview.show_path_to(x, y)
|
||||
|
||||
def on_cell_exit(x, y):
|
||||
pass # Path updates on enter, so no action needed
|
||||
|
||||
grid.on_cell_enter = on_cell_enter
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Context Menu
|
||||
|
||||
Right-click to show context-sensitive options.
|
||||
|
||||
```python
|
||||
class ContextMenu:
|
||||
def __init__(self, scene_ui):
|
||||
self.ui = scene_ui
|
||||
self.frame = None
|
||||
self.options = []
|
||||
|
||||
def show(self, x, y, options):
|
||||
"""
|
||||
options: list of (label, callback) tuples
|
||||
x, y: screen coordinates
|
||||
"""
|
||||
self.close() # Close any existing menu
|
||||
|
||||
height = len(options) * 24 + 8
|
||||
self.frame = mcrfpy.Frame(pos=(x, y), size=(120, height))
|
||||
self.frame.fill_color = mcrfpy.Color(40, 40, 50)
|
||||
self.frame.outline = 1
|
||||
self.frame.outline_color = mcrfpy.Color(80, 80, 100)
|
||||
self.frame.z_index = 500
|
||||
self.ui.append(self.frame)
|
||||
|
||||
for i, (label, callback) in enumerate(options):
|
||||
item = mcrfpy.Frame(pos=(2, 2 + i * 24), size=(116, 22))
|
||||
item.fill_color = mcrfpy.Color(40, 40, 50)
|
||||
self.frame.children.append(item)
|
||||
|
||||
text = mcrfpy.Caption(pos=(8, 3), text=label)
|
||||
text.fill_color = mcrfpy.Color(200, 200, 200)
|
||||
item.children.append(text)
|
||||
|
||||
# Hover effect
|
||||
item.on_enter = lambda i=item: setattr(i, 'fill_color', mcrfpy.Color(60, 60, 80))
|
||||
item.on_exit = lambda i=item: setattr(i, 'fill_color', mcrfpy.Color(40, 40, 50))
|
||||
|
||||
# Click handler
|
||||
def make_handler(cb):
|
||||
return lambda x, y, btn: (cb(), self.close())
|
||||
item.on_click = make_handler(callback)
|
||||
|
||||
def close(self):
|
||||
if self.frame:
|
||||
self.ui.remove(self.frame)
|
||||
self.frame = None
|
||||
|
||||
context_menu = ContextMenu(ui)
|
||||
|
||||
def on_cell_click(x, y, button):
|
||||
if button == 1: # Right click
|
||||
# Find entity at position
|
||||
target = None
|
||||
for entity in grid.entities:
|
||||
if entity.x == x and entity.y == y:
|
||||
target = entity
|
||||
break
|
||||
|
||||
# Build context menu
|
||||
mouse_x, mouse_y = mcrfpy.getMousePos()
|
||||
|
||||
if target and target != player:
|
||||
options = [
|
||||
("Examine", lambda: examine(target)),
|
||||
("Attack", lambda: attack(target)),
|
||||
("Talk", lambda: talk(target)),
|
||||
]
|
||||
else:
|
||||
point = grid.at(x, y)
|
||||
options = [
|
||||
("Move here", lambda: player.pos.__setitem__(slice(None), (x, y))),
|
||||
("Examine ground", lambda: examine_cell(x, y)),
|
||||
]
|
||||
|
||||
context_menu.show(mouse_x, mouse_y, options)
|
||||
|
||||
elif button == 0: # Left click closes menu
|
||||
context_menu.close()
|
||||
|
||||
grid.on_cell_click = on_cell_click
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tile Inspector Panel
|
||||
|
||||
Click cell to show information panel.
|
||||
|
||||
```python
|
||||
class TileInspector:
|
||||
def __init__(self, parent, pos):
|
||||
self.frame = mcrfpy.Frame(pos=pos, size=(200, 150))
|
||||
self.frame.fill_color = mcrfpy.Color(30, 30, 40, 230)
|
||||
self.frame.outline = 1
|
||||
self.frame.outline_color = mcrfpy.Color(80, 80, 100)
|
||||
self.frame.visible = False
|
||||
parent.append(self.frame)
|
||||
|
||||
self.title = mcrfpy.Caption(pos=(10, 8), text="Cell Info")
|
||||
self.title.fill_color = mcrfpy.Color(180, 180, 200)
|
||||
self.frame.children.append(self.title)
|
||||
|
||||
self.info_lines = []
|
||||
for i in range(5):
|
||||
line = mcrfpy.Caption(pos=(10, 30 + i * 20), text="")
|
||||
line.fill_color = mcrfpy.Color(160, 160, 180)
|
||||
self.frame.children.append(line)
|
||||
self.info_lines.append(line)
|
||||
|
||||
def show(self, grid, x, y):
|
||||
point = grid.at(x, y)
|
||||
|
||||
self.title.text = f"Cell ({x}, {y})"
|
||||
self.info_lines[0].text = f"Walkable: {point.walkable}"
|
||||
self.info_lines[1].text = f"Transparent: {point.transparent}"
|
||||
|
||||
# Count entities at this position
|
||||
entities_here = [e for e in grid.entities if e.x == x and e.y == y]
|
||||
self.info_lines[2].text = f"Entities: {len(entities_here)}"
|
||||
|
||||
if entities_here:
|
||||
self.info_lines[3].text = f" {entities_here[0].name or 'unnamed'}"
|
||||
else:
|
||||
self.info_lines[3].text = ""
|
||||
|
||||
self.info_lines[4].text = ""
|
||||
|
||||
self.frame.visible = True
|
||||
|
||||
def hide(self):
|
||||
self.frame.visible = False
|
||||
|
||||
inspector = TileInspector(ui, (700, 50))
|
||||
|
||||
def on_cell_click(x, y, button):
|
||||
if button == 0:
|
||||
inspector.show(grid, x, y)
|
||||
|
||||
grid.on_cell_click = on_cell_click
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [[Entity-Management]] - Entity behavior and pathfinding
|
||||
- [[Grid-System]] - Layer management and rendering
|
||||
- [[UI-Widget-Patterns]] - Non-grid UI patterns
|
||||
- [[Input-and-Events]] - Event API reference
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2025-11-29*
|
||||
Loading…
Add table
Add a link
Reference in a new issue