refactor: harden client runtime and exports

This commit is contained in:
ANG-Server
2026-07-23 00:59:00 +08:00
parent 5d2339a29b
commit fc872fe8af
27 changed files with 902 additions and 856 deletions

View File

@@ -0,0 +1,92 @@
extends RefCounted
var _parent: Node2D
var _collision_layer: int
var _collision_mask: int
var _bodies: Dictionary = {}
func _init(parent: Node2D, collision_layer: int, collision_mask: int) -> void:
_parent = parent
_collision_layer = collision_layer
_collision_mask = collision_mask
func sync(decor_id: String, item: Dictionary) -> void:
var collision_size := _to_vector2(item.get("collision_size", Vector2.ZERO))
if collision_size == Vector2.ZERO:
remove(decor_id)
return
var collision_offset := _to_vector2(item.get("collision_offset", Vector2.ZERO))
var body := _bodies.get(decor_id, null) as StaticBody2D
var shape_node: CollisionShape2D
if body == null or not is_instance_valid(body):
body = StaticBody2D.new()
body.name = "DecorCollision_%s" % decor_id
body.collision_layer = _collision_layer
body.collision_mask = _collision_mask
shape_node = CollisionShape2D.new()
shape_node.name = "CollisionShape2D"
shape_node.shape = RectangleShape2D.new()
body.add_child(shape_node)
_parent.add_child(body)
_bodies[decor_id] = body
else:
shape_node = body.get_node_or_null("CollisionShape2D") as CollisionShape2D
if shape_node == null:
shape_node = CollisionShape2D.new()
shape_node.name = "CollisionShape2D"
shape_node.shape = RectangleShape2D.new()
body.add_child(shape_node)
var scale := _to_float(item.get("scale", item.get("default_scale", 1.0)), 1.0)
var rotation_degrees := _to_float(
item.get("rotation_degrees", item.get("default_rotation_degrees", 0.0)),
0.0
)
body.rotation_degrees = rotation_degrees
body.global_position = Vector2(
_to_float(item.get("position_x", 0.0), 0.0),
_to_float(item.get("position_y", 0.0), 0.0)
) + collision_offset.rotated(deg_to_rad(rotation_degrees)) * scale
shape_node.position = Vector2.ZERO
var rectangle := shape_node.shape as RectangleShape2D
if rectangle == null:
rectangle = RectangleShape2D.new()
shape_node.shape = rectangle
rectangle.size = Vector2(maxf(8.0, collision_size.x * scale), maxf(8.0, collision_size.y * scale))
func set_disabled(decor_id: String, disabled: bool) -> void:
var body := _bodies.get(decor_id, null) as StaticBody2D
if body == null or not is_instance_valid(body):
return
var shape_node := body.get_node_or_null("CollisionShape2D") as CollisionShape2D
if shape_node != null:
shape_node.disabled = disabled
func remove(decor_id: String) -> void:
var body := _bodies.get(decor_id, null) as Node
if body != null and is_instance_valid(body):
body.queue_free()
_bodies.erase(decor_id)
func _to_vector2(value: Variant) -> Vector2:
if value is Vector2:
return value
if value is Dictionary:
var dictionary: Dictionary = value
return Vector2(
_to_float(dictionary.get("x", 0.0), 0.0),
_to_float(dictionary.get("y", 0.0), 0.0)
)
return Vector2.ZERO
func _to_float(value: Variant, fallback: float) -> float:
match typeof(value):
TYPE_FLOAT, TYPE_INT:
return float(value)
TYPE_STRING:
var text := str(value).strip_edges()
return fallback if text.is_empty() else text.to_float()
TYPE_BOOL:
return 1.0 if bool(value) else 0.0
_:
return fallback

View File

@@ -0,0 +1 @@
uid://djp28p0cx377d

View File

@@ -0,0 +1,60 @@
extends RefCounted
var _undo_entries: Array[Dictionary] = []
var _redo_entries: Array[Dictionary] = []
func clear() -> void:
_undo_entries.clear()
_redo_entries.clear()
func record_item(before: Dictionary, after: Dictionary) -> bool:
if before == after:
return false
var decor_id := str(after.get("decor_id", before.get("decor_id", ""))).strip_edges()
if decor_id.is_empty():
return false
_undo_entries.append({
"decor_id": decor_id,
"before": before.duplicate(true),
"after": after.duplicate(true),
})
_redo_entries.clear()
return true
func record_bulk(before_items: Dictionary, after_items: Dictionary) -> bool:
if before_items == after_items:
return false
_undo_entries.append({
"before_items": _duplicate_item_map(before_items),
"after_items": _duplicate_item_map(after_items),
})
_redo_entries.clear()
return true
func can_undo() -> bool:
return not _undo_entries.is_empty()
func can_redo() -> bool:
return not _redo_entries.is_empty()
func take_undo() -> Dictionary:
if _undo_entries.is_empty():
return {}
var entry: Dictionary = _undo_entries.pop_back()
_redo_entries.append(entry)
return entry
func take_redo() -> Dictionary:
if _redo_entries.is_empty():
return {}
var entry: Dictionary = _redo_entries.pop_back()
_undo_entries.append(entry)
return entry
func _duplicate_item_map(source: Dictionary) -> Dictionary:
var duplicate: Dictionary = {}
for decor_id_variant in source.keys():
var item_variant: Variant = source.get(decor_id_variant, {})
if item_variant is Dictionary:
duplicate[str(decor_id_variant)] = (item_variant as Dictionary).duplicate(true)
return duplicate

View File

@@ -0,0 +1 @@
uid://bvmpisyit53q0