saving game state

pull/6/head
John Montagu, the 4th Earl of Sandvich 2023-08-07 02:38:51 -07:00
parent 9583a1c624
commit 95e323ed63
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
34 changed files with 511 additions and 242 deletions

Binary file not shown.

View File

@ -0,0 +1,9 @@
~ start
Death Count\: {{Stats.DeathCount}} forsenPls
- Save
do World.SaveGame()
=> END
- Do nothing
=> END

View File

@ -0,0 +1,15 @@
[remap]
importer="dialogue_manager_compiler_8"
type="Resource"
uid="uid://c2om4y0fm81yr"
path="res://.godot/imported/clone-machine.dialogue-8810934a67eacdad52469e9ef5f970fb.tres"
[deps]
source_file="res://Assets/Dialogue/clone-machine.dialogue"
dest_files=["res://.godot/imported/clone-machine.dialogue-8810934a67eacdad52469e9ef5f970fb.tres"]
[params]
defaults=true

Binary file not shown.

View File

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://dajb6kb64otkq"
path="res://.godot/imported/cinder-block-move.wav-31f9f0e94dc8ff5911113ce81f087608.sample"
[deps]
source_file="res://Assets/Sounds/cinder-block-move.wav"
dest_files=["res://.godot/imported/cinder-block-move.wav-31f9f0e94dc8ff5911113ce81f087608.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View File

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://ciyi5ectbbscr"
path="res://.godot/imported/rock-impact.wav-9c8cc497be5f59299eae3f339de44d60.sample"
[deps]
source_file="res://Assets/Sounds/rock-impact.wav"
dest_files=["res://.godot/imported/rock-impact.wav-9c8cc497be5f59299eae3f339de44d60.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bqc7k0rstj7xt"
path="res://.godot/imported/lidl-okayeg-placeholder.png-ddb44da36147757b2a8d4a20c3173cad.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Sprites/lidl-okayeg-placeholder.png"
dest_files=["res://.godot/imported/lidl-okayeg-placeholder.png-ddb44da36147757b2a8d4a20c3173cad.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -78,3 +78,4 @@ texture = SubResource("AtlasTexture_n00hm")
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
stream = ExtResource("4_p2qp6")
bus = &"UI"

View File

@ -14,6 +14,9 @@ public abstract partial class Boss : Enemy
[Export]
public AudioStream Music { get; set; }
[Export]
public Vector2 InitialPosition { get; set; }
public abstract int Intensity { get; }
private bool _isActive;
@ -35,10 +38,15 @@ public abstract partial class Boss : Enemy
{
base._Ready();
Death += (Events.HealthChangedArgs args) =>
Death += (Events.HurtArgs args) =>
{
UpdateBossStatus(true);
};
this.GetWorld().CurrentPlayer.Death += (args) =>
{
Reset();
};
}
protected void UpdateBossStatus(bool status)
@ -46,4 +54,24 @@ public abstract partial class Boss : Enemy
GetNode<State.Global.GlobalState>("/root/GlobalState")
.Progression.BossStatus[SceneFilePath] = status;
}
protected virtual void Reset()
{
IsActive = false;
// reset animations
foreach (var child in GetNode("Animations").GetChildren())
{
if (child is AnimationPlayer anim)
{
if (anim.HasAnimation("RESET"))
{
anim.Play("RESET");
}
}
}
StateMachine.ChangeState(StateMachine.InitialState);
ThinkerStateMachine.ChangeState(ThinkerStateMachine.InitialState);
}
}

View File

@ -30,10 +30,13 @@ public partial class Character : CharacterBody2D, IFaction
public float Stealth { get; protected set; } = 0;
[Signal]
public delegate void HurtEventHandler(Events.HealthChangedArgs args);
public delegate void HealthChangedEventHandler(float oldHP, float newHP);
[Signal]
public delegate void DeathEventHandler(Events.HealthChangedArgs args);
public delegate void HurtEventHandler(Events.HurtArgs args);
[Signal]
public delegate void DeathEventHandler(Events.HurtArgs args);
protected float _mass = 1.0f;
@ -242,7 +245,7 @@ public partial class Character : CharacterBody2D, IFaction
Vector2 knockbackDir = default) => damage;
public virtual void OnReceivedDamage(
protected virtual void OnReceivedDamage(
float damage,
Character inflictor,
float knockback,
@ -284,13 +287,6 @@ public partial class Character : CharacterBody2D, IFaction
}
}
// if anyone involved is a player, shake their screen
Player plr = inflictor as Player ?? this as Player;
if (plr is not null)
{
//plr.Camera.Shake(1, 0.4f);
}
if (this.GetNode("Effects/HurtSound") is AudioStreamPlayer2D sound)
{
// very small pitch deviation
@ -298,7 +294,7 @@ public partial class Character : CharacterBody2D, IFaction
sound.At(GlobalPosition).WithPitchDeviation(0.125f).PlayOneShot();
}
Events.HealthChangedArgs args = new Events.HealthChangedArgs
Events.HurtArgs args = new Events.HurtArgs
{
Attacker = inflictor,
OldHealth = oldHealth,
@ -317,6 +313,14 @@ public partial class Character : CharacterBody2D, IFaction
}
}
/// <summary>
/// For debugging purposes
/// </summary>
public void Inflict(float damage)
{
OnReceivedDamage(damage, null, 0);
}
public virtual void Footstep()
{
if (GetNode("Effects/Footstep") is AudioStreamPlayer2D player)

View File

@ -1,8 +1,6 @@
using Godot;
using SupaLidlGame.Extensions;
using SupaLidlGame.State.Character;
using SupaLidlGame.BoundingBoxes;
using SupaLidlGame.State.Thinker;
namespace SupaLidlGame.Characters;
@ -104,13 +102,12 @@ public partial class Doc : Boss
// when we are hurt, start the boss fight
Hurt += (Events.HealthChangedArgs args) =>
Hurt += (Events.HurtArgs args) =>
{
if (!IsActive)
{
IsActive = true;
Inventory.SelectedItem = Lance;
//DialogueManager.ShowExampleDialogueBalloon();
}
};
}
@ -123,48 +120,4 @@ public partial class Doc : Boss
}
base._Process(delta);
}
protected override float ReceiveDamage(
float damage,
Character inflictor,
float knockback,
Vector2 knockbackDir = default)
{
if (IsActive)
{
return base.ReceiveDamage(
damage, inflictor, knockback, knockbackDir);
}
return 1;
}
/*
public override void OnReceivedDamage(
float damage,
Character inflictor,
float knockback,
Vector2 knockbackDir = default)
{
GetNode<GpuParticles2D>("Effects/HurtParticles")
.SetDirection(knockbackDir);
base.OnReceivedDamage(damage, inflictor, knockback, knockbackDir);
}
*/
protected override void Think()
{
if (BossStateMachine.CurrentState is State.NPC.Doc.DocLanceState)
{
if (ThinkerStateMachine.CurrentState is not DashDefensive)
{
ThinkerStateMachine.ChangeState<DashDefensive>(out var _);
}
}
else
{
base.Think();
}
}
}

View File

@ -701,6 +701,8 @@ libraries = {
"": SubResource("AnimationLibrary_kks2p")
}
[node name="Attack" type="AnimationPlayer" parent="Animations"]
[node name="Misc" type="AnimationPlayer" parent="Animations"]
libraries = {
"": SubResource("AnimationLibrary_kjxam")

View File

@ -25,11 +25,10 @@ public sealed partial class Player : Character
public override void _Ready()
{
InteractionRay = GetNode<InteractionRay>("Direction2D/InteractionRay");
Death += async (Events.HealthChangedArgs args) =>
Death += async (Events.HurtArgs args) =>
{
HurtAnimation.Play("death");
await ToSignal(HurtAnimation, "animation_finished");
Visible = false;
};
base._Ready();
@ -54,7 +53,7 @@ public sealed partial class Player : Character
public void Spawn()
{
Health = 100;
Visible = true;
HurtAnimation.Play("spawn");
}
public override void ModifyVelocity()
@ -69,7 +68,7 @@ public sealed partial class Player : Character
// TODO: implement visual effects for stun
}
public override void OnReceivedDamage(
protected override void OnReceivedDamage(
float damage,
Character inflictor,
float knockback,

View File

@ -72,7 +72,6 @@ public partial class Balloon : CanvasLayer
{
Next(dialogueLine.NextId);
}
};
margin.Resized += () => HandleResize();
@ -105,6 +104,7 @@ public partial class Balloon : CanvasLayer
private async void Next(string nextId)
{
DialogueLine = await DialogueManager.GetNextDialogueLine(resource, nextId, temporaryGameStates ?? new Array<Variant>());
GD.Print(DialogueLine.Text);
}
@ -265,6 +265,7 @@ public partial class Balloon : CanvasLayer
{
isWaitingForInput = true;
balloon.FocusMode = Control.FocusModeEnum.All;
balloon.GrabFocus();
}
}
}

View File

@ -1,6 +1,8 @@
using Godot;
using GodotUtilities;
using SupaLidlGame.BoundingBoxes;
using SupaLidlGame.Extensions;
using DialogueManagerRuntime;
namespace SupaLidlGame.Entities;
@ -14,16 +16,27 @@ public partial class Campfire : StaticBody2D, Utils.IInteractive
[Signal]
public delegate void UseEventHandler();
[Export(PropertyHint.File, "*.dialogue")]
public Resource DialogueResource { get; set; }
public override void _Ready()
{
InteractionTrigger = GetNode<InteractionTrigger>("InteractionTrigger");
_light = GetNode<PointLight2D>("PointLight2D");
InteractionTrigger.Interaction += () =>
{
// save the player's spawn position to be their position on interaction
EmitSignal(SignalName.Use);
this.GetAncestor<Utils.World>().SetSpawn(GlobalPosition);
var world = this.GetWorld();
// TODO: implement and use max health
world.CurrentPlayer.Health = 100;
//this.GetAncestor<Utils.World>().SetSpawn(GlobalPosition);
world.DialogueBalloon.Start(DialogueResource, "start");
//DialogueManager.ShowExampleDialogueBalloon(DialogueResource, "start");
};
}

View File

@ -1,105 +1,23 @@
[gd_scene load_steps=18 format=3 uid="uid://dhl071rj5wyvx"]
[gd_scene load_steps=7 format=3 uid="uid://dhl071rj5wyvx"]
[ext_resource type="Texture2D" uid="uid://yqet0b22i70d" path="res://Assets/Sprites/campfire.png" id="1_7eor7"]
[ext_resource type="Script" path="res://Entities/Campfire.cs" id="1_w4gfp"]
[ext_resource type="Resource" uid="uid://c2om4y0fm81yr" path="res://Assets/Dialogue/clone-machine.dialogue" id="2_s6cen"]
[ext_resource type="Texture2D" uid="uid://coarr28adgo1u" path="res://Assets/Sprites/Particles/point-light.png" id="3_lm3vq"]
[ext_resource type="Texture2D" uid="uid://bqc7k0rstj7xt" path="res://Assets/Sprites/lidl-okayeg-placeholder.png" id="3_o7u16"]
[ext_resource type="PackedScene" uid="uid://dldnp8eunxj3q" path="res://BoundingBoxes/InteractionTrigger.tscn" id="4_yoo3p"]
[sub_resource type="AtlasTexture" id="AtlasTexture_68qj1"]
atlas = ExtResource("1_7eor7")
region = Rect2(0, 0, 16, 16)
[sub_resource type="AtlasTexture" id="AtlasTexture_rt0be"]
atlas = ExtResource("1_7eor7")
region = Rect2(16, 0, 16, 16)
[sub_resource type="AtlasTexture" id="AtlasTexture_0embb"]
atlas = ExtResource("1_7eor7")
region = Rect2(32, 0, 16, 16)
[sub_resource type="AtlasTexture" id="AtlasTexture_victi"]
atlas = ExtResource("1_7eor7")
region = Rect2(48, 0, 16, 16)
[sub_resource type="AtlasTexture" id="AtlasTexture_jg745"]
atlas = ExtResource("1_7eor7")
region = Rect2(64, 0, 16, 16)
[sub_resource type="SpriteFrames" id="SpriteFrames_o6lfi"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": SubResource("AtlasTexture_68qj1")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_rt0be")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_0embb")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_victi")
}],
"loop": true,
"name": &"active",
"speed": 5.0
}, {
"frames": [{
"duration": 1.0,
"texture": SubResource("AtlasTexture_jg745")
}],
"loop": true,
"name": &"default",
"speed": 8.0
}]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_ubam4"]
size = Vector2(16, 4)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_p0hsm"]
size = Vector2(20, 10)
[sub_resource type="Gradient" id="Gradient_cr0xb"]
offsets = PackedFloat32Array(0, 0.644444, 1)
colors = PackedColorArray(2, 1.6, 0.8, 1, 0.863171, 0.393552, 0.380535, 1, 0.304405, 0.24341, 0.229614, 1)
[sub_resource type="GradientTexture1D" id="GradientTexture1D_pjqf7"]
gradient = SubResource("Gradient_cr0xb")
[sub_resource type="Curve" id="Curve_jm83f"]
_data = [Vector2(0.144578, 1), 0.0, 0.0, 0, 0, Vector2(1, 0.4), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_jpefi"]
curve = SubResource("Curve_jm83f")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_xtx2m"]
emission_shape = 1
emission_sphere_radius = 5.0
particle_flag_disable_z = true
direction = Vector3(0, -1, 0)
spread = 0.0
gravity = Vector3(0, -16, 0)
initial_velocity_min = 16.0
initial_velocity_max = 16.0
orbit_velocity_min = 0.0
orbit_velocity_max = 0.0
radial_accel_min = -40.0
radial_accel_max = -20.0
scale_min = 2.0
scale_max = 4.0
scale_curve = SubResource("CurveTexture_jpefi")
color_ramp = SubResource("GradientTexture1D_pjqf7")
[sub_resource type="CircleShape2D" id="CircleShape2D_pdkmj"]
radius = 20.0
[node name="Campfire" type="StaticBody2D"]
texture_filter = 3
position = Vector2(0, -8)
script = ExtResource("1_w4gfp")
DialogueResource = ExtResource("2_s6cen")
[node name="Sprite2D" type="AnimatedSprite2D" parent="."]
sprite_frames = SubResource("SpriteFrames_o6lfi")
animation = &"active"
frame_progress = 0.343741
[node name="Sprite2D" type="Sprite2D" parent="."]
position = Vector2(0, -8)
texture = ExtResource("3_o7u16")
[node name="PointLight2D" type="PointLight2D" parent="."]
color = Color(0.996078, 0.780392, 0.615686, 1)
@ -109,18 +27,17 @@ shadow_enabled = true
texture = ExtResource("3_lm3vq")
texture_scale = 0.25
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(0, 6)
shape = SubResource("RectangleShape2D_ubam4")
[node name="InteractionTrigger" parent="." instance=ExtResource("4_yoo3p")]
position = Vector2(0, -5)
[node name="CollisionShape2D" parent="InteractionTrigger" index="0"]
position = Vector2(0, 5)
shape = SubResource("RectangleShape2D_p0hsm")
shape = SubResource("CircleShape2D_pdkmj")
[node name="GPUParticles2D" type="GPUParticles2D" parent="."]
amount = 40
process_material = SubResource("ParticleProcessMaterial_xtx2m")
[node name="Popup" parent="InteractionTrigger" index="1"]
offset_top = -49.0
offset_bottom = -19.0
[node name="Label" parent="InteractionTrigger/Popup" index="0"]
text = "Clone Machine"
[editable path="InteractionTrigger"]

View File

@ -9,7 +9,7 @@ public partial class DynamicDoor : StaticBody2D
public string MapStateKey { get; set; }
[Export]
public Godot.Collections.Array<Node2D> VisibleOnToggle { get; set; } = new();
public Godot.Collections.Array<NodePath> VisibleOnToggle { get; set; } = new();
[Export]
public bool DefaultState { get; set; }
@ -22,6 +22,7 @@ public partial class DynamicDoor : StaticBody2D
var globalState = this.GetGlobalState();
globalState.MapState.MapStateBoolChanged += OnMapStateChanged;
SetAnimations(false);
RefreshMapState((bool)globalState.MapState[MapStateKey]);
}
@ -30,12 +31,7 @@ public partial class DynamicDoor : StaticBody2D
GD.Print("Map state changed");
if (key == MapStateKey)
{
foreach (Node2D node in VisibleOnToggle)
{
// this is so extra effects are not played or showed when the
// door opens/closes from loading the map state.
node.Visible = true;
}
SetAnimations(true);
RefreshMapState(value);
}
}
@ -55,10 +51,31 @@ public partial class DynamicDoor : StaticBody2D
public virtual void Open()
{
_animPlayer?.TryPlay("open");
//this.GetWorld().CurrentPlayer.Camera.Shake(1, 0.5f);
}
public virtual void Close()
{
_animPlayer?.TryPlay("cose");
_animPlayer?.TryPlay("close");
//this.GetWorld().CurrentPlayer.Camera.Shake(2, 0.25f);
}
public void SetAnimations(bool isEnabled)
{
foreach (var animKey in _animPlayer.GetAnimationList())
{
var anim = _animPlayer.GetAnimation(animKey);
for (int i = 0; i < anim.GetTrackCount(); i++)
{
foreach (var nodePath in VisibleOnToggle)
{
if (anim.TrackGetPath(i) == nodePath)
{
GD.Print($"Disabled anim for {nodePath}");
anim.TrackSetEnabled(i, isEnabled);
}
}
}
}
}
}

View File

@ -12,7 +12,7 @@ public partial class DynamicDoorSwitch : StaticBody2D
public string MapStateKey { get; set; }
[Export]
public Godot.Collections.Array<Node2D> VisibleOnToggle { get; set; } = new();
public Godot.Collections.Array<NodePath> VisibleOnToggle { get; set; } = new();
private AnimationPlayer _animPlayer;
@ -22,18 +22,16 @@ public partial class DynamicDoorSwitch : StaticBody2D
var globalState = this.GetGlobalState();
var doorState = globalState.MapState[MapStateKey];
if (!doorState.Equals(default))
{
if (!(bool)doorState)
{
InteractionTrigger.Interaction += OnInteraction;
}
}
InteractionTrigger.Interaction += OnInteraction;
// disable specific animations when entering initial state
SetAnimations(false);
RefreshMapState(doorState);
}
private void RefreshMapState(Variant value)
{
if (value.Equals(default))
if (value.Equals(default) || value.VariantType != Variant.Type.Bool)
{
return;
}
@ -51,14 +49,38 @@ public partial class DynamicDoorSwitch : StaticBody2D
public void OnInteraction()
{
var globalState = this.GetGlobalState();
globalState.MapState[MapStateKey] = true;
RefreshMapState(true);
GD.Print($"{MapStateKey} is now on");
foreach (Node2D node in VisibleOnToggle)
Variant oldState = globalState.MapState[MapStateKey];
bool newState;
globalState.MapState[MapStateKey] = newState = oldState.VariantType switch
{
node.Visible = true;
Variant.Type.Bool => !(bool)oldState,
_ => true,
};
SetAnimations(true);
RefreshMapState(newState);
GD.Print($"Map state \"{MapStateKey}\" {oldState} -> {newState}");
//var a = _animPlayer.CurrentAnimation;
//GetNode<Node2D>(nodePath).SetProcess(true);
}
public void SetAnimations(bool isEnabled)
{
foreach (var animKey in _animPlayer.GetAnimationList())
{
var anim = _animPlayer.GetAnimation(animKey);
for (int i = 0; i < anim.GetTrackCount(); i++)
{
foreach (var nodePath in VisibleOnToggle)
{
if (anim.TrackGetPath(i) == nodePath)
{
GD.Print($"Disabled anim for {nodePath}");
anim.TrackSetEnabled(i, isEnabled);
}
}
}
}
}
}

View File

@ -0,0 +1,10 @@
namespace SupaLidlGame.Events;
public partial class HealthChangedArgs : Args
{
public float OldHealth { get; set; }
public float NewHealth { get; set; }
public float Damage { get; set; }
}

View File

@ -1,14 +1,8 @@
namespace SupaLidlGame.Events;
public partial class HealthChangedArgs : Args
public partial class HurtArgs : HealthChangedArgs
{
public Characters.Character Attacker { get; set; }
public Items.Weapon Weapon { get; set; }
public float OldHealth { get; set; }
public float NewHealth { get; set; }
public float Damage { get; set; }
}

View File

@ -1,8 +1,10 @@
[gd_scene load_steps=14 format=3 uid="uid://c5i3cq1kewljv"]
[gd_scene load_steps=18 format=3 uid="uid://c5i3cq1kewljv"]
[ext_resource type="Script" path="res://Entities/DynamicDoor.cs" id="1_t6mj1"]
[ext_resource type="Texture2D" uid="uid://crddevohy65we" path="res://Assets/Sprites/Props/arena-gate.png" id="2_kvtjv"]
[ext_resource type="Texture2D" uid="uid://c1a7lvb4uuwfy" path="res://Assets/Sprites/Particles/circle-16.png" id="3_i2hr3"]
[ext_resource type="AudioStream" uid="uid://ciyi5ectbbscr" path="res://Assets/Sounds/rock-impact.wav" id="4_n2fp8"]
[ext_resource type="AudioStream" uid="uid://dajb6kb64otkq" path="res://Assets/Sounds/cinder-block-move.wav" id="5_xjq01"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_yrdxe"]
size = Vector2(32, 25)
@ -62,6 +64,18 @@ tracks/1/keys = {
"update": 1,
"values": [false]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("GPUParticles2D:emitting")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
[sub_resource type="Animation" id="Animation_unbll"]
resource_name = "close"
@ -89,6 +103,33 @@ tracks/1/keys = {
"update": 1,
"values": [false]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("GPUParticles2D:emitting")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
tracks/3/type = "audio"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("AudioStreamPlayer2D")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"clips": [{
"end_offset": 0.0,
"start_offset": 0.0,
"stream": ExtResource("4_n2fp8")
}],
"times": PackedFloat32Array(0)
}
tracks/3/use_blend = true
[sub_resource type="Animation" id="Animation_1nbqx"]
resource_name = "open"
@ -116,18 +157,90 @@ tracks/1/keys = {
"update": 1,
"values": [true]
}
tracks/2/type = "audio"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("AudioStreamPlayer2D")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"clips": [{
"end_offset": 0.0,
"start_offset": 0.0,
"stream": ExtResource("5_xjq01")
}],
"times": PackedFloat32Array(0)
}
tracks/2/use_blend = true
[sub_resource type="Animation" id="Animation_s143f"]
resource_name = "open_init"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Gate:frame")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [1]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("CollisionShape2D:disabled")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
[sub_resource type="Animation" id="Animation_5kkkm"]
resource_name = "close_init"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Gate:frame")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("CollisionShape2D:disabled")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_3eva7"]
_data = {
"RESET": SubResource("Animation_ylaba"),
"close": SubResource("Animation_unbll"),
"open": SubResource("Animation_1nbqx")
"close_init": SubResource("Animation_5kkkm"),
"open": SubResource("Animation_1nbqx"),
"open_init": SubResource("Animation_s143f")
}
[node name="ArenaDoor" type="StaticBody2D"]
collision_mask = 0
script = ExtResource("1_t6mj1")
MapStateKey = "b_arena_depths_door_1"
VisibleOnToggle = [NodePath("GPUParticles2D"), NodePath("AudioStreamPlayer2D")]
[node name="Gate" type="Sprite2D" parent="."]
texture = ExtResource("2_kvtjv")
@ -151,3 +264,6 @@ libraries = {
}
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."]
max_distance = 512.0
attenuation = 8.0
bus = &"Effects"

View File

@ -96,6 +96,7 @@ _data = {
[node name="ArenaDoorSwitch" type="StaticBody2D" node_paths=PackedStringArray("InteractionTrigger")]
script = ExtResource("1_78qab")
InteractionTrigger = NodePath("InteractionTrigger")
VisibleOnToggle = [null]
[node name="InteractionTrigger" parent="." instance=ExtResource("2_7j8p7")]

View File

@ -21,15 +21,16 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
stretch = true
stretch_shrink = 2
stretch_shrink = 3
[node name="UIViewport" type="SubViewport" parent="CanvasLayer/SubViewportContainer"]
transparent_bg = true
handle_input_locally = false
size = Vector2i(960, 540)
size = Vector2i(640, 360)
render_target_update_mode = 4
[node name="DialogBalloon" parent="CanvasLayer/SubViewportContainer/UIViewport" instance=ExtResource("6_2bdwl")]
layer = 2
[node name="CanvasLayer" type="CanvasLayer" parent="CanvasLayer/SubViewportContainer/UIViewport"]

View File

@ -11,7 +11,7 @@
[ext_resource type="PackedScene" uid="uid://dldnp8eunxj3q" path="res://BoundingBoxes/InteractionTrigger.tscn" id="9_3401j"]
[ext_resource type="Script" path="res://BoundingBoxes/ConnectorBox.cs" id="9_fg062"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_o5jr8"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_4fu3e"]
resource_local_to_scene = true
shader = ExtResource("5_h8k5p")
shader_parameter/color = Vector4(1, 1, 1, 1)
@ -113,7 +113,7 @@ visible = false
position = Vector2(120, -112)
[node name="Doc" parent="Entities" index="0" instance=ExtResource("4_ej0f3")]
material = SubResource("ShaderMaterial_o5jr8")
material = SubResource("ShaderMaterial_4fu3e")
[node name="PointLight2D" type="PointLight2D" parent="Entities" index="1"]
position = Vector2(168, -42)

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=42 format=3 uid="uid://c72uqjjtxpi3g"]
[gd_scene load_steps=46 format=3 uid="uid://c72uqjjtxpi3g"]
[ext_resource type="PackedScene" uid="uid://clwv2owvk6abe" path="res://Scenes/BaseMap.tscn" id="1_ci4ij"]
[ext_resource type="TileSet" uid="uid://l61kbx31ug4p" path="res://Scenes/Maps/ArenaTileset.tres" id="2_m6h7j"]
@ -9,8 +9,11 @@
[ext_resource type="PackedScene" uid="uid://c1w7t6irnohfx" path="res://Entities/Torch.tscn" id="6_wx4iy"]
[ext_resource type="PackedScene" uid="uid://ceadk7pam7vab" path="res://Entities/TorchLamp.tscn" id="8_3rpj6"]
[ext_resource type="PackedScene" uid="uid://hm3ek8vqt1d4" path="res://Props/Bookshelf.tscn" id="9_qsthf"]
[ext_resource type="Script" path="res://Utils/InteractionTriggerDialogue.cs" id="10_apvp5"]
[ext_resource type="Texture2D" uid="uid://dcwerlcsr0nj0" path="res://Assets/Sprites/Props/tree-dark-3.png" id="10_i5qot"]
[ext_resource type="PackedScene" uid="uid://dldnp8eunxj3q" path="res://BoundingBoxes/InteractionTrigger.tscn" id="10_scl41"]
[ext_resource type="Texture2D" uid="uid://dl2q3k0w8e4fp" path="res://Assets/Sprites/Props/tree-dark.png" id="10_sx2hw"]
[ext_resource type="Resource" uid="uid://dilmuoilweoeh" path="res://Assets/Dialogue/books.dialogue" id="11_fpxpb"]
[ext_resource type="Texture2D" uid="uid://jhqfflef5yde" path="res://Assets/Sprites/Props/tree-dark-2.png" id="11_wjfe8"]
[ext_resource type="Texture2D" uid="uid://ds0xg848g3jid" path="res://Assets/Sprites/Props/tree-dark-5.png" id="12_md5xh"]
[ext_resource type="Texture2D" uid="uid://8rbstele3ckq" path="res://Assets/Sprites/Props/tree-dark-4.png" id="13_j84wc"]
@ -22,6 +25,7 @@
[ext_resource type="PackedScene" uid="uid://bou5pccj0147e" path="res://Entities/ArenaEntrance.tscn" id="20_tcd0x"]
[ext_resource type="PackedScene" uid="uid://c5i3cq1kewljv" path="res://Props/ArenaDoor.tscn" id="21_nvrif"]
[ext_resource type="PackedScene" uid="uid://bs6xuujve2tsf" path="res://Props/ArenaDoorSwitch.tscn" id="22_ay852"]
[ext_resource type="PackedScene" uid="uid://dhl071rj5wyvx" path="res://Entities/Campfire.tscn" id="23_mykt8"]
[sub_resource type="Gradient" id="Gradient_jupxw"]
offsets = PackedFloat32Array(0.00740741, 0.2, 1)
@ -346,8 +350,21 @@ texture = SubResource("CanvasTexture_1u3bg")
texture = ExtResource("3_lgup0")
texture_scale = 0.15
[node name="StaticBookshelf" parent="Props" index="13" instance=ExtResource("9_qsthf")]
[node name="StaticBookshelf" parent="Props" index="13" node_paths=PackedStringArray("InteractionTrigger") instance=ExtResource("9_qsthf")]
position = Vector2(64, 456)
script = ExtResource("10_apvp5")
InteractionTrigger = NodePath("InteractionTrigger")
DialogueResource = ExtResource("11_fpxpb")
DialogueTitle = "unwanted_frequencies"
[node name="InteractionTrigger" parent="Props/StaticBookshelf" index="6" instance=ExtResource("10_scl41")]
[node name="Popup" parent="Props/StaticBookshelf/InteractionTrigger" index="1"]
offset_top = -57.0
offset_bottom = -27.0
[node name="Label" parent="Props/StaticBookshelf/InteractionTrigger/Popup" index="0"]
text = "Read"
[node name="StaticBookshelf2" parent="Props" index="14" instance=ExtResource("9_qsthf")]
position = Vector2(96, 456)
@ -580,6 +597,13 @@ MapStateKey = "b_arena_depths_2"
position = Vector2(24, 332)
MapStateKey = "b_arena_depths_2"
[node name="Campfire" parent="Entities" index="5" instance=ExtResource("23_mykt8")]
position = Vector2(256, -9)
[node name="Campfire2" parent="Entities" index="6" instance=ExtResource("23_mykt8")]
position = Vector2(80, 373)
[editable path="Props/StaticBookshelf"]
[editable path="Props/StaticBookshelf/InteractionTrigger"]
[editable path="Props/StaticBookshelf2"]
[editable path="Props/StaticBookshelf3"]

View File

@ -40,8 +40,8 @@ public abstract partial class PlayerState : CharacterState
public override CharacterState Process(double delta)
{
Character.Direction = Godot.Input.GetVector("ui_left", "ui_right",
"ui_up", "ui_down");
Character.Direction = Godot.Input.GetVector("left", "right",
"up", "down");
Character.LookTowardsDirection();
Vector2 mousePos = Character.GetGlobalMousePosition();

View File

@ -4,14 +4,17 @@ namespace SupaLidlGame.State.Global;
public partial class GlobalState : Node
{
//public Utils.World World { get; set; }
[Export]
public Progression Progression { get; set; } = new();
[Export]
public MapState MapState { get; set; } = new();
[Export]
public Stats Stats { get; set; } = new();
[Signal]
public delegate void SummonBossEventHandler(string bossName);
public void Print(string str) => GD.Print(str);
}

View File

@ -0,0 +1,15 @@
using Godot;
namespace SupaLidlGame.State.Global;
public partial class Stats : Resource
{
[Export]
public Vector2 SaveLocation { get; set; }
[Export]
public string SaveMapKey { get; set; }
[Export]
public int DeathCount { get; set; } = 0;
}

View File

@ -3,8 +3,8 @@ using System.Collections.Generic;
public class CacheStore<TKey, TVal>
{
// default TTL is 5 mins
public ulong TimeToLive { get; } = 3000;
// default TTL is 1 min
public ulong TimeToLive { get; } = 60000;
private Dictionary<TKey, CacheItem<TVal>> _store = new();

View File

@ -64,8 +64,6 @@ public partial class World : Node
private string _currentMapResourcePath;
//private Entities.Campfire _lastCampfire = null;
public Vector2 SaveLocation { get; set; }
public string SaveMapKey { get; set; }
private const string PLAYER_PATH = "res://Characters/Player.tscn";
private PackedScene _playerScene;
@ -96,10 +94,12 @@ public partial class World : Node
// create a player (currently unparented)
CreatePlayer();
// TODO: create start menu and load game from there
LoadGame();
EventBus = this.GetEventBus();
EventBus.RequestMoveToArea += (Events.RequestAreaArgs args) =>
{
GD.Print("request move to area");
MoveToArea(args.Area, args.Connector);
};
@ -110,8 +110,16 @@ public partial class World : Node
{
CurrentBoss = boss;
UIController.BossBar.Boss = boss;
MusicPlayer.Stream = boss.Music;
MusicPlayer.Play();
MusicPlayer.Stream = boss?.Music;
// TODO: use an audio manager
if (MusicPlayer.Stream is null)
{
MusicPlayer.Stop();
}
else
{
MusicPlayer.Play();
}
}
public void DeregisterBoss(Boss boss)
@ -207,7 +215,7 @@ public partial class World : Node
{
CurrentPlayer = _playerScene.Instantiate<Player>();
CurrentPlayer.Death += (Events.HealthChangedArgs args) =>
CurrentPlayer.Death += (Events.HurtArgs args) =>
{
// TODO: respawn the player at the last campfire.
GetTree().CreateTimer(3).Timeout += () =>
@ -216,7 +224,7 @@ public partial class World : Node
};
};
CurrentPlayer.Hurt += (Events.HealthChangedArgs args) =>
CurrentPlayer.Hurt += (Events.HurtArgs args) =>
{
// TODO: move this to UI controller and add a setup method
var bar = UIController.GetNode<UI.HealthBar>("Top/Margin/HealthBar");
@ -251,11 +259,11 @@ public partial class World : Node
public void MoveToArea(string path, string connector)
{
_currentConnector = connector;
if (path != _currentMapResourcePath)
GD.Print($"Moving to area {path} - {connector}");
if (path != CurrentMap.SceneFilePath)
{
var scene = ResourceLoader.Load<PackedScene>(path);
LoadScene(path);
_currentMapResourcePath = path;
//_currentMapResourcePath = path;
}
// after finished loading, move our player to the connector
@ -272,22 +280,26 @@ public partial class World : Node
public void SaveGame()
{
ResourceSaver.Save(GlobalState.Progression, "user://progression.save");
ResourceSaver.Save(GlobalState.MapState, "user://map-state.save");
throw new System.NotImplementedException();
SetSpawn(CurrentPlayer.GlobalPosition);
// TODO: create a single save resource file
ResourceSaver.Save(GlobalState.Progression, "user://progression.tres");
ResourceSaver.Save(GlobalState.MapState, "user://map-state.tres");
ResourceSaver.Save(GlobalState.Stats, "user://stats.tres");
}
public void LoadGame()
{
var prog = ResourceLoader.Load<Progression>("user://progression.save");
var mapState = ResourceLoader.Load<MapState>("user://map-state.save");
GlobalState.Progression = prog;
GlobalState.MapState = mapState;
var prog = ResourceLoader.Load<Progression>("user://progression.tres");
var mapState = ResourceLoader.Load<MapState>("user://map-state.tres");
var stats = ResourceLoader.Load<Stats>("user://stats.tres");
GlobalState.Progression = prog ?? new Progression();
GlobalState.MapState = mapState ?? new MapState();
GlobalState.Stats = stats ?? new Stats();
// load the player scene
// TODO: implement
throw new System.NotImplementedException();
}
/// <summary>
@ -304,9 +316,9 @@ public partial class World : Node
if (mapKey is null)
{
mapKey = CurrentMap.SceneFilePath;
SaveLocation = position;
SaveMapKey = mapKey;
}
GlobalState.Stats.SaveLocation = position;
GlobalState.Stats.SaveMapKey = mapKey;
}
public void SpawnPlayer()
@ -314,11 +326,11 @@ public partial class World : Node
// TODO: add max health property
//CurrentPlayer.Health = 100;
//CurrentPlayer.Sprite.Visible = true;
if (CurrentMap.SceneFilePath != SaveMapKey)
if (CurrentMap.SceneFilePath != GlobalState.Stats.SaveMapKey)
{
LoadScene(SaveMapKey);
LoadScene(GlobalState.Stats.SaveMapKey);
}
CurrentPlayer.GlobalPosition = SaveLocation;
CurrentPlayer.GlobalPosition = GlobalState.Stats.SaveLocation;
CurrentPlayer.Spawn();
}

View File

@ -17,12 +17,12 @@ config/icon="res://icon.svg"
[autoload]
DebugConsole="*res://Debug/DebugConsole.cs"
DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd"
GlobalState="*res://State/Global/GlobalState.cs"
EventBus="*res://Events/EventBus.cs"
Panku="*res://addons/panku_console/console.tscn"
World="*res://Scenes/Level.tscn"
DebugConsole="*res://Debug/DebugConsole.cs"
Panku="*res://addons/panku_console/console.tscn"
[dialogue_manager]
@ -46,27 +46,33 @@ enabled=PackedStringArray("res://addons/dialogue_manager/plugin.cfg", "res://add
[input]
ui_accept={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":4194309,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":4194310,"echo":false,"script":null)
]
}
ui_left={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":65,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null)
]
}
ui_right={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":68,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null)
]
}
ui_up={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":87,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":11,"pressure":0.0,"pressed":false,"script":null)
]
}
ui_down={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null)
]
}
@ -111,10 +117,34 @@ emote={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":71,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
up={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null)
]
}
down={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null)
]
}
left={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null)
]
}
right={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null)
]
}
[internationalization]
locale/translations_pot_files=PackedStringArray("res://Assets/Dialog/doc.dialogue", "res://Assets/Dialogue/doc.dialogue", "res://Assets/Dialogue/spellbook.dialogue", "res://Assets/Dialogue/books.dialogue")
locale/translations_pot_files=PackedStringArray("res://Assets/Dialog/doc.dialogue", "res://Assets/Dialogue/doc.dialogue", "res://Assets/Dialogue/spellbook.dialogue", "res://Assets/Dialogue/books.dialogue", "res://Assets/Dialogue/clone-machine.dialogue")
[layer_names]