Skip to content
bozar42 edited this page Apr 22, 2020 · 3 revisions

06: Restrict Movement

Source code.

Detecting and handling collision is the most complicated part in the demo. Thus we divide it into two chapters. In this chapter, we restrict PC's movement inside the dungeon and outside walls. An invalid movement does not end PC's current turn. In Chapter 7, PC will be able to bump attack NPCs. PC's turn ends after attacking. We also need to remove dead NPCs. Below is the screenshot for Chapter 6.

IMAGE: Chapter 6 screenshot

Forbid PC To Leave The Dungeon

Check out commit: 54dc879.

First, comment print() lines in PCMove.gd and Schedule.gd so as not to fill the output board with unwanted text. Add DungeonBoard (Node2D node) to MainScene and attach DungeonBoard.gd to it.

# DungeonBoard.gd

func is_inside_dungeon(x: int, y: int) -> bool:
    return (x > -1) and (x < _new_DungeonSize.MAX_X) \
            and (y > -1) and (y < _new_DungeonSize.MAX_Y)

Let PCMove._try_move() call DungeonBoard.is_inside_dungeon() to forbid invalid movement.

# PCMove.gd

func _unhandled_input(event: InputEvent) -> void:
    var source: Array = _new_ConvertCoord.vector_to_array(_pc.position)
    var target: Array

    if _is_move_input(event):
        target = _get_new_position(event, source)
        _try_move(target[0], target[1])


func _try_move(x: int, y: int) -> void:
    if not _ref_DungeonBoard.is_inside_dungeon(x, y):
        print("Cannot leave dungeon.")
    else:
        set_process_unhandled_input(false)
        _pc.position = _new_ConvertCoord.index_to_vector(x, y)
        _ref_Schedule.end_turn()

Forbid PC To Pass Through Walls

DungeonBoard.gd does more things than analyzing coordinates. Just like a chess board records the position of every chess piece, DungeonBoard records every dungeon sprite's position. We use a dictionary to mimic a 2D array. The dictionary's key is an integer representing the row, and its value is an array filled with sprites. Since we need to record two types of sprites, Wall and Dwarf, there should be two such boards. In conclusion, the data structure of DungeonBoard._sprite_dict is like this:

_sprite_dict: Dictionary<group_name: String,
        Dictionary<row: int, sprites: Array>>

In DungeonBoard.gd, first we initialize the dictionary. Then fill it with sprites by receiving sprite_created. The last step is to add two public methods, has_sprite() and get_sprite(), to inspect its content.

# DungeonBoard.gd

var _sprite_dict: Dictionary


func _ready() -> void:
    _init_dict()


func has_sprite(group_name: String, x: int, y: int) -> bool:
    return get_sprite(group_name, x, y) != null


func get_sprite(group_name: String, x: int, y: int) -> Sprite:
    if not is_inside_dungeon(x, y):
        return null
    return _sprite_dict[group_name][x][y]


func _on_InitWorld_sprite_created(new_sprite: Sprite) -> void:
    var pos: Array
    var group: String

    if new_sprite.is_in_group(_new_GroupName.DWARF):
        group = _new_GroupName.DWARF
    elif new_sprite.is_in_group(_new_GroupName.WALL):
        group = _new_GroupName.WALL
    else:
        return

    pos = _new_ConvertCoord.vector_to_array(new_sprite.position)
    _sprite_dict[group][pos[0]][pos[1]] = new_sprite


func _init_dict() -> void:
    var groups = [_new_GroupName.DWARF, _new_GroupName.WALL]

    for g in groups:
        _sprite_dict[g] = {}
        for x in range(_new_DungeonSize.MAX_X):
            _sprite_dict[g][x] = []
            _sprite_dict[g][x].resize(_new_DungeonSize.MAX_Y)

Let PCMove._try_move() to detect wall sprites.

# PCMove.gd

func _try_move(x: int, y: int) -> void:
    if not _ref_DungeonBoard.is_inside_dungeon(x, y):
        print("Cannot leave dungeon.")
    elif _ref_DungeonBoard.has_sprite(_new_GroupName.WALL, x, y):
        print("Cannot pass wall.")
    else:
        # The rest remains unchanged.

Printing text to the output panel is convenient for developers, while not so obvious to players. We will change this in Chapter 8.

Clone this wiki locally