first commit
This commit is contained in:
29
code/lua_env.lua
Normal file
29
code/lua_env.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
local lua_env = {
|
||||
extends = Node,
|
||||
env = {}
|
||||
}
|
||||
|
||||
-- optional helper to reset environment
|
||||
function lua_env:reset_env()
|
||||
self.env = {}
|
||||
setmetatable(self.env, { __index = _G }) -- fall back to globals
|
||||
end
|
||||
-- the core runscript
|
||||
function lua_env:run_script(code, node)
|
||||
local chunk, err = load(code, "runscript", "t", self.env)
|
||||
if not chunk then
|
||||
print("[LuaRuntime] Syntax error: ", err)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Call the chunk, optionally passing context
|
||||
local ok, result = pcall(chunk, node)
|
||||
if not ok then
|
||||
print("[LuaRuntime] Runtime error: ", result)
|
||||
return false
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
return lua_env
|
||||
1
code/lua_env.lua.uid
Normal file
1
code/lua_env.lua.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c01rdot180cne
|
||||
50
code/player.gd
Normal file
50
code/player.gd
Normal file
@@ -0,0 +1,50 @@
|
||||
extends CharacterBody2D
|
||||
|
||||
var speed = 16*16; # godot transformation per second
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
var texts = [];
|
||||
var lastChange = 0;
|
||||
var hit = 0;
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
RenderingServer.set_default_clear_color(Color.BLACK)
|
||||
var root = get_node("../")
|
||||
var texturespace = root.get_meta("resourcepack");
|
||||
for text in ["player-w1","player-w2"]:
|
||||
texts.append(load("res://"+texturespace+"/text/"+text+".png"))
|
||||
|
||||
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _physics_process(delta: float) -> void:
|
||||
var movedelta = Vector2.ZERO
|
||||
if not self.get_meta("can_walk"):
|
||||
return
|
||||
|
||||
if Input.is_action_pressed("movedown"):
|
||||
movedelta.y += 1
|
||||
if Input.is_action_pressed("moveup"):
|
||||
movedelta.y -= 1
|
||||
if Input.is_action_pressed("moveleft"):
|
||||
movedelta.x -= 1
|
||||
if Input.is_action_pressed("moveright"):
|
||||
movedelta.x += 1
|
||||
|
||||
if movedelta.length() > 0:
|
||||
movedelta = movedelta.normalized() * speed
|
||||
move_and_collide(Vector2((movedelta * delta).x,0))
|
||||
move_and_collide(Vector2(0,(movedelta * delta).y))
|
||||
lastChange += delta
|
||||
if lastChange > 0.4:
|
||||
lastChange = 0
|
||||
hit += 1
|
||||
if hit > texts.size()-1:
|
||||
hit = 0
|
||||
get_node("Sprite2D").texture = texts[hit]
|
||||
get_node("Walk").play()
|
||||
func _process(delta: float) -> void:
|
||||
var camera = get_node("../cam")
|
||||
var campos: Vector2 = camera.position
|
||||
camera.position = campos.lerp(self.position, 1.0 - exp(-8 * delta))
|
||||
1
code/player.gd.uid
Normal file
1
code/player.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cc4y82skj2obl
|
||||
205
code/world.gd
Normal file
205
code/world.gd
Normal file
@@ -0,0 +1,205 @@
|
||||
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)
|
||||
1
code/world.gd.uid
Normal file
1
code/world.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://be6h32vns32hm
|
||||
Reference in New Issue
Block a user