From 0f518727c28d3204415db14c7ca0e4f7cb653677 Mon Sep 17 00:00:00 2001 From: jacopograndi Date: Thu, 9 Dec 2021 00:52:59 +0100 Subject: working --- scripts/bullet.gd | 33 ++++ scripts/debug.gd | 22 +++ scripts/enemies.gd | 117 +++++++++++++ scripts/fx/fx_enemy_damage.gd | 29 ++++ scripts/gui.gd | 12 ++ scripts/movement.gd | 385 ++++++++++++++++++++++++++++++++++++++++++ scripts/path.gd | 72 ++++++++ scripts/turret.gd | 113 +++++++++++++ scripts/utils.gd | 10 ++ 9 files changed, 793 insertions(+) create mode 100644 scripts/bullet.gd create mode 100644 scripts/debug.gd create mode 100644 scripts/enemies.gd create mode 100644 scripts/fx/fx_enemy_damage.gd create mode 100644 scripts/gui.gd create mode 100644 scripts/movement.gd create mode 100644 scripts/path.gd create mode 100644 scripts/turret.gd create mode 100644 scripts/utils.gd (limited to 'scripts') 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) -- cgit v1.2.3-54-g00ecf