aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/bullet.gd33
-rw-r--r--scripts/debug.gd22
-rw-r--r--scripts/enemies.gd117
-rw-r--r--scripts/fx/fx_enemy_damage.gd29
-rw-r--r--scripts/gui.gd12
-rw-r--r--scripts/movement.gd385
-rw-r--r--scripts/path.gd72
-rw-r--r--scripts/turret.gd113
-rw-r--r--scripts/utils.gd10
9 files changed, 793 insertions, 0 deletions
diff --git a/scripts/bullet.gd b/scripts/bullet.gd
new file mode 100644
index 0000000..9b4434d
--- /dev/null
+++ b/scripts/bullet.gd
@@ -0,0 +1,33 @@
+extends Spatial
+
+var _enemies_holder
+var shooter
+
+var timer = 0
+var time_life = 3
+var speed = 7
+var hit_something = false
+
+func _ready():
+ _enemies_holder = get_tree().root.get_child(0).find_node("enemies")
+ $Area.connect("body_entered", self, "collided")
+
+func _physics_process(delta):
+ var forward_dir = -global_transform.basis.z.normalized()
+ global_translate(forward_dir * speed * delta)
+
+ timer += delta
+ if timer >= time_life:
+ queue_free()
+
+func collided(body):
+ var parent = body.get_parent()
+ if parent == shooter: return
+
+ if hit_something == false:
+ var groups = parent.get_groups()
+ if "enemies" in groups:
+ _enemies_holder.damage(parent.name)
+
+ hit_something = true
+ queue_free()
diff --git a/scripts/debug.gd b/scripts/debug.gd
new file mode 100644
index 0000000..339eec7
--- /dev/null
+++ b/scripts/debug.gd
@@ -0,0 +1,22 @@
+extends Panel
+
+var _player
+var _path
+var _enemies
+
+func _ready():
+ _path = get_tree().root.get_child(0).find_node("path")
+ _player = get_tree().root.get_child(0).find_node("player")
+ _enemies = get_tree().root.get_child(0).find_node("enemies")
+
+func _process(delta):
+ pass
+
+
+func _on_Button_button_down():
+ _enemies.spawn()
+
+
+func _on_Button2_button_down():
+ var res = _path.load_nodes()
+ _path.hide()
diff --git a/scripts/enemies.gd b/scripts/enemies.gd
new file mode 100644
index 0000000..068814a
--- /dev/null
+++ b/scripts/enemies.gd
@@ -0,0 +1,117 @@
+extends Node
+
+var _path
+var _enemy_blue
+var _dissolve_mat
+
+var _fx_holder
+var _fx_enemy_damage
+
+var _shapes = {}
+
+var serial_enemy = 0
+var enemies = {}
+
+
+func _ready():
+ _fx_holder = get_tree().root.get_child(0).find_node("fx")
+ _path = get_tree().root.get_child(0).find_node("path")
+ _dissolve_mat = load("res://shaders/dissolve_mat.tres")
+ _enemy_blue = load("res://scenes/enemy.tscn")
+ _fx_enemy_damage = load("res://scenes/fx/enemy_damage.tscn")
+
+ load_shapes()
+
+func load_shapes():
+ _shapes = {}
+ var dir = Directory.new()
+ dir.open("res://models/shapes")
+ dir.list_dir_begin(true)
+ var shape = dir.get_next()
+ while shape != "":
+ if (shape.ends_with(".glb")):
+ var model = load("res://models/shapes/" + shape)
+ var sname = shape.substr(0, shape.length()-4)
+ _shapes[sname] = model
+ shape = dir.get_next()
+
+func spawn():
+ var instance = _enemy_blue.instance()
+ add_child(instance)
+ instance.transform.origin = _path.nodes[0].transform.origin;
+ instance.name = str(serial_enemy)
+ var instance_model = _shapes[_shapes.keys()[randi() % _shapes.size()]].instance()
+ instance.add_child(instance_model)
+ var axis : Vector3 = Quat(Vector3(0, randf()*TAU, 0)) * Vector3.RIGHT
+ enemies[serial_enemy] = {
+ "cur": 0, "hp": 10, "rel": 0, "ops": instance_model.name,
+ "axis": [axis.x, axis.y, axis.z] }
+ serial_enemy += 1
+
+func node_from_id (id):
+ return get_node(str(id))
+
+func _physics_process(delta):
+ var delist = []
+ for child in get_children():
+ var id = str(child.name).to_int()
+ var enemy = enemies[id]
+
+ if enemy.hp <= 0:
+ if enemy.ops == "T":
+ delist.append(id)
+ enemy.rel = 0
+ continue
+ else:
+ child.get_node(enemy.ops).queue_free()
+ enemy.ops = enemy.ops.substr(1, enemy.ops.length()-1)
+ enemy.hp = 10
+ var instance_model = _shapes[enemy.ops].instance()
+ child.add_child(instance_model)
+
+ var speed = 1
+ enemy.rel += speed * delta
+ while enemy.rel > 1:
+ enemy.rel -= 1
+ enemy.cur += 1
+
+ var destination = enemy.cur+1
+ if destination >= _path.nodes.size():
+ delist.append(id)
+ enemy.rel = 0
+ continue
+
+ var from = _path.nodes[enemy.cur].transform.origin
+ var to = _path.nodes[destination].transform.origin
+ child.transform.origin = lerp(from, to, enemy.rel)
+
+ var axis = Vector3(enemy.axis[0], enemy.axis[1], enemy.axis[2])
+ child.transform.basis = child.transform.basis.rotated(axis, delta)
+
+ for id in delist:
+ get_node(str(id)).queue_free()
+ enemies.erase(id)
+
+
+func damage(name):
+ var id = int(name)
+ var enemy = enemies[id]
+ enemies[id].hp -= 1
+
+ fx_damage(name)
+
+func fx_damage(name):
+ var id = int(name)
+ var enemy = enemies[id]
+
+ var instance = _fx_enemy_damage.instance()
+ _fx_holder.add_child(instance)
+
+ var node = node_from_id(id)
+ instance.transform = node.transform;
+ instance.refresh_basis()
+
+ var instance_model = _shapes[enemy["ops"]].instance()
+ instance.add_child(instance_model)
+
+ instance.refresh_shader(_dissolve_mat)
diff --git a/scripts/fx/fx_enemy_damage.gd b/scripts/fx/fx_enemy_damage.gd
new file mode 100644
index 0000000..48f4629
--- /dev/null
+++ b/scripts/fx/fx_enemy_damage.gd
@@ -0,0 +1,29 @@
+extends Spatial
+
+var timer = 0
+var timer_life = 0.5
+
+var anim_size = 1
+
+var _mesh : MeshInstance
+
+var base
+
+func refresh_shader(mat):
+ _mesh = get_child(0).get_child(0)
+ _mesh.set_surface_material(0, mat)
+
+func refresh_basis():
+ base = transform.basis
+
+func _physics_process(delta):
+ timer += delta
+ if timer > timer_life:
+ queue_free()
+
+ var amt = timer / timer_life
+
+ _mesh.get_active_material(0).set_shader_param("offset", amt)
+
+ anim_size = 1+ amt * 0.1
+ transform.basis = base.scaled(Vector3(anim_size, anim_size, anim_size))
diff --git a/scripts/gui.gd b/scripts/gui.gd
new file mode 100644
index 0000000..bae4ffc
--- /dev/null
+++ b/scripts/gui.gd
@@ -0,0 +1,12 @@
+extends Panel
+
+var _player
+var _label
+
+func _ready():
+ _player = self.get_parent().get_parent().find_node("player")
+ _label = self.find_node("Label")
+
+func _process(delta):
+ _label.text = str(_player.sel_map[_player.sel])
+
diff --git a/scripts/movement.gd b/scripts/movement.gd
new file mode 100644
index 0000000..1601053
--- /dev/null
+++ b/scripts/movement.gd
@@ -0,0 +1,385 @@
+extends KinematicBody
+
+var vel = Vector3()
+var rot = Quat()
+var rotx = Quat()
+var roty = Quat()
+
+var sensitivity_mouse = 0.1
+
+var ray
+
+var _normal = Vector3.ZERO
+var _colliding = false
+var _colliding_group = []
+var _colliding_node
+
+var _world
+
+var _turret : PackedScene
+var _turret_holder
+var _turret_blues = []
+
+var _attach_point
+var _attach_point_holder
+
+var _path_start
+var _path
+var _path_end
+var _path_holder
+
+var sel = 0;
+var sel_map = [
+ "turrets", "path start", "path", "path end", "attach point"
+]
+
+func _ready():
+ ray = find_node("RayCast")
+
+ ray.enabled = true
+ ray.collide_with_areas = false
+
+ Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
+
+ _world = self.get_parent().find_node("world")
+ _turret_holder = self.get_parent().find_node("turrets")
+ _path_holder = self.get_parent().find_node("path")
+ _attach_point_holder = self.get_parent().find_node("attach")
+
+ _turret = load("res://scenes/turret.tscn")
+ _path_start = load("res://scenes/path_start.tscn")
+ _path = load("res://scenes/path.tscn")
+ _path_end = load("res://scenes/path_end.tscn")
+ _attach_point = load("res://scenes/attach_point.tscn")
+
+ load_turrets()
+
+ #_save()
+ _load()
+
+func load_turrets():
+ _turret_blues = []
+ var dir = Directory.new()
+ dir.open("res://models/turrets")
+ dir.list_dir_begin(true)
+ var turr = dir.get_next()
+ while turr != "":
+ if (turr.ends_with(".glb")):
+ _turret_blues.append(load("res://models/turrets/" + turr))
+ turr = dir.get_next()
+
+func get_map_state ():
+ var state = {}
+
+ state["voxels"] = []
+ for pos in _world.get_voxels():
+ var vox = { "pos": [pos.x, pos.y, pos.z], "id":_world.get_voxel_id(pos) }
+ state["voxels"] += [vox]
+
+ state["path"] = []
+ for node in _path_holder.get_children():
+ var pos = node.transform.origin;
+ var rot = node.transform.basis.get_rotation_quat();
+ var pobj = {
+ "pos": [pos.x, pos.y, pos.z],
+ "rot": [rot.x, rot.y, rot.z, rot.w],
+ "id": node.name
+ }
+ state["path"] += [pobj]
+
+ state["attach"] = []
+ for node in _attach_point_holder.get_children():
+ var pos = node.transform.origin;
+ var rot = node.transform.basis.get_rotation_quat();
+ var pobj = {
+ "pos": [pos.x, pos.y, pos.z],
+ "rot": [rot.x, rot.y, rot.z, rot.w],
+ "id": node.name
+ }
+ state["attach"] += [pobj]
+
+ return state
+
+func set_map_state (state):
+ _world.erase_voxels()
+ for vox in state["voxels"]:
+ var vecpos = Vector3(vox.pos[0], vox.pos[1], vox.pos[2]);
+ _world.set_voxel(vecpos, vox.id)
+ _world.update_mesh()
+
+ clear_children(_path_holder)
+ for pobj in state["path"]:
+ var blue = _path
+ if "start" in pobj.id:
+ blue = _path_start
+ if "end" in pobj.id:
+ blue = _path_end
+ var instance = blue.instance()
+ _path_holder.add_child(instance)
+ var vecpos = Vector3(pobj.pos[0], pobj.pos[1], pobj.pos[2]);
+ instance.transform.origin = vecpos;
+ var quat = Quat(pobj.rot[0], pobj.rot[1], pobj.rot[2], pobj.rot[3]);
+ instance.transform.basis = Basis(quat);
+
+ clear_children(_attach_point_holder)
+ for pobj in state["attach"]:
+ var blue = _attach_point
+ var instance = blue.instance()
+ _attach_point_holder.add_child(instance)
+ var vecpos = Vector3(pobj.pos[0], pobj.pos[1], pobj.pos[2]);
+ instance.transform.origin = vecpos;
+ var quat = Quat(pobj.rot[0], pobj.rot[1], pobj.rot[2], pobj.rot[3]);
+ instance.transform.basis = Basis(quat);
+
+
+func _save():
+ var save_game = File.new()
+ save_game.open("user://map0.json", File.WRITE)
+ save_game.store_string(to_json(get_map_state()))
+ save_game.close()
+
+func _load():
+ var save_game = File.new()
+ save_game.open("user://map0.json", File.READ)
+ var raw = save_game.get_as_text()
+ save_game.close()
+
+ print(raw)
+ var state = parse_json(raw)
+ set_map_state(state)
+
+func _process(delta):
+ if Input.is_action_just_released("save"):
+ _save()
+ if Input.is_action_just_released("load"):
+ _load()
+
+func clear_children (node):
+ for n in node.get_children():
+ node.remove_child(n)
+ n.queue_free()
+
+func _move_input(delta):
+ var dir = Vector3()
+
+ var input_movement_vector = Vector3()
+ if Input.is_action_pressed("movement_forward"):
+ input_movement_vector.z -= 1
+ if Input.is_action_pressed("movement_backward"):
+ input_movement_vector.z += 1
+ if Input.is_action_pressed("movement_left"):
+ input_movement_vector.x -= 1
+ if Input.is_action_pressed("movement_right"):
+ input_movement_vector.x += 1
+ if Input.is_action_pressed("movement_up"):
+ input_movement_vector.y += 1
+ if Input.is_action_pressed("movement_down"):
+ input_movement_vector.y -= 1
+ input_movement_vector = input_movement_vector.normalized()
+
+ if Input.is_action_just_pressed("ui_cancel"):
+ if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
+ Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
+ else:
+ Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
+
+ dir = rot * input_movement_vector
+
+ vel += dir * delta;
+ vel *= 0.8
+
+ var collision = self.move_and_collide(vel)
+ if collision: vel = Vector3(0, 0, 0)
+
+func _physics_process(delta):
+ if Input.is_action_just_released("cycle_forward"):
+ sel += 1
+ if sel >= len(sel_map):
+ sel = len(sel_map)-1
+ if Input.is_action_just_released("cycle_backward"):
+ sel -= 1
+ if sel < 0:
+ sel = 0
+
+ if "path" in sel_map[sel]:
+ ray.collision_mask = 3
+ else:
+ ray.collision_mask = 1
+
+ _move_input(delta)
+ _pointer()
+
+func check_overlap_pointer():
+ var ptr = self.get_parent().find_node("pointer");
+
+ var overlap = false
+ var space = get_world().direct_space_state
+
+ var center = ptr.transform.origin + _normal*0.25
+
+ var shape: = BoxShape.new()
+ shape.extents = Vector3(0.24, 0.24, 0.24)
+
+ var params: = PhysicsShapeQueryParameters.new()
+ params.set_shape(shape)
+ params.collision_mask = 3
+ params.transform = Transform.IDENTITY
+ params.transform.origin = center
+
+ var result = space.intersect_shape(params)
+ for body in result:
+ if !body.collider.get_parent().is_in_group("attach"):
+ overlap = true
+ break
+
+ for pos in _world.get_voxels():
+ var wpos = pos * 0.5
+ wpos += Vector3.ONE * (0.5 / 2)
+ var dist = (wpos - center).length_squared()
+ if dist < 0.1:
+ overlap = true
+ break
+
+ for node in _path_holder.get_children():
+ var pos = node.transform.origin
+ var dist = (pos - center).length_squared()
+ if dist < 0.1:
+ overlap = true
+ break
+
+ return overlap
+
+func place_turret ():
+ var ptr = self.get_parent().find_node("pointer");
+ if Input.is_action_just_pressed("use"):
+ if _colliding && "attach" in _colliding_group:
+ if !check_overlap_pointer():
+ var instance = _turret.instance()
+ _turret_holder.add_child(instance)
+ instance.transform.origin = ptr.transform.origin;
+ instance.transform.basis = Basis(ptr.transform.basis.get_rotation_quat());
+ instance.refresh_normal()
+ var instance_model = _turret_blues[0].instance()
+ instance_model.name = "model"
+ instance.add_child(instance_model)
+ instance.refresh_model()
+
+ if Input.is_action_just_pressed("cancel"):
+ if _colliding && "turrets" in _colliding_group:
+ _colliding_node.queue_free()
+
+func place_start_path ():
+ var ptr = self.get_parent().find_node("pointer");
+ if Input.is_action_just_pressed("use"):
+ if _colliding && "voxels" in _colliding_group:
+ if !check_overlap_pointer():
+ var instance = _path_start.instance()
+ _path_holder.add_child(instance)
+ instance.transform.origin = ptr.transform.origin + _normal * 0.25;
+ instance.transform.basis = Basis(ptr.transform.basis.get_rotation_quat());
+
+ if Input.is_action_just_pressed("cancel"):
+ if _colliding && "path" in _colliding_group:
+ _colliding_node.queue_free()
+
+func place_path ():
+ var ptr = self.get_parent().find_node("pointer");
+ if Input.is_action_just_pressed("use"):
+ if _colliding && "path" in _colliding_group:
+ if !check_overlap_pointer():
+ var instance = _path.instance()
+ _path_holder.add_child(instance)
+ instance.transform.origin = ptr.transform.origin + _normal * 0.25;
+ instance.transform.basis = Basis(ptr.transform.basis.get_rotation_quat());
+ instance.set_name("path")
+
+ _colliding_node.transform.basis = Basis(ptr.transform.basis.get_rotation_quat());
+
+ if Input.is_action_just_pressed("cancel"):
+ if _colliding && "path" in _colliding_group:
+ _colliding_node.queue_free()
+
+func place_path_end ():
+ var ptr = self.get_parent().find_node("pointer");
+ if Input.is_action_just_pressed("use"):
+ if _colliding && "path" in _colliding_group:
+ if !check_overlap_pointer():
+ var instance = _path_end.instance()
+ _path_holder.add_child(instance)
+ instance.transform.origin = ptr.transform.origin + _normal * 0.25
+ instance.transform.basis = Basis(ptr.transform.basis.get_rotation_quat());
+
+ if Input.is_action_just_pressed("cancel"):
+ if _colliding && "path" in _colliding_group:
+ _colliding_node.queue_free()
+
+func place_attach ():
+ var ptr = self.get_parent().find_node("pointer");
+ if Input.is_action_just_pressed("use"):
+ if _colliding && "voxels" in _colliding_group:
+ if !check_overlap_pointer():
+ var instance = _attach_point.instance()
+ _attach_point_holder.add_child(instance)
+ instance.transform.origin = ptr.transform.origin;
+ instance.transform.basis = Basis(ptr.transform.basis.get_rotation_quat());
+
+ if Input.is_action_just_pressed("cancel"):
+ if _colliding && "attach" in _colliding_group:
+ _colliding_node.queue_free()
+
+func _pointer ():
+ var ptr = self.get_parent().find_node("pointer");
+
+ match sel:
+ 0:
+ place_turret()
+ 1:
+ place_start_path()
+ 2:
+ place_path()
+ 3:
+ place_path_end()
+ 4:
+ place_attach()
+
+ var from = self.transform.origin
+ var to = from + (rot * Vector3.FORWARD) * 5
+
+ ptr.transform.origin = to
+
+ if ray.is_colliding():
+ _colliding = true
+ _normal = ray.get_collision_normal().normalized();
+ var pos = ray.get_collision_point()
+
+ var node = ray.get_collider().get_parent()
+ _colliding_node = node
+ _colliding_group = node.get_groups()
+
+ if ("voxels" in _colliding_group or "path" in _colliding_group):
+ var _cursor_position = ray.get_collision_point() - _normal * (0.5 / 2)
+ var tran = 0.5 *_cursor_position
+ tran += Vector3.ONE * (0.5 / 2) + _normal* (0.5 / 2)
+ pos = tran
+
+ if ("attach" in _colliding_group):
+ pos = node.global_transform.origin;
+ _normal = (node.global_transform.basis.get_rotation_quat() * Vector3.UP).normalized();
+
+ ptr.transform.basis = Basis(Utils.quat_look(_normal, Vector3.UP))
+ ptr.transform.origin = pos
+ else:
+ _colliding = false
+ _colliding_group = []
+
+func _input(event):
+ if event is InputEventMouseMotion:
+ var mouse = Vector2(
+ deg2rad(event.relative.x) * -1,
+ deg2rad(event.relative.y) * -1)
+ mouse *= sensitivity_mouse
+ rotx *= Quat(Vector3(0, mouse.x, 0))
+ roty *= Quat(Vector3(mouse.y, 0, 0))
+
+ rot = (rotx * roty).normalized()
+ self.transform.basis = Basis(rot)
diff --git a/scripts/path.gd b/scripts/path.gd
new file mode 100644
index 0000000..bd3b40f
--- /dev/null
+++ b/scripts/path.gd
@@ -0,0 +1,72 @@
+extends Node
+
+const dirs = [
+ Vector3.UP, Vector3.DOWN,
+ Vector3.FORWARD, Vector3.BACK,
+ Vector3.LEFT, Vector3.RIGHT
+]
+
+var nodes = []
+
+
+func _ready():
+ pass # Replace with function body.
+
+func next_node (node, dir):
+ var pos : Vector3 = node.transform.origin
+ var rot : Quat = node.transform.basis.get_rotation_quat()
+ var forward : Vector3 = rot * dir
+ var probe : Vector3 = pos + forward * 0.5
+
+ for child in get_children():
+ var diff : Vector3 = child.transform.origin - probe
+ var dist : float = diff.length_squared()
+ if dist < 0.1:
+ return child
+
+ return null
+
+func load_nodes ():
+ var start = null
+ for child in get_children():
+ if "start" in child.name:
+ start = child
+ if start == null:
+ return "failed to find start"
+
+ var next = null
+ for dir in dirs:
+ var cand = next_node(start, dir)
+ if cand != null:
+ if next == null:
+ next = cand
+ else:
+ return "failed: more than one path from start"
+ if next == null:
+ return "failed to find path next to start"
+
+ nodes = [start, next]
+ var iter = next
+ while true:
+ iter = next_node(iter, Vector3.UP)
+ nodes.append(iter)
+ if iter == null:
+ return "failed to follow path"
+ if "end" in iter.name:
+ break
+
+ return "ok"
+
+func hide ():
+ for child in get_children():
+ var mesh = child.find_node("MeshInstance")
+ mesh.visible = false
+
+func show ():
+ for child in get_children():
+ var mesh = child.find_node("MeshInstance")
+ mesh.visible = false
+
+# Called every frame. 'delta' is the elapsed time since the previous frame.
+#func _process(delta):
+# pass
diff --git a/scripts/turret.gd b/scripts/turret.gd
new file mode 100644
index 0000000..cc90dd4
--- /dev/null
+++ b/scripts/turret.gd
@@ -0,0 +1,113 @@
+extends Spatial
+
+var _path
+var _enemies
+var _projectiles_holder
+
+var _base
+var _gun
+var _shooting_point
+var _normal
+
+var aim_mode = "first"
+var _target = null
+
+var _range = 10
+
+var projectile
+
+var cooldown = 0.1
+var cooldown_timer = 0
+
+func _ready():
+ _path = get_tree().root.get_child(0).find_node("path")
+ _enemies = get_tree().root.get_child(0).find_node("enemies")
+ _projectiles_holder = get_tree().root.get_child(0).find_node("projectiles")
+
+ projectile = load("res://scenes/projectiles/bullet.tscn")
+
+func refresh_normal ():
+ _normal = transform.basis * Vector3.UP
+ _shooting_point = transform.origin + 0.25 * _normal
+
+func refresh_model():
+ _base = get_node("model").find_node("pivot*").find_node("base*")
+ _gun = _base.find_node("gun*")
+
+func filter_in_range(set):
+ var filtered = []
+ for target in set:
+ var node = _enemies.node_from_id(target)
+ var dist = (node.transform.origin - _shooting_point).length_squared()
+ if dist < _range*_range:
+ filtered += [target]
+ return filtered
+
+func filter_visible(set):
+ var space: PhysicsDirectSpaceState = get_world().direct_space_state as PhysicsDirectSpaceState
+ var from = _shooting_point
+
+ var filtered = []
+ for target in set:
+ var node = _enemies.node_from_id(target)
+ var to = node.transform.origin
+ var result = space.intersect_ray(from, to, _path.nodes, 1)
+ if result.size() > 0:
+ var hit = result.collider.get_parent()
+ if hit == node:
+ filtered += [ target ]
+ return filtered
+
+func get_target():
+ var ids = []
+ for id in _enemies.enemies: ids.append(id)
+ var set = ids
+ set = filter_in_range(set)
+ set = filter_visible(set)
+
+ if set.size() > 0:
+ return set[0]
+ else: return null
+
+func _physics_process(delta):
+ if !_enemies.enemies.has(_target):
+ _target = null
+ else:
+ if _enemies.enemies[_target].hp <= 0:
+ _target = null
+
+ if _target == null:
+ _target = get_target()
+
+ _target = get_target()
+
+ if _target != null:
+ var enemy = _enemies.node_from_id(_target)
+ var direction = (enemy.transform.origin - _shooting_point).normalized()
+
+ var proj_normal = direction - _normal.dot(direction) * _normal
+ var base_rot = Transform().looking_at(proj_normal, _normal).basis.get_rotation_quat()
+
+ var perp = proj_normal.cross(_normal).normalized()
+ var proj_forward = direction - perp.dot(direction) * perp
+ var gun_rot = Transform().looking_at(proj_forward, perp).basis.get_rotation_quat()
+
+ gun_rot = Quat(direction, PI/2) * gun_rot
+
+ _base.global_transform.basis = Basis(base_rot)
+ _gun.global_transform.basis = Basis(gun_rot)
+
+ cooldown_timer += delta
+ if cooldown_timer > cooldown:
+ cooldown_timer -= cooldown
+ shoot(direction)
+
+func shoot (dir):
+ shoot_projectile(dir)
+
+func shoot_projectile (dir):
+ var instance = projectile.instance()
+ _projectiles_holder.add_child(instance)
+ instance.transform = Transform().looking_at(dir, _normal)
+ instance.transform.origin = _shooting_point + dir*0.3;
+ instance.shooter = self
diff --git a/scripts/utils.gd b/scripts/utils.gd
new file mode 100644
index 0000000..a0ecae4
--- /dev/null
+++ b/scripts/utils.gd
@@ -0,0 +1,10 @@
+class_name Utils
+extends Object
+
+static func quat_look (target, up):
+ var dot = target.dot(up)
+ var angle = acos(dot)
+ var axis = up.cross(target).normalized()
+ if angle == 0: axis = up
+ if angle == PI: axis = Vector3.RIGHT
+ return Quat(axis, angle)