item, inventory, and interaction system
This commit is contained in:
parent
60e50e902f
commit
6df47a294b
7 changed files with 166 additions and 4 deletions
|
@ -1,8 +1,13 @@
|
||||||
import { Area3D } from 'godot'
|
import { Area3D, Signal1 } from 'godot'
|
||||||
import Interactor from './interactor'
|
import Interactor from './interactor'
|
||||||
|
import { signal } from 'godot.annotations'
|
||||||
|
|
||||||
export default class Interactable extends Area3D {
|
export default class Interactable extends Area3D {
|
||||||
|
@signal()
|
||||||
|
readonly interacted!: Signal1<Interactor>
|
||||||
|
|
||||||
interact(interactor: Interactor) {
|
interact(interactor: Interactor) {
|
||||||
console.log('awawawawawa', interactor)
|
this.interacted.emit(interactor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
30
src/interactable/door.ts
Normal file
30
src/interactable/door.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { Node3D, Variant } from 'godot'
|
||||||
|
import { export_ } from 'godot.annotations'
|
||||||
|
import Interactor from '../interactor'
|
||||||
|
import ItemData from '../item_data'
|
||||||
|
import Inventory from '../inventory'
|
||||||
|
|
||||||
|
export default class Door extends Node3D {
|
||||||
|
@export_(Variant.Type.TYPE_BOOL)
|
||||||
|
readonly locked: boolean = false
|
||||||
|
|
||||||
|
@export_(Variant.Type.TYPE_BOOL)
|
||||||
|
readonly requires_key: boolean = false
|
||||||
|
|
||||||
|
@export_(Variant.Type.TYPE_OBJECT, { class_: ItemData })
|
||||||
|
readonly key_item?: ItemData
|
||||||
|
|
||||||
|
open(interactor: Interactor): void {
|
||||||
|
console.log('trying to open...')
|
||||||
|
|
||||||
|
if (this.locked && this.requires_key) {
|
||||||
|
console.log('this door requires a key')
|
||||||
|
const inventory = Inventory.find_inventory(interactor.root_node)
|
||||||
|
if (inventory != null && this.key_item != null && inventory.has(this.key_item)) {
|
||||||
|
console.log('interactor has a', this.key_item.name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('this door is opened some other way')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import { Area3D, Callable } from 'godot'
|
import { Area3D, Callable, Node, NodePath, Variant } from 'godot'
|
||||||
import Interactable from './interactable'
|
import Interactable from './interactable'
|
||||||
|
import { export_ } from 'godot.annotations'
|
||||||
|
|
||||||
class InteractableDistance {
|
class InteractableDistance {
|
||||||
static Empty = new InteractableDistance(Infinity)
|
static Empty = new InteractableDistance(Infinity)
|
||||||
|
@ -14,9 +15,19 @@ class InteractableDistance {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Interactor extends Area3D {
|
export default class Interactor extends Area3D {
|
||||||
|
@export_(Variant.Type.TYPE_NODE_PATH)
|
||||||
|
private _root_node: NodePath = new NodePath('.')
|
||||||
|
private _root!: Node
|
||||||
|
|
||||||
|
get root_node() {
|
||||||
|
return this._root
|
||||||
|
}
|
||||||
|
|
||||||
_interactables: Array<Interactable> = []
|
_interactables: Array<Interactable> = []
|
||||||
|
|
||||||
_ready(): void {
|
_ready(): void {
|
||||||
|
this._root = this.get_node(this._root_node)
|
||||||
|
|
||||||
this.area_entered.connect(Callable.create(
|
this.area_entered.connect(Callable.create(
|
||||||
this,
|
this,
|
||||||
this._on_area_entered
|
this._on_area_entered
|
||||||
|
|
69
src/inventory.ts
Normal file
69
src/inventory.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import { clampi, Node, StringName } from 'godot'
|
||||||
|
import ItemData from './item_data'
|
||||||
|
import { GArrayEnumerator } from './collection/enumerable'
|
||||||
|
import { Enumerable } from '../addons/enumerable-ts/src/index'
|
||||||
|
|
||||||
|
class ItemInstance {
|
||||||
|
readonly resource: ItemData
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.resource.name
|
||||||
|
}
|
||||||
|
|
||||||
|
get type() {
|
||||||
|
return this.resource.type
|
||||||
|
}
|
||||||
|
|
||||||
|
get max_quantity() {
|
||||||
|
return this.resource.max_quantity
|
||||||
|
}
|
||||||
|
|
||||||
|
quantity: number
|
||||||
|
|
||||||
|
constructor(resource: ItemData, quantity: number = 1) {
|
||||||
|
this.resource = resource
|
||||||
|
this.quantity = quantity
|
||||||
|
}
|
||||||
|
|
||||||
|
set_quantity(quantity: number = 1) {
|
||||||
|
this.quantity = clampi(quantity, 0, this.max_quantity)
|
||||||
|
}
|
||||||
|
|
||||||
|
increase_quantity(quantity: number = 1) {
|
||||||
|
this.set_quantity(this.quantity + quantity)
|
||||||
|
}
|
||||||
|
|
||||||
|
decrease_quantity(quantity: number = 1) {
|
||||||
|
this.set_quantity(this.quantity - quantity)
|
||||||
|
}
|
||||||
|
|
||||||
|
has_none(): boolean {
|
||||||
|
return this.quantity <= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Inventory extends Node {
|
||||||
|
items: Map<StringName, ItemInstance> = new Map()
|
||||||
|
|
||||||
|
static find_inventory(root: Node): Inventory | null | undefined {
|
||||||
|
const child_enumerator: GArrayEnumerator<Node> = new GArrayEnumerator(root.get_children())
|
||||||
|
const children = Enumerable.from(child_enumerator)
|
||||||
|
return children.find(child => child instanceof Inventory) as Inventory
|
||||||
|
}
|
||||||
|
|
||||||
|
add(item: ItemData, quantity: number = 1) {
|
||||||
|
const existing_item = this.items.get(item.name)
|
||||||
|
|
||||||
|
if (!existing_item) {
|
||||||
|
this.items.set(item.name, new ItemInstance(item, quantity))
|
||||||
|
} else {
|
||||||
|
existing_item.increase_quantity(quantity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
has(item: ItemData): boolean {
|
||||||
|
console.log('checking for', item.name)
|
||||||
|
return this.items.has(item.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
22
src/item_data.ts
Normal file
22
src/item_data.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Resource, StringName, Variant } from 'godot'
|
||||||
|
import { export_, export_enum, export_multiline } from 'godot.annotations'
|
||||||
|
|
||||||
|
enum ItemType {
|
||||||
|
None,
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ItemData extends Resource {
|
||||||
|
@export_(Variant.Type.TYPE_STRING_NAME)
|
||||||
|
readonly name: StringName = ''
|
||||||
|
|
||||||
|
@export_enum(ItemType)
|
||||||
|
readonly type: ItemType = ItemType.None
|
||||||
|
|
||||||
|
@export_multiline()
|
||||||
|
readonly description: string = ''
|
||||||
|
|
||||||
|
@export_(Variant.Type.TYPE_INT)
|
||||||
|
readonly max_quantity: number = 99
|
||||||
|
}
|
||||||
|
|
26
src/item_pickup.ts
Normal file
26
src/item_pickup.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { Node, Variant } from 'godot'
|
||||||
|
import { export_, export_range } from 'godot.annotations'
|
||||||
|
import ItemData from './item_data'
|
||||||
|
import Interactor from './interactor'
|
||||||
|
import Inventory from './inventory'
|
||||||
|
|
||||||
|
export default class ItemPickup extends Node {
|
||||||
|
@export_(Variant.Type.TYPE_OBJECT, { class_: ItemData })
|
||||||
|
readonly resource!: ItemData
|
||||||
|
|
||||||
|
@export_range(0, 99)
|
||||||
|
readonly quantity: number = 1
|
||||||
|
|
||||||
|
add_to_inventory(interactor: Interactor) {
|
||||||
|
const inventory = Inventory.find_inventory(interactor.root_node)
|
||||||
|
|
||||||
|
if (inventory) {
|
||||||
|
inventory.add(this.resource, this.quantity)
|
||||||
|
} else {
|
||||||
|
console.error(interactor.root_node.get_name(), 'has no inventory')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.queue_free()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ export default class Player extends CharacterBody3D {
|
||||||
turn_speed = 1
|
turn_speed = 1
|
||||||
_rotation_speed = 2 * Math.PI
|
_rotation_speed = 2 * Math.PI
|
||||||
|
|
||||||
|
|
||||||
is_running() {
|
is_running() {
|
||||||
return this.player_input.is_running && !this.velocity.is_zero_approx()
|
return this.player_input.is_running && !this.velocity.is_zero_approx()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue