aboutsummaryrefslogtreecommitdiff
path: root/internal/behavior/behavior.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/behavior/behavior.go')
-rw-r--r--internal/behavior/behavior.go92
1 files changed, 83 insertions, 9 deletions
diff --git a/internal/behavior/behavior.go b/internal/behavior/behavior.go
index d31a0a5..69b91cd 100644
--- a/internal/behavior/behavior.go
+++ b/internal/behavior/behavior.go
@@ -1,5 +1,7 @@
package behavior
+import "gopkg.in/yaml.v3"
+
type GatherConfig struct {
Skill string `yaml:"skill"`
Tools []string `yaml:"tools"`
@@ -43,16 +45,88 @@ type TalkOption struct {
Action *NodeAction `yaml:"action,omitempty"`
}
+// NodeAction is the inline-only effects payload — used by talk nodes,
+// talk options, and any callers that need a simple "do these effects now"
+// primitive (use/look/kill interactions, exits). It is the embedded
+// subset of StepAction (the universal superset used by on_enter and triggers).
type NodeAction struct {
- SetGlobalFlags map[string]any `yaml:"set_global_flags"`
- SetPlayerFlags map[string]any `yaml:"set_player_flags"`
- GiveItem string `yaml:"give_item"`
- TakeItem string `yaml:"take_item"`
- Teleport int `yaml:"teleport"`
- Heal int `yaml:"heal"`
- Credits int `yaml:"credits"`
- ReputationCost int `yaml:"reputation_cost"`
- ApsNode bool `yaml:"aps_node"`
+ SetGlobalFlags map[string]any `yaml:"set_global_flags,omitempty"`
+ SetPlayerFlags map[string]any `yaml:"set_player_flags,omitempty"`
+ GiveItem string `yaml:"give_item,omitempty"`
+ TakeItem string `yaml:"take_item,omitempty"`
+ Teleport int `yaml:"teleport,omitempty"`
+ Heal int `yaml:"heal,omitempty"`
+ Credits int `yaml:"credits,omitempty"`
+ ApsNode bool `yaml:"aps_node,omitempty"`
+}
+
+// Interaction is one conditional trigger entry shared by on_use, on_look
+// (objects) and on_kill (mobs). Item filters by the appropriate held/weapon
+// item depending on the interaction kind (see itemMatch on applyInteraction).
+// The first entry whose item filter and Condition pass wins and fires exactly
+// once.
+//
+// Action is a full StepAction so on_kill may broadcast a kill announcement or
+// spawn a follow-up mob; inline callers (use/look) simply leave the
+// sequence-only fields empty.
+type Interaction struct {
+ Item string `yaml:"item_id,omitempty"`
+ Condition *Condition `yaml:"condition,omitempty"`
+ Message string `yaml:"message,omitempty"`
+ Action *StepAction `yaml:"action,omitempty"`
+}
+
+// StepAction is the universal effect primitive — one superset struct shared
+// across all effect executors (on_enter steps, room/global triggers, talk
+// nodes, use/look/kill interactions, exit traversal). The embedded
+// NodeAction holds the inline-only effects (flags/items/teleport/heal/credits/
+// aps_node); the additional fields are the sequence-only effects (message,
+// broadcasts, mob spawn/despawn, delay). Condition is evaluated per-step by
+// the on_enter / trigger schedulers; inline callers ignore it (the gating
+// happens at the parent Interaction.ExitDef level instead).
+type StepAction struct {
+ NodeAction `yaml:",inline"`
+ Condition *Condition `yaml:"condition,omitempty"`
+ Message string `yaml:"message,omitempty"`
+ Broadcast string `yaml:"broadcast,omitempty"`
+ BroadcastGlobal string `yaml:"broadcast_global,omitempty"`
+ SpawnMob *SpawnMobConfig `yaml:"spawn_mob,omitempty"`
+ DespawnMob string `yaml:"despawn_mob,omitempty"`
+ Delay int `yaml:"delay,omitempty"`
+}
+
+// IsTimed reports whether the step carries sequence semantics (any non-zero
+// field means it needs per-tick scheduling rather than synchronous printing).
+func (s StepAction) IsTimed() bool {
+ return s.Delay > 0 || s.Message != "" || s.Broadcast != "" || s.BroadcastGlobal != "" ||
+ s.SpawnMob != nil || s.DespawnMob != "" ||
+ len(s.SetGlobalFlags) > 0 || len(s.SetPlayerFlags) > 0 ||
+ s.GiveItem != "" || s.TakeItem != "" || s.Teleport != 0 || s.Heal != 0 ||
+ s.Credits != 0 || s.ApsNode
+}
+
+// SpawnMobConfig configures a transient mob spawned by a trigger or on_enter
+// step. It accepts either a bare string (the mob id) or a full map at
+// unmarshal time.
+type SpawnMobConfig struct {
+ ID string `yaml:"id"`
+ OwnerOnly bool `yaml:"owner_only"`
+ DespawnOnLeave bool `yaml:"despawn_on_leave"`
+ DespawnRooms []int `yaml:"despawn_rooms"`
+ DespawnTicks float64 `yaml:"despawn_ticks"`
+}
+
+func (s *SpawnMobConfig) UnmarshalYAML(value *yaml.Node) error {
+ if value.Kind == yaml.ScalarNode {
+ var id string
+ if err := value.Decode(&id); err != nil {
+ return err
+ }
+ s.ID = id
+ return nil
+ }
+ type raw SpawnMobConfig
+ return value.Decode((*raw)(s))
}
// ShopConfig is a root-level mob property (mob YAML `shop:`). Buy prices are