@tool extends Node3D class_name LevelInstance @export_tool_button("视野锁定", "Callable") var view_focus_on: Callable = func() -> void: grid_block_material.set_shader_parameter("is_focus", true) set_focus_editor() @export_tool_button("视野解锁", "Callable") var view_focus_off: Callable = func() -> void: grid_block_material.set_shader_parameter("is_focus", false) set_focus() @export var size: Vector3i = Vector3i.ONE: set(value): if (value.x < 1) or (value.y < 1) or (value.z < 1): value = Vector3i.ONE size = value _on_size_change() @export var is_force_battle: bool @export var enemy_nums: Array[LevelEnemy] @export_group("快速操作") @export_tool_button("清空", "Callable") var remove_all: Callable = func() -> void: var level_main: GridMap = %LevelGridMain as GridMap var id_min: Vector3i = Util.get_level_grid_pos2(pos_min()) var id_max: Vector3i = Util.get_level_grid_pos2(pos_max()) for x in range(id_min.x, id_max.x + 1): for y in range(id_min.y, id_max.y + 1): for z in range(id_min.z, id_max.z + 1): var pos: Vector3i = Vector3i(x, y, z) level_main.set_cell_item(pos, GridMap.INVALID_CELL_ITEM) @export_tool_button("快速创建", "Callable") var quick_build: Callable = func() -> void: var level_main: GridMap = %LevelGridMain as GridMap var id_min: Vector3i = Util.get_level_grid_pos2(pos_min()) var id_max: Vector3i = Util.get_level_grid_pos2(pos_max()) for x in range(id_min.x, id_max.x + 1): for y in range(id_min.y, id_max.y + 1): for z in range(id_min.z, id_max.z + 1): var pos: Vector3i = Vector3i(x, y, z) if x == id_min.x or x == id_max.x or y == id_min.y or z == id_min.z: level_main.set_cell_item(pos, 2) @export_group("替换item_id") @export var id_from: int = 0 @export var id_to: int = 0 @export_tool_button("替换", "Callable") var replace_id: Callable = func() -> void: var level_main: GridMap = %LevelGridMain as GridMap var id_min: Vector3i = Util.get_level_grid_pos2(pos_min()) var id_max: Vector3i = Util.get_level_grid_pos2(pos_max()) for x in range(id_min.x, id_max.x + 1): for y in range(id_min.y, id_max.y + 1): for z in range(id_min.z, id_max.z + 1): var pos: Vector3i = Vector3i(x, y, z) if level_main.get_cell_item(pos) == id_from: level_main.set_cell_item(pos, id_to, level_main.get_cell_item_orientation(pos)) @export_tool_button("打印item详情", "Callable") var print_item: Callable = func() -> void: var level_main: GridMap = %LevelGridMain as GridMap var id_min: Vector3i = Util.get_level_grid_pos2(pos_min()) var id_max: Vector3i = Util.get_level_grid_pos2(pos_max()) var mesh_library: MeshLibrary = level_main.get_mesh_library() var item_count_map: Dictionary = {} for x in range(id_min.x, id_max.x + 1): for y in range(id_min.y, id_max.y + 1): for z in range(id_min.z, id_max.z + 1): var pos: Vector3i = Vector3i(x, y, z) var item_id: int = level_main.get_cell_item(pos) if item_id != GridMap.INVALID_CELL_ITEM: if item_count_map.has(item_id): item_count_map[item_id] += 1 else: item_count_map[item_id] = 1 for item_id: int in item_count_map: print("[", item_id, "]", mesh_library.get_item_name(item_id), ":", item_count_map[item_id]) @onready var grid_block_material: Material = load("res://render/material/level_grid_block.tres") as Material #character var level_area_cfg: LevelAreaCfg var character_spots: Array[ChacacterSpot] var character_born_spots: Array[ChacacterBornSpot] var level_active_spots: Array[LevelActiveSpot] var enemy_cfgs: Array[CharacterCfg] #main var mark_white_list: Array[Vector3i] var mark_black_list: Array[Vector3i] var grid_main_cache_cells: Array[GridMainCacheCellData] class ChacacterSpot: var pos: Vector3 var cfg: CharacterCfg class ChacacterBornSpot: var pos: Vector3 var type: int class LevelActiveSpot: var pos: Vector3 var scene: PackedScene class GridMainCacheCellData: var pos: Vector3i var id: int var orientation: int func _on_size_change() -> void: var level_range: CSGBox3D = $LevelRange as CSGBox3D var size_basic: Vector3 = Setting.size_basic level_range.size = Vector3(size.x * size_basic.x, size.y * size_basic.y, size.z * size_basic.z) level_range.position = level_range.size / 2 func get_level_size() -> Vector2: var level_range: CSGBox3D = $LevelRange as CSGBox3D var level_size: Vector3 = level_range.size return Vector2(level_size.x, level_size.z) func get_level_pos() -> Vector2: var pos: Vector3 = get_global_position() return Vector2(pos.x, pos.z) func set_level_range_visible(value: bool) -> void: var level_range: CSGBox3D = $LevelRange as CSGBox3D level_range.visible = value func set_focus_editor() -> void: var level_range: CSGBox3D = $LevelRange as CSGBox3D var level_size: Vector3 = level_range.size var focus_min: Vector3 = get_global_position() var focus_max: Vector3 = get_global_position() + level_size - Vector3(0, Setting.pixel_size * Setting.grid_pixel_size, Setting.pixel_size * Setting.grid_pixel_size) grid_block_material.set_shader_parameter("focus_min", focus_min) grid_block_material.set_shader_parameter("focus_max", focus_max) func set_focus() -> void: var level_range: CSGBox3D = $LevelRange as CSGBox3D var level_size: Vector3 = level_range.size var focus_min: Vector3 = get_global_position() var focus_max: Vector3 = get_global_position() + level_size + Vector3(0, Setting.pixel_size * Setting.grid_pixel_size, Setting.pixel_size * Setting.grid_pixel_size) grid_block_material.set_shader_parameter("focus_min", focus_min) grid_block_material.set_shader_parameter("focus_max", focus_max) func init_level_character() -> void: var level_character: GridMap = %LevelGridCharacter as GridMap character_spots = [] character_born_spots = [] level_active_spots = [] enemy_cfgs = [] var id_min: Vector3i = Util.get_level_grid_pos2(pos_min()) var id_max: Vector3i = Util.get_level_grid_pos2(pos_max()) var mesh_library: MeshLibrary = level_character.get_mesh_library() var used_cells: Array[Vector3i] = level_character.get_used_cells() for pos in used_cells: if pos.x < id_min.x or pos.x > id_max.x: continue if pos.y < id_min.y or pos.y > id_max.y: continue if pos.z < id_min.z or pos.z > id_max.z: continue var float_pos: Vector3 = Util.get_level_float_pos2_floor(pos) var item_id: int = level_character.get_cell_item(pos) if item_id == GridMap.INVALID_CELL_ITEM: continue var item_name: String = mesh_library.get_item_name(item_id) if item_name.begins_with("chara_spot_"): var character_born_spot = ChacacterBornSpot.new() character_born_spot.pos = float_pos character_born_spots.append(character_born_spot) elif item_name.begins_with("chara_enemy_"): var character_name: String = item_name.trim_prefix("chara_enemy_") var character_cfg: CharacterCfg = Util.get_character_cfg_by_name(character_name) if not character_cfg: continue var character_spot = ChacacterSpot.new() character_spot.cfg = character_cfg character_spot.pos = float_pos character_spots.append(character_spot) elif item_name.begins_with("chara_active_"): var scene_name: String = item_name.trim_prefix("chara_active_") var scene: PackedScene = Util.get_level_active_scene_by_name(scene_name) if not scene: continue var level_active_spot = LevelActiveSpot.new() level_active_spot.scene = scene level_active_spot.pos = float_pos level_active_spots.append(level_active_spot) for enemy_num: LevelEnemy in enemy_nums: for i in enemy_num.num: enemy_cfgs.append(enemy_num.cfg) func init_level_main() -> void: var level_main: GridMap = %LevelGridMain as GridMap mark_white_list.clear() mark_black_list.clear() grid_main_cache_cells.clear() var id_min: Vector3i = Util.get_level_grid_pos2(pos_min()) var id_max: Vector3i = Util.get_level_grid_pos2(pos_max()) # 需要延申空气墙的黑块 var air_wall_mark_black_list: Array[Vector3i] = [] # 正前面黑块 for x in range(id_min.x, id_max.x + 1): var pos: Vector3i = Vector3i(x, id_min.y, id_max.z) var has_link = false var mark_black_list_check: Array[Vector3i] = [] for y in range(id_min.y, id_max.y - 1): var pos_check: Vector3i = Vector3i(x, y + 1, id_max.z) if not check_grid(level_main, pos_check): var pos_forward: Vector3i = Vector3i(x, y + 1, id_max.z + 1) var float_pos_check: Vector3 = Util.get_level_float_pos2(pos_forward) if Global.level_mgr.get_level_by_pos(float_pos_check): if y + 1 < id_max.y: air_wall_mark_black_list.append(pos_check + Vector3i(0, 1, 0)) has_link = true break mark_black_list_check.append(Vector3i(x, y, id_max.z)) if has_link: pass # for pos_check in mark_black_list_check: # mark_black_list.append(pos_check) else: air_wall_mark_black_list.append(pos) mark_black_list.append(pos) # 左面黑块 var black_y_max_left: int = id_max.y - 1 for z in range(id_min.z + 1, id_max.z): var has_link = false var mark_black_list_check: Array[Vector3i] = [] for y in range(id_min.y - 1, black_y_max_left): var pos_check: Vector3i = Vector3i(id_min.x, y + 1, z) if not check_grid(level_main, pos_check): has_link = true black_y_max_left = max(y, id_min.y) if y + 1 < id_max.y: air_wall_mark_black_list.append(pos_check + Vector3i(0, 1, 0)) break mark_black_list_check.append(pos_check) if not has_link: for pos_check in mark_black_list_check: mark_black_list.append(pos_check) var pos_check: Vector3i = Vector3i(id_min.x, black_y_max_left, z) air_wall_mark_black_list.append(pos_check) # 右边面黑块 var black_y_max_right: int = id_max.y - 1 for z in range(id_min.z + 1, id_max.z): var has_link = false var mark_black_list_check: Array[Vector3i] = [] for y in range(id_min.y - 1, black_y_max_right): var pos_check: Vector3i = Vector3i(id_max.x, y + 1, z) if not check_grid(level_main, pos_check): has_link = true black_y_max_right = max(y, id_min.y) if y + 1 < id_max.y: air_wall_mark_black_list.append(pos_check + Vector3i(0, 1, 0)) break mark_black_list_check.append(pos_check) if not has_link: for pos_check in mark_black_list_check: mark_black_list.append(pos_check) var pos_check: Vector3i = Vector3i(id_max.x, black_y_max_right, z) air_wall_mark_black_list.append(pos_check) # 黑块cache for mark_black in mark_black_list: add_grid_main_cache_cell(level_main, mark_black) # 黑块延申空气墙 for mark_black in air_wall_mark_black_list: for y in range(mark_black.y + 1, id_max.y): var pos: Vector3i = Vector3i(mark_black.x, y, mark_black.z) add_grid_main_cache_cell(level_main, pos) mark_white_list.append(pos) # 顶面 for x in range(id_min.x, id_max.x + 1): for z in range(id_min.z, id_max.z + 1): var pos: Vector3i = Vector3i(x, id_max.y, z) var pos_up: Vector3i = Vector3i(x, id_max.y + 1, z) var pos_down: Vector3i = Vector3i(x, id_max.y - 1, z) var is_link = not check_grid(level_main, pos) and not check_grid(level_main, pos_up) and not check_grid(level_main, pos_down) if is_link and not pos_down in mark_white_list: var float_pos_check: Vector3 = Util.get_level_float_pos2(pos_up) if Global.level_mgr.get_level_by_pos(float_pos_check): continue add_grid_main_cache_cell(level_main, pos) if pos_down in mark_white_list: mark_white_list.append(pos) else: if check_grid(level_main, pos_down): mark_black_list.append(pos) else: mark_white_list.append(pos) func check_grid(level_main: GridMap, pos: Vector3i) -> bool: var id: int = level_main.get_cell_item(pos) return id != GridMap.INVALID_CELL_ITEM func add_grid_main_cache_cell(level_main: GridMap, pos: Vector3i) -> void: var id: int = level_main.get_cell_item(pos) if id == GridMap.INVALID_CELL_ITEM: return var cache_data = GridMainCacheCellData.new() cache_data.pos = pos cache_data.id = id cache_data.orientation = level_main.get_cell_item_orientation(pos) grid_main_cache_cells.append(cache_data) func set_replace(is_active: bool) -> void: var level_main: GridMap = %LevelGridMain as GridMap if is_active: for pos in mark_white_list: level_main.set_cell_item(pos, 0) for pos in mark_black_list: level_main.set_cell_item(pos, 1) else: for pos in mark_white_list: level_main.set_cell_item(pos, GridMap.INVALID_CELL_ITEM) for pos in mark_black_list: level_main.set_cell_item(pos, GridMap.INVALID_CELL_ITEM) for cache_data in grid_main_cache_cells: level_main.set_cell_item(cache_data.pos, cache_data.id, cache_data.orientation) func get_character_spots() -> Array[ChacacterSpot]: return character_spots func get_character_born_spots() -> Array[ChacacterBornSpot]: return character_born_spots func get_enemy_cfgs() -> Array[CharacterCfg]: return enemy_cfgs func get_level_active_spots() -> Array[LevelActiveSpot]: return level_active_spots func pos_min() -> Vector3: return get_global_position() func pos_max() -> Vector3: var level_range: CSGBox3D = $LevelRange as CSGBox3D var level_size: Vector3 = level_range.size return get_global_position() + level_size func outter_pos_min() -> Vector3: return pos_min() - Vector3(0.72, 0.72, 0.72) func outter_pos_max() -> Vector3: return pos_max() - Vector3(0.72, 0.72, 0.72) func is_in_active_area(pos: Vector3) -> bool: var active_pos_min: Vector3 = pos_min() + Vector3(0.96, 0, 0.96) var active_pos_max: Vector3 = pos_max() - Vector3(0.96, 0, 0.96) return pos.x >= active_pos_min.x and pos.x <= active_pos_max.x and \ pos.z >= active_pos_min.z and pos.z <= active_pos_max.z and \ pos.y >= active_pos_min.y and pos.y <= active_pos_max.y