206 lines
6.4 KiB
GDScript3
206 lines
6.4 KiB
GDScript3
|
|
extends Area2D
|
||
|
|
|
||
|
|
# GLOBAL CFG
|
||
|
|
|
||
|
|
@onready var fade_rect: ColorRect = get_node("../Fader/ColorRect")
|
||
|
|
|
||
|
|
func fade_out(duration := 1.0):
|
||
|
|
fade_rect.visible = true
|
||
|
|
fade_rect.modulate.a = 0.0
|
||
|
|
var tween = get_tree().create_tween()
|
||
|
|
tween.tween_property(fade_rect, "modulate:a", 1.0, duration)
|
||
|
|
|
||
|
|
func fade_in(duration := 1.0):
|
||
|
|
fade_rect.visible = true
|
||
|
|
fade_rect.modulate.a = 1.0
|
||
|
|
var tween = get_tree().create_tween()
|
||
|
|
tween.tween_property(fade_rect, "modulate:a", 0.0, duration)
|
||
|
|
tween.tween_callback(Callable(self, "_on_fade_in_complete"))
|
||
|
|
|
||
|
|
func wait(seconds: float) -> void:
|
||
|
|
await get_tree().create_timer(seconds).timeout
|
||
|
|
|
||
|
|
func _on_fade_in_complete():
|
||
|
|
fade_rect.visible = false
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
const scalef = 3;
|
||
|
|
const textsize = 16;
|
||
|
|
|
||
|
|
func load_json(path: String): # stolen from reddit then updated to godot 4 lmao
|
||
|
|
var file := FileAccess.open(path, FileAccess.READ)
|
||
|
|
var content = file.get_as_text()
|
||
|
|
file.close()
|
||
|
|
var parsed_json = JSON.parse_string(content)
|
||
|
|
return parsed_json
|
||
|
|
|
||
|
|
func loadstring(path: String): # stolen from reddit then updated to godot 4 lmao
|
||
|
|
var file := FileAccess.open(path, FileAccess.READ)
|
||
|
|
var content = file.get_as_text()
|
||
|
|
file.close()
|
||
|
|
return content
|
||
|
|
|
||
|
|
var rng = RandomNumberGenerator.new()
|
||
|
|
|
||
|
|
# Called when the node enters the scene tree for the first time.
|
||
|
|
func _ready() -> void:
|
||
|
|
await fade_in(1.0)
|
||
|
|
get_node("../Player").set_meta("can_walk",true)
|
||
|
|
for child in get_children(): child.queue_free()
|
||
|
|
var root = get_node("../")
|
||
|
|
var world = load_json("res://worlds/"+self.get_meta("world")+".json");
|
||
|
|
if world.has("mapscript"):
|
||
|
|
var script = loadstring("res://worlds/"+self.get_meta("world")+".lua")
|
||
|
|
get_node("../LuaEnv").reset_env()
|
||
|
|
get_node("../LuaEnv").run_script(script,self)
|
||
|
|
var assetcfg = load_json("res://assetcfg.json")
|
||
|
|
var texturespace = root.get_meta("resourcepack");
|
||
|
|
var worldassemble = {};
|
||
|
|
var plr = get_node("../Player")
|
||
|
|
plr.position = Vector2(world["pspawn"]["w"]*(16*scalef),world["pspawn"]["h"]*(16*scalef))
|
||
|
|
|
||
|
|
for y in range(world["height"]+1):
|
||
|
|
for x in range(world["width"]+1):
|
||
|
|
if not worldassemble.has(y):
|
||
|
|
worldassemble[y] = {}
|
||
|
|
worldassemble[y][x] = {"thing":world["fill"],"action":"none"}
|
||
|
|
for modifier in world["features"]:
|
||
|
|
var x = int(modifier["w"])
|
||
|
|
var y = int(modifier["h"])
|
||
|
|
worldassemble[y][x] = modifier["t"]
|
||
|
|
# assuming this script extends Node2D or Area2D that builds the world
|
||
|
|
for y in range(worldassemble.size()):
|
||
|
|
for x in range(worldassemble[y].size()):
|
||
|
|
var along = worldassemble[y][x]
|
||
|
|
var walpole = along["thing"]
|
||
|
|
var cfg = assetcfg[walpole]
|
||
|
|
|
||
|
|
# combine base data from cfg and instance-specific data from along
|
||
|
|
var tile_type = along.get("action", cfg.get("action", "default"))
|
||
|
|
var is_collidable = along.get("collidable", cfg.get("collidable", false))
|
||
|
|
var tex_path = "res://%s/text/%s" % [texturespace, cfg["textname"]]
|
||
|
|
var tex = load(tex_path)
|
||
|
|
|
||
|
|
# --- choose node type
|
||
|
|
var tile_node: Node2D
|
||
|
|
if tile_type == "tp":
|
||
|
|
tile_node = Area2D.new() # teleporter
|
||
|
|
elif is_collidable:
|
||
|
|
tile_node = StaticBody2D.new() # solid
|
||
|
|
else:
|
||
|
|
tile_node = Area2D.new() # non-collidable sensor/decor
|
||
|
|
|
||
|
|
add_child(tile_node)
|
||
|
|
tile_node.position = Vector2(x * (scalef * textsize), y * (scalef * textsize))
|
||
|
|
tile_node.name = "tile_%s_%s" % [x, y]
|
||
|
|
tile_node.set_meta("collidable", is_collidable)
|
||
|
|
|
||
|
|
# --- sprite
|
||
|
|
var sprite = Sprite2D.new()
|
||
|
|
sprite.texture = tex
|
||
|
|
sprite.scale = Vector2(scalef, scalef)
|
||
|
|
tile_node.add_child(sprite)
|
||
|
|
|
||
|
|
# optional random rotation
|
||
|
|
if cfg.get("allowRandRot", false):
|
||
|
|
sprite.rotation_degrees = 90 * rng.randi_range(0, 3)
|
||
|
|
|
||
|
|
# --- collision shape for solids and sensors
|
||
|
|
if is_collidable or tile_type == "tp":
|
||
|
|
var col = CollisionShape2D.new()
|
||
|
|
var shape = RectangleShape2D.new()
|
||
|
|
shape.size = Vector2(16 * scalef, 16 * scalef)
|
||
|
|
col.shape = shape
|
||
|
|
col.position = Vector2.ZERO
|
||
|
|
tile_node.add_child(col)
|
||
|
|
|
||
|
|
# --- teleporter setup
|
||
|
|
if tile_type == "tp":
|
||
|
|
var target_world = along.get("target_world", cfg.get("target_world", "default"))
|
||
|
|
tile_node.set_meta("world", target_world)
|
||
|
|
tile_node.connect("body_entered", Callable(self, "_on_tp_entered").bind(tile_node))
|
||
|
|
_add_world_borders(world["width"], world["height"])
|
||
|
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||
|
|
func _process(delta: float) -> void:
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
func _on_tp_entered(body: Node, source: Node) -> void:
|
||
|
|
if not body.is_in_group("player"):
|
||
|
|
return
|
||
|
|
|
||
|
|
print("Teleport triggered by:", body.name)
|
||
|
|
await fade_out(1.0)
|
||
|
|
get_node("../Player").set_meta("can_walk",false)
|
||
|
|
await wait(1.0)
|
||
|
|
if body.has_method("set_velocity"):
|
||
|
|
body.set_velocity(Vector2.ZERO)
|
||
|
|
|
||
|
|
if source.has_meta("world"):
|
||
|
|
var new_world = source.get_meta("world")
|
||
|
|
print("Teleporting to world:", new_world)
|
||
|
|
|
||
|
|
set_meta("world", new_world)
|
||
|
|
call_deferred("_ready") # safely rebuild
|
||
|
|
else:
|
||
|
|
print("⚠️ Teleporter node has no 'world' meta set!")
|
||
|
|
|
||
|
|
func _add_world_borders(width: int, height: int) -> void:
|
||
|
|
var border_thickness = 32.0 * scalef
|
||
|
|
var w_px = width * (textsize * scalef)
|
||
|
|
var h_px = height * (textsize * scalef)
|
||
|
|
|
||
|
|
var positions = {
|
||
|
|
"top": Vector2(w_px / 2, -border_thickness / 2),
|
||
|
|
"bottom": Vector2(w_px / 2, h_px + border_thickness / 2),
|
||
|
|
"left": Vector2(-border_thickness / 2, h_px / 2),
|
||
|
|
"right": Vector2(w_px + border_thickness / 2, h_px / 2)
|
||
|
|
}
|
||
|
|
|
||
|
|
for name in positions.keys():
|
||
|
|
var wall = StaticBody2D.new()
|
||
|
|
wall.name = "border_%s" % name
|
||
|
|
var shape = RectangleShape2D.new()
|
||
|
|
if name in ["top", "bottom"]:
|
||
|
|
shape.size = Vector2(w_px + border_thickness, border_thickness)
|
||
|
|
else:
|
||
|
|
shape.size = Vector2(border_thickness, h_px + border_thickness)
|
||
|
|
var col = CollisionShape2D.new()
|
||
|
|
col.shape = shape
|
||
|
|
wall.position = positions[name]
|
||
|
|
wall.add_child(col)
|
||
|
|
add_child(wall)
|
||
|
|
|
||
|
|
@onready var label: RichTextLabel = null
|
||
|
|
@onready var uitypebox: ColorRect = null
|
||
|
|
var typing_speed := 0.03 # seconds between characters
|
||
|
|
var typing = false # to prevent overlapping animations
|
||
|
|
|
||
|
|
func ui_showmesg(t: String) -> void:
|
||
|
|
label = get_node("../UI/bg/RichTextLabel")
|
||
|
|
uitypebox = get_node("../UI/bg")
|
||
|
|
if typing:
|
||
|
|
return # skip if already typing (optional safeguard)
|
||
|
|
uitypebox.visible = true
|
||
|
|
typing = true
|
||
|
|
print(label, " valid? ", is_instance_valid(label))
|
||
|
|
|
||
|
|
label.text = t
|
||
|
|
label.visible_characters = 0
|
||
|
|
await _type_text(t)
|
||
|
|
await wait(4)
|
||
|
|
label = get_node("../UI/bg/RichTextLabel")
|
||
|
|
uitypebox = get_node("../UI/bg")
|
||
|
|
label.visible_characters = 0
|
||
|
|
uitypebox.visible = false
|
||
|
|
typing = false
|
||
|
|
|
||
|
|
|
||
|
|
func _type_text(t: String) -> void:
|
||
|
|
var i := 0
|
||
|
|
while i < t.length():
|
||
|
|
i += 1
|
||
|
|
label.visible_characters = i
|
||
|
|
await wait(typing_speed)
|