fix alot of shit for webrtc to work
This commit is contained in:
@@ -17,6 +17,7 @@ var is_being_held: bool = false
|
||||
var held_by_player = null
|
||||
var is_frozen: bool = false
|
||||
var thrown_by_player = null # Track who threw this box
|
||||
var is_broken: bool = false
|
||||
|
||||
# Physics for thrown objects
|
||||
var throw_velocity: Vector2 = Vector2.ZERO
|
||||
@@ -47,6 +48,17 @@ func _ready():
|
||||
collision_layer = 2 # Layer 2 for objects
|
||||
collision_mask = 1 | 2 | 4 # Collide with players, other objects, and walls
|
||||
|
||||
# Ensure deterministic name for network sync
|
||||
if has_meta("object_index") and not name.begins_with("InteractableObject_"):
|
||||
name = "InteractableObject_%d" % get_meta("object_index")
|
||||
elif name.begins_with("InteractableObject_"):
|
||||
# Ensure meta matches name if it already has a consistent name
|
||||
var index_str = name.substr(20)
|
||||
if index_str.is_valid_int():
|
||||
var name_index = index_str.to_int()
|
||||
if not has_meta("object_index") or get_meta("object_index") != name_index:
|
||||
set_meta("object_index", name_index)
|
||||
|
||||
# No gravity in top-down
|
||||
motion_mode = MOTION_MODE_FLOATING
|
||||
|
||||
@@ -164,9 +176,14 @@ func _handle_air_collision():
|
||||
|
||||
# Box breaks (only if destroyable)
|
||||
if is_destroyable:
|
||||
# Sync break to OTHER clients via RPC BEFORE breaking locally
|
||||
# Use game_world to route the RPC to avoid node path resolution issues
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and game_world.has_method("_rpc_to_ready_peers"):
|
||||
game_world._rpc_to_ready_peers("_sync_object_break", [name])
|
||||
|
||||
_break_into_pieces()
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
_sync_break.rpc()
|
||||
|
||||
return
|
||||
|
||||
@@ -181,11 +198,14 @@ func _handle_air_collision():
|
||||
|
||||
# Hit a player! Break locally and sync to others (only if destroyable)
|
||||
if is_destroyable:
|
||||
_break_into_pieces()
|
||||
# Sync break to OTHER clients via RPC BEFORE breaking locally
|
||||
# Use game_world to route the RPC to avoid node path resolution issues
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and game_world.has_method("_rpc_to_ready_peers"):
|
||||
game_world._rpc_to_ready_peers("_sync_object_break", [name])
|
||||
|
||||
# Sync break to OTHER clients
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
_sync_break.rpc()
|
||||
_break_into_pieces()
|
||||
|
||||
# Damage and knockback player using RPC
|
||||
# Pass the thrower's position for accurate direction
|
||||
@@ -214,24 +234,28 @@ func _handle_air_collision():
|
||||
|
||||
# Hit another box! Break both locally (only if destroyable)
|
||||
if is_destroyable:
|
||||
# Sync break to OTHER clients via RPC BEFORE breaking locally
|
||||
# Use game_world to route the RPC to avoid node path resolution issues
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and game_world.has_method("_rpc_to_ready_peers"):
|
||||
game_world._rpc_to_ready_peers("_sync_object_break", [name])
|
||||
# Tell the other box to break too
|
||||
if collider.has_method("can_be_destroyed") and collider.can_be_destroyed():
|
||||
game_world._rpc_to_ready_peers("_sync_object_break", [collider.name])
|
||||
|
||||
_break_into_pieces()
|
||||
if collider.has_method("_break_into_pieces") and collider.has_method("can_be_destroyed") and collider.can_be_destroyed():
|
||||
collider._break_into_pieces()
|
||||
|
||||
# Sync break to OTHER clients
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
_sync_break.rpc()
|
||||
# Tell the other box to break too
|
||||
if collider.has_method("_sync_break") and collider.has_method("can_be_destroyed") and collider.can_be_destroyed():
|
||||
collider._sync_break.rpc()
|
||||
|
||||
print(name, " hit another box!")
|
||||
return
|
||||
|
||||
func _break_into_pieces():
|
||||
func _break_into_pieces(silent: bool = false):
|
||||
# Only break if destroyable
|
||||
if not is_destroyable:
|
||||
if not is_destroyable or is_broken:
|
||||
return
|
||||
is_broken = true
|
||||
|
||||
var sprite_texture = $Sprite2D.texture
|
||||
var frame_width = sprite_texture.get_width() / $Sprite2D.hframes
|
||||
@@ -254,27 +278,28 @@ func _break_into_pieces():
|
||||
Rect2(frame_x + frame_width / 2, frame_y + frame_height / 2, frame_width / 2, frame_height / 2) # Bottom-right
|
||||
]
|
||||
|
||||
for i in range(4):
|
||||
var tp = tileParticleScene.instantiate() as CharacterBody2D
|
||||
var spr2D = tp.get_node("Sprite2D") as Sprite2D
|
||||
tp.global_position = global_position
|
||||
if not silent:
|
||||
for i in range(4):
|
||||
var tp = tileParticleScene.instantiate() as CharacterBody2D
|
||||
var spr2D = tp.get_node("Sprite2D") as Sprite2D
|
||||
tp.global_position = global_position
|
||||
|
||||
# Set up the sprite's texture and region
|
||||
spr2D.texture = sprite_texture
|
||||
spr2D.region_enabled = true
|
||||
spr2D.region_rect = regions[i]
|
||||
|
||||
# Add some randomness to the velocity
|
||||
var speed = randf_range(170, 200)
|
||||
var dir = directions[i] + Vector2(randf_range(-0.2, 0.2), randf_range(-0.2, 0.2))
|
||||
tp.velocity = dir * speed
|
||||
|
||||
# Add some rotation
|
||||
tp.angular_velocity = randf_range(-7, 7)
|
||||
|
||||
get_parent().call_deferred("add_child", tp)
|
||||
|
||||
# Set up the sprite's texture and region
|
||||
spr2D.texture = sprite_texture
|
||||
spr2D.region_enabled = true
|
||||
spr2D.region_rect = regions[i]
|
||||
|
||||
# Add some randomness to the velocity
|
||||
var speed = randf_range(170, 200)
|
||||
var dir = directions[i] + Vector2(randf_range(-0.2, 0.2), randf_range(-0.2, 0.2))
|
||||
tp.velocity = dir * speed
|
||||
|
||||
# Add some rotation
|
||||
tp.angular_velocity = randf_range(-7, 7)
|
||||
|
||||
get_parent().call_deferred("add_child", tp)
|
||||
|
||||
play_destroy_sound()
|
||||
play_destroy_sound()
|
||||
self.set_deferred("collision_layer", 0)
|
||||
$Shadow.visible = false
|
||||
$Sprite2DAbove.visible = false
|
||||
@@ -292,18 +317,47 @@ func _break_into_pieces():
|
||||
ItemLootHelper.spawn_item_loot(item, global_position, entities_node, game_world)
|
||||
print(name, " dropped item: ", item.item_name, " when broken")
|
||||
|
||||
if ($SfxShatter.playing):
|
||||
await $SfxShatter.finished
|
||||
if ($SfxBreakCrate.playing):
|
||||
await $SfxBreakCrate.finished
|
||||
if not silent:
|
||||
if ($SfxShatter.playing):
|
||||
await $SfxShatter.finished
|
||||
if ($SfxBreakCrate.playing):
|
||||
await $SfxBreakCrate.finished
|
||||
# Remove self
|
||||
queue_free()
|
||||
|
||||
func can_be_grabbed() -> bool:
|
||||
return is_grabbable and not is_being_held
|
||||
|
||||
func _get_configured_object_type() -> String:
|
||||
# Prefer the configured type from dungeon data if available
|
||||
var idx = -1
|
||||
if name.begins_with("InteractableObject_"):
|
||||
var index_str = name.substr(20)
|
||||
if index_str.is_valid_int():
|
||||
idx = index_str.to_int()
|
||||
elif has_meta("object_index"):
|
||||
idx = get_meta("object_index")
|
||||
|
||||
if idx >= 0:
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and "dungeon_data" in game_world and game_world.dungeon_data.has("interactable_objects"):
|
||||
var objects = game_world.dungeon_data.interactable_objects
|
||||
if idx < objects.size():
|
||||
var obj_data = objects[idx]
|
||||
if obj_data is Dictionary and obj_data.has("type"):
|
||||
return obj_data.type
|
||||
|
||||
return object_type
|
||||
|
||||
func can_be_lifted() -> bool:
|
||||
# Can be lifted if it's liftable (being held is OK - we're checking if it CAN be lifted)
|
||||
var resolved_type = object_type
|
||||
if resolved_type == "":
|
||||
resolved_type = _get_configured_object_type()
|
||||
if resolved_type in ["Box", "Pot", "LiftableBarrel"]:
|
||||
return true
|
||||
if resolved_type in ["Chest", "Pillar", "PushableBarrel", "PushableHighBox"]:
|
||||
return false
|
||||
return is_liftable
|
||||
|
||||
func can_be_thrown() -> bool:
|
||||
@@ -321,8 +375,16 @@ func on_grabbed(by_player):
|
||||
# Client - send request to server
|
||||
if by_player:
|
||||
var player_peer_id = by_player.get_multiplayer_authority()
|
||||
print("Chest: Client sending RPC to open chest, player_peer_id: ", player_peer_id)
|
||||
_request_chest_open.rpc_id(1, player_peer_id)
|
||||
# Use consistent object name based on object_index to avoid NodePath issues
|
||||
var chest_name = name
|
||||
if has_meta("object_index"):
|
||||
chest_name = "InteractableObject_%d" % get_meta("object_index")
|
||||
print("Chest: Client sending RPC to open chest, player_peer_id: ", player_peer_id, " chest_name: ", chest_name)
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and game_world.has_method("_request_chest_open_by_name"):
|
||||
game_world._request_chest_open_by_name.rpc_id(1, chest_name, player_peer_id)
|
||||
else:
|
||||
push_warning("Chest: GameWorld not ready, cannot send chest open request for " + chest_name)
|
||||
else:
|
||||
# Server or single player - open directly
|
||||
_open_chest(by_player)
|
||||
@@ -397,10 +459,10 @@ func _sync_box_state(pos: Vector2, vel: Vector2, z_pos: float, z_vel: float, air
|
||||
is_airborne = airborne
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func _sync_break():
|
||||
func _sync_break(silent: bool = false):
|
||||
# Sync break to all clients including server (called by whoever breaks the box)
|
||||
if not is_queued_for_deletion():
|
||||
_break_into_pieces()
|
||||
if not is_queued_for_deletion() and not is_broken:
|
||||
_break_into_pieces(silent)
|
||||
|
||||
# Object type setup functions
|
||||
func setup_pot():
|
||||
@@ -448,7 +510,21 @@ func setup_box():
|
||||
|
||||
var box_frames = [7, 26]
|
||||
if sprite:
|
||||
sprite.frame = box_frames[randi() % box_frames.size()]
|
||||
# Use deterministic randomness based on dungeon seed and position
|
||||
# This ensures host and clients get the same box variant
|
||||
var box_seed = 0
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and "dungeon_seed" in game_world:
|
||||
box_seed = game_world.dungeon_seed
|
||||
# Add position and object_index to seed to make each box unique but deterministic
|
||||
box_seed += int(global_position.x) * 1000 + int(global_position.y)
|
||||
if has_meta("object_index"):
|
||||
box_seed += get_meta("object_index") * 10000
|
||||
|
||||
var rng = RandomNumberGenerator.new()
|
||||
rng.seed = box_seed
|
||||
var index = rng.randi() % box_frames.size()
|
||||
sprite.frame = box_frames[index]
|
||||
|
||||
func setup_chest():
|
||||
object_type = "Chest"
|
||||
@@ -503,7 +579,19 @@ func setup_pushable_high_box():
|
||||
|
||||
var bottom_frames = [24, 25]
|
||||
var top_frames = [5, 6]
|
||||
var index = randi() % bottom_frames.size()
|
||||
|
||||
# Use deterministic randomness based on dungeon seed and position
|
||||
# This ensures host and clients get the same chest variant
|
||||
var highbox_seed = 0
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and "dungeon_seed" in game_world:
|
||||
highbox_seed = game_world.dungeon_seed
|
||||
# Add position to seed to make each chest unique but deterministic
|
||||
highbox_seed += int(global_position.x) * 1000 + int(global_position.y)
|
||||
|
||||
var rng = RandomNumberGenerator.new()
|
||||
rng.seed = highbox_seed
|
||||
var index = rng.randi() % bottom_frames.size()
|
||||
|
||||
if sprite:
|
||||
sprite.frame = bottom_frames[index]
|
||||
@@ -519,6 +607,14 @@ func _open_chest(by_player: Node = null):
|
||||
return
|
||||
$SfxOpenChest.play()
|
||||
is_chest_opened = true
|
||||
|
||||
# Track opened chest for syncing to new clients
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and has_meta("object_index"):
|
||||
var obj_index = get_meta("object_index")
|
||||
game_world.opened_chests[obj_index] = true
|
||||
LogManager.log("Chest: Tracked opened chest with index " + str(obj_index), LogManager.CATEGORY_NETWORK)
|
||||
if sprite and chest_opened_frame >= 0:
|
||||
sprite.frame = chest_opened_frame
|
||||
|
||||
@@ -579,7 +675,12 @@ func _open_chest(by_player: Node = null):
|
||||
# Sync chest opening visual to all clients (item already given on server)
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
var player_peer_id = by_player.get_multiplayer_authority() if by_player else 0
|
||||
_sync_chest_open.rpc(selected_loot.type if by_player else "coin", player_peer_id)
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and game_world.has_method("_rpc_to_ready_peers"):
|
||||
var chest_name = name
|
||||
if has_meta("object_index"):
|
||||
chest_name = "InteractableObject_%d" % get_meta("object_index")
|
||||
game_world._rpc_to_ready_peers("_sync_chest_open_by_name", [chest_name, selected_loot.type if by_player else "coin", player_peer_id])
|
||||
else:
|
||||
push_error("Chest: ERROR - No valid player to give item to!")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user