From 843fa6db681e494bf46e191655323a40577542b5 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Sun, 5 Jul 2026 20:06:53 -0400 Subject: feat: remove deprecated use/station interaction model --- internal/admin/static/admin.css | 6 +- internal/admin/static/objecteditor.js | 54 ++++++------- internal/behavior/behavior.go | 15 +--- internal/behavior/types.go | 6 -- internal/game/act.go | 4 - internal/game/act_state.go | 2 - internal/game/act_use.go | 141 ---------------------------------- internal/game/cmd_use.go | 6 +- internal/game/cmd_verbs.go | 4 - internal/object/object.go | 6 +- internal/validate/checks.go | 35 --------- internal/world/room.go | 2 - 12 files changed, 31 insertions(+), 250 deletions(-) delete mode 100644 internal/game/act_use.go (limited to 'internal') diff --git a/internal/admin/static/admin.css b/internal/admin/static/admin.css index bef3589..1f6aeec 100644 --- a/internal/admin/static/admin.css +++ b/internal/admin/static/admin.css @@ -281,9 +281,9 @@ g.ud-hover:hover text{font-weight:bold} .panel-tab-content .section-add-btn:hover{background:var(--hover)} .panel-tab-content .mini-label{font-size:9px;color:#888;display:block;margin-bottom:1px;margin-top:3px} .panel-tab-content .mini-input{width:60px!important;display:inline-block;margin-right:4px} -.obj-card{background:rgba(255,255,255,.02);border:1px solid var(--border);border-radius:5px;margin-bottom:10px;overflow:hidden;flex:1 1 calc(50% - 5px);min-width:340px;max-width:calc(50% - 5px)} -.obj-card.obj-card-full{flex:1 1 100%;max-width:100%} -.obj-fields-wrap{display:flex;flex-wrap:wrap;gap:10px} +.obj-card{background:rgba(255,255,255,.02);border:1px solid var(--border);border-radius:5px;margin-bottom:10px;overflow:hidden;break-inside:avoid;display:inline-block;width:100%} +.obj-card.obj-card-full{column-span:all} +.obj-fields-wrap{column-count:2;column-gap:10px} .obj-card-header{display:flex;align-items:center;gap:8px;padding:8px 12px;background:rgba(15,52,96,.3);cursor:pointer;user-select:none;font-size:13px;font-weight:bold} .obj-card-header:hover{background:rgba(15,52,96,.5)} .obj-card-arrow{font-size:10px;width:14px;color:#888} diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js index 7e2d04b..d4cca21 100644 --- a/internal/admin/static/objecteditor.js +++ b/internal/admin/static/objecteditor.js @@ -60,30 +60,6 @@ var SECTIONS = [ ]} ] }, - { - id: 'use', label: 'Use (Station)', path: 'use', - detect: function(d) { return d.use; }, - fields: [ - ['message', 'Message', 'text', 'You use the station...'], - ['wait', 'Wait', 'number', '4', 'narrow'], - ['skill', 'Skill', 'text', 'e.g. smithing'], - ['level', 'Level', 'number', '1', 'narrow'], - ['xp', 'XP', 'number', '25', 'narrow'], - ['fail_message', 'Fail Message', 'text', 'You fail to use the station.'], - ], - inline: [ - {label:'Success', path:'success', fields:[ - ['base', 'Base', 'number', '0.5', 'narrow'], ['per_level', 'Per Lvl', 'number', '0.01', 'narrow'], ['cap', 'Cap', 'number', '0.95', 'narrow'] - ]}, - {label:'Consume', key:'consume', type:'kv', valType:'number'}, - ], - subtables: [ - {label:'Reward', key:'reward', single:true, fields:[ - ['item_id', 'Item ID', 'search', '', '', 'items'], ['weight', 'Weight', 'number'], ['quantity', 'Quantity', 'number'], - ['message', 'Message', 'text'], - ]} - ] - }, { id: 'safespot', label: 'Safespot', path: 'safespot', detect: function(d) { return d.safespot; }, @@ -113,18 +89,16 @@ var SECTIONS = [ ] }, { - id: 'on_look', label: 'On Look', path: 'on_look', + id: 'on_look', label: 'On Look', labelHint: '(what happens when you "look ")', path: 'on_look', detect: function(d) { return d.on_look; }, fields: [ ['set_flags', 'Set Flags', 'json', '{"flag":"value"}'], ['set_player_flags', 'Set Player Flags', 'json', '{"pflag":"value"}'], - ['give_item', 'Give Item', 'text', 'e.g. keycard'], - ['take_item', 'Take Item', 'text', 'e.g. bomb'], + ['give_item', 'Give Item', 'search', 'e.g. keycard', '', null, 'items'], + ['take_item', 'Take Item', 'search', 'e.g. bomb', '', null, 'items'], ['teleport', 'Teleport', 'number', '5', 'narrow'], ['heal', 'Heal', 'number', '10', 'narrow'], ['credits', 'Credits', 'number', '100', 'narrow'], - ['reputation_cost', 'Reputation Cost', 'number', '1', 'narrow'], - ['aps_node', 'APS Node', 'checkbox'], ] }, ]; @@ -253,6 +227,8 @@ function renderCard(sec, data) { h += renderCoreCard(data); } else if (sec.id === 'steal') { h += renderStealCard(data); + } else if (sec.id === 'on_look') { + h += renderOnLookCard(data); } else { h += '
'; sec.fields.forEach(function(f) { @@ -484,6 +460,24 @@ function renderStealCard(data) { return h; } +function renderOnLookCard(data) { + var sec = SECTIONS.find(function(s) { return s.id === 'on_look'; }); + var h = '
'; + h += renderField(sec, sec.fields[0], data, -1, null, 'flex:1'); + h += renderField(sec, sec.fields[1], data, -1, null, 'flex:1'); + h += '
'; + h += '
'; + h += renderField(sec, sec.fields[2], data, -1, null, 'flex:1'); + h += renderField(sec, sec.fields[3], data, -1, null, 'flex:1'); + h += '
'; + h += '
'; + h += renderField(sec, sec.fields[4], data, -1); + h += renderField(sec, sec.fields[5], data, -1); + h += renderField(sec, sec.fields[6], data, -1); + h += '
'; + return h; +} + function renderArraySection(sec, data) { var arr = data[cardPath(sec)] || []; var h = ''; @@ -783,8 +777,6 @@ function addSection() { objectData[sec.path] = {}; } else if (sec.id === 'gather') { objectData[sec.path] = {skill:'', tools:[], success:{base:0, per_level:0, cap:1}, drops:[], gather_message:'', fail_message:'', respawn_timer:0, respawn_broadcast:'', deplete_timer:0, nest_chance:0}; - } else if (sec.id === 'use') { - objectData[sec.path] = {message:'', wait:1, consume:{}, reward:{}, fail_message:'', success:{base:0, per_level:0, cap:1}, skill:'', level:1, xp:0}; } else if (sec.id === 'safespot') { objectData[sec.path] = {tier:1, max_block_size:'', max_occupants:1, unsafe_chance:0, decay_ticks:0, decay_chance:0, respawn_on_hide:false, respawn_ticks:0, levels:[]}; } else { diff --git a/internal/behavior/behavior.go b/internal/behavior/behavior.go index 06e2715..7d8ce8f 100644 --- a/internal/behavior/behavior.go +++ b/internal/behavior/behavior.go @@ -153,17 +153,4 @@ type Condition struct { AnyOf []Condition `yaml:"any_of"` } -type UseConfig struct { - StartMessage string `yaml:"start_message"` - TicksPerCycle float64 `yaml:"ticks_per_cycle"` - Consume map[string]int `yaml:"consume"` - Reward DropEntry `yaml:"reward"` - FailMessage string `yaml:"fail_message"` - Success *SuccessFormula `yaml:"success"` - Skill string `yaml:"skill"` - Level int `yaml:"level"` - XP int `yaml:"xp"` - SuccessMessage string `yaml:"success_message"` - EndMessage string `yaml:"end_message"` - BroadcastMessage string `yaml:"broadcast_message"` -} + diff --git a/internal/behavior/types.go b/internal/behavior/types.go index 5fdebdb..99f09a0 100644 --- a/internal/behavior/types.go +++ b/internal/behavior/types.go @@ -31,7 +31,6 @@ const ( TypeGather ActionType = "gather" TypeSteal ActionType = "steal" TypeTalk ActionType = "talk" - TypeUse ActionType = "use" TypeBurn ActionType = "burn" TypeStoke ActionType = "stoke" TypeSearch ActionType = "search" @@ -117,11 +116,6 @@ type TalkData struct { PendingChosen bool } -type UseData struct { - Cfg *UseConfig - Step int -} - type UseInteractionData struct { Message string Action *NodeAction diff --git a/internal/game/act.go b/internal/game/act.go index 9e07886..f2ea121 100644 --- a/internal/game/act.go +++ b/internal/game/act.go @@ -178,8 +178,6 @@ func (g *Game) startAction(sess *net.Session, verb, target string) { } else { g.startTalk(sess, p, obj) } - case "use": - g.startUse(sess, p, obj) default: sess.WriteLine(fmt.Sprintf("You can't %s that.", verb)) } @@ -210,8 +208,6 @@ func (g *Game) AdvanceActions() { switch p.Action.Type { case "gather": g.advanceGather(sess, p) - case "use": - g.advanceUse(sess, p) case "burn": g.advanceBurn(sess, p) case "stoke": diff --git a/internal/game/act_state.go b/internal/game/act_state.go index efc5f7d..3840c63 100644 --- a/internal/game/act_state.go +++ b/internal/game/act_state.go @@ -45,8 +45,6 @@ func (g *Game) playerActionDisplay(p *player.Player) string { return "stealing from " + a.TargetName case behavior.TypeTalk: return "talking to " + a.TargetName - case behavior.TypeUse: - return "using a " + a.TargetName case behavior.TypeBurn: return "trying to start a fire" case behavior.TypeStoke: diff --git a/internal/game/act_use.go b/internal/game/act_use.go deleted file mode 100644 index dcddd52..0000000 --- a/internal/game/act_use.go +++ /dev/null @@ -1,141 +0,0 @@ -package game - -import ( - "fmt" - "math/rand" - "strings" - - "thehouseoficarus/internal/behavior" - "thehouseoficarus/internal/engine" - "thehouseoficarus/internal/net" - "thehouseoficarus/internal/object" - "thehouseoficarus/internal/player" -) - -func (g *Game) startUse(sess *net.Session, p *player.Player, obj *object.ObjectDef) { - cfg := obj.Use - if cfg == nil { - sess.WriteLine("You can't use this.") - return - } - - for itemID := range cfg.Consume { - if !p.HasItem(itemID) { - defName := itemID - if def, err := g.ItemStore.Load(itemID); err == nil { - defName = g.itemColorize(sess, def, def.Name) - } - sess.WriteLine(fmt.Sprintf("You need %s to use this.", defName)) - return - } - } - - if cfg.Success == nil && p.FirstFreeSlot() == -1 { - sess.WriteLine("Your inventory is full.") - return - } - - if cfg.BroadcastMessage != "" { - g.broadcastAction(sess, strings.ReplaceAll(cfg.BroadcastMessage, "%o", obj.Name), p.Name) - } else { - g.broadcastAction(sess, "%s uses the %s.", p.Name, obj.Name) - } - - p.Action = &behavior.Action{ - Type: "use", - TargetID: obj.ID, - TargetName: obj.Name, - WaitLeft: engine.ToTicks(1), - Data: &behavior.UseData{Cfg: cfg, Step: 0}, - } - - sess.WriteLine(fmt.Sprintf("%s", cfg.StartMessage)) -} - -func (g *Game) advanceUse(sess *net.Session, p *player.Player) { - d, ok := p.Action.Data.(*behavior.UseData) - if !ok || d == nil || d.Cfg == nil { - g.cancelAction(p) - return - } - cfg := d.Cfg - - for itemID, qty := range cfg.Consume { - if !p.HasItem(itemID) { - defName := itemID - if def, err := g.ItemStore.Load(itemID); err == nil { - defName = g.itemColorize(sess, def, def.Name) - } - sess.WriteLine(fmt.Sprintf("You've run out of %s.", defName)) - g.cancelAction(p) - return - } - if !p.RemoveItem(itemID, qty) { - sess.WriteLine(fmt.Sprintf("You need %d of %s.", qty, itemID)) - g.cancelAction(p) - return - } - } - - if cfg.Success != nil { - skillLevel := p.Level(player.SkillName(cfg.Skill)) - chance := behavior.SuccessChance(*cfg.Success, skillLevel, cfg.Level) - if rand.Float64() >= chance { - if cfg.FailMessage != "" { - sess.WriteLine(cfg.FailMessage) - } - p.Action.WaitLeft = engine.ToTicks(cfg.TicksPerCycle) - return - } - } - - freeSlot := p.FirstFreeSlot() - if freeSlot == -1 { - sess.WriteLine("Your inventory is full.") - g.cancelAction(p) - return - } - - qty := cfg.Reward.Quantity - if qty <= 0 { - qty = 1 - } - - p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: cfg.Reward.ItemID, Quantity: qty}) - if cfg.XP > 0 { - g.awardSkillXP(sess, p, player.SkillName(cfg.Skill), cfg.XP) - } - g.AccountStore.SaveCharacter(p) - - itemName := cfg.Reward.ItemID - rewardDef, _ := g.ItemStore.Load(cfg.Reward.ItemID) - if rewardDef != nil { - itemName = rewardDef.Name - } - coloredName := g.itemColorize(sess, rewardDef, itemName) - - var line string - if cfg.SuccessMessage != "" { - line = strings.ReplaceAll(cfg.SuccessMessage, "%n", coloredName) - } else { - line = fmt.Sprintf("You make a %s.", coloredName) - } - if cfg.XP > 0 && p.OptionBool("xp_drops") { - line += g.formatXpDropSingle(sess, p, player.SkillName(cfg.Skill), cfg.XP) - } - sess.WriteLine(line) - - if p.FirstFreeSlot() == -1 { - if cfg.EndMessage != "" { - sess.WriteLine(cfg.EndMessage) - } - sess.WriteLine("Your inventory is full.") - g.cancelAction(p) - return - } - if cfg.EndMessage != "" { - sess.WriteLine(cfg.EndMessage) - } - - p.Action.WaitLeft = engine.ToTicks(cfg.TicksPerCycle) -} diff --git a/internal/game/cmd_use.go b/internal/game/cmd_use.go index 8540074..22fc169 100644 --- a/internal/game/cmd_use.go +++ b/internal/game/cmd_use.go @@ -68,7 +68,7 @@ func (g *Game) doUse(sess *net.Session, input string) { return } - g.startAction(sess, "use", input) + sess.WriteLine("You can't use that.") } func (g *Game) useRoomObject(sess *net.Session, p *player.Player, input string) bool { @@ -214,7 +214,7 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName, objSt := &objInstances[0] def, _ := g.ObjectStore.Load(objSt.DefID) if def == nil { - g.startAction(sess, "use", itemBName) + sess.WriteLine("You can't use that.") return } @@ -305,7 +305,7 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName, return } - g.startAction(sess, "use", itemBName) + sess.WriteLine("You can't use that.") return } diff --git a/internal/game/cmd_verbs.go b/internal/game/cmd_verbs.go index 57db0a6..e4f1fa1 100644 --- a/internal/game/cmd_verbs.go +++ b/internal/game/cmd_verbs.go @@ -75,10 +75,6 @@ func (g *Game) executeVerbs(sess *net.Session, args []string, rawInput string) { addUnique(§ionObjs, &seen, "talk "+name) } - if bt == "use" { - addUnique(§ionObjs, &seen, "use "+name) - } - for _, ui := range def.UseInteractions { if ui.Item == "" && (ui.Condition == nil || g.checkCondition(sess, ui.Condition)) { addUnique(§ionObjs, &seen, "use "+name) diff --git a/internal/object/object.go b/internal/object/object.go index 5b45a91..89f01a4 100644 --- a/internal/object/object.go +++ b/internal/object/object.go @@ -31,7 +31,6 @@ type ObjectDef struct { Gather *behavior.GatherConfig `yaml:"gather,omitempty"` Talk *behavior.TalkConfig `yaml:"talk,omitempty"` - Use *behavior.UseConfig `yaml:"use,omitempty"` Safespot *SafespotConfig `yaml:"safespot,omitempty"` OnLook *behavior.NodeAction `yaml:"on_look,omitempty"` } @@ -55,10 +54,9 @@ type SafespotLevel struct { func (d *ObjectDef) IsGatherable() bool { return d.Gather != nil } func (d *ObjectDef) IsTalkable() bool { return d.Talk != nil } -func (d *ObjectDef) IsUsable() bool { return d.Use != nil } func (d *ObjectDef) IsSafespot() bool { return d.Safespot != nil } func (d *ObjectDef) IsInteractable() bool { - return d.Gather != nil || d.Talk != nil || d.Use != nil || d.Safespot != nil + return d.Gather != nil || d.Talk != nil || d.Safespot != nil } func (d *ObjectDef) BehaviorType() string { @@ -67,8 +65,6 @@ func (d *ObjectDef) BehaviorType() string { return "gather" case d.Talk != nil: return "talk" - case d.Use != nil: - return "use" case d.Safespot != nil: return "safespot" } diff --git a/internal/validate/checks.go b/internal/validate/checks.go index 0fc5b8f..41b34e0 100644 --- a/internal/validate/checks.go +++ b/internal/validate/checks.go @@ -189,9 +189,6 @@ func validateLocalObject(roomID int, robj world.RoomObject, itemIDs map[string]b if def.Talk != nil { bad = append(bad, "talk") } - if def.Use != nil { - bad = append(bad, "use") - } if def.Safespot != nil { bad = append(bad, "safespot") } @@ -587,9 +584,6 @@ func validateObjects(s Source) []Issue { issues = append(issues, validateTalkConfig(fmt.Sprintf("Object %q: talk", id), obj.Talk, itemIDs, roomIndex)...) } - if obj.Use != nil { - issues = append(issues, validateUseConfig(fmt.Sprintf("Object %q: use", id), obj.Use, itemIDs)...) - } if obj.OnLook != nil { issues = append(issues, validateNodeAction(fmt.Sprintf("Object %q: on_look", id), obj.OnLook, itemIDs, roomIndex)...) @@ -1234,35 +1228,6 @@ func validateExitReciprocity(s Source) []Issue { return issues } -func validateUseConfig(prefix string, cfg *behavior.UseConfig, itemIDs map[string]bool) []Issue { - var issues []Issue - if cfg == nil { - return issues - } - - for itemID := range cfg.Consume { - if itemID != "" && !itemIDs[itemID] { - issues = append(issues, Issue{ - Level: "ERROR", - Type: "reference", - Message: fmt.Sprintf("%s: consumes nonexistent item %q", - prefix, itemID), - }) - } - } - - if cfg.Reward.ItemID != "" && !itemIDs[cfg.Reward.ItemID] { - issues = append(issues, Issue{ - Level: "ERROR", - Type: "reference", - Message: fmt.Sprintf("%s: reward item %q does not exist", - prefix, cfg.Reward.ItemID), - }) - } - - return issues -} - func validateNodeAction(prefix string, na *behavior.NodeAction, itemIDs map[string]bool, roomIndex map[int]bool) []Issue { var issues []Issue if na == nil { diff --git a/internal/world/room.go b/internal/world/room.go index f66ce1d..9aa93f0 100644 --- a/internal/world/room.go +++ b/internal/world/room.go @@ -213,7 +213,6 @@ func (ro RoomObject) MarshalYAML() (interface{}, error) { Steal *object.ObjectSteal `yaml:"steal,omitempty"` Gather *behavior.GatherConfig `yaml:"gather,omitempty"` Talk *behavior.TalkConfig `yaml:"talk,omitempty"` - Use *behavior.UseConfig `yaml:"use,omitempty"` Safespot *object.SafespotConfig `yaml:"safespot,omitempty"` OnLook *behavior.NodeAction `yaml:"on_look,omitempty"` } @@ -230,7 +229,6 @@ func (ro RoomObject) MarshalYAML() (interface{}, error) { Steal: def.Steal, Gather: def.Gather, Talk: def.Talk, - Use: def.Use, Safespot: def.Safespot, OnLook: def.OnLook, }, nil -- cgit v1.2.3