Files
kicking-high/proto3/godot/scenes/meta-ai.gd
2020-04-15 12:16:14 +03:00

261 lines
8.5 KiB
GDScript

extends Node
signal spawn_player(pos)
signal spawn_npc(xform)
enum states {STATE_PREINIT, STATE_INIT, STATE_INIT_ASTAR, STATE_EXECUTE, STATE_SLEEP}
var state = states.STATE_PREINIT
var astar = null
var sleep_delay = 0.0
var npcs
func start():
print("ai starting")
state = states.STATE_INIT
func _ready():
astar = AStar.new()
global.astar = astar
func calc_visible_lists():
for g in get_tree().get_nodes_in_group("npc"):
if g.get_meta("smart_object"):
continue
var bb = g.get_node("blackboard")
var enemy_list = []
var visibility_list = []
var visible_to_list = []
var distance_all = {}
var dist2char = {}
for gn in get_tree().get_nodes_in_group("characters"):
if g == gn:
continue
if gn.get_meta("smart_object"):
continue
if gn.get_meta("grabbed"):
continue
distance_all[gn] = g.global_transform.origin.distance_to(gn.global_transform.origin)
if !dist2char.has(distance_all[gn]):
dist2char[distance_all[gn]] = [gn]
else:
dist2char[distance_all[gn]].push_back(gn)
if rpg.is_enemy(g, gn):
enemy_list.push_back(gn)
if global.visibility_check(g, gn):
visibility_list.push_back(gn)
if global.visibility_check(gn, g):
visible_to_list.push_back(gn)
var all_distances = dist2char.keys().duplicate()
all_distances.sort()
var closest
var closest_male
var closest_female
var closest_visible
var closest_visible_male
var closest_visible_female
var closest_enemy
var closest_visible_enemy
for k in all_distances:
if !closest:
closest = dist2char[k][0]
if !closest_visible && dist2char[k][0] in visibility_list:
closest_visible = dist2char[k][0]
if !closest_male && dist2char[k][0].is_in_group("male"):
closest_male = dist2char[k][0]
if !closest_female && dist2char[k][0].is_in_group("female"):
closest_female = dist2char[k][0]
if !closest_visible_male && dist2char[k][0].is_in_group("male") && dist2char[k][0] in visibility_list:
closest_visible_male = dist2char[k][0]
if !closest_visible_female && dist2char[k][0].is_in_group("female") && dist2char[k][0] in visibility_list:
closest_visible_female = dist2char[k][0]
if !closest_enemy:
if rpg.is_enemy(g, dist2char[k][0]):
closest_enemy = dist2char[k][0]
if !closest_visible_enemy:
if rpg.is_enemy(g, dist2char[k][0]) && dist2char[k][0] in visibility_list:
closest_visible_enemy = dist2char[k][0]
bb.set("visible", visibility_list)
bb.set("visible_to", visible_to_list)
bb.set("closest", closest)
bb.set("closest_female", closest_female)
bb.set("closest_male", closest_male)
bb.set("closest_visible", closest_visible)
bb.set("closest_visible_female", closest_visible_female)
bb.set("closest_visible_male", closest_visible_male)
bb.set("closest_enemy", closest_enemy)
bb.set("closest_visible_enemy", closest_visible_enemy)
if closest_enemy:
bb.set("last_enemy", closest_enemy)
bb.set("closest_enemy_distance", distance_all[closest_enemy])
if closest_female:
bb.set("closest_female_distance", distance_all[closest_female])
if closest_male:
bb.set("closest_male_distance", distance_all[closest_male])
if closest_visible_female:
bb.set("closest_visible_female_distance", distance_all[closest_visible_female])
if closest_visible_male:
bb.set("closest_visible_male_distance", distance_all[closest_visible_male])
func global_distance(a: Spatial, b:Spatial):
var posa = a.global_transform.origin
var posb = b.global_transform.origin
return posa.distance_to(posb)
func spawn_characters():
for e in get_tree().get_nodes_in_group("spawn"):
if e.name == "player-spawn":
print("player: ", e.global_transform.origin)
emit_signal("spawn_player", e)
if global.player:
for e in get_tree().get_nodes_in_group("spawn"):
if global_distance(global.player, e) < 5.0:
print("spawn ", e.name)
if e.name.begins_with("npc-spawn"):
print("npc")
emit_signal("spawn_npc", e)
var path_nodes = []
var endings = []
var connections = {}
func _physics_process(delta):
match state:
states.STATE_INIT:
print("starting AI")
path_nodes = get_tree().get_nodes_in_group("path")
endings = []
connections = {}
state = states.STATE_INIT_ASTAR
states.STATE_INIT_ASTAR:
if path_nodes.size() > 0:
var e = path_nodes.pop_front()
print(e.name)
var new_points = Array(e.curve.get_baked_points())
var new_ids = []
for r in new_points:
var pt = r + e.global_transform.origin
var id = astar.get_closest_point(r)
if id >= 0:
var spos = astar.get_point_position(id)
if spos.distance_to(pt) > 0.15:
id = astar.get_available_point_id()
astar.add_point(id, pt)
else:
id = astar.get_available_point_id()
astar.add_point(id, pt)
if !id in new_ids:
new_ids.push_back(id)
endings.push_back(new_ids[0])
endings.push_back(new_ids[new_ids.size() - 1])
for s in range(new_ids.size() - 1):
if !astar.are_points_connected(new_ids[s], new_ids[s + 1]):
if new_ids[s] != new_ids[s + 1]:
astar.connect_points(new_ids[s], new_ids[s + 1])
connections[new_ids[s]] = new_ids[s + 1]
connections[new_ids[s + 1]] = new_ids[s]
else:
print("path nodes passed")
var space_state = get_parent().get_world().direct_space_state
for id1 in endings:
for id2 in endings:
if id1 == id2:
continue
if astar.are_points_connected(id1, id2):
continue
var p0 = astar.get_point_position(id1)
var p1 = astar.get_point_position(id2)
var front_result = space_state.intersect_ray(p0 + Vector3(0, 0.5, 0), p1 + Vector3(0, 0.5, 0), [], 0x1)
if !front_result.has("collider"):
astar.connect_points(id1, id2)
print("astar complete")
spawn_characters()
print("characters spawned")
$blackboard.set("metaai", self)
state = states.STATE_EXECUTE
states.STATE_EXECUTE:
if randf() > 0.7:
calc_visible_lists()
for g in get_tree().get_nodes_in_group("npc"):
var bb = g.get_node("blackboard")
bb.set("delta", delta)
bb.set("metaai", self)
if g.get_meta("smart_object"):
continue
if g.get_meta("grabbed"):
continue
var space_state = g.get_world().direct_space_state
var up = Vector3(0, 1, 0)
var npc_pos = g.global_transform.origin
var front = -g.global_transform.basis[2]
var right = g.global_transform.basis[0]
var vl = g.velocity
vl.y = 0
var v = vl.length() * 0.2
var npc_raycasts = [
{
"name": "front_result",
"from": npc_pos + up,
"to": npc_pos + up + front * (0.1 + v),
"mask": 0xffff
},
{
"name": "front_right_result",
"from": npc_pos + up + right * 0.1,
"to": + npc_pos + up + front * (0.2 + v) + right * (0.1 + v * 0.5),
"mask": 0xffff
},
{
"name": "front_left_result",
"from": npc_pos + up - right * 0.1,
"to": npc_pos + up + front * (0.2 + v) - right * (0.1 + v * 0.5),
"mask": 0xffff
},
{
"name": "low_obstacle_result",
"from": npc_pos + up * 0.3,
"to": npc_pos + up * 0.3 + front * (0.1 + v),
"radius": 0.2,
"mask": 0xffff
},
]
for r in npc_raycasts:
if r.has("radius"):
var shape : = SphereShape.new()
shape.radius = r.radius
var query = PhysicsShapeQueryParameters.new()
query.set_shape(query)
query.collision_mask = r.mask
query.exclude = [g]
query.transform.origin = r.from
var motion = space_state.cast_motion(shape, r.to - r.from)
if motion.size() == 0:
bb.set(r.name, {})
else:
query.transform.origin = r.from + (r.to - r.from) * motion[1]
var results = space_state.intersect_shape(query, 1)
if results.size() > 0:
var d = results[0].duplicate()
d.motion = motion
bb.set(r.name, results[0])
else:
bb.set(r.name, {})
else:
var result = space_state.intersect_ray(r.from, r.to, [g], r.mask)
if result.has("collider"):
print(r.name, " ", result, " ", v, " ", r.from.distance_to(r.to), " ", r.from.distance_to(result.position))
bb.set(r.name, result)
if g.has_meta("target_loc"):
var loc = g.get_meta("target_loc")
var target_result = space_state.intersect_ray(npc_pos + up, loc + up,
[g], 0xffff)
bb.set("target_result", target_result)
else:
bb.set("target_result", {})
$behavior_tree.tick(g, bb)
sleep_delay = 0.3 + randf() * 0.4
state = states.STATE_SLEEP
states.STATE_SLEEP:
sleep_delay -= delta
if sleep_delay <= 0.0:
state = states.STATE_EXECUTE