Files
swarm-farm/entities/mecha/mecha.gd
T
2026-09-01 22:41:37 +02:00

143 lines
3.9 KiB
GDScript

@tool
extends CharacterBody3D
class_name Mecha
const STEP_MOVE_DISPLACEMENT = 3
const RECOIL_STRENGHT = 0.5
const MOUSE_SENSIVITY = 0.01
const ZOOM_SENSIVITY = 1.
const MAX_ZOOM = 60.
const MIN_ZOOM = 15.
const MAX_HP : float = 50.
signal died
@export var hp : float = MAX_HP
@export var max_speed : float = 15.
@export var turn_speed : float = 5.
var destination := Vector2()
@onready var legs : Array[MechaLeg] = [
%Leg1,
%Leg2,
%Leg3,
%Leg4,
]
var turn_normal : Vector2 = Vector2.UP
var selected_item = 0
var current_recoil = 0.
func _ready():
for l in legs:
l.step.connect(next_leg_can_step)
func next_leg_can_step():
for l in legs:
l.can_step = false
await get_tree().create_timer(MechaLeg.STEP_DURATION / len(legs)).timeout
for l in legs:
l.can_step = true
func _process(delta):
if not Engine.is_editor_hint():
var target = calculate_target_position()
%Target.global_position = target
var target_turn = (Vector2(position.x,position.z) - Vector2(target.x,target.z)).normalized()
turn_normal = lerp(
turn_normal,
target_turn,
min(turn_speed * delta, 1.)
)
var speed = 0.
var item_used = false
for i in range(len(%Inventory.get_children())):
var item = %Inventory.get_children()[i]
if item is Weapon:
var used = item.process_fire(
delta,
self,
selected_item == i and Input.is_action_pressed("fire")
)
if used:
item_used = true
if not item_used and Input.is_action_pressed("move_to"):
speed = max_speed
var front_factor = 1 - (turn_normal - target_turn).length() / 2.
velocity = position.direction_to(target) * speed * front_factor
%Model.rotation.y = - turn_normal.angle()
# Recoil
%Model.position = Vector3(turn_normal.x, 0, turn_normal.y) * current_recoil * RECOIL_STRENGHT
current_recoil = lerp(current_recoil, 0., 0.5)
%StepTargets.position.x = lerp(
0,
-STEP_MOVE_DISPLACEMENT,
speed/max_speed
)
%Camera3D.look_at(self.position)
move_and_slide()
func calculate_target_position() -> Vector3:
var camera : Camera3D = get_viewport().get_camera_3d()
if camera:
var ray_normal := camera.project_ray_normal(get_viewport().get_mouse_position())
var ray_origin := camera.project_ray_origin(get_viewport().get_mouse_position())
var plane = Plane(Vector3.UP, 0)
var intersection = plane.intersects_ray(ray_origin, ray_normal)
if intersection:
return intersection
return global_position
func get_target_angle() -> float:
return - turn_normal.angle() + PI / 2
func get_projectile_spwan_point() -> Vector3:
return global_position + Vector3.UP * 2.
func add_recoil(recoil : = 1.):
current_recoil += recoil
func hit(damage = 1.):
hp = max(0, hp - damage)
if hp == 0:
die()
func die():
died.emit()
func _unhandled_input(event):
if Input.is_action_just_pressed("zoom_in"):
%Camera3D.position.z = max(MIN_ZOOM, %Camera3D.position.z - ZOOM_SENSIVITY)
if Input.is_action_just_pressed("zoom_out"):
%Camera3D.position.z = min(MAX_ZOOM, %Camera3D.position.z + ZOOM_SENSIVITY)
if event is InputEventMouseMotion and Input.is_action_pressed("turn_camera") and Input.mouse_mode == Input.MOUSE_MODE_VISIBLE:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
%CameraPivot.rotation.y -= event.relative.x * MOUSE_SENSIVITY
%CameraPivot.rotation.x -= event.relative.y * MOUSE_SENSIVITY
if %CameraPivot.rotation.x > deg_to_rad(-20):
%CameraPivot.rotation.x = deg_to_rad(-20)
if %CameraPivot.rotation.x < deg_to_rad(-80):
%CameraPivot.rotation.x = deg_to_rad(-80)