feat: add stylized villager presentation
This commit is contained in:
+69
-8
@@ -13,9 +13,16 @@ const DEBUG_LOGS := true
|
||||
|
||||
@onready var nav_agent: NavigationAgent3D = $NavigationAgent3D
|
||||
@onready var body_mesh: MeshInstance3D = $MeshInstance3D
|
||||
@onready var head_mesh: MeshInstance3D = $Head
|
||||
@onready var hair_mesh: MeshInstance3D = $Hair
|
||||
@onready var left_arm: MeshInstance3D = $ArmLeft
|
||||
@onready var right_arm: MeshInstance3D = $ArmRight
|
||||
@onready var left_leg: MeshInstance3D = $LegLeft
|
||||
@onready var right_leg: MeshInstance3D = $LegRight
|
||||
@onready var profession_prop: MeshInstance3D = $ProfessionProp
|
||||
@onready var profession_label: Label3D = $ProfessionLabel
|
||||
@onready var carried_food_visual: MeshInstance3D = $CarriedFood
|
||||
@onready var carried_wood_visual: Node3D = $CarriedWood
|
||||
@onready var task_glyph_root: Node3D = $TaskGlyphRoot
|
||||
@onready var task_glyph: MeshInstance3D = $TaskGlyphRoot/TaskGlyph
|
||||
|
||||
@@ -35,6 +42,8 @@ var path_pending := false
|
||||
var is_dead_visual := false
|
||||
var dead_material := StandardMaterial3D.new()
|
||||
var debug_overlay_visible := true
|
||||
var walk_phase := 0.0
|
||||
var glyph_phase := 0.0
|
||||
|
||||
|
||||
func debug_log(message: String) -> void:
|
||||
@@ -46,16 +55,62 @@ func _ready() -> void:
|
||||
dead_material.albedo_color = Color(0.25, 0.25, 0.25, 1.0)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if is_dead_visual:
|
||||
return
|
||||
glyph_phase = fmod(glyph_phase + delta * 2.2, TAU)
|
||||
if task_glyph_root.visible:
|
||||
task_glyph_root.position.y = 2.35 + sin(glyph_phase) * 0.045
|
||||
task_glyph_root.rotation.y = glyph_phase * 0.35
|
||||
|
||||
var horizontal_speed := Vector2(velocity.x, velocity.z).length()
|
||||
var blend := minf(delta * 9.0, 1.0)
|
||||
if horizontal_speed > 0.1:
|
||||
walk_phase = fmod(walk_phase + delta * horizontal_speed * 3.8, TAU)
|
||||
var swing := sin(walk_phase)
|
||||
left_leg.rotation_degrees.x = lerpf(left_leg.rotation_degrees.x, swing * 28.0, blend)
|
||||
right_leg.rotation_degrees.x = lerpf(right_leg.rotation_degrees.x, -swing * 28.0, blend)
|
||||
left_arm.rotation_degrees.x = lerpf(left_arm.rotation_degrees.x, -swing * 20.0, blend)
|
||||
right_arm.rotation_degrees.x = lerpf(right_arm.rotation_degrees.x, swing * 20.0, blend)
|
||||
var step_bob := absf(sin(walk_phase * 2.0)) * 0.035
|
||||
body_mesh.position.y = lerpf(body_mesh.position.y, 1.05 + step_bob, blend)
|
||||
head_mesh.position.y = lerpf(head_mesh.position.y, 1.68 + step_bob, blend)
|
||||
hair_mesh.position.y = lerpf(hair_mesh.position.y, 1.9 + step_bob, blend)
|
||||
else:
|
||||
left_leg.rotation_degrees.x = lerpf(left_leg.rotation_degrees.x, 0.0, blend)
|
||||
right_leg.rotation_degrees.x = lerpf(right_leg.rotation_degrees.x, 0.0, blend)
|
||||
left_arm.rotation_degrees.x = lerpf(left_arm.rotation_degrees.x, 0.0, blend)
|
||||
right_arm.rotation_degrees.x = lerpf(right_arm.rotation_degrees.x, 0.0, blend)
|
||||
body_mesh.position.y = lerpf(body_mesh.position.y, 1.05, blend)
|
||||
head_mesh.position.y = lerpf(head_mesh.position.y, 1.68, blend)
|
||||
hair_mesh.position.y = lerpf(hair_mesh.position.y, 1.9, blend)
|
||||
|
||||
|
||||
func setup_from_sim(npc: SimNPC) -> void:
|
||||
sim_id = npc.id
|
||||
npc_name = npc.npc_name
|
||||
profession = npc.profession
|
||||
name = "NPC_%s_%s" % [sim_id, npc_name]
|
||||
_apply_personal_details()
|
||||
_apply_profession_presentation()
|
||||
set_carried_food_visible(npc.get_inventory_amount(SimulationIds.RESOURCE_FOOD) > 0.0)
|
||||
set_carried_wood_visible(npc.get_inventory_amount(SimulationIds.RESOURCE_WOOD) > 0.0)
|
||||
set_task_presentation(npc.current_task, npc.task_state)
|
||||
|
||||
|
||||
func _apply_personal_details() -> void:
|
||||
var hair_colors := [
|
||||
Color(0.16, 0.09, 0.055, 1.0),
|
||||
Color(0.29, 0.16, 0.075, 1.0),
|
||||
Color(0.09, 0.065, 0.05, 1.0),
|
||||
Color(0.42, 0.27, 0.12, 1.0),
|
||||
]
|
||||
var hair_material := StandardMaterial3D.new()
|
||||
hair_material.albedo_color = hair_colors[maxi(sim_id, 0) % hair_colors.size()]
|
||||
hair_material.roughness = 0.9
|
||||
hair_mesh.material_override = hair_material
|
||||
|
||||
|
||||
func _apply_profession_presentation() -> void:
|
||||
var definition := SimulationDefinitions.get_profession(profession)
|
||||
if definition == null:
|
||||
@@ -67,6 +122,8 @@ func _apply_profession_presentation() -> void:
|
||||
body_material.albedo_color = definition.visual_color
|
||||
body_material.roughness = 0.82
|
||||
body_mesh.material_override = body_material
|
||||
left_arm.material_override = body_material
|
||||
right_arm.material_override = body_material
|
||||
|
||||
var prop_material := StandardMaterial3D.new()
|
||||
prop_material.albedo_color = definition.prop_color
|
||||
@@ -117,6 +174,10 @@ func set_carried_food_visible(is_visible: bool) -> void:
|
||||
carried_food_visual.visible = is_visible and not is_dead_visual
|
||||
|
||||
|
||||
func set_carried_wood_visible(is_visible: bool) -> void:
|
||||
carried_wood_visual.visible = is_visible and not is_dead_visual
|
||||
|
||||
|
||||
func set_task_presentation(action_id: StringName, task_state: StringName) -> void:
|
||||
if task_glyph_root == null or task_glyph == null:
|
||||
return
|
||||
@@ -264,6 +325,7 @@ func _report_navigation_failure_once() -> void:
|
||||
func apply_dead_visual_state() -> void:
|
||||
is_dead_visual = true
|
||||
carried_food_visual.visible = false
|
||||
carried_wood_visual.visible = false
|
||||
profession_prop.visible = false
|
||||
profession_label.visible = false
|
||||
task_glyph_root.visible = false
|
||||
@@ -278,9 +340,8 @@ func apply_dead_visual_state() -> void:
|
||||
scale = Vector3(0.8, 0.25, 1.2)
|
||||
rotation_degrees.x = 90.0
|
||||
|
||||
for child in get_children():
|
||||
if child is MeshInstance3D:
|
||||
child.material_override = dead_material
|
||||
for child in find_children("*", "MeshInstance3D", true, false):
|
||||
(child as MeshInstance3D).material_override = dead_material
|
||||
|
||||
collision_layer = 0
|
||||
collision_mask = 0
|
||||
@@ -305,7 +366,7 @@ func _create_task_glyph_mesh(action_id: StringName) -> PrimitiveMesh:
|
||||
food.radius = 0.18
|
||||
food.height = 0.26
|
||||
return food
|
||||
SimulationIds.ACTION_GATHER_WOOD:
|
||||
SimulationIds.ACTION_GATHER_WOOD, SimulationIds.ACTION_DEPOSIT_WOOD:
|
||||
var wood := BoxMesh.new()
|
||||
wood.size = Vector3(0.18, 0.42, 0.18)
|
||||
task_glyph.rotation_degrees = Vector3(0, 0, -18)
|
||||
@@ -325,7 +386,7 @@ func _create_task_glyph_mesh(action_id: StringName) -> PrimitiveMesh:
|
||||
var book := BoxMesh.new()
|
||||
book.size = Vector3(0.36, 0.08, 0.24)
|
||||
return book
|
||||
SimulationIds.ACTION_REST:
|
||||
SimulationIds.ACTION_REST, SimulationIds.ACTION_SLEEP:
|
||||
var rest := SphereMesh.new()
|
||||
rest.radius = 0.2
|
||||
rest.height = 0.12
|
||||
@@ -337,7 +398,7 @@ func _create_task_glyph_material(action_id: StringName) -> StandardMaterial3D:
|
||||
var material := StandardMaterial3D.new()
|
||||
material.roughness = 0.65
|
||||
material.emission_enabled = true
|
||||
material.emission_energy_multiplier = 0.35
|
||||
material.emission_energy_multiplier = 0.12
|
||||
var color := _get_task_glyph_color(action_id)
|
||||
material.albedo_color = color
|
||||
material.emission = color
|
||||
@@ -348,7 +409,7 @@ func _get_task_glyph_color(action_id: StringName) -> Color:
|
||||
match action_id:
|
||||
SimulationIds.ACTION_GATHER_FOOD, SimulationIds.ACTION_EAT, SimulationIds.ACTION_WITHDRAW_FOOD:
|
||||
return Color(0.95, 0.42, 0.25, 1.0)
|
||||
SimulationIds.ACTION_GATHER_WOOD:
|
||||
SimulationIds.ACTION_GATHER_WOOD, SimulationIds.ACTION_DEPOSIT_WOOD:
|
||||
return Color(0.58, 0.34, 0.16, 1.0)
|
||||
SimulationIds.ACTION_DEPOSIT_FOOD:
|
||||
return Color(0.95, 0.72, 0.32, 1.0)
|
||||
@@ -356,6 +417,6 @@ func _get_task_glyph_color(action_id: StringName) -> Color:
|
||||
return Color(0.36, 0.58, 0.95, 1.0)
|
||||
SimulationIds.ACTION_STUDY:
|
||||
return Color(0.62, 0.46, 0.9, 1.0)
|
||||
SimulationIds.ACTION_REST:
|
||||
SimulationIds.ACTION_REST, SimulationIds.ACTION_SLEEP:
|
||||
return Color(0.4, 0.78, 0.72, 1.0)
|
||||
return Color(0.9, 0.9, 0.85, 1.0)
|
||||
|
||||
Reference in New Issue
Block a user