Add lever and jump pad components with activation logic

This commit is contained in:
2025-04-25 22:41:35 +02:00
parent 9b2ca61163
commit 8959fd4b9f
323 changed files with 12844 additions and 18 deletions

View File

@@ -0,0 +1,23 @@
class_name SS2D_Action
extends RefCounted
## Base class for all plugin actions.
##
## UndoRedo system will call [method do] and [method undo].
# @virtual
## Returns string to be used as a name in editor History tab.
func get_name() -> String:
return "UntitledAction"
# @virtual
## Do action here.
func do() -> void:
pass
# @virtual
## Undo action here.
func undo() -> void:
pass

View File

@@ -0,0 +1,56 @@
extends SS2D_Action
## ActionAddCollisionNodes
var _shape: SS2D_Shape
var _saved_index: int
var _saved_pos: Vector2
func _init(shape: SS2D_Shape) -> void:
_shape = shape
func get_name() -> String:
return "Add Collision Nodes"
func do() -> void:
_saved_index = _shape.get_index()
_saved_pos = _shape.position
var owner := _shape.owner
var static_body := StaticBody2D.new()
static_body.position = _shape.position
_shape.position = Vector2.ZERO
_shape.get_parent().add_child(static_body, true)
static_body.owner = owner
_shape.get_parent().remove_child(_shape)
static_body.add_child(_shape, true)
_shape.owner = owner
var poly: CollisionPolygon2D = CollisionPolygon2D.new()
static_body.add_child(poly, true)
poly.owner = owner
# TODO: Make this a option at some point
poly.modulate.a = 0.3
poly.visible = false
_shape.collision_polygon_node_path = _shape.get_path_to(poly)
func undo() -> void:
var owner := _shape.owner
var parent := _shape.get_parent()
var grandparent := _shape.get_parent().get_parent()
parent.remove_child(_shape)
grandparent.remove_child(parent)
parent.free()
grandparent.add_child(_shape)
_shape.owner = owner
grandparent.move_child(_shape, _saved_index)
_shape.position = _saved_pos

View File

@@ -0,0 +1,44 @@
extends SS2D_Action
## ActionAddPoint
const ActionInvertOrientation := preload("res://addons/rmsmartshape/actions/action_invert_orientation.gd")
var _invert_orientation: ActionInvertOrientation
var _commit_update: bool
var _shape: SS2D_Shape
var _key: int
var _position: Vector2
var _idx: int
func _init(shape: SS2D_Shape, position: Vector2, idx: int = -1, commit_update: bool = true) -> void:
_shape = shape
_position = position
_commit_update = commit_update
_idx = _shape.adjust_add_point_index(idx)
_key = _shape.reserve_key()
_invert_orientation = ActionInvertOrientation.new(shape)
func get_name() -> String:
return "Add Point at (%d, %d)" % [_position.x, _position.y]
func do() -> void:
_shape.begin_update()
_key = _shape.add_point(_position, _idx, _key)
_invert_orientation.do()
if _commit_update:
_shape.end_update()
func undo() -> void:
_shape.begin_update()
_invert_orientation.undo()
_shape.remove_point(_key)
_shape.end_update()
func get_key() -> int:
return _key

View File

@@ -0,0 +1,40 @@
extends SS2D_Action
## ActionCloseShape
const ActionInvertOrientation := preload("res://addons/rmsmartshape/actions/action_invert_orientation.gd")
var _invert_orientation: ActionInvertOrientation
var _shape: SS2D_Shape
var _key: int
var _performed: bool
func _init(shape: SS2D_Shape) -> void:
_shape = shape
_invert_orientation = ActionInvertOrientation.new(shape)
func get_name() -> String:
return "Close Shape"
func do() -> void:
_performed = _shape.can_close()
if _performed:
_shape.begin_update()
_key = _shape.close_shape(_key)
_invert_orientation.do()
_shape.end_update()
func undo() -> void:
if _performed:
_shape.begin_update()
_invert_orientation.undo()
_shape.remove_point(_key)
_shape.end_update()
func get_key() -> int:
return _key

View File

@@ -0,0 +1,41 @@
extends SS2D_Action
## ActionCutEdge
##
## A delegate action that selects an action to perform based on the edge
## location and shape state.
var ActionOpenShape := preload("res://addons/rmsmartshape/actions/action_open_shape.gd")
var ActionDeletePoint := preload("res://addons/rmsmartshape/actions/action_delete_point.gd")
var ActionSplitShape := preload("res://addons/rmsmartshape/actions/action_split_shape.gd")
var _shape: SS2D_Shape
var _action: SS2D_Action
func _init(shape: SS2D_Shape, key_edge_start: int, key_edge_end: int) -> void:
_shape = shape
var key_first: int = shape.get_point_key_at_index(0)
var key_last: int = shape.get_point_key_at_index(shape.get_point_count()-1)
if _shape.is_shape_closed():
_action = ActionOpenShape.new(shape, key_edge_start)
elif key_edge_start == key_first:
_action = ActionDeletePoint.new(shape, key_edge_start)
elif key_edge_end == key_last:
_action = ActionDeletePoint.new(shape, key_edge_end)
else:
_action = ActionSplitShape.new(shape, key_edge_start)
func get_name() -> String:
return _action.get_name()
func do() -> void:
_action.do()
func undo() -> void:
_action.undo()

View File

@@ -0,0 +1,45 @@
extends SS2D_Action
## ActionDeleteControlPoint
enum PointType {POINT_IN, POINT_OUT}
const ActionInvertOrientation := preload("res://addons/rmsmartshape/actions/action_invert_orientation.gd")
var _invert_orientation: ActionInvertOrientation
var _shape: SS2D_Shape
var _key: int
var _point_type: PointType
var _old_value: Vector2
func _init(shape: SS2D_Shape, key: int, point_type: PointType) -> void:
_shape = shape
_key = key
_point_type = point_type
_old_value = shape.get_point_in(key) if _point_type == PointType.POINT_IN else shape.get_point_out(key)
_invert_orientation = ActionInvertOrientation.new(shape)
func get_name() -> String:
return "Delete Control Point " + ("In" if _point_type == PointType.POINT_IN else "Out")
func do() -> void:
_shape.begin_update()
if _point_type == PointType.POINT_IN:
_shape.set_point_in(_key, Vector2.ZERO)
else:
_shape.set_point_out(_key, Vector2.ZERO)
_invert_orientation.do()
_shape.end_update()
func undo() -> void:
_shape.begin_update()
_invert_orientation.undo()
if _point_type == PointType.POINT_IN:
_shape.set_point_in(_key, _old_value)
else:
_shape.set_point_out(_key, _old_value)
_shape.end_update()

View File

@@ -0,0 +1,13 @@
extends "res://addons/rmsmartshape/actions/action_delete_points.gd"
## ActionDeletePoint
func _init(shape: SS2D_Shape, key: int, commit_update: bool = true) -> void:
var keys: PackedInt32Array = [key]
super(shape, keys, commit_update)
func get_name() -> String:
var pos := _shape.get_point_position(_keys[0])
return "Delete Point at (%d, %d)" % [pos.x, pos.y]

View File

@@ -0,0 +1,88 @@
extends SS2D_Action
## ActionDeletePoints
const TUP := preload("res://addons/rmsmartshape/lib/tuple.gd")
const ActionInvertOrientation := preload("res://addons/rmsmartshape/actions/action_invert_orientation.gd")
var _invert_orientation: ActionInvertOrientation
const ActionCloseShape := preload("res://addons/rmsmartshape/actions/action_close_shape.gd")
var _close_shape: ActionCloseShape
var _shape: SS2D_Shape
var _keys: PackedInt32Array
var _indicies: PackedInt32Array
var _positions: PackedVector2Array
var _points_in: PackedVector2Array
var _points_out: PackedVector2Array
var _properties: Array[SS2D_VertexProperties]
var _constraints: Array[Dictionary]
var _was_closed: bool
var _commit_update: bool
func _init(shape: SS2D_Shape, keys: PackedInt32Array, commit_update: bool = true) -> void:
_shape = shape
_invert_orientation = ActionInvertOrientation.new(shape)
_close_shape = ActionCloseShape.new(shape)
_commit_update = commit_update
for k in keys:
add_point_to_delete(k)
func get_name() -> String:
return "Delete Points %s" % [_keys]
func do() -> void:
_shape.begin_update()
_was_closed = _shape.is_shape_closed()
var first_run := _positions.size() == 0
for k in _keys:
if first_run:
_indicies.append(_shape.get_point_index(k))
_positions.append(_shape.get_point_position(k))
_points_in.append(_shape.get_point_in(k))
_points_out.append(_shape.get_point_out(k))
_properties.append(_shape.get_point_properties(k))
_shape.remove_point(k)
if _was_closed:
_close_shape.do()
_invert_orientation.do()
if _commit_update:
_shape.end_update()
func undo() -> void:
_shape.begin_update()
_invert_orientation.undo()
if _was_closed:
_close_shape.undo()
for i in range(_keys.size()-1, -1, -1):
_shape.add_point(_positions[i], _indicies[i], _keys[i])
_shape.set_point_in(_keys[i], _points_in[i])
_shape.set_point_out(_keys[i], _points_out[i])
_shape.set_point_properties(_keys[i], _properties[i])
# Restore point constraints.
for i in range(_keys.size()-1, -1, -1):
for tuple: Vector2i in _constraints[i]:
_shape.set_constraint(tuple[0], tuple[1], _constraints[i][tuple])
_shape.end_update()
func add_point_to_delete(key: int) -> void:
_keys.push_back(key)
var constraints := _shape.get_point_array().get_point_constraints(key)
# Save point constraints.
_constraints.append(constraints)
for tuple: Vector2i in constraints:
var constraint: SS2D_Point_Array.CONSTRAINT = constraints[tuple]
if constraint == SS2D_Point_Array.CONSTRAINT.NONE:
continue
var key_other := SS2D_IndexTuple.get_other_value(tuple, key)
if constraint & SS2D_Point_Array.CONSTRAINT.ALL:
if not _keys.has(key_other):
add_point_to_delete(key_other)

View File

@@ -0,0 +1,33 @@
extends SS2D_Action
## ActionInvertOrientation
var _shape: SS2D_Shape
var _performed: bool
func _init(shape: SS2D_Shape) -> void:
_shape = shape
func get_name() -> String:
return "Invert Orientation"
func do() -> void:
_performed = should_invert_orientation(_shape)
if _performed:
_shape.invert_point_order()
func undo() -> void:
if _performed:
_shape.invert_point_order()
func should_invert_orientation(s: SS2D_Shape) -> bool:
if s == null:
return false
if not s.is_shape_closed():
return false
return not s.are_points_clockwise() and s.get_point_count() >= 3

View File

@@ -0,0 +1,26 @@
extends SS2D_Action
## ActionMakeShapeUnique
var _shape: SS2D_Shape
var _old_array: SS2D_Point_Array
var _new_array: SS2D_Point_Array
func _init(shape: SS2D_Shape) -> void:
_shape = shape
_old_array = shape.get_point_array()
_new_array = _shape.get_point_array().clone(true)
func get_name() -> String:
return "Make Shape Unique"
func do() -> void:
_shape.set_point_array(_new_array)
func undo() -> void:
_shape.set_point_array(_old_array)

View File

@@ -0,0 +1,43 @@
extends SS2D_Action
## ActionMoveControlPoints
var _shape: SS2D_Shape
var _keys: PackedInt32Array
var _old_points_in: PackedVector2Array
var _old_points_out: PackedVector2Array
var _new_points_in: PackedVector2Array
var _new_points_out: PackedVector2Array
func _init(s: SS2D_Shape, keys: PackedInt32Array,
old_points_in: PackedVector2Array, old_points_out: PackedVector2Array) -> void:
_shape = s
_keys = keys
_old_points_in = old_points_in
_old_points_out = old_points_out
for key in _keys:
_new_points_in.append(_shape.get_point_in(key))
_new_points_out.append(_shape.get_point_out(key))
func get_name() -> String:
return "Move Control Point"
func do() -> void:
_assign_points_in_out(_keys, _new_points_in, _new_points_out)
func undo() -> void:
_assign_points_in_out(_keys, _old_points_in, _old_points_out)
func _assign_points_in_out(keys: PackedInt32Array, in_positions: PackedVector2Array, out_positions: PackedVector2Array) -> void:
_shape.begin_update()
for i in keys.size():
if _shape.get_point_in(keys[i]) != in_positions[i]:
_shape.set_point_in(keys[i], in_positions[i])
if _shape.get_point_out(keys[i]) != out_positions[i]:
_shape.set_point_out(keys[i], out_positions[i])
_shape.end_update()

View File

@@ -0,0 +1,44 @@
extends SS2D_Action
## ActionMoveVerticies
const ActionInvertOrientation := preload("res://addons/rmsmartshape/actions/action_invert_orientation.gd")
var _invert_orientation: ActionInvertOrientation
var _shape: SS2D_Shape
var _keys: PackedInt32Array
var _old_positions: PackedVector2Array
var _new_positions: PackedVector2Array
func _init(s: SS2D_Shape, keys: PackedInt32Array, old_positions: PackedVector2Array) -> void:
_shape = s
_keys = keys.duplicate()
_old_positions = old_positions.duplicate()
_new_positions = PackedVector2Array()
for k in _keys:
_new_positions.append(_shape.get_point_position(k))
_invert_orientation = ActionInvertOrientation.new(_shape)
func get_name() -> String:
if _keys.size() == 1:
return "Move Vertex to (%d, %d)" % [_new_positions[0].x, _new_positions[0].y]
else:
return "Move Verticies"
func do() -> void:
_shape.begin_update()
for i in _keys.size():
_shape.set_point_position(_keys[i], _new_positions[i])
_invert_orientation.do()
_shape.end_update()
func undo() -> void:
_shape.begin_update()
_invert_orientation.undo()
for i in range(_keys.size() - 1, -1, -1):
_shape.set_point_position(_keys[i], _old_positions[i])
_shape.end_update()

View File

@@ -0,0 +1,31 @@
extends SS2D_Action
## ActionOpenShape
var _shape: SS2D_Shape
var _cut_idx: int
var _closing_key: int
func _init(shape: SS2D_Shape, edge_start_key: int) -> void:
_shape = shape
_cut_idx = shape.get_point_index(edge_start_key)
func get_name() -> String:
return "Open Shape"
func do() -> void:
_shape.begin_update()
var last_idx: int = _shape.get_point_count() - 1
_closing_key = _shape.get_point_key_at_index(last_idx)
_shape.open_shape_at_edge(_cut_idx)
_shape.end_update()
func undo() -> void:
_shape.begin_update()
_shape.undo_open_shape_at_edge(_cut_idx, _closing_key)
_shape.end_update()

View File

@@ -0,0 +1,44 @@
extends SS2D_Action
## ActionSetPivot
var _shape: SS2D_Shape
var _new_pos: Vector2
var _old_pos: Vector2
func _init(s: SS2D_Shape, pos: Vector2) -> void:
_shape = s
_new_pos = pos
_old_pos = _shape.global_position
func get_name() -> String:
return "Set Pivot"
func do() -> void:
_set_pivot(_new_pos)
func undo() -> void:
_set_pivot(_old_pos)
func _set_pivot(shape_position: Vector2) -> void:
var shape_gt: Transform2D = _shape.get_global_transform()
_shape.global_position = shape_position
_shape.begin_update()
_shape.disable_constraints()
for i in _shape.get_point_count():
var key: int = _shape.get_point_key_at_index(i)
var point: Vector2 = _shape.get_point_position(key)
_shape.set_point_position(key, _shape.to_local(shape_gt * point))
_shape.enable_constraints()
_shape.end_update()

View File

@@ -0,0 +1,10 @@
extends "res://addons/rmsmartshape/actions/action_add_point.gd"
## ActionSplitCurve
func _init(shape: SS2D_Shape, idx: int, gpoint: Vector2, xform: Transform2D, commit_update: bool = true) -> void:
super._init(shape, xform.affine_inverse() * gpoint, idx, commit_update)
func get_name() -> String:
return "Split Curve at (%d, %d)" % [_position.x, _position.y]

View File

@@ -0,0 +1,74 @@
extends SS2D_Action
## ActionSplitShape
##
## How it's done:
## 1. First, the shape is copied and added to the scene tree.
## 2. Then, points of the splitted shape are deleted from first point to split point.
## 3. Finally, points of the original shape are deleted from the point after split point to last point.
const ActionDeletePoints := preload("res://addons/rmsmartshape/actions/action_delete_points.gd")
var _delete_points_from_original: ActionDeletePoints
var _shape: SS2D_Shape
var _splitted: SS2D_Shape
var _splitted_collision: CollisionPolygon2D
var _split_idx: int
func _init(shape: SS2D_Shape, split_point_key: int) -> void:
assert(shape.is_shape_closed() == false)
_shape = shape
_split_idx = shape.get_point_index(split_point_key)
_splitted = null
_splitted_collision = null
_delete_points_from_original = null
func get_name() -> String:
return "Split Shape"
func do() -> void:
if not is_instance_valid(_splitted):
_splitted = _shape.clone()
_splitted.begin_update()
for i in range(0, _split_idx + 1):
_splitted.remove_point_at_index(0)
_splitted.end_update()
_shape.get_parent().add_child(_splitted, true)
_splitted.set_owner(_shape.get_tree().get_edited_scene_root())
# Add a collision shape node if the original shape has one.
if (not _shape.collision_polygon_node_path.is_empty() and _shape.has_node(_shape.collision_polygon_node_path)):
var collision_polygon_original := _shape.get_node(_shape.collision_polygon_node_path) as CollisionPolygon2D
if not is_instance_valid(_splitted_collision):
_splitted_collision = CollisionPolygon2D.new()
_splitted_collision.visible = collision_polygon_original.visible
_splitted_collision.modulate = collision_polygon_original.modulate
collision_polygon_original.get_parent().add_child(_splitted_collision, true)
_splitted_collision.set_owner(collision_polygon_original.get_tree().get_edited_scene_root())
_splitted.collision_polygon_node_path = _splitted.get_path_to(_splitted_collision)
if _delete_points_from_original == null:
var delete_keys := PackedInt32Array()
for i in range(_shape.get_point_count() - 1, _split_idx, -1):
delete_keys.append(_shape.get_point_key_at_index(i))
_delete_points_from_original = ActionDeletePoints.new(_shape, delete_keys)
_delete_points_from_original.do()
func undo() -> void:
_splitted.set_owner(null)
_splitted.get_parent().remove_child(_splitted)
if is_instance_valid(_splitted_collision):
_splitted_collision.set_owner(null)
_splitted_collision.get_parent().remove_child(_splitted_collision)
_delete_points_from_original.undo()
func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE:
if is_instance_valid(_splitted) and _splitted.get_parent() == null:
_splitted.queue_free()
if is_instance_valid(_splitted_collision) and _splitted_collision.get_parent() == null:
_splitted_collision.queue_free()