Files
academy2-game/autoload/characters.gd
2021-11-22 01:09:02 +03:00

612 lines
18 KiB
GDScript3

extends Characters_
# Declare member variables here. Examples:
# var a = 2
# var b = "text"
# Called when the node enters the scene tree for the first time.
# Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta):
onready var female = preload("res://characters/vroid1-female.tscn")
onready var male = preload("res://characters/vroid1-man.tscn")
onready var face_ctrl = preload("res://scenes/face/head_comtrol.tscn")
onready var modules = {
# "physics": load("res://scripts/modules/character_physics.gd"),
"cmdq": preload("res://scripts/modules/cmdq.gd"),
"marker": preload("res://scripts/modules/npc_marker.gd"),
"sacrifice": preload("res://scripts/modules/npc_sacrifice.gd"),
"nun": preload("res://scripts/modules/npc_nun.gd"),
"player": preload("res://scripts/modules/player_controls.gd"),
"player_clothes": preload("res://scripts/modules/player_clothes.gd"),
"hurtboxes": preload("res://scripts/modules/character_hurtboxes.gd"),
"student": preload("res://scripts/modules/npc_student.gd")
}
var face_data_path = "res://scenes/face/"
var hair_data_path = "res://scenes/hair/"
var female_faces = []
var mesh_female_faces = {}
var male_faces = []
var mesh_male_faces = {}
var female_hairs = []
var male_hairs = []
var hair_materials = []
var name_data = {}
var rnd
var roommates = {}
#var _crowd: DetourCrowdManager
func _ready():
set_root_motion_mod(Transform())
var fd = File.new()
fd.open("res://data/names.json", File.READ)
var names = fd.get_as_text()
var result = JSON.parse(names)
name_data = result.result
rnd = RandomNumberGenerator.new()
var data_fd: = File.new()
for g in ["male", "female"]:
var fp = face_data_path + g + "-face.tscn"
if data_fd.file_exists(fp):
match g:
"female":
female_faces.push_back(fp)
mesh_female_faces[fp] = load(fp)
"male":
male_faces.push_back(fp)
mesh_male_faces[fp] = load(fp)
for id in range(10000):
var fp_m = face_data_path + "male-face" + str(id) + ".tscn"
var fp_f = face_data_path + "female-face" + str(id) + ".tscn"
var hp_m = hair_data_path + "male-hair" + str(id) + ".tscn"
var hp_f = hair_data_path + "female-hair" + str(id) + ".tscn"
var mat = hair_data_path + "hair" + str(id) + ".tres"
if data_fd.file_exists(fp_m):
male_faces.push_back(fp_m)
mesh_male_faces[fp_m] = load(fp_m)
if data_fd.file_exists(fp_f):
female_faces.push_back(fp_f)
mesh_female_faces[fp_f] = load(fp_f)
if data_fd.file_exists(hp_m):
male_hairs.push_back(hp_m)
if data_fd.file_exists(hp_f):
female_hairs.push_back(hp_f)
if data_fd.file_exists(mat):
hair_materials.push_back(mat)
assert(male_faces.size() > 0)
assert(female_faces.size() > 0)
assert(male_hairs.size() > 0)
assert(female_hairs.size() > 0)
assert(hair_materials.size() > 0)
func get_relationship(obj1, obj2, s) -> int:
var stats1 = {}
var stats2 = {}
if obj1.has_meta("stats"):
stats1 = obj1.get_meta("stats")
if obj2.has_meta("stats"):
stats2 = obj2.get_meta("stats")
if !stats2.has("relationships"):
return 0
var rel = stats2.relationships
var cid = stats1.firstname + stats1.lastname
if !rel.has(cid):
return 0
var rel_data = rel[cid]
if rel_data.has(s):
return rel_data[s]
return 0
func set_relationships(obj1, obj2, s, v: int) -> void:
assert(obj1)
assert(obj2)
var stats1 = {}
var stats2 = {}
if obj1.has_meta("stats"):
stats1 = obj1.get_meta("stats")
if obj2.has_meta("stats"):
stats2 = obj2.get_meta("stats")
if !stats2.has("relationships"):
stats2.relationships = {}
var rel = stats2.relationships
var cid = stats1.firstname + stats1.lastname
if !rel.has(cid):
rel[cid] = {}
rel[cid][s] = v
print(stats2)
obj2.set_meta("stats", stats2)
func get_face_node(sc: Node) -> Node:
var face_node_paths = ["skeleton/Skeleton/head/face", "Skeleton/head/face"]
for e in face_node_paths:
if sc.has_node(e):
print("face node: ", e)
return sc.get_node(e)
assert(0)
return null
func get_hair_node(sc: Node) -> Node:
var hair_node_paths = ["skeleton/Skeleton/head/hair", "Skeleton/head/hair"]
for e in hair_node_paths:
if sc.has_node(e):
print("hair node: ", e)
return sc.get_node(e)
assert(0)
return null
func compose_kinematic_character(g, enable_modules = [], face = -1, hair = -1, hair_mat = -1):
var body = KinematicBody.new()
var cshape = CollisionShape.new()
body.add_child(cshape)
var capsule = CapsuleShape.new()
var character_data = {
"sex": g,
"modules": enable_modules
}
var face_scene: PackedScene
var hair_scene: PackedScene
var face_i: Node
var hair_i: Node
var face_node: Node
var hair_node: Node
match g:
"female":
if face == -1:
face = rnd.randi() % female_faces.size()
if hair == -1:
hair = rnd.randi() % female_hairs.size()
face_scene = mesh_female_faces[female_faces[face]]
hair_scene = load(female_hairs[hair])
capsule.radius = 0.2
capsule.height = 1.1
capsule.margin = 0.05
cshape.translation.x = 0
cshape.translation.y = 0.751
cshape.translation.z = 0
cshape.rotation = Vector3(-PI/2.0, 0, 0)
var i = female.instance()
body.add_child(i)
i.transform = Transform()
face_node = get_face_node(i)
hair_node = get_hair_node(i)
"male":
if face == -1:
face = rnd.randi() % male_faces.size()
if hair == -1:
hair = rnd.randi() % male_hairs.size()
face_scene = mesh_male_faces[male_faces[face]]
hair_scene = load(male_hairs[hair])
capsule.radius = 0.3
capsule.height = 1.2
capsule.margin = 0.05
cshape.translation.x = 0
cshape.translation.y = 0.899
cshape.translation.z = 0
cshape.rotation = Vector3(-PI/2.0, 0, 0)
var i = male.instance()
body.add_child(i)
i.transform = Transform()
face_node = get_face_node(i)
hair_node = get_hair_node(i)
assert(face_node)
face_i = face_scene.instance()
face_i.add_to_group("face")
prepare_extra_skeleton(face_i, "face")
hair_i = hair_scene.instance()
hair_i.add_to_group("hair")
prepare_extra_skeleton(hair_i, "hair")
if hair_mat == -1:
hair_mat = rnd.randi() % hair_materials.size()
var hmat = load(hair_materials[hair_mat])
assert(hmat)
set_hair_material(hair_i, hmat)
for e in face_node.get_children():
e.queue_free()
for e in hair_node.get_children():
e.queue_free()
face_node.add_child(face_i)
hair_node.add_child(hair_i)
var face_ctrl_i = face_ctrl.instance()
face_ctrl_i.active = true
face_i.add_child(face_ctrl_i)
face_i.set_meta("body", body)
body.set_meta("face_control", face_ctrl_i)
body.set_meta("face_playback", "parameters/state/playback")
face_ctrl_i.add_to_group("face")
face_i.transform = Transform()
character_data.face = face
character_data.hair = hair
character_data.hair_mat = hair_mat
cshape.shape = capsule
body.set_meta("character_data", character_data)
for e in enable_modules:
assert(modules.has(e))
if modules.has(e):
assert(modules[e])
var obj = modules[e].new()
body.add_child(obj)
setup_character_physics(body)
body.add_to_group("character")
return body
func replace_character(obj, g, enable_modules = [], face = -1, hair = -1, hair_mat = -1):
assert(obj)
var xform = obj.global_transform
var p = obj.get_parent()
obj.queue_free()
var body = compose_kinematic_character(g, enable_modules, face, hair, hair_mat)
p.add_child(body)
body.global_transform = xform
var orientation = Transform()
orientation.basis = xform.basis
body.set_meta("orientation", orientation)
return body
const basedir = "res://scenes/clothes/"
func prepare_extra_skeleton(obj, g):
var queue = [obj]
while queue.size() > 0:
var item = queue.pop_front()
if item is Skeleton:
item.add_to_group(g)
break
for g in item.get_children():
queue.push_back(g)
func set_hair_material(hair, mat: Material):
assert(mat)
var queue = [hair]
while queue.size() > 0:
var item = queue.pop_front()
if item is MeshInstance:
item.set_surface_material(0, mat)
break
for g in item.get_children():
queue.push_back(g)
func setup_garments(obj, garments, garments_head, material):
var skel = obj.get_meta("skeleton")
var hair_skel = obj.get_meta("hair_skeleton")
if obj.has_meta("garments"):
print("Can remove garments")
var d = obj.get_meta("garments")
var db = d.body_data
var dh = d.head_data
for e in db + dh:
print("remove: ", e)
# obj.remove_child(e)
if e.get_parent() == obj:
obj.remove_child(e)
e.queue_free()
var gdata_body = []
for g in garments:
var m: MeshInstance = MeshInstance.new()
m.name = g
print("loading: ", basedir + g + ".mesh")
var geo: ArrayMesh = load(basedir + g + ".mesh")
assert(geo)
print("mesh: ", geo, "mat: ", material)
geo.surface_set_material(0, material)
m.mesh = geo
# m.skeleton = m.get_path_to(root.get_meta("skeleton"))
# = root.get_meta("skeleton")
skel.add_child(m)
m.transform = Transform()
gdata_body.push_back(m)
var gdata_head = []
for g in garments_head:
var m: MeshInstance = MeshInstance.new()
m.name = g
var geo: ArrayMesh = load(basedir + g + ".mesh")
print("mesh: ", geo, "mat: ", material)
geo.surface_set_material(0, material)
m.mesh = geo
# m.skeleton = m.get_path_to(root.get_meta("skeleton"))
# = root.get_meta("skeleton")
hair_skel.add_child(m)
m.transform = Transform()
gdata_head.push_back(m)
var gdata = {
"body": garments,
"head": garments_head,
"material": material,
"body_data": gdata_body,
"head_data": gdata_head
}
obj.set_meta("garments", gdata)
func lastname():
var lastnames = name_data.lastname
var d = lastnames.size()
return lastnames[rnd.randi() % d].to_lower().capitalize()
func firstname(g):
var firstnames = name_data[g].firstname
var d = firstnames.size()
return firstnames[rnd.randi() % d].to_lower().capitalize()
func generate_stats(npc: Node):
var stats = npc.get_meta("stats")
var strength = 10 + rnd.randi() % 100
var max_health = 10 + rnd.randi() % 100
var max_stamina = 10 + rnd.randi() % 100
stats.tiredness = 0.0
stats.hunger = 0.0
stats.thirst = 0.0
stats.libido = 0.0
stats.toilet = 0.0
stats.stress = 0.0
stats.health = max_health
stats.stamina = max_stamina
stats.max_health = max_health
stats.max_stamina = max_stamina
stats.violence = rnd.randi() % (int(strength * 0.5))
stats.xp = 0
npc.set_meta("stats", stats)
var stat_changes = {
"sleep":{
"tiredness": -0.3,
"stress": -0.03
},
"eating": {
"hunger": -0.9,
"stress": -0.01
},
"drinking": {
"thirst": -2.8,
"stress": -0.01
},
"use_tap": {
"thirst": -2.8,
"stress": -0.01
},
"locomotion": {
"tiredness": 0.0015,
"hunger": 0.025,
"thirst": 0.045,
"stress": 0.0001,
"libido": 0.00015,
"toilet": 0.0002
}
}
func update_stats(npc: Node):
var stats = npc.get_meta("stats")
if stats.tiredness < 10.0:
if stats.stamina < stats.max_stamina:
stats.stamina += 0.1
stats.stamina = clamp(stats.stamina, 0, stats.max_stamina)
var animtree = npc.get_meta("animation_tree")
var state = animtree["parameters/state/playback"].get_current_node()
if state in stat_changes.keys():
for s in stat_changes[state].keys():
stats[s] += stat_changes[state][s]
if stats[s] < 0:
stats[s] = 0.0
npc.set_meta("stats", stats)
# Action part should be integrated into SmartObjectManager
func check_leave_smart(npc: Node):
return
if !npc.has_meta("smart_object"):
return
var sm = npc.get_meta("smart_object")
var new_goal = calculate_goal(npc)
if !sm.is_in_group(new_goal):
# var animtree = npc.get_meta("animation_tree")
# animtree["parameters/state/playback"].travel("locomotion")
npc.remove_meta("smart_object")
func calculate_goal(npc: Node):
var utility = {
"sleep": ["tiredness", 900],
"hungry": ["hunger", 1000],
"thirsty": ["thirst", 1300],
"relieve_stress": ["stress", 800],
"have_sex": ["libido", 500],
"idle": ["", 600]
}
var stats = npc.get_meta("stats")
var rutility = -10000
var goal = "idle"
var last_goal = ""
var last_utility = -1
if npc.has_meta("last_goal"):
last_goal = npc.get_meta("last_goal")
goal = npc.get_meta("last_goal")
if npc.has_meta("last_utility"):
last_utility = int(npc.get_meta("last_utility"))
rutility = int(npc.get_meta("last_utility") * 2.0)
for k in utility.keys():
var st = 1.0
if utility[k][0] != "":
st = stats[utility[k][0]]
var mul = utility[k][1]
var cutil = int(st * mul)
if rutility < cutil:
rutility = cutil
goal = k
npc.set_meta("last_goal", goal)
if goal == last_goal:
npc.set_meta("last_utility", last_utility)
else:
npc.set_meta("last_utility", rutility)
return goal
var cooldown = 0.0
var close_groups = {}
func walkto_(npc, target):
assert(npc.has_meta("agent_id"))
if npc.has_mete("agent_id"):
walkto_agent(npc, target)
# if npc.has_meta("_target"):
# var otarget = npc.get_meta("_target")
# if target == otarget:
# return
# assert(npc.has_meta("agent_id"))
# npc.set_meta("_target", target)
## print("walk to ", target)
## print("walk to ", target)
## print("walk to ", target)
# var agent_id = npc.get_meta("agent_id")
# if target is Spatial:
# get_crowd().set_agent_target_position(agent_id, target.global_transform.origin)
# elif target is Vector3:
# get_crowd().set_agent_target_position(agent_id, target)
#func update_to_agent(root):
# if !_crowd:
# return
# if !root.has_meta("agent_id"):
# _crowd.add_agent(root, 0, false, PoolRealArray([0.2, 1.5, 0.3, 3.0]))
# if !root.has_meta("agent_id"):
# return
# if !root.has_meta("_target"):
# return
# var velocity = root.get_meta("agent_velocity")
# if velocity.length() == 0:
# characters.set_walk_speed(root, 0, 0)
# else:
## print("agent_velocity=", velocity)
# var vel1 = velocity
# vel1.y = 0
# var xform: = Transform().looking_at(vel1, Vector3.UP)
# xform = xform.orthonormalized()
# var orientation = root.get_meta("orientation")
# orientation.basis = orientation.basis.slerp(xform.basis, get_physics_process_delta_time())
# root.set_meta("orientation", orientation)
# characters.set_walk_speed(root, clamp(vel1.length() / 6.5, 0.0, 1.0), 0)
#
#func character_physics(root):
# var delta = get_physics_process_delta_time()
# var animtree = root.get_meta("animation_tree")
# var orientation = root.get_meta("orientation")
# var root_motion = animtree.get_root_motion_transform()
# orientation *= root_motion
# var h_velocity = orientation.origin / delta
# var velocity: = Vector3()
# velocity.x = h_velocity.x
# velocity.z = h_velocity.z
# velocity.y = h_velocity.y
## if root.has_meta("climb"):
## print("has climb meta")
## if root.has_meta("cmdqueue"):
## print("+has cmdqueue")
# if !root.has_meta("vehicle") && !root.has_meta("cmdqueue"):
# if root is KinematicBody:
# if !root.is_on_floor():
# velocity += Vector3(0, -9.8, 0)
# velocity = root.move_and_slide(velocity, Vector3.UP, true, 4, 0.785, false)
# elif root.has_meta("cmdqueue") && root.has_meta("cmdq_walk"):
# if root is KinematicBody:
# if !root.is_on_floor() && !root.has_meta("climb"):
# velocity += Vector3(0, -9.8, 0)
# velocity = root.move_and_slide(velocity, Vector3.UP, true, 4, 0.785, false)
# elif root.has_meta("cmdqueue") && root.has_meta("climb"):
# if root is KinematicBody:
# velocity.y = h_velocity.y
# velocity = root.move_and_slide(velocity, Vector3.UP, true, 4, 0.785, false)
# orientation.origin = Vector3()
# orientation = orientation.orthonormalized()
# root.global_transform.basis = orientation.basis
# root.set_meta("orientation", orientation)
# if root.has_meta("vehicle"):
# var vehicle: VehicleBody = get_meta("vehicle")
# if has_meta("vehicle_offset"):
# var xform = get_meta("vehicle_offset")
# root.global_transform = vehicle.global_transform * xform
# if root.has_node("blackboard"):
# var bb = root.get_node("blackboard")
# bb.set("velocity", velocity)
# update_to_agent(root)
func check_main_animtree(item):
if !item is AnimationTree:
return false
for g in ["hair", "face"]:
if item.is_in_group(g):
return false
return true
func check_main_skeleton(item):
if !item is Skeleton:
return false
for g in ["hair", "face"]:
if item.is_in_group(g):
return false
return true
func setup_character_physics(root):
var queue = [root]
while queue.size() > 0:
var item = queue.pop_front()
if check_main_animtree(item):
root.set_meta("animation_tree", item)
if check_main_skeleton(item):
root.set_meta("skeleton", item)
if item is Skeleton && item.is_in_group("hair"):
root.set_meta("hair_skeleton", item)
if item is Skeleton && item.is_in_group("face"):
root.set_meta("face_skeleton", item)
for e in item.get_children():
queue.push_back(e)
# var velocity = Vector3()
var orientation = Transform()
if root.is_inside_tree():
orientation = root.global_transform
orientation.origin = Vector3()
root.set_meta("orientation", orientation)
func _process(delta):
if cooldown > 0.0:
cooldown -= delta
return
for e in get_tree().get_nodes_in_group("character"):
var gr = []
for o in get_tree().get_nodes_in_group("character"):
if e == o:
continue
var e_org = e.global_transform.origin
var o_org = o.global_transform.origin
if e_org.distance_to(o_org) < 1.2:
gr.push_back(o)
close_groups[e] = gr
if e.has_meta("animation_tree"):
var animtree = e.get_meta("animation_tree")
var state = animtree["parameters/state/playback"].get_current_node()
var face_playback = e.get_meta("face_playback")
var face_ctrl = e.get_meta("face_control")
var p = face_ctrl[face_playback]
if state == "sleeping":
if p.get_current_node() != "sleeping":
p.travel("sleeping")
else:
if p.get_current_node() != "locomotion":
p.travel("locomotion")
cooldown = 0.2
func get_player():
var c = get_viewport().get_camera()
if !c:
return null
if !c.has_meta("player"):
return null
var player = c.get_meta("player")
if !player:
return null
return player
func _physics_process(delta):
var player = get_player()
if !player:
return
if player.has_meta("animation_tree") && !player.has_meta("vehicle"):
character_physics(player)
# for e in get_tree().get_nodes_in_group("character") + [player]:
# if e && e.has_meta("animation_tree"):
# character_physics(e)