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 { signal } from 'godot.annotations'
|
||||
|
||||
export default class Interactable extends Area3D {
|
||||
@signal()
|
||||
readonly interacted!: Signal1<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 { export_ } from 'godot.annotations'
|
||||
|
||||
class InteractableDistance {
|
||||
static Empty = new InteractableDistance(Infinity)
|
||||
|
@ -14,9 +15,19 @@ class InteractableDistance {
|
|||
}
|
||||
|
||||
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> = []
|
||||
|
||||
_ready(): void {
|
||||
this._root = this.get_node(this._root_node)
|
||||
|
||||
this.area_entered.connect(Callable.create(
|
||||
this,
|
||||
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
|
||||
_rotation_speed = 2 * Math.PI
|
||||
|
||||
|
||||
is_running() {
|
||||
return this.player_input.is_running && !this.velocity.is_zero_approx()
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue