simnpcs2

package
v0.0.0-...-ab92d4e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 8, 2024 License: Apache-2.0 Imports: 12 Imported by: 0

README

Simnpcs2

This package is a skeleton for a rebuilt version of the simnpcs and gamecs package. It is a work in progress, so ignore this for now.

alt text

TODO

  • Add noise for obstacle generation
  • Add AI
    • Add needs and remediaion (food, water, sleep, etc.)
    • Add combat / interactions
    • Add pathfinding
  • Add inventory
  • Add events
    • Add event system
      • Damage events
      • Interactions
      • Item pickup
    • Add event logging
    • Add quadtree for event detection
  • Exports
    • Unify rendering
    • Draw list of agents and their needs
    • Add GIF export
      • Make it optional
    • Add WEBP export
      • Make it optional
      • Prevent cgo crashes on exit

Brainstorming

This section just contains random thoughts and ideas.

AI

The AI should be able to do the following:

  • Recognize any needs (based on Maslow's hierarchy of needs)
  • Perform simple actions like:
    • Walk to a certain point
    • Perceive the environment
    • Find items (of specific types)
    • Pick up items
    • Consume items
    • Interact with other NPCs
  • Can chain certain actions together to complete tasks (e.g. hungry? -> find food -> eat -> resume previous task)
  • Can be interrupted by other NPCs
  • Can be interrupted by events (e.g. other NPCs, items, etc.)
  • Tasks can be sorted by priority (and feasibility)
  • Tasks can be interrupted and resumed (or completely restarted?)
  • Memory
    • Remember positive experiences (e.g. finding food)
    • Remember negative experiences (e.g. being attacked by another NPC)
    • Remember locations (e.g. where food was found)
Needs

Needs are a way to determine what an NPC should do. They are based on Maslow's hierarchy of needs. The needs are:

  • Physiological (e.g. food, water, sleep)
    • Basic mockup
    • Full implementation
  • Safety (e.g. shelter, protection)
    • Attack other NPCs
    • Run away from other NPCs
  • Belongingness (e.g. friends, family)
  • Esteem (e.g. respect, recognition)
  • Self-actualization (e.g. personal growth, fulfillment)
Perception

Perception is a way to determine what an NPC can see. It should be able to do the following:

  • See other NPCs
  • See items
  • See obstacles
  • See the environment (e.g. water, trees, etc.)
Interactions

Interactions are a way for NPCs to interact with each other. They should be able to do the following:

  • Talk to each other
  • Trade items
  • Fight each other
Inventory

The inventory is a way for NPCs to store items. It should be able to do the following:

  • Store items
  • Remove items
  • Check if an item is in the inventory
Items

Items represent objects in the environment that can be moved, picked up, and, depending on the type, consumed. They should be able to do the following:

  • Be picked up
  • Be consumed
  • Be dropped
  • Be moved
  • Be stored in an inventory
Obstacles

Obstacles are objects in the environment that NPCs cannot move through. They should be able to do the following:

  • Be generated based on a noise function / heightmap
Tasks

Tasks are a way to determine what an NPC should do. They should be able to do the following:

  • Be sorted by priority
  • Be sorted by feasibility
  • Be interrupted and resumed (or completely restarted?)
Insight

Insight helps npcs to glean what an npc is doing. For example, if a npc is chasing another npc, the npc being chased will know that it is being chased. The higher the insight, the more information the npc can glean, like for example subsequent actions.

For this, we should first implement the planning/task system including chained tasks. Then we can implement insight, which will reveal the current task of the npc (like the current state in the state machine or the current node in the behavior tree).

Documentation

Index

Constants

View Source
const (
	HungerPeckish   = 20
	HungerHungry    = 40
	HungerStarving  = 60
	HungerStarved   = 100
	HungerPerSecond = 1.0
)
View Source
const (
	ExhaustionTired     = 10
	ExhaustionExhausted = 40
	ExhaustionDead      = 60
	ExhaustionPerSecond = 1.0
)

Variables

View Source
var (
	ErrNoPathFound = errors.New("no path found")
	ErrNoNodeFound = errors.New("no node found")
)
View Source
var ErrGridSize = errors.New("grid size must be at least 2x2")

Functions

This section is empty.

Types

type AI

type AI struct {
	World       *World        // underlying world
	Being       *Being        // underlying being
	Perception  *Perception   // perception of the world
	Pathfinding *Pathfinding  // steering behaviors
	Needs       *Needs        // basic needs
	Destination *vectors.Vec2 // current destination
	Home        vectors.Vec2  // home position
}

func NewAI

func NewAI(being *Being) *AI

NewAI returns a new AI.

func (*AI) Dead

func (a *AI) Dead() bool

Dead returns true if the being this AI is controlling is dead.

func (*AI) ID

func (a *AI) ID() int64

ID returns the ID of the being this AI is controlling.

func (*AI) Notify

func (a *AI) Notify(event *Event)

Notify notifies the AI of an event.

func (*AI) Pos

func (a *AI) Pos() vectors.Vec2

Pos returns the position of the being this AI is controlling.

func (*AI) String

func (a *AI) String() string

String returns a string representation of the AI.

func (*AI) TakeDamage

func (a *AI) TakeDamage(damage float64, attacker Entity)

TakeDamage registers incoming damage from an attacker. TODO: Find a better way to do this. Maybe via an event system?

func (*AI) Type

func (a *AI) Type() EntityType

Type returns the type of the being this AI is controlling.

func (*AI) Update

func (a *AI) Update(delta float64)

Update updates the state of the AI and its underlying components.

type Being

type Being struct {
	*CompMoveable  // Position and speed.
	*CompStats     // Stats.
	*EventListener // Event listener.
	World          *World
	// contains filtered or unexported fields
}

Being represents a being in the world.

func NewBeing

func NewBeing(world *World) *Being

func (*Being) Dead

func (b *Being) Dead() bool

Dead returns true if the being is dead.

func (*Being) ID

func (b *Being) ID() int64

ID returns the ID of the being.

func (*Being) InMeleeRange

func (b *Being) InMeleeRange(target Entity) bool

InMeleeRange returns true if the being is in melee range of the target.

func (*Being) Pos

func (b *Being) Pos() vectors.Vec2

Pos returns the position of the being.

func (*Being) String

func (b *Being) String() string

String returns a string representation of the being.

func (*Being) TakeDamage

func (b *Being) TakeDamage(damage float64, attacker Entity)

TakeDamage reduces the health of the being. TODO: Find a better way to do this. Maybe via an event system?

func (*Being) Type

func (b *Being) Type() EntityType

Type returns the type of the being.

func (*Being) Update

func (b *Being) Update(delta float64)

Update updates the state of the Being.

type CompMoveable

type CompMoveable struct {
	Pos   vectors.Vec2
	Speed vectors.Vec2
}

CompMoveable is a movable component.

func (*CompMoveable) Update

func (c *CompMoveable) Update(delta float64)

Update moves the position in the component by the speed within the given time.

type CompStats

type CompStats struct {
	Health         float64 // Current health.
	HealthMax      float64 // Maximum health.
	StarvationRate float64 // How fast we starve.
	Starvation     float64 // Current level of starvation.
	ExhaustionRate float64 // How fast we get tired.
	Exhaustion     float64 // Current level of exhaustion.
}

CompStats is a stats component.

func (*CompStats) String

func (c *CompStats) String() string

String returns a string representation of the stats.

func (*CompStats) Update

func (c *CompStats) Update(delta float64)

Update updates the state of the stats.

type Config

type Config struct {
	GridWidth, GridHeight int
	InvalidNodes          []Node
	WeightedNodes         []Node
}

Config holds important settings to perform the calculation

GridWidth and GridHeight are required and represents the size of the grid

InvalidNodes can be used to add not accessible nodes like obstacles etc. WeightedNodes can be used to add nodes to be avoided like mud or mountains

type Entity

type Entity interface {
	ID() int64                                  // ID returns the ID of the entity.
	String() string                             // String returns a string representation of the entity.
	Type() EntityType                           // Type returns the type of the entity.
	Pos() vectors.Vec2                          // Pos returns the momentary position of the entity.
	Update(delta float64)                       // Update updates the state of the entity.
	Dead() bool                                 // Dead returns true if the entity is dead.
	TakeDamage(damage float64, attacker Entity) // TakeDamage applies damage to the entity.
}

type EntityType

type EntityType int
const (
	EntityTypeBeing EntityType = iota
	EntityTypeItem
	EntityTypeObstacle
)

type Event

type Event struct {
	EventType EventType
	Source    Entity
	Target    Entity
	Data      interface{}
}

Event represents an event that happened in the world.

type EventAttackData

type EventAttackData struct {
	Damage float64
}

EventAttackData represents the data for an EventAttack event.

type EventListener

type EventListener struct {
	Events []*Event
}

EventListener is an event listener that can be added to an Entity.

func (*EventListener) FindType

func (l *EventListener) FindType(eventType EventType) *Event

FindType finds an event of a specific type. This is a temporary solution until we have a better event system.

func (*EventListener) Notify

func (l *EventListener) Notify(event *Event)

Notify notifies the listener of an event.

func (*EventListener) Update

func (l *EventListener) Update(delta float64)

Update updates the EventListener system.

type EventType

type EventType int

EventType represents the type of an event.

const (
	// EventAttack is an event that is triggered when an attack is made.
	EventAttack EventType = iota
)

type Events

type Events struct {
	Events []*Event // Recent events (will be cleared after each update).
}

Events is an event manager for communicating events like damage taken, a being killed, etc.

func (*Events) Add

func (e *Events) Add(eventType EventType, source, target Entity, data interface{})

func (*Events) Update

func (e *Events) Update(delta float64)

Update updates the Events system.

type Item

type Item struct {
	Position vectors.Vec2
	// contains filtered or unexported fields
}

Item represents a static item in the world.

func (*Item) ID

func (i *Item) ID() int64

ID returns the ID of the item.

func (*Item) Pos

func (i *Item) Pos() vectors.Vec2

Pos returns the position of the item.

func (*Item) Type

func (i *Item) Type() EntityType

Type returns the type of the item.

type List

type List struct {
	// contains filtered or unexported fields
}

List represents a list of nodes

func NewList

func NewList() *List

NewList creates a new list

func (*List) Add

func (l *List) Add(nodes ...Node)

Add one or more nodes to the list

func (*List) All

func (l *List) All() []Node

All returns the full list of nodes

func (*List) Clear

func (l *List) Clear()

Clear removes all nodes from the list

func (*List) Contains

func (l *List) Contains(searchNode Node) bool

Contains check if a node is in the list

func (*List) GetIndex

func (l *List) GetIndex(searchNode Node) int

GetIndex returns the index of the node in the list if the node is not found the return value is -1

func (*List) GetIndexOfMinF

func (l *List) GetIndexOfMinF() int

GetIndexOfMinF returns the index of the nodes list with the smallest node.F value

if no node is found it returns -1

func (*List) GetMinFNode

func (l *List) GetMinFNode() (Node, error)

GetMinFNode returns the node with the smallest node.F value

func (*List) IsEmpty

func (l *List) IsEmpty() bool

IsEmpty returns if the nodes list has nodes or not

func (*List) Remove

func (l *List) Remove(removeNode Node)

Remove a node from the list if the node is not found we do nothing

type Need

type Need int
const (
	NeedSurvival Need = iota
	NeedHealth
	NeedSatiation
	NeedRest
	NeedConflict
	NeedMax
)

type Needs

type Needs struct {
	*AI
	Enemy      Entity        // current enemy
	Needs      [NeedMax]bool // determines which needs need to be met
	Prioities  []Need        // determines the order of needs
	Aggression float64       // determines how aggressive the AI is
}

Needs represents the basic needs of an AI.

func (*Needs) ActOnConflict

func (n *Needs) ActOnConflict()

func (*Needs) ActOnExhaustion

func (n *Needs) ActOnExhaustion()

func (*Needs) ActOnNeed

func (n *Needs) ActOnNeed(need Need)

ActOnNeed causes the AI to act on the given need.

func (*Needs) ActOnSurvival

func (n *Needs) ActOnSurvival()

func (*Needs) EvalNeeds

func (n *Needs) EvalNeeds()

func (*Needs) EvalThreats

func (n *Needs) EvalThreats()

func (*Needs) String

func (n *Needs) String() string

String returns a string representation of the needs.

func (*Needs) Update

func (n *Needs) Update(delta float64)

Update updates the state of the needs.

type Node

type Node struct {
	X, Y      int
	Weighting int
	// contains filtered or unexported fields
}

Node represents a simple node X and Y represents the nodes coordinates on the grid

IMPORTANT: The grid coordinates starts on the "bottom left" -> X:0 / Y:0

With the Weighting value you can set the nodes heavy grade so a node with mud or water are heavier as gras or street

func (Node) String

func (n Node) String() string

String returns formatted values of the node

type Notifiable

type Notifiable interface {
	Notify(event *Event)
}

Notifiable is an interface that can be implemented by entities that want to be notified of events.

type Pathfinding

type Pathfinding struct {
	*AI
	Waypoints   []int           // Cell indices of the waypoints.
	WaypointIdx int             // Current waypoint.
	Mode        PathfindingMode // Current mode.
}

Pathfinding represents the pathfinding of an AI.

func (*Pathfinding) SetDestination

func (p *Pathfinding) SetDestination(dest *vectors.Vec2, mode PathfindingMode) error

SetDestination sets the destination of the AI, and calculates the path.

func (*Pathfinding) Update

func (p *Pathfinding) Update(delta float64)

Update updates the pathfinding of the AI.

type PathfindingMode

type PathfindingMode int
const (
	PathfindingModeNone PathfindingMode = iota
	PathfindingModeMoveTo
	PathfindingModeFollow
	PathfindingModeChase
	PathfindingModeFleeTo
)

type Perception

type Perception struct {
	*AI
	Entities    []Entity // All entities we can see.
	EntitiesNew []Entity // Newly appeared entities.
	Items       []*Item  // All items we can see.
	ItemsNew    []*Item  // Newly appeared items.
}

Perception represents the perception of an AI of the world.

func (*Perception) CanSeeEntity

func (p *Perception) CanSeeEntity(e Entity) bool

CanSeeEntity returns true if the AI can see the given entity. TODO: Instead we should just take a position and check if we have line of sight to that position, plus some modifier based on the entity's size or stealth.

func (*Perception) Update

func (p *Perception) Update(delta float64)

Update updates the perception of the world. TODO: Based on insight, we should be able to gain an understanding of the motives of other beings. This will allow us to make better decisions, like whether to attack or flee from a threat.

type World

type World struct {
	Beings []Entity
	Items  []*Item
	Width  int
	Height int
	Cells  []bool
	Events *Events
	// contains filtered or unexported fields
}

World represents the game world.

func NewWorld

func NewWorld(w, h int, seed int64) *World

NewWorld creates a new world.

func (*World) AddBeing

func (w *World) AddBeing()

AddBeing adds a random being to the world.

func (*World) AddItem

func (w *World) AddItem()

AddItem adds a random item to the world.

func (*World) CellIdxToPos

func (w *World) CellIdxToPos(idx int) *vectors.Vec2

CellIdxToPos returns the position for a cell index.

func (*World) CheckIdxReachable

func (w *World) CheckIdxReachable(idx int) error

CheckIdxReachable checks if a cell is reachable.

func (World) ExportGif

func (w World) ExportGif(path string) error

Export all frames to a GIF under the given path.

func (World) ExportWebp

func (m World) ExportWebp(name string) error

func (*World) GetEntitiesInRadius

func (w *World) GetEntitiesInRadius(pos vectors.Vec2, radius float64) []Entity

GetEntitiesInRadius returns all entities within a radius of a position.

func (*World) GetItemsInRadius

func (w *World) GetItemsInRadius(pos vectors.Vec2, radius float64) []*Item

GetItemsInRadius returns all items within a radius of a position.

func (*World) Pathfind

func (w *World) Pathfind(start, end *vectors.Vec2) ([]int, error)

func (*World) PosToCellIdx

func (w *World) PosToCellIdx(pos *vectors.Vec2) int

PosToCellIdx returns the cell index for a position.

func (*World) Update

func (w *World) Update(delta float64)

Update updates the state of the world.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL