diff --git a/addons/dialogic/Core/DialogicGameHandler.gd b/addons/dialogic/Core/DialogicGameHandler.gd
index a6e6e0303..30ba65255 100644
--- a/addons/dialogic/Core/DialogicGameHandler.gd
+++ b/addons/dialogic/Core/DialogicGameHandler.gd
@@ -138,6 +138,9 @@ var Portraits := preload("res://addons/dialogic/Modules/Character/subsystem_port
var Save := preload("res://addons/dialogic/Modules/Save/subsystem_save.gd").new():
get: return get_subsystem("Save")
+var ScreenShake := preload("res://addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd").new():
+ get: return get_subsystem("ScreenShake")
+
var Settings := preload("res://addons/dialogic/Modules/Settings/subsystem_settings.gd").new():
get: return get_subsystem("Settings")
diff --git a/addons/dialogic/Modules/Clear/clear_shake.svg b/addons/dialogic/Modules/Clear/clear_shake.svg
new file mode 100644
index 000000000..e413ff7e1
--- /dev/null
+++ b/addons/dialogic/Modules/Clear/clear_shake.svg
@@ -0,0 +1,76 @@
+
+
diff --git a/addons/dialogic/Modules/Clear/clear_shake.svg.import b/addons/dialogic/Modules/Clear/clear_shake.svg.import
new file mode 100644
index 000000000..1ec3d43f5
--- /dev/null
+++ b/addons/dialogic/Modules/Clear/clear_shake.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bw5d6gq4xj4fc"
+path="res://.godot/imported/clear_shake.svg-7755f0aa1227e0b25e8ad6fa6ac6cbe5.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/dialogic/Modules/Clear/clear_shake.svg"
+dest_files=["res://.godot/imported/clear_shake.svg-7755f0aa1227e0b25e8ad6fa6ac6cbe5.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/dialogic/Modules/Clear/event_clear.gd b/addons/dialogic/Modules/Clear/event_clear.gd
index 9b1b3d7aa..c59248ff6 100644
--- a/addons/dialogic/Modules/Clear/event_clear.gd
+++ b/addons/dialogic/Modules/Clear/event_clear.gd
@@ -12,6 +12,7 @@ var clear_textbox := true
var clear_portraits := true
var clear_style := true
var clear_music := true
+var clear_screen_shake := true
var clear_portrait_positions := true
var clear_background := true
@@ -49,6 +50,11 @@ func _execute() -> void:
dialogic.Audio.stop_all_channels(final_time)
if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout
+ if clear_screen_shake and dialogic.has_subsystem('ScreenShake'):
+ dialogic.ScreenShake.update_shake_x(0.0, 0.0, final_time)
+ dialogic.ScreenShake.update_shake_y(0.0, 0.0, final_time)
+ if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout
+
if clear_style and dialogic.has_subsystem('Styles'):
dialogic.Styles.change_style()
@@ -88,6 +94,7 @@ func get_shortcode_parameters() -> Dictionary:
"text" : {"property": "clear_textbox", "default": true},
"portraits" : {"property": "clear_portraits", "default": true},
"music" : {"property": "clear_music", "default": true},
+ "shake" : {"property": "clear_screen_shake", "default": true},
"background": {"property": "clear_background", "default": true},
"positions" : {"property": "clear_portrait_positions", "default": true},
"style" : {"property": "clear_style", "default": true},
@@ -110,5 +117,6 @@ func build_event_editor() -> void:
add_body_edit('clear_portraits', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_characters.svg"), 'tooltip':'Clear Portraits'})
add_body_edit('clear_background', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_background.svg"), 'tooltip':'Clear Background'})
add_body_edit('clear_music', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_music.svg"), 'tooltip':'Clear Audio'})
+ add_body_edit('clear_screen_shake', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_shake.svg"), 'tooltip':'Clear Screen Shake'})
add_body_edit('clear_style', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_style.svg"), 'tooltip':'Clear Style'})
add_body_edit('clear_portrait_positions', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_positions.svg"), 'tooltip':'Clear Portrait Positions'})
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg
new file mode 100644
index 000000000..4fe7970d3
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg
@@ -0,0 +1,58 @@
+
+
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg.import b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg.import
new file mode 100644
index 000000000..8b76f35dc
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dp28d3waw6sh8"
+path="res://.godot/imported/icon.svg-c4786c180265dc13234ef8ceb5240800.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg"
+dest_files=["res://.godot/imported/icon.svg-c4786c180265dc13234ef8ceb5240800.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/part_config.cfg b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/part_config.cfg
new file mode 100644
index 000000000..275f8d95c
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/part_config.cfg
@@ -0,0 +1,7 @@
+[style]
+type = "Layer"
+name = "Screen Shake Layer"
+author = "SaliaNifo"
+description = "A layer for applying screen shake to the layers below it."
+scene = "screen_shake_layer.tscn"
+icon = "icon.svg"
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png
new file mode 100644
index 000000000..488729ba8
Binary files /dev/null and b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png differ
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png.import b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png.import
new file mode 100644
index 000000000..94989c8df
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cwhucf14pvkfs"
+path="res://.godot/imported/preview.png-f4b273872c0dd56a22386a20ccb82f99.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png"
+dest_files=["res://.godot/imported/preview.png-f4b273872c0dd56a22386a20ccb82f99.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd
new file mode 100644
index 000000000..9162300ec
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd
@@ -0,0 +1,2 @@
+@tool
+extends DialogicLayoutLayer
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd.uid b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd.uid
new file mode 100644
index 000000000..8a53a3d7c
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd.uid
@@ -0,0 +1 @@
+uid://pf00w14gv64m
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader
new file mode 100644
index 000000000..74584f028
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader
@@ -0,0 +1,35 @@
+shader_type canvas_item;
+
+uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, repeat_disable, filter_nearest;
+
+uniform float amplitude_x : hint_range(0.0, 1.0) = 0.0;
+uniform float frequency_x = 0.0;
+uniform float phase_x = 0.0;
+uniform float time_x = 0.0;
+
+uniform float amplitude_y : hint_range(0.0, 1.0) = 0.0;
+uniform float frequency_y = 0.0;
+uniform float phase_y = 0.0;
+uniform float time_y = 0.0;
+
+uniform vec3 clear_color : source_color = vec3(0.0, 0.0, 0.0);
+
+void fragment() {
+ vec2 uv = SCREEN_UV;
+
+ float angular_frequency_x = 2.0 * PI * frequency_x;
+ float displacement_x = amplitude_x * sin(angular_frequency_x * (time_x + phase_x));
+
+ float angular_frequency_y = 2.0 * PI * frequency_y;
+ float displacement_y = amplitude_y * sin(angular_frequency_y * (time_y + phase_y));
+
+ uv.x += displacement_x / 1.0;
+ uv.y += displacement_y / 1.0;
+
+ if (uv.x > 1.0 || uv.y > 1.0 || uv.x < 0.0 || uv.y < 0.0) {
+ COLOR.rgb = clear_color;
+ COLOR.a = 1.0;
+ } else {
+ COLOR = texture(SCREEN_TEXTURE, uv);
+ }
+}
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader.uid b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader.uid
new file mode 100644
index 000000000..8b7caabe7
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader.uid
@@ -0,0 +1 @@
+uid://ckbeep0lno2h1
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn
new file mode 100644
index 000000000..5ad82033f
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn
@@ -0,0 +1,40 @@
+[gd_scene load_steps=5 format=3 uid="uid://cdvx2jejg8xln"]
+
+[ext_resource type="Script" uid="uid://pf00w14gv64m" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd" id="1_l682i"]
+[ext_resource type="Shader" uid="uid://ckbeep0lno2h1" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader" id="2_7h78g"]
+[ext_resource type="Script" uid="uid://bb1ano1eqhunf" path="res://addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd" id="3_uqmkn"]
+
+[sub_resource type="ShaderMaterial" id="ShaderMaterial_4u8q6"]
+shader = ExtResource("2_7h78g")
+shader_parameter/amplitude_x = 0.0
+shader_parameter/frequency_x = 0.0
+shader_parameter/phase_x = 0.0
+shader_parameter/time_x = 0.0
+shader_parameter/amplitude_y = 0.0
+shader_parameter/frequency_y = 0.0
+shader_parameter/phase_y = 0.0
+shader_parameter/time_y = 0.0
+shader_parameter/clear_color = Color(0, 0, 0, 1)
+
+[node name="ScreenShakeLayer" type="Control"]
+layout_direction = 2
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_l682i")
+
+[node name="BackBufferCopy" type="BackBufferCopy" parent="."]
+copy_mode = 2
+
+[node name="DialogicNode_ScreenShaker" type="ColorRect" parent="."]
+material = SubResource("ShaderMaterial_4u8q6")
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("3_uqmkn")
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres b/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres
index ce61f628c..45a98d82e 100644
--- a/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres
@@ -1,4 +1,4 @@
-[gd_resource type="Resource" script_class="DialogicStyle" load_steps=18 format=3 uid="uid://dgkmuyvy5qc35"]
+[gd_resource type="Resource" script_class="DialogicStyle" load_steps=20 format=3 uid="uid://dgkmuyvy5qc35"]
[ext_resource type="PackedScene" uid="uid://c1k5m0w3r40xf" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_FullBackground/full_background_layer.tscn" id="1_sde84"]
[ext_resource type="Script" uid="uid://bwg6yncmh2cml" path="res://addons/dialogic/Resources/dialogic_style_layer.gd" id="2_i34tx"]
@@ -8,6 +8,7 @@
[ext_resource type="PackedScene" uid="uid://dhk6j6eb6e3q" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.tscn" id="6_36eid"]
[ext_resource type="PackedScene" uid="uid://cvgf4c6gg0tsy" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer.tscn" id="7_hx5el"]
[ext_resource type="PackedScene" uid="uid://lx24i8fl6uo" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.tscn" id="8_00chv"]
+[ext_resource type="PackedScene" uid="uid://cdvx2jejg8xln" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn" id="9_nxqau"]
[ext_resource type="Script" uid="uid://dv08k6ljua6fm" path="res://addons/dialogic/Resources/dialogic_style.gd" id="9_sdr6x"]
[sub_resource type="Resource" id="Resource_1cyj6"]
@@ -49,10 +50,15 @@ script = ExtResource("2_i34tx")
scene = ExtResource("8_00chv")
overrides = {}
+[sub_resource type="Resource" id="Resource_sw2nc"]
+script = ExtResource("2_i34tx")
+scene = ExtResource("9_nxqau")
+overrides = {}
+
[resource]
script = ExtResource("9_sdr6x")
name = "Speaker Textbox Style"
-layer_list = Array[String](["10", "11", "12", "13", "14", "15", "16"])
+layer_list = Array[String](["10", "17", "11", "12", "13", "14", "15", "16"])
layer_info = {
"": SubResource("Resource_1cyj6"),
"10": SubResource("Resource_jk75o"),
@@ -61,7 +67,8 @@ layer_info = {
"13": SubResource("Resource_axty6"),
"14": SubResource("Resource_xh5pc"),
"15": SubResource("Resource_ytmk0"),
-"16": SubResource("Resource_yjxtw")
+"16": SubResource("Resource_yjxtw"),
+"17": SubResource("Resource_sw2nc")
}
base_overrides = {}
layers = Array[ExtResource("2_i34tx")]([])
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres b/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres
index 673fb886f..68e96297e 100644
--- a/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres
@@ -1,4 +1,4 @@
-[gd_resource type="Resource" script_class="DialogicStyle" load_steps=20 format=3 uid="uid://8t1mr302tmqs"]
+[gd_resource type="Resource" script_class="DialogicStyle" load_steps=22 format=3 uid="uid://8t1mr302tmqs"]
[ext_resource type="Script" uid="uid://dv08k6ljua6fm" path="res://addons/dialogic/Resources/dialogic_style.gd" id="1_mvpc0"]
[ext_resource type="Script" uid="uid://bwg6yncmh2cml" path="res://addons/dialogic/Resources/dialogic_style_layer.gd" id="2_3b8ue"]
@@ -9,6 +9,7 @@
[ext_resource type="PackedScene" uid="uid://dsbwnp5hegnu3" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Glossary/glossary_popup_layer.tscn" id="7_vw5f4"]
[ext_resource type="PackedScene" uid="uid://dhk6j6eb6e3q" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.tscn" id="8_tc6v1"]
[ext_resource type="PackedScene" uid="uid://cvgf4c6gg0tsy" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer.tscn" id="9_tufw5"]
+[ext_resource type="PackedScene" uid="uid://cdvx2jejg8xln" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn" id="10_0qncn"]
[ext_resource type="PackedScene" uid="uid://lx24i8fl6uo" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.tscn" id="10_8v8jj"]
[sub_resource type="Resource" id="Resource_3dixn"]
@@ -55,10 +56,15 @@ script = ExtResource("2_3b8ue")
scene = ExtResource("10_8v8jj")
overrides = {}
+[sub_resource type="Resource" id="Resource_tnh7v"]
+script = ExtResource("2_3b8ue")
+scene = ExtResource("10_0qncn")
+overrides = {}
+
[resource]
script = ExtResource("1_mvpc0")
name = "Visual Novel Style"
-layer_list = Array[String](["10", "11", "12", "13", "14", "15", "16", "17"])
+layer_list = Array[String](["10", "11", "18", "12", "13", "14", "15", "16", "17"])
layer_info = {
"": SubResource("Resource_3dixn"),
"10": SubResource("Resource_gen8e"),
@@ -68,8 +74,9 @@ layer_info = {
"14": SubResource("Resource_pvwog"),
"15": SubResource("Resource_spe5r"),
"16": SubResource("Resource_jf1ol"),
-"17": SubResource("Resource_gs5pl")
+"17": SubResource("Resource_gs5pl"),
+"18": SubResource("Resource_tnh7v")
}
base_overrides = {}
layers = Array[ExtResource("2_3b8ue")]([])
-metadata/_latest_layer = "17"
+metadata/_latest_layer = "18"
diff --git a/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd b/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd
new file mode 100644
index 000000000..06ae9ef32
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd
@@ -0,0 +1,111 @@
+@tool
+extends DialogicEvent
+class_name DialogicScreenShakeEvent
+
+## Event to shake the screen below the screen shake layer.
+
+### Settings
+
+enum Direction {HORIZONTAL, VERTICAL}
+
+## The strength of the screen shake.
+var amplitude: float = 0.1
+## The speed of the screen shake.
+var frequency: float = 1.0
+## The direction of the screen shake.
+var direction := Direction.HORIZONTAL
+## The time the fade animation will take. Leave at 0 for instant change.
+var fade: float = 0.0
+## The time the screen should shake for (excludes fade time).
+## Leave at 0 to keep shaking until the next screen shake event.
+var duration: float = 10.0
+## If true, the event will wait for completion before continuing
+## (only if duration is greater than 0.0 or fade greater than 0.0 and amplitude is 0.0).
+var wait := false
+
+#region EXECUTION
+################################################################################
+
+func _execute() -> void:
+ if direction == Direction.HORIZONTAL:
+ dialogic.ScreenShake.update_shake_x(amplitude, frequency, fade, duration)
+
+ if wait and (duration > 0.0 or (fade > 0.0 and amplitude == 0.0)):
+ await dialogic.ScreenShake.shake_horizontal_finished
+ else:
+ dialogic.ScreenShake.update_shake_y(amplitude, frequency, fade, duration)
+
+ if wait and (duration > 0.0 or (fade > 0.0 and amplitude == 0.0)):
+ await dialogic.ScreenShake.shake_vertical_finished
+
+ finish()
+
+#endregion
+
+#region INITIALIZE
+################################################################################
+
+func _init() -> void:
+ event_name = "Shake Screen"
+ set_default_color('Color8')
+ event_category = "Visuals"
+ event_sorting_index = 2
+
+#endregion
+
+#region SAVING/LOADING
+################################################################################
+
+func get_shortcode() -> String:
+ return "shake_screen"
+
+
+func get_shortcode_parameters() -> Dictionary:
+ return {
+ "amplitude" : {"property": "amplitude", "default": 0.1},
+ "frequency" : {"property": "frequency", "default": 1.0},
+ "direction" : {"property": "direction", "default": Direction.HORIZONTAL},
+ "fade" : {"property": "fade", "default": 0.0},
+ "duration" : {"property": "duration", "default": 10.0},
+ "wait" : {"property": "wait", "default": false},
+ }
+
+#endregion
+
+#region EDITOR REPRESENTATION
+################################################################################
+
+func build_event_editor() -> void:
+ add_header_edit("direction", ValueType.FIXED_OPTIONS, {
+ 'left_text': 'Shake screen',
+ 'options': [
+ {
+ 'label': 'Horizontally',
+ 'value': Direction.HORIZONTAL,
+ 'icon': ["MirrorX", "EditorIcons"]
+ },
+ {
+ 'label': 'Vertically',
+ 'value': Direction.VERTICAL,
+ 'icon': ["MirrorY", "EditorIcons"]
+ }
+ ]})
+
+ add_header_edit("amplitude", ValueType.NUMBER, {
+ 'left_text': 'with amplitude:',
+ 'min': 0.0,
+ 'max': 1.0,
+ 'step': 0.01,
+ })
+
+ add_header_edit("frequency", ValueType.NUMBER, {
+ 'left_text': 'and frequency:',
+ 'min': 0.1,
+ 'suffix': 'hz',
+ })
+
+ add_body_edit("fade", ValueType.NUMBER, {'left_text':'Fade time:'})
+ add_body_edit("duration", ValueType.NUMBER, {'left_text': 'Duration:'}, 'amplitude > 0.0')
+ add_body_edit("wait", ValueType.BOOL, {'left_text':'Wait for completion:'}, '(fade > 0.0 and amplitude == 0.0) or duration > 0.0')
+
+#endregion
diff --git a/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd.uid b/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd.uid
new file mode 100644
index 000000000..db350ccba
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd.uid
@@ -0,0 +1 @@
+uid://chapa5hltp6ac
diff --git a/addons/dialogic/Modules/ScreenShake/icon.png b/addons/dialogic/Modules/ScreenShake/icon.png
new file mode 100644
index 000000000..14b815a9d
Binary files /dev/null and b/addons/dialogic/Modules/ScreenShake/icon.png differ
diff --git a/addons/dialogic/Modules/ScreenShake/icon.png.import b/addons/dialogic/Modules/ScreenShake/icon.png.import
new file mode 100644
index 000000000..eb641d720
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/icon.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://m74xxddbt4sj"
+path="res://.godot/imported/icon.png-27bf81e8d508931fcff8d7fc07586e55.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/dialogic/Modules/ScreenShake/icon.png"
+dest_files=["res://.godot/imported/icon.png-27bf81e8d508931fcff8d7fc07586e55.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/dialogic/Modules/ScreenShake/index.gd b/addons/dialogic/Modules/ScreenShake/index.gd
new file mode 100644
index 000000000..7294e40f3
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/index.gd
@@ -0,0 +1,8 @@
+@tool
+extends DialogicIndexer
+
+func _get_events() -> Array:
+ return [this_folder.path_join('event_screen_shake.gd')]
+
+func _get_subsystems() -> Array:
+ return [{'name':'ScreenShake', 'script':this_folder.path_join('subsystem_screen_shake.gd')}]
diff --git a/addons/dialogic/Modules/ScreenShake/index.gd.uid b/addons/dialogic/Modules/ScreenShake/index.gd.uid
new file mode 100644
index 000000000..be34217ac
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/index.gd.uid
@@ -0,0 +1 @@
+uid://cpq1bgdnqrmth
diff --git a/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd b/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd
new file mode 100644
index 000000000..09a77dcaf
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd
@@ -0,0 +1,68 @@
+class_name DialogicNode_ScreenShaker
+extends ColorRect
+
+
+const ROLLOVER_TIME = 3600.0
+
+@onready var time_x: float = material.get_shader_parameter('time_x')
+@onready var phase_x: float = material.get_shader_parameter('phase_x')
+@onready var last_frequency_x: float = material.get_shader_parameter('frequency_x')
+
+@onready var time_y: float = material.get_shader_parameter('time_y')
+@onready var phase_y: float = material.get_shader_parameter('phase_y')
+@onready var last_frequency_y: float = material.get_shader_parameter('frequency_y')
+
+
+func _ready() -> void:
+ add_to_group('dialogic_screen_shaker')
+
+ material.set_shader_parameter(
+ 'clear_color',
+ ProjectSettings.get_setting('rendering/environment/defaults/default_clear_color', Color.BLACK))
+
+
+func _process(delta: float) -> void:
+ if not DialogicUtil.autoload().paused:
+ time_x += delta
+ time_x = fposmod(time_x, ROLLOVER_TIME)
+ material.set_shader_parameter('time_x', time_x)
+
+ time_y += delta
+ time_y = fposmod(time_y, ROLLOVER_TIME)
+ material.set_shader_parameter('time_y', time_y)
+
+
+func reset_x() -> void:
+ time_x = 0.0
+ phase_x = 0.0
+ last_frequency_x = 0.0
+
+ material.set_shader_parameter('time_x', 0.0)
+ material.set_shader_parameter('phase_x', 0.0)
+
+
+func reset_y() -> void:
+ time_y = 0.0
+ phase_y = 0.0
+ last_frequency_y = 0.0
+
+ material.set_shader_parameter('time_y', 0.0)
+ material.set_shader_parameter('phase_y', 0.0)
+
+
+func update_frequency_x(new_frequency: float) -> void:
+ if new_frequency != 0.0:
+ phase_x = (last_frequency_x * (time_x + phase_x) / new_frequency) - time_x
+ last_frequency_x = new_frequency
+
+ material.set_shader_parameter('frequency_x', new_frequency)
+ material.set_shader_parameter('phase_x', phase_x)
+
+
+func update_frequency_y(new_frequency: float) -> void:
+ if new_frequency != 0.0:
+ phase_y = (last_frequency_y * (time_y + phase_y) / new_frequency) - time_y
+ last_frequency_y = new_frequency
+
+ material.set_shader_parameter('frequency_y', new_frequency)
+ material.set_shader_parameter('phase_y', phase_y)
diff --git a/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd.uid b/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd.uid
new file mode 100644
index 000000000..88db6f40c
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd.uid
@@ -0,0 +1 @@
+uid://bb1ano1eqhunf
diff --git a/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd b/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd
new file mode 100644
index 000000000..d2cc22185
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd
@@ -0,0 +1,221 @@
+extends DialogicSubsystem
+
+## Subsystem that manages screen shaking.
+
+
+## Whenever a horizontal screen shake is started, this signal is emitted and
+## contains a dictionary with the following keys: [br]
+## [br]
+## Key | Value Type | Value [br]
+## ------------ | ------------- | ----- [br]
+## `amplitude` | [type float] | The strength of the screen shake. [br]
+## `frequency` | [type float] | The speed of the screen shake. [br]
+## `fade` | [type float] | The time the fade animation will take. Leave at 0 for instant change. [br]
+## `duration` | [type float] | The time the screen should shake for (excludes fade time). [br]
+signal shake_horizontal_started(info: Dictionary)
+
+## Whenever a horizontal screen shake has finished, this signal is emitted.
+signal shake_horizontal_finished()
+
+## Whenever a vertical screen shake is started, this signal is emitted and
+## contains a dictionary with the following keys: [br]
+## [br]
+## Key | Value Type | Value [br]
+## ------------ | ------------- | ----- [br]
+## `amplitude` | [type float] | The strength of the screen shake. [br]
+## `frequency` | [type float] | The speed of the screen shake. [br]
+## `fade` | [type float] | The time the fade animation will take. Leave at 0 for instant change. [br]
+## `duration` | [type float] | The time the screen should shake for (excludes fade time). [br]
+signal shake_vertical_started(info: Dictionary)
+
+## Whenever a vertical screen shake has finished, this signal is emitted.
+signal shake_vertical_finished()
+
+#region STATE
+####################################################################################################
+
+func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR) -> void:
+ update_shake_x()
+ update_shake_y()
+
+
+func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void:
+ if load_flag == LoadFlags.ONLY_DNODES:
+ return
+ var info: Dictionary = dialogic.current_state_info.get('screen_shake', {'x':{},'y':{}})
+ if info['x'].is_empty() or info['x'].get('amplitude', 0.0) == 0.0:
+ update_shake_x()
+ else:
+ update_shake_x(info['x'].amplitude, info['x'].frequency, info['x'].fade, info['x'].duration)
+ if info['y'].is_empty() or info['y'].get('amplitude', 0.0) == 0.0:
+ update_shake_y()
+ else:
+ update_shake_y(info['y'].amplitude, info['y'].frequency, info['y'].fade, info['y'].duration)
+
+#endregion
+
+#region MAIN METHODS
+####################################################################################################
+
+func update_shake_x(amplitude: float = 0.0, frequency: float = 0.0, fade: float = 0.0, duration: float = 0.0) -> void:
+ var previous_settings = dialogic.current_state_info.get('screen_shake', {'x':{},'y':{}})
+
+ if not dialogic.current_state_info.has('screen_shake'):
+ dialogic.current_state_info['screen_shake'] = {'x':{},'y':{}}
+
+ dialogic.current_state_info['screen_shake']['x'] = {
+ 'amplitude': amplitude,
+ 'frequency': frequency,
+ 'fade': fade,
+ 'duration': duration,
+ }
+
+ shake_horizontal_started.emit(dialogic.current_state_info['screen_shake']['x'])
+
+ var screen_shaker := get_screen_shaker()
+
+ if not screen_shaker:
+ return
+
+ if previous_settings['x'].is_empty():
+ screen_shaker.reset_x()
+
+ if fade > 0.0:
+ var current_amplitude := screen_shaker.material.get_shader_parameter('amplitude_x') as float
+ var current_frequency := screen_shaker.material.get_shader_parameter('frequency_x') as float
+ var tween := get_tree().create_tween()
+ tween.tween_method(_tween_amplitude_x, current_amplitude, amplitude, fade)
+ tween.set_parallel()
+ tween.tween_method(_tween_frequency_x, current_frequency, frequency, fade)
+ await tween.finished
+ else:
+ screen_shaker.material.set_shader_parameter('amplitude_x', amplitude)
+ screen_shaker.update_frequency_x(frequency)
+
+ if duration > 0.0 and amplitude > 0.0:
+ await get_tree().create_timer(duration).timeout
+ elif amplitude == 0.0:
+ dialogic.current_state_info['screen_shake']['x'] = {}
+ shake_horizontal_finished.emit()
+ return
+ else:
+ return
+
+ if fade > 0.0:
+ var current_amplitude := screen_shaker.material.get_shader_parameter('amplitude_x') as float
+ var current_frequency := screen_shaker.material.get_shader_parameter('frequency_x') as float
+ var tween := get_tree().create_tween()
+ tween.tween_method(_tween_amplitude_x, current_amplitude, 0.0, fade)
+ tween.set_parallel()
+ tween.tween_method(_tween_frequency_x, current_frequency, 0.0, fade)
+ await tween.finished
+ else:
+ screen_shaker.material.set_shader_parameter('amplitude_x', 0.0)
+ screen_shaker.update_frequency_x(frequency)
+
+ dialogic.current_state_info['screen_shake']['x'] = {}
+ shake_horizontal_finished.emit()
+
+
+func update_shake_y(amplitude: float = 0.0, frequency: float = 0.0, fade: float = 0.0, duration: float = 0.0) -> void:
+ var previous_settings = dialogic.current_state_info.get('screen_shake', {'x':{},'y':{}})
+ dialogic.current_state_info['screen_shake']['y'] = {
+ 'amplitude': amplitude,
+ 'frequency': frequency,
+ 'fade': fade,
+ 'duration': duration,
+ }
+
+ shake_vertical_started.emit(dialogic.current_state_info['screen_shake']['y'])
+
+ var screen_shaker := get_screen_shaker()
+
+ if not screen_shaker:
+ return
+
+ if previous_settings['y'].is_empty():
+ screen_shaker.reset_y()
+
+ if fade > 0.0:
+ var current_amplitude := screen_shaker.material.get_shader_parameter('amplitude_y') as float
+ var current_frequency := screen_shaker.material.get_shader_parameter('frequency_y') as float
+ var tween := get_tree().create_tween()
+ tween.tween_method(_tween_amplitude_y, current_amplitude, amplitude, fade)
+ tween.set_parallel()
+ tween.tween_method(_tween_frequency_y, current_frequency, frequency, fade)
+ await tween.finished
+ else:
+ screen_shaker.material.set_shader_parameter('amplitude_y', amplitude)
+ screen_shaker.update_frequency_y(frequency)
+
+ if duration > 0.0 and amplitude > 0.0:
+ await get_tree().create_timer(duration).timeout
+ elif amplitude == 0.0:
+ dialogic.current_state_info['screen_shake']['y'] = {}
+ shake_vertical_finished.emit()
+ return
+ else:
+ return
+
+ if fade > 0.0:
+ var current_amplitude := screen_shaker.material.get_shader_parameter('amplitude_y') as float
+ var current_frequency := screen_shaker.material.get_shader_parameter('frequency_y') as float
+ var tween := get_tree().create_tween()
+ tween.tween_method(_tween_amplitude_y, current_amplitude, 0.0, fade)
+ tween.set_parallel()
+ tween.tween_method(_tween_frequency_y, current_frequency, 0.0, fade)
+ await tween.finished
+ else:
+ screen_shaker.material.set_shader_parameter('amplitude_y', 0.0)
+ screen_shaker.update_frequency_y(frequency)
+
+ dialogic.current_state_info['screen_shake']['y'] = {}
+ shake_vertical_finished.emit()
+
+#endregion
+
+func _tween_amplitude_x(value: float) -> void:
+ var screen_shaker := get_screen_shaker()
+
+ if not screen_shaker:
+ return
+
+ screen_shaker.material.set_shader_parameter('amplitude_x', value)
+
+
+func _tween_amplitude_y(value: float) -> void:
+ var screen_shaker := get_screen_shaker()
+
+ if not screen_shaker:
+ return
+
+ screen_shaker.material.set_shader_parameter('amplitude_y', value)
+
+
+func _tween_frequency_x(value: float) -> void:
+ var screen_shaker := get_screen_shaker()
+
+ if not screen_shaker:
+ return
+
+ screen_shaker.update_frequency_x(value)
+
+
+func _tween_frequency_y(value: float) -> void:
+ var screen_shaker := get_screen_shaker()
+
+ if not screen_shaker:
+ return
+
+ screen_shaker.update_frequency_y(value)
+
+
+func get_screen_shaker() -> DialogicNode_ScreenShaker:
+ var screen_shaker: DialogicNode_ScreenShaker = null
+
+ if dialogic.has_subsystem('Styles'):
+ screen_shaker = dialogic.Styles.get_first_node_in_layout('dialogic_screen_shaker')
+ else:
+ screen_shaker = get_tree().get_first_node_in_group('dialogic_screen_shaker')
+
+ return screen_shaker
diff --git a/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd.uid b/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd.uid
new file mode 100644
index 000000000..f0dddb392
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd.uid
@@ -0,0 +1 @@
+uid://l5ry6a5mdtxn