summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/audio_stream_player_2d.cpp162
-rw-r--r--scene/2d/audio_stream_player_2d.h10
-rw-r--r--scene/2d/tile_map.cpp58
-rw-r--r--scene/2d/tile_map.h4
-rw-r--r--scene/3d/audio_stream_player_3d.cpp165
-rw-r--r--scene/3d/audio_stream_player_3d.h15
-rw-r--r--scene/audio/audio_stream_player.cpp137
-rw-r--r--scene/audio/audio_stream_player.h8
-rw-r--r--scene/gui/code_edit.cpp26
-rw-r--r--scene/gui/link_button.cpp16
-rw-r--r--scene/gui/link_button.h1
-rw-r--r--scene/gui/rich_text_label.cpp24
-rw-r--r--scene/gui/rich_text_label.h6
-rw-r--r--scene/gui/text_edit.cpp158
-rw-r--r--scene/gui/text_edit.h21
-rw-r--r--scene/main/node.cpp67
-rw-r--r--scene/main/node.h14
-rw-r--r--scene/main/scene_tree.h2
-rw-r--r--scene/resources/audio_stream_sample.cpp4
-rw-r--r--scene/resources/audio_stream_sample.h2
-rw-r--r--scene/resources/tile_set.cpp689
-rw-r--r--scene/resources/tile_set.h88
22 files changed, 1218 insertions, 459 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index ea491e8b0e..9280e35c3f 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -59,33 +59,47 @@ void AudioStreamPlayer2D::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
//update anything related to position first, if possible of course
+ if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
+ _update_panning();
+ }
- if (!stream_playback.is_valid()) {
- return;
+ if (setplay.get() >= 0 && stream.is_valid()) {
+ active.set();
+ Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
+ ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
+ AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get());
+ stream_playbacks.push_back(new_playback);
+ setplay.set(-1);
}
- if (setplay.get() >= 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
- _update_panning();
- if (setplay.get() >= 0) {
- active.set();
- AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get());
- setplay.set(-1);
+
+ if (!stream_playbacks.is_empty() && active.is_set()) {
+ // Stop playing if no longer active.
+ Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
+ emit_signal(SNAME("finished"));
+ playbacks_to_remove.push_back(playback);
+ }
+ }
+ // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
+ for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
+ stream_playbacks.erase(playback);
+ }
+ if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
+ // This node is no longer actively playing audio.
+ active.clear();
+ set_physics_process_internal(false);
}
}
- // Stop playing if no longer active.
- if (active.is_set() && !AudioServer::get_singleton()->is_playback_active(stream_playback)) {
- active.clear();
- set_physics_process_internal(false);
- emit_signal(SNAME("finished"));
+ while (stream_playbacks.size() > max_polyphony) {
+ AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
+ stream_playbacks.remove(0);
}
}
}
StringName AudioStreamPlayer2D::_get_actual_bus() {
- if (!stream_playback.is_valid()) {
- return SNAME("Master");
- }
-
Vector2 global_pos = get_global_position();
//check if any area is diverting sound into a bus
@@ -113,12 +127,10 @@ StringName AudioStreamPlayer2D::_get_actual_bus() {
}
void AudioStreamPlayer2D::_update_panning() {
- if (!stream_playback.is_valid()) {
+ if (!active.is_set() || stream.is_null()) {
return;
}
- last_mix_count = AudioServer::get_singleton()->get_mix_count();
-
Ref<World2D> world_2d = get_world_2d();
ERR_FAIL_COND(world_2d.is_null());
@@ -164,28 +176,20 @@ void AudioStreamPlayer2D::_update_panning() {
volume_vector.write[0] = AudioFrame(l, r) * multiplier;
}
- AudioServer::get_singleton()->set_playback_bus_exclusive(stream_playback, _get_actual_bus(), volume_vector);
-}
-
-void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
- if (stream_playback.is_valid()) {
- stop();
+ for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_bus_exclusive(playback, _get_actual_bus(), volume_vector);
}
- stream_playback.unref();
- stream.unref();
- if (p_stream.is_valid()) {
- stream_playback = p_stream->instance_playback();
- if (stream_playback.is_valid()) {
- stream = p_stream;
- } else {
- stream.unref();
- }
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
}
- if (p_stream.is_valid() && stream_playback.is_null()) {
- stream.unref();
- }
+ last_mix_count = AudioServer::get_singleton()->get_mix_count();
+}
+
+void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
+ stop();
+ stream = p_stream;
}
Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
@@ -203,8 +207,8 @@ float AudioStreamPlayer2D::get_volume_db() const {
void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) {
ERR_FAIL_COND(p_pitch_scale <= 0.0);
pitch_scale = p_pitch_scale;
- if (stream_playback.is_valid()) {
- AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, p_pitch_scale);
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_pitch_scale(playback, p_pitch_scale);
}
}
@@ -213,44 +217,50 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
}
void AudioStreamPlayer2D::play(float p_from_pos) {
- stop();
- if (stream.is_valid()) {
- stream_playback = stream->instance_playback();
+ if (stream.is_null()) {
+ return;
}
- if (stream_playback.is_valid()) {
- setplay.set(p_from_pos);
- set_physics_process_internal(true);
+ ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
+ if (stream->is_monophonic() && is_playing()) {
+ stop();
}
+
+ setplay.set(p_from_pos);
+ active.set();
+ set_physics_process_internal(true);
}
void AudioStreamPlayer2D::seek(float p_seconds) {
- if (stream_playback.is_valid() && active.is_set()) {
+ if (is_playing()) {
+ stop();
play(p_seconds);
}
}
void AudioStreamPlayer2D::stop() {
- if (stream_playback.is_valid()) {
- active.clear();
- AudioServer::get_singleton()->stop_playback_stream(stream_playback);
- set_physics_process_internal(false);
- setplay.set(-1);
+ setplay.set(-1);
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->stop_playback_stream(playback);
}
+ stream_playbacks.clear();
+ active.clear();
+ set_physics_process_internal(false);
}
bool AudioStreamPlayer2D::is_playing() const {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->is_playback_active(stream_playback);
+ for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (AudioServer::get_singleton()->is_playback_active(playback)) {
+ return true;
+ }
}
-
return false;
}
float AudioStreamPlayer2D::get_playback_position() {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->get_playback_position(stream_playback);
+ // Return the playback position of the most recently started playback stream.
+ if (!stream_playbacks.is_empty()) {
+ return AudioServer::get_singleton()->get_playback_position(stream_playbacks[stream_playbacks.size() - 1]);
}
-
return 0;
}
@@ -284,11 +294,7 @@ void AudioStreamPlayer2D::_set_playing(bool p_enable) {
}
bool AudioStreamPlayer2D::_is_active() const {
- if (stream_playback.is_valid()) {
- // TODO make sure this doesn't change any behavior w.r.t. pauses. Is a paused stream active?
- return AudioServer::get_singleton()->is_playback_active(stream_playback);
- }
- return false;
+ return active.is_set();
}
void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const {
@@ -336,21 +342,35 @@ uint32_t AudioStreamPlayer2D::get_area_mask() const {
}
void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
- // TODO this does not have perfect recall, fix that maybe? If the stream isn't set, we can't persist this bool.
- if (stream_playback.is_valid()) {
- AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
+ // TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted.
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
}
}
bool AudioStreamPlayer2D::get_stream_paused() const {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+ // There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
+ if (!stream_playbacks.is_empty()) {
+ return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
}
return false;
}
Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
- return stream_playback;
+ if (!stream_playbacks.is_empty()) {
+ return stream_playbacks[stream_playbacks.size() - 1];
+ }
+ return nullptr;
+}
+
+void AudioStreamPlayer2D::set_max_polyphony(int p_max_polyphony) {
+ if (p_max_polyphony > 0) {
+ max_polyphony = p_max_polyphony;
+ }
+}
+
+int AudioStreamPlayer2D::get_max_polyphony() const {
+ return max_polyphony;
}
void AudioStreamPlayer2D::_bind_methods() {
@@ -391,6 +411,9 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused);
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused);
+ ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer2D::set_max_polyphony);
+ ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer2D::get_max_polyphony);
+
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -401,6 +424,7 @@ void AudioStreamPlayer2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "1,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 6428fbe017..5360fd4934 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -51,10 +51,10 @@ private:
Viewport *viewport = nullptr; //pointer only used for reference to previous mix
};
- Ref<AudioStreamPlayback> stream_playback;
+ Vector<Ref<AudioStreamPlayback>> stream_playbacks;
Ref<AudioStream> stream;
- SafeFlag active;
+ SafeFlag active{ false };
SafeNumeric<float> setplay{ -1.0 };
Vector<AudioFrame> volume_vector;
@@ -64,7 +64,8 @@ private:
float volume_db = 0.0;
float pitch_scale = 1.0;
bool autoplay = false;
- StringName default_bus = "Master";
+ StringName default_bus = SNAME("Master");
+ int max_polyphony = 1;
void _set_playing(bool p_enable);
bool _is_active() const;
@@ -119,6 +120,9 @@ public:
void set_stream_paused(bool p_pause);
bool get_stream_paused() const;
+ void set_max_polyphony(int p_max_polyphony);
+ int get_max_polyphony() const;
+
Ref<AudioStreamPlayback> get_stream_playback();
AudioStreamPlayer2D();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 729cc95bd7..e1552b3b60 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -314,16 +314,38 @@ int TileMap::get_quadrant_size() const {
return quadrant_size;
}
-void TileMap::set_layers_count(int p_layers_count) {
- ERR_FAIL_COND(p_layers_count < 0);
- _clear_internals();
+int TileMap::get_layers_count() const {
+ return layers.size();
+}
- layers.resize(p_layers_count);
+void TileMap::add_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = layers.size();
+ }
+
+ ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
+
+ layers.insert(p_to_pos, TileMapLayer());
_recreate_internals();
notify_property_list_changed();
- if (selected_layer >= p_layers_count) {
- selected_layer = -1;
+ emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
+}
+
+void TileMap::move_layer(int p_layer, int p_to_pos) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
+
+ TileMapLayer tl = layers[p_layer];
+ layers.insert(p_to_pos, tl);
+ layers.remove(p_to_pos < p_layer ? p_layer + 1 : p_layer);
+ _recreate_internals();
+ notify_property_list_changed();
+
+ if (selected_layer == p_layer) {
+ selected_layer = p_to_pos < p_layer ? p_to_pos - 1 : p_to_pos;
}
emit_signal(SNAME("changed"));
@@ -331,8 +353,20 @@ void TileMap::set_layers_count(int p_layers_count) {
update_configuration_warnings();
}
-int TileMap::get_layers_count() const {
- return layers.size();
+void TileMap::remove_layer(int p_layer) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+
+ layers.remove(p_layer);
+ _recreate_internals();
+ notify_property_list_changed();
+
+ if (selected_layer >= p_layer) {
+ selected_layer -= 1;
+ }
+
+ emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
}
void TileMap::set_layer_name(int p_layer, String p_name) {
@@ -2896,8 +2930,10 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size);
ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size);
- ClassDB::bind_method(D_METHOD("set_layers_count", "layers_count"), &TileMap::set_layers_count);
ClassDB::bind_method(D_METHOD("get_layers_count"), &TileMap::get_layers_count);
+ ClassDB::bind_method(D_METHOD("add_layer"), &TileMap::add_layer);
+ ClassDB::bind_method(D_METHOD("move_layer"), &TileMap::move_layer);
+ ClassDB::bind_method(D_METHOD("remove_layer"), &TileMap::remove_layer);
ClassDB::bind_method(D_METHOD("set_layer_name", "layer", "name"), &TileMap::set_layer_name);
ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name);
ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled);
@@ -2942,9 +2978,7 @@ void TileMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode");
- ADD_GROUP("Layers", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "layers_count"), "set_layers_count", "get_layers_count");
- ADD_PROPERTY_DEFAULT("layers_count", 1);
+ ADD_ARRAY("layers", "layer_");
ADD_PROPERTY_DEFAULT("format", FORMAT_1);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 4e2d76a7b7..3ac50fc7cc 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -308,8 +308,10 @@ public:
static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0));
// Layers management.
- void set_layers_count(int p_layers_count);
int get_layers_count() const;
+ void add_layer(int p_to_pos);
+ void move_layer(int p_layer, int p_to_pos);
+ void remove_layer(int p_layer);
void set_layer_name(int p_layer, String p_name);
String get_layer_name(int p_layer) const;
void set_layer_enabled(int p_layer, bool p_visible);
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 16d5b9da3e..907c6cd03a 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -271,28 +271,45 @@ void AudioStreamPlayer3D::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
//update anything related to position first, if possible of course
-
- if (!stream_playback.is_valid()) {
- return;
+ Vector<AudioFrame> volume_vector;
+ if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
+ volume_vector = _update_panning();
}
- //start playing if requested
- if (setplay.get() >= 0) {
- Vector<AudioFrame> volume_vector = _update_panning();
- AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get());
+ if (setplay.get() >= 0 && stream.is_valid()) {
active.set();
+ Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
+ ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
+ Map<StringName, Vector<AudioFrame>> bus_map;
+ bus_map[_get_actual_bus()] = volume_vector;
+ AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), linear_attenuation, attenuation_filter_cutoff_hz, actual_pitch_scale);
+ stream_playbacks.push_back(new_playback);
setplay.set(-1);
}
- if (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) {
- _update_panning();
- last_mix_count = AudioServer::get_singleton()->get_mix_count();
+ if (!stream_playbacks.is_empty() && active.is_set()) {
+ // Stop playing if no longer active.
+ Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
+ emit_signal(SNAME("finished"));
+ playbacks_to_remove.push_back(playback);
+ }
+ }
+ // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
+ for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
+ stream_playbacks.erase(playback);
+ }
+ if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
+ // This node is no longer actively playing audio.
+ active.clear();
+ set_physics_process_internal(false);
+ }
}
- // Stop playing if no longer active.
- if (!active.is_set()) {
- set_physics_process_internal(false);
- emit_signal(SNAME("finished"));
+ while (stream_playbacks.size() > max_polyphony) {
+ AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
+ stream_playbacks.remove(0);
}
}
}
@@ -330,9 +347,6 @@ Area3D *AudioStreamPlayer3D::_get_overriding_area() {
}
StringName AudioStreamPlayer3D::_get_actual_bus() {
- if (!stream_playback.is_valid()) {
- return SNAME("Master");
- }
Area3D *overriding_area = _get_overriding_area();
if (overriding_area && overriding_area->is_overriding_audio_bus() && !overriding_area->is_using_reverb_bus()) {
return overriding_area->get_audio_bus_name();
@@ -347,7 +361,9 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
frame = AudioFrame(0, 0);
}
- ERR_FAIL_COND_V(stream_playback.is_null(), output_volume_vector);
+ if (!active.is_set() || stream.is_null()) {
+ return output_volume_vector;
+ }
Vector3 linear_velocity;
@@ -422,7 +438,10 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
}
}
- AudioServer::get_singleton()->set_playback_highshelf_params(stream_playback, Math::db2linear(db_att), attenuation_filter_cutoff_hz);
+ linear_attenuation = Math::db2linear(db_att);
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_highshelf_params(playback, linear_attenuation, attenuation_filter_cutoff_hz);
+ }
//TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from
// speakers not facing the source) - this could be made distance dependent.
_calc_output_vol(local_pos.normalized(), 4.0, output_volume_vector);
@@ -447,7 +466,10 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
} else {
bus_volumes[bus] = output_volume_vector;
}
- AudioServer::get_singleton()->set_playback_bus_volumes_linear(stream_playback, bus_volumes);
+
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_bus_volumes_linear(playback, bus_volumes);
+ }
if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
Vector3 listener_velocity;
@@ -458,9 +480,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity);
- if (local_velocity == Vector3()) {
- AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
- } else {
+ if (local_velocity != Vector3()) {
float approaching = local_pos.normalized().dot(local_velocity.normalized());
float velocity = local_velocity.length();
float speed_of_sound = 343.0;
@@ -468,34 +488,23 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
float doppler_pitch_scale = pitch_scale * speed_of_sound / (speed_of_sound + velocity * approaching);
doppler_pitch_scale = CLAMP(doppler_pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff
- AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, doppler_pitch_scale);
+ actual_pitch_scale = doppler_pitch_scale;
+ } else {
+ actual_pitch_scale = pitch_scale;
}
} else {
- AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
+ actual_pitch_scale = pitch_scale;
+ }
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_pitch_scale(playback, actual_pitch_scale);
}
}
return output_volume_vector;
}
void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
- if (stream_playback.is_valid()) {
- stop();
- stream_playback.unref();
- stream.unref();
- }
-
- if (p_stream.is_valid()) {
- stream_playback = p_stream->instance_playback();
- if (stream_playback.is_valid()) {
- stream = p_stream;
- } else {
- stream.unref();
- }
- }
-
- if (p_stream.is_valid() && stream_playback.is_null()) {
- stream.unref();
- }
+ stop();
+ stream = p_stream;
}
Ref<AudioStream> AudioStreamPlayer3D::get_stream() const {
@@ -536,40 +545,47 @@ float AudioStreamPlayer3D::get_pitch_scale() const {
}
void AudioStreamPlayer3D::play(float p_from_pos) {
- if (stream_playback.is_valid()) {
- setplay.set(p_from_pos);
- set_physics_process_internal(true);
+ if (stream.is_null()) {
+ return;
+ }
+ ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
+ if (stream->is_monophonic() && is_playing()) {
+ stop();
}
+ setplay.set(p_from_pos);
+ active.set();
+ set_physics_process_internal(true);
}
void AudioStreamPlayer3D::seek(float p_seconds) {
- if (stream_playback.is_valid() && active.is_set()) {
- play(p_seconds);
- }
+ stop();
+ play(p_seconds);
}
void AudioStreamPlayer3D::stop() {
- if (stream_playback.is_valid()) {
- active.clear();
- AudioServer::get_singleton()->stop_playback_stream(stream_playback);
- set_physics_process_internal(false);
- setplay.set(-1);
+ setplay.set(-1);
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->stop_playback_stream(playback);
}
+ stream_playbacks.clear();
+ active.clear();
+ set_physics_process_internal(false);
}
bool AudioStreamPlayer3D::is_playing() const {
- if (stream_playback.is_valid()) {
- return active.is_set() || setplay.get() >= 0;
+ for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (AudioServer::get_singleton()->is_playback_active(playback)) {
+ return true;
+ }
}
-
return false;
}
float AudioStreamPlayer3D::get_playback_position() {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->get_playback_position(stream_playback);
+ // Return the playback position of the most recently started playback stream.
+ if (!stream_playbacks.is_empty()) {
+ return AudioServer::get_singleton()->get_playback_position(stream_playbacks[stream_playbacks.size() - 1]);
}
-
return 0;
}
@@ -729,20 +745,35 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking()
}
void AudioStreamPlayer3D::set_stream_paused(bool p_pause) {
- if (stream_playback.is_valid()) {
- AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
+ // TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted.
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
}
}
bool AudioStreamPlayer3D::get_stream_paused() const {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+ // There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
+ if (!stream_playbacks.is_empty()) {
+ return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
}
return false;
}
Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() {
- return stream_playback;
+ if (!stream_playbacks.is_empty()) {
+ return stream_playbacks[stream_playbacks.size() - 1];
+ }
+ return nullptr;
+}
+
+void AudioStreamPlayer3D::set_max_polyphony(int p_max_polyphony) {
+ if (p_max_polyphony > 0) {
+ max_polyphony = p_max_polyphony;
+ }
+}
+
+int AudioStreamPlayer3D::get_max_polyphony() const {
+ return max_polyphony;
}
void AudioStreamPlayer3D::_bind_methods() {
@@ -810,6 +841,9 @@ void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer3D::set_stream_paused);
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer3D::get_stream_paused);
+ ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer3D::set_max_polyphony);
+ ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer3D::get_max_polyphony);
+
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -823,6 +857,7 @@ void AudioStreamPlayer3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
ADD_GROUP("Emission Angle", "emission_angle");
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index f915abad2b..abd5a841b2 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -31,6 +31,7 @@
#ifndef AUDIO_STREAM_PLAYER_3D_H
#define AUDIO_STREAM_PLAYER_3D_H
+#include "core/os/mutex.h"
#include "scene/3d/area_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/velocity_tracker_3d.h"
@@ -68,10 +69,10 @@ private:
};
- Ref<AudioStreamPlayback> stream_playback;
+ Vector<Ref<AudioStreamPlayback>> stream_playbacks;
Ref<AudioStream> stream;
- SafeFlag active;
+ SafeFlag active{ false };
SafeNumeric<float> setplay{ -1.0 };
AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE;
@@ -79,8 +80,11 @@ private:
float unit_size = 10.0;
float max_db = 3.0;
float pitch_scale = 1.0;
+ // Internally used to take doppler tracking into account.
+ float actual_pitch_scale = 1.0;
bool autoplay = false;
- StringName bus = "Master";
+ StringName bus = SNAME("Master");
+ int max_polyphony = 1;
uint64_t last_mix_count = -1;
@@ -106,6 +110,8 @@ private:
float attenuation_filter_cutoff_hz = 5000.0;
float attenuation_filter_db = -24.0;
+ float linear_attenuation = 0;
+
float max_distance = 0.0;
Ref<VelocityTracker3D> velocity_tracker;
@@ -146,6 +152,9 @@ public:
void set_bus(const StringName &p_bus);
StringName get_bus() const;
+ void set_max_polyphony(int p_max_polyphony);
+ int get_max_polyphony() const;
+
void set_autoplay(bool p_enable);
bool is_autoplay_enabled();
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index f7b7604fd5..0334ba667b 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -42,17 +42,29 @@ void AudioStreamPlayer::_notification(int p_what) {
}
if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- if (stream_playback.is_valid() && active.is_set() && !AudioServer::get_singleton()->is_playback_active(stream_playback)) {
+ Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
+ emit_signal(SNAME("finished"));
+ playbacks_to_remove.push_back(playback);
+ }
+ }
+ // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
+ for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
+ stream_playbacks.erase(playback);
+ }
+ if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
+ // This node is no longer actively playing audio.
active.clear();
set_process_internal(false);
- emit_signal(SNAME("finished"));
}
}
if (p_what == NOTIFICATION_EXIT_TREE) {
- if (stream_playback.is_valid()) {
- AudioServer::get_singleton()->stop_playback_stream(stream_playback);
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->stop_playback_stream(playback);
}
+ stream_playbacks.clear();
}
if (p_what == NOTIFICATION_PAUSED) {
@@ -68,24 +80,8 @@ void AudioStreamPlayer::_notification(int p_what) {
}
void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
- if (stream_playback.is_valid()) {
- stop();
- stream_playback.unref();
- stream.unref();
- }
-
- if (p_stream.is_valid()) {
- stream_playback = p_stream->instance_playback();
- if (stream_playback.is_valid()) {
- stream = p_stream;
- } else {
- stream.unref();
- }
- }
-
- if (p_stream.is_valid() && stream_playback.is_null()) {
- stream.unref();
- }
+ stop();
+ stream = p_stream;
}
Ref<AudioStream> AudioStreamPlayer::get_stream() const {
@@ -95,7 +91,10 @@ Ref<AudioStream> AudioStreamPlayer::get_stream() const {
void AudioStreamPlayer::set_volume_db(float p_volume) {
volume_db = p_volume;
- AudioServer::get_singleton()->set_playback_all_bus_volumes_linear(stream_playback, _get_volume_vector());
+ Vector<AudioFrame> volume_vector = _get_volume_vector();
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_all_bus_volumes_linear(playback, volume_vector);
+ }
}
float AudioStreamPlayer::get_volume_db() const {
@@ -106,57 +105,83 @@ void AudioStreamPlayer::set_pitch_scale(float p_pitch_scale) {
ERR_FAIL_COND(p_pitch_scale <= 0.0);
pitch_scale = p_pitch_scale;
- AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
+ }
}
float AudioStreamPlayer::get_pitch_scale() const {
return pitch_scale;
}
+void AudioStreamPlayer::set_max_polyphony(int p_max_polyphony) {
+ if (p_max_polyphony > 0) {
+ max_polyphony = p_max_polyphony;
+ }
+}
+
+int AudioStreamPlayer::get_max_polyphony() const {
+ return max_polyphony;
+}
+
void AudioStreamPlayer::play(float p_from_pos) {
- stop();
- if (stream.is_valid()) {
- stream_playback = stream->instance_playback();
+ if (stream.is_null()) {
+ return;
+ }
+ ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
+ if (stream->is_monophonic() && is_playing()) {
+ stop();
}
- if (stream_playback.is_valid()) {
- AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos);
- active.set();
+ Ref<AudioStreamPlayback> stream_playback = stream->instance_playback();
+ ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+
+ AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos);
+ stream_playbacks.push_back(stream_playback);
+ active.set();
+ set_process_internal(true);
+ while (stream_playbacks.size() > max_polyphony) {
+ AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
+ stream_playbacks.remove(0);
}
}
void AudioStreamPlayer::seek(float p_seconds) {
- if (stream_playback.is_valid() && active.is_set()) {
+ if (is_playing()) {
+ stop();
play(p_seconds);
}
}
void AudioStreamPlayer::stop() {
- if (stream_playback.is_valid()) {
- active.clear();
- AudioServer::get_singleton()->stop_playback_stream(stream_playback);
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->stop_playback_stream(playback);
}
+ stream_playbacks.clear();
+ active.clear();
+ set_process_internal(false);
}
bool AudioStreamPlayer::is_playing() const {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->is_playback_active(stream_playback);
+ for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (AudioServer::get_singleton()->is_playback_active(playback)) {
+ return true;
+ }
}
-
return false;
}
float AudioStreamPlayer::get_playback_position() {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->get_playback_position(stream_playback);
+ // Return the playback position of the most recently started playback stream.
+ if (!stream_playbacks.is_empty()) {
+ return AudioServer::get_singleton()->get_playback_position(stream_playbacks[stream_playbacks.size() - 1]);
}
-
return 0;
}
void AudioStreamPlayer::set_bus(const StringName &p_bus) {
bus = p_bus;
- if (stream_playback.is_valid()) {
- AudioServer::get_singleton()->set_playback_bus_exclusive(stream_playback, p_bus, _get_volume_vector());
+ for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_bus_exclusive(playback, p_bus, _get_volume_vector());
}
}
@@ -166,7 +191,7 @@ StringName AudioStreamPlayer::get_bus() const {
return bus;
}
}
- return "Master";
+ return SNAME("Master");
}
void AudioStreamPlayer::set_autoplay(bool p_enable) {
@@ -194,22 +219,25 @@ void AudioStreamPlayer::_set_playing(bool p_enable) {
}
bool AudioStreamPlayer::_is_active() const {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->is_playback_active(stream_playback);
+ for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (AudioServer::get_singleton()->is_playback_active(playback)) {
+ return true;
+ }
}
return false;
}
void AudioStreamPlayer::set_stream_paused(bool p_pause) {
- // TODO this does not have perfect recall, fix that maybe? If the stream isn't set, we can't persist this bool.
- if (stream_playback.is_valid()) {
- AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
+ // TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted.
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
}
}
bool AudioStreamPlayer::get_stream_paused() const {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+ // There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
+ if (!stream_playbacks.is_empty()) {
+ return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
}
return false;
}
@@ -271,7 +299,10 @@ void AudioStreamPlayer::_bus_layout_changed() {
}
Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() {
- return stream_playback;
+ if (!stream_playbacks.is_empty()) {
+ return stream_playbacks[stream_playbacks.size() - 1];
+ }
+ return nullptr;
}
void AudioStreamPlayer::_bind_methods() {
@@ -306,6 +337,9 @@ void AudioStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused);
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused);
+ ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer::set_max_polyphony);
+ ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer::get_max_polyphony);
+
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer::get_stream_playback);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -315,6 +349,7 @@ void AudioStreamPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
ADD_SIGNAL(MethodInfo("finished"));
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index 76b6698c9d..7205fce204 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -46,7 +46,7 @@ public:
};
private:
- Ref<AudioStreamPlayback> stream_playback;
+ Vector<Ref<AudioStreamPlayback>> stream_playbacks;
Ref<AudioStream> stream;
SafeFlag active;
@@ -54,7 +54,8 @@ private:
float pitch_scale = 1.0;
float volume_db = 0.0;
bool autoplay = false;
- StringName bus = "Master";
+ StringName bus = SNAME("Master");
+ int max_polyphony = 1;
MixTarget mix_target = MIX_TARGET_STEREO;
@@ -85,6 +86,9 @@ public:
void set_pitch_scale(float p_pitch_scale);
float get_pitch_scale() const;
+ void set_max_polyphony(int p_max_polyphony);
+ int get_max_polyphony() const;
+
void play(float p_from_pos = 0.0);
void seek(float p_seconds);
void stop();
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 5f3ab18cca..d05762b6c0 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -1400,7 +1400,8 @@ void CodeEdit::fold_line(int p_line) {
}
/* Find the last line to be hidden. */
- int end_line = get_line_count();
+ const int line_count = get_line_count() - 1;
+ int end_line = line_count;
int in_comment = is_in_comment(p_line);
int in_string = (in_comment == -1) ? is_in_string(p_line) : -1;
@@ -1408,7 +1409,7 @@ void CodeEdit::fold_line(int p_line) {
end_line = get_delimiter_end_position(p_line, get_line(p_line).size() - 1).y;
/* End line is the same therefore we have a block. */
if (end_line == p_line) {
- for (int i = p_line + 1; i < get_line_count(); i++) {
+ for (int i = p_line + 1; i <= line_count; i++) {
if ((in_string != -1 && is_in_string(i) == -1) || (in_comment != -1 && is_in_comment(i) == -1)) {
end_line = i - 1;
break;
@@ -1417,14 +1418,27 @@ void CodeEdit::fold_line(int p_line) {
}
} else {
int start_indent = get_indent_level(p_line);
- for (int i = p_line + 1; i < get_line_count(); i++) {
+ for (int i = p_line + 1; i <= line_count; i++) {
if (get_line(p_line).strip_edges().size() == 0 || is_in_string(i) != -1 || is_in_comment(i) != -1) {
end_line = i;
continue;
}
- if (get_indent_level(i) <= start_indent && get_line(i).strip_edges().size() != 0) {
+ if (i == line_count) {
+ /* Do not fold empty last line of script if any */
+ end_line = i;
+ if (get_line(i).strip_edges().size() == 0) {
+ end_line--;
+ }
+ break;
+ }
+
+ if ((get_indent_level(i) <= start_indent && get_line(i).strip_edges().size() != 0)) {
+ /* Keep an empty line unfolded if any */
end_line = i - 1;
+ if (get_line(i - 1).strip_edges().size() == 0 && i - 2 > p_line) {
+ end_line = i - 2;
+ }
break;
}
}
@@ -1937,7 +1951,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
if (pre_brace_pair == -1 && post_brace_pair == -1 && get_caret_column() > 0 && get_caret_column() < get_line(caret_line).length()) {
pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column() + 1);
- if (pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1)) {
+ if (pre_brace_pair != -1 && pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1)) {
remove_text(caret_line, get_caret_column() - 2, caret_line, get_caret_column());
if (_get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1) != pre_brace_pair) {
set_caret_column(get_caret_column() - 1);
@@ -2744,7 +2758,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
/* If we have a space, previous word might be a keyword. eg "func |". */
} else if (cofs > 0 && line[cofs - 1] == ' ') {
int ofs = cofs - 1;
- while (ofs >= 0 && line[ofs] == ' ') {
+ while (ofs > 0 && line[ofs] == ' ') {
ofs--;
}
prev_is_word = _is_char(line[ofs]);
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 419d49bccf..925e6f5b97 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -41,12 +41,16 @@ void LinkButton::_shape() {
} else {
text_buf->set_direction((TextServer::Direction)text_direction);
}
- TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, text));
- text_buf->add_string(text, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, xl_text));
+ text_buf->add_string(xl_text, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
}
void LinkButton::set_text(const String &p_text) {
+ if (text == p_text) {
+ return;
+ }
text = p_text;
+ xl_text = atr(text);
_shape();
minimum_size_changed();
update();
@@ -141,7 +145,13 @@ Size2 LinkButton::get_minimum_size() const {
void LinkButton::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_TRANSLATION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ xl_text = atr(text);
+ _shape();
+
+ minimum_size_changed();
+ update();
+ } break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
update();
} break;
diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h
index 7eaa9f88b6..231543c63c 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -47,6 +47,7 @@ public:
private:
String text;
+ String xl_text;
Ref<TextLine> text_buf;
UnderlineMode underline_mode = UNDERLINE_MODE_ALWAYS;
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index fbc67d8a24..aeadfd78ee 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -3946,24 +3946,15 @@ float RichTextLabel::get_percent_visible() const {
return percent_visible;
}
-void RichTextLabel::set_effects(const Vector<Variant> &effects) {
- custom_effects.clear();
- for (int i = 0; i < effects.size(); i++) {
- Ref<RichTextEffect> effect = Ref<RichTextEffect>(effects[i]);
- custom_effects.push_back(effect);
- }
-
+void RichTextLabel::set_effects(Array p_effects) {
+ custom_effects = p_effects;
if ((bbcode != "") && use_bbcode) {
parse_bbcode(bbcode);
}
}
-Vector<Variant> RichTextLabel::get_effects() {
- Vector<Variant> r;
- for (int i = 0; i < custom_effects.size(); i++) {
- r.push_back(custom_effects[i]);
- }
- return r;
+Array RichTextLabel::get_effects() {
+ return custom_effects;
}
void RichTextLabel::install_effect(const Variant effect) {
@@ -4279,12 +4270,13 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item
Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
for (int i = 0; i < custom_effects.size(); i++) {
- if (!custom_effects[i].is_valid()) {
+ Ref<RichTextEffect> effect = custom_effects[i];
+ if (!effect.is_valid()) {
continue;
}
- if (custom_effects[i]->get_bbcode() == p_bbcode_identifier) {
- return custom_effects[i];
+ if (effect->get_bbcode() == p_bbcode_identifier) {
+ return effect;
}
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index ae04a7e684..f25a8bf193 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -363,7 +363,7 @@ private:
ItemMeta *meta_hovering = nullptr;
Variant current_meta;
- Vector<Ref<RichTextEffect>> custom_effects;
+ Array custom_effects;
void _invalidate_current_line(ItemFrame *p_frame);
void _validate_line_caches(ItemFrame *p_frame);
@@ -577,8 +577,8 @@ public:
void set_percent_visible(float p_percent);
float get_percent_visible() const;
- void set_effects(const Vector<Variant> &effects);
- Vector<Variant> get_effects();
+ void set_effects(Array p_effects);
+ Array get_effects();
void install_effect(const Variant effect);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index b9e9a4d450..f64c07df76 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -37,6 +37,7 @@
#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
+#include "core/string/string_builder.h"
#include "core/string/translation.h"
#include "scene/main/window.h"
@@ -78,7 +79,11 @@ void TextEdit::Text::set_font_size(int p_font_size) {
}
void TextEdit::Text::set_tab_size(int p_tab_size) {
+ if (tab_size == p_tab_size) {
+ return;
+ }
tab_size = p_tab_size;
+ tab_size_dirty = true;
}
int TextEdit::Text::get_tab_size() const {
@@ -118,10 +123,8 @@ int TextEdit::Text::get_line_width(int p_line, int p_wrap_index) const {
return text[p_line].data_buf->get_size().x;
}
-int TextEdit::Text::get_line_height(int p_line, int p_wrap_index) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), 0);
-
- return text[p_line].data_buf->get_line_size(p_wrap_index).y;
+int TextEdit::Text::get_line_height() const {
+ return line_height;
}
void TextEdit::Text::set_width(float p_width) {
@@ -153,6 +156,36 @@ _FORCE_INLINE_ const String &TextEdit::Text::operator[](int p_line) const {
return text[p_line].data;
}
+void TextEdit::Text::_calculate_line_height() {
+ int height = 0;
+ for (int i = 0; i < text.size(); i++) {
+ // Found another line with the same height...nothing to update.
+ if (text[i].height == line_height) {
+ height = line_height;
+ break;
+ }
+ height = MAX(height, text[i].height);
+ }
+ line_height = height;
+}
+
+void TextEdit::Text::_calculate_max_line_width() {
+ int width = 0;
+ for (int i = 0; i < text.size(); i++) {
+ if (is_hidden(i)) {
+ continue;
+ }
+
+ // Found another line with the same width...nothing to update.
+ if (text[i].width == max_width) {
+ width = max_width;
+ break;
+ }
+ width = MAX(width, text[i].width);
+ }
+ max_width = width;
+}
+
void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Vector<Vector2i> &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
@@ -182,17 +215,54 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_
tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
text.write[p_line].data_buf->tab_align(tabs);
}
+
+ // Update height.
+ const int old_height = text.write[p_line].height;
+ const int wrap_amount = get_line_wrap_amount(p_line);
+ int height = font->get_height(font_size);
+ for (int i = 0; i <= wrap_amount; i++) {
+ height = MAX(height, text[p_line].data_buf->get_line_size(i).y);
+ }
+ text.write[p_line].height = height;
+
+ // If this line has shrunk, this may no longer the the tallest line.
+ if (old_height == line_height && height < line_height) {
+ _calculate_line_height();
+ } else {
+ line_height = MAX(height, line_height);
+ }
+
+ // Update width.
+ const int old_width = text.write[p_line].width;
+ int width = get_line_width(p_line);
+ text.write[p_line].width = width;
+
+ // If this line has shrunk, this may no longer the the longest line.
+ if (old_width == max_width && width < max_width) {
+ _calculate_max_line_width();
+ } else if (!is_hidden(p_line)) {
+ max_width = MAX(width, max_width);
+ }
}
void TextEdit::Text::invalidate_all_lines() {
for (int i = 0; i < text.size(); i++) {
text.write[i].data_buf->set_width(width);
- if (tab_size > 0) {
- Vector<float> tabs;
- tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
- text.write[i].data_buf->tab_align(tabs);
+ if (tab_size_dirty) {
+ if (tab_size > 0) {
+ Vector<float> tabs;
+ tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
+ text.write[i].data_buf->tab_align(tabs);
+ }
+ // Tabs have changes, force width update.
+ text.write[i].width = get_line_width(i);
}
}
+
+ if (tab_size_dirty) {
+ _calculate_max_line_width();
+ tab_size_dirty = false;
+ }
}
void TextEdit::Text::invalidate_all() {
@@ -211,16 +281,8 @@ void TextEdit::Text::clear() {
insert(0, "", Vector<Vector2i>());
}
-int TextEdit::Text::get_max_width(bool p_exclude_hidden) const {
- // Quite some work, but should be fast enough.
-
- int max = 0;
- for (int i = 0; i < text.size(); i++) {
- if (!p_exclude_hidden || !is_hidden(i)) {
- max = MAX(max, get_line_width(i));
- }
- }
- return max;
+int TextEdit::Text::get_max_width() const {
+ return max_width;
}
void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override) {
@@ -243,7 +305,20 @@ void TextEdit::Text::insert(int p_at, const String &p_text, const Vector<Vector2
}
void TextEdit::Text::remove(int p_at) {
+ int height = text[p_at].height;
+ int width = text[p_at].width;
+
text.remove(p_at);
+
+ // If this is the tallest line, we need to get the next tallest.
+ if (height == line_height) {
+ _calculate_line_height();
+ }
+
+ // If this is the longest line, we need to get the next longest.
+ if (width == max_width) {
+ _calculate_max_line_width();
+ }
}
void TextEdit::Text::add_gutter(int p_at) {
@@ -293,7 +368,7 @@ void TextEdit::_notification(int p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
call_deferred(SNAME("_update_scrollbars"));
- call_deferred(SNAME("_update_wrap_at"));
+ call_deferred(SNAME("_update_wrap_at_column"));
}
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
@@ -644,8 +719,9 @@ void TextEdit::_notification(int p_what) {
int characters = 0;
int tabs = 0;
for (int j = 0; j < str.length(); j++) {
- if (color_map.has(last_wrap_column + j)) {
- current_color = color_map[last_wrap_column + j].get("color");
+ const Variant *color_data = color_map.getptr(last_wrap_column + j);
+ if (color_data != nullptr) {
+ current_color = (color_data->operator Dictionary()).get("color", font_color);
if (!editable) {
current_color.a = font_readonly_color.a;
}
@@ -1023,8 +1099,9 @@ void TextEdit::_notification(int p_what) {
char_ofs = 0;
}
for (int j = 0; j < gl_size; j++) {
- if (color_map.has(glyphs[j].start)) {
- current_color = color_map[glyphs[j].start].get("color");
+ const Variant *color_data = color_map.getptr(glyphs[j].start);
+ if (color_data != nullptr) {
+ current_color = (color_data->operator Dictionary()).get("color", font_color);
if (!editable && current_color.a > font_readonly_color.a) {
current_color.a = font_readonly_color.a;
}
@@ -2548,15 +2625,15 @@ void TextEdit::set_text(const String &p_text) {
}
String TextEdit::get_text() const {
- String longthing;
- int len = text.size();
- for (int i = 0; i < len; i++) {
- longthing += text[i];
- if (i != len - 1) {
- longthing += "\n";
+ StringBuilder ret_text;
+ const int text_size = text.size();
+ for (int i = 0; i < text_size; i++) {
+ ret_text += text[i];
+ if (i != text_size - 1) {
+ ret_text += "\n";
}
}
- return longthing;
+ return ret_text.as_string();
}
int TextEdit::get_line_count() const {
@@ -2592,13 +2669,7 @@ int TextEdit::get_line_width(int p_line, int p_wrap_index) const {
}
int TextEdit::get_line_height() const {
- int height = font->get_height(font_size);
- for (int i = 0; i < text.size(); i++) {
- for (int j = 0; j <= text.get_line_wrap_amount(i); j++) {
- height = MAX(height, text.get_line_height(i, j));
- }
- }
- return height + line_spacing;
+ return text.get_line_height() + line_spacing;
}
int TextEdit::get_indent_level(int p_line) const {
@@ -4151,6 +4222,9 @@ TextEdit::GutterType TextEdit::get_gutter_type(int p_gutter) const {
void TextEdit::set_gutter_width(int p_gutter, int p_width) {
ERR_FAIL_INDEX(p_gutter, gutters.size());
+ if (gutters[p_gutter].width == p_width) {
+ return;
+ }
gutters.write[p_gutter].width = p_width;
_update_gutter_width();
}
@@ -4166,6 +4240,9 @@ int TextEdit::get_total_gutter_width() const {
void TextEdit::set_gutter_draw(int p_gutter, bool p_draw) {
ERR_FAIL_INDEX(p_gutter, gutters.size());
+ if (gutters[p_gutter].draw == p_draw) {
+ return;
+ }
gutters.write[p_gutter].draw = p_draw;
_update_gutter_width();
}
@@ -5458,7 +5535,7 @@ void TextEdit::_update_scrollbars() {
}
int visible_width = size.width - style_normal->get_minimum_size().width;
- int total_width = text.get_max_width(true) + vmin.x + gutters_width + gutter_padding;
+ int total_width = text.get_max_width() + vmin.x + gutters_width + gutter_padding;
if (draw_minimap) {
total_width += minimap_width;
@@ -5910,8 +5987,6 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
text.set_hidden(p_line, false);
}
- text.invalidate_cache(p_line);
-
r_end_line = p_line + substrings.size() - 1;
r_end_column = text[r_end_line].length() - postinsert_text.length();
@@ -5968,8 +6043,6 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
}
text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text));
- text.invalidate_cache(p_from_line);
-
if (!text_changed_dirty && !setting_text) {
if (is_inside_tree()) {
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
@@ -5986,7 +6059,6 @@ TextEdit::TextEdit() {
set_default_cursor_shape(CURSOR_IBEAM);
text.set_tab_size(text.get_tab_size());
- text.clear();
h_scroll = memnew(HScrollBar);
v_scroll = memnew(VScrollBar);
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 07999c2442..e996bba983 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -144,6 +144,8 @@ private:
Color background_color = Color(0, 0, 0, 0);
bool hidden = false;
+ int height = 0;
+ int width = 0;
Line() {
data_buf.instantiate();
@@ -152,6 +154,7 @@ private:
private:
bool is_dirty = false;
+ bool tab_size_dirty = false;
mutable Vector<Line> text;
Ref<Font> font;
@@ -162,11 +165,16 @@ private:
TextServer::Direction direction = TextServer::DIRECTION_AUTO;
bool draw_control_chars = false;
+ int line_height = -1;
+ int max_width = -1;
int width = -1;
int tab_size = 4;
int gutter_count = 0;
+ void _calculate_line_height();
+ void _calculate_max_line_width();
+
public:
void set_tab_size(int p_tab_size);
int get_tab_size() const;
@@ -176,9 +184,9 @@ private:
void set_direction_and_language(TextServer::Direction p_direction, const String &p_language);
void set_draw_control_chars(bool p_draw_control_chars);
- int get_line_height(int p_line, int p_wrap_index) const;
+ int get_line_height() const;
int get_line_width(int p_line, int p_wrap_index = -1) const;
- int get_max_width(bool p_exclude_hidden = false) const;
+ int get_max_width() const;
void set_width(float p_width);
int get_line_wrap_amount(int p_line) const;
@@ -187,7 +195,14 @@ private:
const Ref<TextParagraph> get_line_data(int p_line) const;
void set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override);
- void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; }
+ void set_hidden(int p_line, bool p_hidden) {
+ text.write[p_line].hidden = p_hidden;
+ if (!p_hidden && text[p_line].width > max_width) {
+ max_width = text[p_line].width;
+ } else if (p_hidden && text[p_line].width == max_width) {
+ _calculate_max_line_width();
+ }
+ }
bool is_hidden(int p_line) const { return text[p_line].hidden; }
void insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override);
void remove(int p_at);
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index f2a2648140..05409b7bfe 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -123,28 +123,27 @@ void Node::_notification(int p_notification) {
}
} break;
case NOTIFICATION_READY: {
- if (get_script_instance()) {
- if (GDVIRTUAL_IS_OVERRIDDEN(_input)) {
- set_process_input(true);
- }
-
- if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) {
- set_process_unhandled_input(true);
- }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_input)) {
+ set_process_input(true);
+ }
- if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_key_input)) {
- set_process_unhandled_key_input(true);
- }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) {
+ set_process_unhandled_input(true);
+ }
- if (GDVIRTUAL_IS_OVERRIDDEN(_process)) {
- set_process(true);
- }
- if (GDVIRTUAL_IS_OVERRIDDEN(_physics_process)) {
- set_physics_process(true);
- }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_key_input)) {
+ set_process_unhandled_key_input(true);
+ }
- GDVIRTUAL_CALL(_ready);
+ if (GDVIRTUAL_IS_OVERRIDDEN(_process)) {
+ set_process(true);
}
+ if (GDVIRTUAL_IS_OVERRIDDEN(_physics_process)) {
+ set_physics_process(true);
+ }
+
+ GDVIRTUAL_CALL(_ready);
+
if (data.filename.length()) {
ERR_FAIL_COND(!is_inside_tree());
get_multiplayer()->scene_enter_exit_notify(data.filename, this, true);
@@ -517,32 +516,32 @@ void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int
}
}
-void Node::set_network_authority(int p_peer_id, bool p_recursive) {
- data.network_authority = p_peer_id;
+void Node::set_multiplayer_authority(int p_peer_id, bool p_recursive) {
+ data.multiplayer_authority = p_peer_id;
if (p_recursive) {
for (int i = 0; i < data.children.size(); i++) {
- data.children[i]->set_network_authority(p_peer_id, true);
+ data.children[i]->set_multiplayer_authority(p_peer_id, true);
}
}
}
-int Node::get_network_authority() const {
- return data.network_authority;
+int Node::get_multiplayer_authority() const {
+ return data.multiplayer_authority;
}
-bool Node::is_network_authority() const {
+bool Node::is_multiplayer_authority() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
- return get_multiplayer()->get_network_unique_id() == data.network_authority;
+ return get_multiplayer()->get_unique_id() == data.multiplayer_authority;
}
/***** RPC CONFIG ********/
-uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel) {
+uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
for (int i = 0; i < data.rpc_methods.size(); i++) {
if (data.rpc_methods[i].name == p_method) {
- MultiplayerAPI::RPCConfig &nd = data.rpc_methods.write[i];
+ Multiplayer::RPCConfig &nd = data.rpc_methods.write[i];
nd.rpc_mode = p_rpc_mode;
nd.transfer_mode = p_transfer_mode;
nd.channel = p_channel;
@@ -550,7 +549,7 @@ uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_
}
}
// New method
- MultiplayerAPI::RPCConfig nd;
+ Multiplayer::RPCConfig nd;
nd.name = p_method;
nd.rpc_mode = p_rpc_mode;
nd.transfer_mode = p_transfer_mode;
@@ -643,7 +642,7 @@ Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::Cal
void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer()->rpcp(this, p_peer_id, true, p_method, p_arg, p_argcount);
+ get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
@@ -664,7 +663,7 @@ void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
multiplayer = p_multiplayer;
}
-Vector<MultiplayerAPI::RPCConfig> Node::get_node_rpc_methods() const {
+Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const {
return data.rpc_methods;
}
@@ -2738,15 +2737,15 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("request_ready"), &Node::request_ready);
- ClassDB::bind_method(D_METHOD("set_network_authority", "id", "recursive"), &Node::set_network_authority, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("get_network_authority"), &Node::get_network_authority);
+ ClassDB::bind_method(D_METHOD("set_multiplayer_authority", "id", "recursive"), &Node::set_multiplayer_authority, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("get_multiplayer_authority"), &Node::get_multiplayer_authority);
- ClassDB::bind_method(D_METHOD("is_network_authority"), &Node::is_network_authority);
+ ClassDB::bind_method(D_METHOD("is_multiplayer_authority"), &Node::is_multiplayer_authority);
ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer);
ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer);
- ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description);
diff --git a/scene/main/node.h b/scene/main/node.h
index d0246ebe84..198501eeac 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -127,8 +127,8 @@ private:
ProcessMode process_mode = PROCESS_MODE_INHERIT;
Node *process_owner = nullptr;
- int network_authority = 1; // Server by default.
- Vector<MultiplayerAPI::RPCConfig> rpc_methods;
+ int multiplayer_authority = 1; // Server by default.
+ Vector<Multiplayer::RPCConfig> rpc_methods;
// Variables used to properly sort the node when processing, ignored otherwise.
// TODO: Should move all the stuff below to bits.
@@ -462,12 +462,12 @@ public:
bool is_displayed_folded() const;
/* NETWORK */
- void set_network_authority(int p_peer_id, bool p_recursive = true);
- int get_network_authority() const;
- bool is_network_authority() const;
+ void set_multiplayer_authority(int p_peer_id, bool p_recursive = true);
+ int get_multiplayer_authority() const;
+ bool is_multiplayer_authority() const;
- uint16_t rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC
- Vector<MultiplayerAPI::RPCConfig> get_node_rpc_methods() const;
+ uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC
+ Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const;
void rpc(const StringName &p_method, VARIANT_ARG_LIST); // RPC, honors RPCMode, TransferMode, channel
void rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); // RPC to specific peer(s), honors RPCMode, TransferMode, channel
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index b1b94ae910..19331c1906 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -31,7 +31,7 @@
#ifndef SCENE_TREE_H
#define SCENE_TREE_H
-#include "core/io/multiplayer_api.h"
+#include "core/multiplayer/multiplayer_api.h"
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
#include "core/templates/self_list.h"
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index 2ab9b7b5a4..d018103e64 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -480,6 +480,10 @@ float AudioStreamSample::get_length() const {
return float(len) / mix_rate;
}
+bool AudioStreamSample::is_monophonic() const {
+ return false;
+}
+
void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) {
AudioServer::get_singleton()->lock();
if (data) {
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index 8bf3d29123..24198e3c98 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -136,6 +136,8 @@ public:
virtual float get_length() const override; //if supported, otherwise return 0
+ virtual bool is_monophonic() const override;
+
void set_data(const Vector<uint8_t> &p_data);
Vector<uint8_t> get_data() const;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index fcd31143a8..d383961d87 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -205,25 +205,46 @@ bool TileSet::is_uv_clipping() const {
return uv_clipping;
};
-void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) {
- ERR_FAIL_COND(p_occlusion_layers_count < 0);
- if (occlusion_layers.size() == p_occlusion_layers_count) {
- return;
- }
+int TileSet::get_occlusion_layers_count() const {
+ return occlusion_layers.size();
+};
- occlusion_layers.resize(p_occlusion_layers_count);
+void TileSet::add_occlusion_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = occlusion_layers.size();
+ }
+ ERR_FAIL_INDEX(p_index, occlusion_layers.size() + 1);
+ occlusion_layers.insert(p_index, OcclusionLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_occlusion_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_occlusion_layers_count() const {
- return occlusion_layers.size();
-};
+void TileSet::move_occlusion_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, occlusion_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, occlusion_layers.size() + 1);
+ occlusion_layers.insert(p_to_pos, occlusion_layers[p_from_index]);
+ occlusion_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_occlusion_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_occlusion_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, occlusion_layers.size());
+ occlusion_layers.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_occlusion_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
void TileSet::set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask) {
ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size());
@@ -247,25 +268,45 @@ bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const {
return occlusion_layers[p_layer_index].sdf_collision;
}
-// Physics
-void TileSet::set_physics_layers_count(int p_physics_layers_count) {
- ERR_FAIL_COND(p_physics_layers_count < 0);
- if (physics_layers.size() == p_physics_layers_count) {
- return;
- }
+int TileSet::get_physics_layers_count() const {
+ return physics_layers.size();
+}
- physics_layers.resize(p_physics_layers_count);
+void TileSet::add_physics_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = physics_layers.size();
+ }
+ ERR_FAIL_INDEX(p_index, physics_layers.size() + 1);
+ physics_layers.insert(p_index, PhysicsLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_physics_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_physics_layers_count() const {
- return physics_layers.size();
+void TileSet::move_physics_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, physics_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, physics_layers.size() + 1);
+ physics_layers.insert(p_to_pos, physics_layers[p_from_index]);
+ physics_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_physics_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_physics_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, physics_layers.size());
+ physics_layers.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_physics_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer) {
@@ -301,17 +342,45 @@ Ref<PhysicsMaterial> TileSet::get_physics_layer_physics_material(int p_layer_ind
}
// Terrains
-void TileSet::set_terrain_sets_count(int p_terrains_sets_count) {
- ERR_FAIL_COND(p_terrains_sets_count < 0);
+int TileSet::get_terrain_sets_count() const {
+ return terrain_sets.size();
+}
+
+void TileSet::add_terrain_set(int p_index) {
+ if (p_index < 0) {
+ p_index = terrain_sets.size();
+ }
+ ERR_FAIL_INDEX(p_index, terrain_sets.size() + 1);
+ terrain_sets.insert(p_index, TerrainSet());
- terrain_sets.resize(p_terrains_sets_count);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_terrain_set(p_index);
+ }
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_terrain_sets_count() const {
- return terrain_sets.size();
+void TileSet::move_terrain_set(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, terrain_sets.size());
+ ERR_FAIL_INDEX(p_to_pos, terrain_sets.size() + 1);
+ terrain_sets.insert(p_to_pos, terrain_sets[p_from_index]);
+ terrain_sets.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_terrain_set(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_terrain_set(int p_index) {
+ ERR_FAIL_INDEX(p_index, terrain_sets.size());
+ terrain_sets.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_terrain_set(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) {
@@ -330,36 +399,61 @@ TileSet::TerrainMode TileSet::get_terrain_set_mode(int p_terrain_set) const {
return terrain_sets[p_terrain_set].mode;
}
-void TileSet::set_terrains_count(int p_terrain_set, int p_terrains_layers_count) {
+int TileSet::get_terrains_count(int p_terrain_set) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1);
+ return terrain_sets[p_terrain_set].terrains.size();
+}
+
+void TileSet::add_terrain(int p_terrain_set, int p_index) {
ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
- ERR_FAIL_COND(p_terrains_layers_count < 0);
- if (terrain_sets[p_terrain_set].terrains.size() == p_terrains_layers_count) {
- return;
+ Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
+ if (p_index < 0) {
+ p_index = terrains.size();
}
-
- int old_size = terrain_sets[p_terrain_set].terrains.size();
- terrain_sets.write[p_terrain_set].terrains.resize(p_terrains_layers_count);
+ ERR_FAIL_INDEX(p_index, terrains.size() + 1);
+ terrains.insert(p_index, Terrain());
// Default name and color
- for (int i = old_size; i < terrain_sets.write[p_terrain_set].terrains.size(); i++) {
- float hue_rotate = (i * 2 % 16) / 16.0;
- Color c;
- c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5);
- terrain_sets.write[p_terrain_set].terrains.write[i].color = c;
- terrain_sets.write[p_terrain_set].terrains.write[i].name = String(vformat("Terrain %d", i));
+ float hue_rotate = (terrains.size() % 16) / 16.0;
+ Color c;
+ c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5);
+ terrains.write[p_index].color = c;
+ terrains.write[p_index].name = String(vformat("Terrain %d", p_index));
+
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_terrain(p_terrain_set, p_index);
}
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
- }
+ notify_property_list_changed();
+ emit_changed();
+}
+void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
+
+ ERR_FAIL_INDEX(p_from_index, terrains.size());
+ ERR_FAIL_INDEX(p_to_pos, terrains.size() + 1);
+ terrains.insert(p_to_pos, terrains[p_from_index]);
+ terrains.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
+ }
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_terrains_count(int p_terrain_set) const {
- ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1);
- return terrain_sets[p_terrain_set].terrains.size();
+void TileSet::remove_terrain(int p_terrain_set, int p_index) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
+
+ ERR_FAIL_INDEX(p_index, terrains.size());
+ terrains.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_terrain(p_terrain_set, p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name) {
@@ -485,24 +579,45 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh
}
// Navigation
-void TileSet::set_navigation_layers_count(int p_navigation_layers_count) {
- ERR_FAIL_COND(p_navigation_layers_count < 0);
- if (navigation_layers.size() == p_navigation_layers_count) {
- return;
- }
+int TileSet::get_navigation_layers_count() const {
+ return navigation_layers.size();
+}
- navigation_layers.resize(p_navigation_layers_count);
+void TileSet::add_navigation_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = navigation_layers.size();
+ }
+ ERR_FAIL_INDEX(p_index, navigation_layers.size() + 1);
+ navigation_layers.insert(p_index, NavigationLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_navigation_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_navigation_layers_count() const {
- return navigation_layers.size();
+void TileSet::move_navigation_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, navigation_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, navigation_layers.size() + 1);
+ navigation_layers.insert(p_to_pos, navigation_layers[p_from_index]);
+ navigation_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_navigation_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_navigation_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, navigation_layers.size());
+ navigation_layers.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_navigation_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_navigation_layer_layers(int p_layer_index, uint32_t p_layers) {
@@ -517,30 +632,52 @@ uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const {
}
// Custom data.
-void TileSet::set_custom_data_layers_count(int p_custom_data_layers_count) {
- ERR_FAIL_COND(p_custom_data_layers_count < 0);
- if (custom_data_layers.size() == p_custom_data_layers_count) {
- return;
- }
-
- custom_data_layers.resize(p_custom_data_layers_count);
+int TileSet::get_custom_data_layers_count() const {
+ return custom_data_layers.size();
+}
- for (Map<String, int>::Element *E = custom_data_layers_by_name.front(); E; E = E->next()) {
- if (E->get() >= custom_data_layers.size()) {
- custom_data_layers_by_name.erase(E);
- }
+void TileSet::add_custom_data_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = custom_data_layers.size();
}
+ ERR_FAIL_INDEX(p_index, custom_data_layers.size() + 1);
+ custom_data_layers.insert(p_index, CustomDataLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_custom_data_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_custom_data_layers_count() const {
- return custom_data_layers.size();
+void TileSet::move_custom_data_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, custom_data_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, custom_data_layers.size() + 1);
+ custom_data_layers.insert(p_to_pos, custom_data_layers[p_from_index]);
+ custom_data_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_custom_data_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_custom_data_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, custom_data_layers.size());
+ custom_data_layers.remove(p_index);
+ for (KeyValue<String, int> E : custom_data_layers_by_name) {
+ if (E.value == p_index) {
+ custom_data_layers_by_name.erase(E.key);
+ break;
+ }
+ }
+
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_custom_data_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
int TileSet::get_custom_data_layer_by_name(String p_value) const {
@@ -1110,7 +1247,11 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) {
int terrain = tile_data->get_peering_bit_terrain(cell_neighbor);
if (terrain >= 0) {
- bit_counts[terrain] += 1;
+ if (terrain >= (int)bit_counts.size()) {
+ WARN_PRINT(vformat("Invalid peering bit terrain: %d", terrain));
+ } else {
+ bit_counts[terrain] += 1;
+ }
}
}
}
@@ -1831,13 +1972,13 @@ void TileSet::_compatibility_conversion() {
if (ctd->occluder.is_valid()) {
if (get_occlusion_layers_count() < 1) {
- set_occlusion_layers_count(1);
+ add_occlusion_layer();
}
tile_data->set_occluder(0, ctd->occluder);
}
if (ctd->navigation.is_valid()) {
if (get_navigation_layers_count() < 1) {
- set_navigation_layers_count(1);
+ add_navigation_layer();
}
tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
}
@@ -1847,7 +1988,7 @@ void TileSet::_compatibility_conversion() {
// Add the shapes.
if (ctd->shapes.size() > 0) {
if (get_physics_layers_count() < 1) {
- set_physics_layers_count(1);
+ add_physics_layer();
}
}
for (int k = 0; k < ctd->shapes.size(); k++) {
@@ -1922,13 +2063,13 @@ void TileSet::_compatibility_conversion() {
tile_data->set_z_index(ctd->z_index);
if (ctd->autotile_occluder_map.has(coords)) {
if (get_occlusion_layers_count() < 1) {
- set_occlusion_layers_count(1);
+ add_occlusion_layer();
}
tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]);
}
if (ctd->autotile_navpoly_map.has(coords)) {
if (get_navigation_layers_count() < 1) {
- set_navigation_layers_count(1);
+ add_navigation_layer();
}
tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
}
@@ -1942,7 +2083,7 @@ void TileSet::_compatibility_conversion() {
// Add the shapes.
if (ctd->shapes.size() > 0) {
if (get_physics_layers_count() < 1) {
- set_physics_layers_count(1);
+ add_physics_layer();
}
}
for (int k = 0; k < ctd->shapes.size(); k++) {
@@ -2206,15 +2347,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "light_mask") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= occlusion_layers.size()) {
- set_occlusion_layers_count(index + 1);
+ while (index >= occlusion_layers.size()) {
+ add_occlusion_layer();
}
set_occlusion_layer_light_mask(index, p_value);
return true;
} else if (components[1] == "sdf_collision") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
- if (index >= occlusion_layers.size()) {
- set_occlusion_layers_count(index + 1);
+ while (index >= occlusion_layers.size()) {
+ add_occlusion_layer();
}
set_occlusion_layer_sdf_collision(index, p_value);
return true;
@@ -2225,23 +2366,22 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "collision_layer") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= physics_layers.size()) {
- set_physics_layers_count(index + 1);
+ while (index >= physics_layers.size()) {
+ add_physics_layer();
}
set_physics_layer_collision_layer(index, p_value);
return true;
} else if (components[1] == "collision_mask") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= physics_layers.size()) {
- set_physics_layers_count(index + 1);
+ while (index >= physics_layers.size()) {
+ add_physics_layer();
}
set_physics_layer_collision_mask(index, p_value);
return true;
} else if (components[1] == "physics_material") {
Ref<PhysicsMaterial> physics_material = p_value;
- ERR_FAIL_COND_V(!physics_material.is_valid(), false);
- if (index >= physics_layers.size()) {
- set_physics_layers_count(index + 1);
+ while (index >= physics_layers.size()) {
+ add_physics_layer();
}
set_physics_layer_physics_material(index, physics_material);
return true;
@@ -2252,37 +2392,30 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(terrain_set_index < 0, false);
if (components[1] == "mode") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
+ while (terrain_set_index >= terrain_sets.size()) {
+ add_terrain_set();
}
set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value)));
- } else if (components[1] == "terrains_count") {
- ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
- }
- set_terrains_count(terrain_set_index, p_value);
- return true;
} else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) {
int terrain_index = components[1].trim_prefix("terrain_").to_int();
ERR_FAIL_COND_V(terrain_index < 0, false);
if (components[2] == "name") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
+ while (terrain_set_index >= terrain_sets.size()) {
+ add_terrain_set();
}
- if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
- set_terrains_count(terrain_set_index, terrain_index + 1);
+ while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ add_terrain(terrain_set_index);
}
set_terrain_name(terrain_set_index, terrain_index, p_value);
return true;
} else if (components[2] == "color") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
+ while (terrain_set_index >= terrain_sets.size()) {
+ add_terrain_set();
}
- if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
- set_terrains_count(terrain_set_index, terrain_index + 1);
+ while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ add_terrain(terrain_set_index);
}
set_terrain_color(terrain_set_index, terrain_index, p_value);
return true;
@@ -2294,8 +2427,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "layers") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= navigation_layers.size()) {
- set_navigation_layers_count(index + 1);
+ while (index >= navigation_layers.size()) {
+ add_navigation_layer();
}
set_navigation_layer_layers(index, p_value);
return true;
@@ -2306,15 +2439,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "name") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
- if (index >= custom_data_layers.size()) {
- set_custom_data_layers_count(index + 1);
+ while (index >= custom_data_layers.size()) {
+ add_custom_data_layer();
}
set_custom_data_name(index, p_value);
return true;
} else if (components[1] == "type") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= custom_data_layers.size()) {
- set_custom_data_layers_count(index + 1);
+ while (index >= custom_data_layers.size()) {
+ add_custom_data_layer();
}
set_custom_data_type(index, Variant::Type(int(p_value)));
return true;
@@ -2402,9 +2535,6 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
if (components[1] == "mode") {
r_ret = get_terrain_set_mode(terrain_set_index);
return true;
- } else if (components[1] == "terrains_count") {
- r_ret = get_terrains_count(terrain_set_index);
- return true;
} else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) {
int terrain_index = components[1].trim_prefix("terrain_").to_int();
if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
@@ -2522,7 +2652,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) {
p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides"));
- p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::NIL, vformat("terrain_set_%d/terrains", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, vformat("terrain_set_%d/terrain_", terrain_set_index)));
for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) {
p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index)));
p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index)));
@@ -2590,16 +2720,20 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping);
ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping);
- ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count);
ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count);
+ ClassDB::bind_method(D_METHOD("add_occlusion_layer", "to_position"), &TileSet::add_occlusion_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_occlusion_layer", "layer_index", "to_position"), &TileSet::move_occlusion_layer);
+ ClassDB::bind_method(D_METHOD("remove_occlusion_layer", "layer_index"), &TileSet::remove_occlusion_layer);
ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask);
ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask);
ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision);
ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision);
// Physics
- ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count);
ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count);
+ ClassDB::bind_method(D_METHOD("add_physics_layer", "to_position"), &TileSet::add_physics_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_physics_layer", "layer_index", "to_position"), &TileSet::move_physics_layer);
+ ClassDB::bind_method(D_METHOD("remove_physics_layer", "layer_index"), &TileSet::remove_physics_layer);
ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer);
ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer);
ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask);
@@ -2608,27 +2742,35 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material);
// Terrains
- ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count);
ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count);
+ ClassDB::bind_method(D_METHOD("add_terrain_set", "to_position"), &TileSet::add_terrain_set, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_terrain_set", "layer_index", "to_position"), &TileSet::move_terrain_set);
+ ClassDB::bind_method(D_METHOD("remove_terrain_set", "layer_index"), &TileSet::remove_terrain_set);
ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode);
ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode);
- ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count);
ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count);
+ ClassDB::bind_method(D_METHOD("add_terrain", "terrain_set", "to_position"), &TileSet::add_terrain, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_terrain", "terrain_set", "terrain_index", "to_position"), &TileSet::move_terrain);
+ ClassDB::bind_method(D_METHOD("remove_terrain", "terrain_set", "terrain_index"), &TileSet::remove_terrain);
ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name);
ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name);
ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color);
ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color);
// Navigation
- ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count);
ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count);
+ ClassDB::bind_method(D_METHOD("add_navigation_layer", "to_position"), &TileSet::add_navigation_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_navigation_layer", "layer_index", "to_position"), &TileSet::move_navigation_layer);
+ ClassDB::bind_method(D_METHOD("remove_navigation_layer", "layer_index"), &TileSet::remove_navigation_layer);
ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers);
// Custom data
- ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count);
ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count);
+ ClassDB::bind_method(D_METHOD("add_custom_data_layer", "to_position"), &TileSet::add_custom_data_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_custom_data_layer", "layer_index", "to_position"), &TileSet::move_custom_data_layer);
+ ClassDB::bind_method(D_METHOD("remove_custom_data_layer", "layer_index"), &TileSet::remove_custom_data_layer);
// Tile proxies
ClassDB::bind_method(D_METHOD("set_source_level_tile_proxy", "source_from", "source_to"), &TileSet::set_source_level_tile_proxy);
@@ -2653,19 +2795,19 @@ void TileSet::_bind_methods() {
ADD_GROUP("Rendering", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count");
+ ADD_ARRAY("occlusion_layers", "occlusion_layer_");
ADD_GROUP("Physics", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count");
+ ADD_ARRAY("physics_layers", "physics_layer_");
ADD_GROUP("Terrains", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count");
+ ADD_ARRAY("terrain_sets", "terrain_set_");
ADD_GROUP("Navigation", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count");
+ ADD_ARRAY("navigation_layers", "navigation_layer_");
ADD_GROUP("Custom data", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count");
+ ADD_ARRAY("custom_data_layers", "custom_data_layer_");
// -- Enum binding --
BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE);
@@ -2750,6 +2892,150 @@ void TileSetAtlasSource::notify_tile_data_properties_should_change() {
}
}
+void TileSetAtlasSource::add_occlusion_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_occlusion_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_occlusion_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_occlusion_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_occlusion_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_occlusion_layer(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_physics_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_physics_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_physics_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_physics_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_physics_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_physics_layer(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_terrain_set(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_terrain_set(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_terrain_set(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_terrain_set(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_terrain_set(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_terrain_set(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_terrain(int p_terrain_set, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_terrain(p_terrain_set, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_terrain(int p_terrain_set, int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_terrain(p_terrain_set, p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_navigation_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_navigation_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_navigation_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_navigation_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_navigation_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_navigation_layer(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_custom_data_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_custom_data_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_custom_data_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_custom_data_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_custom_data_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_custom_data_layer(p_index);
+ }
+ }
+}
+
void TileSetAtlasSource::reset_state() {
// Reset all TileData.
for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
@@ -3575,6 +3861,155 @@ void TileData::notify_tile_data_properties_should_change() {
emit_signal(SNAME("changed"));
}
+void TileData::add_occlusion_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = occluders.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1);
+ occluders.insert(p_to_pos, Ref<OccluderPolygon2D>());
+}
+
+void TileData::move_occlusion_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, occluders.size());
+ ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1);
+ occluders.insert(p_to_pos, occluders[p_from_index]);
+ occluders.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_occlusion_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, occluders.size());
+ occluders.remove(p_index);
+}
+
+void TileData::add_physics_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = physics.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, physics.size() + 1);
+ physics.insert(p_to_pos, PhysicsLayerTileData());
+}
+
+void TileData::move_physics_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, physics.size());
+ ERR_FAIL_INDEX(p_to_pos, physics.size() + 1);
+ physics.insert(p_to_pos, physics[p_from_index]);
+ physics.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_physics_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, physics.size());
+ physics.remove(p_index);
+}
+
+void TileData::add_terrain_set(int p_to_pos) {
+ if (p_to_pos >= 0 && p_to_pos <= terrain_set) {
+ terrain_set += 1;
+ }
+}
+
+void TileData::move_terrain_set(int p_from_index, int p_to_pos) {
+ if (p_from_index == terrain_set) {
+ terrain_set = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos;
+ } else {
+ if (p_from_index < terrain_set) {
+ terrain_set -= 1;
+ }
+ if (p_to_pos <= terrain_set) {
+ terrain_set += 1;
+ }
+ }
+}
+
+void TileData::remove_terrain_set(int p_index) {
+ if (p_index == terrain_set) {
+ terrain_set = -1;
+ for (int i = 0; i < 16; i++) {
+ terrain_peering_bits[i] = -1;
+ }
+ } else if (terrain_set > p_index) {
+ terrain_set -= 1;
+ }
+}
+
+void TileData::add_terrain(int p_terrain_set, int p_to_pos) {
+ if (terrain_set == p_terrain_set) {
+ for (int i = 0; i < 16; i++) {
+ if (p_to_pos >= 0 && p_to_pos <= terrain_peering_bits[i]) {
+ terrain_peering_bits[i] += 1;
+ }
+ }
+ }
+}
+
+void TileData::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
+ if (terrain_set == p_terrain_set) {
+ for (int i = 0; i < 16; i++) {
+ if (p_from_index == terrain_peering_bits[i]) {
+ terrain_peering_bits[i] = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos;
+ } else {
+ if (p_from_index < terrain_peering_bits[i]) {
+ terrain_peering_bits[i] -= 1;
+ }
+ if (p_to_pos <= terrain_peering_bits[i]) {
+ terrain_peering_bits[i] += 1;
+ }
+ }
+ }
+ }
+}
+
+void TileData::remove_terrain(int p_terrain_set, int p_index) {
+ if (terrain_set == p_terrain_set) {
+ for (int i = 0; i < 16; i++) {
+ if (terrain_peering_bits[i] == p_index) {
+ terrain_peering_bits[i] = -1;
+ } else if (terrain_peering_bits[i] > p_index) {
+ terrain_peering_bits[i] -= 1;
+ }
+ }
+ }
+}
+
+void TileData::add_navigation_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = navigation.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1);
+ navigation.insert(p_to_pos, Ref<NavigationPolygon>());
+}
+
+void TileData::move_navigation_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, navigation.size());
+ ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1);
+ navigation.insert(p_to_pos, navigation[p_from_index]);
+ navigation.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_navigation_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, navigation.size());
+ navigation.remove(p_index);
+}
+
+void TileData::add_custom_data_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = custom_data.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1);
+ custom_data.insert(p_to_pos, Variant());
+}
+
+void TileData::move_custom_data_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, custom_data.size());
+ ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1);
+ custom_data.insert(p_to_pos, navigation[p_from_index]);
+ custom_data.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_custom_data_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, custom_data.size());
+ custom_data.remove(p_index);
+}
+
void TileData::reset_state() {
occluders.clear();
physics.clear();
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 35e6999d13..29a71d31d2 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -225,10 +225,10 @@ private:
bool terrain_bits_meshes_dirty = true;
// Navigation
- struct Navigationlayer {
+ struct NavigationLayer {
uint32_t layers = 1;
};
- Vector<Navigationlayer> navigation_layers;
+ Vector<NavigationLayer> navigation_layers;
// CustomData
struct CustomDataLayer {
@@ -298,16 +298,20 @@ public:
void set_uv_clipping(bool p_uv_clipping);
bool is_uv_clipping() const;
- void set_occlusion_layers_count(int p_occlusion_layers_count);
int get_occlusion_layers_count() const;
+ void add_occlusion_layer(int p_index = -1);
+ void move_occlusion_layer(int p_from_index, int p_to_pos);
+ void remove_occlusion_layer(int p_index);
void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask);
int get_occlusion_layer_light_mask(int p_layer_index) const;
void set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision);
bool get_occlusion_layer_sdf_collision(int p_layer_index) const;
// Physics
- void set_physics_layers_count(int p_physics_layers_count);
int get_physics_layers_count() const;
+ void add_physics_layer(int p_index = -1);
+ void move_physics_layer(int p_from_index, int p_to_pos);
+ void remove_physics_layer(int p_index);
void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer);
uint32_t get_physics_layer_collision_layer(int p_layer_index) const;
void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask);
@@ -315,13 +319,19 @@ public:
void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material);
Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const;
- // Terrains
- void set_terrain_sets_count(int p_terrains_sets_count);
+ // Terrain sets
int get_terrain_sets_count() const;
+ void add_terrain_set(int p_index = -1);
+ void move_terrain_set(int p_from_index, int p_to_pos);
+ void remove_terrain_set(int p_index);
void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode);
TerrainMode get_terrain_set_mode(int p_terrain_set) const;
- void set_terrains_count(int p_terrain_set, int p_terrains_count);
+
+ // Terrains
int get_terrains_count(int p_terrain_set) const;
+ void add_terrain(int p_terrain_set, int p_index = -1);
+ void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
+ void remove_terrain(int p_terrain_set, int p_index);
void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name);
String get_terrain_name(int p_terrain_set, int p_terrain_index) const;
void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color);
@@ -330,14 +340,18 @@ public:
bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
// Navigation
- void set_navigation_layers_count(int p_navigation_layers_count);
int get_navigation_layers_count() const;
+ void add_navigation_layer(int p_index = -1);
+ void move_navigation_layer(int p_from_index, int p_to_pos);
+ void remove_navigation_layer(int p_index);
void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers);
uint32_t get_navigation_layer_layers(int p_layer_index) const;
// Custom data
- void set_custom_data_layers_count(int p_custom_data_layers_count);
int get_custom_data_layers_count() const;
+ void add_custom_data_layer(int p_index = -1);
+ void move_custom_data_layer(int p_from_index, int p_to_pos);
+ void remove_custom_data_layer(int p_index);
int get_custom_data_layer_by_name(String p_value) const;
void set_custom_data_name(int p_layer_id, String p_value);
String get_custom_data_name(int p_layer_id) const;
@@ -397,6 +411,24 @@ public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set);
virtual void notify_tile_data_properties_should_change(){};
+ virtual void add_occlusion_layer(int p_index){};
+ virtual void move_occlusion_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_occlusion_layer(int p_index){};
+ virtual void add_physics_layer(int p_index){};
+ virtual void move_physics_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_physics_layer(int p_index){};
+ virtual void add_terrain_set(int p_index){};
+ virtual void move_terrain_set(int p_from_index, int p_to_pos){};
+ virtual void remove_terrain_set(int p_index){};
+ virtual void add_terrain(int p_terrain_set, int p_index){};
+ virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos){};
+ virtual void remove_terrain(int p_terrain_set, int p_index){};
+ virtual void add_navigation_layer(int p_index){};
+ virtual void move_navigation_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_navigation_layer(int p_index){};
+ virtual void add_custom_data_layer(int p_index){};
+ virtual void move_custom_data_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_custom_data_layer(int p_index){};
virtual void reset_state() override{};
// Tiles.
@@ -448,6 +480,24 @@ public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set) override;
virtual void notify_tile_data_properties_should_change() override;
+ virtual void add_occlusion_layer(int p_index) override;
+ virtual void move_occlusion_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_occlusion_layer(int p_index) override;
+ virtual void add_physics_layer(int p_index) override;
+ virtual void move_physics_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_physics_layer(int p_index) override;
+ virtual void add_terrain_set(int p_index) override;
+ virtual void move_terrain_set(int p_from_index, int p_to_pos) override;
+ virtual void remove_terrain_set(int p_index) override;
+ virtual void add_terrain(int p_terrain_set, int p_index) override;
+ virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) override;
+ virtual void remove_terrain(int p_terrain_set, int p_index) override;
+ virtual void add_navigation_layer(int p_index) override;
+ virtual void move_navigation_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_navigation_layer(int p_index) override;
+ virtual void add_custom_data_layer(int p_index) override;
+ virtual void move_custom_data_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_custom_data_layer(int p_index) override;
virtual void reset_state() override;
// Base properties.
@@ -528,7 +578,7 @@ public:
int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
- // Scenes sccessors. Lot are similar to "Alternative tiles".
+ // Scenes accessors. Lot are similar to "Alternative tiles".
int get_scene_tiles_count() { return get_alternative_tiles_count(Vector2i()); }
int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); };
bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); };
@@ -597,6 +647,24 @@ public:
// Not exposed.
void set_tile_set(const TileSet *p_tile_set);
void notify_tile_data_properties_should_change();
+ void add_occlusion_layer(int p_index);
+ void move_occlusion_layer(int p_from_index, int p_to_pos);
+ void remove_occlusion_layer(int p_index);
+ void add_physics_layer(int p_index);
+ void move_physics_layer(int p_from_index, int p_to_pos);
+ void remove_physics_layer(int p_index);
+ void add_terrain_set(int p_index);
+ void move_terrain_set(int p_from_index, int p_to_pos);
+ void remove_terrain_set(int p_index);
+ void add_terrain(int p_terrain_set, int p_index);
+ void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
+ void remove_terrain(int p_terrain_set, int p_index);
+ void add_navigation_layer(int p_index);
+ void move_navigation_layer(int p_from_index, int p_to_pos);
+ void remove_navigation_layer(int p_index);
+ void add_custom_data_layer(int p_index);
+ void move_custom_data_layer(int p_from_index, int p_to_pos);
+ void remove_custom_data_layer(int p_index);
void reset_state();
void set_allow_transform(bool p_allow_transform);
bool is_allowing_transform() const;