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
## A hammer.
extends Area2D
class_name Hammer

@icon("res://assets/hammers/hammer01.png")

@onready var left_cast := $LeftCast as RayCast2D
@onready var right_cast := $RightCast as RayCast2D
@onready var target_cast := $TargetCast as RayCast2D
@onready var trail := $Trail as Trail2D
@onready var outline_shader := ($Sprite as Sprite2D).material as ShaderMaterial
@onready var target_finder := $TargetFinder as Area2D
@onready var hitbox := $Hitbox as Hitbox
@onready var hitbox_c := hitbox.get_child(0) as CollisionShape2D
@onready var c := $Collision as CollisionShape2D

## The current velocity
var velocity := Vector2.ZERO

## The direction to go in
var direction := Vector2.ZERO

## Acceleration
@export var acceleration := 100.0

## Maximum speed
@export var top_speed := 3.0

## The amount it can turn towards its target
@export var steer_force = 0.01

## The hit enum.
enum HITS {_a, _b, NONE, PLAYER, ENEMY}

## These nodes want to have their collision mask set to the current enemy.
@onready var hitmasks = [target_finder, hitbox, left_cast, right_cast, self, target_cast]

## Pick which layers to hit.
@export var hits: HITS = HITS.PLAYER:
	set(value):
		for node in hitmasks:
			node.set_collision_mask_value(hits, false)
		hits = value
		hitbox.monitoring = hits != HITS.NONE
		target_finder.monitoring = not is_instance_valid(target)
		if value == HITS.NONE:
			return
		for node in hitmasks:
			node.set_collision_mask_value(hits, true)
		hitbox_c.shape.size.x = 8 if hits == HITS.ENEMY else 1
		c.shape.size.x = 8 if hits == HITS.ENEMY else 1

## The amount of time before gravity kicks in.
@export var lifetime := 3.0

## The gravity
@export var grav := 4.0

## The target
var target: Node2D = null

func _ready() -> void:
	unhighlight()
	hits = hits

## Lerps direction towards [param to].
func dirlerp(to: Vector2) -> void:
	direction = lerp(direction, to, steer_force * clampf(lifetime, 0, 1))

## Moves the direction towards the target.
func seek() -> void:
	if is_instance_valid(target):
		target_cast.force_raycast_update()
		if is_enemy(target_cast.get_collider()): # course is good, dont touch anything
			return
		dirlerp(global_position.direction_to(target.global_position))
		anticrash()
	elif target_finder.monitoring == false:
		target = null
		target_finder.monitoring = true

func is_enemy(n) -> bool:
	if is_instance_valid(target) and is_instance_valid(n) and n.get_class() == target.get_class(): return true
	return false

## Tries not to crash.
func anticrash() -> void:
	var is_wall := func is_wall(ray: RayCast2D) -> bool:
		ray.force_raycast_update()
		return not is_enemy(ray.get_collider())

	var results: Array[bool] = [is_wall.call(left_cast), is_wall.call(right_cast)]
	if results.count(true) == 2: return # resign to our fate

	for i in range(2):
		if results[i]:
			dirlerp(direction.rotated(0.174 if i == 0 else -0.174))

## Highlights this hammer. See also [method unhighlight].
func highlight() -> void:
	outline_shader.set_shader_parameter(&"line_width", .75)


## Un-highlights this hammer. See also [method highlight].
func unhighlight() -> void:
	outline_shader.set_shader_parameter(&"line_width", 0)


func _physics_process(delta: float) -> void:
	lifetime -= delta
	if lifetime < 0:
		velocity.y += grav * delta
	else:
		seek()
		velocity += (direction * acceleration * delta)
		if velocity.y < 0:
			velocity.y = lerpf(velocity.y, 0, .1)  # hard to move up
	velocity.x = clampf(velocity.x, -top_speed, top_speed)
	velocity.y = clampf(velocity.y, -top_speed, top_speed)
	rotation = velocity.angle() + PI / 2  # face forward
	global_position += velocity

# we crashed
func _on_body_entered(_body: Node2D) -> void:
	trail.emitting = false
	target_finder.monitoring = false
	hitbox.monitoring = false
	set_collision_layer_value(7, true)
	global_position += velocity.limit_length(1)  # go into the wall a little
	velocity = Vector2.ZERO
	hits = HITS.NONE
	target = null
	steer_force = 0.05
	lifetime = 3
	set_physics_process(false)
	trail.clear_points()


## Throws this [Hammer].
func throw(p_direction: Vector2) -> void:
	set_collision_layer_value(7, false)
	direction = p_direction
	trail.emitting = true
	set_physics_process(true)


func _on_target_finder_node_entered(_n: Node2D) -> void:
	if target != null:
		push_error("Huh.")
		return
	var bods: Array[Node2D] = target_finder.get_overlapping_bodies() + target_finder.get_overlapping_areas()
	var space = get_world_2d().direct_space_state
	var current := {closest = null, dist = 0}
	for bod in bods:
		var res := space.intersect_ray(PhysicsRayQueryParameters2D.create(global_position, bod.global_position.move_toward(global_position, .1), pow(2, 1-1)))
		if res.is_empty():
			var dist := global_position.distance_to(bod.global_position)
			if current.dist < dist:
				current.dist = dist
				current.closest = bod
	if current.closest != null:
		target = current.closest
		target_finder.set_deferred(&"monitoring", false)