LevelLoader.gd 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. extends Node
  2. var entity_map := {}
  3. @onready var editor: LevelEditor = owner
  4. const base64_charset := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  5. @onready var level: Level = $"../Level"
  6. @onready var level_bg: LevelBG = $"../Level/LevelBG"
  7. var sub_level_file = null
  8. func _ready() -> void:
  9. load_entity_map()
  10. func load_level(level_idx := 0) -> void:
  11. clear_level()
  12. sub_level_file = editor.level_file["Levels"][level_idx]
  13. build_level()
  14. func clear_level() -> void:
  15. for layer in 5:
  16. for i in editor.entity_layer_nodes[layer].get_children():
  17. if i is Player:
  18. reset_player(i)
  19. continue
  20. i.queue_free()
  21. var connect_tiles = editor.tile_layer_nodes[layer].get_used_cells_by_id(0, Vector2i(13, 8))
  22. editor.tile_layer_nodes[layer].clear()
  23. for i in connect_tiles:
  24. editor.tile_layer_nodes[layer].set_cell(i, 0, Vector2i(13, 8))
  25. editor.entity_tiles = [{}, {}, {}, {}, {}]
  26. func load_entity_map() -> void:
  27. entity_map = JSON.parse_string(FileAccess.open(EntityIDMapper.MAP_PATH, FileAccess.READ).get_as_text())
  28. func build_level() -> void:
  29. if sub_level_file.is_empty():
  30. return
  31. var layer_id := 0
  32. for layer in sub_level_file["Layers"]:
  33. for chunk_id in layer:
  34. var chunk = layer[chunk_id]
  35. add_tiles(LevelSaver.decompress_string(chunk["Tiles"]), int(chunk_id), int(layer_id))
  36. add_entities(LevelSaver.decompress_string(chunk["Entities"]), int(chunk_id), int(layer_id))
  37. layer_id += 1
  38. apply_level_data(sub_level_file["Data"])
  39. apply_bg_data(sub_level_file["BG"])
  40. func add_tiles(chunk := "", chunk_id := 0, layer := 0) -> void:
  41. for tile in chunk.split("=", false):
  42. var tile_position := Vector2i.ZERO
  43. var tile_atlas_position := Vector2i.ZERO
  44. var source_id := 0
  45. tile_position = decode_tile_position_from_chars(tile[0], tile[1], chunk_id)
  46. source_id = base64_charset.find(tile[4])
  47. tile_atlas_position = Vector2i(base64_charset.find(tile[2]), base64_charset.find(tile[3]))
  48. editor.tile_layer_nodes[layer].set_cell(tile_position, source_id, tile_atlas_position)
  49. func add_entities(chunk := "", chunk_id := 0, layer := 0) -> void:
  50. for entity in chunk.split("=", false):
  51. var entity_id = entity.get_slice(",", 1)
  52. var entity_chunk_position = entity.get_slice(",", 0)
  53. var entity_tile_position = decode_tile_position_from_chars(entity_chunk_position[0], entity_chunk_position[1], chunk_id)
  54. var entity_node: Node = null
  55. if entity_map.has(entity_id) == false:
  56. Global.log_error("MISSING ENTITY ID!!!! JOE FORGOT TO UPDATE THE MAP AGAIN :(")
  57. continue
  58. if entity_map[entity_id][0] != "res://Scenes/Prefabs/Entities/Player.tscn":
  59. entity_node = load(entity_map[entity_id][0]).instantiate()
  60. else:
  61. entity_node = get_tree().get_first_node_in_group("Players")
  62. var offset = entity_map[entity_id][1].split(",")
  63. entity_node.global_position = entity_tile_position * 16 + (Vector2i(8, 8) + Vector2i(int(offset[0]), int(offset[1])))
  64. editor.entity_layer_nodes[layer].add_child(entity_node)
  65. entity_node.reset_physics_interpolation()
  66. entity_node.owner = editor
  67. editor.entity_tiles[layer][entity_tile_position] = entity_node
  68. entity_node.set_meta("tile_position", entity_tile_position)
  69. entity_node.set_meta("tile_offset", Vector2(int(offset[0]), int(offset[1])))
  70. if entity_node.has_node("EditorPropertyExposer"):
  71. entity_node.get_node("EditorPropertyExposer").apply_string(entity)
  72. func reset_player(player: Player) -> void: ## Function literally here to just reset the player back to default starting, if loading into a level file, that hasnt been written yet (pipes)
  73. player.show()
  74. player.state_machine.transition_to("Normal")
  75. player.global_position = Vector2(-232, 0)
  76. func gzip_encode(text: String) -> String:
  77. var bytes = Marshalls.base64_to_raw(text)
  78. bytes.compress(FileAccess.COMPRESSION_GZIP)
  79. return Marshalls.raw_to_base64(bytes)
  80. func gzip_decode(text: String) -> String:
  81. var bytes = Marshalls.base64_to_raw(text)
  82. bytes.decompress_dynamic(-1, FileAccess.COMPRESSION_GZIP)
  83. return Marshalls.raw_to_base64(bytes)
  84. func apply_level_data(data := "") -> void:
  85. var split = data.split("=")
  86. var values := []
  87. for i in split:
  88. if i.length() == 2:
  89. values.append(decode_from_base64_2char(i))
  90. elif i.length() == 1:
  91. values.append(base64_charset.find(i))
  92. else:
  93. values.append(i)
  94. level.theme = Level.THEME_IDXS[values[0]]
  95. Global.level_theme = level.theme
  96. level.theme_time = ["Day", "Night"][values[1]]
  97. Global.theme_time = level.theme_time
  98. editor.bgm_id = values[2]
  99. level.campaign = ["SMB1", "SMBLL", "SMBS", "SMBANN"][values[3]]
  100. Global.current_campaign = level.campaign
  101. level.can_backscroll = bool(values[4])
  102. level.vertical_height = -int(values[5])
  103. level.time_limit = int(values[6])
  104. %ThemeTime.selected = values[1]
  105. %LevelMusic.selected = values[2]
  106. %Campaign.selected = values[3]
  107. %BackScroll.set_pressed_no_signal(bool(values[4]))
  108. %HeightLimit.value = values[5]
  109. %TimeLimit.value = values[6]
  110. %SubLevelID.selected = editor.sub_level_id
  111. ResourceSetterNew.cache.clear()
  112. Global.level_theme_changed.emit()
  113. func apply_bg_data(data := "") -> void:
  114. var split = data.split("=", false)
  115. var id := 0
  116. const BG_VALUES := ["primary_layer", "second_layer", "second_layer_offset", "time_of_day", "particles", "liquid_layer", "overlay_clouds"]
  117. var SELECTORS = [%PrimaryLayer, %SecondLayer, %SecondLayerOffset, %TimeOfDay, %Particles, %LiquidLayer, %OverlayClouds]
  118. for i in split:
  119. var value := 0
  120. if i.length() > 1:
  121. value = (decode_from_base64_2char(i))
  122. else:
  123. value = (base64_charset.find(i))
  124. if SELECTORS[id] is SpinBox:
  125. SELECTORS[id].value = value
  126. elif SELECTORS[id] is Button:
  127. SELECTORS[id].set_pressed_no_signal(bool(value))
  128. else:
  129. SELECTORS[id].selected = value
  130. level_bg.set_value(value, BG_VALUES[id])
  131. id += 1
  132. func decode_tile_position_from_chars(char_x: String, char_y: String, chunk_idx: int) -> Vector2i:
  133. var local_x = base64_charset.find(char_x)
  134. var local_y = base64_charset.find(char_y)
  135. return Vector2i(local_x + (chunk_idx * 32), local_y - 30)
  136. func decode_from_base64_2char(encoded: String) -> int:
  137. if encoded.length() != 2:
  138. push_error("Encoded string must be exactly 2 characters.")
  139. return -1
  140. var idx1 = base64_charset.find(encoded[0])
  141. var idx2 = base64_charset.find(encoded[1])
  142. if idx1 == -1 or idx2 == -1:
  143. push_error("Invalid character in base64 string.")
  144. return -1
  145. return (idx1 << 6) | idx2
  146. func tile_to_chunk_idx(tile_position := Vector2i.ZERO) -> int:
  147. return floor(tile_position.x / 32.0)