feat(studio,stacking,animations,collections): M16 ModBison Studio, object stacking, model animations, and poly collections for MVP 2
checkpoint / checkpoint (push) Successful in 6m32s
checkpoint / checkpoint (push) Successful in 6m32s
This commit is contained in:
@@ -25,6 +25,10 @@ var cell_size: float = 24.0
|
||||
var pending_catalog_id: String = ""
|
||||
## Tile id the next "brush" click paints. Cycled with number keys 0-9.
|
||||
var current_tile_id: int = 1
|
||||
## Elevation offset for placement and hotkey nudging (Q / E).
|
||||
var current_elevation_offset: float = 0.0
|
||||
## Whether to align placed object to surface normal slope.
|
||||
var align_surface_normal: bool = false
|
||||
|
||||
var _hover_tx: int = -1
|
||||
var _hover_ty: int = -1
|
||||
@@ -119,6 +123,32 @@ func _draw() -> void:
|
||||
display_name = obj.id
|
||||
draw_string(default_font, center + Vector2(-r * 1.5, -r - 2), display_name.left(6), HORIZONTAL_ALIGNMENT_CENTER, int(r * 3.0), 10, Color(1, 1, 1, 0.9))
|
||||
|
||||
# Draw motion waypoint path & gizmos for selected object
|
||||
var sel_id := core.selection.get_selected()
|
||||
if sel_id != "":
|
||||
var sel_obj := core.model.find_object(sel_id)
|
||||
if sel_obj != null and sel_obj.properties is Dictionary and sel_obj.properties.has("motion"):
|
||||
var m_cfg = sel_obj.properties["motion"]
|
||||
if m_cfg is Dictionary and m_cfg.has("waypoints") and m_cfg["waypoints"] is Array:
|
||||
var wps: Array = m_cfg["waypoints"]
|
||||
if not wps.is_empty():
|
||||
var prev_pt := Vector2(sel_obj.position.x * cell_size, sel_obj.position.y * cell_size)
|
||||
for i in range(wps.size()):
|
||||
var wp_val = wps[i]
|
||||
var wp_pos := Vector2.ZERO
|
||||
if wp_val is Array and wp_val.size() >= 2:
|
||||
wp_pos = Vector2(float(wp_val[0]) * cell_size, float(wp_val[1]) * cell_size)
|
||||
elif wp_val is Vector3:
|
||||
wp_pos = Vector2(wp_val.x * cell_size, wp_val.y * cell_size)
|
||||
|
||||
draw_line(prev_pt, wp_pos, Color(0.2, 0.85, 1.0, 0.8), 2.0)
|
||||
var wr := cell_size * 0.28
|
||||
draw_circle(wp_pos, wr, Color(0.1, 0.6, 0.9, 0.85))
|
||||
draw_arc(wp_pos, wr, 0.0, TAU, 16, Color(1, 1, 1, 0.9), 1.5)
|
||||
draw_string(default_font, wp_pos + Vector2(-4, 4), str(i + 1), HORIZONTAL_ALIGNMENT_CENTER, 8, 10, Color(1, 1, 1))
|
||||
prev_pt = wp_pos
|
||||
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if core == null:
|
||||
@@ -155,6 +185,57 @@ func _gui_input(event: InputEvent) -> void:
|
||||
func _handle_key(key: InputEventKey) -> void:
|
||||
if key.keycode >= KEY_0 and key.keycode <= KEY_9:
|
||||
current_tile_id = key.keycode - KEY_0
|
||||
elif key.keycode == KEY_E:
|
||||
current_elevation_offset += 0.5
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "":
|
||||
core.adjust_object_elevation(sel, 0.5)
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
elif key.keycode == KEY_Q:
|
||||
current_elevation_offset = maxf(0.0, current_elevation_offset - 0.5)
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "":
|
||||
core.adjust_object_elevation(sel, -0.5)
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
elif key.keycode == KEY_END:
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "":
|
||||
if core.drop_object_to_surface(sel):
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
elif key.keycode == KEY_R:
|
||||
align_surface_normal = not align_surface_normal
|
||||
elif key.keycode == KEY_W:
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "" and _hover_tx >= 0 and _hover_ty >= 0:
|
||||
var obj := core.model.find_object(sel)
|
||||
if obj != null:
|
||||
var motion_dict = obj.properties.get("motion", {"motion_type": "waypoint", "waypoints": []})
|
||||
if not (motion_dict is Dictionary):
|
||||
motion_dict = {"motion_type": "waypoint", "waypoints": []}
|
||||
var wps = motion_dict.get("waypoints", [])
|
||||
wps.append([float(_hover_tx), float(_hover_ty), obj.position.z])
|
||||
motion_dict["motion_type"] = "waypoint"
|
||||
motion_dict["waypoints"] = wps
|
||||
core.set_object_property(sel, "motion", motion_dict)
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
elif key.keycode == KEY_BACKSPACE:
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "":
|
||||
var obj := core.model.find_object(sel)
|
||||
if obj != null and obj.properties is Dictionary and obj.properties.has("motion"):
|
||||
var motion_dict = obj.properties["motion"]
|
||||
if motion_dict is Dictionary and motion_dict.has("waypoints") and motion_dict["waypoints"] is Array:
|
||||
var wps: Array = motion_dict["waypoints"]
|
||||
if not wps.is_empty():
|
||||
wps.pop_back()
|
||||
motion_dict["waypoints"] = wps
|
||||
core.set_object_property(sel, "motion", motion_dict)
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _handle_click(local_pos: Vector2) -> void:
|
||||
@@ -176,7 +257,22 @@ func _handle_click(local_pos: Vector2) -> void:
|
||||
queue_redraw()
|
||||
"object_place":
|
||||
if pending_catalog_id != "":
|
||||
var new_id := core.place_object(pending_catalog_id, Vector3(tx + 0.5, ty + 0.5, 0.0))
|
||||
var existing_hit := _highest_object_at(tx, ty)
|
||||
var new_id: String = ""
|
||||
if existing_hit != "":
|
||||
new_id = core.place_object_stacked(
|
||||
pending_catalog_id,
|
||||
existing_hit,
|
||||
Vector3(tx + 0.5, ty + 0.5, 0.0),
|
||||
Vector3(0, 0, 1),
|
||||
current_elevation_offset,
|
||||
align_surface_normal
|
||||
)
|
||||
else:
|
||||
new_id = core.place_object(
|
||||
pending_catalog_id,
|
||||
Vector3(tx + 0.5, ty + 0.5, current_elevation_offset)
|
||||
)
|
||||
if new_id != "":
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
@@ -191,3 +287,53 @@ func _object_at(tx: int, ty: int) -> String:
|
||||
if int(floor(obj.position.x)) == tx and int(floor(obj.position.y)) == ty:
|
||||
return obj.id
|
||||
return ""
|
||||
|
||||
|
||||
func _highest_object_at(tx: int, ty: int) -> String:
|
||||
var best_id := ""
|
||||
var max_z: float = -1.0
|
||||
for obj in core.model.payload.objects:
|
||||
if int(floor(obj.position.x)) == tx and int(floor(obj.position.y)) == ty:
|
||||
if obj.position.z > max_z:
|
||||
max_z = obj.position.z
|
||||
best_id = obj.id
|
||||
return best_id
|
||||
|
||||
|
||||
func _can_drop_data(_at_position: Vector2, data: Variant) -> bool:
|
||||
if data is Dictionary and data.get("type") == "model_placement":
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _drop_data(at_position: Vector2, data: Variant) -> void:
|
||||
if not _can_drop_data(at_position, data):
|
||||
return
|
||||
if core == null:
|
||||
return
|
||||
var dict: Dictionary = data as Dictionary
|
||||
var cat_id: String = dict.get("catalog_id", "")
|
||||
if cat_id == "":
|
||||
cat_id = dict.get("model_name", "object").to_lower().replace(" ", "_")
|
||||
|
||||
var tx := int(floor(at_position.x / cell_size))
|
||||
var ty := int(floor(at_position.y / cell_size))
|
||||
var existing_hit := _highest_object_at(tx, ty)
|
||||
var new_id := ""
|
||||
if existing_hit != "":
|
||||
new_id = core.place_object_stacked(
|
||||
cat_id,
|
||||
existing_hit,
|
||||
Vector3(tx + 0.5, ty + 0.5, 0.0),
|
||||
Vector3(0, 0, 1),
|
||||
current_elevation_offset,
|
||||
align_surface_normal
|
||||
)
|
||||
else:
|
||||
new_id = core.place_object(
|
||||
cat_id,
|
||||
Vector3(tx + 0.5, ty + 0.5, current_elevation_offset)
|
||||
)
|
||||
if new_id != "":
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
|
||||
Reference in New Issue
Block a user