Add gathering, unified inventory, and real-time world
This commit is contained in:
parent
173371e6fc
commit
fa57edca83
113 changed files with 6700 additions and 1307 deletions
26
gathering/catalog/beetle_stag_common.tres
Normal file
26
gathering/catalog/beetle_stag_common.tres
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
|
||||
[ext_resource type="Resource" path="res://fish/species/beetle_stag_common/beetle_stag_common.tres" id="2_catch"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_data")
|
||||
type_id = &"beetle_stag_common"
|
||||
catch_data = ExtResource("2_catch")
|
||||
required_tool_id = &"crab_net"
|
||||
spawn_anchor_set_id = &"starter_reachable_tree_trunks"
|
||||
population = 3
|
||||
requires_sneaking = false
|
||||
movement_speed = 0.0
|
||||
roam_radius = 0.1
|
||||
scare_radius = 0.1
|
||||
capture_radius = 0.34
|
||||
interaction_range = 2.8
|
||||
charge_duration = 1.0
|
||||
sprite_pixel_size = 0.005
|
||||
sprite_tilt_degrees = 0.0
|
||||
capture_respawn_min_seconds = 90.0
|
||||
capture_respawn_max_seconds = 150.0
|
||||
scare_respawn_min_seconds = 0.0
|
||||
scare_respawn_max_seconds = 0.0
|
||||
minimum_respawn_spacing_seconds = 30.0
|
||||
27
gathering/catalog/clam_manila.tres
Normal file
27
gathering/catalog/clam_manila.tres
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
|
||||
[ext_resource type="Resource" path="res://fish/species/clam_manila/clam_manila.tres" id="2_catch"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_data")
|
||||
type_id = &"clam_manila"
|
||||
catch_data = ExtResource("2_catch")
|
||||
required_tool_id = &"standard_shovel"
|
||||
diggable_area_id = &"starter_beach"
|
||||
minimum_surface_y = -0.44
|
||||
population = 3
|
||||
presentation_mode = 1
|
||||
requires_sneaking = false
|
||||
active_lifetime_seconds = 10.0
|
||||
movement_speed = 0.0
|
||||
roam_radius = 0.1
|
||||
scare_radius = 0.1
|
||||
capture_radius = 0.38
|
||||
interaction_range = 2.4
|
||||
charge_duration = 0.55
|
||||
capture_respawn_min_seconds = 8.0
|
||||
capture_respawn_max_seconds = 14.0
|
||||
scare_respawn_min_seconds = 0.0
|
||||
scare_respawn_max_seconds = 0.0
|
||||
minimum_respawn_spacing_seconds = 4.0
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
[gd_resource type="Resource" script_class="GatherableCatalog" load_steps=6 format=3]
|
||||
[gd_resource type="Resource" script_class="GatherableCatalog" load_steps=8 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://gathering/gatherable_catalog.gd" id="1_catalog"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/crab_brown.tres" id="2_brown"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/crab_ghost.tres" id="3_ghost"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/crab_blue.tres" id="4_blue"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/crab_dungeness.tres" id="5_dungeness"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/clam_manila.tres" id="6_clam"]
|
||||
[ext_resource type="Resource" path="res://gathering/catalog/beetle_stag_common.tres" id="7_beetle"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_catalog")
|
||||
entries = [ExtResource("2_brown"), ExtResource("3_ghost"), ExtResource("4_blue"), ExtResource("5_dungeness")]
|
||||
entries = [ExtResource("2_brown"), ExtResource("3_ghost"), ExtResource("4_blue"), ExtResource("5_dungeness"), ExtResource("6_clam"), ExtResource("7_beetle")]
|
||||
|
|
|
|||
|
|
@ -4,12 +4,22 @@ extends Resource
|
|||
const FishDataType = preload("res://fish/fish_data.gd")
|
||||
const FishQualityType = preload("res://fish/fish_quality.gd")
|
||||
|
||||
enum PresentationMode {
|
||||
VISIBLE_CREATURE,
|
||||
WATER_SPURT,
|
||||
}
|
||||
|
||||
@export var type_id: StringName
|
||||
@export var catch_data: FishDataType
|
||||
@export var required_tool_id: StringName
|
||||
@export var surface_materials: Array[StringName] = []
|
||||
@export var diggable_area_id: StringName
|
||||
@export var spawn_anchor_set_id: StringName
|
||||
@export_range(-100.0, 100.0, 0.01) var minimum_surface_y: float = 0.08
|
||||
@export_range(0, 64, 1) var population: int = 0
|
||||
@export var presentation_mode: PresentationMode = PresentationMode.VISIBLE_CREATURE
|
||||
@export var requires_sneaking: bool = true
|
||||
@export_range(0.0, 120.0, 0.1) var active_lifetime_seconds: float = 0.0
|
||||
@export_range(0.0, 10.0, 0.05) var movement_speed: float = 0.35
|
||||
@export_range(0.1, 20.0, 0.1) var roam_radius: float = 3.5
|
||||
@export_range(0.1, 20.0, 0.1) var scare_radius: float = 2.8
|
||||
|
|
@ -41,17 +51,33 @@ const FishQualityType = preload("res://fish/fish_quality.gd")
|
|||
|
||||
|
||||
func is_valid() -> bool:
|
||||
var uses_diggable_area: bool = not diggable_area_id.is_empty()
|
||||
var uses_spawn_anchors: bool = not spawn_anchor_set_id.is_empty()
|
||||
var movement_parameters_valid: bool = (
|
||||
movement_speed >= 0.0
|
||||
and (
|
||||
presentation_mode == PresentationMode.WATER_SPURT
|
||||
or (roam_radius > 0.0 and scare_radius > 0.0)
|
||||
)
|
||||
)
|
||||
return (
|
||||
not type_id.is_empty()
|
||||
and catch_data != null
|
||||
and catch_data.is_valid_catalog_entry()
|
||||
and catch_data.collection_method != FishDataType.CollectionMethod.FISHING
|
||||
and not required_tool_id.is_empty()
|
||||
and not surface_materials.is_empty()
|
||||
and (
|
||||
uses_diggable_area
|
||||
or uses_spawn_anchors
|
||||
or not surface_materials.is_empty()
|
||||
)
|
||||
and (
|
||||
not uses_diggable_area
|
||||
or catch_data.collection_method
|
||||
== FishDataType.CollectionMethod.DIGGING
|
||||
)
|
||||
and population > 0
|
||||
and movement_speed >= 0.0
|
||||
and roam_radius > 0.0
|
||||
and scare_radius > 0.0
|
||||
and movement_parameters_valid
|
||||
and _quality_multipliers_are_valid(
|
||||
quality_movement_speed_multipliers
|
||||
)
|
||||
|
|
@ -67,6 +93,18 @@ func is_valid() -> bool:
|
|||
)
|
||||
|
||||
|
||||
func is_stationary_hotspot() -> bool:
|
||||
return presentation_mode == PresentationMode.WATER_SPURT
|
||||
|
||||
|
||||
func is_stationary_spawn() -> bool:
|
||||
return is_stationary_hotspot() or not spawn_anchor_set_id.is_empty()
|
||||
|
||||
|
||||
func can_be_scared() -> bool:
|
||||
return not is_stationary_spawn() and scare_radius > 0.0
|
||||
|
||||
|
||||
func get_movement_speed_for_quality(quality: int) -> float:
|
||||
return movement_speed * _quality_multiplier(
|
||||
quality_movement_speed_multipliers,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ var _charging: bool = false
|
|||
var _charge_elapsed: float = 0.0
|
||||
var _charge_duration: float = 2.0
|
||||
var _request_id: String = ""
|
||||
var _active_tool_id: StringName
|
||||
var _gameplay_input_enabled: bool = false
|
||||
|
||||
|
||||
|
|
@ -76,15 +77,40 @@ func is_net_selected() -> bool:
|
|||
)
|
||||
|
||||
|
||||
func is_shovel_selected() -> bool:
|
||||
return _is_owned_tool_selected(FishingShopStockType.STANDARD_SHOVEL_ID)
|
||||
|
||||
|
||||
func _is_owned_tool_selected(tool_id: StringName) -> bool:
|
||||
return (
|
||||
_bag != null
|
||||
and _hotbar != null
|
||||
and _hotbar.get_selected_item_id() == tool_id
|
||||
and _bag.owns_item(tool_id)
|
||||
)
|
||||
|
||||
|
||||
func _get_selected_gathering_tool_id() -> StringName:
|
||||
if is_net_selected():
|
||||
return FishingShopStockType.CRAB_NET_ID
|
||||
if is_shovel_selected():
|
||||
return FishingShopStockType.STANDARD_SHOVEL_ID
|
||||
return StringName()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var selected_tool_id := _get_selected_gathering_tool_id()
|
||||
var input_available: bool = (
|
||||
_gameplay_input_enabled
|
||||
and is_net_selected()
|
||||
and not selected_tool_id.is_empty()
|
||||
and _player != null
|
||||
and _player.is_local_control_enabled()
|
||||
and _fishing_spot != null
|
||||
and _fishing_spot.can_change_hotbar_selection()
|
||||
)
|
||||
if _charging and selected_tool_id != _active_tool_id:
|
||||
_cancel_charge()
|
||||
input_available = false
|
||||
if not input_available:
|
||||
if _charging:
|
||||
_cancel_charge()
|
||||
|
|
@ -105,7 +131,11 @@ func _process(delta: float) -> void:
|
|||
var valid_target: bool = (
|
||||
fully_charged
|
||||
and not _target_entity_id.is_empty()
|
||||
and _player.is_sneaking()
|
||||
and (
|
||||
target_entry == null
|
||||
or not target_entry.requires_sneaking
|
||||
or _player.is_sneaking()
|
||||
)
|
||||
)
|
||||
_marker.material_override = (
|
||||
_marker_valid_material if valid_target else _marker_invalid_material
|
||||
|
|
@ -127,7 +157,7 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
if (
|
||||
_charging
|
||||
or not _gameplay_input_enabled
|
||||
or not is_net_selected()
|
||||
or _get_selected_gathering_tool_id().is_empty()
|
||||
or _fishing_spot == null
|
||||
or not _fishing_spot.can_change_hotbar_selection()
|
||||
):
|
||||
|
|
@ -136,10 +166,11 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
if _request_id.is_empty():
|
||||
return
|
||||
_charging = true
|
||||
_active_tool_id = _get_selected_gathering_tool_id()
|
||||
_charge_elapsed = 0.0
|
||||
_charge_duration = maxf(
|
||||
_service.get_charge_duration_for_tool(
|
||||
FishingShopStockType.CRAB_NET_ID
|
||||
_active_tool_id
|
||||
),
|
||||
0.1,
|
||||
)
|
||||
|
|
@ -151,11 +182,18 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
if not _charging:
|
||||
return
|
||||
var fully_charged: bool = _charge_elapsed >= _charge_duration
|
||||
var target_entry: GatherableData = _service.get_entry_for_entity(
|
||||
_target_entity_id
|
||||
)
|
||||
var valid_capture: bool = (
|
||||
fully_charged
|
||||
and _marker_has_surface
|
||||
and not _target_entity_id.is_empty()
|
||||
and _player.is_sneaking()
|
||||
and (
|
||||
target_entry == null
|
||||
or not target_entry.requires_sneaking
|
||||
or _player.is_sneaking()
|
||||
)
|
||||
)
|
||||
_player.play_net_strike_visual()
|
||||
if valid_capture:
|
||||
|
|
@ -265,7 +303,7 @@ func _update_target_entity() -> void:
|
|||
_target_entity_id = (
|
||||
_service.find_capture_target(
|
||||
_marker_position,
|
||||
FishingShopStockType.CRAB_NET_ID,
|
||||
_active_tool_id,
|
||||
)
|
||||
if _marker_has_surface and _service != null
|
||||
else ""
|
||||
|
|
@ -284,6 +322,7 @@ func _reset_charge_state() -> void:
|
|||
_charging = false
|
||||
_charge_elapsed = 0.0
|
||||
_request_id = ""
|
||||
_active_tool_id = StringName()
|
||||
_target_entity_id = ""
|
||||
_marker_has_surface = false
|
||||
_set_marker_visible(false)
|
||||
|
|
|
|||
|
|
@ -4,11 +4,16 @@ extends Node3D
|
|||
const GatherableDataType = preload("res://gathering/gatherable_data.gd")
|
||||
const REFERENCE_SPRITE_PIXEL_SIZE: float = 0.001
|
||||
const REFERENCE_SPRITE_HEIGHT: float = 0.22
|
||||
const WATER_SPURT_INTERVAL_SECONDS: float = 1.15
|
||||
const WATER_SPURT_COLOR := Color(0.48, 0.82, 0.84, 1.0)
|
||||
const WATER_SPURT_HOLE_COLOR := Color(0.23, 0.27, 0.24, 1.0)
|
||||
|
||||
var entity_id: String = ""
|
||||
var type_id: StringName
|
||||
var data: GatherableDataType
|
||||
var _sprite: Sprite3D
|
||||
var _water_spurt_root: Node3D
|
||||
var _water_spurt_elapsed: float = 0.0
|
||||
var _target_position: Vector3
|
||||
var _target_yaw: float = 0.0
|
||||
var _has_state: bool = false
|
||||
|
|
@ -24,14 +29,25 @@ func configure(
|
|||
entity_id = configured_entity_id
|
||||
data = configured_data
|
||||
type_id = data.type_id if data != null else StringName()
|
||||
_ensure_visual()
|
||||
if data != null and data.catch_data != null:
|
||||
if data != null and data.is_stationary_hotspot():
|
||||
_ensure_water_spurt_visual()
|
||||
_water_spurt_elapsed = (
|
||||
float(abs(hash(entity_id)) % 1000) / 1000.0
|
||||
* WATER_SPURT_INTERVAL_SECONDS
|
||||
)
|
||||
else:
|
||||
_ensure_visual()
|
||||
if data != null and data.catch_data != null and _sprite != null:
|
||||
_sprite.texture = data.catch_data.display_texture
|
||||
_sprite.pixel_size = data.sprite_pixel_size
|
||||
_sprite.position.y = (
|
||||
REFERENCE_SPRITE_HEIGHT
|
||||
* data.sprite_pixel_size
|
||||
/ REFERENCE_SPRITE_PIXEL_SIZE
|
||||
0.0
|
||||
if not data.spawn_anchor_set_id.is_empty()
|
||||
else (
|
||||
REFERENCE_SPRITE_HEIGHT
|
||||
* data.sprite_pixel_size
|
||||
/ REFERENCE_SPRITE_PIXEL_SIZE
|
||||
)
|
||||
)
|
||||
_sprite.rotation_degrees.x = data.sprite_tilt_degrees
|
||||
apply_network_state(position, yaw, true)
|
||||
|
|
@ -58,6 +74,10 @@ func play_despawn(with_dust: bool) -> void:
|
|||
_despawning = true
|
||||
if with_dust:
|
||||
_emit_dust()
|
||||
if _water_spurt_root != null:
|
||||
_water_spurt_root.visible = false
|
||||
queue_free()
|
||||
return
|
||||
var tween: Tween = create_tween()
|
||||
tween.set_parallel(true)
|
||||
tween.set_trans(Tween.TRANS_QUAD)
|
||||
|
|
@ -80,6 +100,14 @@ func _process(delta: float) -> void:
|
|||
_target_yaw,
|
||||
1.0 - exp(-8.0 * delta),
|
||||
)
|
||||
if _water_spurt_root != null:
|
||||
_water_spurt_elapsed += delta
|
||||
if _water_spurt_elapsed >= WATER_SPURT_INTERVAL_SECONDS:
|
||||
_water_spurt_elapsed = fmod(
|
||||
_water_spurt_elapsed,
|
||||
WATER_SPURT_INTERVAL_SECONDS,
|
||||
)
|
||||
_emit_water_spurt()
|
||||
|
||||
|
||||
func _ensure_visual() -> void:
|
||||
|
|
@ -95,6 +123,66 @@ func _ensure_visual() -> void:
|
|||
add_child(_sprite)
|
||||
|
||||
|
||||
func _ensure_water_spurt_visual() -> void:
|
||||
if _water_spurt_root != null:
|
||||
return
|
||||
_water_spurt_root = Node3D.new()
|
||||
_water_spurt_root.name = "WaterSpurt"
|
||||
add_child(_water_spurt_root)
|
||||
var hole_material := StandardMaterial3D.new()
|
||||
hole_material.albedo_color = WATER_SPURT_HOLE_COLOR
|
||||
hole_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
hole_material.roughness = 1.0
|
||||
hole_material.metallic = 0.0
|
||||
var hole_mesh := CylinderMesh.new()
|
||||
hole_mesh.top_radius = 0.035
|
||||
hole_mesh.bottom_radius = 0.035
|
||||
hole_mesh.height = 0.004
|
||||
hole_mesh.radial_segments = 12
|
||||
hole_mesh.rings = 1
|
||||
hole_mesh.material = hole_material
|
||||
var hole := MeshInstance3D.new()
|
||||
hole.name = "BurrowMark"
|
||||
hole.position.y = 0.008
|
||||
hole.mesh = hole_mesh
|
||||
hole.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
_water_spurt_root.add_child(hole)
|
||||
_emit_water_spurt()
|
||||
|
||||
|
||||
func _emit_water_spurt() -> void:
|
||||
if _water_spurt_root == null or not _water_spurt_root.visible:
|
||||
return
|
||||
var particles := CPUParticles3D.new()
|
||||
particles.name = "WaterDroplets"
|
||||
particles.position.y = 0.035
|
||||
particles.amount = 6
|
||||
particles.lifetime = 0.42
|
||||
particles.one_shot = true
|
||||
particles.explosiveness = 1.0
|
||||
particles.direction = Vector3.UP
|
||||
particles.spread = 32.0
|
||||
particles.gravity = Vector3(0.0, -3.4, 0.0)
|
||||
particles.initial_velocity_min = 0.8
|
||||
particles.initial_velocity_max = 1.2
|
||||
particles.scale_amount_min = 0.75
|
||||
particles.scale_amount_max = 1.0
|
||||
var water_material := StandardMaterial3D.new()
|
||||
water_material.albedo_color = WATER_SPURT_COLOR
|
||||
water_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
water_material.roughness = 1.0
|
||||
water_material.metallic = 0.0
|
||||
var droplet_mesh := BoxMesh.new()
|
||||
droplet_mesh.size = Vector3(0.022, 0.035, 0.022)
|
||||
droplet_mesh.material = water_material
|
||||
particles.mesh = droplet_mesh
|
||||
_water_spurt_root.add_child(particles)
|
||||
particles.emitting = true
|
||||
var cleanup := create_tween()
|
||||
cleanup.tween_interval(0.55)
|
||||
cleanup.tween_callback(particles.queue_free)
|
||||
|
||||
|
||||
func _emit_dust() -> void:
|
||||
var particles := CPUParticles3D.new()
|
||||
particles.name = "DustPoof"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue