Refresh menus chat and player expression
BIN
items/icons/equipment/64_cooler_plus.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
40
items/icons/equipment/64_cooler_plus.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://baht2hredfky8"
|
||||||
|
path="res://.godot/imported/64_cooler_plus.png-d0daa39246f92fd9774ec1721970efad.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://items/icons/equipment/64_cooler_plus.png"
|
||||||
|
dest_files=["res://.godot/imported/64_cooler_plus.png-d0daa39246f92fd9774ec1721970efad.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
|
@ -36,7 +36,7 @@ var _pending_rewards: Array[Dictionary] = []
|
||||||
var _lifetime_claimed: Array[String] = []
|
var _lifetime_claimed: Array[String] = []
|
||||||
var _total_catches: int = 0
|
var _total_catches: int = 0
|
||||||
var _total_sold: int = 0
|
var _total_sold: int = 0
|
||||||
var _previous_hour: float = WorldTimeService.DEFAULT_START_HOUR
|
var _daily_clock_hour: float = WorldTimeService.DEFAULT_START_HOUR
|
||||||
|
|
||||||
|
|
||||||
func setup(
|
func setup(
|
||||||
|
|
@ -55,8 +55,8 @@ func setup(
|
||||||
_world_time = world_time
|
_world_time = world_time
|
||||||
_world_weather = world_weather
|
_world_weather = world_weather
|
||||||
_session = session
|
_session = session
|
||||||
_previous_hour = _world_time.get_time_hours()
|
_daily_clock_hour = _world_time.get_time_hours()
|
||||||
_world_time.time_changed.connect(_on_time_changed)
|
_world_time.natural_time_advanced.connect(_on_natural_time_advanced)
|
||||||
_session.state_changed.connect(_on_session_state_changed)
|
_session.state_changed.connect(_on_session_state_changed)
|
||||||
_collection.collection_changed.connect(_on_current_state_changed)
|
_collection.collection_changed.connect(_on_current_state_changed)
|
||||||
_experience.experience_changed.connect(_on_experience_changed)
|
_experience.experience_changed.connect(_on_experience_changed)
|
||||||
|
|
@ -86,7 +86,6 @@ func set_save_manager(save_manager: PlayerSaveManager) -> void:
|
||||||
|
|
||||||
func begin_progression_session() -> void:
|
func begin_progression_session() -> void:
|
||||||
_progression_ready = true
|
_progression_ready = true
|
||||||
_previous_hour = _world_time.get_time_hours()
|
|
||||||
if _session.is_host():
|
if _session.is_host():
|
||||||
_activate_host_board()
|
_activate_host_board()
|
||||||
changed.emit()
|
changed.emit()
|
||||||
|
|
@ -155,7 +154,7 @@ func get_time_until_refresh_text() -> String:
|
||||||
if _active_plan_id.is_empty() or _world_time == null:
|
if _active_plan_id.is_empty() or _world_time == null:
|
||||||
return "daily jobs unavailable"
|
return "daily jobs unavailable"
|
||||||
var elapsed: float = fposmod(
|
var elapsed: float = fposmod(
|
||||||
_world_time.get_time_hours() - DAILY_REFRESH_HOUR,
|
_daily_clock_hour - DAILY_REFRESH_HOUR,
|
||||||
WorldTimeService.HOURS_PER_DAY,
|
WorldTimeService.HOURS_PER_DAY,
|
||||||
)
|
)
|
||||||
var hours_remaining: float = WorldTimeService.HOURS_PER_DAY - elapsed
|
var hours_remaining: float = WorldTimeService.HOURS_PER_DAY - elapsed
|
||||||
|
|
@ -185,7 +184,10 @@ func claim(claim_id: String) -> bool:
|
||||||
|
|
||||||
|
|
||||||
func get_host_board_network_data() -> Dictionary:
|
func get_host_board_network_data() -> Dictionary:
|
||||||
return _host_board.duplicate(true)
|
var board := _host_board.duplicate(true)
|
||||||
|
if not board.is_empty():
|
||||||
|
board["daily_clock_hour"] = _daily_clock_hour
|
||||||
|
return board
|
||||||
|
|
||||||
|
|
||||||
func apply_remote_board(board: Dictionary) -> bool:
|
func apply_remote_board(board: Dictionary) -> bool:
|
||||||
|
|
@ -198,6 +200,8 @@ func apply_remote_board(board: Dictionary) -> bool:
|
||||||
_daily_progress.clear()
|
_daily_progress.clear()
|
||||||
_daily_completions.clear()
|
_daily_completions.clear()
|
||||||
_active_board = board.duplicate(true)
|
_active_board = board.duplicate(true)
|
||||||
|
if board.has("daily_clock_hour"):
|
||||||
|
_daily_clock_hour = float(board["daily_clock_hour"])
|
||||||
changed.emit()
|
changed.emit()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
@ -223,6 +227,7 @@ func to_save_data() -> Dictionary:
|
||||||
"active_plan_id": _active_plan_id,
|
"active_plan_id": _active_plan_id,
|
||||||
"daily_progress": progress,
|
"daily_progress": progress,
|
||||||
"daily_completions": completions,
|
"daily_completions": completions,
|
||||||
|
"daily_clock_hour": _daily_clock_hour,
|
||||||
"pending_rewards": _pending_rewards.duplicate(true),
|
"pending_rewards": _pending_rewards.duplicate(true),
|
||||||
"lifetime_claimed": _lifetime_claimed.duplicate(),
|
"lifetime_claimed": _lifetime_claimed.duplicate(),
|
||||||
"statistics": {
|
"statistics": {
|
||||||
|
|
@ -246,6 +251,12 @@ func restore_from_save_data(data: Dictionary) -> bool:
|
||||||
var completions: Dictionary = data.get("daily_completions", {})
|
var completions: Dictionary = data.get("daily_completions", {})
|
||||||
for key: Variant in completions:
|
for key: Variant in completions:
|
||||||
_daily_completions[str(key)] = mini(int(completions[key]), 1)
|
_daily_completions[str(key)] = mini(int(completions[key]), 1)
|
||||||
|
_daily_clock_hour = float(data.get(
|
||||||
|
"daily_clock_hour",
|
||||||
|
_world_time.get_time_hours()
|
||||||
|
if _world_time != null
|
||||||
|
else WorldTimeService.DEFAULT_START_HOUR,
|
||||||
|
))
|
||||||
_pending_rewards.clear()
|
_pending_rewards.clear()
|
||||||
var rewards: Array = data.get("pending_rewards", [])
|
var rewards: Array = data.get("pending_rewards", [])
|
||||||
var restored_daily_rewards: Dictionary[String, bool] = {}
|
var restored_daily_rewards: Dictionary[String, bool] = {}
|
||||||
|
|
@ -290,6 +301,7 @@ func reset_to_defaults() -> void:
|
||||||
_lifetime_claimed.clear()
|
_lifetime_claimed.clear()
|
||||||
_total_catches = 0
|
_total_catches = 0
|
||||||
_total_sold = 0
|
_total_sold = 0
|
||||||
|
_daily_clock_hour = WorldTimeService.DEFAULT_START_HOUR
|
||||||
changed.emit()
|
changed.emit()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -300,6 +312,7 @@ static func default_save_data() -> Dictionary:
|
||||||
"active_plan_id": "",
|
"active_plan_id": "",
|
||||||
"daily_progress": {},
|
"daily_progress": {},
|
||||||
"daily_completions": {},
|
"daily_completions": {},
|
||||||
|
"daily_clock_hour": WorldTimeService.DEFAULT_START_HOUR,
|
||||||
"pending_rewards": [],
|
"pending_rewards": [],
|
||||||
"lifetime_claimed": [],
|
"lifetime_claimed": [],
|
||||||
"statistics": {"fish_caught": 0, "fish_sold": 0},
|
"statistics": {"fish_caught": 0, "fish_sold": 0},
|
||||||
|
|
@ -363,6 +376,16 @@ static func validate_save_data(value: Variant) -> bool:
|
||||||
var host_board: Dictionary = data.get("host_board", {})
|
var host_board: Dictionary = data.get("host_board", {})
|
||||||
if not host_board.is_empty() and not validate_board(host_board):
|
if not host_board.is_empty() and not validate_board(host_board):
|
||||||
return false
|
return false
|
||||||
|
if (
|
||||||
|
data.has("daily_clock_hour")
|
||||||
|
and (
|
||||||
|
typeof(data["daily_clock_hour"]) not in [TYPE_FLOAT, TYPE_INT]
|
||||||
|
or not is_finite(float(data["daily_clock_hour"]))
|
||||||
|
or float(data["daily_clock_hour"]) < 0.0
|
||||||
|
or float(data["daily_clock_hour"]) >= WorldTimeService.HOURS_PER_DAY
|
||||||
|
)
|
||||||
|
):
|
||||||
|
return false
|
||||||
var progress: Dictionary = data.get("daily_progress", {})
|
var progress: Dictionary = data.get("daily_progress", {})
|
||||||
if progress.size() > 8:
|
if progress.size() > 8:
|
||||||
return false
|
return false
|
||||||
|
|
@ -475,19 +498,22 @@ func _apply_host_weather_plan() -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func _on_time_changed(
|
func _on_natural_time_advanced(hours: float) -> void:
|
||||||
time_hours: float,
|
if not is_finite(hours) or hours <= 0.0:
|
||||||
_phase: WorldTimeService.Phase,
|
return
|
||||||
) -> void:
|
var previous_hour := _daily_clock_hour
|
||||||
|
_daily_clock_hour = fposmod(
|
||||||
|
_daily_clock_hour + hours,
|
||||||
|
WorldTimeService.HOURS_PER_DAY,
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
_progression_ready
|
_progression_ready
|
||||||
and _session != null
|
and _session != null
|
||||||
and _session.is_host()
|
and _session.is_host()
|
||||||
and _crossed_daily_refresh(_previous_hour, time_hours)
|
and _crossed_daily_refresh(previous_hour, _daily_clock_hour)
|
||||||
):
|
):
|
||||||
var cycle: int = int(_host_board.get("cycle", -1)) + 1
|
var cycle: int = int(_host_board.get("cycle", -1)) + 1
|
||||||
_generate_host_board(maxi(cycle, 0))
|
_generate_host_board(maxi(cycle, 0))
|
||||||
_previous_hour = time_hours
|
|
||||||
|
|
||||||
|
|
||||||
func _on_session_state_changed(state: NetworkSession.State) -> void:
|
func _on_session_state_changed(state: NetworkSession.State) -> void:
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,21 @@ func send_local_message(body: String) -> bool:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func broadcast_system_message(body: String) -> bool:
|
||||||
|
if _session == null or not _session.is_host():
|
||||||
|
return false
|
||||||
|
var clean := NetworkChatProtocol.sanitize_body(body)
|
||||||
|
if clean.is_empty():
|
||||||
|
return false
|
||||||
|
_broadcast(_make_message(
|
||||||
|
NetworkChatProtocol.Kind.SYSTEM,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
clean,
|
||||||
|
))
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
func get_history() -> Array[Dictionary]:
|
func get_history() -> Array[Dictionary]:
|
||||||
var result: Array[Dictionary] = []
|
var result: Array[Dictionary] = []
|
||||||
for message: Dictionary in _history:
|
for message: Dictionary in _history:
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,13 @@ class_name NetworkProfilePreferences
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
const FORMAT_VERSION: int = 1
|
const FORMAT_VERSION: int = 1
|
||||||
|
const VoiceProfilesType = preload(
|
||||||
|
"res://player/animalese_voice_profiles.gd"
|
||||||
|
)
|
||||||
var profile_id: String = ""
|
var profile_id: String = ""
|
||||||
var display_name: String = "Player"
|
var display_name: String = "Player"
|
||||||
|
var voice_id: String = VoiceProfilesType.DEFAULT_ID
|
||||||
|
var speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID
|
||||||
var created_at_unix: int = 0
|
var created_at_unix: int = 0
|
||||||
var _profile_path := ""
|
var _profile_path := ""
|
||||||
var _expected_hash := ""
|
var _expected_hash := ""
|
||||||
|
|
@ -48,19 +53,39 @@ func load_or_create() -> bool:
|
||||||
Time.get_ticks_usec(),
|
Time.get_ticks_usec(),
|
||||||
]
|
]
|
||||||
display_name = "Player"
|
display_name = "Player"
|
||||||
|
voice_id = VoiceProfilesType.DEFAULT_ID
|
||||||
|
speech_speed_id = VoiceProfilesType.DEFAULT_SPEED_ID
|
||||||
created_at_unix = int(Time.get_unix_time_from_system())
|
created_at_unix = int(Time.get_unix_time_from_system())
|
||||||
return _save_atomic()
|
return _save_atomic()
|
||||||
|
|
||||||
|
|
||||||
func set_display_name(value: String) -> bool:
|
func set_display_name(value: String) -> bool:
|
||||||
|
return set_profile_identity(value, voice_id, speech_speed_id)
|
||||||
|
|
||||||
|
|
||||||
|
func set_profile_identity(
|
||||||
|
value: String,
|
||||||
|
selected_voice_id: String,
|
||||||
|
selected_speech_speed_id: String,
|
||||||
|
) -> bool:
|
||||||
var clean_name: String = value.strip_edges()
|
var clean_name: String = value.strip_edges()
|
||||||
if not is_valid_display_name(clean_name):
|
if (
|
||||||
|
not is_valid_display_name(clean_name)
|
||||||
|
or not VoiceProfilesType.is_valid(selected_voice_id)
|
||||||
|
or not VoiceProfilesType.is_valid_speed(selected_speech_speed_id)
|
||||||
|
):
|
||||||
return false
|
return false
|
||||||
var previous: String = display_name
|
var previous_name: String = display_name
|
||||||
|
var previous_voice_id: String = voice_id
|
||||||
|
var previous_speech_speed_id: String = speech_speed_id
|
||||||
display_name = clean_name
|
display_name = clean_name
|
||||||
|
voice_id = selected_voice_id
|
||||||
|
speech_speed_id = selected_speech_speed_id
|
||||||
if _save_atomic():
|
if _save_atomic():
|
||||||
return true
|
return true
|
||||||
display_name = previous
|
display_name = previous_name
|
||||||
|
voice_id = previous_voice_id
|
||||||
|
speech_speed_id = previous_speech_speed_id
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -109,6 +134,12 @@ func _load_existing() -> bool:
|
||||||
return false
|
return false
|
||||||
profile_id = loaded_id
|
profile_id = loaded_id
|
||||||
display_name = loaded_name
|
display_name = loaded_name
|
||||||
|
voice_id = VoiceProfilesType.sanitized_id(
|
||||||
|
str(data.get("voice_id", VoiceProfilesType.DEFAULT_ID))
|
||||||
|
)
|
||||||
|
speech_speed_id = VoiceProfilesType.sanitized_speed_id(
|
||||||
|
str(data.get("speech_speed_id", VoiceProfilesType.DEFAULT_SPEED_ID))
|
||||||
|
)
|
||||||
created_at_unix = int(data["created_at_unix"])
|
created_at_unix = int(data["created_at_unix"])
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
@ -118,6 +149,8 @@ func _save_atomic() -> bool:
|
||||||
"format_version": FORMAT_VERSION,
|
"format_version": FORMAT_VERSION,
|
||||||
"profile_id": profile_id,
|
"profile_id": profile_id,
|
||||||
"display_name": display_name,
|
"display_name": display_name,
|
||||||
|
"voice_id": voice_id,
|
||||||
|
"speech_speed_id": speech_speed_id,
|
||||||
"created_at_unix": created_at_unix,
|
"created_at_unix": created_at_unix,
|
||||||
}
|
}
|
||||||
var result := PortableFileGuard.write_guarded(
|
var result := PortableFileGuard.write_guarded(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
class_name NetworkProfileService
|
class_name NetworkProfileService
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
|
const VoiceProfilesType = preload(
|
||||||
|
"res://player/animalese_voice_profiles.gd"
|
||||||
|
)
|
||||||
|
|
||||||
signal conflict_result(
|
signal conflict_result(
|
||||||
request_id: String,
|
request_id: String,
|
||||||
has_conflict: bool,
|
has_conflict: bool,
|
||||||
|
|
@ -18,6 +22,8 @@ var _preferences: NetworkProfilePreferences
|
||||||
var _appearance_store: PlayerAppearanceStore
|
var _appearance_store: PlayerAppearanceStore
|
||||||
var _spawn_service: PlayerSpawnService
|
var _spawn_service: PlayerSpawnService
|
||||||
var _pending_apply: Dictionary[String, Dictionary] = {}
|
var _pending_apply: Dictionary[String, Dictionary] = {}
|
||||||
|
var _pending_voice_ids: Dictionary[String, String] = {}
|
||||||
|
var _pending_speech_speed_ids: Dictionary[String, String] = {}
|
||||||
var _host_pending_apply: Dictionary[String, Dictionary] = {}
|
var _host_pending_apply: Dictionary[String, Dictionary] = {}
|
||||||
var _latest_check_id: String = ""
|
var _latest_check_id: String = ""
|
||||||
var _latest_check_name: String = ""
|
var _latest_check_name: String = ""
|
||||||
|
|
@ -40,6 +46,7 @@ func setup(
|
||||||
_session.join_authenticated.connect(_on_join_authenticated)
|
_session.join_authenticated.connect(_on_join_authenticated)
|
||||||
_session.state_changed.connect(_on_session_state_changed)
|
_session.state_changed.connect(_on_session_state_changed)
|
||||||
_apply_to_avatar(1, _appearance_store.get_snapshot())
|
_apply_to_avatar(1, _appearance_store.get_snapshot())
|
||||||
|
_apply_local_voice_to_avatar()
|
||||||
|
|
||||||
|
|
||||||
func get_persisted_name() -> String:
|
func get_persisted_name() -> String:
|
||||||
|
|
@ -50,6 +57,14 @@ func get_persisted_appearance() -> Dictionary:
|
||||||
return _appearance_store.get_snapshot()
|
return _appearance_store.get_snapshot()
|
||||||
|
|
||||||
|
|
||||||
|
func get_persisted_voice_id() -> String:
|
||||||
|
return _preferences.voice_id
|
||||||
|
|
||||||
|
|
||||||
|
func get_persisted_speech_speed_id() -> String:
|
||||||
|
return _preferences.speech_speed_id
|
||||||
|
|
||||||
|
|
||||||
func get_identity_fingerprint() -> String:
|
func get_identity_fingerprint() -> String:
|
||||||
return (
|
return (
|
||||||
_session.get_local_identity_fingerprint()
|
_session.get_local_identity_fingerprint()
|
||||||
|
|
@ -87,11 +102,15 @@ func apply_profile(
|
||||||
display_name: String,
|
display_name: String,
|
||||||
appearance: Dictionary,
|
appearance: Dictionary,
|
||||||
use_anyway: bool,
|
use_anyway: bool,
|
||||||
|
voice_id: String = VoiceProfilesType.DEFAULT_ID,
|
||||||
|
speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
var clean_name := display_name.strip_edges()
|
var clean_name := display_name.strip_edges()
|
||||||
if (
|
if (
|
||||||
not NetworkProfilePreferences.is_valid_display_name(clean_name)
|
not NetworkProfilePreferences.is_valid_display_name(clean_name)
|
||||||
or not CharacterCustomizationCatalog.validate_snapshot(appearance)
|
or not CharacterCustomizationCatalog.validate_snapshot(appearance)
|
||||||
|
or not VoiceProfilesType.is_valid(voice_id)
|
||||||
|
or not VoiceProfilesType.is_valid_speed(speech_speed_id)
|
||||||
):
|
):
|
||||||
apply_finished.emit(false, "Check the player name and appearance choices.")
|
apply_finished.emit(false, "Check the player name and appearance choices.")
|
||||||
return false
|
return false
|
||||||
|
|
@ -108,6 +127,8 @@ func apply_profile(
|
||||||
"profile_update", NetworkProfileProtocol.signature_fields(request)
|
"profile_update", NetworkProfileProtocol.signature_fields(request)
|
||||||
)
|
)
|
||||||
_pending_apply[request_id] = request
|
_pending_apply[request_id] = request
|
||||||
|
_pending_voice_ids[request_id] = voice_id
|
||||||
|
_pending_speech_speed_ids[request_id] = speech_speed_id
|
||||||
if _session == null or not _session.is_gameplay_session_active():
|
if _session == null or not _session.is_gameplay_session_active():
|
||||||
_apply_local_result(request_id, true, "", false, PackedStringArray())
|
_apply_local_result(request_id, true, "", false, PackedStringArray())
|
||||||
elif _session.is_host():
|
elif _session.is_host():
|
||||||
|
|
@ -264,20 +285,46 @@ func _apply_local_result(
|
||||||
return
|
return
|
||||||
if not accepted:
|
if not accepted:
|
||||||
_pending_apply.erase(request_id)
|
_pending_apply.erase(request_id)
|
||||||
|
_pending_voice_ids.erase(request_id)
|
||||||
|
_pending_speech_speed_ids.erase(request_id)
|
||||||
conflict_result.emit(request_id, conflict, suggestions)
|
conflict_result.emit(request_id, conflict, suggestions)
|
||||||
apply_finished.emit(false, message)
|
apply_finished.emit(false, message)
|
||||||
return
|
return
|
||||||
var previous_name := _preferences.display_name
|
var previous_name := _preferences.display_name
|
||||||
if not _preferences.set_display_name(str(request["display_name"])):
|
var previous_voice_id := _preferences.voice_id
|
||||||
|
var previous_speech_speed_id := _preferences.speech_speed_id
|
||||||
|
var requested_voice_id: String = _pending_voice_ids.get(
|
||||||
|
request_id,
|
||||||
|
VoiceProfilesType.DEFAULT_ID,
|
||||||
|
)
|
||||||
|
var requested_speech_speed_id: String = _pending_speech_speed_ids.get(
|
||||||
|
request_id,
|
||||||
|
VoiceProfilesType.DEFAULT_SPEED_ID,
|
||||||
|
)
|
||||||
|
if not _preferences.set_profile_identity(
|
||||||
|
str(request["display_name"]),
|
||||||
|
requested_voice_id,
|
||||||
|
requested_speech_speed_id,
|
||||||
|
):
|
||||||
_pending_apply.erase(request_id)
|
_pending_apply.erase(request_id)
|
||||||
|
_pending_voice_ids.erase(request_id)
|
||||||
|
_pending_speech_speed_ids.erase(request_id)
|
||||||
apply_finished.emit(false, "Profile could not be saved.")
|
apply_finished.emit(false, "Profile could not be saved.")
|
||||||
return
|
return
|
||||||
if not _appearance_store.save_snapshot(request["appearance"]):
|
if not _appearance_store.save_snapshot(request["appearance"]):
|
||||||
_preferences.set_display_name(previous_name)
|
_preferences.set_profile_identity(
|
||||||
|
previous_name,
|
||||||
|
previous_voice_id,
|
||||||
|
previous_speech_speed_id,
|
||||||
|
)
|
||||||
_pending_apply.erase(request_id)
|
_pending_apply.erase(request_id)
|
||||||
|
_pending_voice_ids.erase(request_id)
|
||||||
|
_pending_speech_speed_ids.erase(request_id)
|
||||||
apply_finished.emit(false, "Profile could not be saved.")
|
apply_finished.emit(false, "Profile could not be saved.")
|
||||||
return
|
return
|
||||||
_pending_apply.erase(request_id)
|
_pending_apply.erase(request_id)
|
||||||
|
_pending_voice_ids.erase(request_id)
|
||||||
|
_pending_speech_speed_ids.erase(request_id)
|
||||||
if _session != null and _session.is_gameplay_session_active():
|
if _session != null and _session.is_gameplay_session_active():
|
||||||
if _session.is_host():
|
if _session.is_host():
|
||||||
_session.apply_canonical_profile(
|
_session.apply_canonical_profile(
|
||||||
|
|
@ -304,6 +351,7 @@ func _apply_local_result(
|
||||||
_session.get_local_peer_id() if _session != null else 1,
|
_session.get_local_peer_id() if _session != null else 1,
|
||||||
_appearance_store.get_snapshot(),
|
_appearance_store.get_snapshot(),
|
||||||
)
|
)
|
||||||
|
_apply_local_voice_to_avatar()
|
||||||
apply_finished.emit(true, "Profile saved.")
|
apply_finished.emit(true, "Profile saved.")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -361,6 +409,7 @@ func _on_join_authenticated() -> void:
|
||||||
_apply_to_avatar(
|
_apply_to_avatar(
|
||||||
_session.get_local_peer_id(), _appearance_store.get_snapshot()
|
_session.get_local_peer_id(), _appearance_store.get_snapshot()
|
||||||
)
|
)
|
||||||
|
_apply_local_voice_to_avatar()
|
||||||
|
|
||||||
|
|
||||||
func _on_peer_removed(_peer_id: int) -> void:
|
func _on_peer_removed(_peer_id: int) -> void:
|
||||||
|
|
@ -400,6 +449,8 @@ func _refresh_latest_check() -> void:
|
||||||
func _on_session_state_changed(state: NetworkSession.State) -> void:
|
func _on_session_state_changed(state: NetworkSession.State) -> void:
|
||||||
if state == NetworkSession.State.INACTIVE:
|
if state == NetworkSession.State.INACTIVE:
|
||||||
_pending_apply.clear()
|
_pending_apply.clear()
|
||||||
|
_pending_voice_ids.clear()
|
||||||
|
_pending_speech_speed_ids.clear()
|
||||||
_host_pending_apply.clear()
|
_host_pending_apply.clear()
|
||||||
_latest_check_id = ""
|
_latest_check_id = ""
|
||||||
_latest_check_name = ""
|
_latest_check_name = ""
|
||||||
|
|
@ -465,5 +516,16 @@ func _apply_to_avatar(peer_id: int, appearance: Dictionary) -> void:
|
||||||
avatar.apply_appearance_snapshot(appearance)
|
avatar.apply_appearance_snapshot(appearance)
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_local_voice_to_avatar() -> void:
|
||||||
|
if _spawn_service == null:
|
||||||
|
return
|
||||||
|
var local_peer_id := (
|
||||||
|
_session.get_local_peer_id() if _session != null else 1
|
||||||
|
)
|
||||||
|
var avatar := _spawn_service.get_avatar(local_peer_id)
|
||||||
|
if avatar != null:
|
||||||
|
avatar.apply_animalese_voice_id(_preferences.voice_id)
|
||||||
|
|
||||||
|
|
||||||
func _new_id() -> String:
|
func _new_id() -> String:
|
||||||
return Crypto.new().generate_random_bytes(16).hex_encode()
|
return Crypto.new().generate_random_bytes(16).hex_encode()
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@ func setup(session: NetworkSession, world_time: WorldTimeService) -> void:
|
||||||
_world_time = world_time
|
_world_time = world_time
|
||||||
_session.state_changed.connect(_on_session_state_changed)
|
_session.state_changed.connect(_on_session_state_changed)
|
||||||
_session.peer_authenticated.connect(_on_peer_authenticated)
|
_session.peer_authenticated.connect(_on_peer_authenticated)
|
||||||
|
if not _world_time.authoritative_time_set.is_connected(
|
||||||
|
_on_authoritative_time_set
|
||||||
|
):
|
||||||
|
_world_time.authoritative_time_set.connect(
|
||||||
|
_on_authoritative_time_set
|
||||||
|
)
|
||||||
set_process(true)
|
set_process(true)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -82,6 +88,13 @@ func _on_peer_authenticated(peer_id: int, _display_name: String) -> void:
|
||||||
_send_snapshot(peer_id)
|
_send_snapshot(peer_id)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_authoritative_time_set(_time_hours: float) -> void:
|
||||||
|
if _session == null or not _session.is_host():
|
||||||
|
return
|
||||||
|
_sync_elapsed = 0.0
|
||||||
|
_broadcast_snapshot()
|
||||||
|
|
||||||
|
|
||||||
func _broadcast_snapshot() -> void:
|
func _broadcast_snapshot() -> void:
|
||||||
if _session == null or not _session.is_host():
|
if _session == null or not _session.is_host():
|
||||||
return
|
return
|
||||||
|
|
|
||||||
57
player/animalese_voice_profiles.gd
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
class_name AnimaleseVoiceProfiles
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
const DEFAULT_ID: String = "natural"
|
||||||
|
const DEFAULT_SPEED_ID: String = "normal"
|
||||||
|
const OPTIONS: Array[Dictionary] = [
|
||||||
|
{"id": "tiny", "label": "tiny", "pitch": 1.24},
|
||||||
|
{"id": "bright", "label": "bright", "pitch": 1.10},
|
||||||
|
{"id": "natural", "label": "natural", "pitch": 1.0},
|
||||||
|
{"id": "mellow", "label": "mellow", "pitch": 0.89},
|
||||||
|
{"id": "deep", "label": "deep", "pitch": 0.77},
|
||||||
|
]
|
||||||
|
const SPEED_OPTIONS: Array[Dictionary] = [
|
||||||
|
{"id": "slow", "label": "slow", "characters_per_second": 20.0},
|
||||||
|
{"id": "relaxed", "label": "relaxed", "characters_per_second": 24.0},
|
||||||
|
{"id": "normal", "label": "normal", "characters_per_second": 28.0},
|
||||||
|
{"id": "quick", "label": "quick", "characters_per_second": 34.0},
|
||||||
|
{"id": "rapid", "label": "rapid", "characters_per_second": 40.0},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
static func is_valid(voice_id: String) -> bool:
|
||||||
|
for option: Dictionary in OPTIONS:
|
||||||
|
if str(option.get("id", "")) == voice_id:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
static func sanitized_id(voice_id: String) -> String:
|
||||||
|
return voice_id if is_valid(voice_id) else DEFAULT_ID
|
||||||
|
|
||||||
|
|
||||||
|
static func pitch_for(voice_id: String) -> float:
|
||||||
|
var resolved_id := sanitized_id(voice_id)
|
||||||
|
for option: Dictionary in OPTIONS:
|
||||||
|
if str(option.get("id", "")) == resolved_id:
|
||||||
|
return float(option.get("pitch", 1.0))
|
||||||
|
return 1.0
|
||||||
|
|
||||||
|
|
||||||
|
static func is_valid_speed(speed_id: String) -> bool:
|
||||||
|
for option: Dictionary in SPEED_OPTIONS:
|
||||||
|
if str(option.get("id", "")) == speed_id:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
static func sanitized_speed_id(speed_id: String) -> String:
|
||||||
|
return speed_id if is_valid_speed(speed_id) else DEFAULT_SPEED_ID
|
||||||
|
|
||||||
|
|
||||||
|
static func speed_for(speed_id: String) -> float:
|
||||||
|
var resolved_id := sanitized_speed_id(speed_id)
|
||||||
|
for option: Dictionary in SPEED_OPTIONS:
|
||||||
|
if str(option.get("id", "")) == resolved_id:
|
||||||
|
return float(option.get("characters_per_second", 28.0))
|
||||||
|
return 28.0
|
||||||
1
player/animalese_voice_profiles.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://dlut32tq2v66j
|
||||||
185
player/player.gd
|
|
@ -50,6 +50,7 @@ const CONTROLLER_ZOOM_TRIGGER_AXIS: JoyAxis = JOY_AXIS_TRIGGER_LEFT
|
||||||
var appearance_snapshot: Dictionary = (
|
var appearance_snapshot: Dictionary = (
|
||||||
CharacterCustomizationCatalog.default_snapshot()
|
CharacterCustomizationCatalog.default_snapshot()
|
||||||
)
|
)
|
||||||
|
var animalese_voice_id: String = "natural"
|
||||||
var active_bait_id: StringName = StringName()
|
var active_bait_id: StringName = StringName()
|
||||||
signal active_bait_changed(item_id: StringName)
|
signal active_bait_changed(item_id: StringName)
|
||||||
|
|
||||||
|
|
@ -76,6 +77,14 @@ func apply_appearance_snapshot(snapshot: Dictionary) -> void:
|
||||||
appearance_snapshot = snapshot.duplicate(true)
|
appearance_snapshot = snapshot.duplicate(true)
|
||||||
PlayerVisualPresenter.apply_appearance(_visuals, appearance_snapshot)
|
PlayerVisualPresenter.apply_appearance(_visuals, appearance_snapshot)
|
||||||
|
|
||||||
|
|
||||||
|
func apply_animalese_voice_id(voice_id: String) -> void:
|
||||||
|
animalese_voice_id = voice_id
|
||||||
|
|
||||||
|
|
||||||
|
func get_animalese_voice_id() -> String:
|
||||||
|
return animalese_voice_id
|
||||||
|
|
||||||
class ShowcaseCameraSnapshot:
|
class ShowcaseCameraSnapshot:
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
|
|
@ -129,6 +138,8 @@ class ShowcaseCameraSnapshot:
|
||||||
@export var zoom_smoothing: float = 12.0
|
@export var zoom_smoothing: float = 12.0
|
||||||
@export_range(0.1, 12.0, 0.1) var controller_zoom_speed: float = 4.0
|
@export_range(0.1, 12.0, 0.1) var controller_zoom_speed: float = 4.0
|
||||||
@export_range(0.0, 1.0, 0.01) var controller_trigger_threshold: float = 0.55
|
@export_range(0.0, 1.0, 0.01) var controller_trigger_threshold: float = 0.55
|
||||||
|
@export_range(1.0, 40.0, 0.5) var free_camera_speed: float = 10.0
|
||||||
|
@export_range(1.0, 10.0, 0.5) var free_camera_sprint_multiplier: float = 3.0
|
||||||
|
|
||||||
@onready var _visuals: Node3D = %Visuals
|
@onready var _visuals: Node3D = %Visuals
|
||||||
@onready var _character_rig: Node3D = get_node_or_null(
|
@onready var _character_rig: Node3D = get_node_or_null(
|
||||||
|
|
@ -141,6 +152,7 @@ class ShowcaseCameraSnapshot:
|
||||||
@onready var _camera_pitch: Node3D = %CameraPitch
|
@onready var _camera_pitch: Node3D = %CameraPitch
|
||||||
@onready var _spring_arm: SpringArm3D = %SpringArm3D
|
@onready var _spring_arm: SpringArm3D = %SpringArm3D
|
||||||
@onready var _camera: Camera3D = %Camera3D
|
@onready var _camera: Camera3D = %Camera3D
|
||||||
|
@onready var _player_collision_shape: CollisionShape3D = $CollisionShape3D
|
||||||
@onready var inventory: FishInventoryType = %Inventory
|
@onready var inventory: FishInventoryType = %Inventory
|
||||||
@onready var collection_log: CollectionLogType = %CollectionLog
|
@onready var collection_log: CollectionLogType = %CollectionLog
|
||||||
@onready var wallet: PlayerWalletType = %Wallet
|
@onready var wallet: PlayerWalletType = %Wallet
|
||||||
|
|
@ -165,6 +177,11 @@ var _catch_attachment_offset: Vector3 = Vector3(0.0, 0.08, 0.04)
|
||||||
var _gravity: float = float(ProjectSettings.get_setting("physics/3d/default_gravity"))
|
var _gravity: float = float(ProjectSettings.get_setting("physics/3d/default_gravity"))
|
||||||
var _camera_dragging: bool = false
|
var _camera_dragging: bool = false
|
||||||
var _camera_input_enabled: bool = true
|
var _camera_input_enabled: bool = true
|
||||||
|
var _free_camera_active: bool = false
|
||||||
|
var _free_camera_body: CharacterBody3D
|
||||||
|
var _free_camera: Camera3D
|
||||||
|
var _free_camera_yaw: float = 0.0
|
||||||
|
var _free_camera_pitch: float = 0.0
|
||||||
var _movement_enabled: bool = true
|
var _movement_enabled: bool = true
|
||||||
var _water_recovery_active: bool = false
|
var _water_recovery_active: bool = false
|
||||||
var _remote_recovery_presentation_active: bool = false
|
var _remote_recovery_presentation_active: bool = false
|
||||||
|
|
@ -197,6 +214,8 @@ var _network_target_visual_yaw: float = 0.0
|
||||||
var _network_snapshot_ready: bool = false
|
var _network_snapshot_ready: bool = false
|
||||||
var _character_animation_name: StringName = &""
|
var _character_animation_name: StringName = &""
|
||||||
var _sitting: bool = false
|
var _sitting: bool = false
|
||||||
|
var _sitting_intent_pending: bool = false
|
||||||
|
var _sitting_intent_sequence: int = -1
|
||||||
var _held_fish_visible: bool = false
|
var _held_fish_visible: bool = false
|
||||||
var _showcase_animation_active: bool = false
|
var _showcase_animation_active: bool = false
|
||||||
var _fishing_rod: Node3D
|
var _fishing_rod: Node3D
|
||||||
|
|
@ -205,6 +224,8 @@ var _controller_mapping_manager: ControllerMappingManagerType
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
if not bag.contents_changed.is_connected(_on_bag_contents_changed):
|
||||||
|
bag.contents_changed.connect(_on_bag_contents_changed)
|
||||||
PlayerVisualPresenter.apply_appearance(_visuals, appearance_snapshot)
|
PlayerVisualPresenter.apply_appearance(_visuals, appearance_snapshot)
|
||||||
_initialize_fishing_rod()
|
_initialize_fishing_rod()
|
||||||
_target_zoom = clampf(_spring_arm.spring_length, minimum_zoom, maximum_zoom)
|
_target_zoom = clampf(_spring_arm.spring_length, minimum_zoom, maximum_zoom)
|
||||||
|
|
@ -213,6 +234,14 @@ func _ready() -> void:
|
||||||
_initialize_held_item_attachment()
|
_initialize_held_item_attachment()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_bag_contents_changed() -> void:
|
||||||
|
if (
|
||||||
|
not active_bait_id.is_empty()
|
||||||
|
and bag.get_quantity(active_bait_id) <= 0
|
||||||
|
):
|
||||||
|
unequip_bait()
|
||||||
|
|
||||||
|
|
||||||
func set_controller_mapping_manager(
|
func set_controller_mapping_manager(
|
||||||
mapping_manager: ControllerMappingManagerType,
|
mapping_manager: ControllerMappingManagerType,
|
||||||
) -> void:
|
) -> void:
|
||||||
|
|
@ -267,8 +296,18 @@ func _physics_process(delta: float) -> void:
|
||||||
if _network_interpolation_enabled:
|
if _network_interpolation_enabled:
|
||||||
_update_network_interpolation(delta)
|
_update_network_interpolation(delta)
|
||||||
return
|
return
|
||||||
|
if local_control_enabled and _free_camera_active:
|
||||||
|
_update_free_camera_physics()
|
||||||
|
velocity.x = 0.0
|
||||||
|
velocity.z = 0.0
|
||||||
|
if not is_on_floor():
|
||||||
|
velocity.y -= _gravity * fall_gravity_multiplier * delta
|
||||||
|
move_and_slide()
|
||||||
|
_network_jump_pending = false
|
||||||
|
return
|
||||||
var jump_requested: bool = (
|
var jump_requested: bool = (
|
||||||
_movement_enabled
|
_movement_enabled
|
||||||
|
and not _free_camera_active
|
||||||
and (
|
and (
|
||||||
(local_control_enabled and Input.is_action_just_pressed("jump"))
|
(local_control_enabled and Input.is_action_just_pressed("jump"))
|
||||||
or (_network_authoritative_simulation and _network_jump_pending)
|
or (_network_authoritative_simulation and _network_jump_pending)
|
||||||
|
|
@ -276,7 +315,7 @@ func _physics_process(delta: float) -> void:
|
||||||
)
|
)
|
||||||
if _sitting:
|
if _sitting:
|
||||||
if jump_requested:
|
if jump_requested:
|
||||||
_set_sitting(false)
|
_set_sitting(false, local_control_enabled)
|
||||||
else:
|
else:
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
_network_jump_pending = false
|
_network_jump_pending = false
|
||||||
|
|
@ -370,6 +409,8 @@ func _process(delta: float) -> void:
|
||||||
)
|
)
|
||||||
if not local_control_enabled:
|
if not local_control_enabled:
|
||||||
return
|
return
|
||||||
|
if _free_camera_active:
|
||||||
|
return
|
||||||
|
|
||||||
if (
|
if (
|
||||||
_camera_input_enabled
|
_camera_input_enabled
|
||||||
|
|
@ -519,7 +560,7 @@ func toggle_sitting() -> void:
|
||||||
])
|
])
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
_set_sitting(should_sit)
|
_set_sitting(should_sit, local_control_enabled)
|
||||||
|
|
||||||
|
|
||||||
func _has_any_character_animation(candidates: Array[StringName]) -> bool:
|
func _has_any_character_animation(candidates: Array[StringName]) -> bool:
|
||||||
|
|
@ -531,10 +572,16 @@ func _has_any_character_animation(candidates: Array[StringName]) -> bool:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
func _set_sitting(should_sit: bool) -> void:
|
func _set_sitting(
|
||||||
|
should_sit: bool,
|
||||||
|
is_local_intent: bool = false,
|
||||||
|
) -> void:
|
||||||
if _sitting == should_sit:
|
if _sitting == should_sit:
|
||||||
return
|
return
|
||||||
_sitting = should_sit
|
_sitting = should_sit
|
||||||
|
if is_local_intent:
|
||||||
|
_sitting_intent_pending = true
|
||||||
|
_sitting_intent_sequence = -1
|
||||||
if _sitting:
|
if _sitting:
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
_character_animation_name = &""
|
_character_animation_name = &""
|
||||||
|
|
@ -548,6 +595,10 @@ func is_sitting() -> bool:
|
||||||
func _unhandled_input(event: InputEvent) -> void:
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
if not local_control_enabled or not _camera_input_enabled:
|
if not local_control_enabled or not _camera_input_enabled:
|
||||||
return
|
return
|
||||||
|
if event.is_action_pressed("toggle_free_camera"):
|
||||||
|
_set_free_camera_active(not _free_camera_active)
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
|
|
||||||
if event.is_action("camera_drag"):
|
if event.is_action("camera_drag"):
|
||||||
_camera_dragging = event.is_pressed()
|
_camera_dragging = event.is_pressed()
|
||||||
|
|
@ -555,9 +606,14 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||||
return
|
return
|
||||||
|
|
||||||
if event is InputEventMouseMotion and _camera_dragging:
|
if event is InputEventMouseMotion and _camera_dragging:
|
||||||
|
if _free_camera_active:
|
||||||
|
_rotate_free_camera(event.relative * mouse_sensitivity)
|
||||||
|
else:
|
||||||
_rotate_camera(event.relative * mouse_sensitivity)
|
_rotate_camera(event.relative * mouse_sensitivity)
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
return
|
return
|
||||||
|
if _free_camera_active:
|
||||||
|
return
|
||||||
|
|
||||||
var mouse_zoom_in: bool = (
|
var mouse_zoom_in: bool = (
|
||||||
event is InputEventMouseButton
|
event is InputEventMouseButton
|
||||||
|
|
@ -617,6 +673,98 @@ func _rotate_camera(delta_rotation: Vector2) -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _set_free_camera_active(active: bool) -> void:
|
||||||
|
if _free_camera_active == active:
|
||||||
|
return
|
||||||
|
if active:
|
||||||
|
var camera_transform: Transform3D = _camera.global_transform
|
||||||
|
var duplicated_camera := _camera.duplicate() as Camera3D
|
||||||
|
if duplicated_camera == null:
|
||||||
|
return
|
||||||
|
var camera_body := CharacterBody3D.new()
|
||||||
|
camera_body.name = "FreeCameraBody"
|
||||||
|
camera_body.collision_layer = 0
|
||||||
|
camera_body.collision_mask = collision_mask
|
||||||
|
camera_body.motion_mode = CharacterBody3D.MOTION_MODE_FLOATING
|
||||||
|
add_child(camera_body)
|
||||||
|
camera_body.top_level = true
|
||||||
|
camera_body.global_position = camera_transform.origin
|
||||||
|
var camera_collision := CollisionShape3D.new()
|
||||||
|
camera_collision.name = "FreeCameraCollision"
|
||||||
|
camera_collision.shape = _player_collision_shape.shape
|
||||||
|
camera_body.add_child(camera_collision)
|
||||||
|
duplicated_camera.name = "FreeCamera"
|
||||||
|
duplicated_camera.unique_name_in_owner = false
|
||||||
|
camera_body.add_child(duplicated_camera)
|
||||||
|
duplicated_camera.transform = Transform3D(
|
||||||
|
camera_transform.basis,
|
||||||
|
Vector3.ZERO,
|
||||||
|
)
|
||||||
|
_free_camera_body = camera_body
|
||||||
|
_free_camera = duplicated_camera
|
||||||
|
var camera_rotation: Vector3 = _free_camera.global_rotation
|
||||||
|
_free_camera_yaw = camera_rotation.y
|
||||||
|
_free_camera_pitch = camera_rotation.x
|
||||||
|
_camera.current = false
|
||||||
|
_free_camera.current = true
|
||||||
|
_free_camera_active = true
|
||||||
|
return
|
||||||
|
_free_camera_active = false
|
||||||
|
_camera_dragging = false
|
||||||
|
if _free_camera != null:
|
||||||
|
_free_camera.current = false
|
||||||
|
_free_camera = null
|
||||||
|
if _free_camera_body != null:
|
||||||
|
_free_camera_body.queue_free()
|
||||||
|
_free_camera_body = null
|
||||||
|
_camera.current = local_control_enabled
|
||||||
|
|
||||||
|
|
||||||
|
func _update_free_camera_physics() -> void:
|
||||||
|
if _free_camera == null or _free_camera_body == null:
|
||||||
|
_set_free_camera_active(false)
|
||||||
|
return
|
||||||
|
var input_vector: Vector2 = Input.get_vector(
|
||||||
|
"move_left",
|
||||||
|
"move_right",
|
||||||
|
"move_forward",
|
||||||
|
"move_backward",
|
||||||
|
)
|
||||||
|
var vertical_input: float = (
|
||||||
|
Input.get_action_strength("jump")
|
||||||
|
- Input.get_action_strength("sneak")
|
||||||
|
)
|
||||||
|
var movement: Vector3 = (
|
||||||
|
_free_camera.global_basis.x * input_vector.x
|
||||||
|
+ _free_camera.global_basis.z * input_vector.y
|
||||||
|
+ Vector3.UP * vertical_input
|
||||||
|
)
|
||||||
|
if movement.length_squared() > 1.0:
|
||||||
|
movement = movement.normalized()
|
||||||
|
var movement_speed: float = free_camera_speed
|
||||||
|
if Input.is_action_pressed("sprint"):
|
||||||
|
movement_speed *= free_camera_sprint_multiplier
|
||||||
|
_free_camera_body.velocity = movement * movement_speed
|
||||||
|
_free_camera_body.move_and_slide()
|
||||||
|
|
||||||
|
|
||||||
|
func _rotate_free_camera(delta_rotation: Vector2) -> void:
|
||||||
|
if _free_camera == null:
|
||||||
|
return
|
||||||
|
_free_camera_yaw -= delta_rotation.x
|
||||||
|
var vertical_direction: float = -1.0 if invert_camera_y else 1.0
|
||||||
|
_free_camera_pitch = clampf(
|
||||||
|
_free_camera_pitch - delta_rotation.y * vertical_direction,
|
||||||
|
deg_to_rad(minimum_pitch_degrees),
|
||||||
|
deg_to_rad(maximum_pitch_degrees),
|
||||||
|
)
|
||||||
|
_free_camera.global_rotation = Vector3(
|
||||||
|
_free_camera_pitch,
|
||||||
|
_free_camera_yaw,
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _set_target_zoom(value: float) -> void:
|
func _set_target_zoom(value: float) -> void:
|
||||||
_target_zoom = clampf(value, minimum_zoom, maximum_zoom)
|
_target_zoom = clampf(value, minimum_zoom, maximum_zoom)
|
||||||
|
|
||||||
|
|
@ -633,6 +781,8 @@ func _apply_axis_deadzone(value: float) -> float:
|
||||||
|
|
||||||
|
|
||||||
func set_local_control(enabled: bool) -> void:
|
func set_local_control(enabled: bool) -> void:
|
||||||
|
if not enabled and _free_camera_active:
|
||||||
|
_set_free_camera_active(false)
|
||||||
local_control_enabled = enabled
|
local_control_enabled = enabled
|
||||||
if is_node_ready():
|
if is_node_ready():
|
||||||
_camera.current = enabled
|
_camera.current = enabled
|
||||||
|
|
@ -657,7 +807,9 @@ func configure_network_remote(authoritative_simulation: bool) -> void:
|
||||||
|
|
||||||
|
|
||||||
func capture_network_input(sequence: int) -> Dictionary:
|
func capture_network_input(sequence: int) -> Dictionary:
|
||||||
if not _movement_enabled or _water_recovery_active:
|
if _sitting_intent_pending and _sitting_intent_sequence < 0:
|
||||||
|
_sitting_intent_sequence = sequence
|
||||||
|
if not _movement_enabled or _water_recovery_active or _free_camera_active:
|
||||||
return {
|
return {
|
||||||
"sequence": sequence,
|
"sequence": sequence,
|
||||||
"axis": [0.0, 0.0],
|
"axis": [0.0, 0.0],
|
||||||
|
|
@ -734,6 +886,16 @@ func apply_local_prediction_correction(snapshot: Dictionary) -> void:
|
||||||
var parsed: Dictionary = _parse_network_snapshot(snapshot)
|
var parsed: Dictionary = _parse_network_snapshot(snapshot)
|
||||||
if parsed.is_empty():
|
if parsed.is_empty():
|
||||||
return
|
return
|
||||||
|
var acknowledged_input: int = parsed["acknowledged_input"]
|
||||||
|
var sitting_intent_acknowledged: bool = (
|
||||||
|
_sitting_intent_pending
|
||||||
|
and _sitting_intent_sequence >= 0
|
||||||
|
and acknowledged_input >= _sitting_intent_sequence
|
||||||
|
)
|
||||||
|
if sitting_intent_acknowledged:
|
||||||
|
_sitting_intent_pending = false
|
||||||
|
_sitting_intent_sequence = -1
|
||||||
|
if not _sitting_intent_pending:
|
||||||
_set_sitting(bool(parsed["sitting"]))
|
_set_sitting(bool(parsed["sitting"]))
|
||||||
var authoritative_position: Vector3 = parsed["position"]
|
var authoritative_position: Vector3 = parsed["position"]
|
||||||
var error_distance: float = global_position.distance_to(
|
var error_distance: float = global_position.distance_to(
|
||||||
|
|
@ -780,6 +942,17 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary:
|
||||||
float(network_velocity[2])
|
float(network_velocity[2])
|
||||||
)
|
)
|
||||||
var visual_yaw: float = float(snapshot.get("visual_yaw", 0.0))
|
var visual_yaw: float = float(snapshot.get("visual_yaw", 0.0))
|
||||||
|
if (
|
||||||
|
snapshot.has("acknowledged_input")
|
||||||
|
and (
|
||||||
|
typeof(snapshot.get("acknowledged_input")) != TYPE_INT
|
||||||
|
or int(snapshot.get("acknowledged_input")) < 0
|
||||||
|
)
|
||||||
|
):
|
||||||
|
return {}
|
||||||
|
var acknowledged_input: int = int(
|
||||||
|
snapshot.get("acknowledged_input", 0)
|
||||||
|
)
|
||||||
if snapshot.has("sitting") and typeof(snapshot.get("sitting")) != TYPE_BOOL:
|
if snapshot.has("sitting") and typeof(snapshot.get("sitting")) != TYPE_BOOL:
|
||||||
return {}
|
return {}
|
||||||
var sitting: bool = bool(snapshot.get("sitting", false))
|
var sitting: bool = bool(snapshot.get("sitting", false))
|
||||||
|
|
@ -793,6 +966,7 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary:
|
||||||
"position": parsed_position,
|
"position": parsed_position,
|
||||||
"velocity": parsed_velocity,
|
"velocity": parsed_velocity,
|
||||||
"visual_yaw": visual_yaw,
|
"visual_yaw": visual_yaw,
|
||||||
|
"acknowledged_input": acknowledged_input,
|
||||||
"sitting": sitting,
|
"sitting": sitting,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -835,6 +1009,9 @@ func set_camera_input_enabled(enabled: bool) -> void:
|
||||||
|
|
||||||
|
|
||||||
func set_camera_active(active: bool) -> void:
|
func set_camera_active(active: bool) -> void:
|
||||||
|
if _free_camera_active and _free_camera != null:
|
||||||
|
_free_camera.current = active
|
||||||
|
else:
|
||||||
_camera.current = active
|
_camera.current = active
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
71
scripts/generate_placeholder_animalese.gd
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
const OUTPUT_DIRECTORY := "res://sound/dialogue/animalese/placeholder"
|
||||||
|
const SAMPLE_RATE: int = 22050
|
||||||
|
const SAMPLE_SECONDS: float = 0.055
|
||||||
|
const CHARACTERS := "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
const VOWELS := "aeiou"
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
var absolute_directory := ProjectSettings.globalize_path(OUTPUT_DIRECTORY)
|
||||||
|
var directory_error: Error = DirAccess.make_dir_recursive_absolute(
|
||||||
|
absolute_directory
|
||||||
|
)
|
||||||
|
if directory_error != OK:
|
||||||
|
push_error("Could not create animalese sample directory.")
|
||||||
|
quit(1)
|
||||||
|
return
|
||||||
|
for character_index: int in range(CHARACTERS.length()):
|
||||||
|
var character := CHARACTERS.substr(character_index, 1)
|
||||||
|
_save_sample(character, character_index)
|
||||||
|
_save_sample("fallback", CHARACTERS.length())
|
||||||
|
quit()
|
||||||
|
|
||||||
|
|
||||||
|
func _save_sample(sample_name: String, sample_index: int) -> void:
|
||||||
|
var stream := _build_sample(sample_name, sample_index)
|
||||||
|
var output_path := "%s/%s" % [OUTPUT_DIRECTORY, sample_name]
|
||||||
|
var save_error: Error = stream.save_to_wav(output_path)
|
||||||
|
if save_error != OK:
|
||||||
|
push_error("Could not save animalese sample: %s" % sample_name)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_sample(sample_name: String, sample_index: int) -> AudioStreamWAV:
|
||||||
|
var frame_count := roundi(SAMPLE_RATE * SAMPLE_SECONDS)
|
||||||
|
var pcm := PackedByteArray()
|
||||||
|
pcm.resize(frame_count * 2)
|
||||||
|
var base_frequency := 245.0 + float(sample_index % 7) * 18.0
|
||||||
|
var formant_one := 620.0 + float(sample_index % 5) * 85.0
|
||||||
|
var formant_two := 1320.0 + float(sample_index % 6) * 115.0
|
||||||
|
var is_vowel := VOWELS.contains(sample_name)
|
||||||
|
var noise_seed := sample_index * 7919 + 104729
|
||||||
|
for frame_index: int in range(frame_count):
|
||||||
|
var time := float(frame_index) / SAMPLE_RATE
|
||||||
|
var attack := minf(time / 0.004, 1.0)
|
||||||
|
var release := minf((SAMPLE_SECONDS - time) / 0.014, 1.0)
|
||||||
|
var envelope := maxf(minf(attack, release), 0.0)
|
||||||
|
noise_seed = int(
|
||||||
|
(noise_seed * 1103515245 + 12345) & 0x7fffffff
|
||||||
|
)
|
||||||
|
var noise := float(noise_seed % 65536) / 32767.5 - 1.0
|
||||||
|
var voiced := (
|
||||||
|
sin(TAU * base_frequency * time) * 0.42
|
||||||
|
+ sin(TAU * base_frequency * 2.0 * time) * 0.16
|
||||||
|
)
|
||||||
|
var formants := (
|
||||||
|
sin(TAU * formant_one * time) * 0.18
|
||||||
|
+ sin(TAU * formant_two * time) * 0.10
|
||||||
|
)
|
||||||
|
var consonant_noise := noise * (0.08 if is_vowel else 0.24)
|
||||||
|
var value := envelope * (voiced + formants + consonant_noise) * 0.72
|
||||||
|
var signed_sample := clampi(roundi(value * 32767.0), -32768, 32767)
|
||||||
|
var encoded_sample := signed_sample if signed_sample >= 0 else signed_sample + 65536
|
||||||
|
pcm[frame_index * 2] = encoded_sample & 0xff
|
||||||
|
pcm[frame_index * 2 + 1] = (encoded_sample >> 8) & 0xff
|
||||||
|
var stream := AudioStreamWAV.new()
|
||||||
|
stream.set("format", AudioStreamWAV.FORMAT_16_BITS)
|
||||||
|
stream.mix_rate = SAMPLE_RATE
|
||||||
|
stream.stereo = false
|
||||||
|
stream.data = pcm
|
||||||
|
return stream
|
||||||
1
scripts/generate_placeholder_animalese.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cwqm75icgcr4u
|
||||||
BIN
sound/dialogue/animalese/placeholder/a.wav
Normal file
24
sound/dialogue/animalese/placeholder/a.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://dvajjy4bwadio"
|
||||||
|
path="res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/a.wav"
|
||||||
|
dest_files=["res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/b.wav
Normal file
24
sound/dialogue/animalese/placeholder/b.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://cs30tdyuo6wtc"
|
||||||
|
path="res://.godot/imported/b.wav-eed860af2033bda258bd172899061b45.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/b.wav"
|
||||||
|
dest_files=["res://.godot/imported/b.wav-eed860af2033bda258bd172899061b45.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/c.wav
Normal file
24
sound/dialogue/animalese/placeholder/c.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://2inox7x31xam"
|
||||||
|
path="res://.godot/imported/c.wav-61dc49533634805d70c71d509b63e37b.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/c.wav"
|
||||||
|
dest_files=["res://.godot/imported/c.wav-61dc49533634805d70c71d509b63e37b.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/d.wav
Normal file
24
sound/dialogue/animalese/placeholder/d.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://yvha8eukw6l7"
|
||||||
|
path="res://.godot/imported/d.wav-910bc02bc42bdb1b37561959a317c28a.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/d.wav"
|
||||||
|
dest_files=["res://.godot/imported/d.wav-910bc02bc42bdb1b37561959a317c28a.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/e.wav
Normal file
24
sound/dialogue/animalese/placeholder/e.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://8ujopf8aqs83"
|
||||||
|
path="res://.godot/imported/e.wav-7da5f8fcd017bf931de2bd49dc00f652.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/e.wav"
|
||||||
|
dest_files=["res://.godot/imported/e.wav-7da5f8fcd017bf931de2bd49dc00f652.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/f.wav
Normal file
24
sound/dialogue/animalese/placeholder/f.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://c0gv4cphnfcxf"
|
||||||
|
path="res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/f.wav"
|
||||||
|
dest_files=["res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/fallback.wav
Normal file
24
sound/dialogue/animalese/placeholder/fallback.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://bw0pod4tmexm0"
|
||||||
|
path="res://.godot/imported/fallback.wav-7a40516b188f10028ac39bdc24fb52e5.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/fallback.wav"
|
||||||
|
dest_files=["res://.godot/imported/fallback.wav-7a40516b188f10028ac39bdc24fb52e5.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/g.wav
Normal file
24
sound/dialogue/animalese/placeholder/g.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://c7706p0fc8y7c"
|
||||||
|
path="res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/g.wav"
|
||||||
|
dest_files=["res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/h.wav
Normal file
24
sound/dialogue/animalese/placeholder/h.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://cl66wka105xmo"
|
||||||
|
path="res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/h.wav"
|
||||||
|
dest_files=["res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/i.wav
Normal file
24
sound/dialogue/animalese/placeholder/i.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://jicgdbnyt6bm"
|
||||||
|
path="res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/i.wav"
|
||||||
|
dest_files=["res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/j.wav
Normal file
24
sound/dialogue/animalese/placeholder/j.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://cji2evr0u3m6c"
|
||||||
|
path="res://.godot/imported/j.wav-2f355374ffcea77f8dc630ee45879444.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/j.wav"
|
||||||
|
dest_files=["res://.godot/imported/j.wav-2f355374ffcea77f8dc630ee45879444.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/k.wav
Normal file
24
sound/dialogue/animalese/placeholder/k.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://eypaqf1iaq5"
|
||||||
|
path="res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/k.wav"
|
||||||
|
dest_files=["res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/l.wav
Normal file
24
sound/dialogue/animalese/placeholder/l.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://bixgnaq4idkhj"
|
||||||
|
path="res://.godot/imported/l.wav-b21006c3b048e0c797d6baf19ab0f663.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/l.wav"
|
||||||
|
dest_files=["res://.godot/imported/l.wav-b21006c3b048e0c797d6baf19ab0f663.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/m.wav
Normal file
24
sound/dialogue/animalese/placeholder/m.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://dan4o17oreugv"
|
||||||
|
path="res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/m.wav"
|
||||||
|
dest_files=["res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/n.wav
Normal file
24
sound/dialogue/animalese/placeholder/n.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://dxup6pw80km61"
|
||||||
|
path="res://.godot/imported/n.wav-84e9a6fa6664222868d647187a09f374.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/n.wav"
|
||||||
|
dest_files=["res://.godot/imported/n.wav-84e9a6fa6664222868d647187a09f374.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/o.wav
Normal file
24
sound/dialogue/animalese/placeholder/o.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://lntkug4avr2k"
|
||||||
|
path="res://.godot/imported/o.wav-6c18faa9701875d7753d91afcd1b09e6.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/o.wav"
|
||||||
|
dest_files=["res://.godot/imported/o.wav-6c18faa9701875d7753d91afcd1b09e6.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/p.wav
Normal file
24
sound/dialogue/animalese/placeholder/p.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://crubsdrd220eh"
|
||||||
|
path="res://.godot/imported/p.wav-c32b759c822f258d8d947827adf8127a.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/p.wav"
|
||||||
|
dest_files=["res://.godot/imported/p.wav-c32b759c822f258d8d947827adf8127a.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/q.wav
Normal file
24
sound/dialogue/animalese/placeholder/q.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://ct0decr76agoq"
|
||||||
|
path="res://.godot/imported/q.wav-d494b927c6f2b8bfbaa44d1f81887731.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/q.wav"
|
||||||
|
dest_files=["res://.godot/imported/q.wav-d494b927c6f2b8bfbaa44d1f81887731.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/r.wav
Normal file
24
sound/dialogue/animalese/placeholder/r.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://dpq7rin4pr1n3"
|
||||||
|
path="res://.godot/imported/r.wav-d1d419e3246c0e918ff51a9dd662a742.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/r.wav"
|
||||||
|
dest_files=["res://.godot/imported/r.wav-d1d419e3246c0e918ff51a9dd662a742.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/s.wav
Normal file
24
sound/dialogue/animalese/placeholder/s.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://c00meumj2blfa"
|
||||||
|
path="res://.godot/imported/s.wav-15abb20075aacc8d5fe95cd206ae25ce.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/s.wav"
|
||||||
|
dest_files=["res://.godot/imported/s.wav-15abb20075aacc8d5fe95cd206ae25ce.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/t.wav
Normal file
24
sound/dialogue/animalese/placeholder/t.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://dr0dp5v8fdb8r"
|
||||||
|
path="res://.godot/imported/t.wav-3a26c5be0f8e3007adf1daa05a6a1ea2.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/t.wav"
|
||||||
|
dest_files=["res://.godot/imported/t.wav-3a26c5be0f8e3007adf1daa05a6a1ea2.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/u.wav
Normal file
24
sound/dialogue/animalese/placeholder/u.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://bln1bkdu20muo"
|
||||||
|
path="res://.godot/imported/u.wav-06e44c423f3d1cd6cdd34b54270211bb.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/u.wav"
|
||||||
|
dest_files=["res://.godot/imported/u.wav-06e44c423f3d1cd6cdd34b54270211bb.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/v.wav
Normal file
24
sound/dialogue/animalese/placeholder/v.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://y3g1s3pw3bn0"
|
||||||
|
path="res://.godot/imported/v.wav-5a9986e91dbdd0da4f0583b35a986b1c.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/v.wav"
|
||||||
|
dest_files=["res://.godot/imported/v.wav-5a9986e91dbdd0da4f0583b35a986b1c.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/w.wav
Normal file
24
sound/dialogue/animalese/placeholder/w.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://5ml2iuf8cpgt"
|
||||||
|
path="res://.godot/imported/w.wav-d3e9035a1fc5e7946be782a0d4f95e64.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/w.wav"
|
||||||
|
dest_files=["res://.godot/imported/w.wav-d3e9035a1fc5e7946be782a0d4f95e64.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/x.wav
Normal file
24
sound/dialogue/animalese/placeholder/x.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://b5al0m4de1m5x"
|
||||||
|
path="res://.godot/imported/x.wav-b1beaddcc7d6663882ddd8b86e65db56.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/x.wav"
|
||||||
|
dest_files=["res://.godot/imported/x.wav-b1beaddcc7d6663882ddd8b86e65db56.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/y.wav
Normal file
24
sound/dialogue/animalese/placeholder/y.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://c6hust77yamoe"
|
||||||
|
path="res://.godot/imported/y.wav-5ef178912b2921bb7c790b12e2e82337.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/y.wav"
|
||||||
|
dest_files=["res://.godot/imported/y.wav-5ef178912b2921bb7c790b12e2e82337.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
BIN
sound/dialogue/animalese/placeholder/z.wav
Normal file
24
sound/dialogue/animalese/placeholder/z.wav.import
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://djvv63b0jlll"
|
||||||
|
path="res://.godot/imported/z.wav-3e6ff24f53b1014b381cc80ee9fb1a84.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://sound/dialogue/animalese/placeholder/z.wav"
|
||||||
|
dest_files=["res://.godot/imported/z.wav-3e6ff24f53b1014b381cc80ee9fb1a84.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
||||||
123
ui/animalese_voice.gd
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
class_name AnimaleseVoice
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
const SAMPLE_DIRECTORY := "res://sound/dialogue/animalese/placeholder"
|
||||||
|
const SUPPORTED_CHARACTERS := "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
const DEFAULT_CHARACTERS_PER_SECOND: float = 28.0
|
||||||
|
const POLYPHONY: int = 8
|
||||||
|
const VoiceProfilesType = preload(
|
||||||
|
"res://player/animalese_voice_profiles.gd"
|
||||||
|
)
|
||||||
|
const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd")
|
||||||
|
|
||||||
|
var base_pitch: float = 1.0
|
||||||
|
var volume_db: float = -13.0
|
||||||
|
|
||||||
|
var _samples: Dictionary[String, AudioStream] = {}
|
||||||
|
var _player: AudioStreamPlayer
|
||||||
|
var _playback: AudioStreamPlaybackPolyphonic
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_load_samples()
|
||||||
|
var polyphonic_stream := AudioStreamPolyphonic.new()
|
||||||
|
polyphonic_stream.polyphony = POLYPHONY
|
||||||
|
_player = AudioStreamPlayer.new()
|
||||||
|
_player.name = "AnimaleseAudio"
|
||||||
|
_player.stream = polyphonic_stream
|
||||||
|
add_child(_player)
|
||||||
|
_player.play()
|
||||||
|
_playback = (
|
||||||
|
_player.get_stream_playback() as AudioStreamPlaybackPolyphonic
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func speak_text(
|
||||||
|
owner: Node,
|
||||||
|
text: String,
|
||||||
|
voice_key: String,
|
||||||
|
voice_profile_id: String = VoiceProfilesType.DEFAULT_ID,
|
||||||
|
characters_per_second: float = -1.0,
|
||||||
|
) -> Tween:
|
||||||
|
var speech_tween := owner.create_tween()
|
||||||
|
var resolved_characters_per_second := (
|
||||||
|
characters_per_second
|
||||||
|
if characters_per_second > 0.0
|
||||||
|
else TypewriterRevealType.get_characters_per_second()
|
||||||
|
)
|
||||||
|
var character_seconds := 1.0 / maxf(
|
||||||
|
resolved_characters_per_second,
|
||||||
|
1.0,
|
||||||
|
)
|
||||||
|
var voice_pitch := (
|
||||||
|
base_pitch * VoiceProfilesType.pitch_for(voice_profile_id)
|
||||||
|
)
|
||||||
|
for character_index: int in range(text.length()):
|
||||||
|
speech_tween.tween_interval(character_seconds)
|
||||||
|
speech_tween.tween_callback(
|
||||||
|
_play_character.bind(
|
||||||
|
text.substr(character_index, 1),
|
||||||
|
character_index,
|
||||||
|
text,
|
||||||
|
voice_key,
|
||||||
|
voice_pitch,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return speech_tween
|
||||||
|
|
||||||
|
|
||||||
|
func _load_samples() -> void:
|
||||||
|
for character_index: int in range(SUPPORTED_CHARACTERS.length()):
|
||||||
|
var character := SUPPORTED_CHARACTERS.substr(character_index, 1)
|
||||||
|
_load_sample(character)
|
||||||
|
_load_sample("fallback")
|
||||||
|
|
||||||
|
|
||||||
|
func _load_sample(sample_name: String) -> void:
|
||||||
|
var sample_path := "%s/%s.wav" % [SAMPLE_DIRECTORY, sample_name]
|
||||||
|
var sample := load(sample_path) as AudioStream
|
||||||
|
if sample != null:
|
||||||
|
_samples[sample_name] = sample
|
||||||
|
|
||||||
|
|
||||||
|
func _play_character(
|
||||||
|
character: String,
|
||||||
|
character_index: int,
|
||||||
|
full_text: String,
|
||||||
|
voice_key: String,
|
||||||
|
voice_pitch: float,
|
||||||
|
) -> void:
|
||||||
|
if _playback == null or character.strip_edges().is_empty():
|
||||||
|
return
|
||||||
|
var normalized := character.to_lower()
|
||||||
|
if normalized in [".", ",", "!", "?", ":", ";", "-", "_"]:
|
||||||
|
return
|
||||||
|
var sample_name := (
|
||||||
|
normalized if SUPPORTED_CHARACTERS.contains(normalized) else "fallback"
|
||||||
|
)
|
||||||
|
var sample: AudioStream = _samples.get(sample_name)
|
||||||
|
if sample == null:
|
||||||
|
return
|
||||||
|
var speaker_variation := (
|
||||||
|
float(posmod(hash(voice_key), 1001)) / 1000.0 - 0.5
|
||||||
|
) * 0.16
|
||||||
|
var character_variation := (
|
||||||
|
float(posmod(hash("%s:%d" % [voice_key, character_index]), 1001))
|
||||||
|
/ 1000.0
|
||||||
|
- 0.5
|
||||||
|
) * 0.10
|
||||||
|
var question_lift := 0.0
|
||||||
|
if full_text.ends_with("?") and character_index >= full_text.length() * 3 / 4:
|
||||||
|
var final_progress := (
|
||||||
|
float(character_index) / maxf(float(full_text.length() - 1), 1.0)
|
||||||
|
)
|
||||||
|
question_lift = lerpf(0.0, 0.14, final_progress)
|
||||||
|
var pitch := clampf(
|
||||||
|
voice_pitch
|
||||||
|
+ speaker_variation
|
||||||
|
+ character_variation
|
||||||
|
+ question_lift,
|
||||||
|
0.72,
|
||||||
|
1.45,
|
||||||
|
)
|
||||||
|
_playback.play_stream(sample, 0.0, volume_db, pitch)
|
||||||
1
ui/animalese_voice.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://djst00wf8yntj
|
||||||
BIN
ui/assets/logbook/ui_logbook.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
40
ui/assets/logbook/ui_logbook.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bnwbcopsjmi3s"
|
||||||
|
path="res://.godot/imported/ui_logbook.png-e45558834671114c04857bb4a6091575.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/assets/logbook/ui_logbook.png"
|
||||||
|
dest_files=["res://.godot/imported/ui_logbook.png-e45558834671114c04857bb4a6091575.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
167
ui/chat_ui.gd
|
|
@ -6,6 +6,7 @@ const RECENT_SECONDS: float = 8.0
|
||||||
const SPEECH_SECONDS: float = 6.0
|
const SPEECH_SECONDS: float = 6.0
|
||||||
const DRAFT_SAVE_DELAY: float = 0.4
|
const DRAFT_SAVE_DELAY: float = 0.4
|
||||||
const IDLE_ALPHA: float = 0.58
|
const IDLE_ALPHA: float = 0.58
|
||||||
|
const CHAT_SURFACE_COLOR := Color(0.025, 0.13, 0.19, 0.94)
|
||||||
const ORIGINAL_PANEL_WIDTH: float = 390.0
|
const ORIGINAL_PANEL_WIDTH: float = 390.0
|
||||||
const PANEL_WIDTH: float = ORIGINAL_PANEL_WIDTH * 0.85
|
const PANEL_WIDTH: float = ORIGINAL_PANEL_WIDTH * 0.85
|
||||||
const COMPACT_HEIGHT: float = 250.0
|
const COMPACT_HEIGHT: float = 250.0
|
||||||
|
|
@ -33,12 +34,31 @@ const CLOCK_SIZE := Vector2(116.0, 34.0)
|
||||||
const CLOCK_EDGE_MARGIN: float = 10.0
|
const CLOCK_EDGE_MARGIN: float = 10.0
|
||||||
const WEATHER_ICON_SIZE := Vector2(34.0, 34.0)
|
const WEATHER_ICON_SIZE := Vector2(34.0, 34.0)
|
||||||
const WEATHER_ICON_GAP: float = 6.0
|
const WEATHER_ICON_GAP: float = 6.0
|
||||||
|
const CHAT_SHOW_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/pictograms/arrow_light_right_more.png"
|
||||||
|
)
|
||||||
|
const CHAT_HIDE_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/pictograms/arrow_light_left_more.png"
|
||||||
|
)
|
||||||
|
const CHAT_EXPAND_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/pictograms/arrow_light_up_more.png"
|
||||||
|
)
|
||||||
|
const CHAT_COMPACT_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/pictograms/arrow_light_down_more.png"
|
||||||
|
)
|
||||||
const WorldTimeServiceType = preload("res://world/world_time_service.gd")
|
const WorldTimeServiceType = preload("res://world/world_time_service.gd")
|
||||||
const WorldWeatherServiceType = preload(
|
const WorldWeatherServiceType = preload(
|
||||||
"res://world/world_weather_service.gd"
|
"res://world/world_weather_service.gd"
|
||||||
)
|
)
|
||||||
const WeatherIconType = preload("res://ui/weather_icon.gd")
|
const WeatherIconType = preload("res://ui/weather_icon.gd")
|
||||||
|
|
||||||
|
const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd")
|
||||||
|
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
|
||||||
|
const VoiceProfilesType = preload(
|
||||||
|
"res://player/animalese_voice_profiles.gd"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
enum PresentationState {
|
enum PresentationState {
|
||||||
COLLAPSED,
|
COLLAPSED,
|
||||||
COMPACT,
|
COMPACT,
|
||||||
|
|
@ -62,6 +82,7 @@ var _height_button: Button
|
||||||
var _unread_indicator: Label
|
var _unread_indicator: Label
|
||||||
var _hint: Label
|
var _hint: Label
|
||||||
var _speech_layer: Control
|
var _speech_layer: Control
|
||||||
|
var _animalese_voice: AnimaleseVoiceType
|
||||||
var _clock_panel: PanelContainer
|
var _clock_panel: PanelContainer
|
||||||
var _clock_label: Label
|
var _clock_label: Label
|
||||||
var _weather_icon: WeatherIconType
|
var _weather_icon: WeatherIconType
|
||||||
|
|
@ -379,6 +400,15 @@ func _build_ui() -> void:
|
||||||
_collapse_button.mouse_filter = Control.MOUSE_FILTER_STOP
|
_collapse_button.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
_collapse_button.focus_mode = Control.FOCUS_ALL
|
_collapse_button.focus_mode = Control.FOCUS_ALL
|
||||||
_collapse_button.z_index = 3
|
_collapse_button.z_index = 3
|
||||||
|
_collapse_button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
_collapse_button.expand_icon = true
|
||||||
|
_collapse_button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
_collapse_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
_collapse_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
|
_collapse_button.add_theme_constant_override(
|
||||||
|
"icon_max_width",
|
||||||
|
roundi(minf(HANDLE_SIZE.x, HANDLE_SIZE.y) * 0.5),
|
||||||
|
)
|
||||||
_collapse_button.tooltip_text = "Collapse chat"
|
_collapse_button.tooltip_text = "Collapse chat"
|
||||||
_collapse_button.pressed.connect(func() -> void:
|
_collapse_button.pressed.connect(func() -> void:
|
||||||
if _presentation_state == PresentationState.COLLAPSED:
|
if _presentation_state == PresentationState.COLLAPSED:
|
||||||
|
|
@ -405,6 +435,15 @@ func _build_ui() -> void:
|
||||||
_height_button.mouse_filter = Control.MOUSE_FILTER_STOP
|
_height_button.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
_height_button.focus_mode = Control.FOCUS_ALL
|
_height_button.focus_mode = Control.FOCUS_ALL
|
||||||
_height_button.z_index = 3
|
_height_button.z_index = 3
|
||||||
|
_height_button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
_height_button.expand_icon = true
|
||||||
|
_height_button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
_height_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
_height_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
|
_height_button.add_theme_constant_override(
|
||||||
|
"icon_max_width",
|
||||||
|
roundi(minf(HANDLE_SIZE.x, HANDLE_SIZE.y) * 0.5),
|
||||||
|
)
|
||||||
_height_button.pressed.connect(func() -> void:
|
_height_button.pressed.connect(func() -> void:
|
||||||
if _presentation_state == PresentationState.COLLAPSED:
|
if _presentation_state == PresentationState.COLLAPSED:
|
||||||
_visible_state_before_collapse = (
|
_visible_state_before_collapse = (
|
||||||
|
|
@ -461,6 +500,9 @@ func _build_ui() -> void:
|
||||||
_speech_layer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
_speech_layer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
_speech_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
_speech_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
add_child(_speech_layer)
|
add_child(_speech_layer)
|
||||||
|
_animalese_voice = AnimaleseVoiceType.new()
|
||||||
|
_animalese_voice.name = "PlayerAnimaleseVoice"
|
||||||
|
add_child(_animalese_voice)
|
||||||
_draft_save_timer = Timer.new()
|
_draft_save_timer = Timer.new()
|
||||||
_draft_save_timer.one_shot = true
|
_draft_save_timer.one_shot = true
|
||||||
_draft_save_timer.wait_time = DRAFT_SAVE_DELAY
|
_draft_save_timer.wait_time = DRAFT_SAVE_DELAY
|
||||||
|
|
@ -478,7 +520,7 @@ func _build_ui() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _chat_panel_style() -> StyleBoxFlat:
|
func _chat_panel_style() -> StyleBoxFlat:
|
||||||
var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94))
|
var style := _borderless_style(CHAT_SURFACE_COLOR)
|
||||||
if _mobile_mode:
|
if _mobile_mode:
|
||||||
style.corner_radius_top_left = 0
|
style.corner_radius_top_left = 0
|
||||||
style.corner_radius_top_right = 0
|
style.corner_radius_top_right = 0
|
||||||
|
|
@ -509,7 +551,7 @@ func _refresh_input_ownership() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _clock_panel_style() -> StyleBoxFlat:
|
func _clock_panel_style() -> StyleBoxFlat:
|
||||||
var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94))
|
var style := _borderless_style(CHAT_SURFACE_COLOR)
|
||||||
style.set_corner_radius_all(12)
|
style.set_corner_radius_all(12)
|
||||||
style.content_margin_left = 10
|
style.content_margin_left = 10
|
||||||
style.content_margin_right = 10
|
style.content_margin_right = 10
|
||||||
|
|
@ -522,6 +564,10 @@ func _borderless_style(color: Color) -> StyleBoxFlat:
|
||||||
var style := StyleBoxFlat.new()
|
var style := StyleBoxFlat.new()
|
||||||
style.bg_color = color
|
style.bg_color = color
|
||||||
style.set_border_width_all(0)
|
style.set_border_width_all(0)
|
||||||
|
style.anti_aliasing = false
|
||||||
|
style.shadow_size = 0
|
||||||
|
style.shadow_color = Color.TRANSPARENT
|
||||||
|
style.shadow_offset = Vector2.ZERO
|
||||||
return style
|
return style
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -530,6 +576,12 @@ func _flat_chat_button_style(color: Color) -> StyleBoxFlat:
|
||||||
style.bg_color = color
|
style.bg_color = color
|
||||||
style.set_border_width_all(0)
|
style.set_border_width_all(0)
|
||||||
style.set_corner_radius_all(0)
|
style.set_corner_radius_all(0)
|
||||||
|
if _dock_right:
|
||||||
|
style.corner_radius_top_left = 10
|
||||||
|
style.corner_radius_bottom_left = 10
|
||||||
|
else:
|
||||||
|
style.corner_radius_top_right = 10
|
||||||
|
style.corner_radius_bottom_right = 10
|
||||||
style.anti_aliasing = false
|
style.anti_aliasing = false
|
||||||
style.shadow_size = 0
|
style.shadow_size = 0
|
||||||
style.shadow_color = Color.TRANSPARENT
|
style.shadow_color = Color.TRANSPARENT
|
||||||
|
|
@ -572,6 +624,18 @@ func _apply_flat_chat_button(button: Button) -> void:
|
||||||
button.add_theme_stylebox_override(
|
button.add_theme_stylebox_override(
|
||||||
"disabled", _flat_chat_button_style(UtilityPageStyle.OCEAN_DISABLED)
|
"disabled", _flat_chat_button_style(UtilityPageStyle.OCEAN_DISABLED)
|
||||||
)
|
)
|
||||||
|
for state: StringName in [
|
||||||
|
&"normal",
|
||||||
|
&"hover",
|
||||||
|
&"pressed",
|
||||||
|
&"hover_pressed",
|
||||||
|
&"focus",
|
||||||
|
&"disabled",
|
||||||
|
]:
|
||||||
|
button.add_theme_stylebox_override(
|
||||||
|
state,
|
||||||
|
_flat_chat_button_style(CHAT_SURFACE_COLOR),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _speech_panel_style() -> StyleBoxFlat:
|
func _speech_panel_style() -> StyleBoxFlat:
|
||||||
|
|
@ -605,6 +669,8 @@ func _send() -> void:
|
||||||
if _send_pending:
|
if _send_pending:
|
||||||
return
|
return
|
||||||
var body := _entry.text
|
var body := _entry.text
|
||||||
|
if _handle_chat_command(body):
|
||||||
|
return
|
||||||
_send_pending = true
|
_send_pending = true
|
||||||
_pending_send_body = NetworkChatProtocol.sanitize_body(body)
|
_pending_send_body = NetworkChatProtocol.sanitize_body(body)
|
||||||
_entry.editable = false
|
_entry.editable = false
|
||||||
|
|
@ -616,6 +682,59 @@ func _send() -> void:
|
||||||
_set_status("Sending…")
|
_set_status("Sending…")
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_chat_command(body: String) -> bool:
|
||||||
|
var command_text := body.strip_edges()
|
||||||
|
if not command_text.begins_with("/"):
|
||||||
|
return false
|
||||||
|
var parts: PackedStringArray = command_text.split(" ", false)
|
||||||
|
var command := String(parts[0]).trim_prefix("/").to_lower()
|
||||||
|
match command:
|
||||||
|
"time":
|
||||||
|
_handle_time_command(parts)
|
||||||
|
"":
|
||||||
|
_set_status("Enter a command after /.")
|
||||||
|
_:
|
||||||
|
_set_status("Unknown command: /%s" % command)
|
||||||
|
_entry.clear()
|
||||||
|
_flush_draft()
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_time_command(parts: PackedStringArray) -> void:
|
||||||
|
if parts.size() != 2:
|
||||||
|
_set_status("Usage: /time [dawn, day, dusk, night]")
|
||||||
|
return
|
||||||
|
if _session == null or not _session.is_host():
|
||||||
|
_set_status("Only the host can change world time.")
|
||||||
|
return
|
||||||
|
if _world_time == null:
|
||||||
|
_set_status("World time is unavailable.")
|
||||||
|
return
|
||||||
|
var phase_name := String(parts[1]).to_lower()
|
||||||
|
var target_hour: float
|
||||||
|
match phase_name:
|
||||||
|
"dawn":
|
||||||
|
target_hour = WorldTimeServiceType.DAWN_START_HOUR
|
||||||
|
"day":
|
||||||
|
target_hour = WorldTimeServiceType.DAWN_END_HOUR
|
||||||
|
"dusk":
|
||||||
|
target_hour = WorldTimeServiceType.DUSK_START_HOUR
|
||||||
|
"night":
|
||||||
|
target_hour = WorldTimeServiceType.DUSK_END_HOUR
|
||||||
|
_:
|
||||||
|
_set_status("Usage: /time [dawn, day, dusk, night]")
|
||||||
|
return
|
||||||
|
if not _world_time.set_authoritative_time(target_hour):
|
||||||
|
_set_status("World time could not be changed.")
|
||||||
|
return
|
||||||
|
_set_status("")
|
||||||
|
_service.broadcast_system_message(
|
||||||
|
"World time set to %s (%s)."
|
||||||
|
% [phase_name, _world_time.get_clock_text()]
|
||||||
|
)
|
||||||
|
close_chat()
|
||||||
|
|
||||||
|
|
||||||
func _on_local_message_confirmed(message: Dictionary) -> void:
|
func _on_local_message_confirmed(message: Dictionary) -> void:
|
||||||
if (
|
if (
|
||||||
not _send_pending
|
not _send_pending
|
||||||
|
|
@ -664,10 +783,27 @@ func _on_message(message: Dictionary) -> void:
|
||||||
)
|
)
|
||||||
bubble.add_child(label)
|
bubble.add_child(label)
|
||||||
_speech_layer.add_child(bubble)
|
_speech_layer.add_child(bubble)
|
||||||
|
var reveal_seconds := TypewriterRevealType.start(label)
|
||||||
|
var voice_profile_id: String = VoiceProfilesType.DEFAULT_ID
|
||||||
|
var speaker_avatar := _spawn.get_avatar(peer_id)
|
||||||
|
if speaker_avatar != null:
|
||||||
|
voice_profile_id = VoiceProfilesType.sanitized_id(
|
||||||
|
speaker_avatar.get_animalese_voice_id()
|
||||||
|
)
|
||||||
|
_animalese_voice.speak_text(
|
||||||
|
label,
|
||||||
|
label.text,
|
||||||
|
str(peer_id),
|
||||||
|
voice_profile_id,
|
||||||
|
)
|
||||||
_speech[peer_id] = {
|
_speech[peer_id] = {
|
||||||
"bubble": bubble,
|
"bubble": bubble,
|
||||||
"pointer": pointer,
|
"pointer": pointer,
|
||||||
"expires": Time.get_ticks_msec() / 1000.0 + SPEECH_SECONDS,
|
"expires": (
|
||||||
|
Time.get_ticks_msec() / 1000.0
|
||||||
|
+ reveal_seconds
|
||||||
|
+ SPEECH_SECONDS
|
||||||
|
),
|
||||||
"fingerprint": str(message.get("sender_fingerprint", "")),
|
"fingerprint": str(message.get("sender_fingerprint", "")),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -770,6 +906,8 @@ func set_dock_right(should_dock_right: bool) -> void:
|
||||||
if not is_node_ready() or _panel == null:
|
if not is_node_ready() or _panel == null:
|
||||||
return
|
return
|
||||||
_panel.add_theme_stylebox_override("panel", _chat_panel_style())
|
_panel.add_theme_stylebox_override("panel", _chat_panel_style())
|
||||||
|
_apply_flat_chat_button(_collapse_button)
|
||||||
|
_apply_flat_chat_button(_height_button)
|
||||||
_refresh_handle_labels(_presentation_state)
|
_refresh_handle_labels(_presentation_state)
|
||||||
_layout_presentation(false)
|
_layout_presentation(false)
|
||||||
|
|
||||||
|
|
@ -800,21 +938,24 @@ func is_mobile_mode() -> bool:
|
||||||
func _refresh_handle_labels(state: PresentationState) -> void:
|
func _refresh_handle_labels(state: PresentationState) -> void:
|
||||||
var collapsed := state == PresentationState.COLLAPSED
|
var collapsed := state == PresentationState.COLLAPSED
|
||||||
var height_state := _visible_state_before_collapse if collapsed else state
|
var height_state := _visible_state_before_collapse if collapsed else state
|
||||||
if _mobile_mode:
|
_collapse_button.text = ""
|
||||||
_collapse_button.text = "v" if collapsed else "^"
|
_collapse_button.icon = CHAT_SHOW_ICON if collapsed else CHAT_HIDE_ICON
|
||||||
elif _dock_right:
|
|
||||||
_collapse_button.text = "<" if collapsed else ">"
|
|
||||||
else:
|
|
||||||
_collapse_button.text = ">" if collapsed else "<"
|
|
||||||
_collapse_button.tooltip_text = (
|
_collapse_button.tooltip_text = (
|
||||||
"Show chat" if collapsed else "Hide chat"
|
"Show chat" if collapsed else "Hide chat"
|
||||||
)
|
)
|
||||||
_height_button.text = "v" if height_state == PresentationState.EXPANDED else "^"
|
_collapse_button.accessibility_name = _collapse_button.tooltip_text
|
||||||
|
_height_button.text = ""
|
||||||
|
_height_button.icon = (
|
||||||
|
CHAT_COMPACT_ICON
|
||||||
|
if height_state == PresentationState.EXPANDED
|
||||||
|
else CHAT_EXPAND_ICON
|
||||||
|
)
|
||||||
_height_button.tooltip_text = (
|
_height_button.tooltip_text = (
|
||||||
"Compact chat"
|
"Compact chat"
|
||||||
if height_state == PresentationState.EXPANDED
|
if height_state == PresentationState.EXPANDED
|
||||||
else "Expand chat history"
|
else "Expand chat history"
|
||||||
)
|
)
|
||||||
|
_height_button.accessibility_name = _height_button.tooltip_text
|
||||||
|
|
||||||
|
|
||||||
func _update_panel_opacity(immediate_recheck: bool = false) -> void:
|
func _update_panel_opacity(immediate_recheck: bool = false) -> void:
|
||||||
|
|
@ -823,10 +964,8 @@ func _update_panel_opacity(immediate_recheck: bool = false) -> void:
|
||||||
if _presentation_state == PresentationState.COLLAPSED:
|
if _presentation_state == PresentationState.COLLAPSED:
|
||||||
if _height_tween == null or not _height_tween.is_running():
|
if _height_tween == null or not _height_tween.is_running():
|
||||||
_panel.modulate.a = IDLE_ALPHA
|
_panel.modulate.a = IDLE_ALPHA
|
||||||
_collapse_button.modulate.a = (
|
_collapse_button.modulate.a = IDLE_ALPHA
|
||||||
1.0 if _collapsed_has_unread else 0.82
|
_height_button.modulate.a = IDLE_ALPHA
|
||||||
)
|
|
||||||
_height_button.modulate.a = 0.82
|
|
||||||
return
|
return
|
||||||
var recent := (
|
var recent := (
|
||||||
Time.get_ticks_msec() / 1000.0 - _last_message_time < RECENT_SECONDS
|
Time.get_ticks_msec() / 1000.0 - _last_message_time < RECENT_SECONDS
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
class_name BubbleButton
|
class_name BubbleButton
|
||||||
extends Button
|
extends Button
|
||||||
|
|
||||||
|
const ICON_FILL_RATIO: float = 0.76
|
||||||
|
|
||||||
@export var profile: BubbleMenuProfile
|
@export var profile: BubbleMenuProfile
|
||||||
|
|
||||||
@export_group("Authored Layout")
|
@export_group("Authored Layout")
|
||||||
|
|
@ -37,6 +39,7 @@ func _ready() -> void:
|
||||||
resized.connect(_update_pivot)
|
resized.connect(_update_pivot)
|
||||||
_update_pivot()
|
_update_pivot()
|
||||||
apply_profile()
|
apply_profile()
|
||||||
|
_apply_icon_presentation(neutral_size)
|
||||||
|
|
||||||
|
|
||||||
func apply_layout(
|
func apply_layout(
|
||||||
|
|
@ -49,6 +52,7 @@ func apply_layout(
|
||||||
neutral_position = position
|
neutral_position = position
|
||||||
presented_size = bubble_size
|
presented_size = bubble_size
|
||||||
_update_pivot()
|
_update_pivot()
|
||||||
|
_apply_icon_presentation(bubble_size)
|
||||||
var label_control: Control = get_label_control()
|
var label_control: Control = get_label_control()
|
||||||
var font_size := clampi(
|
var font_size := clampi(
|
||||||
roundi(minf(bubble_size.x, bubble_size.y) * font_size_ratio),
|
roundi(minf(bubble_size.x, bubble_size.y) * font_size_ratio),
|
||||||
|
|
@ -63,6 +67,9 @@ func get_layout_size(layout_scale: float, compact: bool) -> Vector2:
|
||||||
if compact:
|
if compact:
|
||||||
bubble_size.x = maxf(bubble_size.x, compact_minimum_size.x)
|
bubble_size.x = maxf(bubble_size.x, compact_minimum_size.x)
|
||||||
bubble_size.y = maxf(bubble_size.y, compact_minimum_size.y)
|
bubble_size.y = maxf(bubble_size.y, compact_minimum_size.y)
|
||||||
|
if _uses_icon_only_presentation():
|
||||||
|
var diameter: float = maxf(bubble_size.x, bubble_size.y)
|
||||||
|
bubble_size = Vector2(diameter, diameter)
|
||||||
return bubble_size
|
return bubble_size
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -78,6 +85,24 @@ func get_label_control() -> Control:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func _uses_icon_only_presentation() -> bool:
|
||||||
|
return icon != null and text.is_empty() and label_control_path.is_empty()
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_icon_presentation(bubble_size: Vector2) -> void:
|
||||||
|
if not _uses_icon_only_presentation():
|
||||||
|
return
|
||||||
|
alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
|
expand_icon = true
|
||||||
|
texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
add_theme_constant_override(
|
||||||
|
"icon_max_width",
|
||||||
|
maxi(1, roundi(minf(bubble_size.x, bubble_size.y) * ICON_FILL_RATIO)),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func advance_emphasis(delta: float) -> void:
|
func advance_emphasis(delta: float) -> void:
|
||||||
var target: float = 1.0 if _hovered or _focused else 0.0
|
var target: float = 1.0 if _hovered or _focused else 0.0
|
||||||
emphasis = move_toward(
|
emphasis = move_toward(
|
||||||
|
|
@ -122,6 +147,8 @@ func calculate_visual_scale(
|
||||||
* motion_scale
|
* motion_scale
|
||||||
* lerpf(1.0, profile.emphasized_deformation_scale, emphasis)
|
* lerpf(1.0, profile.emphasized_deformation_scale, emphasis)
|
||||||
)
|
)
|
||||||
|
if _uses_icon_only_presentation():
|
||||||
|
deformation_amount = 0.0
|
||||||
var hover_scale: float = lerpf(
|
var hover_scale: float = lerpf(
|
||||||
1.0,
|
1.0,
|
||||||
profile.hover_focus_scale,
|
profile.hover_focus_scale,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/reel_speed_upgrade.svg" id="3_reel"]
|
[ext_resource type="Texture2D" path="res://items/icons/placeholder/reel_speed_upgrade.svg" id="3_reel"]
|
||||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/barrier_power_upgrade.svg" id="4_barrier"]
|
[ext_resource type="Texture2D" path="res://items/icons/placeholder/barrier_power_upgrade.svg" id="4_barrier"]
|
||||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/fish_coin.svg" id="5_coin"]
|
[ext_resource type="Texture2D" path="res://items/icons/placeholder/fish_coin.svg" id="5_coin"]
|
||||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/cooler_expansion.svg" id="6_cooler"]
|
[ext_resource type="Texture2D" path="res://items/icons/equipment/64_cooler_plus.png" id="6_cooler"]
|
||||||
|
|
||||||
[node name="FishingShop" type="Control"]
|
[node name="FishingShop" type="Control"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
@ -196,7 +196,7 @@ layout_mode = 2
|
||||||
theme_override_constants/separation = 9
|
theme_override_constants/separation = 9
|
||||||
|
|
||||||
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
|
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
|
||||||
custom_minimum_size = Vector2(44, 44)
|
custom_minimum_size = Vector2(88, 88)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("3_reel")
|
texture = ExtResource("3_reel")
|
||||||
expand_mode = 1
|
expand_mode = 1
|
||||||
|
|
@ -250,7 +250,7 @@ layout_mode = 2
|
||||||
theme_override_constants/separation = 9
|
theme_override_constants/separation = 9
|
||||||
|
|
||||||
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
|
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
|
||||||
custom_minimum_size = Vector2(44, 44)
|
custom_minimum_size = Vector2(88, 88)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("4_barrier")
|
texture = ExtResource("4_barrier")
|
||||||
expand_mode = 1
|
expand_mode = 1
|
||||||
|
|
@ -304,7 +304,7 @@ layout_mode = 2
|
||||||
theme_override_constants/separation = 9
|
theme_override_constants/separation = 9
|
||||||
|
|
||||||
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
|
[node name="Icon" type="TextureRect" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
|
||||||
custom_minimum_size = Vector2(44, 44)
|
custom_minimum_size = Vector2(88, 88)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("6_cooler")
|
texture = ExtResource("6_cooler")
|
||||||
expand_mode = 1
|
expand_mode = 1
|
||||||
|
|
|
||||||
241
ui/game_ui.gd
|
|
@ -12,6 +12,7 @@ const PlayerMenuType = preload("res://ui/player_menu.gd")
|
||||||
const PlayerType = preload("res://player/player.gd")
|
const PlayerType = preload("res://player/player.gd")
|
||||||
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
|
const ItemDataType = preload("res://items/item_data.gd")
|
||||||
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||||
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||||
const HotbarUIType = preload("res://ui/hotbar.gd")
|
const HotbarUIType = preload("res://ui/hotbar.gd")
|
||||||
|
|
@ -57,6 +58,12 @@ const PlayerExperienceType = preload(
|
||||||
const UIReferencePresentationType = preload(
|
const UIReferencePresentationType = preload(
|
||||||
"res://ui/ui_reference_presentation.gd"
|
"res://ui/ui_reference_presentation.gd"
|
||||||
)
|
)
|
||||||
|
const MAIN_BUBBLE_PROFILE: BubbleMenuProfile = preload(
|
||||||
|
"res://ui/components/bubble_menu/bubble_menu_profile.tres"
|
||||||
|
)
|
||||||
|
const ACTIVE_BAIT_EMPTY_ICON: Texture2D = preload(
|
||||||
|
"res://ui/icons/pictograms/x_dark.png"
|
||||||
|
)
|
||||||
|
|
||||||
signal pixelation_settings_visibility_changed(is_visible: bool)
|
signal pixelation_settings_visibility_changed(is_visible: bool)
|
||||||
signal crisp_reset_focus_requested
|
signal crisp_reset_focus_requested
|
||||||
|
|
@ -85,6 +92,9 @@ const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0
|
||||||
|
|
||||||
@onready var _status_label: Label = %StatusLabel
|
@onready var _status_label: Label = %StatusLabel
|
||||||
@onready var _gameplay_transient_hud: Control = %GameplayTransientHUD
|
@onready var _gameplay_transient_hud: Control = %GameplayTransientHUD
|
||||||
|
@onready var _active_bait_button: Button = %ActiveBaitButton
|
||||||
|
@onready var _active_bait_quantity_badge: Panel = %ActiveBaitQuantityBadge
|
||||||
|
@onready var _active_bait_quantity: Label = %ActiveBaitQuantity
|
||||||
@onready var _experience_presentation: Control = %ExperiencePresentation
|
@onready var _experience_presentation: Control = %ExperiencePresentation
|
||||||
@onready var _catch_track: Control = %CatchTrack
|
@onready var _catch_track: Control = %CatchTrack
|
||||||
@onready var _green_catch_progress: ProgressBar = %GreenCatchProgress
|
@onready var _green_catch_progress: ProgressBar = %GreenCatchProgress
|
||||||
|
|
@ -101,6 +111,13 @@ const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0
|
||||||
@onready var _experience_progress: ProgressBar = %ExperienceProgress
|
@onready var _experience_progress: ProgressBar = %ExperienceProgress
|
||||||
@onready var _experience_bubble: PanelContainer = %ExperienceBubble
|
@onready var _experience_bubble: PanelContainer = %ExperienceBubble
|
||||||
@onready var _experience_bubble_label: Label = %ExperienceBubbleLabel
|
@onready var _experience_bubble_label: Label = %ExperienceBubbleLabel
|
||||||
|
const TypewriterRevealType = preload("res://ui/typewriter_reveal.gd")
|
||||||
|
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
|
||||||
|
const SHOP_ANIMALESE_VOICE_ID: String = "natural"
|
||||||
|
const SHOP_ANIMALESE_BASE_PITCH: float = 1.08
|
||||||
|
const SHOP_SPEECH_CHARACTERS_PER_SECOND: float = 28.0
|
||||||
|
|
||||||
|
|
||||||
@onready var _canonical_stage: Control = %CanonicalStage
|
@onready var _canonical_stage: Control = %CanonicalStage
|
||||||
@onready var _ui_root: Control = %UIRoot
|
@onready var _ui_root: Control = %UIRoot
|
||||||
@onready var _player_menu: PlayerMenuType = %PlayerMenu
|
@onready var _player_menu: PlayerMenuType = %PlayerMenu
|
||||||
|
|
@ -109,7 +126,13 @@ const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0
|
||||||
@onready var _pause_menu: PauseMenuType = %PauseMenu
|
@onready var _pause_menu: PauseMenuType = %PauseMenu
|
||||||
@onready var _hotbar_ui: HotbarUIType = %Hotbar
|
@onready var _hotbar_ui: HotbarUIType = %Hotbar
|
||||||
@onready var _fishing_shop: FishingShopType = %FishingShop
|
@onready var _fishing_shop: FishingShopType = %FishingShop
|
||||||
@onready var _shop_prompt: PanelContainer = %ShopPrompt
|
@onready var _shop_prompt: Control = %ShopPrompt
|
||||||
|
@onready var _shop_prompt_bubble: PanelContainer = %ShopPromptBubble
|
||||||
|
@onready var _shop_prompt_message: Label = %ShopPromptMessage
|
||||||
|
@onready var _shop_prompt_key_badge: Panel = %ShopPromptKeyBadge
|
||||||
|
@onready var _shop_prompt_key: Label = %ShopPromptKey
|
||||||
|
@onready var _shop_prompt_pointer: Polygon2D = %ShopPromptPointer
|
||||||
|
var _shop_animalese_voice: AnimaleseVoiceType
|
||||||
@onready var _effect_status: Label = %EffectStatus
|
@onready var _effect_status: Label = %EffectStatus
|
||||||
@onready var _chat_ui: ChatUIType = %ChatUI
|
@onready var _chat_ui: ChatUIType = %ChatUI
|
||||||
@onready var _emote_radial_menu: EmoteRadialMenuType = %EmoteRadialMenu
|
@onready var _emote_radial_menu: EmoteRadialMenuType = %EmoteRadialMenu
|
||||||
|
|
@ -129,6 +152,8 @@ const FISHING_PANEL_SHOWCASE_BOTTOM_OFFSET: float = -104.0
|
||||||
|
|
||||||
var _showcase_active: bool = false
|
var _showcase_active: bool = false
|
||||||
var _player: PlayerType
|
var _player: PlayerType
|
||||||
|
var _bag: PlayerBagType
|
||||||
|
var _item_catalog: ItemCatalogType
|
||||||
var _player_menu_open: bool = false
|
var _player_menu_open: bool = false
|
||||||
var _gameplay_ui_enabled: bool = false
|
var _gameplay_ui_enabled: bool = false
|
||||||
var _fishing_spot: FishingSpotType
|
var _fishing_spot: FishingSpotType
|
||||||
|
|
@ -163,6 +188,8 @@ var _settings_manager: PlayerSettingsManagerType
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
_apply_active_bait_indicator_style()
|
||||||
|
_refresh_active_bait_indicator()
|
||||||
# Reward feedback must remain above full-screen canonical menus. Keeping the
|
# Reward feedback must remain above full-screen canonical menus. Keeping the
|
||||||
# overlay as the final stage child makes that ownership explicit instead of
|
# overlay as the final stage child makes that ownership explicit instead of
|
||||||
# relying on scene declaration order when another menu adds high-z children.
|
# relying on scene declaration order when another menu adds high-z children.
|
||||||
|
|
@ -236,10 +263,27 @@ func setup(
|
||||||
world_sun: DirectionalLight3D,
|
world_sun: DirectionalLight3D,
|
||||||
) -> void:
|
) -> void:
|
||||||
_player = player
|
_player = player
|
||||||
|
_bag = bag
|
||||||
|
_item_catalog = item_catalog
|
||||||
_settings_manager = settings_manager
|
_settings_manager = settings_manager
|
||||||
_fishing_spot = fishing_spot
|
_fishing_spot = fishing_spot
|
||||||
_item_effects = item_effects
|
_item_effects = item_effects
|
||||||
_experience = experience
|
_experience = experience
|
||||||
|
if (
|
||||||
|
_player != null
|
||||||
|
and not _player.active_bait_changed.is_connected(
|
||||||
|
_on_hud_active_bait_changed
|
||||||
|
)
|
||||||
|
):
|
||||||
|
_player.active_bait_changed.connect(_on_hud_active_bait_changed)
|
||||||
|
if (
|
||||||
|
_bag != null
|
||||||
|
and not _bag.contents_changed.is_connected(
|
||||||
|
_on_hud_bait_inventory_changed
|
||||||
|
)
|
||||||
|
):
|
||||||
|
_bag.contents_changed.connect(_on_hud_bait_inventory_changed)
|
||||||
|
_refresh_active_bait_indicator()
|
||||||
if (
|
if (
|
||||||
_experience != null
|
_experience != null
|
||||||
and not _experience.experience_awarded.is_connected(
|
and not _experience.experience_awarded.is_connected(
|
||||||
|
|
@ -1174,6 +1218,88 @@ func get_pause_menu() -> PauseMenuType:
|
||||||
return _pause_menu
|
return _pause_menu
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_active_bait_indicator_style() -> void:
|
||||||
|
var normal_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_normal_style()
|
||||||
|
var hover_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_hover_style()
|
||||||
|
var pressed_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_pressed_style()
|
||||||
|
var disabled_style: StyleBoxFlat = MAIN_BUBBLE_PROFILE.make_disabled_style()
|
||||||
|
for style: StyleBoxFlat in [
|
||||||
|
normal_style,
|
||||||
|
hover_style,
|
||||||
|
pressed_style,
|
||||||
|
disabled_style,
|
||||||
|
]:
|
||||||
|
style.set_corner_radius_all(36)
|
||||||
|
style.content_margin_left = 13.5
|
||||||
|
style.content_margin_top = 13.5
|
||||||
|
style.content_margin_right = 13.5
|
||||||
|
style.content_margin_bottom = 13.5
|
||||||
|
_active_bait_button.add_theme_stylebox_override("normal", normal_style)
|
||||||
|
_active_bait_button.add_theme_stylebox_override("hover", hover_style)
|
||||||
|
_active_bait_button.add_theme_stylebox_override("focus", hover_style)
|
||||||
|
_active_bait_button.add_theme_stylebox_override("pressed", pressed_style)
|
||||||
|
_active_bait_button.add_theme_stylebox_override("disabled", disabled_style)
|
||||||
|
for state: StringName in [
|
||||||
|
&"icon_normal_color",
|
||||||
|
&"icon_hover_color",
|
||||||
|
&"icon_focus_color",
|
||||||
|
&"icon_pressed_color",
|
||||||
|
]:
|
||||||
|
_active_bait_button.add_theme_color_override(state, Color.WHITE)
|
||||||
|
var badge_style := StyleBoxFlat.new()
|
||||||
|
badge_style.bg_color = Color("0b5558")
|
||||||
|
badge_style.set_corner_radius_all(12)
|
||||||
|
badge_style.anti_aliasing = false
|
||||||
|
_active_bait_quantity_badge.add_theme_stylebox_override(
|
||||||
|
"panel",
|
||||||
|
badge_style,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _refresh_active_bait_indicator() -> void:
|
||||||
|
if not is_node_ready():
|
||||||
|
return
|
||||||
|
var item: ItemDataType
|
||||||
|
if (
|
||||||
|
_player != null
|
||||||
|
and _item_catalog != null
|
||||||
|
and not _player.active_bait_id.is_empty()
|
||||||
|
):
|
||||||
|
item = _item_catalog.get_item_by_id(_player.active_bait_id)
|
||||||
|
var has_active_bait: bool = item != null and item.is_bait()
|
||||||
|
_active_bait_button.icon = (
|
||||||
|
item.icon
|
||||||
|
if has_active_bait and item.icon != null
|
||||||
|
else ACTIVE_BAIT_EMPTY_ICON
|
||||||
|
)
|
||||||
|
_active_bait_quantity_badge.visible = has_active_bait
|
||||||
|
if not has_active_bait:
|
||||||
|
_active_bait_button.tooltip_text = "no bait selected"
|
||||||
|
return
|
||||||
|
var quantity: int = (
|
||||||
|
_bag.get_quantity(item.item_id)
|
||||||
|
if _bag != null
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
_active_bait_quantity.text = str(quantity)
|
||||||
|
_active_bait_quantity.add_theme_font_size_override(
|
||||||
|
"font_size",
|
||||||
|
13 if quantity >= 100 else 15,
|
||||||
|
)
|
||||||
|
_active_bait_button.tooltip_text = "%s ×%d" % [
|
||||||
|
item.display_name,
|
||||||
|
quantity,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
func _on_hud_active_bait_changed(_item_id: StringName) -> void:
|
||||||
|
_refresh_active_bait_indicator()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_hud_bait_inventory_changed() -> void:
|
||||||
|
_refresh_active_bait_indicator()
|
||||||
|
|
||||||
|
|
||||||
func set_gameplay_ui_enabled(enabled: bool) -> void:
|
func set_gameplay_ui_enabled(enabled: bool) -> void:
|
||||||
_gameplay_ui_enabled = enabled
|
_gameplay_ui_enabled = enabled
|
||||||
_gameplay_transient_hud.visible = enabled and not _player_menu_open
|
_gameplay_transient_hud.visible = enabled and not _player_menu_open
|
||||||
|
|
@ -1231,7 +1357,12 @@ func get_fishing_shop() -> FishingShopType:
|
||||||
return _fishing_shop
|
return _fishing_shop
|
||||||
|
|
||||||
|
|
||||||
func set_shop_prompt_visible(is_visible: bool) -> void:
|
func set_shop_prompt_visible(
|
||||||
|
is_visible: bool,
|
||||||
|
world_anchor: Vector3 = Vector3(0.0, INF, 0.0),
|
||||||
|
) -> void:
|
||||||
|
_apply_shop_prompt_style()
|
||||||
|
var was_visible := _shop_prompt.visible
|
||||||
_shop_prompt.visible = (
|
_shop_prompt.visible = (
|
||||||
is_visible
|
is_visible
|
||||||
and _gameplay_ui_enabled
|
and _gameplay_ui_enabled
|
||||||
|
|
@ -1239,6 +1370,112 @@ func set_shop_prompt_visible(is_visible: bool) -> void:
|
||||||
and not _player_menu_open
|
and not _player_menu_open
|
||||||
and not _shop_open
|
and not _shop_open
|
||||||
)
|
)
|
||||||
|
if _shop_prompt.visible:
|
||||||
|
if not was_visible:
|
||||||
|
_ensure_shop_animalese_voice()
|
||||||
|
TypewriterRevealType.start(
|
||||||
|
_shop_prompt_message,
|
||||||
|
SHOP_SPEECH_CHARACTERS_PER_SECOND,
|
||||||
|
)
|
||||||
|
_shop_animalese_voice.speak_text(
|
||||||
|
_shop_prompt_message,
|
||||||
|
_shop_prompt_message.text,
|
||||||
|
"shopkeeper",
|
||||||
|
SHOP_ANIMALESE_VOICE_ID,
|
||||||
|
SHOP_SPEECH_CHARACTERS_PER_SECOND,
|
||||||
|
)
|
||||||
|
if world_anchor.is_finite():
|
||||||
|
_position_shop_prompt(world_anchor)
|
||||||
|
|
||||||
|
|
||||||
|
func _ensure_shop_animalese_voice() -> void:
|
||||||
|
if _shop_animalese_voice != null:
|
||||||
|
return
|
||||||
|
_shop_animalese_voice = AnimaleseVoiceType.new()
|
||||||
|
_shop_animalese_voice.name = "ShopAnimaleseVoice"
|
||||||
|
_shop_animalese_voice.base_pitch = SHOP_ANIMALESE_BASE_PITCH
|
||||||
|
add_child(_shop_animalese_voice)
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_shop_prompt_style() -> void:
|
||||||
|
if _shop_prompt.has_meta(&"shop_prompt_styled"):
|
||||||
|
return
|
||||||
|
_shop_prompt.set_meta(&"shop_prompt_styled", true)
|
||||||
|
var bubble_style := StyleBoxFlat.new()
|
||||||
|
bubble_style.bg_color = Color(UtilityPageStyle.OCEAN_PANEL_MID, 0.96)
|
||||||
|
bubble_style.set_border_width_all(0)
|
||||||
|
bubble_style.set_corner_radius_all(12)
|
||||||
|
bubble_style.anti_aliasing = false
|
||||||
|
_shop_prompt_bubble.add_theme_stylebox_override("panel", bubble_style)
|
||||||
|
var badge_style := StyleBoxFlat.new()
|
||||||
|
badge_style.bg_color = Color("0b5558")
|
||||||
|
badge_style.set_border_width_all(0)
|
||||||
|
badge_style.set_corner_radius_all(12)
|
||||||
|
badge_style.anti_aliasing = false
|
||||||
|
_shop_prompt_key_badge.add_theme_stylebox_override("panel", badge_style)
|
||||||
|
for label: Label in [_shop_prompt_message, _shop_prompt_key]:
|
||||||
|
label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
||||||
|
label.add_theme_color_override(
|
||||||
|
"font_color",
|
||||||
|
UtilityPageStyle.OCEAN_TEXT_PRIMARY,
|
||||||
|
)
|
||||||
|
_shop_prompt_pointer.color = Color(
|
||||||
|
UtilityPageStyle.OCEAN_PANEL_MID,
|
||||||
|
0.96,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _position_shop_prompt(world_anchor: Vector3) -> void:
|
||||||
|
if _player == null:
|
||||||
|
_shop_prompt.hide()
|
||||||
|
return
|
||||||
|
var camera: Camera3D = _player.get_gameplay_camera()
|
||||||
|
if camera == null or camera.is_position_behind(world_anchor):
|
||||||
|
_shop_prompt.hide()
|
||||||
|
return
|
||||||
|
var camera_viewport_size := camera.get_viewport().get_visible_rect().size
|
||||||
|
var ui_viewport_size: Vector2 = _canonical_stage.size
|
||||||
|
if camera_viewport_size.x <= 0.0 or camera_viewport_size.y <= 0.0:
|
||||||
|
_shop_prompt.hide()
|
||||||
|
return
|
||||||
|
var screen_position := (
|
||||||
|
camera.unproject_position(world_anchor)
|
||||||
|
* ui_viewport_size
|
||||||
|
/ camera_viewport_size
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
screen_position.x < -80.0
|
||||||
|
or screen_position.x > ui_viewport_size.x + 80.0
|
||||||
|
or screen_position.y < -80.0
|
||||||
|
or screen_position.y > ui_viewport_size.y + 80.0
|
||||||
|
):
|
||||||
|
_shop_prompt.hide()
|
||||||
|
return
|
||||||
|
var pointer_height := 10.0
|
||||||
|
var desired := screen_position - Vector2(
|
||||||
|
_shop_prompt.size.x * 0.5,
|
||||||
|
_shop_prompt.size.y + pointer_height,
|
||||||
|
)
|
||||||
|
_shop_prompt.position = Vector2(
|
||||||
|
clampf(
|
||||||
|
desired.x,
|
||||||
|
8.0,
|
||||||
|
ui_viewport_size.x - _shop_prompt.size.x - 8.0,
|
||||||
|
),
|
||||||
|
clampf(
|
||||||
|
desired.y,
|
||||||
|
8.0,
|
||||||
|
ui_viewport_size.y - _shop_prompt.size.y - pointer_height - 8.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_shop_prompt_pointer.position = Vector2(
|
||||||
|
clampf(
|
||||||
|
screen_position.x - _shop_prompt.position.x,
|
||||||
|
20.0,
|
||||||
|
_shop_prompt.size.x - 20.0,
|
||||||
|
),
|
||||||
|
_shop_prompt.size.y - 1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func get_screen_fade() -> ScreenFade:
|
func get_screen_fade() -> ScreenFade:
|
||||||
|
|
|
||||||
131
ui/game_ui.tscn
|
|
@ -82,6 +82,71 @@ grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="ActiveBaitIndicator" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
z_index = 54
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 3
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -88.0
|
||||||
|
offset_top = -88.0
|
||||||
|
offset_right = -16.0
|
||||||
|
offset_bottom = -16.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 0
|
||||||
|
mouse_filter = 2
|
||||||
|
theme = ExtResource("3_theme")
|
||||||
|
|
||||||
|
[node name="ActiveBaitButton" type="Button" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ActiveBaitIndicator"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
focus_mode = 0
|
||||||
|
texture_filter = 1
|
||||||
|
expand_icon = true
|
||||||
|
alignment = 1
|
||||||
|
icon_alignment = 1
|
||||||
|
vertical_icon_alignment = 1
|
||||||
|
|
||||||
|
[node name="ActiveBaitQuantityBadge" type="Panel" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ActiveBaitIndicator"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
z_index = 2
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -27.0
|
||||||
|
offset_top = 3.0
|
||||||
|
offset_right = -3.0
|
||||||
|
offset_bottom = 27.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="ActiveBaitQuantity" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ActiveBaitIndicator/ActiveBaitQuantityBadge"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = 1.0
|
||||||
|
offset_right = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_colors/font_color = Color(0.905882, 0.960784, 0.956863, 1)
|
||||||
|
theme_override_font_sizes/font_size = 15
|
||||||
|
text = "0"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="ExperiencePresentation" type="Control" parent="UIRoot/CanonicalStage"]
|
[node name="ExperiencePresentation" type="Control" parent="UIRoot/CanonicalStage"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
z_index = 200
|
z_index = 200
|
||||||
|
|
@ -333,35 +398,67 @@ unique_name_in_owner = true
|
||||||
[node name="QuickRadialMenu" parent="UIRoot/CanonicalStage" instance=ExtResource("12_quick")]
|
[node name="QuickRadialMenu" parent="UIRoot/CanonicalStage" instance=ExtResource("12_quick")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[node name="ShopPrompt" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
[node name="ShopPrompt" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
z_index = 55
|
z_index = 55
|
||||||
anchors_preset = 7
|
offset_right = 300.0
|
||||||
anchor_left = 0.5
|
offset_bottom = 72.0
|
||||||
anchor_top = 1.0
|
|
||||||
anchor_right = 0.5
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
offset_left = -150.0
|
|
||||||
offset_top = -284.0
|
|
||||||
offset_right = 150.0
|
|
||||||
offset_bottom = -246.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 0
|
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
theme = ExtResource("3_theme")
|
theme = ExtResource("3_theme")
|
||||||
|
|
||||||
[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"]
|
[node name="ShopPromptBubble" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/ShopPromptBubble"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 12
|
theme_override_constants/margin_left = 12
|
||||||
theme_override_constants/margin_top = 7
|
theme_override_constants/margin_top = 8
|
||||||
theme_override_constants/margin_right = 12
|
theme_override_constants/margin_right = 12
|
||||||
theme_override_constants/margin_bottom = 7
|
theme_override_constants/margin_bottom = 8
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/Margin"]
|
[node name="ShopPromptMessage" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/ShopPromptBubble/Margin"]
|
||||||
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "press e to open fishing shop"
|
theme_override_font_sizes/font_size = 18
|
||||||
|
text = "Hey, if you've got any fish I've got cash! OwO"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
autowrap_mode = 2
|
||||||
|
|
||||||
|
[node name="ShopPromptKeyBadge" type="Panel" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 282.0
|
||||||
|
offset_top = -8.0
|
||||||
|
offset_right = 306.0
|
||||||
|
offset_bottom = 16.0
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="ShopPromptKey" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt/ShopPromptKeyBadge"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_font_sizes/font_size = 15
|
||||||
|
text = "E"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="ShopPromptPointer" type="Polygon2D" parent="UIRoot/CanonicalStage/GameplayTransientHUD/ShopPrompt"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
polygon = PackedVector2Array(-8, 0, 8, 0, 0, 10)
|
||||||
|
|
||||||
[node name="FishingShop" parent="UIRoot/CanonicalStage" instance=ExtResource("8_shop")]
|
[node name="FishingShop" parent="UIRoot/CanonicalStage" instance=ExtResource("8_shop")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
|
||||||
BIN
ui/icons/pictograms/arrow_dark_down_full.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_dark_down_full.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://d4aah3j5brkij"
|
||||||
|
path="res://.godot/imported/arrow_dark_down_full.png-0e7bb59c41a81c0bd02261d6a7b319ef.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/arrow_dark_down_full.png"
|
||||||
|
dest_files=["res://.godot/imported/arrow_dark_down_full.png-0e7bb59c41a81c0bd02261d6a7b319ef.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/arrow_dark_up_full.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_dark_up_full.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://d34x31jx5p5ll"
|
||||||
|
path="res://.godot/imported/arrow_dark_up_full.png-907f3a8653cf6bf8b6ba95ad31b18e0d.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/arrow_dark_up_full.png"
|
||||||
|
dest_files=["res://.godot/imported/arrow_dark_up_full.png-907f3a8653cf6bf8b6ba95ad31b18e0d.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/arrow_light_down_more.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_light_down_more.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cmc7sguttrl2o"
|
||||||
|
path="res://.godot/imported/arrow_light_down_more.png-a02fb5d09f2022507ddb6c06e3f22732.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/arrow_light_down_more.png"
|
||||||
|
dest_files=["res://.godot/imported/arrow_light_down_more.png-a02fb5d09f2022507ddb6c06e3f22732.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/arrow_light_left_more.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
40
ui/icons/pictograms/arrow_light_left_more.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b3lx64prsccyk"
|
||||||
|
path="res://.godot/imported/arrow_light_left_more.png-a92f7c96dbcafd9c1b2cb612e94fef44.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/arrow_light_left_more.png"
|
||||||
|
dest_files=["res://.godot/imported/arrow_light_left_more.png-a92f7c96dbcafd9c1b2cb612e94fef44.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/arrow_light_right_more.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_light_right_more.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cedstrt57et53"
|
||||||
|
path="res://.godot/imported/arrow_light_right_more.png-56d3df49848f9fee268e5c4b6c279828.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/arrow_light_right_more.png"
|
||||||
|
dest_files=["res://.godot/imported/arrow_light_right_more.png-56d3df49848f9fee268e5c4b6c279828.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/arrow_light_up_more.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/arrow_light_up_more.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dqu71oklbvofk"
|
||||||
|
path="res://.godot/imported/arrow_light_up_more.png-7e8472cc9f410835ab24173229e09c9b.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/arrow_light_up_more.png"
|
||||||
|
dest_files=["res://.godot/imported/arrow_light_up_more.png-7e8472cc9f410835ab24173229e09c9b.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/check_mark_dark.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
40
ui/icons/pictograms/check_mark_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://3x5tryb721th"
|
||||||
|
path="res://.godot/imported/check_mark_dark.png-d9abda15f33a6a8f8c29c5f148cda086.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/check_mark_dark.png"
|
||||||
|
dest_files=["res://.godot/imported/check_mark_dark.png-d9abda15f33a6a8f8c29c5f148cda086.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/check_mark_light.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
40
ui/icons/pictograms/check_mark_light.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dtibvrik7ug0j"
|
||||||
|
path="res://.godot/imported/check_mark_light.png-83417416d6580b7b5eb41987b7af5ccf.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/check_mark_light.png"
|
||||||
|
dest_files=["res://.godot/imported/check_mark_light.png-83417416d6580b7b5eb41987b7af5ccf.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/online_dark.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
40
ui/icons/pictograms/online_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://uxkgiwd7py03"
|
||||||
|
path="res://.godot/imported/online_dark.png-081123db8a385b31ed626b7c95b20c3c.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/online_dark.png"
|
||||||
|
dest_files=["res://.godot/imported/online_dark.png-081123db8a385b31ed626b7c95b20c3c.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/save_dark.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
40
ui/icons/pictograms/save_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b71gds1q8ny61"
|
||||||
|
path="res://.godot/imported/save_dark.png-6dbadbfe5b8057ede23f77629e3c1226.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/save_dark.png"
|
||||||
|
dest_files=["res://.godot/imported/save_dark.png-6dbadbfe5b8057ede23f77629e3c1226.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/settings_dark.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
40
ui/icons/pictograms/settings_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ul28xl5qed55"
|
||||||
|
path="res://.godot/imported/settings_dark.png-7196550e6c3f5a58c1402ab0ccc9c9d0.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/settings_dark.png"
|
||||||
|
dest_files=["res://.godot/imported/settings_dark.png-7196550e6c3f5a58c1402ab0ccc9c9d0.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/undo_dark.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
40
ui/icons/pictograms/undo_dark.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://deux1h722j33u"
|
||||||
|
path="res://.godot/imported/undo_dark.png-652c08d6b38e19b70b66927ecfc6222c.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/icons/pictograms/undo_dark.png"
|
||||||
|
dest_files=["res://.godot/imported/undo_dark.png-652c08d6b38e19b70b66927ecfc6222c.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
ui/icons/pictograms/undo_light.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |