a game about throwing hammers made for the github game off
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
extends Hittable
class_name Player

const DustEffect := preload("res://fx/dust.tscn")
const JumpEffect := preload("res://fx/jump.tscn")
const DoubleJumpEffect := preload("res://fx/double_jump.tscn")
const WallJumpEffect := preload("res://fx/wall_dust.tscn")

signal hp_changed(health: int)

## Accel
@export var accel := 512

## Topspeed
@export var top_speed := 64

## Jump force
@export var jump_force := 150

## The topspeed at which we slide down the wall
@export var max_wall_slide_fall_speed := 110

## The standard speed at which we slide down the wall
@export var wall_slide_fall_speed := 42

## Friction
@export var frict := 0.25

## How much less movement control to have in the air
@export var air_movement_modifier := 0.95

## Max hp
@export var max_health := 10

@onready var sprite := $Sprite as Sprite2D
@onready var anims := $Player as AnimationPlayer
@onready var pickup_area := $PickupArea as Area2D
@onready var aim_gizmo := $AimGizmo as AimGizmo

## The coyote jump timer.
## Allows you to jump after leaving the ground if the timer has not run out.
@onready var coyote := $CoyoteJump as Timer
@onready var GRAVITY: float = ProjectSettings.get_setting(&"physics/2d/default_gravity")

func _init() -> void:
  Globals.player = self

func _exit_tree() -> void:
  Globals.player = null

## State enum.
enum State { MOVE, WALL_SLIDE, STOP }

## Aiming the hammer.
var aiming := false

## Can we double jump now?
var double_jump := true

## The current state, one of the State enums.
var state := State.MOVE

## Have we just jumped?
var just_jumped := false

## The hammer we carry.
var current_hammer: Hammer = null

## The last highlit hammer.
var last_highlit: Hammer = null

var health := max_health:
  set(hp):
    health = hp
    hp_changed.emit(hp)
    if hp == 0:
      queue_free()


func _physics_process(delta: float) -> void:
  just_jumped = false
  match state:
    State.STOP:
      velocity = Vector2.ZERO
      play(&"idle")
    State.MOVE:
      var input := Input.get_axis(&"left", &"right")
      if aiming:
        input = 0
      apply_force(input, delta)
      apply_friction(input)

      jump_check()

      apply_gravity(delta)

      animate(input)
      move()
      wall_slide_check()
    State.WALL_SLIDE:
      play(&"wall_slide")
      var wall_axis := get_wall_axis()
      if wall_axis != 0:
        sprite.scale.x = wall_axis

      wall_slide_jump_check(wall_axis)
      wall_slide_drop(delta)
      move()
      wall_detatch(wall_axis, delta)
  check_throw()
  hammer_highlight()
  hammer_pickup()
  hammer_move()

## Creates floor dust.
func dust() -> void:
  var dust_position := global_position
  dust_position.x += randf_range(-4, 4)
  Utils.instance_scene_on_main(DustEffect, dust_position)
  # SoundFx.play("Step", -20)

## Applys gravity.
func apply_gravity(delta: float) -> void:
  velocity.y += GRAVITY * delta
  velocity.y = minf(velocity.y, jump_force)

## Applys force with the [param input] of the player.
func apply_force(input: float, delta: float) -> void:
  if input != 0:
    velocity.x += input * accel * delta
    velocity.x = clampf(velocity.x, -top_speed, top_speed)
    if not is_on_floor():
      velocity.x *= air_movement_modifier

## Applys friction to the player.
func apply_friction(input: float) -> void:
  if input == 0 and not is_zero_approx(velocity.x) and is_on_floor():
    velocity.x = lerpf(velocity.x, 0, frict)

func hammer_highlight() -> void:
  if not current_hammer:
    var hamms := pickup_area.get_overlapping_areas()
    if hamms.is_empty():
      if last_highlit:
        last_highlit.unhighlight()
        last_highlit = null
    elif not last_highlit in hamms:
      if last_highlit:
        last_highlit.unhighlight()
      last_highlit = hamms[0]
      hamms[0].highlight()

func hammer_pickup() -> void:
  if Input.is_action_just_pressed("pickup") and last_highlit and not current_hammer:
    last_highlit.unhighlight()
    current_hammer = last_highlit
    Globals.levelmanager.current_level.remove_child(current_hammer)
    current_hammer.position.y = -14
    add_child(current_hammer)
    move_child(current_hammer, 0)


func hammer_move() -> void:
  if current_hammer:
    current_hammer.position.x = 6 * sprite.scale.x
    current_hammer.rotation = 0.087 * sprite.scale.x

func check_throw() -> void:
  if current_hammer and Input.is_action_just_pressed("throw"):
    aim_gizmo.enabled = true
    aiming = true
    aim_gizmo.show()

## Plays animations for the move state.
func animate(input: float) -> void:
  if sign(input) != 0:
    sprite.scale.x = sign(input)

  if not is_on_floor():
    play(&"jump")
    return

  if velocity.x != 0:
    play(&"run", sign(velocity.x * sprite.scale.x))
  else:
    play(&"idle")

## Plays a [param anim] with a speed of [param speed].
## If speed is negative, animation plays backwards.
func play(anim: StringName, speed: float = 1.0) -> void:
  anims.play(anim, -1, speed)


## Checks if we should jump.
func jump_check() -> void:
  var want2jump := Input.is_action_just_pressed(&"jump")
  if want2jump and (is_on_floor() or coyote.time_left > 0):
    jump(jump_force)
    just_jumped = true
  else:
    if want2jump and velocity.y < -jump_force / 2:
      velocity.y = -jump_force / 2

    if want2jump and double_jump == true:
      jump(jump_force * .75)
      double_jump = false

## Jumps with [param force] force.
func jump(force: float) -> void:
  # SoundFx.play("Jump", -20)
  if double_jump:
    Utils.instance_scene_on_main(JumpEffect, global_position)
  else:
    Utils.instance_scene_on_main(DoubleJumpEffect, global_position)
  velocity.y = -force

## Uses the velocity to move_and_slide.
func move() -> void:
  var was_in_air := not is_on_floor()
  var was_on_floor := is_on_floor()
  var last_position := position
  var last_velocity := velocity
  move_and_slide()

  # landing
  if was_in_air and is_on_floor():
    velocity.x = last_velocity.x
    double_jump = true
    Utils.instance_scene_on_main(JumpEffect, global_position)

  # just left ground
  if was_on_floor and not is_on_floor() and not just_jumped:
    position.y = last_position.y
    coyote.start()
    Utils.instance_scene_on_main(JumpEffect, global_position)

## Checks if we should enter a wall slide.
func wall_slide_check():
  if not is_on_floor() and is_on_wall_only():
    state = State.WALL_SLIDE
    double_jump = true
    dust()

## Checks what wall we are against.
func get_wall_axis() -> int:
  var is_right_wall := test_move(transform, Vector2.RIGHT)
  var is_left_wall := test_move(transform, Vector2.LEFT)
  return int(is_left_wall) - int(is_right_wall)

## Checks if we should jump off the [param wall_axis].
func wall_slide_jump_check(wall_axis: int) -> void:
  if Input.is_action_just_pressed(&"jump"):
    velocity.x = wall_axis * top_speed
    velocity.y = -jump_force / 1.25
    state = State.MOVE
    wall_dust(wall_axis)
    # SoundFx.play("Jump", -20)

## Creates dust against the [param wall_axis].
func wall_dust(wall_axis: int) -> void:
  var dust_position = global_position + Vector2(wall_axis * 4, -2)
  var dust_fx := Utils.instance_scene_on_main(WallJumpEffect, dust_position) as Node2D
  dust_fx.scale.x = wall_axis


## Slides down the wall.
func wall_slide_drop(delta: float) -> void:
  var max_slide_speed := wall_slide_fall_speed
  if Input.is_action_pressed("down"):
    max_slide_speed = max_wall_slide_fall_speed
  velocity.y = min(velocity.y + GRAVITY * delta, max_slide_speed)

## Checks if we should detatch from the wall.
func wall_detatch(wall_axis: int, delta: float) -> void:
  var detached := false
  if aiming: return

  if Input.is_action_just_pressed("right"):
    velocity.x = accel * delta
    detached = true

  if Input.is_action_just_pressed("left"):
    velocity.x = -accel * delta
    detached = true

  if detached:
    state = State.MOVE
    wall_dust(wall_axis)

  if wall_axis == 0 or is_on_floor():
    state = State.MOVE

func hit(damage: int) -> void:
  health -= damage

## Disable the aim gizmo.
func disable_aim_gizmo() -> void:
  aiming = false
  aim_gizmo.enabled = false
  aim_gizmo.hide()

## Throws the hammer.
func throw(rot: float) -> void:
  rot += randf_range(-0.01, 0.01)
  remove_child(current_hammer)
  current_hammer.position.x = 0 # center
  current_hammer.global_position = to_global(current_hammer.position)
  Globals.levelmanager.current_level.add_child(current_hammer)
  current_hammer.hit_player = false
  current_hammer.hit_enemys = true
  current_hammer.throw(Vector2.from_angle(rot))
  current_hammer = null
  disable_aim_gizmo()