Adding inventory stacking and overflow to gather mechanic
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 44s
Deploy Promiscuity Inventory API / deploy (push) Successful in 58s
Deploy Promiscuity Locations API / deploy (push) Successful in 58s
k8s smoke test / test (push) Successful in 9s
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 44s
Deploy Promiscuity Inventory API / deploy (push) Successful in 58s
Deploy Promiscuity Locations API / deploy (push) Successful in 58s
k8s smoke test / test (push) Successful in 9s
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
extends Node3D
|
||||
|
||||
const CHARACTER_API_URL := "https://pchar.ranaze.com/api/Characters"
|
||||
const LOCATION_API_URL := "https://ploc.ranaze.com/api/Locations"
|
||||
const CHARACTER_API_URL := "https://pchar.ranaze.com/api/Characters"
|
||||
const LOCATION_API_URL := "https://ploc.ranaze.com/api/Locations"
|
||||
const INVENTORY_API_URL := "https://pinv.ranaze.com/api/inventory"
|
||||
|
||||
@export var tile_size := 16.0
|
||||
@export var tile_size := 8.0
|
||||
@export var block_height := 1.0
|
||||
@export_range(1, 8, 1) var tile_radius := 3
|
||||
@export var tracked_node_path: NodePath
|
||||
@@ -151,11 +152,12 @@ func _spawn_tile(coord: Vector2i, location_data: Dictionary) -> void:
|
||||
tile_body.add_child(tile)
|
||||
|
||||
tile.add_child(_create_tile_border())
|
||||
if show_tile_labels:
|
||||
tile_root.add_child(_create_tile_label(String(location_data.get("name", ""))))
|
||||
_update_tile_object(tile_root, location_data)
|
||||
|
||||
_tile_nodes[coord] = tile_root
|
||||
if show_tile_labels:
|
||||
tile_root.add_child(_create_tile_label(String(location_data.get("name", ""))))
|
||||
_update_tile_object(tile_root, location_data)
|
||||
_update_tile_inventory(tile_root, location_data)
|
||||
|
||||
_tile_nodes[coord] = tile_root
|
||||
|
||||
|
||||
func _update_tile(coord: Vector2i, location_data: Dictionary) -> void:
|
||||
@@ -163,47 +165,77 @@ func _update_tile(coord: Vector2i, location_data: Dictionary) -> void:
|
||||
if tile_root == null:
|
||||
return
|
||||
|
||||
if show_tile_labels:
|
||||
var label := tile_root.get_node_or_null("LocationNameLabel") as Label3D
|
||||
if label:
|
||||
label.text = String(location_data.get("name", ""))
|
||||
|
||||
_update_tile_object(tile_root, location_data)
|
||||
if show_tile_labels:
|
||||
var label := tile_root.get_node_or_null("LocationNameLabel") as Label3D
|
||||
if label:
|
||||
label.text = String(location_data.get("name", ""))
|
||||
|
||||
_update_tile_object(tile_root, location_data)
|
||||
_update_tile_inventory(tile_root, location_data)
|
||||
|
||||
|
||||
func _update_tile_object(tile_root: Node3D, location_data: Dictionary) -> void:
|
||||
var existing := tile_root.get_node_or_null("LocationObject")
|
||||
if existing:
|
||||
existing.queue_free()
|
||||
|
||||
var object_data_variant: Variant = location_data.get("locationObject", {})
|
||||
if typeof(object_data_variant) != TYPE_DICTIONARY:
|
||||
return
|
||||
|
||||
var object_data := object_data_variant as Dictionary
|
||||
if object_data.is_empty():
|
||||
return
|
||||
|
||||
var object_root := Node3D.new()
|
||||
object_root.name = "LocationObject"
|
||||
object_root.position = Vector3(0.0, (block_height * 0.5) + 0.6, 0.0)
|
||||
tile_root.add_child(object_root)
|
||||
|
||||
var object_mesh := MeshInstance3D.new()
|
||||
object_mesh.name = "ObjectMesh"
|
||||
object_mesh.mesh = SphereMesh.new()
|
||||
object_mesh.scale = Vector3(0.6, 0.4, 0.6)
|
||||
object_mesh.material_override = _create_object_material(String(object_data.get("objectKey", "")))
|
||||
object_root.add_child(object_mesh)
|
||||
|
||||
var object_label := Label3D.new()
|
||||
object_label.name = "ObjectLabel"
|
||||
object_label.text = _build_object_label(object_data)
|
||||
object_label.position = Vector3(0.0, 0.6, 0.0)
|
||||
object_label.billboard = BaseMaterial3D.BILLBOARD_ENABLED
|
||||
object_label.pixel_size = 0.01
|
||||
object_label.outline_size = 10
|
||||
object_root.add_child(object_label)
|
||||
func _update_tile_object(tile_root: Node3D, location_data: Dictionary) -> void:
|
||||
var object_data_variant: Variant = location_data.get("locationObject", {})
|
||||
if typeof(object_data_variant) != TYPE_DICTIONARY:
|
||||
var existing_missing := tile_root.get_node_or_null("LocationObject")
|
||||
if existing_missing:
|
||||
existing_missing.queue_free()
|
||||
return
|
||||
|
||||
var object_data := object_data_variant as Dictionary
|
||||
if object_data.is_empty():
|
||||
var existing_empty := tile_root.get_node_or_null("LocationObject")
|
||||
if existing_empty:
|
||||
existing_empty.queue_free()
|
||||
return
|
||||
|
||||
var object_root := tile_root.get_node_or_null("LocationObject") as Node3D
|
||||
if object_root == null:
|
||||
object_root = Node3D.new()
|
||||
object_root.name = "LocationObject"
|
||||
object_root.position = Vector3(0.0, (block_height * 0.5) + 0.6, 0.0)
|
||||
tile_root.add_child(object_root)
|
||||
|
||||
var object_mesh := object_root.get_node_or_null("ObjectMesh") as MeshInstance3D
|
||||
if object_mesh == null:
|
||||
object_mesh = MeshInstance3D.new()
|
||||
object_mesh.name = "ObjectMesh"
|
||||
object_mesh.mesh = SphereMesh.new()
|
||||
object_mesh.scale = Vector3(0.6, 0.4, 0.6)
|
||||
object_root.add_child(object_mesh)
|
||||
object_mesh.material_override = _create_object_material(String(object_data.get("objectKey", "")))
|
||||
|
||||
var object_label := object_root.get_node_or_null("ObjectLabel") as Label3D
|
||||
if object_label == null:
|
||||
object_label = Label3D.new()
|
||||
object_label.name = "ObjectLabel"
|
||||
object_label.position = Vector3(0.0, 0.6, 0.0)
|
||||
object_label.billboard = BaseMaterial3D.BILLBOARD_ENABLED
|
||||
object_label.pixel_size = 0.01
|
||||
object_label.outline_size = 10
|
||||
object_root.add_child(object_label)
|
||||
object_label.text = _build_object_label(object_data)
|
||||
|
||||
|
||||
func _update_tile_inventory(tile_root: Node3D, location_data: Dictionary) -> void:
|
||||
var floor_items: Array = location_data.get("floorItems", [])
|
||||
var existing_label := tile_root.get_node_or_null("FloorInventoryLabel") as Label3D
|
||||
if floor_items.is_empty():
|
||||
if existing_label:
|
||||
existing_label.queue_free()
|
||||
return
|
||||
|
||||
if existing_label == null:
|
||||
existing_label = Label3D.new()
|
||||
existing_label.name = "FloorInventoryLabel"
|
||||
existing_label.position = Vector3(0.0, (block_height * 0.5) + 1.25, 0.0)
|
||||
existing_label.billboard = BaseMaterial3D.BILLBOARD_ENABLED
|
||||
existing_label.pixel_size = 0.01
|
||||
existing_label.outline_size = 10
|
||||
existing_label.modulate = Color(1.0, 0.95, 0.75, 1.0)
|
||||
tile_root.add_child(existing_label)
|
||||
|
||||
existing_label.text = _build_floor_inventory_label(floor_items)
|
||||
|
||||
|
||||
func _create_tile_border() -> MeshInstance3D:
|
||||
@@ -273,15 +305,37 @@ func _create_object_material(object_key: String) -> StandardMaterial3D:
|
||||
return material
|
||||
|
||||
|
||||
func _build_object_label(object_data: Dictionary) -> String:
|
||||
func _build_object_label(object_data: Dictionary) -> String:
|
||||
var object_name := String(object_data.get("name", "")).strip_edges()
|
||||
var state_variant: Variant = object_data.get("state", {})
|
||||
var remaining_quantity := 0
|
||||
if typeof(state_variant) == TYPE_DICTIONARY:
|
||||
remaining_quantity = int((state_variant as Dictionary).get("remainingQuantity", 0))
|
||||
if object_name.is_empty():
|
||||
object_name = "Object"
|
||||
return "%s x%d" % [object_name, remaining_quantity]
|
||||
if object_name.is_empty():
|
||||
object_name = "Object"
|
||||
return "%s x%d" % [object_name, remaining_quantity]
|
||||
|
||||
|
||||
func _build_floor_inventory_label(floor_items: Array) -> String:
|
||||
var parts: Array[String] = []
|
||||
for entry in floor_items:
|
||||
if typeof(entry) != TYPE_DICTIONARY:
|
||||
continue
|
||||
var item := entry as Dictionary
|
||||
parts.append("%s x%d" % [
|
||||
String(item.get("itemKey", "")).strip_edges(),
|
||||
int(item.get("quantity", 0))
|
||||
])
|
||||
if parts.size() >= 3:
|
||||
break
|
||||
|
||||
if parts.is_empty():
|
||||
return ""
|
||||
|
||||
var label := "Floor: %s" % ", ".join(parts)
|
||||
if floor_items.size() > parts.size():
|
||||
label += " ..."
|
||||
return label
|
||||
|
||||
|
||||
func _ensure_selected_location_exists(coord: Vector2i) -> void:
|
||||
@@ -291,7 +345,8 @@ func _ensure_selected_location_exists(coord: Vector2i) -> void:
|
||||
"id": "",
|
||||
"name": _selected_location_name(coord),
|
||||
"biomeKey": "plains",
|
||||
"locationObject": {}
|
||||
"locationObject": {},
|
||||
"floorItems": []
|
||||
}
|
||||
|
||||
|
||||
@@ -367,13 +422,16 @@ func _load_existing_locations() -> void:
|
||||
"id": String(location.get("id", "")).strip_edges(),
|
||||
"name": location_name,
|
||||
"biomeKey": String(location.get("biomeKey", "plains")).strip_edges(),
|
||||
"locationObject": _parse_location_object(location.get("locationObject", {}))
|
||||
"locationObject": _parse_location_object(location.get("locationObject", {})),
|
||||
"floorItems": []
|
||||
}
|
||||
loaded_count += 1
|
||||
|
||||
print("LocationLevel loaded %d visible locations for character %s." % [loaded_count, _character_id])
|
||||
if loaded_count == 0:
|
||||
push_warning("Visible locations request succeeded but returned 0 locations for character %s." % _character_id)
|
||||
|
||||
print("LocationLevel loaded %d visible locations for character %s." % [loaded_count, _character_id])
|
||||
if loaded_count == 0:
|
||||
push_warning("Visible locations request succeeded but returned 0 locations for character %s." % _character_id)
|
||||
|
||||
await _load_visible_location_inventories()
|
||||
|
||||
_locations_loaded = true
|
||||
_locations_refresh_in_flight = false
|
||||
@@ -467,9 +525,10 @@ func _interact_with_location_async(location_id: String, object_id: String) -> vo
|
||||
_interact_in_flight = false
|
||||
return
|
||||
|
||||
var interaction := parsed as Dictionary
|
||||
_apply_interaction_result(location_id, interaction)
|
||||
_interact_in_flight = false
|
||||
var interaction := parsed as Dictionary
|
||||
_apply_interaction_result(location_id, interaction)
|
||||
await _refresh_location_inventory(location_id)
|
||||
_interact_in_flight = false
|
||||
|
||||
|
||||
func _apply_interaction_result(location_id: String, interaction: Dictionary) -> void:
|
||||
@@ -562,6 +621,83 @@ func _parse_location_object(value: Variant) -> Dictionary:
|
||||
"name": String(object_data.get("name", "")).strip_edges(),
|
||||
"state": state
|
||||
}
|
||||
|
||||
|
||||
func _parse_floor_inventory_items(value: Variant) -> Array:
|
||||
var items: Array = []
|
||||
if typeof(value) != TYPE_ARRAY:
|
||||
return items
|
||||
|
||||
for entry in value:
|
||||
if typeof(entry) != TYPE_DICTIONARY:
|
||||
continue
|
||||
var item := entry as Dictionary
|
||||
items.append({
|
||||
"itemKey": String(item.get("itemKey", "")).strip_edges(),
|
||||
"quantity": int(item.get("quantity", 0)),
|
||||
"slot": item.get("slot", null)
|
||||
})
|
||||
|
||||
return items
|
||||
|
||||
|
||||
func _load_visible_location_inventories() -> void:
|
||||
for coord_variant in _known_locations.keys():
|
||||
var coord: Vector2i = coord_variant
|
||||
var location_data: Dictionary = _known_locations[coord]
|
||||
var location_id := String(location_data.get("id", "")).strip_edges()
|
||||
if location_id.is_empty():
|
||||
continue
|
||||
var floor_items := await _fetch_location_inventory(location_id)
|
||||
location_data["floorItems"] = floor_items
|
||||
_known_locations[coord] = location_data
|
||||
|
||||
|
||||
func _refresh_location_inventory(location_id: String) -> void:
|
||||
if location_id.is_empty():
|
||||
return
|
||||
var floor_items := await _fetch_location_inventory(location_id)
|
||||
for coord_variant in _known_locations.keys():
|
||||
var coord: Vector2i = coord_variant
|
||||
var location_data: Dictionary = _known_locations[coord]
|
||||
if String(location_data.get("id", "")).strip_edges() != location_id:
|
||||
continue
|
||||
location_data["floorItems"] = floor_items
|
||||
_known_locations[coord] = location_data
|
||||
_update_tile(coord, location_data)
|
||||
return
|
||||
|
||||
|
||||
func _fetch_location_inventory(location_id: String) -> Array:
|
||||
var request := HTTPRequest.new()
|
||||
add_child(request)
|
||||
|
||||
var headers := PackedStringArray()
|
||||
if not AuthState.access_token.is_empty():
|
||||
headers.append("Authorization: Bearer %s" % AuthState.access_token)
|
||||
|
||||
var err := request.request("%s/by-owner/location/%s" % [INVENTORY_API_URL, location_id], headers, HTTPClient.METHOD_GET)
|
||||
if err != OK:
|
||||
request.queue_free()
|
||||
push_warning("Failed to request floor inventory for location %s: %s" % [location_id, err])
|
||||
return []
|
||||
|
||||
var result: Array = await request.request_completed
|
||||
request.queue_free()
|
||||
|
||||
var result_code: int = result[0]
|
||||
var response_code: int = result[1]
|
||||
var response_body: String = result[3].get_string_from_utf8()
|
||||
if result_code != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
|
||||
push_warning("Failed to load floor inventory for location %s (%s/%s): %s" % [location_id, result_code, response_code, response_body])
|
||||
return []
|
||||
|
||||
var parsed: Variant = JSON.parse_string(response_body)
|
||||
if typeof(parsed) != TYPE_DICTIONARY:
|
||||
return []
|
||||
|
||||
var payload := parsed as Dictionary
|
||||
return _parse_floor_inventory_items(payload.get("items", []))
|
||||
|
||||
|
||||
func _get_biome_material(tile: MeshInstance3D, biome_key: String) -> Material:
|
||||
|
||||
Reference in New Issue
Block a user