820 lines
23 KiB
GDScript3
820 lines
23 KiB
GDScript3
extends AIScriptModule
|
|
|
|
|
|
# Declare member variables here. Examples:
|
|
# var a = 2
|
|
# var b = "text"
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
var skel
|
|
var hair_skel
|
|
var name
|
|
|
|
var garments_female_lingerie = ["female-panties1", "female-bra1"]
|
|
var garments_head_female = []
|
|
var garments_male_lingerie = ["male-panties1"]
|
|
var garments_head_male = []
|
|
var garments_female_main = ["female-shirt_skirt1"]
|
|
var garments_male_main = ["male-pants1", "male-shoes1"]
|
|
var basedir = "res://scenes/clothes/"
|
|
var material_female = preload("res://scenes/clothes/nun-clothes.material")
|
|
var material_male = preload("res://scenes/clothes/clothes-male.material")
|
|
|
|
var state = 0
|
|
var rnd: RandomNumberGenerator
|
|
const attack_distance = 2.0
|
|
const guard_distance = 2.0
|
|
const engage_distance = 10.0
|
|
const flee_distance = 5.0
|
|
|
|
var init_blackboard = {
|
|
"stamina": 100.0,
|
|
"health": 100.0,
|
|
"melee_weapon_equipped": false,
|
|
"attack_cooldown": 0.0,
|
|
"guard_cooldown": 0.0,
|
|
"flee_cooldown": 0.0,
|
|
"guard": false,
|
|
"melee_damage": false,
|
|
"dot": 0.0
|
|
}
|
|
|
|
func look_at(tick, ch):
|
|
var root = get_character(tick)
|
|
var current = root.global_transform
|
|
var at = ch.global_transform.origin
|
|
at.y = current.origin.y
|
|
root.global_transform = current.looking_at(at, Vector3.UP)
|
|
|
|
func combat_event(ev, data, blackboard):
|
|
var root = blackboard.self
|
|
var cam = root.get_viewport().get_camera()
|
|
var player
|
|
if cam:
|
|
player = cam.get_meta("player")
|
|
assert(player)
|
|
match ev:
|
|
"about_to_attack":
|
|
var who = data[0]
|
|
if who != root:
|
|
var where = who.global_transform.origin
|
|
var curpos = root.global_transform.origin
|
|
var dst = where.distance_squared_to(curpos)
|
|
if dst < guard_distance * guard_distance:
|
|
if who == player:
|
|
blackboard["guard"] = true
|
|
# blackboard_set(tick, "melee_damage", true)
|
|
"damage":
|
|
var who = data[0]
|
|
var weapon = data[1]
|
|
if who != root:
|
|
var where = who.global_transform.origin
|
|
var curpos = root.global_transform.origin
|
|
var dst = where.distance_squared_to(curpos)
|
|
if dst < attack_distance * attack_distance:
|
|
if who == player:
|
|
blackboard["melee_damage"] = true
|
|
# blackboard_set(tick, "melee_damage", true)
|
|
|
|
func init(tick):
|
|
assert(tick)
|
|
assert(!get_memory(tick).has("initialized"))
|
|
name = "bandit_ai"
|
|
var root = get_character(tick)
|
|
for e in init_blackboard.keys():
|
|
blackboard_set(tick, e, init_blackboard[e])
|
|
blackboard_set(tick, "self", root)
|
|
assert(root.has_meta("skeleton"))
|
|
rnd = RandomNumberGenerator.new()
|
|
rnd.randomize()
|
|
root.add_to_group("bandits")
|
|
root.add_to_group("bandit")
|
|
combat.connect("event", self, "combat_event", [blackboard_get_dict(tick)])
|
|
|
|
var character_data = root.get_meta("character_data")
|
|
if character_data.sex == "female":
|
|
var g = garments_female_lingerie
|
|
var h = []
|
|
g += garments_female_main
|
|
h += garments_head_female
|
|
characters.call_deferred("setup_garments", root, g, h, material_female)
|
|
else:
|
|
var g = garments_male_lingerie
|
|
var h = []
|
|
g += garments_male_main
|
|
h += garments_head_male
|
|
characters.call_deferred("setup_garments", root, g, h, material_male)
|
|
state = 0
|
|
for e in conf_behaviors.keys():
|
|
if !run_behaviors.has(e):
|
|
run_behaviors[e] = conf_behaviors[e].new()
|
|
get_memory(tick).initialized = true
|
|
var cooldown = 0.0
|
|
|
|
class base_bhv:
|
|
var active = false
|
|
var finished = false
|
|
func run(delta: float, bb: Dictionary):
|
|
pass
|
|
func stop():
|
|
pass
|
|
func activate():
|
|
pass
|
|
|
|
class walkto extends base_bhv:
|
|
signal arrived(where)
|
|
var state = 0
|
|
var where: Vector3
|
|
var speed: = 0.0
|
|
var gap: = 0.0
|
|
var root
|
|
var timeout: = 0.0
|
|
var pdist = INF
|
|
var cost: = 0.0
|
|
func look_at(root):
|
|
var current = root.global_transform
|
|
var at = where
|
|
at.y = current.origin.y
|
|
root.global_transform = current.looking_at(at, Vector3.UP)
|
|
root.set_meta("orientation", Transform(root.global_transform.basis, Vector3()))
|
|
func run(delta: float, bb: Dictionary):
|
|
assert(active)
|
|
assert(root)
|
|
var gpos = root.global_transform.origin
|
|
var dst = gpos.distance_squared_to(where)
|
|
match state:
|
|
0:
|
|
look_at(root)
|
|
characters.animation_node_travel(root, "locomotion")
|
|
characters.set_walk_speed(root, speed, 0)
|
|
state = 1
|
|
1:
|
|
bb.stamina -= cost * delta
|
|
timeout -= delta
|
|
state = 2
|
|
2:
|
|
if dst < gap * gap:
|
|
emit_signal("arrived", gpos)
|
|
state = 10
|
|
elif timeout < 0.0:
|
|
root.global_transform.origin = where
|
|
emit_signal("arrived", gpos)
|
|
state = 20
|
|
elif dst > pdist && dst > gap * gap:
|
|
root.global_transform.origin = where
|
|
emit_signal("arrived", gpos)
|
|
state = 30
|
|
printerr("missed the target: ", sqrt(dst), gap)
|
|
elif dst < 3.0 * gap * gap && timeout > 0.0:
|
|
characters.set_walk_speed(root, 0.5 * speed, 0)
|
|
10:
|
|
finished = true
|
|
20:
|
|
finished = true
|
|
30:
|
|
finished = true
|
|
200:
|
|
timeout -= delta
|
|
if timeout < 0.0:
|
|
root.global_transform.origin = where
|
|
characters.set_walk_speed(root, 0, 0)
|
|
finished = true
|
|
state = 300
|
|
300:
|
|
printerr("should not be here: ", sqrt(dst), gap)
|
|
breakpoint
|
|
# print("walkto state: ", state)
|
|
|
|
pdist = dst
|
|
func start(root: Node, where: Vector3, speed: float, gap: float, cost: float, timeout: float):
|
|
assert(root)
|
|
assert(!active)
|
|
state = 0
|
|
self.root = root
|
|
self.where = where
|
|
self.gap = gap
|
|
self.timeout = timeout
|
|
self.speed = speed
|
|
self.cost = cost
|
|
active = true
|
|
func stop():
|
|
assert(active)
|
|
assert(root)
|
|
characters.set_walk_speed(root, 0, 0)
|
|
active = false
|
|
finished = false
|
|
state = 0
|
|
# print("walkto stopped")
|
|
|
|
class attack extends base_bhv:
|
|
var root
|
|
var anim
|
|
var event
|
|
var state = 0
|
|
func start(root: Node, anim: String, event: String):
|
|
self.root = root
|
|
self.anim = anim
|
|
self.event = event
|
|
active = true
|
|
func look_at(root, bb):
|
|
var current = root.global_transform
|
|
var at = bb.enemy_pos
|
|
at.y = current.origin.y
|
|
root.global_transform = current.looking_at(at, Vector3.UP)
|
|
root.set_meta("orientation", Transform(root.global_transform.basis, Vector3()))
|
|
func run(delta: float, bb: Dictionary):
|
|
assert(active)
|
|
assert(root)
|
|
# print("attack state: ", state, " ", active, " ", finished)
|
|
if finished:
|
|
return
|
|
# print("attack running")
|
|
bb.attack_cooldown -= delta
|
|
if bb.attack_cooldown > 0.0:
|
|
return
|
|
match state:
|
|
0:
|
|
combat.emit_signal("event", event, [root])
|
|
look_at(root, bb)
|
|
state = 1
|
|
1:
|
|
bb.stamina -= 0.5 * delta
|
|
bb.stamina -= 3500 * delta
|
|
characters.animation_node_travel(root, anim)
|
|
# print("attack: ", anim)
|
|
state = 2
|
|
2:
|
|
bb.stamina -= 5 * delta
|
|
bb.attack_cooldown = 1.8
|
|
finished = true
|
|
# print("attack ", anim, " ", event, " done")
|
|
func stop():
|
|
assert(active)
|
|
assert(root)
|
|
active = false
|
|
finished = false
|
|
state = 0
|
|
class take_weapon extends base_bhv:
|
|
var weapon
|
|
var root
|
|
func start(root: Node, weapon):
|
|
self.root = root
|
|
self.weapon = weapon
|
|
active = true
|
|
func activate():
|
|
pass
|
|
func run(delta: float, bb: Dictionary):
|
|
assert(active)
|
|
assert(root)
|
|
if finished:
|
|
return
|
|
if bb.melee_weapon_equipped:
|
|
return
|
|
var skel = root.get_meta("skeleton")
|
|
var wslot = skel.get_node("wrist_r/weapon_right")
|
|
wslot.set_meta("owner", root)
|
|
inventory.equip(wslot, weapon)
|
|
bb.melee_weapon_equipped = true
|
|
finished = true
|
|
func stop():
|
|
# can't re-run this one
|
|
assert(active)
|
|
assert(root)
|
|
finished = false
|
|
active = false
|
|
finished = false
|
|
|
|
class guard extends base_bhv:
|
|
var root
|
|
var anim1
|
|
var anim2
|
|
var state = 0
|
|
func start(root: Node, anim1, anim2: String):
|
|
self.root = root
|
|
self.anim1 = anim1
|
|
self.anim2 = anim2
|
|
active = true
|
|
func run(delta: float, bb: Dictionary):
|
|
assert(active)
|
|
assert(root)
|
|
# print("guard state: ", state, " ", finished)
|
|
if finished:
|
|
return
|
|
match state:
|
|
0:
|
|
# guard
|
|
var anim
|
|
if bb.dot < 0:
|
|
anim = anim1
|
|
else:
|
|
anim = anim2
|
|
characters.animation_node_travel(root, anim)
|
|
# print("guard playing animation ", anim)
|
|
state = 1
|
|
1:
|
|
state = 2
|
|
2:
|
|
bb.guard_cooldown = 1.5
|
|
finished = true
|
|
class get_damage extends base_bhv:
|
|
var root
|
|
var anim1
|
|
var anim2
|
|
var state = 0
|
|
var cost = 0.0
|
|
func start(root: Node, anim1, anim2: String, cost: float):
|
|
self.root = root
|
|
self.anim1 = anim1
|
|
self.anim2 = anim2
|
|
self.cost = cost
|
|
active = true
|
|
func run(delta: float, bb: Dictionary):
|
|
assert(active)
|
|
assert(root)
|
|
if finished:
|
|
return
|
|
match state:
|
|
0:
|
|
# guard
|
|
var anim
|
|
if bb.dot < 0:
|
|
anim = anim1
|
|
else:
|
|
anim = anim2
|
|
characters.animation_node_travel(root, anim)
|
|
if bb.stamina > 50.0:
|
|
bb.health -= cost
|
|
else:
|
|
bb.health -= 5.0 * cost
|
|
bb.stamina = 5.0
|
|
# print("guard playing animation ", anim)
|
|
state = 1
|
|
1:
|
|
state = 2
|
|
2:
|
|
bb.guard_cooldown = 1.5
|
|
finished = true
|
|
func stop():
|
|
assert(active)
|
|
assert(root)
|
|
active = false
|
|
finished = false
|
|
state = 0
|
|
# print("guard stopped")
|
|
|
|
class rest extends base_bhv:
|
|
var root
|
|
func start(root: Node):
|
|
self.root = root
|
|
active = true
|
|
func run(delta: float, bb: Dictionary):
|
|
assert(active)
|
|
assert(root)
|
|
if finished:
|
|
return
|
|
if bb.stamina < 100.0:
|
|
# print("rest: stamina: ", bb.stamina)
|
|
bb.stamina += 15500.0 * delta
|
|
else:
|
|
finished = true
|
|
func stop():
|
|
assert(active)
|
|
assert(root)
|
|
active = false
|
|
finished = false
|
|
class unconcious extends base_bhv:
|
|
var root
|
|
var anim1
|
|
var anim2
|
|
var state = 0
|
|
func start(root: Node, anim1: String, anim2: String):
|
|
self.root = root
|
|
self.anim1 = anim1
|
|
self.anim2 = anim2
|
|
active = true
|
|
state = 0
|
|
|
|
func run(delta: float, bb: Dictionary):
|
|
assert(active)
|
|
assert(root)
|
|
assert(anim1)
|
|
assert(anim2)
|
|
match state:
|
|
0:
|
|
characters.animation_node_travel(root, anim1)
|
|
state = 1
|
|
# if finished:
|
|
# return
|
|
# if bb.stamina < 100.0:
|
|
# print("rest: stamina: ", bb.stamina)
|
|
# bb.stamina += 15500.0 * delta
|
|
# else:
|
|
# finished = true
|
|
func stop():
|
|
assert(active)
|
|
assert(root)
|
|
active = false
|
|
finished = false
|
|
|
|
#var walk_to: walkto
|
|
#var flee_to: walkto
|
|
#var melee_attack: attack
|
|
#var take_melee_weapon: take_weapon
|
|
#var guard_melee: guard
|
|
#var do_rest: rest
|
|
|
|
var conf_behaviors: = {
|
|
"take_melee_weapon": take_weapon,
|
|
"approach": walkto,
|
|
"flee": walkto,
|
|
"melee_attack": attack,
|
|
"guard": guard,
|
|
"rest": rest,
|
|
"get_melee_damage": get_damage,
|
|
"unconcious": unconcious
|
|
}
|
|
|
|
var pdst = INF
|
|
class health_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "health") <= 0.0:
|
|
return 0.0
|
|
var ret = blackboard_get(tick, "health") / 100.0
|
|
return clamp(ret, 0.0, 1.0)
|
|
class no_melee_weapon_equipped_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if !blackboard_get(tick, "melee_weapon_equipped"):
|
|
return 1.0
|
|
return 0.0
|
|
class threat_consideration extends AIUtilityScriptedConsideration:
|
|
const engage_distance = 20.0
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "enemy_distance") < engage_distance * engage_distance:
|
|
return 1.0
|
|
return 0.0
|
|
class enemy_far_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "enemy_distance") < 1.6 * 1.6:
|
|
return 0.0
|
|
return 1.0
|
|
class half_or_more_stamina_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "stamina") > 50.0:
|
|
return 1.0
|
|
return 0.0
|
|
class has_melee_weapon_equipped_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "melee_weapon_equipped"):
|
|
return 1.0
|
|
return 0.0
|
|
class melee_attack_ready_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "attack_cooldown") <= 0.0:
|
|
return 1.0
|
|
return 0.0
|
|
class twenty_or_more_stamina_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "stamina") > 20.0:
|
|
return 1.0
|
|
return 0.0
|
|
class melee_attack_distance_consideration extends AIUtilityScriptedConsideration:
|
|
const attack_distance = 2.0
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "enemy_distance") < attack_distance * attack_distance:
|
|
return 1.0
|
|
return 0.0
|
|
class need_healing_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "health") >= 100.0:
|
|
return 0.0
|
|
var ret = 1.0 - blackboard_get(tick, "health") / 100.0
|
|
return clamp(ret, 0.0, 1.0)
|
|
class need_rest_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "stamina") >= 100.0:
|
|
return 0.0
|
|
return clamp(1.0 - blackboard_get(tick, "stamina") / 100.0, 0.0, 1.0)
|
|
class critical_health_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "health") < 10.0:
|
|
return 1.0
|
|
return 0.0
|
|
class critical_stamina_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "stamina") < 10.0:
|
|
return 1.0
|
|
return 0.0
|
|
class flee_or_more_distance_consideration extends AIUtilityScriptedConsideration:
|
|
const flee_distance = 5.0
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "enemy_distance") >= flee_distance * flee_distance:
|
|
return 1.0
|
|
return 0.0
|
|
class dangerous_distance_consideration extends AIUtilityScriptedConsideration:
|
|
const flee_distance = 5.0
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "enemy_distance") < flee_distance * flee_distance:
|
|
return 1.0
|
|
return 0.0
|
|
class ready_to_flee_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "flee_cooldown") < 0.0:
|
|
return 1.0
|
|
return 0.0
|
|
class has_energy_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "stamina") >= 10.0:
|
|
return 1.0
|
|
return 0.0
|
|
class ready_to_guard_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "guard_cooldown") < 0.0:
|
|
return 1.0
|
|
return 0.0
|
|
class guarding_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "guard"):
|
|
return 1.0
|
|
return 0.0
|
|
class melee_damage_consideration extends AIUtilityScriptedConsideration:
|
|
func evalute(tick, delta):
|
|
if blackboard_get(tick, "melee_damage"):
|
|
return 1.0
|
|
return 0.0
|
|
var considerations_data = {
|
|
"take_melee_weapon": {
|
|
"considerations": [
|
|
{"c": health_consideration.new(), "score": 1.0},
|
|
{"c": no_melee_weapon_equipped_consideration.new(), "score": 1.0},
|
|
{"c": threat_consideration.new(), "score": 1.0}
|
|
],
|
|
},
|
|
"approach": {
|
|
"considerations": [
|
|
{"c": health_consideration.new(), "score": 1.0},
|
|
{"c": enemy_far_consideration.new(), "score": 1.0},
|
|
{"c": half_or_more_stamina_consideration.new(), "score": 1.0},
|
|
{"c": threat_consideration.new(), "score": 1.0}
|
|
],
|
|
},
|
|
"melee_attack": {
|
|
"considerations": [
|
|
{"c": health_consideration.new(), "score": 1.0},
|
|
{"c": has_melee_weapon_equipped_consideration.new(), "score": 1.0},
|
|
{"c": melee_attack_ready_consideration.new(), "score": 1.0},
|
|
{"c": twenty_or_more_stamina_consideration.new(), "score": 1.0},
|
|
{"c": melee_attack_distance_consideration.new(), "score": 1.0}
|
|
],
|
|
},
|
|
"flee": {
|
|
"considerations": [
|
|
{"c": health_consideration.new(), "score": 1.0},
|
|
{"c": ready_to_flee_consideration.new(), "score": 1.0},
|
|
{"c": has_energy_consideration.new(), "score": 1.0},
|
|
{"c": dangerous_distance_consideration.new(), "score": 1.0},
|
|
],
|
|
},
|
|
"guard": {
|
|
"considerations": [
|
|
{"c": health_consideration.new(), "score": 1.0},
|
|
{"c": ready_to_guard_consideration.new(), "score": 1.0},
|
|
{"c": guarding_consideration.new(), "score": 1.0},
|
|
],
|
|
},
|
|
"unconcious": {
|
|
"considerations": [
|
|
{"c": critical_health_consideration.new(), "score": 1.0}
|
|
],
|
|
},
|
|
"rest": {
|
|
"considerations": [
|
|
{"c": critical_stamina_consideration.new(), "score": 1.0},
|
|
{"c": flee_or_more_distance_consideration.new(), "score": 1.0}
|
|
],
|
|
},
|
|
"get_melee_damage": {
|
|
"considerations": [
|
|
{"c": health_consideration.new(), "score": 1.0},
|
|
{"c": melee_damage_consideration.new(), "score": 1.0},
|
|
]
|
|
}
|
|
}
|
|
func calc_utility(tick: AITick, utility: String, delta: float):
|
|
var v = 0.0
|
|
if considerations_data.has(utility):
|
|
v = 1.0
|
|
for b in considerations_data[utility].considerations:
|
|
assert(b.c)
|
|
v *= b.c._evalute(tick, delta) * b.score
|
|
if v <= 0.0:
|
|
break
|
|
match utility:
|
|
"take_melee_weapon":
|
|
v *= 110.0
|
|
# if blackboard_get(tick, "health") <= 0.0:
|
|
# return 0.0
|
|
# if blackboard_get(tick, "melee_weapon_equipped"):
|
|
# return 0.0
|
|
# else:
|
|
# if blackboard_get(tick, "enemy_distance") < engage_distance * engage_distance:
|
|
# return 110.0
|
|
"approach":
|
|
v *= 100.0
|
|
# if blackboard_get(tick, "health") <= 0.0:
|
|
# return 0.0
|
|
# if blackboard_get(tick, "enemy_distance") < 1.6 * 1.6:
|
|
# return 0.0
|
|
# if blackboard_get(tick, "stamina") > 50.0 && blackboard_get(tick, "enemy_distance") < engage_distance * engage_distance:
|
|
# return 10.0 + (blackboard_get(tick, "enemy_distance") - 10.0) / 6.0
|
|
"melee_attack":
|
|
v *= 50.0
|
|
# if blackboard_get(tick, "health") <= 0.0:
|
|
# return 0.0
|
|
# if blackboard_get(tick, "melee_weapon_equipped"):
|
|
# if blackboard_get(tick, "attack_cooldown") <= 0.0:
|
|
# var d = attack_distance * attack_distance
|
|
# if blackboard_get(tick, "stamina") > 20.0 && blackboard_get(tick, "enemy_distance") <= d:
|
|
# return 50.0
|
|
"flee":
|
|
v *= 300.0
|
|
# if blackboard_get(tick, "health") <= 0.0:
|
|
# return 0.0
|
|
# if blackboard_get(tick, "flee_cooldown") > 0.0:
|
|
# return 0.0
|
|
# if blackboard_get(tick, "stamina") <= 50.0 && blackboard_get(tick, "stamina") > 10.0 && blackboard_get(tick, "enemy_distance") < flee_distance * flee_distance:
|
|
# return 100.0 + clamp((100.0 - blackboard_get(tick, "stamina")), 0, 90.0) * 2.0
|
|
# if blackboard_get(tick, "stamina") <= 10.0 && blackboard_get(tick, "enemy_distance") <= flee_distance * flee_distance * 0.5:
|
|
# return 250.0
|
|
# if blackboard_get(tick, "stamina") <= 50.0 && blackboard_get(tick, "enemy_distance") < engage_distance * engage_distance:
|
|
# if blackboard_get(tick, "randf") < 0.3:
|
|
# return 100.0
|
|
# if blackboard_get(tick, "enemy_distance") < 1.4 * 1.4:
|
|
# return 150.0
|
|
# if blackboard_get(tick, "health") < 50 && blackboard_get(tick, "health") > 15:
|
|
# return 160
|
|
"guard":
|
|
if blackboard_get(tick, "guard"):
|
|
blackboard_set(tick, "guard", false)
|
|
v *= 900.0
|
|
# if blackboard_get(tick, "health") <= 0.0:
|
|
# return 0.0
|
|
# if blackboard_get(tick, "guard_cooldown") > 0.0:
|
|
# return 0.0
|
|
# elif blackboard_get(tick, "guard"):
|
|
# blackboard_set(tick, "guard", false)
|
|
# return 400.0
|
|
# elif blackboard_get(tick, "enemy_distance") < guard_distance * guard_distance * 0.3 && blackboard_get(tick, "stamina") > 10.0:
|
|
# return 80
|
|
# elif blackboard_get(tick, "enemy_distance") < attack_distance * attack_distance * 0.3:
|
|
# return 10.0
|
|
"unconcious":
|
|
v *= 2000.0
|
|
# if blackboard_get(tick, "health") < 10:
|
|
# return 2000
|
|
"rest":
|
|
v *= 150.0
|
|
# if blackboard_get(tick, "stamina") >= 100.0:
|
|
# return 0.0
|
|
# if blackboard_get(tick, "stamina") <= 10.0 && blackboard_get(tick, "enemy_distance") >= flee_distance * flee_distance:
|
|
# return 100.0 + blackboard_get(tick, "enemy_distance") / 10.0
|
|
"get_melee_damage":
|
|
v *= 3000.0
|
|
if blackboard_get(tick, "melee_damage"):
|
|
blackboard_set(tick, "melee_damage", false)
|
|
# if blackboard_get(tick, "health") <= 0.0:
|
|
# return 0.0
|
|
# if blackboard_get(tick, "melee_damage"):
|
|
# blackboard_set(tick, "melee_damage", false)
|
|
# print("DAMAGE2 ", blackboard_get(tick, "health"))
|
|
# return 1000
|
|
_:
|
|
assert(false)
|
|
return v
|
|
var last_behavior
|
|
func select_behavior(tick, delta):
|
|
var best_behavior
|
|
var best_utility
|
|
best_behavior = "rest"
|
|
best_utility = 0.0
|
|
for e in conf_behaviors.keys():
|
|
var utility = calc_utility(tick, e, delta)
|
|
if e == last_behavior:
|
|
utility *= 2.0
|
|
if best_utility < utility:
|
|
best_utility = utility
|
|
best_behavior = e
|
|
last_behavior = best_behavior
|
|
return best_behavior
|
|
|
|
var last_bhv
|
|
var last_running = []
|
|
var current_running = []
|
|
var run_behaviors = {}
|
|
func update_physics(tick, delta):
|
|
assert(get_memory(tick).initialized)
|
|
var root = get_character(tick)
|
|
assert(root)
|
|
var cam = root.get_viewport().get_camera()
|
|
if !cam:
|
|
return
|
|
if !cam.has_meta("player"):
|
|
return
|
|
blackboard_set(tick, "self", get_character(tick))
|
|
var space: PhysicsDirectSpaceState = root.get_world().get_direct_space_state()
|
|
var o = root.global_transform.origin
|
|
var p = cam.get_meta("player").global_transform
|
|
pdst = o.distance_squared_to(p.origin)
|
|
blackboard_set(tick, "enemy_distance", pdst)
|
|
blackboard_set(tick, "randf", rnd.randf())
|
|
blackboard_set(tick, "enemy_pos", p.origin)
|
|
blackboard_set(tick, "space", space)
|
|
# var adest = o + (p.origin - o).normalized() * min(2.0, sqrt(pdst))
|
|
var adest = p.origin
|
|
var away = (o - p.origin).normalized()
|
|
var attack_xform = Transform(root.global_transform.basis, o + away * 1.5)
|
|
|
|
var fdest = o + away * 4.0
|
|
var enemy_dir: Vector3 = Transform(p.basis, Vector3()).xform(Vector3(0, 0, -1))
|
|
var root_dir: Vector3 = Transform(root.global_transform.basis, Vector3()).xform(Vector3(0, 0, -1))
|
|
var dot = root_dir.dot(enemy_dir)
|
|
blackboard_set(tick, "dot", dot)
|
|
|
|
var prev_state = state
|
|
match state:
|
|
0:
|
|
last_running = current_running.duplicate()
|
|
current_running = []
|
|
var bhv = select_behavior(tick, delta)
|
|
if last_bhv != bhv:
|
|
# print("behavior: ", bhv)
|
|
last_bhv = bhv
|
|
if !run_behaviors[bhv].active:
|
|
match bhv:
|
|
"take_melee_weapon":
|
|
run_behaviors[bhv].start(root, "s_dagger")
|
|
"approach":
|
|
var dest = adest
|
|
if pdst > 25.0:
|
|
run_behaviors[bhv].start(root, dest, 0.5, 3.0, 4.0, 6.0)
|
|
else:
|
|
run_behaviors[bhv].start(root, dest, 0.2, 1.5, 2.0, 6.0)
|
|
"melee_attack":
|
|
run_behaviors[bhv].start(root, "attack-melee1", "about_to_attack")
|
|
"flee":
|
|
# walk away from enemy (player)
|
|
var dest = fdest
|
|
run_behaviors[bhv].start(root, dest, 0.3, 1.5, 20.0, 6.0)
|
|
"rest":
|
|
run_behaviors[bhv].start(root)
|
|
"guard":
|
|
run_behaviors[bhv].start(root, "guard-melee1", "guard-front-melee1")
|
|
"get_melee_damage":
|
|
run_behaviors[bhv].start(root, "guard-melee1", "guard-front-melee1", 10.0)
|
|
"unconcious":
|
|
run_behaviors[bhv].start(root, "fall-front", "fall-back")
|
|
_:
|
|
assert(false)
|
|
# print("adding behavior: ", bhv)
|
|
current_running.push_back(run_behaviors[bhv])
|
|
for e in current_running:
|
|
if !e in last_running:
|
|
if !e.active:
|
|
e.activate()
|
|
e.run(delta, blackboard_get_dict(tick))
|
|
#FIXME: mess :(
|
|
if e == run_behaviors["flee"]:
|
|
blackboard_set(tick, "flee_cooldown", 2.0 + rnd.randf() * 3.0)
|
|
if e.finished && e.active:
|
|
# print("in current run but finished")
|
|
e.stop()
|
|
for e in last_running:
|
|
if !e in current_running:
|
|
if e.active:
|
|
# print("not in current run but was in last")
|
|
e.stop()
|
|
1:
|
|
state = 2
|
|
2:
|
|
state = 3
|
|
3:
|
|
state = 0
|
|
if blackboard_get(tick, "stamina") < 100.0:
|
|
blackboard_set(tick, "stamina", blackboard_get(tick, "stamina") + delta)
|
|
if blackboard_get(tick, "attack_cooldown") > 0.0:
|
|
blackboard_set(tick, "attack_cooldown", blackboard_get(tick, "attack_cooldown") - delta)
|
|
if blackboard_get(tick, "guard_cooldown") > 0.0:
|
|
blackboard_set(tick, "guard_cooldown", blackboard_get(tick, "guard_cooldown") - delta)
|
|
if blackboard_get(tick, "flee_cooldown") > 0.0:
|
|
blackboard_set(tick, "flee_cooldown", blackboard_get(tick, "flee_cooldown") - delta)
|
|
blackboard_set(tick, "stamina", clamp(blackboard_get(tick, "stamina"), 0, 100.0))
|
|
blackboard_set(tick, "health", clamp(blackboard_get(tick, "health"), 0, 100.0))
|
|
return ERR_BUSY
|
|
func update(tick, delta):
|
|
return ERR_BUSY
|
|
|
|
##
|
|
# if prev_state != state:
|
|
# print("new state: ", state)
|
|
# print("running ", current_running.size(), " behaviors")
|