diff --git a/assets/effects/pixelDissolve.gdshader b/assets/effects/pixelDissolve.gdshader new file mode 100644 index 0000000..9f2f6ce --- /dev/null +++ b/assets/effects/pixelDissolve.gdshader @@ -0,0 +1,45 @@ +shader_type canvas_item; + +uniform sampler2D noise_tex_normal; +uniform sampler2D noise_tex; +uniform float progress : hint_range(0.0, 1.0); +uniform float scale : hint_range(0.0, 100.0); +uniform float strength = 1.0; + +// If your sprite doesn't have enough space and the explosion gets clipped, +// you can uncomment this and adjust the scale +void vertex() { + VERTEX *= scale; + + UV *= scale; + UV -= (scale - 1.0) / 2.0; +} + +void fragment() { + vec2 direction = texture(noise_tex_normal, UV).xy; // We're using normal map as direction + direction -= 0.5; // Since our normal map is a texture, it ranges from 0.0 to 1.0... + direction *= 2.0; // ...so we're going to make it range from -1.0 to 1.0. + direction = direction * strength * progress; + + // UV for exploded texture + vec2 tex_size = 1.0 / TEXTURE_PIXEL_SIZE; // Real texture size in pixels + vec2 uv = floor(UV * tex_size) / (tex_size - 1.0); // Pixelate UV to snap pixels + uv = uv - direction; // Distort UV + + // Texture with exploded UV + vec4 tex = texture(TEXTURE, uv); + + // Dissolve alpha + float dissolve = texture(noise_tex, UV).x; + dissolve = step(progress, dissolve); + tex.a *= dissolve; + + // Border (in case the edge of your sprite stretches, otherwise you can remove this block) + vec2 border_uv = uv * 2.0 - 1.0; + border_uv = clamp(abs(border_uv), 0.0, 1.0); + float border = max(border_uv.x, border_uv.y); + border = ceil(1.0 - border); + tex.a *= border; + + COLOR = tex; +} \ No newline at end of file diff --git a/assets/effects/pixelDissolve.tres b/assets/effects/pixelDissolve.tres new file mode 100644 index 0000000..1879bd4 --- /dev/null +++ b/assets/effects/pixelDissolve.tres @@ -0,0 +1,13 @@ +[gd_resource type="ShaderMaterial" load_steps=3 format=2] + +[ext_resource path="res://assets/effects/pixelDissolve.gdshader" type="Shader" id=1] +[ext_resource path="res://assets/effects/noise.png" type="Texture" id=2] + +[resource] +resource_local_to_scene = true +shader = ExtResource( 1 ) +shader_param/progress = 0.0 +shader_param/scale = 1.0 +shader_param/strength = 1.0 +shader_param/noise_tex_normal = ExtResource( 2 ) +shader_param/noise_tex = ExtResource( 2 ) diff --git a/project.godot b/project.godot index f319513..d8dad77 100644 --- a/project.godot +++ b/project.godot @@ -261,6 +261,7 @@ ui_click={ 2d_physics/layer_7="finnemajig" 2d_physics/layer_8="dropThrough" 2d_physics/layer_9="food" +2d_physics/layer_10="dynamicUI" [physics] @@ -275,6 +276,7 @@ quality/intended_usage/framebuffer_allocation=0 quality/intended_usage/framebuffer_allocation.mobile=0 2d/snapping/use_gpu_pixel_snap=true threads/thread_model=2 +quality/depth/hdr=false environment/default_environment="res://default_env.tres" quality/2d/use_pixel_snap=true environment/2d/use_nvidia_rect_flicker_workaround=true diff --git a/src/Actors/Blobby/Blobby.tscn b/src/Actors/Blobby/Blobby.tscn index 446274c..8d1b14a 100644 --- a/src/Actors/Blobby/Blobby.tscn +++ b/src/Actors/Blobby/Blobby.tscn @@ -3201,6 +3201,7 @@ tracks/10/keys = { [sub_resource type="Animation" id=9] length = 0.5 loop = true +step = 0.05 tracks/0/type = "value" tracks/0/path = NodePath(".:frame") tracks/0/interp = 1 @@ -3328,10 +3329,10 @@ tracks/10/loop_wrap = true tracks/10/imported = false tracks/10/enabled = true tracks/10/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), +"times": PoolRealArray( 0, 0.25, 0.5 ), +"transitions": PoolRealArray( 1, 1, 1 ), "update": 0, -"values": [ Vector2( 1, 1 ) ] +"values": [ Vector2( 1, 1 ), Vector2( 1, 1 ), Vector2( 1, 1 ) ] } [sub_resource type="Animation" id=99] @@ -3474,6 +3475,7 @@ tracks/10/keys = { [sub_resource type="Animation" id=73] length = 0.15 loop = true +step = 0.01 tracks/0/type = "value" tracks/0/path = NodePath(".:frame") tracks/0/interp = 1 @@ -3601,16 +3603,17 @@ tracks/10/loop_wrap = true tracks/10/imported = false tracks/10/enabled = true tracks/10/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), +"times": PoolRealArray( 0, 0.07 ), +"transitions": PoolRealArray( 1, 1 ), "update": 0, -"values": [ Vector2( 1, 1 ) ] +"values": [ Vector2( 1, 1 ), Vector2( 1.3, 1 ) ] } [sub_resource type="Animation" id=101] resource_name = "wallslideToJumpRight" length = 0.15 loop = true +step = 0.01 tracks/0/type = "value" tracks/0/path = NodePath(".:frame") tracks/0/interp = 1 @@ -3738,10 +3741,10 @@ tracks/10/loop_wrap = true tracks/10/imported = false tracks/10/enabled = true tracks/10/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), +"times": PoolRealArray( 0, 0.08 ), +"transitions": PoolRealArray( 1, 1 ), "update": 0, -"values": [ Vector2( -1, 1 ) ] +"values": [ Vector2( -1, 1 ), Vector2( -1.3, 1 ) ] } [sub_resource type="Animation" id=74] diff --git a/src/Actors/BlobbyCam.gd b/src/Actors/BlobbyCam.gd index 1d87db2..a5213cd 100644 --- a/src/Actors/BlobbyCam.gd +++ b/src/Actors/BlobbyCam.gd @@ -17,7 +17,7 @@ var target_offset: Vector2 = Vector2(0,0) export var camera_horizontal_shift = 60 export var offset_reset_seconds := 1 export var offset_adapt_seconds := 1 -export var offset_input_seconds := 0.618 +export var offset_input_seconds := 0.618 * 2 onready var levelState := $"%LevelState" onready var signalManager := $"%SignalManager" @@ -35,7 +35,8 @@ var texture = ImageTexture.new() var prev_pos: Vector2 var camera_state := "centered" -var screen_rect = Vector2(ProjectSettings.get_setting("display/window/size/width") * zoom.x, ProjectSettings.get_setting("display/window/size/height") * zoom.y ) +var screen_rect = Vector2() +var old_screen_rect = Vector2(ProjectSettings.get_setting("display/window/size/width") * zoom.x, ProjectSettings.get_setting("display/window/size/height") * zoom.y ) var screen_center = Vector2() var screen_bottom = Vector2() var screen_top = Vector2() @@ -45,206 +46,215 @@ var screen_right = Vector2() # Gets the camera limits from the tilemap of the level # Requires "TileMap" to be a sibling of blobby func _ready(): - _set_boundaries() - self.position = blobby.global_position - image.create(128, 2, false, Image.FORMAT_RGBAH) - # TODO Test Performance - _update_lighting_shader() - # TODO Trigger when needed - signalManager.connect("terminal_activated", self, "_on_SignalManager_terminal_activated") - signalManager.connect("player_died", self, "_death_cam") + _set_boundaries() + get_tree().get_root().connect("size_changed", self, "_set_boundaries") + self.position = blobby.global_position + image.create(128, 2, false, Image.FORMAT_RGBAH) + # TODO Test Performance + _update_lighting_shader() + # TODO Trigger when needed + signalManager.connect("terminal_activated", self, "_on_SignalManager_terminal_activated") + signalManager.connect("player_died", self, "_death_cam") func _on_SignalManager_terminal_activated(animation_number: int = 0): - get_node("LightAnimationPlayer").play("Pulsing") + get_node("LightAnimationPlayer").play("Pulsing") #func _draw(): # draw_line(Vector2((limit_left - position.x), screen_center.y), screen_left, Color(255, 0, 0), 1) -func _process(delta: float) -> void: +func _physics_process(delta: float) -> void: # update() - screen_center = (get_camera_screen_center() - position) - screen_bottom = screen_center + Vector2(0, screen_rect.y/2) - screen_top = screen_center - Vector2(0, screen_rect.y/2) - screen_left = screen_center - Vector2(screen_rect.x/2, 0) - screen_right = screen_center + Vector2(screen_rect.x/2, 0) - var was_adjusted := false - if(!levelState.is_dead): - was_adjusted = _adjust_offset(delta) + screen_center = (get_camera_screen_center() - position) + screen_bottom = screen_center + Vector2(0, screen_rect.y/2) + screen_top = screen_center - Vector2(0, screen_rect.y/2) + screen_left = screen_center - Vector2(screen_rect.x/2, 0) + screen_right = screen_center + Vector2(screen_rect.x/2, 0) + var was_adjusted := false + if(!levelState.is_dead): + was_adjusted = _adjust_offset(delta) - if(anim_player.is_playing() || was_adjusted): - position = blobby.position - prev_pos = position - _update_lighting_shader() - return - var player_vel = (blobby.position - prev_pos)/delta - # TODO Take average of velocity here - if(abs(player_vel.x) >= blobby.max_velocity["walk"] * 0.97 - && (sign(player_vel.x) == sign(target_offset.x) || target_offset.x == 0)): - if(player_vel.x > 0): - right_move_time += delta - left_move_time = max(0, left_move_time - delta) - slow_time = max(0, slow_time - delta) - else: - left_move_time += delta - right_move_time = max(0, right_move_time - delta) - slow_time = max(0, slow_time - delta) - elif(abs(player_vel.x) <= blobby.max_velocity["walk"] * 0.9 - || sign(player_vel.x) != sign(target_offset.x) || target_offset.x == 0): - slow_time += delta - left_move_time = max(0, left_move_time - delta) - right_move_time = max(0, right_move_time - delta) + if(anim_player.is_playing() || was_adjusted): + position = blobby.position + prev_pos = position + _update_lighting_shader() + return + var player_vel = (blobby.position - prev_pos)/delta + # TODO Take average of velocity here + if(abs(player_vel.x) >= blobby.max_velocity["walk"] * 0.97 + && (sign(player_vel.x) == sign(target_offset.x) || target_offset.x == 0)): + if(player_vel.x > 0): + right_move_time += delta + left_move_time = max(0, left_move_time - delta) + slow_time = max(0, slow_time - delta) + else: + left_move_time += delta + right_move_time = max(0, right_move_time - delta) + slow_time = max(0, slow_time - delta) + elif(abs(player_vel.x) <= blobby.max_velocity["walk"] * 0.9 + || sign(player_vel.x) != sign(target_offset.x) || target_offset.x == 0): + slow_time += delta + left_move_time = max(0, left_move_time - delta) + right_move_time = max(0, right_move_time - delta) - _adapt_to_movement(player_vel) - if abs(player_vel.x) <= blobby.max_velocity["walk"] * 0.9: - _adapt_to_input(player_vel, delta) - position = blobby.position - prev_pos = position - _update_lighting_shader() + _adapt_to_movement(player_vel) + if abs(player_vel.x) <= blobby.max_velocity["walk"] * 0.9: + _adapt_to_input(player_vel, delta) + position = blobby.position + prev_pos = position + _update_lighting_shader() +# TODO This has to be redone when the screen is resized in any way +# Otherwise the boundaries will not be correct anymore func _set_boundaries(): - # This is ok, because it only happens on initialization - # But it is also quite fickle - var tilemap = get_node("./%TileMap") - # TODO: This goes wrong when overwriting old tiles with new sprites - # New pngs -> completely new tiles and rebuild map - var rect = tilemap.get_used_rect() - var cell_size = tilemap.cell_size - # TODO +2 is fixed for camera issue in adjust horizontal - limit_right = rect.end.x * cell_size.x - 8 - limit_left = rect.position.x * cell_size.x + 8 - limit_top = rect.position.y * cell_size.y + 8 - limit_bottom = rect.end.y * cell_size.y - 8 - original_limit_left = limit_left - original_limit_right = limit_right - original_limit_top = limit_top - original_limit_bottom = limit_bottom - var screen_size = get_viewport_rect() - var h_pixels = limit_right - limit_left - var v_pixels = limit_bottom - limit_top - # TODO: Fix that it can zoom both? - if screen_size.end.x * original_x_zoom - h_pixels > 0: - zoom.x = h_pixels / screen_size.end.x - zoom.y = zoom.x - if screen_size.end.y * original_y_zoom - v_pixels > 0: - zoom.y = v_pixels / screen_size.end.y - zoom.x = zoom.y + screen_rect = get_viewport_rect().size + screen_rect.x *= zoom.x + screen_rect.y *= zoom.y + original_x_zoom = zoom.x + original_y_zoom = zoom.y + # This is ok, because it only happens on initialization + # But it is also quite fickle + var tilemap = get_node("./%TileMap") + # TODO: This goes wrong when overwriting old tiles with new sprites + # New pngs -> completely new tiles and rebuild map + var rect = tilemap.get_used_rect() + var cell_size = tilemap.cell_size + # TODO is fixed for camera issue in adjust horizontal + limit_right = rect.end.x * cell_size.x - 6 + limit_left = rect.position.x * cell_size.x + 6 + limit_top = rect.position.y * cell_size.y + 6 + limit_bottom = rect.end.y * cell_size.y - 6 + original_limit_left = limit_left + original_limit_right = limit_right + original_limit_top = limit_top + original_limit_bottom = limit_bottom + var screen_size = get_viewport_rect() + var h_pixels = limit_right - limit_left + var v_pixels = limit_bottom - limit_top + # TODO: Fix that it can zoom both? + if screen_size.end.x * original_x_zoom - h_pixels > 0: + zoom.x = h_pixels / screen_size.end.x + zoom.y = zoom.x + if screen_size.end.y * original_y_zoom - v_pixels > 0: + zoom.y = v_pixels / screen_size.end.y + zoom.x = zoom.y # Smoothing the camera limits in godot ruins something func _adapt_to_movement(velocity: Vector2) -> void: - var offset_track - var center = get_camera_screen_center() - var left_edge_pos = center.x - screen_rect.x/2 + camera_horizontal_shift - var right_edge_pos = center.x + screen_rect.x/2 - camera_horizontal_shift - if(left_move_time >= offset_adapt_seconds && !anim_player.is_playing()): - left_move_time = 0 - target_offset.x = -camera_horizontal_shift - if(offset == target_offset): - return - offset_track = shiftLeft.find_track(".:offset") - shiftLeft.track_set_key_value(offset_track, 0, offset) - shiftLeft.track_set_key_value(offset_track, 1, target_offset) - camera_state = "shiftedLeft" - anim_player.play("shiftingLeft") - elif(right_move_time >= offset_adapt_seconds && !anim_player.is_playing()): - right_move_time = 0 - target_offset.x = camera_horizontal_shift - if(offset == target_offset): - return - offset_track = shiftRight.find_track(".:offset") - shiftRight.track_set_key_value(offset_track, 0, offset) - shiftRight.track_set_key_value(offset_track, 1, target_offset) - camera_state = "shiftedRight" - anim_player.play("shiftingRight") - elif(slow_time >= offset_reset_seconds && - !(Input.is_action_pressed("up") || Input.is_action_pressed("duck"))): - slow_time = 0 - target_offset.x = 0 - if(offset == target_offset): - return - if(left_edge_pos > limit_left && limit_right > right_edge_pos): - offset_track = shiftCenter.find_track(".:offset") - shiftCenter.track_set_key_value(offset_track, 0, offset) - shiftCenter.track_set_key_value(offset_track, 1, target_offset) - camera_state = "centered" - anim_player.play("shiftingCenter") - return + var offset_track + var center = get_camera_screen_center() + var left_edge_pos = center.x - screen_rect.x/2 + camera_horizontal_shift + var right_edge_pos = center.x + screen_rect.x/2 - camera_horizontal_shift + if(left_move_time >= offset_adapt_seconds && !anim_player.is_playing()): + left_move_time = 0 + target_offset.x = -camera_horizontal_shift + if(offset == target_offset): + return + offset_track = shiftLeft.find_track(".:offset") + shiftLeft.track_set_key_value(offset_track, 0, offset) + shiftLeft.track_set_key_value(offset_track, 1, target_offset) + camera_state = "shiftedLeft" + anim_player.play("shiftingLeft") + elif(right_move_time >= offset_adapt_seconds && !anim_player.is_playing()): + right_move_time = 0 + target_offset.x = camera_horizontal_shift + if(offset == target_offset): + return + offset_track = shiftRight.find_track(".:offset") + shiftRight.track_set_key_value(offset_track, 0, offset) + shiftRight.track_set_key_value(offset_track, 1, target_offset) + camera_state = "shiftedRight" + anim_player.play("shiftingRight") + elif(slow_time >= offset_reset_seconds && + !(Input.is_action_pressed("up") || Input.is_action_pressed("duck"))): + slow_time = 0 + target_offset.x = 0 + if(offset == target_offset): + return + if(left_edge_pos > limit_left && limit_right > right_edge_pos): + offset_track = shiftCenter.find_track(".:offset") + shiftCenter.track_set_key_value(offset_track, 0, offset) + shiftCenter.track_set_key_value(offset_track, 1, target_offset) + camera_state = "centered" + anim_player.play("shiftingCenter") + return func _adapt_to_input(velocity: Vector2, delta: float) -> void: - # TODO Den bug dass man damit durch die map gucken kann wenn man sich weiter bewegt - # lasse ich erstmal drin - if(velocity.length() > 20.0): - input_time = 0 - return - if(input_time < offset_input_seconds): - input_time += delta - return - if Input.is_action_pressed("duck"): - if(original_limit_bottom - position.y - 2 > screen_bottom.y && offset.y < 48): - offset.y += 1 - elif Input.is_action_pressed("up"): - if(original_limit_top - position.y + 2 < screen_top.y && offset.y > -48): - offset.y -= 1 + # TODO Den bug dass man damit durch die map gucken kann wenn man sich weiter bewegt + # lasse ich erstmal drin + if(velocity.length() > 20.0): + input_time = 0 + return + if(input_time < offset_input_seconds): + input_time += delta + return + if Input.is_action_pressed("duck"): + if(original_limit_bottom - position.y - 2 > screen_bottom.y && offset.y < 48): + offset.y += 0.5 + elif Input.is_action_pressed("up"): + if(original_limit_top - position.y + 2 < screen_top.y && offset.y > -48): + offset.y -= 0.5 # TODO This is a regulatory problem, it doesn't adapt fast enough # TODO Maybe just make background black and dont bother func _adjust_offset(delta: float) -> bool: - var new_offset = offset - if (limit_left - position.x - screen_left.x > 0.1): - if (anim_player.is_playing()): - anim_player.stop(true) - new_offset.x += (limit_left - position.x - screen_left.x)/2 - if (limit_right - position.x - screen_right.x < 0.1): - if (anim_player.is_playing()): - anim_player.stop(true) - new_offset.x += (limit_right - position.x - screen_right.x)/2 - if (limit_top - position.y - screen_top.y > 0.001): - new_offset.y += (limit_top - position.y - screen_top.y)/2 - if (limit_bottom - position.y - screen_bottom.y < 0.001): - new_offset.y += (limit_bottom - position.y - screen_bottom.y)/2 - if(abs(offset.x) >= abs(new_offset.x) + 1): - offset = new_offset - return true - else: - return false - - + var new_offset = offset + if (limit_left - position.x - screen_left.x > 0.1): + if (anim_player.is_playing()): + anim_player.stop(true) + new_offset.x += (limit_left - position.x - screen_left.x)/1.5 + if (limit_right - position.x - screen_right.x < 0.1): + if (anim_player.is_playing()): + anim_player.stop(true) + new_offset.x += (limit_right - position.x - screen_right.x)/1.5 + if (limit_top - position.y - screen_top.y > 0.001): + new_offset.y += (limit_top - position.y - screen_top.y)/1.5 + if (limit_bottom - position.y - screen_bottom.y < 0.001): + new_offset.y += (limit_bottom - position.y - screen_bottom.y)/1.5 + #print(abs(offset.x) - abs(new_offset.x)) + if(abs(offset.x) > abs(new_offset.x) || abs(offset.y) > abs(new_offset.y)): + offset = new_offset + return true + else: + return false + + func reset_limits() -> void: - limit_left = original_limit_left - limit_right = original_limit_right - limit_bottom = original_limit_bottom - limit_top = original_limit_top + limit_left = original_limit_left + limit_right = original_limit_right + limit_bottom = original_limit_bottom + limit_top = original_limit_top func _death_cam(animation_number: int = 0) -> void: - if(animation_number < 1): - $CameraAnimationPlayer.play("deathCamJustZoom") - if(animation_number == 1): - $CameraAnimationPlayer.play("deathCamLateRotation") + if(animation_number < 1): + $CameraAnimationPlayer.play("deathCamJustZoom") + if(animation_number == 1): + $CameraAnimationPlayer.play("deathCamLateRotation") func _update_lighting_shader() -> void: - # Props to gameendaevour - # TODO get this into a central world update management system - var lights = get_tree().get_nodes_in_group("light") - image.lock() - for i in lights.size(): - var light = lights[i] - # TODO To make the lighting affect all layers properly - # I would have the access the global positions of nodes in different Z layers - # without the projection to the global center layer. + # Props to gameendaevour + # TODO get this into a central world update management system + var lights = get_tree().get_nodes_in_group("light") + image.lock() + for i in lights.size(): + var light = lights[i] + # TODO To make the lighting affect all layers properly + # I would have the access the global positions of nodes in different Z layers + # without the projection to the global center layer. - # var vtrans = get_canvas_transform() - # var top_left = -vtrans.origin / vtrans.get_scale() - # var vsize = get_viewport_rect().size - # var t = Transform2D(0, (top_left + 0.5*vsize/vtrans.get_scale()).rotated(rotation)) - image.set_pixel(i, 0, Color( - light.position.x, light.position.y, - light.strength, light.radius - )) - image.set_pixel(i, 1, light.color) - image.unlock() + # var vtrans = get_canvas_transform() + # var top_left = -vtrans.origin / vtrans.get_scale() + # var vsize = get_viewport_rect().size + # var t = Transform2D(0, (top_left + 0.5*vsize/vtrans.get_scale()).rotated(rotation)) + image.set_pixel(i, 0, Color( + light.position.x, light.position.y, + light.strength, light.radius + )) + image.set_pixel(i, 1, light.color) + image.unlock() - texture.create_from_image(image) + texture.create_from_image(image) - material.set_shader_param("n_lights", lights.size()) - material.set_shader_param("light_data", texture) - material.set_shader_param("global_transform", get_global_transform()) - material.set_shader_param("viewport_transform", get_viewport_transform()) + material.set_shader_param("n_lights", lights.size()) + material.set_shader_param("light_data", texture) + material.set_shader_param("global_transform", get_global_transform()) + material.set_shader_param("viewport_transform", get_viewport_transform()) diff --git a/src/Actors/BlobbyCam.tscn b/src/Actors/BlobbyCam.tscn index bac55d1..f7a6046 100644 --- a/src/Actors/BlobbyCam.tscn +++ b/src/Actors/BlobbyCam.tscn @@ -868,6 +868,7 @@ z_index = 3 rotating = true current = true zoom = Vector2( 0.75, 0.75 ) +process_mode = 0 drag_margin_h_enabled = true drag_margin_v_enabled = true drag_margin_left = 0.08 @@ -971,10 +972,10 @@ texture = ExtResource( 8 ) [node name="AnimatedSprite" type="AnimatedSprite" parent="ParallaxBackground/ParallaxLayer5"] frames = SubResource( 7 ) -frame = 3 +frame = 4 playing = true [node name="AnimatedSprite2" type="AnimatedSprite" parent="ParallaxBackground/ParallaxLayer5"] frames = SubResource( 8 ) -frame = 8 +frame = 9 playing = true diff --git a/src/Actors/Enemies/Beings/WhatAreFrog.gd b/src/Actors/Enemies/Beings/WhatAreFrog.gd index 6b8e0af..6bf49f1 100644 --- a/src/Actors/Enemies/Beings/WhatAreFrog.gd +++ b/src/Actors/Enemies/Beings/WhatAreFrog.gd @@ -53,466 +53,466 @@ var reversing_possible_searching := true func _ready(): - default_jump_distance = default_jump_distance * tilemap.cell_size.x - jump_timer = Timer.new() - jump_timer.set_one_shot(true) - jump_timer.connect("timeout", self, "jump") - target_lost_timer = Timer.new() - target_lost_timer.set_one_shot(true) - target_lost_timer.connect("timeout", self, "loose_target") - add_child(jump_timer) - add_child(target_lost_timer) - # TODO Stays harmless for now - #if(is_bound): add_to_group("harmful") - $LeashAnchor.visible = is_bound + default_jump_distance = default_jump_distance * tilemap.cell_size.x + jump_timer = Timer.new() + jump_timer.set_one_shot(true) + jump_timer.connect("timeout", self, "jump") + target_lost_timer = Timer.new() + target_lost_timer.set_one_shot(true) + target_lost_timer.connect("timeout", self, "loose_target") + add_child(jump_timer) + add_child(target_lost_timer) + # TODO Stays harmless for now + #if(is_bound): add_to_group("harmful") + $LeashAnchor.visible = is_bound func bind_to_anchor(anchor_node: Node2D, radius: float ) -> void: - anchor = anchor_node - movement_radius = radius * block_size - is_bound = true - $LeashAnchor.visible = true + anchor = anchor_node + movement_radius = radius * block_size + is_bound = true + $LeashAnchor.visible = true func _on_StompDetector_body_entered(body: Node) -> void: - if !body.is_in_group("player") || is_hurt: - return - var incoming_vel_vector: Vector2 = body.velocity.normalized() - # TODO This is not the right angle somehow + if !body.is_in_group("player") || is_hurt: + return + var incoming_vel_vector: Vector2 = body.velocity.normalized() + # TODO This is not the right angle somehow # print(rad2deg(abs(incoming_vel_vector.angle_to(Vector2.DOWN.rotated(rotation))))) # if abs(incoming_vel_vector.angle_to(\Vector2.DOWN.rotated(rotation))) > deg2rad(60): # print("too shallow entry") # return - signalManager.emit_signal("got_stomped") - remove_from_group("harmful") - # TODO Weakpoint group is not needed per se - $StompDetector.remove_from_group("weakpoint") - get_node("EnemyBody").disabled = true - is_hurt = true - $FrogSprite.material = invincible_shader - $HurtTimer.start() + signalManager.emit_signal("got_stomped") + remove_from_group("harmful") + # TODO Weakpoint group is not needed per se + $StompDetector.remove_from_group("weakpoint") + get_node("EnemyBody").disabled = true + is_hurt = true + $FrogSprite.material = invincible_shader + $HurtTimer.start() func execute_movement(delta: float) -> void: - # Navigation2DServer.map_get_path() - current_delta = delta - detect_timer += delta - velocity.y += _gravity * delta - if(is_bound): - var next_position = global_position + velocity * current_delta - var current_distance = global_position.distance_to(anchor.global_position) - var new_distance = next_position.distance_to(anchor.global_position) - # TODO Fix this in respects to x and y distances and movement dampening - # Maybe use mathemathematics or something idfc - if(current_distance >= movement_radius && new_distance > current_distance): - velocity.x = velocity.x * 0.8 - velocity.y = velocity.y * 0.8 - was_restricted = true - velocity = move_and_slide(velocity, FLOOR_NORMAL, false, 4, 0.785398,false) - if(is_on_floor()): - velocity = Vector2(0,0) - - # Reverse direction when hitting limit + # Navigation2DServer.map_get_path() + current_delta = delta + detect_timer += delta + velocity.y += _gravity * delta + if(is_bound): + var next_position = global_position + velocity * current_delta + var current_distance = global_position.distance_to(anchor.global_position) + var new_distance = next_position.distance_to(anchor.global_position) + # TODO Fix this in respects to x and y distances and movement dampening + # Maybe use mathemathematics or something idfc + if(current_distance >= movement_radius && new_distance > current_distance): + velocity.x = velocity.x * 0.8 + velocity.y = velocity.y * 0.8 + was_restricted = true + velocity = move_and_slide(velocity, FLOOR_NORMAL, false, 4, 0.785398,false) + if(is_on_floor()): + velocity = Vector2(0,0) + + # Reverse direction when hitting limit func die() -> void: - levelState.kills += 1 - queue_free() + levelState.kills += 1 + queue_free() func _on_EnemySkin_area_entered(area:Area2D) -> void: - if area.is_in_group("harmful"): - get_node("EnemyBody").disabled = true - die() + if area.is_in_group("harmful"): + get_node("EnemyBody").disabled = true + die() func _on_EnemySkin_body_entered(body: Node) -> void: - if body.is_in_group("frogfood"): - loose_target() - body.die() + if body.is_in_group("frogfood"): + loose_target() + body.die() func searching() -> Vector2: - if(detect_timer > 0.333): - search_next_target() - detect_timer = 0.0 - if(is_on_floor()): - if(jump_timer.is_stopped()): - jump_timer.start(rng.randfn(jump_time_search, jump_time_standard_deviation)) - if(in_air): - in_air = false - else: - if(!in_air): - start_x = global_position.x - reversing_possible_searching = true - jump_timer.stop() - in_air = true - return velocity + if(detect_timer > 0.333): + search_next_target() + detect_timer = 0.0 + if(is_on_floor()): + if(jump_timer.is_stopped()): + jump_timer.start(rng.randfn(jump_time_search, jump_time_standard_deviation)) + if(in_air): + in_air = false + else: + if(!in_air): + start_x = global_position.x + reversing_possible_searching = true + jump_timer.stop() + in_air = true + return velocity func search_next_target(): - if(target != null && !weakref(target).get_ref()): - return - detect_food() - if(food_target == null): - detect_player() + if(target != null && !weakref(target).get_ref()): + return + detect_food() + if(food_target == null): + detect_player() func hunting() -> Vector2: - var was_target_freed = !weakref(target).get_ref() - if(detect_timer > 0.333): - search_next_target() - detect_timer = 0.0 - #TODO Dependent on block size - elif(is_on_floor() && food_target != null && !was_target_freed && - global_position.distance_to(food_target.global_position) <= attack_jump_range * block_size): - - var collider = check_feeler(food_target.global_position - global_position) - if(!was_restricted && collider != null && collider.is_in_group("frogfood")): - jump_timer.stop() - return attack_jump(food_target.global_position) - - if(is_on_floor()): - if(jump_timer.is_stopped()): - jump_timer.start(rng.randfn(jump_time_hunt, jump_time_standard_deviation)) - if(in_air): - in_air = false - - else: - if(!in_air): - start_x = global_position.x - reversing_possible_searching = true - jump_timer.stop() - in_air = true + var was_target_freed = !weakref(target).get_ref() + if(detect_timer > 0.333): + search_next_target() + detect_timer = 0.0 + #TODO Dependent on block size + elif(is_on_floor() && food_target != null && !was_target_freed && + global_position.distance_to(food_target.global_position) <= attack_jump_range * block_size): + + var collider = check_feeler(food_target.global_position - global_position) + if(!was_restricted && collider != null && collider.is_in_group("frogfood")): + jump_timer.stop() + return attack_jump(food_target.global_position) + + if(is_on_floor()): + if(jump_timer.is_stopped()): + jump_timer.start(rng.randfn(jump_time_hunt, jump_time_standard_deviation)) + if(in_air): + in_air = false + + else: + if(!in_air): + start_x = global_position.x + reversing_possible_searching = true + jump_timer.stop() + in_air = true - if(barely_held_back_counter > 1): - barely_held_back_counter = 0 - loose_target() + if(barely_held_back_counter > 1): + barely_held_back_counter = 0 + loose_target() - if(target != null && !was_target_freed && - sign((target.global_position - global_position).x) != get_facing_direction()): - # TODO Waits in front of too small tunnels if it sees the target on the other side - # It's ok behavior for now - reverse_facing_direction() + if(target != null && !was_target_freed && + sign((target.global_position - global_position).x) != get_facing_direction()): + # TODO Waits in front of too small tunnels if it sees the target on the other side + # It's ok behavior for now + reverse_facing_direction() - return velocity + return velocity func detect_food() -> void: - # TODO What if food spawns in - food_sources = get_tree().get_nodes_in_group("frogfood") - if(food_sources.empty()): - return - var i = 0 - var min_dist_f_index = 0 - var min_dist = (food_sources[0].global_position - global_position).length() - var food_node = null - for f in food_sources: - var new_dist = (food_sources[i].global_position - global_position).length() - min_dist = new_dist if new_dist < min_dist else min_dist - min_dist_f_index = i if new_dist < min_dist else min_dist_f_index - i += 1 - food_node = food_sources[min_dist_f_index] - #TODO Depends on height of blobby sprite since blobbys bottom and not his middle is on y=0 - vision_raycast.cast_to = (food_node.global_position - global_position).normalized() * block_size * vision_distance - var ray_angle_to_facing = vision_raycast.cast_to.angle_to(orientation.cast_to) - vision_raycast.force_raycast_update() - var collider = vision_raycast.get_collider() - if(abs(ray_angle_to_facing) < PI/3 && collider != null && collider.is_in_group("frogfood")): - target_lost_timer.stop() - target = collider - food_target = collider - elif(target != null && target_lost_timer.is_stopped()): - target_lost_timer.start(loose_target_seconds) + # TODO What if food spawns in + food_sources = get_tree().get_nodes_in_group("frogfood") + if(food_sources.empty()): + return + var i = 0 + var min_dist_f_index = 0 + var min_dist = (food_sources[0].global_position - global_position).length() + var food_node = null + for f in food_sources: + var new_dist = (food_sources[i].global_position - global_position).length() + min_dist = new_dist if new_dist < min_dist else min_dist + min_dist_f_index = i if new_dist < min_dist else min_dist_f_index + i += 1 + food_node = food_sources[min_dist_f_index] + #TODO Depends on height of blobby sprite since blobbys bottom and not his middle is on y=0 + vision_raycast.cast_to = (food_node.global_position - global_position).normalized() * block_size * vision_distance + var ray_angle_to_facing = vision_raycast.cast_to.angle_to(orientation.cast_to) + vision_raycast.force_raycast_update() + var collider = vision_raycast.get_collider() + if(abs(ray_angle_to_facing) < PI/3 && collider != null && collider.is_in_group("frogfood")): + target_lost_timer.stop() + target = collider + food_target = collider + elif(target != null && target_lost_timer.is_stopped()): + target_lost_timer.start(loose_target_seconds) func detect_player() -> void: - var player - if(players.empty()): - # print("no player found") - return - player = players[0] - #TODO Depends on height of blobby sprite since blobbys bottom and not his middle is on y=0 - vision_raycast.cast_to = (player.global_position - global_position - Vector2(0, 9)).normalized() * block_size * vision_distance - var ray_angle_to_facing = vision_raycast.cast_to.angle_to(orientation.cast_to) - vision_raycast.force_raycast_update() - var collider = vision_raycast.get_collider() - if(abs(ray_angle_to_facing) < PI/4 && collider != null && collider.is_in_group("player")): - target_lost_timer.stop() - target = collider - elif(target != null && target_lost_timer.is_stopped()): - target_lost_timer.start(loose_target_seconds) + var player + if(players.empty()): + # print("no player found") + return + player = players[0] + #TODO Depends on height of blobby sprite since blobbys bottom and not his middle is on y=0 + vision_raycast.cast_to = (player.global_position - global_position - Vector2(0, 9)).normalized() * block_size * vision_distance + var ray_angle_to_facing = vision_raycast.cast_to.angle_to(orientation.cast_to) + vision_raycast.force_raycast_update() + var collider = vision_raycast.get_collider() + if(abs(ray_angle_to_facing) < PI/4 && collider != null && collider.is_in_group("player")): + target_lost_timer.stop() + target = collider + elif(target != null && target_lost_timer.is_stopped()): + target_lost_timer.start(loose_target_seconds) func sleeping() -> Vector2: - jump_timer.stop() - detect_player() - return velocity + jump_timer.stop() + detect_player() + return velocity func loose_target() -> void: - # print("frog target lost") - target = null - food_target = null + # print("frog target lost") + target = null + food_target = null func jump(): - # print("jump calculation initiated") - # Can only reverse once per jump calculation - has_reversed = false - var zero_vector = Vector2(0,0) - var v: Vector2 = velocity_for_jump_distance(default_jump_distance, deg2rad(default_jump_angle)) - v = correct_jump_direction(v) + # print("jump calculation initiated") + # Can only reverse once per jump calculation + has_reversed = false + var zero_vector = Vector2(0,0) + var v: Vector2 = velocity_for_jump_distance(default_jump_distance, deg2rad(default_jump_angle)) + v = correct_jump_direction(v) - if(is_bound): - var next_position = global_position + v * current_delta - var current_distance = global_position.distance_to(anchor.global_position) - var new_distance = next_position.distance_to(anchor.global_position) - # print(current_distance) - # print(new_distance) - # Would go out of distance - if((new_distance >= movement_radius && new_distance > current_distance) || (new_distance > current_distance && was_restricted)): - if(state_machine.state == "hunting"): - barely_held_back_counter += 1 - if can_reverse_facing_direction() && (barely_held_back_counter == 0 || barely_held_back_counter > 1): - reverse_facing_direction() - was_restricted = false - - if ($Right_Wallcast.is_colliding() && $Left_Wallcast.is_colliding()): - # TODO No idea what it might do in these situations - print("help this is a really tight space :(") - elif (get_facing_direction() < 0 && $Left_Wallcast.is_colliding()): - v = zero_vector - elif (get_facing_direction() > 0 && $Right_Wallcast.is_colliding()): - v = zero_vector - - - v = correct_jump_direction(v) - if(v != zero_vector): - v = consider_jump_headspace(v) - if(v != zero_vector): - v = consider_jump_landing_space(v) - if(v == zero_vector): - # TODO fix that you could call jump from jumping on top - # and let it fail if the top is dangerous for jump height or not safe - v = consider_jumping_on_top() - if(v == zero_vector && can_reverse_facing_direction()): - reverse_facing_direction() - velocity = v - + if(is_bound): + var next_position = global_position + v * current_delta + var current_distance = global_position.distance_to(anchor.global_position) + var new_distance = next_position.distance_to(anchor.global_position) + # print(current_distance) + # print(new_distance) + # Would go out of distance + if((new_distance >= movement_radius && new_distance > current_distance) || (new_distance > current_distance && was_restricted)): + if(state_machine.state == "hunting"): + barely_held_back_counter += 1 + if can_reverse_facing_direction() && (barely_held_back_counter == 0 || barely_held_back_counter > 1): + reverse_facing_direction() + was_restricted = false + + if ($Right_Wallcast.is_colliding() && $Left_Wallcast.is_colliding()): + # TODO No idea what it might do in these situations + print("help this is a really tight space :(") + elif (get_facing_direction() < 0 && $Left_Wallcast.is_colliding()): + v = zero_vector + elif (get_facing_direction() > 0 && $Right_Wallcast.is_colliding()): + v = zero_vector + + + v = correct_jump_direction(v) + if(v != zero_vector): + v = consider_jump_headspace(v) + if(v != zero_vector): + v = consider_jump_landing_space(v) + if(v == zero_vector): + # TODO fix that you could call jump from jumping on top + # and let it fail if the top is dangerous for jump height or not safe + v = consider_jumping_on_top() + if(v == zero_vector && can_reverse_facing_direction()): + reverse_facing_direction() + velocity = v + func correct_jump_direction(v: Vector2) -> Vector2: - if sign(v.x) != get_facing_direction(): - v.x *= -1 - return v + if sign(v.x) != get_facing_direction(): + v.x *= -1 + return v # Cast a ray to the highest point of the jump # Check the highest point for collision # Calculate safe jump height and then a safe jump velocity func consider_jump_headspace(v: Vector2) -> Vector2: - var height = calculate_jump_height(v) - var distance = calculate_jump_distance(v) - var angle = (v * get_facing_direction()).angle() - # Half distance is an estimate of the jumps apex() - #TODO Consider sprite size for height - var height_collider = check_feeler(Vector2(get_facing_direction()*(distance/2), -(height+23))) - if(height_collider != null): - var collision_point = feeler_raycast.get_collision_point() - var target_height = collision_point.y - (feeler_raycast.global_position.y - 23) - # print(feeler_raycast.global_position) - var new_angle = angle * (0.75 if target_height > -26 else 0.95) - var new_distance = default_jump_distance * (0.66 if target_height > -26 else 0.75) - v = velocity_for_jump_distance(new_distance, abs(new_angle)) - v = correct_jump_direction(v) - height = calculate_jump_height(v) * -1 - distance = calculate_jump_distance(v) * get_facing_direction() - if(height < target_height && can_reverse_facing_direction()): - print("no safe height for frog jump") - return Vector2(0,0) - return v + var height = calculate_jump_height(v) + var distance = calculate_jump_distance(v) + var angle = (v * get_facing_direction()).angle() + # Half distance is an estimate of the jumps apex() + #TODO Consider sprite size for height + var height_collider = check_feeler(Vector2(get_facing_direction()*(distance/2), -(height+23))) + if(height_collider != null): + var collision_point = feeler_raycast.get_collision_point() + var target_height = collision_point.y - (feeler_raycast.global_position.y - 23) + # print(feeler_raycast.global_position) + var new_angle = angle * (0.75 if target_height > -26 else 0.95) + var new_distance = default_jump_distance * (0.66 if target_height < -26 else 0.75) + v = velocity_for_jump_distance(new_distance, abs(new_angle)) + v = correct_jump_direction(v) + height = calculate_jump_height(v) * -1 + distance = calculate_jump_distance(v) * get_facing_direction() + if(height < target_height && can_reverse_facing_direction()): + print("no safe height for frog jump") + return Vector2(0,0) + return v # Check the block in jump distance for danger or height # If danger check neighboring blocks: if still danger, then jump closer (or jump over) # If height move to distance which allows 1 block high jump func consider_jump_landing_space(v: Vector2) -> Vector2: - var jump_distance = calculate_jump_distance(v) - var jump_height = calculate_jump_height(v) - var collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2)) - # TODO Unpacked loop, make function or something? - # Shortens the jump in steps to make it more safe - if(!is_jump_path_safe(v, global_position) || collider != null): - jump_distance = calculate_jump_distance(v) - block_size/1.5 - v = change_jump_distance(jump_distance, v) - jump_height = calculate_jump_height(v) - v = correct_jump_direction(v) - collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2)) - if(!is_jump_path_safe(v, global_position) || collider != null): - jump_distance = calculate_jump_distance(v) - block_size/2.0 - v = change_jump_distance(jump_distance, v) - jump_height = calculate_jump_height(v) - v = correct_jump_direction(v) - collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2)) - if((!is_jump_path_safe(v, global_position) || collider != null) && can_reverse_facing_direction()): - # Can be printed when frog would jump into a wall too - print("no safe landing space found") - return Vector2(0,0) - return v + var jump_distance = calculate_jump_distance(v) + var jump_height = calculate_jump_height(v) + var collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2)) + # TODO Unpacked loop, make function or something? + # Shortens the jump in steps to make it more safe + if(!is_jump_path_safe(v, global_position) || collider != null): + jump_distance = calculate_jump_distance(v) - block_size/1.5 + v = change_jump_distance(jump_distance, v) + jump_height = calculate_jump_height(v) + v = correct_jump_direction(v) + collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2)) + if(!is_jump_path_safe(v, global_position) || collider != null): + jump_distance = calculate_jump_distance(v) - block_size/2.0 + v = change_jump_distance(jump_distance, v) + jump_height = calculate_jump_height(v) + v = correct_jump_direction(v) + collider = check_feeler(Vector2(jump_distance * get_facing_direction(), - jump_height/2)) + if((!is_jump_path_safe(v, global_position) || collider != null) && can_reverse_facing_direction()): + # Can be printed when frog would jump into a wall too + print("no safe landing space found") + return Vector2(0,0) + return v func consider_jumping_on_top() -> Vector2: - var collider = check_feeler(Vector2(42 * get_facing_direction(),0)) - var facing = 0 if get_facing_direction() >= 0 else - 1 - if (collider == null): - return Vector2(0,0) - var local_position = tilemap.to_local(feeler_raycast.get_collision_point()) - var map_position = tilemap.world_to_map(local_position) - var tile_position = Vector2(map_position.x + facing, map_position.y) - # TODO Here the climb height of frog is limited to one constantly - var cell_id = tilemap.get_cell(tile_position.x, tile_position.y - 2) - if (cell_id != -1 && - #TODO 9 is the navigation tile, but thats subject to change! - cell_id != 8): - return Vector2(0,0) - var tile_upper_left_corner = tilemap.to_global(tilemap.map_to_world(tile_position)) - var tile_upper_right_corner = Vector2(tile_upper_left_corner.x + tilemap.cell_size.x, tile_upper_left_corner.y) - - var jump_angle = 0 - if(facing < 0): - var frog_bottom_left_corner = Vector2($EnemyBody.global_position.x - $EnemyBody.shape.extents.x, - $EnemyBody.global_position.y + $EnemyBody.shape.extents.y) - jump_angle = frog_bottom_left_corner.angle_to_point(tile_upper_right_corner) - else: - var frog_bottom_right_corner = Vector2($EnemyBody.global_position.x + $EnemyBody.shape.extents.x, - $EnemyBody.global_position.y + $EnemyBody.shape.extents.y) - jump_angle = frog_bottom_right_corner.angle_to_point(tile_upper_left_corner) - PI - - if(abs(rad2deg(jump_angle)) < 78): - return correct_jump_direction(velocity_for_jump_distance(default_jump_distance/2, abs(deg2rad(80)))) - else: - return velocity_for_jump_distance(10, abs(deg2rad(45))) * -1 * facing + var collider = check_feeler(Vector2(42 * get_facing_direction(),0)) + var facing = 0 if get_facing_direction() >= 0 else - 1 + if (collider == null): + return Vector2(0,0) + var local_position = tilemap.to_local(feeler_raycast.get_collision_point()) + var map_position = tilemap.world_to_map(local_position) + var tile_position = Vector2(map_position.x + facing, map_position.y) + # TODO Here the climb height of frog is limited to one constantly + var cell_id = tilemap.get_cell(tile_position.x, tile_position.y - 2) + if (cell_id != -1 && + #TODO 9 is the navigation tile, but thats subject to change! + cell_id != 8): + return Vector2(0,0) + var tile_upper_left_corner = tilemap.to_global(tilemap.map_to_world(tile_position)) + var tile_upper_right_corner = Vector2(tile_upper_left_corner.x + tilemap.cell_size.x, tile_upper_left_corner.y) + + var jump_angle = 0 + if(facing < 0): + var frog_bottom_left_corner = Vector2($EnemyBody.global_position.x - $EnemyBody.shape.extents.x, + $EnemyBody.global_position.y + $EnemyBody.shape.extents.y) + jump_angle = frog_bottom_left_corner.angle_to_point(tile_upper_right_corner) + else: + var frog_bottom_right_corner = Vector2($EnemyBody.global_position.x + $EnemyBody.shape.extents.x, + $EnemyBody.global_position.y + $EnemyBody.shape.extents.y) + jump_angle = frog_bottom_right_corner.angle_to_point(tile_upper_left_corner) - PI + + if(abs(rad2deg(jump_angle)) < 78): + return correct_jump_direction(velocity_for_jump_distance(default_jump_distance/2, abs(deg2rad(80)))) + else: + return velocity_for_jump_distance(10, abs(deg2rad(45))) * -1 * facing # Tries to shorten the jump, so that it lands in a tiles center func jump_to_tile_center(v: Vector2) -> Vector2: - var distance = stepify(calculate_jump_distance(v), 0.01) - if !is_equal_approx(fmod(abs(global_position.x + distance * get_facing_direction()), block_size), (block_size/2.0)): - # print(distance) - # print(global_position.x + distance) - # print(fmod((global_position.x + distance), block_size)) - var new_distance = distance - if(get_facing_direction() < 0): - new_distance = fmod((global_position.x + distance), block_size) - (block_size/2.0) + distance - else: - new_distance = distance + block_size/2.0 - fmod((global_position.x + distance), block_size) - # print("centering distance") - # print(new_distance) - v = change_jump_distance(abs(new_distance), v) - v = correct_jump_direction(v) - return v + var distance = stepify(calculate_jump_distance(v), 0.01) + if !is_equal_approx(fmod(abs(global_position.x + distance * get_facing_direction()), block_size), (block_size/2.0)): + # print(distance) + # print(global_position.x + distance) + # print(fmod((global_position.x + distance), block_size)) + var new_distance = distance + if(get_facing_direction() < 0): + new_distance = fmod((global_position.x + distance), block_size) - (block_size/2.0) + distance + else: + new_distance = distance + block_size/2.0 - fmod((global_position.x + distance), block_size) + # print("centering distance") + # print(new_distance) + v = change_jump_distance(abs(new_distance), v) + v = correct_jump_direction(v) + return v # TODO Depends on Frog Shape and Tile Shape func is_jump_path_safe(v: Vector2, pos: Vector2) -> bool: - var v0 = v.length() - var angle = v.angle() - var jump_distance = calculate_jump_distance(v) - var harmful_nodes = get_tree().get_nodes_in_group("harmful") - harmful_nodes.append_array(get_tree().get_nodes_in_group("pit")) - for node in harmful_nodes: - var node_pos = node.global_position - # TODO Ignores spikes more than 4 blocks below and 3 jumps away - # Also when its too near to one - if (abs(node_pos.x - pos.x) > abs(jump_distance) * 3 - ||abs(node_pos.y - pos.y) > block_size * 4 - || abs(node_pos.x - pos.x) < 1): - continue - var node_y = node_pos.y - block_size/2.0 - var initial_throw_height = node_y - (global_position.y + 9) - var term1 = (pow(v0, 2) * sin(2 * angle)) / (2 * _gravity) - var term2 = ((v0 * cos(angle))/_gravity) * sqrt(pow(v0, 2) * pow(sin(angle), 2) + 2 * _gravity * initial_throw_height) - var distance = abs(term1) + abs(term2) - # print("distance to next spike") - # print(pos.x + sign(v.x) * distance - node_pos.x) - var safe_distance = block_size/2.0 - if (sign(initial_throw_height) < 0): - safe_distance = block_size - if(abs(pos.x + sign(v.x) * distance - node_pos.x) < safe_distance): - return false - return true + var v0 = v.length() + var angle = v.angle() + var jump_distance = calculate_jump_distance(v) + var harmful_nodes = get_tree().get_nodes_in_group("harmful") + harmful_nodes.append_array(get_tree().get_nodes_in_group("pit")) + for node in harmful_nodes: + var node_pos = node.global_position + # TODO Ignores spikes more than 4 blocks below and 3 jumps away + # Also when its too near to one + if (abs(node_pos.x - pos.x) > abs(jump_distance) * 3 + ||abs(node_pos.y - pos.y) > block_size * 4 + || abs(node_pos.x - pos.x) < 1): + continue + var node_y = node_pos.y - block_size/2.0 + var initial_throw_height = node_y - (global_position.y + 9) + var term1 = (pow(v0, 2) * sin(2 * angle)) / (2 * _gravity) + var term2 = ((v0 * cos(angle))/_gravity) * sqrt(pow(v0, 2) * pow(sin(angle), 2) + 2 * _gravity * initial_throw_height) + var distance = abs(term1) + abs(term2) + # print("distance to next spike") + # print(pos.x + sign(v.x) * distance - node_pos.x) + var safe_distance = block_size/2.0 + if (sign(initial_throw_height) < 0): + safe_distance = block_size + if(abs(pos.x + sign(v.x) * distance - node_pos.x) < safe_distance): + return false + return true func calculate_jump_height(v: Vector2) -> float: - return abs((pow(v.length(), 2) * pow(sin(v.angle()), 2))/(2*_gravity)) + return abs((pow(v.length(), 2) * pow(sin(v.angle()), 2))/(2*_gravity)) # Only works for jumps on straight ground func calculate_jump_distance(v: Vector2) -> float: - return abs((pow(v.length(), 2) * sin(-1 * 2 * v.angle()))/(_gravity)) + return abs((pow(v.length(), 2) * sin(-1 * 2 * v.angle()))/(_gravity)) func jump_height_to_velocity(target_height: float, v: Vector2) -> Vector2: - var initial_height = calculate_jump_height(v) - return v.normalized() * sqrt(pow(v.length(),2)/(initial_height/target_height)) + var initial_height = calculate_jump_height(v) + return v.normalized() * sqrt(pow(v.length(),2)/(initial_height/target_height)) # Changes a Vector for a jump to the targeted distance, keeping the angle func change_jump_distance(target_distance: float, v: Vector2) -> Vector2: - var initial_distance = calculate_jump_distance(v) - return v.normalized() * sqrt(pow(v.length(),2)/(initial_distance/target_distance)) + var initial_distance = calculate_jump_distance(v) + return v.normalized() * sqrt(pow(v.length(),2)/(initial_distance/target_distance)) # Takes an angle and a distance to calculate a jump launching at that angle and covering the distance func velocity_for_jump_distance(distance: float = default_jump_distance*block_size, angle: float = deg2rad(default_jump_angle)) -> Vector2: - var abs_velocity = sqrt((distance * _gravity)/sin(2*angle)) - return Vector2(abs_velocity,0).rotated(-1*angle) + var abs_velocity = sqrt((distance * _gravity)/sin(2*angle)) + return Vector2(abs_velocity,0).rotated(-1*angle) func can_reverse_facing_direction() -> bool: - if(is_on_floor() && !has_reversed): - return true - return false + if(is_on_floor() && !has_reversed): + return true + return false # Returns a jump velocity that has the target_position in it's path func attack_jump(target_position: Vector2) -> Vector2: - var target_vector = target_position - global_position - target_vector = Vector2(abs(target_vector.x), target_vector.y) - var jump_angle = target_vector.angle() - var v = Vector2() - # TODO Tunable parameters - if jump_angle < deg2rad(-30): - v = velocity_for_jump_distance(target_vector.x, deg2rad(default_jump_angle)) - v = jump_height_to_velocity(abs(target_vector.y), v) - else: - v = velocity_for_jump_distance(target_vector.x * 1.5,deg2rad(45)) - v = correct_jump_direction(v) - return v + var target_vector = target_position - global_position + target_vector = Vector2(abs(target_vector.x), target_vector.y) + var jump_angle = target_vector.angle() + var v = Vector2() + # TODO Tunable parameters + if jump_angle < deg2rad(-30): + v = velocity_for_jump_distance(target_vector.x, deg2rad(default_jump_angle)) + v = jump_height_to_velocity(abs(target_vector.y), v) + else: + v = velocity_for_jump_distance(target_vector.x * 1.5,deg2rad(45)) + v = correct_jump_direction(v) + return v # Checks the feeler ray for collisions and returns collider or null func check_feeler(v: Vector2, _offset = Vector2(0,0)) -> Object: - var prev_position = feeler_raycast.position - feeler_raycast.position += _offset - feeler_raycast.cast_to = v - feeler_raycast.force_raycast_update() - var collider = feeler_raycast.get_collider() - feeler_raycast.position = prev_position - return collider + var prev_position = feeler_raycast.position + feeler_raycast.position += _offset + feeler_raycast.cast_to = v + feeler_raycast.force_raycast_update() + var collider = feeler_raycast.get_collider() + feeler_raycast.position = prev_position + return collider func reverse_facing_direction() -> void: - has_reversed = true - # print("reversing direction") - orientation.cast_to.x *= -1 + has_reversed = true + # print("reversing direction") + orientation.cast_to.x *= -1 func get_facing_direction() -> float: - return orientation.cast_to.x + return orientation.cast_to.x func _on_HurtTimer_timeout() -> void: - is_hurt = false - #if(is_bound): add_to_group("harmful") - $FrogSprite.material = null + is_hurt = false + #if(is_bound): add_to_group("harmful") + $FrogSprite.material = null diff --git a/src/Autoload/GlobalState.gd b/src/Autoload/GlobalState.gd index 1b189c5..15530fe 100644 --- a/src/Autoload/GlobalState.gd +++ b/src/Autoload/GlobalState.gd @@ -7,42 +7,45 @@ extends Node export(Resource) var gsr func set_progress(value) -> void: - gsr.progress_dict = value - SaveManager.save_default() + gsr.progress_dict = value + SaveManager.save_default() func get_progress() -> Dictionary: - return gsr.progress_dict + return gsr.progress_dict + +func save() -> void: + SaveManager.save_default() func set_savepoint(levelName: String, position: Vector2) -> void: - if(!gsr.progress_dict.has(levelName)): - gsr.progress_dict[levelName] = {} - gsr.progress_dict[levelName]["savepoint"] = position - SaveManager.save_default() + if(!gsr.progress_dict.has(levelName)): + gsr.progress_dict[levelName] = {} + gsr.progress_dict[levelName]["savepoint"] = position + SaveManager.save_default() func get_property_value(levelName: String, propertyName: String) -> int: - if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has(propertyName): - return gsr.progress_dict[levelName][propertyName] - else: - return 0 + if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has(propertyName): + return gsr.progress_dict[levelName][propertyName] + else: + return 0 func get_savepoint(levelName: String) -> Vector2: - if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has("savepoint"): - return gsr.progress_dict[levelName]["savepoint"] - else: - return Vector2() + if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has("savepoint"): + return gsr.progress_dict[levelName]["savepoint"] + else: + return Vector2() # TODO This is permanent immediatly func set_wallet(value) -> void: - gsr.wallet = value - SaveManager.save_default() + gsr.wallet = value + SaveManager.save_default() func reinstate() -> void: - if gsr.input_map.size() <= 1: - InputMap.load_from_globals() - return - for action in gsr.input_map: - InputMap.action_erase_events(action) - # if(gsr.input_map[action].size() > 0 && gsr.input_map[action].size() < 2): - # InputMap.load_from_globals() - for input_event in gsr.input_map[action]: - InputMap.action_add_event(action, input_event) + if gsr.input_map.size() <= 1: + InputMap.load_from_globals() + return + for action in gsr.input_map: + InputMap.action_erase_events(action) + # if(gsr.input_map[action].size() > 0 && gsr.input_map[action].size() < 2): + # InputMap.load_from_globals() + for input_event in gsr.input_map[action]: + InputMap.action_add_event(action, input_event) diff --git a/src/BenefitialObjects/Coin.tscn b/src/BenefitialObjects/Coin.tscn index f0cbdfc..2f2dfd7 100644 --- a/src/BenefitialObjects/Coin.tscn +++ b/src/BenefitialObjects/Coin.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=323 format=2] +[gd_scene load_steps=324 format=2] [ext_resource path="res://assets/neutral object/whitegold orbicle/0020 (Klein)-fs8.png" type="Texture" id=1] [ext_resource path="res://src/BenefitialObjects/Coin.gd" type="Script" id=2] @@ -316,6 +316,7 @@ [ext_resource path="res://assets/neutral object/whitegold orbicle/0312 (Klein)-fs8.png" type="Texture" id=314] [ext_resource path="res://assets/neutral object/whitegold orbicle/0287 (Klein)-fs8.png" type="Texture" id=315] [ext_resource path="res://assets/sounds/BrokenWindows3.ogg" type="AudioStream" id=316] +[ext_resource path="res://assets/environment/blocks/Empty-Navigation-Tile.png" type="Texture" id=317] [sub_resource type="CircleShape2D" id=1] radius = 34.0147 @@ -391,37 +392,6 @@ tracks/2/keys = { "values": [ -13.5, 13.5 ] } -[sub_resource type="Shader" id=5] -code = "shader_type canvas_item; - -uniform float thickness: hint_range(0.0,1.0) = 0.02; - -void fragment() { - vec4 col = texture(TEXTURE, UV); - if ( col.a > 0.1 && - (texture(TEXTURE, UV + vec2(thickness,0.) ).a < 0.1 - || texture(TEXTURE, UV - vec2(thickness,0.) ).a < 0.1 - ||texture(TEXTURE, UV + vec2(0.,thickness) ).a < 0.1 - ||texture(TEXTURE, UV - vec2(0.,thickness) ).a < 0.1)){ - COLOR = col - texture(TEXTURE, UV, 1000.); - } - else if(( col.a < 0.1 && - (texture(TEXTURE, UV + vec2(thickness,0.) ).a > 0.1 - || texture(TEXTURE, UV - vec2(thickness,0.) ).a > 0.3 - ||texture(TEXTURE, UV + vec2(0.,thickness) ).a > 0.3 - ||texture(TEXTURE, UV - vec2(0.,thickness) ).a > 0.3))){ - COLOR = vec4(1.,1.,1.,1.); - } - else{ - COLOR = texture(TEXTURE, UV); - } - -}" - -[sub_resource type="ShaderMaterial" id=6] -shader = SubResource( 5 ) -shader_param/thickness = 0.029 - [sub_resource type="SpriteFrames" id=4] animations = [ { "frames": [ ExtResource( 13 ), ExtResource( 12 ), ExtResource( 36 ), ExtResource( 17 ), ExtResource( 35 ), ExtResource( 8 ), ExtResource( 30 ), ExtResource( 16 ), ExtResource( 18 ), ExtResource( 19 ), ExtResource( 20 ), ExtResource( 31 ), ExtResource( 21 ), ExtResource( 9 ), ExtResource( 22 ), ExtResource( 32 ), ExtResource( 37 ), ExtResource( 7 ), ExtResource( 33 ), ExtResource( 34 ), ExtResource( 1 ), ExtResource( 3 ), ExtResource( 10 ), ExtResource( 4 ), ExtResource( 23 ), ExtResource( 5 ), ExtResource( 14 ), ExtResource( 6 ), ExtResource( 11 ), ExtResource( 24 ), ExtResource( 15 ), ExtResource( 25 ), ExtResource( 26 ), ExtResource( 27 ), ExtResource( 28 ), ExtResource( 29 ), ExtResource( 42 ), ExtResource( 40 ), ExtResource( 43 ), ExtResource( 38 ), ExtResource( 41 ), ExtResource( 39 ), ExtResource( 77 ), ExtResource( 57 ), ExtResource( 86 ), ExtResource( 44 ), ExtResource( 85 ), ExtResource( 89 ), ExtResource( 94 ), ExtResource( 78 ), ExtResource( 61 ), ExtResource( 102 ), ExtResource( 71 ), ExtResource( 68 ), ExtResource( 67 ), ExtResource( 62 ), ExtResource( 69 ), ExtResource( 55 ), ExtResource( 72 ), ExtResource( 90 ), ExtResource( 53 ), ExtResource( 91 ), ExtResource( 99 ), ExtResource( 93 ), ExtResource( 58 ), ExtResource( 92 ), ExtResource( 103 ), ExtResource( 88 ), ExtResource( 95 ), ExtResource( 96 ), ExtResource( 97 ), ExtResource( 98 ), ExtResource( 100 ), ExtResource( 101 ), ExtResource( 70 ), ExtResource( 87 ), ExtResource( 83 ), ExtResource( 104 ), ExtResource( 54 ), ExtResource( 63 ), ExtResource( 45 ), ExtResource( 105 ), ExtResource( 47 ), ExtResource( 46 ), ExtResource( 59 ), ExtResource( 49 ), ExtResource( 82 ), ExtResource( 106 ), ExtResource( 107 ), ExtResource( 48 ), ExtResource( 60 ), ExtResource( 73 ), ExtResource( 50 ), ExtResource( 51 ), ExtResource( 52 ), ExtResource( 74 ), ExtResource( 64 ), ExtResource( 56 ), ExtResource( 79 ), ExtResource( 75 ), ExtResource( 81 ), ExtResource( 65 ), ExtResource( 66 ), ExtResource( 76 ), ExtResource( 80 ), ExtResource( 84 ), ExtResource( 110 ), ExtResource( 113 ), ExtResource( 111 ), ExtResource( 112 ), ExtResource( 108 ), ExtResource( 109 ), ExtResource( 149 ), ExtResource( 155 ), ExtResource( 151 ), ExtResource( 121 ), ExtResource( 136 ), ExtResource( 159 ), ExtResource( 156 ), ExtResource( 153 ), ExtResource( 152 ), ExtResource( 146 ), ExtResource( 166 ), ExtResource( 131 ), ExtResource( 168 ), ExtResource( 176 ), ExtResource( 171 ), ExtResource( 122 ), ExtResource( 140 ), ExtResource( 117 ), ExtResource( 118 ), ExtResource( 128 ), ExtResource( 127 ), ExtResource( 174 ), ExtResource( 167 ), ExtResource( 124 ), ExtResource( 123 ), ExtResource( 169 ), ExtResource( 175 ), ExtResource( 134 ), ExtResource( 142 ), ExtResource( 141 ), ExtResource( 164 ), ExtResource( 126 ), ExtResource( 129 ), ExtResource( 132 ), ExtResource( 125 ), ExtResource( 170 ), ExtResource( 133 ), ExtResource( 130 ), ExtResource( 160 ), ExtResource( 161 ), ExtResource( 135 ), ExtResource( 177 ), ExtResource( 137 ), ExtResource( 138 ), ExtResource( 165 ), ExtResource( 143 ), ExtResource( 172 ), ExtResource( 173 ), ExtResource( 162 ), ExtResource( 139 ), ExtResource( 114 ), ExtResource( 163 ), ExtResource( 157 ), ExtResource( 144 ), ExtResource( 115 ), ExtResource( 116 ), ExtResource( 145 ), ExtResource( 147 ), ExtResource( 119 ), ExtResource( 120 ), ExtResource( 154 ), ExtResource( 148 ), ExtResource( 150 ), ExtResource( 158 ), ExtResource( 222 ), ExtResource( 237 ), ExtResource( 195 ), ExtResource( 191 ), ExtResource( 192 ), ExtResource( 209 ), ExtResource( 240 ), ExtResource( 214 ), ExtResource( 200 ), ExtResource( 181 ), ExtResource( 238 ), ExtResource( 215 ), ExtResource( 241 ), ExtResource( 229 ), ExtResource( 210 ), ExtResource( 185 ), ExtResource( 186 ), ExtResource( 236 ), ExtResource( 233 ), ExtResource( 202 ), ExtResource( 223 ), ExtResource( 211 ), ExtResource( 220 ), ExtResource( 212 ), ExtResource( 204 ), ExtResource( 193 ), ExtResource( 205 ), ExtResource( 178 ), ExtResource( 230 ), ExtResource( 218 ), ExtResource( 182 ), ExtResource( 207 ), ExtResource( 239 ), ExtResource( 187 ), ExtResource( 194 ), ExtResource( 196 ), ExtResource( 226 ), ExtResource( 219 ), ExtResource( 188 ), ExtResource( 213 ), ExtResource( 231 ), ExtResource( 216 ), ExtResource( 206 ), ExtResource( 221 ), ExtResource( 234 ), ExtResource( 208 ), ExtResource( 179 ), ExtResource( 217 ), ExtResource( 224 ), ExtResource( 197 ), ExtResource( 232 ), ExtResource( 189 ), ExtResource( 225 ), ExtResource( 198 ), ExtResource( 199 ), ExtResource( 203 ), ExtResource( 180 ), ExtResource( 227 ), ExtResource( 183 ), ExtResource( 201 ), ExtResource( 184 ), ExtResource( 228 ), ExtResource( 190 ), ExtResource( 235 ), ExtResource( 243 ), ExtResource( 252 ), ExtResource( 249 ), ExtResource( 245 ), ExtResource( 251 ), ExtResource( 242 ), ExtResource( 247 ), ExtResource( 244 ), ExtResource( 246 ), ExtResource( 248 ), ExtResource( 250 ), ExtResource( 253 ), ExtResource( 254 ), ExtResource( 307 ), ExtResource( 264 ), ExtResource( 274 ), ExtResource( 259 ), ExtResource( 278 ), ExtResource( 260 ), ExtResource( 300 ), ExtResource( 263 ), ExtResource( 284 ), ExtResource( 294 ), ExtResource( 261 ), ExtResource( 308 ), ExtResource( 265 ), ExtResource( 309 ), ExtResource( 286 ), ExtResource( 262 ), ExtResource( 271 ), ExtResource( 297 ), ExtResource( 266 ), ExtResource( 269 ), ExtResource( 285 ), ExtResource( 298 ), ExtResource( 267 ), ExtResource( 268 ), ExtResource( 275 ), ExtResource( 310 ), ExtResource( 299 ), ExtResource( 272 ), ExtResource( 270 ), ExtResource( 301 ), ExtResource( 292 ), ExtResource( 281 ), ExtResource( 313 ), ExtResource( 276 ), ExtResource( 315 ), ExtResource( 289 ), ExtResource( 255 ), ExtResource( 273 ), ExtResource( 257 ), ExtResource( 282 ), ExtResource( 287 ), ExtResource( 302 ), ExtResource( 283 ), ExtResource( 256 ), ExtResource( 277 ), ExtResource( 303 ), ExtResource( 304 ), ExtResource( 279 ), ExtResource( 305 ), ExtResource( 306 ), ExtResource( 288 ), ExtResource( 258 ), ExtResource( 280 ), ExtResource( 311 ), ExtResource( 290 ), ExtResource( 291 ), ExtResource( 293 ), ExtResource( 312 ), ExtResource( 295 ), ExtResource( 314 ), ExtResource( 296 ) ], @@ -430,32 +400,80 @@ animations = [ { "speed": 24.0 } ] +[sub_resource type="Shader" id=5] +code = "shader_type canvas_item; + +uniform float ring_radius : hint_range(0.1, 0.5, 0.01) = 0.4; +uniform float thickness_scalar : hint_range(0.0, 0.99, 0.05) = 0.7; +uniform float oscillation_scalar : hint_range(0.0, 0.25, 0.005) = 0.025; +uniform float speed : hint_range(0.0, 50.0, 0.1) = 1.0; +uniform vec4 main_color : hint_color = vec4(1.0,1.0,1.0,1.0); +uniform vec4 lerp_color : hint_color = vec4(1.0,1.0,1.0,1.0); + +float range_lerp(float value, float min1, float min2, float max1, float max2){ + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} + +void fragment() { + // Calculate the distance between the current pixel and the center of the unit + float dist = distance(UV, vec2(0.5, 0.5)); + + // Add a slight oscillation to the size of the ring + float o = cos(TIME * speed); + float ring_size = ring_radius + o * oscillation_scalar; + + float inner_hardness = 0.0; + // Solve for ring alpha channel + float alpha = step(dist, ring_size) * smoothstep(ring_size * (1.0 - thickness_scalar), ring_size, dist); + + // Solve w mix amount for optional color lerping + float w = range_lerp(o, -1.0, 1.0, 1.0, 0.0); + + // Output the final color + COLOR = vec4(mix(main_color.rgb, lerp_color.rgb, w), alpha ); +} + +" + +[sub_resource type="ShaderMaterial" id=6] +shader = SubResource( 5 ) +shader_param/ring_radius = 0.45 +shader_param/thickness_scalar = 0.2 +shader_param/oscillation_scalar = 0.01 +shader_param/speed = 1.0 +shader_param/main_color = Color( 1, 1, 1, 0.286275 ) +shader_param/lerp_color = Color( 0.658824, 0.901961, 0.8, 1 ) + [node name="Coin" type="Area2D"] position = Vector2( -6, -6 ) -scale = Vector2( 0.18, 0.18 ) collision_layer = 4 monitorable = false script = ExtResource( 2 ) [node name="CollisionShape2D" type="CollisionShape2D" parent="."] position = Vector2( 0, 3 ) +scale = Vector2( 0.18, 0.18 ) shape = SubResource( 1 ) [node name="AnimationPlayer" type="AnimationPlayer" parent="."] -autoplay = "oscilating" anims/fade_out = SubResource( 2 ) anims/oscilating = SubResource( 3 ) [node name="AnimatedSprite" type="AnimatedSprite" parent="."] -material = SubResource( 6 ) position = Vector2( 0, 3 ) rotation = -0.235619 -scale = Vector2( 0.2, 0.2 ) +scale = Vector2( 0.036, 0.036 ) frames = SubResource( 4 ) animation = "rotate" -frame = 294 +frame = 272 playing = true +[node name="Sprite" type="Sprite" parent="."] +material = SubResource( 6 ) +position = Vector2( 0.144981, 2.90382 ) +scale = Vector2( 1, 1 ) +texture = ExtResource( 317 ) + [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] stream = ExtResource( 316 ) volume_db = -9.044 diff --git a/src/Levels/1 Tut Level.tscn b/src/Levels/1 Tut Level.tscn index 5ee69ce..092a3cc 100644 --- a/src/Levels/1 Tut Level.tscn +++ b/src/Levels/1 Tut Level.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=17 format=2] +[gd_scene load_steps=15 format=2] [ext_resource path="res://src/Utilities/LevelState.tscn" type="PackedScene" id=1] [ext_resource path="res://src/UserInterface/UserInterface.tscn" type="PackedScene" id=2] @@ -10,10 +10,8 @@ [ext_resource path="res://src/Contraptions/Portal/Portal.tscn" type="PackedScene" id=8] [ext_resource path="res://src/Actors/Blobby/Blobby.tscn" type="PackedScene" id=9] [ext_resource path="res://src/Levels/Level 1.gd" type="Script" id=10] -[ext_resource path="res://addons/controller_icons/objects/Button.gd" type="Script" id=11] -[ext_resource path="res://src/Levels/TutorialThingy.gd" type="Script" id=12] -[sub_resource type="AnimationNodeStateMachinePlayback" id=4] +[sub_resource type="AnimationNodeStateMachinePlayback" id=14] [sub_resource type="ConvexPolygonShape2D" id=12] points = PoolVector2Array( 0, 0, 16, 0, 16, 16, 0, 16 ) @@ -101,18 +99,6 @@ unique_name_in_owner = true [node name="Timer" parent="UserInterface/HUD/HUDOverlay/GetBackTimer" index="0"] wait_time = 20.0 -[node name="TutorialThingy" type="Sprite" parent="."] -z_index = 3 -script = ExtResource( 12 ) - -[node name="ControllerButton" type="Button" parent="TutorialThingy"] -visible = false -margin_left = -10.0 -margin_top = -10.0 -margin_right = 10.0 -margin_bottom = 10.0 -script = ExtResource( 11 ) - [node name="BlobbyCam" parent="." instance=ExtResource( 3 )] unique_name_in_owner = true drag_margin_bottom = 0.3 @@ -121,16 +107,16 @@ drag_margin_bottom = 0.3 visible = true [node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"] -frame = 11 +frame = 3 [node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"] -frame = 4 +frame = 6 [node name="Blobby" parent="." instance=ExtResource( 9 )] unique_name_in_owner = true [node name="AnimationTree" parent="Blobby/BlobbySprite" index="0"] -parameters/playback = SubResource( 4 ) +parameters/playback = SubResource( 14 ) [node name="TileMap" type="TileMap" parent="."] unique_name_in_owner = true diff --git a/src/Levels/2 Tut Level.tscn b/src/Levels/2 Tut Level.tscn index 9921a4e..f8d3e18 100644 --- a/src/Levels/2 Tut Level.tscn +++ b/src/Levels/2 Tut Level.tscn @@ -44,10 +44,10 @@ unique_name_in_owner = true drag_margin_bottom = 0.3 [node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"] -frame = 7 +frame = 5 [node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"] -frame = 12 +frame = 10 [node name="Blobby" parent="." instance=ExtResource( 9 )] unique_name_in_owner = true @@ -228,19 +228,19 @@ position = Vector2( 756, -15 ) z_index = -1 [node name="AnimatedSprite" parent="Coin" index="2"] -frame = 301 +frame = 5 [node name="Coin2" parent="." instance=ExtResource( 11 )] position = Vector2( 744, -120 ) [node name="AnimatedSprite" parent="Coin2" index="2"] -frame = 232 +frame = 250 [node name="Coin3" parent="." instance=ExtResource( 11 )] position = Vector2( 1320, -282 ) [node name="AnimatedSprite" parent="Coin3" index="2"] -frame = 232 +frame = 250 [node name="Coin4" parent="." instance=ExtResource( 11 )] position = Vector2( 2340, -156 ) diff --git a/src/Levels/Actual Level 2.tscn b/src/Levels/Actual Level 2.tscn index 01ce732..f16c021 100644 --- a/src/Levels/Actual Level 2.tscn +++ b/src/Levels/Actual Level 2.tscn @@ -47,10 +47,10 @@ unique_name_in_owner = true drag_margin_bottom = 0.3 [node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"] -frame = 4 +frame = 5 [node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"] -frame = 3 +frame = 7 [node name="Blobby" parent="." instance=ExtResource( 5 )] unique_name_in_owner = true @@ -67,6 +67,9 @@ position = Vector2( 264, -36 ) position = Vector2( 837, 72 ) aggressive = false +[node name="FlyerSprite" parent="Flyer" index="2"] +frame = 0 + [node name="AnimationTree" parent="Flyer/FlyerSprite" index="1"] parameters/playback = SubResource( 5 ) diff --git a/src/Levels/Level 1.gd b/src/Levels/Level 1.gd index d013757..4057964 100644 --- a/src/Levels/Level 1.gd +++ b/src/Levels/Level 1.gd @@ -1 +1,34 @@ extends LevelTemplate + +func _ready() -> void: + var t = Timer.new() + t.set_wait_time(3) + t.set_one_shot(true) + self.add_child(t) + t.start() + yield(t, "timeout") + levelState.register_tutorial("move_right") + spawn_tutorial_thingy("move_right") + t.start() + yield(t, "timeout") + levelState.register_tutorial("move_left") + spawn_tutorial_thingy("move_left") + t.start() + yield(t, "timeout") + levelState.register_tutorial("jump") + spawn_tutorial_thingy("jump") + t.queue_free() + + +func spawn_tutorial_thingy(action: String) -> void: + var show_prompt: bool = GlobalState.gsr["tutorial_prompts"][action] + var thingy = ResourceLoader.load("res://src/UserInterface/TutorialThingy.tscn") + var instance = thingy.instance() + instance.action = action + var center = get_tree().root.get_child(3).get_node("%BlobbyCam").get_camera_screen_center() + var viewport = get_viewport_rect().size - Vector2(40,40) + instance.position = Vector2(rand_range(center.x - viewport.x/2, center.x + viewport.x/2), rand_range(center.y - viewport.y/2, center.y + viewport.y/2)) + instance.velocity *= Vector2(sign(rand_range(-1,1)),sign(rand_range(-1,1))) + add_child(instance) + + diff --git a/src/Levels/New Tiles Level.tscn b/src/Levels/New Tiles Level.tscn index 38fa3ba..cec0b89 100644 --- a/src/Levels/New Tiles Level.tscn +++ b/src/Levels/New Tiles Level.tscn @@ -35,9 +35,6 @@ wait_time = 20.0 unique_name_in_owner = true drag_margin_bottom = 0.3 -[node name="AnimatedSprite" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="4"] -frame = 4 - [node name="AnimatedSprite2" parent="BlobbyCam/ParallaxBackground/ParallaxLayer5" index="5"] frame = 3 diff --git a/src/Levels/Templates/LevelTemplate.gd b/src/Levels/Templates/LevelTemplate.gd index 5bd2381..9d9d606 100644 --- a/src/Levels/Templates/LevelTemplate.gd +++ b/src/Levels/Templates/LevelTemplate.gd @@ -5,6 +5,8 @@ onready var signalManager := $"%SignalManager" onready var levelState := $"%LevelState" func _ready() -> void: - signalManager.emit_signal("level_loaded") - get_tree().paused = false - + # should spawn the tutorial thingies which are still remembered in the progress dictionary + signalManager.emit_signal("level_loaded") + get_tree().paused = false + + diff --git a/src/Levels/TutorialThingy.gd b/src/Levels/TutorialThingy.gd deleted file mode 100644 index e809148..0000000 --- a/src/Levels/TutorialThingy.gd +++ /dev/null @@ -1,38 +0,0 @@ -extends Sprite -var screen_size: Vector2 -var velocity = Vector2(1,1) -onready var blobby = $"%Blobby" -onready var cam = $"%BlobbyCam" - - -func _ready() -> void: - $ControllerButton.path = "move_right" - screen_size = cam.screen_rect - -func _process(delta: float) -> void: - # TODO process less in each frame - self.texture = $ControllerButton.icon - self.scale.x = 0.5 - self.scale.y = 0.5 - var tex_size = Vector2(self.texture.get_width(), self.texture.get_height()/4) - var up_left_pos = cam.get_global_transform().affine_inverse() * (position - tex_size/2) - var down_right_pos = cam.get_global_transform().affine_inverse() * (position + tex_size/2) - - var screen_stretch_factor = get_tree().root.size / screen_size - var blobby_screen_pos = (blobby.get_viewport_transform() * (blobby.position))/screen_stretch_factor - var corrected_screen = screen_size - blobby_screen_pos - print(get_tree().root.get_visible_rect().position) - - if up_left_pos.x + blobby_screen_pos.x <= 0: - velocity.x = 1 - elif down_right_pos.x >= corrected_screen.x: - velocity.x = -1 - - if up_left_pos.y + blobby_screen_pos.y <= 0: - velocity.y = 1 - elif down_right_pos.y >= corrected_screen.y: - velocity.y = -1 - - self.position += velocity - - diff --git a/src/UserInterface/Screens/InGameMenu/PauseScreen.gd b/src/UserInterface/Screens/InGameMenu/PauseScreen.gd index 7404505..52dc4f1 100644 --- a/src/UserInterface/Screens/InGameMenu/PauseScreen.gd +++ b/src/UserInterface/Screens/InGameMenu/PauseScreen.gd @@ -8,34 +8,34 @@ onready var pause_title: Label = get_node("PauseOverlay/Title") var paused := false setget set_paused func _ready(): - #signalManager.connect("player_died", self, "_on_GlobalState_player_died") - $ControlsMenu.visible = false - pass + #signalManager.connect("player_died", self, "_on_GlobalState_player_died") + $ControlsMenu.visible = false + pass func _on_GlobalState_player_died() -> void: - self.paused = true - pause_title.text = "You lost" + self.paused = true + pause_title.text = "You lost" func _unhandled_input(event: InputEvent) -> void: - # TODO don't match for specific text... why did i even consider that... did I pull that from the tutorial??? - if event.is_action_pressed("pause") && pause_title.text != "You lost" && !$ControlsMenu.visible: - #not oder ! schaltet einen boolean um - #Ist self hier notwendig? - self.paused = not paused - get_tree().set_input_as_handled() + # TODO don't match for specific text... why did i even consider that... did I pull that from the tutorial??? + if event.is_action_pressed("pause") && pause_title.text != "You lost" && !$ControlsMenu.visible: + #not oder ! schaltet einen boolean um + #Ist self hier notwendig? + self.paused = not paused + get_tree().set_input_as_handled() func set_paused(value: bool) -> void: - paused = value - get_tree().paused = value - pause_overlay.visible = value - if value == true: - $PauseOverlay/VBoxContainer/Continue.grab_focus() + paused = value + get_tree().paused = value + pause_overlay.visible = value + if value == true: + $PauseOverlay/VBoxContainer/Continue.grab_focus() func _on_Controls_button_up() -> void: - $ControlsMenu.visible = true - $PauseOverlay.visible = false - $ControlsMenu/ProfilesMenu.grab_focus() + $ControlsMenu.visible = true + $PauseOverlay.visible = false + $ControlsMenu/ProfilesMenu.grab_focus() diff --git a/src/UserInterface/Screens/MainMenu/ControlsMenu/ControlsMenu.gd b/src/UserInterface/Screens/MainMenu/ControlsMenu/ControlsMenu.gd index 62e54b5..883160b 100644 --- a/src/UserInterface/Screens/MainMenu/ControlsMenu/ControlsMenu.gd +++ b/src/UserInterface/Screens/MainMenu/ControlsMenu/ControlsMenu.gd @@ -5,36 +5,36 @@ onready var changes_made := false onready var changes_saved := false func _ready(): - $InputMapper.connect('profile_changed', self, 'rebuild') - $InputMapper.initialize_profiles() - $ProfilesMenu.initialize($InputMapper) - $ProfilesMenu.grab_focus() - $InputMapper.change_profile($ProfilesMenu.selected) + $InputMapper.connect('profile_changed', self, 'rebuild') + $InputMapper.initialize_profiles() + $ProfilesMenu.initialize($InputMapper) + $ProfilesMenu.grab_focus() + $InputMapper.change_profile($ProfilesMenu.selected) func rebuild(input_profile): - _action_list.clear() - var first = true - for input_action in input_profile.keys(): - if(input_action.ends_with("_old") || input_action.begins_with(("ui_"))): - continue - var line = _action_list.add_input_line(input_action, input_profile[input_action]) - if first: - $ProfilesMenu.focus_neighbour_bottom = line.get_child(2).get_path() - $Back.focus_neighbour_top = line.get_child(2).get_path() - $Save.focus_neighbour_top = line.get_child(2).get_path() - $Reset.focus_neighbour_top = line.get_child(2).get_path() - first = false - line.get_child(2).focus_neighbour_left = $Back.get_path() - line.get_child(2).focus_neighbour_right = $Save.get_path() - line.connect('change_button_pressed', self, '_on_InputLine_change_button_pressed', [input_action, line]) + _action_list.clear() + var first = true + for input_action in input_profile.keys(): + if(input_action.ends_with("_old") || input_action.begins_with(("ui_"))): + continue + var line = _action_list.add_input_line(input_action, input_profile[input_action]) + if first: + $ProfilesMenu.focus_neighbour_bottom = line.get_child(2).get_path() + $Back.focus_neighbour_top = line.get_child(2).get_path() + $Save.focus_neighbour_top = line.get_child(2).get_path() + $Reset.focus_neighbour_top = line.get_child(2).get_path() + first = false + line.get_child(2).focus_neighbour_left = $Back.get_path() + line.get_child(2).focus_neighbour_right = $Save.get_path() + line.connect('change_button_pressed', self, '_on_InputLine_change_button_pressed', [input_action, line]) func _on_InputLine_change_button_pressed(action_name, line): - var old_event = $InputMapper.get_selected_profile()[action_name] - $"%KeySelectMenu".open() - var event = yield($"%KeySelectMenu", "key_selected") - if event == null: - return - if($InputMapper.change_action_key(action_name, event, old_event)): - changes_made = true - changes_saved = false - line.update_key(event) + var old_event = $InputMapper.get_selected_profile()[action_name] + $"%KeySelectMenu".open() + var event = yield($"%KeySelectMenu", "key_selected") + if event == null: + return + if($InputMapper.change_action_key(action_name, event, old_event)): + changes_made = true + changes_saved = false + line.update_key(event) diff --git a/src/UserInterface/Screens/MainMenu/ControlsMenu/ControlsMenu.tscn b/src/UserInterface/Screens/MainMenu/ControlsMenu/ControlsMenu.tscn index fdedfd7..6f3770d 100644 --- a/src/UserInterface/Screens/MainMenu/ControlsMenu/ControlsMenu.tscn +++ b/src/UserInterface/Screens/MainMenu/ControlsMenu/ControlsMenu.tscn @@ -183,8 +183,6 @@ follow_focus = true [node name="ActionKeyList" type="VBoxContainer" parent="KeymapViewer/ScrollContainer"] unique_name_in_owner = true -margin_right = 552.0 -margin_bottom = 177.0 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource( 14 ) diff --git a/src/UserInterface/TutorialThingy.gd b/src/UserInterface/TutorialThingy.gd new file mode 100644 index 0000000..d516958 --- /dev/null +++ b/src/UserInterface/TutorialThingy.gd @@ -0,0 +1,63 @@ +extends Node2D +export var action = "move_right" +export var velocity = Vector2(0.309,0.309) +export var press_limit = 3 + +onready var levelState := get_tree().root.get_child(3).get_node("%LevelState") +onready var cam = null +onready var button = $Button + +var screen_size: Vector2 +var tex_size: Vector2 +var screen_stretch_factor: Vector2 + +func _unhandled_input(event: InputEvent) -> void: + if(ControllerIcons.parse_event(event) == button.texture): + press_limit -= 1 + if press_limit < -1: + # Should delete itself from the progress dictionary when destroyed + $AnimationPlayer.play("cease_4_exist") + +func _ready() -> void: + # Should save itself into the progress dictionary upon creation + $Button/ControllerButton.path = action + button.texture = $Button/ControllerButton.icon + button.scale.x = 0.4 + button.scale.y = 0.4 + tex_size = Vector2(button.texture.get_width()/2, button.texture.get_height()/2) * button.scale * 0.75 + $Area/CollisionShape2D.shape.extents = tex_size + +func _physics_process(delta: float) -> void: + if(cam == null): + cam = get_tree().root.get_child(3).get_node("%BlobbyCam") + return + button.texture = $Button/ControllerButton.icon + # TODO process less in each frame + var up_left_pos = cam.get_global_transform().affine_inverse() * (position - tex_size * self.scale) + var down_right_pos = cam.get_global_transform().affine_inverse() * (position + tex_size * self.scale) + + if up_left_pos.x <= cam.screen_left.x: + velocity.x = abs(velocity.x) + elif down_right_pos.x >= cam.screen_right.x: + velocity.x = -abs(velocity.x) + + if up_left_pos.y <= cam.screen_top.y: + velocity.y = abs(velocity.y) + elif down_right_pos.y >= cam.screen_bottom.y: + velocity.y = -abs(velocity.y) + + self.position += velocity + + +func _on_Area_area_entered(area: Area2D) -> void: + var d = area.global_position - position + print(d) + if(abs(d.y) > tex_size.y * 2 - 1): + velocity.y *= -1 + pass + else: + velocity.x *=-1 + +func _lesson_learned() -> void: + levelState.absolved_tutorial(action) + queue_free() diff --git a/src/UserInterface/TutorialThingy.tscn b/src/UserInterface/TutorialThingy.tscn new file mode 100644 index 0000000..6ca4837 --- /dev/null +++ b/src/UserInterface/TutorialThingy.tscn @@ -0,0 +1,97 @@ +[gd_scene load_steps=8 format=2] + +[ext_resource path="res://assets/effects/pixelDissolve.tres" type="Material" id=1] +[ext_resource path="res://src/UserInterface/TutorialThingy.gd" type="Script" id=2] +[ext_resource path="res://addons/controller_icons/objects/Button.gd" type="Script" id=3] +[ext_resource path="res://assets/environment/decor/screen/Screen3.png" type="Texture" id=4] + +[sub_resource type="RectangleShape2D" id=3] + +[sub_resource type="Animation" id=4] +length = 0.001 +tracks/0/type = "value" +tracks/0/path = NodePath("Button:material:shader_param/progress") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ 0.0 ] +} + +[sub_resource type="Animation" id=5] +resource_name = "cease_4_exist" +length = 0.618 +tracks/0/type = "value" +tracks/0/path = NodePath("Button:material:shader_param/progress") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0, 0.3 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0.0, 1.0 ] +} +tracks/1/type = "method" +tracks/1/path = NodePath(".") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0.4 ), +"transitions": PoolRealArray( 1 ), +"values": [ { +"args": [ ], +"method": "_lesson_learned" +} ] +} + +[node name="TutorialThingy" type="Node2D"] +script = ExtResource( 2 ) + +[node name="Button" type="Sprite" parent="."] +material = ExtResource( 1 ) +z_index = 3 + +[node name="ControllerButton" type="Button" parent="Button"] +visible = false +margin_right = 12.0 +margin_bottom = 20.0 +script = ExtResource( 3 ) + +[node name="Label" type="Label" parent="."] +visible = false +margin_left = -34.0 +margin_top = -49.0 +margin_right = 99.0 +margin_bottom = 50.0 +text = "Move Right: + + + + +" +uppercase = true + +[node name="Area" type="Area2D" parent="."] +collision_layer = 512 +collision_mask = 512 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area"] +shape = SubResource( 3 ) + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +anims/RESET = SubResource( 4 ) +anims/cease_4_exist = SubResource( 5 ) + +[node name="Sprite" type="Sprite" parent="."] +visible = false +texture = ExtResource( 4 ) + +[connection signal="area_entered" from="Area" to="." method="_on_Area_area_entered"] diff --git a/src/Utilities/GlobalStateResource.gd b/src/Utilities/GlobalStateResource.gd index d499b53..0f21780 100644 --- a/src/Utilities/GlobalStateResource.gd +++ b/src/Utilities/GlobalStateResource.gd @@ -3,5 +3,5 @@ class_name GlobalStateResource export(Dictionary) var progress_dict := {} export(int) var wallet := 0 +export(Dictionary) var tutorial_prompts := {} export(Dictionary) var input_map -export(bool) var show_tutorial diff --git a/src/Utilities/LevelState.gd b/src/Utilities/LevelState.gd index 1b58d92..3b3ae90 100644 --- a/src/Utilities/LevelState.gd +++ b/src/Utilities/LevelState.gd @@ -12,81 +12,92 @@ var frees: = 0 setget set_frees var is_dead: = false setget set_dead func _ready() -> void: - signalManager.connect("level_completed", self, "_on_level_completed") - signalManager.connect("player_died", self, "player_dying") + signalManager.connect("level_completed", self, "_on_level_completed") + signalManager.connect("player_died", self, "player_dying") func reset() -> void: - deaths = 0 - kills = 0 - currency = 0 - frees = 0 - # TODO Maybe not the place for this? - if GlobalState.gsr.progress_dict.has(levelName): - GlobalState.gsr.progress_dict[levelName].erase("savepoint") + deaths = 0 + kills = 0 + currency = 0 + frees = 0 + # TODO Maybe not the place for this? + if GlobalState.gsr.progress_dict.has(levelName): + GlobalState.gsr.progress_dict[levelName].erase("savepoint") func set_currency(value: int) -> void: - currency = value - signalManager.emit_signal("currency_updated") + currency = value + signalManager.emit_signal("currency_updated") func set_deaths(value: int) -> void: - deaths = value + deaths = value func set_kills(value: int) -> void: - kills = value - signalManager.emit_signal("kills_updated") + kills = value + signalManager.emit_signal("kills_updated") func set_frees(value: int) -> void: - frees = value - signalManager.emit_signal("frees_updated") + frees = value + signalManager.emit_signal("frees_updated") func set_dead(value: bool) -> void: - is_dead = value + is_dead = value + +func register_tutorial(action_path: String) -> void: + if(GlobalState.gsr.tutorial_prompts.has(action_path)): + return + GlobalState.gsr.tutorial_prompts[action_path] = true + GlobalState.save() + +func absolved_tutorial(action_path: String) -> void: + if(!GlobalState.gsr.tutorial_prompts.has(action_path)): + return + GlobalState.gsr.tutorial_prompts[action_path] = false + GlobalState.save() # Spends the currency when enough is available # and returns true if so. Else it does not spend and return false. func spend_currency(cost: int) -> bool: - if GlobalState.gsr.wallet + currency < cost: - return false - var remainder = currency - cost - if remainder >= 0: - currency = remainder - else: - currency = 0 - GlobalState.set_wallet(GlobalState.gsr.wallet + remainder) - return true - + if GlobalState.gsr.wallet + currency < cost: + return false + var remainder = currency - cost + if remainder >= 0: + currency = remainder + else: + currency = 0 + GlobalState.set_wallet(GlobalState.gsr.wallet + remainder) + return true func _on_level_completed(): - update_global_state() - reset() + update_global_state() + reset() func update_global_state() -> void: - var progress_dict : Dictionary = GlobalState.get_progress() - var levelProgress : Dictionary = {} + var progress_dict : Dictionary = GlobalState.get_progress() + var levelProgress : Dictionary = {} - levelProgress["currency"] = currency - levelProgress["kills"] = kills - levelProgress["deaths"] = deaths - levelProgress["frees"] = frees - - # TODO Doesnt account for multiple plays of same level - if !progress_dict.has(levelName): - progress_dict[levelName] = levelProgress - else: - progress_dict[levelName]["currency"] = GlobalState.get_property_value(levelName,"currency") + currency - progress_dict[levelName]["kills"] = GlobalState.get_property_value(levelName,"kills") + kills - progress_dict[levelName]["deaths"] = GlobalState.get_property_value(levelName,"deaths") + deaths - progress_dict[levelName]["frees"] = GlobalState.get_property_value(levelName,"frees") + frees + levelProgress["currency"] = currency + levelProgress["kills"] = kills + levelProgress["deaths"] = deaths + levelProgress["frees"] = frees + + # TODO Doesnt account for multiple plays of same level + if !progress_dict.has(levelName): + progress_dict[levelName] = levelProgress + else: + progress_dict[levelName]["currency"] = GlobalState.get_property_value(levelName,"currency") + currency + progress_dict[levelName]["kills"] = GlobalState.get_property_value(levelName,"kills") + kills + progress_dict[levelName]["deaths"] = GlobalState.get_property_value(levelName,"deaths") + deaths + progress_dict[levelName]["frees"] = GlobalState.get_property_value(levelName,"frees") + frees - # TODO Wallet is independant from progress_dict because??? - GlobalState.set_wallet(GlobalState.gsr.wallet + currency) - GlobalState.set_progress(progress_dict) + # TODO Wallet is independant from progress_dict because??? + GlobalState.set_wallet(GlobalState.gsr.wallet + currency) + GlobalState.set_progress(progress_dict) func player_dying(animation_number: int = 0) -> void: - currency = 0 - kills = 0 - frees = 0 - is_dead = true - deaths += 1 - update_global_state() - deaths = 0 + currency = 0 + kills = 0 + frees = 0 + is_dead = true + deaths += 1 + update_global_state() + deaths = 0