270 lines
11 KiB
GDScript
270 lines
11 KiB
GDScript
extends Camera2D
|
|
|
|
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 * 2
|
|
export var alarm_light_shader: Material
|
|
export var fixed_position : bool = false
|
|
|
|
onready var levelState := $"%LevelState"
|
|
onready var signalManager := $"%SignalManager"
|
|
onready var shiftLeft = $CameraAnimationPlayer.get_animation("shiftingLeft")
|
|
onready var shiftRight = $CameraAnimationPlayer.get_animation("shiftingRight")
|
|
onready var shiftCenter = $CameraAnimationPlayer.get_animation("shiftingCenter")
|
|
onready var anim_player := $CameraAnimationPlayer
|
|
onready var original_x_zoom := zoom.x
|
|
onready var original_y_zoom := zoom.y
|
|
onready var blobby := get_node("%Blobby")
|
|
|
|
var horizontal_facing = 0
|
|
var vertical_facing = 0
|
|
var camera_vertical_shift = 0
|
|
var right_move_time: float = 0
|
|
var left_move_time: float = 0
|
|
var slow_time: float = 0
|
|
var input_time: float = 0
|
|
var original_limit_left: int
|
|
var original_limit_right: int
|
|
var original_limit_bottom: int
|
|
var original_limit_top: int
|
|
var camera_is_panning: bool = false
|
|
var target_offset: Vector2 = Vector2(0,0)
|
|
|
|
var terminal_activated: bool = false
|
|
var image = Image.new()
|
|
var texture = ImageTexture.new()
|
|
var prev_pos: Vector2
|
|
var camera_state := "centered"
|
|
|
|
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()
|
|
var screen_left = Vector2()
|
|
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()
|
|
get_tree().get_root().connect("size_changed", self, "_set_boundaries")
|
|
if !fixed_position:
|
|
self.position = blobby.global_position
|
|
image.create(128, 2, false, Image.FORMAT_RGBAH)
|
|
# TODO Test Performance
|
|
material.set_shader_param("light_data", null)
|
|
_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):
|
|
terminal_activated = true
|
|
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 _physics_process(delta: float) -> void:
|
|
if fixed_position:
|
|
return
|
|
# 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)
|
|
|
|
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()
|
|
|
|
# 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():
|
|
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
|
|
|
|
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 += 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)/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
|
|
|
|
func _death_cam(animation_number: int = 0) -> void:
|
|
if(animation_number < 1):
|
|
$CameraAnimationPlayer.play("deathCamJustZoom")
|
|
if(animation_number == 1):
|
|
$CameraAnimationPlayer.play("deathCamLateRotation")
|
|
|
|
# TODO Rename to alarm lights specially
|
|
func _update_lighting_shader() -> void:
|
|
if !terminal_activated: return
|
|
# 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()
|
|
|
|
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())
|