diff --git a/data/coin_flower.tres b/data/coin_flower.tres index a863c7d..5608935 100644 --- a/data/coin_flower.tres +++ b/data/coin_flower.tres @@ -1,13 +1,12 @@ [gd_resource type="Resource" script_class="InventoryItem" load_steps=3 format=3 uid="uid://nrpcuqveh7io"] -[ext_resource type="Texture2D" uid="uid://cb6qv3c0iojfl" path="res://icon.svg" id="1_u477x"] +[ext_resource type="Texture2D" uid="uid://dw3x3h3f34sy3" path="res://assets/coin_flower.png" id="1_vrj0d"] [ext_resource type="Script" path="res://src/lib/inventory_item.gd" id="3_fe16f"] [resource] script = ExtResource("3_fe16f") -icon = ExtResource("1_u477x") +icon = ExtResource("1_vrj0d") name = "Coin Flower" id = &"coin_flower" stackable = false stack_limit = 8 -count = 0 diff --git a/src/ingame/player.gd b/src/ingame/player.gd index 1c34198..90dc056 100644 --- a/src/ingame/player.gd +++ b/src/ingame/player.gd @@ -25,7 +25,7 @@ var _projectile_speed := 12.0 var _interaction_selection: Node3D var controls_disabled := false -var held_thing: InventoryItem +var held_thing := { "item_id": &"empty_hand", "count": 0 } # What the others see. @@ -59,7 +59,7 @@ func _physics_process(delta: float) -> void: ## Process interactivity selection. if id == multiplayer.get_unique_id(): var collider: Object = null - if held_thing == null and _line_of_sight.is_colliding(): + if empty_handed() and _line_of_sight.is_colliding(): collider = _line_of_sight.get_collider(0) if collider != _interaction_selection: if _interaction_selection != null: @@ -103,17 +103,22 @@ func _physics_process(delta: float) -> void: @rpc("any_peer", "call_local", "reliable") -func hold_thing(p_item_instance_id: int) -> void: - var item = instance_from_id(p_item_instance_id) as InventoryItem - assert(item != null) - held_thing = item +func hold_thing(p_bundle: Dictionary) -> void: + var item := GameState.fetch().INVENTORY_ITEM_DB[p_bundle["item_id"]] as InventoryItem + held_thing = p_bundle var base_node := _camera_pivot.get_node("HeldViewmodel") for child in base_node.get_children(): child.queue_free() if item.model != null: var model = item.model.instantiate() base_node.add_child(model) - if id == multiplayer.get_unique_id(): + else: + # Create a icon sprite based one instead. + var model := preload("res://src/quad_viewmodel.tscn").instantiate() + model.reflect_bundle(p_bundle) + base_node.add_child(model) + + if item.model != null and id == multiplayer.get_unique_id(): # Disable depth test and increase render priority. # TODO: in more complex scenarios model scene might want to have its own callback for this. for model in base_node.get_children(): @@ -127,11 +132,15 @@ func hold_thing(p_item_instance_id: int) -> void: @rpc("any_peer", "call_local", "reliable") func throw_thing() -> void: - held_thing = null # TODO: represent 'empty hand' as a unique item ? + held_thing = { "item_id": &"empty_hand", "count": 0 } for child in _camera_pivot.get_node("HeldViewmodel").get_children(): child.queue_free() +func empty_handed() -> bool: + return held_thing["item_id"] == &"empty_hand" + + func _unhandled_input(event: InputEvent) -> void: if controls_disabled or id != multiplayer.get_unique_id(): return @@ -142,12 +151,12 @@ func _unhandled_input(event: InputEvent) -> void: _camera_pivot.rotation.x = clamp(_camera_pivot.rotation.x, -1.2, 1.2) return - if event.is_action_pressed("pick") and held_thing == null: + if event.is_action_pressed("pick") and empty_handed(): if _interaction_selection != null: - hold_thing.rpc(_interaction_selection.owner.item_component.get_instance_id()) _interaction_selection.owner.get_picked_up.rpc() + hold_thing.rpc(_interaction_selection.owner.item_bundle) - if event.is_action_pressed("fire") and held_thing != null: + if event.is_action_pressed("fire") and not empty_handed(): var new_projectile: Node3D = _projectile_scene.instantiate() _projectile_holder.add_child(new_projectile, true) _set_projectile_authority.rpc(new_projectile.get_path(), id) diff --git a/src/ingame/sprout.gd b/src/ingame/sprout.gd index ff82bf9..9addc7c 100644 --- a/src/ingame/sprout.gd +++ b/src/ingame/sprout.gd @@ -5,7 +5,7 @@ extends Node3D @export var _mesh: MeshInstance3D @export var _area: Area3D -@export var item_component: InventoryItem +var item_bundle := { "item_id": &"coin_flower", "count": 1 } var needs_water := true var stage: int = 1 @@ -47,6 +47,7 @@ func water(sender_id: int) -> void: @rpc("any_peer", "call_local", "reliable") func get_picked_up() -> void: + assert(stage == final_stage) stage = 1 _mesh.material_override.set("shader_parameter/albedo_texture", load("res://assets/sprout%s.png" % stage)) _area.collision_layer ^= 1 << 4 diff --git a/src/ingame/sprout.tscn b/src/ingame/sprout.tscn index 4c01a5a..3860090 100644 --- a/src/ingame/sprout.tscn +++ b/src/ingame/sprout.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=10 format=3 uid="uid://bysgtksvovyur"] +[gd_scene load_steps=9 format=3 uid="uid://bysgtksvovyur"] [ext_resource type="Script" path="res://src/ingame/sprout.gd" id="1_snma1"] -[ext_resource type="Resource" uid="uid://nrpcuqveh7io" path="res://data/coin_flower.tres" id="2_amjnn"] [ext_resource type="Texture2D" uid="uid://d35y5ckne72qe" path="res://assets/sprout1.png" id="2_ipgad"] [ext_resource type="Shader" path="res://assets/shaders/interactivity_outline2.gdshader" id="2_oa2it"] [ext_resource type="Texture2D" uid="uid://cwbl0r1e26eja" path="res://assets/drop.png" id="3_kghdv"] @@ -36,7 +35,6 @@ _need_water_drop = NodePath("NeedWaterDrop") _production_timer = NodePath("ProductionTimer") _mesh = NodePath("Mesh") _area = NodePath("Area3D") -item_component = ExtResource("2_amjnn") [node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."] replication_config = SubResource("SceneReplicationConfig_rs2qp") diff --git a/src/ingame/water_bomb.gd b/src/ingame/water_bomb.gd index 286d1bb..015aebc 100644 --- a/src/ingame/water_bomb.gd +++ b/src/ingame/water_bomb.gd @@ -8,8 +8,8 @@ extends Node3D @export var body: RigidBody3D ## no longer exists and shouldn't be considered, but not ready to be freed yet @export var is_dead := false -## by convention is looked up in owner from areas -@export var item_component: InventoryItem + +const item_bundle := { "item_id": &"water_bomb", "count": 1 } var _in_splash_range := {} diff --git a/src/ingame/water_bomb.tscn b/src/ingame/water_bomb.tscn index 63b8d00..130ea8c 100644 --- a/src/ingame/water_bomb.tscn +++ b/src/ingame/water_bomb.tscn @@ -1,8 +1,7 @@ -[gd_scene load_steps=12 format=3 uid="uid://tdsbo3e5ic86"] +[gd_scene load_steps=11 format=3 uid="uid://tdsbo3e5ic86"] [ext_resource type="Script" path="res://src/ingame/water_bomb.gd" id="1_lk5fq"] [ext_resource type="AudioStream" uid="uid://dtjpv2b74g24m" path="res://assets/sfx/splash-small.wav" id="2_0wk8g"] -[ext_resource type="Resource" uid="uid://cmeif37pci2ek" path="res://data/water_bomb.tres" id="2_b3357"] [ext_resource type="PackedScene" uid="uid://ba2mut58elwrh" path="res://assets/water-bomb.glb" id="2_v2imr"] [ext_resource type="AudioStream" uid="uid://blgrl2wl05feq" path="res://assets/sfx/splash-small-quiet.wav" id="3_hgy7l"] @@ -58,7 +57,6 @@ _model = NodePath("Model") _splash_particles = NodePath("SplashParticles") _picking_area = NodePath("PickingArea") body = NodePath("RigidBody3D") -item_component = ExtResource("2_b3357") [node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."] replication_config = SubResource("SceneReplicationConfig_0ebrr") diff --git a/src/lib/game_state.gd b/src/lib/game_state.gd index 2e646ee..e8030b8 100644 --- a/src/lib/game_state.gd +++ b/src/lib/game_state.gd @@ -1,17 +1,18 @@ -class_name GameState -extends Resource +class_name GameState extends Resource +# TODO: build dynamically? const INVENTORY_ITEM_DB = { &"meat": preload("res://data/meat.tres"), &"water_bomb": preload("res://data/water_bomb.tres"), &"coin_flower": preload("res://data/coin_flower.tres"), } -# keys are multiplayer ID ints, values are PlayerData +## keys are multiplayer ID ints, values are PlayerData @export var player_data := {} -# keys are InventoryItem resource IDs (from db), values are InventoryItems +## keys are InventoryItem resource IDs (from db), +## values are { "item": InventoryItem, "count": int } @export var inventory := {} -@export var coins: int +@export var coins: int # TODO: might it make sense to have even them as items? static var _instance := GameState.new() diff --git a/src/lib/inventory_item.gd b/src/lib/inventory_item.gd index 87a3104..b981271 100644 --- a/src/lib/inventory_item.gd +++ b/src/lib/inventory_item.gd @@ -8,5 +8,3 @@ extends Resource @export var stackable := false @export var stack_limit := 8 -# TODO: should it be here? or context dependent, for different inventories -@export var count := 0 diff --git a/src/main/main.gd b/src/main/main.gd index 7a10fbb..9248622 100644 --- a/src/main/main.gd +++ b/src/main/main.gd @@ -185,32 +185,36 @@ func set_coins(value: int) -> void: GameState.fetch().coins = value +## Make sure it exists and all. +func prepate_inventory_idem(item_id: StringName) -> void: + if not GameState.fetch().inventory.has(item_id): + GameState.fetch().inventory[item_id] = { + "item": GameState.INVENTORY_ITEM_DB[item_id], + "count": 0, + } + + @rpc("authority", "call_local", "reliable") func add_inventory_item(item_id: StringName, amount: int) -> void: - if not GameState.fetch().inventory.has(item_id): - GameState.fetch().inventory[item_id] = GameState.INVENTORY_ITEM_DB[item_id].duplicate() - - GameState.fetch().inventory[item_id].count += amount + prepate_inventory_idem(item_id) + GameState.fetch().inventory[item_id]["count"] += amount @rpc("authority", "call_local", "reliable") func remove_inventory_item(item_id: StringName, amount: int) -> void: assert(GameState.fetch().inventory.has(item_id)) - GameState.fetch().inventory[item_id].count -= amount - assert(GameState.fetch().inventory[item_id].count >= 0) + GameState.fetch().inventory[item_id]["count"] -= amount + assert(GameState.fetch().inventory[item_id]["count"] >= 0) - if GameState.fetch().inventory[item_id].count == 0: + if GameState.fetch().inventory[item_id]["count"] == 0: GameState.fetch().inventory.erase(item_id) @rpc("authority", "call_local", "reliable") func set_inventory_item_count(item_id: StringName, value: int) -> void: - assert(value >= 0) - if not GameState.fetch().inventory.has(item_id): - GameState.fetch().inventory[item_id] = GameState.INVENTORY_ITEM_DB[item_id].duplicate() + prepate_inventory_idem(item_id) + GameState.fetch().inventory[item_id]["count"] = value - GameState.fetch().inventory[item_id].count = value - - if GameState.fetch().inventory[item_id].count == 0: + if GameState.fetch().inventory[item_id]["count"] == 0: GameState.fetch().inventory.erase(item_id)