295 lines
10 KiB
GDScript
295 lines
10 KiB
GDScript
extends Node2D
|
|
|
|
@onready var camera: Camera2D = $Camera2D
|
|
@onready var countdown: Timer = $countdown
|
|
#@onready var countdown_label: Label = $hud/countdown_label
|
|
|
|
@onready var cars: Node = $cars
|
|
|
|
@onready var game_hud_animations: AnimationPlayer = $hud/game_hud_animations
|
|
|
|
@onready var times_container: HFlowContainer = $hud/times_container
|
|
@onready var timer_close: Timer = $timer_close
|
|
|
|
@onready var highscore_label: Label = $hud/highscore_label
|
|
|
|
|
|
const caroffset= 32+4 #space cars on start line
|
|
const caroffset_side= 16
|
|
const caroffset_rear= 32+16
|
|
|
|
var viewCarMargin=Vector2(0.7,0.7) #1=zoom out full speed when car is at border. 0.9 zoom out full speed when car is 10% away from border
|
|
var viewCarMargin_zoomstart=viewCarMargin-Vector2(0.2,0.2) #start zooming
|
|
var viewCarMargin_zoombackup=viewCarMargin_zoomstart-Vector2(0.1,0.1) #start zooming back in
|
|
var zoomspeed=0.5
|
|
var zoomspeed_backup=0.1 #relative to screen size
|
|
var zoom_normal=1.5
|
|
const CAMERA_POSITION_SPEED=0.02 #0.0 - 1.0, higher=faster
|
|
var camera_zoom_out_lookahead_time=1 #predicted position of car in t seconds for camera coverage. greater value=earlier zoom out and movement of camera
|
|
|
|
var running=false
|
|
|
|
|
|
var time_close_keypressed=1 #seconds to wait after key pressed at endscreen
|
|
|
|
var game_ended=false
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready() -> void:
|
|
|
|
Gamestate.resetTimeElapsed()
|
|
|
|
#countdown.start()
|
|
game_hud_animations.play("game_start")
|
|
#countdown_label.visible=true
|
|
|
|
#Load Map
|
|
var checkpoints :Array[String]=[]
|
|
var mapscene=load(Gamestate.getSelectedMapSceneName())
|
|
var mapsceneinstance=mapscene.instantiate()
|
|
add_child(mapsceneinstance)
|
|
for mapc in mapsceneinstance.get_children():
|
|
if mapc.name.begins_with("area_cp"):
|
|
checkpoints.append(mapc.name)
|
|
|
|
print(str(checkpoints.size())+" Checkpoints found")
|
|
|
|
var i=0
|
|
var players_ranked :Array = Gamestate.getPlayers().duplicate()
|
|
players_ranked.sort_custom(custom_array_sort_rank) #arrange players by handicap/rank
|
|
for player in Gamestate.getPlayers(): #create all players
|
|
#get player rank
|
|
var i_position=0
|
|
while i_position<players_ranked.size() and player.inputkey!=players_ranked[i_position].inputkey:
|
|
i_position+=1
|
|
|
|
var newcarscene=load("res://scenes/car.tscn")
|
|
var newcarinstance=newcarscene.instantiate()
|
|
cars.add_child(newcarinstance)
|
|
newcarinstance.setPlayerinformation(i,player.color,player)
|
|
|
|
#newcarinstance.setPosition(Vector2(0,ceil(i/2.0)*(fmod(i,2)-0.5)*2.0*caroffset)) #all in a row, alternating up/down
|
|
newcarinstance.setPosition(Vector2(caroffset_rear*-1*i_position,(fmod(i_position,2)-0.5)*2.0*caroffset_side))
|
|
newcarinstance.setCheckpoints(checkpoints,Gamestate.getRounds())
|
|
newcarinstance.getCharacterBody().car_finished.connect(_on_car_finished)
|
|
|
|
var startmarker:Sprite2D=mapsceneinstance.get_node("startmarker")
|
|
if startmarker != null:
|
|
var newmarker:Sprite2D=startmarker.duplicate()
|
|
newmarker.position=newcarinstance.getPosition()
|
|
mapsceneinstance.add_child(newmarker)
|
|
print("added startmarker pos="+str(newmarker.position))
|
|
startmarker.queue_free() #remove template marker
|
|
|
|
|
|
print("i_position="+str(i_position)+" carpos is ="+str(newcarinstance.getPosition())+" instancepos="+str(newcarinstance.position)+" colori="+str(player.colori)+" color="+str(player.color))
|
|
i+=1
|
|
|
|
|
|
|
|
|
|
func custom_array_sort_rank(a, b):
|
|
return a.rank > b.rank #a>b = higher rank (more wins) -> better start position
|
|
|
|
|
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
|
func _process(delta: float) -> void:
|
|
if running:
|
|
Gamestate.addTimeElapsed(delta)
|
|
#if !countdown.is_stopped():
|
|
# countdown_label.text=str(round(countdown.time_left))
|
|
|
|
updateCameraMovement(delta)
|
|
|
|
#$hud/debuglabel.text=""+str(calculatedViewCarMargin)+" / "+str(viewCarMargin_zoomstart)+" zoomspeed="+str(mapped_zoomspeed)
|
|
#$hud/debuglabel.text=""+str(calculatedViewCarMargin)+" / "+str(viewsize)+" zoomspeed="+str(mapped_zoomspeed)
|
|
$hud/timer.text=str(round(Gamestate.getTimeElapsed()*1000)/1000.0)
|
|
|
|
|
|
|
|
if game_ended:
|
|
var anyplayerkeypressed=false
|
|
var id=0
|
|
for p in Gamestate.getPlayers():
|
|
if Input.is_action_pressed(Gamestate.userinput_prefix+str(id)):
|
|
anyplayerkeypressed=true
|
|
id+=1
|
|
|
|
|
|
if anyplayerkeypressed and timer_close.time_left<time_close_keypressed:
|
|
timer_close.wait_time=time_close_keypressed #set time back
|
|
timer_close.stop() #prepare for restarting at this time
|
|
if !anyplayerkeypressed and timer_close.is_stopped():
|
|
timer_close.start() #start timer when no key is pressed
|
|
|
|
|
|
func updateCameraMovement(delta: float):
|
|
|
|
var cars=cars.get_children()
|
|
var displayedCarCount=0
|
|
var maxCarSpeed=0
|
|
var minPos=Vector2.ZERO #min/max x and y position of all cars
|
|
var maxPos=Vector2.ZERO
|
|
|
|
var oneDriving=false
|
|
for c in cars: #check if any one car is still driving
|
|
if !c.hasFinished():
|
|
oneDriving=true
|
|
|
|
for c in cars:
|
|
if !c.hasFinished() or !oneDriving:
|
|
var carpos = c.getPosition()
|
|
var carposLookahead= c.getPositionLookahead(camera_zoom_out_lookahead_time)
|
|
$hud/debuglabel.text=""+str(carpos)+"\n"+str(carposLookahead)
|
|
|
|
maxCarSpeed=max(maxCarSpeed,c.getSpeed())
|
|
if displayedCarCount==0:
|
|
minPos=carpos.min(carposLookahead)
|
|
maxPos=carpos.max(carposLookahead)
|
|
else:
|
|
minPos=minPos.min(carpos).min(carposLookahead)
|
|
maxPos=maxPos.max(carpos).max(carposLookahead)
|
|
|
|
displayedCarCount+=1
|
|
|
|
|
|
camera.position=lerp(camera.position,(minPos+maxPos)/2.0,CAMERA_POSITION_SPEED)
|
|
|
|
|
|
|
|
var viewsize = camera.get_viewport_rect().size
|
|
var carSpread=Vector2(maxPos.x-minPos.x,maxPos.y-minPos.y)
|
|
var calculatedViewCarMargin=carSpread*camera.zoom #when cars are screen width apart this number matches viewsize=camera.get_viewport_rect().size
|
|
|
|
|
|
var viewCarMargin_zoomstart_pixels=viewCarMargin_zoomstart*viewsize/camera.zoom
|
|
var viewCarMargin_pixels=viewCarMargin*viewsize/camera.zoom
|
|
var viewCarMargin_zoombackup_pixels=viewCarMargin_zoombackup*viewsize/camera.zoom
|
|
|
|
var mapped_zoomspeed_x=constrain(remap(calculatedViewCarMargin.x , viewCarMargin_zoomstart_pixels.x,viewCarMargin_pixels.x,0,zoomspeed),0,zoomspeed)
|
|
var mapped_zoomspeed_y=constrain(remap(calculatedViewCarMargin.y , viewCarMargin_zoomstart_pixels.y,viewCarMargin_pixels.y,0,zoomspeed),0,zoomspeed)
|
|
var mapped_zoomspeed=max(mapped_zoomspeed_x,mapped_zoomspeed_y)
|
|
if calculatedViewCarMargin.x>viewCarMargin_zoomstart_pixels.x or calculatedViewCarMargin.y>viewCarMargin_zoomstart_pixels.y: #cars not in view
|
|
camera.zoom-=Vector2(mapped_zoomspeed*delta,mapped_zoomspeed*delta)
|
|
elif calculatedViewCarMargin.x<viewCarMargin_zoombackup_pixels.x and calculatedViewCarMargin.y<viewCarMargin_zoombackup_pixels.y: #cars in view again
|
|
|
|
if camera.zoom.x<zoom_normal:
|
|
camera.zoom+=Vector2(zoomspeed_backup*delta,zoomspeed_backup*delta)
|
|
|
|
func _input(ev):
|
|
#if ev is InputEventKey and
|
|
if Input.is_action_just_pressed("ui_cancel"):
|
|
get_tree().change_scene_to_file("res://scenes/menu.tscn")
|
|
|
|
|
|
func checkPositionInsideView(checkpos: Vector2) -> bool:
|
|
|
|
var viewsize = camera.get_viewport_rect().size
|
|
var canvas_pos = camera.get_viewport().get_canvas_transform().affine_inverse() * -checkpos
|
|
|
|
print("cp="+str(canvas_pos) + " viewsize="+str(viewsize))
|
|
|
|
if canvas_pos.x < 0 and canvas_pos.x > -viewsize.x:
|
|
if canvas_pos.y < 0 and canvas_pos.y > -viewsize.y:
|
|
return true
|
|
return false
|
|
|
|
|
|
func constrain(val,a,b):
|
|
var vmin=min(a,b)
|
|
var vmax=max(a,b)
|
|
return min(vmax,max(vmin,val))
|
|
|
|
func constrain2(val:Vector2,a:Vector2,b:Vector2):
|
|
var vmin=a.min(b)
|
|
var vmax=a.max(b)
|
|
return vmax.min(vmin.max(val))
|
|
|
|
|
|
func _on_countdown_timeout() -> void:
|
|
running=true
|
|
var cars=cars.get_children()
|
|
for c in cars:
|
|
c.setRunning(true)
|
|
#countdown_label.visible=false
|
|
|
|
|
|
func _on_car_finished(playerid,finalTime) -> void:
|
|
times_container.addFinishedPlayer(playerid,finalTime)
|
|
|
|
if times_container.getPlayersFinished() == Gamestate.getPlayers().size(): #all players have finish times
|
|
finishGame()# Game finished
|
|
|
|
|
|
|
|
func finishGame():
|
|
print("Game Finished")
|
|
running=false
|
|
game_ended=true
|
|
|
|
#change rank for players
|
|
|
|
var players_ranked :Array = Gamestate.getPlayers().duplicate()
|
|
players_ranked.sort_custom(custom_array_sort_player_finaltime) #arrange players by handicap/rank
|
|
|
|
var i=0
|
|
for p in players_ranked:
|
|
print("Player "+str(i)+" key="+str(OS.get_keycode_string(p.inputkey))+" was rank "+str(p.rank))
|
|
var floatrank=1.0-(i*1.0/(Gamestate.getPlayers().size()-1)) #winning player=1.0, losing player=0.0
|
|
var rankchangedown=-0.2 #change rank down limit
|
|
var rankchangeup=0.5 #change rank up limit
|
|
p.rank=p.rank+min(max((floatrank-p.rank),rankchangedown),rankchangeup)
|
|
print(" changed to rank "+str(p.rank))
|
|
i+=1
|
|
|
|
|
|
if Gamestate.getPlayers().size()==1: #was played in singleplayer
|
|
var timediff=HighscoreHandler.updateHighscore(Gamestate.getSelectedMapName(),"normal",str(Gamestate.getRounds()),getfinalTimeByPlayer(Gamestate.getPlayers()[0]))
|
|
print("Timediff="+str(timediff))
|
|
timediff=round(timediff*1000)/1000
|
|
highscore_label.visible=true
|
|
|
|
if timediff==0: #same time or first entry
|
|
highscore_label.text=str(timediff)
|
|
highscore_label.modulate=Color(200,200,0)
|
|
elif timediff<0: #new highscore
|
|
highscore_label.text=str(timediff)
|
|
highscore_label.modulate=Color(0,0,200)
|
|
elif timediff>0: #worse time
|
|
highscore_label.text="+"+str(timediff)
|
|
highscore_label.modulate=Color(200,0,0)
|
|
|
|
|
|
game_hud_animations.play("game_end")
|
|
timer_close.start()
|
|
#overlaycolor.visible=true
|
|
|
|
|
|
func custom_array_sort_player_finaltime(a, b):
|
|
#var cars=cars.get_children()
|
|
var finalTimeA=getfinalTimeByPlayer(a)
|
|
var finalTimeB=getfinalTimeByPlayer(b)
|
|
if finalTimeA != null and finalTimeB != null:
|
|
return finalTimeA < finalTimeB
|
|
else:
|
|
return false
|
|
#for c in cars:
|
|
# if c.reference_gamestateplayer==a:
|
|
#finalTimeA=c.getCharacterBody().finalTime
|
|
#elif c.reference_gamestateplayer==b:
|
|
#finalTimeB=c.getCharacterBody().finalTime
|
|
|
|
|
|
func getfinalTimeByPlayer(player):
|
|
var cars=cars.get_children()
|
|
for c in cars:
|
|
if c.reference_gamestateplayer==player:
|
|
return c.getCharacterBody().finalTime
|
|
return null
|
|
|
|
func _on_timer_close_timeout() -> void:
|
|
print("Close Map")
|
|
get_tree().change_scene_to_file("res://scenes/menu.tscn")
|
|
|
|
|