diff options
| -rw-r--r-- | README.md | 16 | ||||
| -rw-r--r-- | TODO.md | 72 | ||||
| -rw-r--r-- | data/behaviors/chop_tree.yaml | 1 | ||||
| -rw-r--r-- | data/behaviors/fish_net.yaml | 1 | ||||
| -rw-r--r-- | data/behaviors/mine_copper.yaml | 1 | ||||
| -rw-r--r-- | internal/action/behavior.go | 1 | ||||
| -rw-r--r-- | internal/game/action_default_target.go | 79 | ||||
| -rw-r--r-- | internal/game/action_gather.go | 8 | ||||
| -rw-r--r-- | internal/game/game.go | 19 |
9 files changed, 116 insertions, 82 deletions
@@ -6,15 +6,15 @@ There is not much here yet. # Gameplay -- Type and wait skilling -- Attack and wait combat +- Type and wait skilling. +- Attack and wait combat. - Semi-AFK and active gameplay styles. -- 600ms game ticks -- 28 slot inventory -- Classless. All attributes are skills that automatically level 1-99 as you use them +- 600ms game ticks. +- 28 slot inventory. +- Classless. All attributes are skills that automatically level 1-99 as you use them. - 100% solo friendly just like you remember from 2001. Grind and chat with friends. -- Ironman-style, slow, grind up your own gear progression. Vendors don't sell craftables like armor. -- A detailed casino for you to waste all the credits you grind up for +- Ironman-style, slow, grind up your own gear progression. Vendors don't sell craftable items. +- A detailed casino for you to waste all the credits you grind up for. ## Skills @@ -42,7 +42,7 @@ There is not much here yet. | Fletching | Create cheap low-tech bows, crossbows, darts, arrows, and bolts. | | Alchemy | Clean herbs and brew potions for temporary buffs. | | Construction | Build a player-owned house for fast travel or to show off rare items. | -| Agility | Higher agility means shorter wait times when AFK travelling with APS. | +| Agility | Get more stamina and shorter wait times when AFK travelling with APS. | # Building @@ -4,7 +4,7 @@ A low-tech sci-fi MUD set on a terraformed asteroid. Runescape-style combat and ## Theme -Not generic fantasy. Takes place on a terraformed asteroid. Technology has regressed — people scrap and scavenge remnants of the old world. "Science" replaces Prayer, "Technology" replaces Magic. Scavenging replaces Runecrafting. +Takes place on a terraformed asteroid. Technology has regressed — people scrap and scavenge remnants of the old world. "Science" replaces Prayer, "Technology" replaces Magic. Scavenging replaces Runecrafting. ## Skills (23 total, all go from 1-99) @@ -193,75 +193,7 @@ See WORLDBUILDING.md for full examples of every data type with explanations. ## Action Plan -### Phase 1: Tiny ASCII Map - -- [x] Limit the room description to 70 characters wide. Add a setting named "maxWidth" in an appropriate section of config.yaml along with comment explaining it. - -Next to the block of room description text should be an ASCII mini-map showing the current room as @ and a grid of the rooms and connections surrounding it. A full-size ASCII map for the "map" command will eventually be implemented so keep that in mind for methodology and potential functions we can reuse. -Each line of the map should print on the maxWidth+2 character after the description (so there's always at least one space between the description and map) -If the map is longer than the description, just keep printing new lines for the map. - -The first line is always ╔═════╗ -The last line is always ╚═════╝ -Each of the 5 inner lines begins and ends with ║ - -The map shows up to 9 rooms and any exits connecting them. The 5 inner lines have 7 characters each (including the border) which be defined like this: - -Line 1: [║, Room A (shown if there is a west exit from North Room or north exit from West Room), — if there is a west exit from North Room, North Room, — if there is an east exit from North Room, Room B (shown if there is an east exit from North Room or north exit from East Room), ║] -Line 2: [║, | if there is a north exit from West Room, Blank, | if there is a north exit from Current Room, ↑ if there is an up exit from Current Room, | if there is a north exit from East Room, ║] -Line 3: [║, West Room, — if there is a west exit from Current Room, @, — if there is an east exit from Current Room, East Room, ║] -Line 4: [║, | if there is a south exit from West Room, ↓ if there is a down exit from Current Room, | if there is a south exit from Current Room, Blank, | if there is a south exit from East Room, ║] -Line 5: [║, Room C (shown if there is a west exit from South Room or south exit from West Room), — if there is a west exit from South Room, South Room, — if there is an east exit from South Room, Room D (shown if there is an east exit from South Room or south exit from East Room), ║] - -Note that North Room, East Room, South Room, and West Room can still show up even if there isn't a direct link from the current room to them. For example if there is a to East Room, then that has an exit north, then that room has an exit west, the 'north room' still shows up on the map. - -Each room has a parameter map_symbol to identify it on the map. - -This is how the map looks when every possible room and exit is shown. o is the map_symbol for each room in this example: - -╔═════╗ -║o—o—o║ -║| |↑|║ -║o—@—o║ -║|↓| |║ -║o—o—o║ -╚═════╝ - -This is a map with fewer connections. Note that 'Room A' and 'North Room' show up despite only being connected through East Room and Room B. - -╔═════╗ -║o—o—o║ -║ |║ -║o—@—o║ -║| ║ -║o ║ -╚═════╝ - - -Here's how the room description would look limited to 70 characters long with the map to the right: - -Central Market -The market square pulses with the unbridled energy of a thousand ╔═════╗ -voices, a symphony of commerce and humanity that hasn't changed in ║o—o—o║ -generations. Overhead, September clouds drift lazily across the sky, ║| |↑|║ -occasionally releasing weak sunlight that transforms the ancient ║o—@—o║ -stone beneath countless feet into patches of burnished gold. ║|↓| |║ - ║o—o—o║ - ╚═════╝ - -- [x] This is a feature that can be toggled with the "tinymap" toggle. If the toggle is off, don't print the map at all. -- [x] Add a toggle "left-tinymap". If this is enabled, the map should be printed on each line before the room description. For example: - -Central Market -╔═════╗ The market square pulses with the unbridled energy of a thousand -║o—o—o║ voices, a symphony of commerce and humanity that hasn't changed in -║| |↑|║ generations. Overhead, September clouds drift lazily across the sky, -║o—@—o║ occasionally releasing weak sunlight that transforms the ancient -║|↓| |║ stone beneath countless feet into patches of burnished gold. -║o—o—o║ -╚═════╝ - -### Phase 1b: Map & Option System +### Phase 1: ASCII map implemented with BFS - [x] Replaced `toggle` command with `option`/`options` — supports bool (on/off), string (choice lists), and int option types - [x] All old toggles migrated to the option system (tinymap, left-tinymap, xpdrops, exits, mobenter, mobleave, mobspawn, reserve, depletion, description) diff --git a/data/behaviors/chop_tree.yaml b/data/behaviors/chop_tree.yaml index 445047d..9072e33 100644 --- a/data/behaviors/chop_tree.yaml +++ b/data/behaviors/chop_tree.yaml @@ -10,6 +10,7 @@ success: per_level: 0.01 cap: 0.95 gather_message: "You swing your axe at the tree..." +depleted_message: "You don't see any usable wood in this tree." fail_message: "You swing but get no logs." drops: - item_id: logs diff --git a/data/behaviors/fish_net.yaml b/data/behaviors/fish_net.yaml index b971710..b5398aa 100644 --- a/data/behaviors/fish_net.yaml +++ b/data/behaviors/fish_net.yaml @@ -9,6 +9,7 @@ success: per_level: 0.005 cap: 0.90 gather_message: "You cast your net into the water..." +depleted_message: "You don't see any fish swimming here." fail_message: "You catch nothing." drops: - item_id: raw_shrimps diff --git a/data/behaviors/mine_copper.yaml b/data/behaviors/mine_copper.yaml index d4b8fbf..b4f27b9 100644 --- a/data/behaviors/mine_copper.yaml +++ b/data/behaviors/mine_copper.yaml @@ -10,6 +10,7 @@ success: per_level: 0.01 cap: 0.95 gather_message: "You swing your pickaxe at the rock..." +depleted_message: "You don't see any ore in this rock." fail_message: "You chip away but get nothing useful." drops: - item_id: copper_ore diff --git a/internal/action/behavior.go b/internal/action/behavior.go index a3c8802..4ffd7e5 100644 --- a/internal/action/behavior.go +++ b/internal/action/behavior.go @@ -9,6 +9,7 @@ type GatherConfig struct { Bait string `yaml:"bait"` Success SuccessFormula `yaml:"success"` GatherMsg string `yaml:"gather_message"` + DepletedMessage string `yaml:"depleted_message"` FailMsg string `yaml:"fail_message"` Drops []DropEntry `yaml:"drops"` DepleteDelay int `yaml:"deplete_delay"` diff --git a/internal/game/action_default_target.go b/internal/game/action_default_target.go new file mode 100644 index 0000000..45060cc --- /dev/null +++ b/internal/game/action_default_target.go @@ -0,0 +1,79 @@ +package game + +import ( + "strings" +) + +func verbToSkill(verb string) string { + switch verb { + case "mine": + return "mining" + case "chop", "cut": + return "woodcutting" + case "fish": + return "fishing" + } + return "" +} + +func (g *Game) resolveDefaultTarget(roomID int, verb string) string { + skill := verbToSkill(verb) + if skill == "" { + return "" + } + + objStates := g.World.AllObjInstances(roomID) + + type candidate struct { + defID string + name string + } + var candidates []candidate + seen := make(map[string]bool) + + for _, st := range objStates { + if seen[st.DefID] { + continue + } + seen[st.DefID] = true + + objDef, err := g.ObjectStore.Load(st.DefID) + if err != nil || objDef.BehaviorID == "" { + continue + } + + bh, err := g.BehaviorStore.Load(objDef.BehaviorID) + if err != nil || bh.Type != "gather" { + continue + } + + cfg, err := g.BehaviorStore.LoadGather(objDef.BehaviorID) + if err != nil { + continue + } + + if strings.EqualFold(cfg.Skill, skill) { + candidates = append(candidates, candidate{defID: st.DefID, name: objDef.Name}) + } + } + + if len(candidates) == 1 { + return candidates[0].name + } + return "" +} + +func (g *Game) resolveDefaultMob(roomID int) string { + mobs := g.MobStore.MobsInRoom(roomID) + if len(mobs) == 0 { + return "" + } + + firstDef := mobs[0].DefID + for _, m := range mobs[1:] { + if m.DefID != firstDef { + return "" + } + } + return mobs[0].Name +} diff --git a/internal/game/action_gather.go b/internal/game/action_gather.go index 62637c3..2ca2e82 100644 --- a/internal/game/action_gather.go +++ b/internal/game/action_gather.go @@ -26,7 +26,13 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje } if st.Depleted { - if cfg.SharedDeplete > 0 { + if cfg.DepletedMessage != "" { + msg := cfg.DepletedMessage + if p.OptionBool("depletion") { + msg += fmt.Sprintf(" (%d ticks to respawn)", st.DepleteTimer) + } + sess.WriteLine(msg) + } else if cfg.SharedDeplete > 0 { sess.WriteLine(fmt.Sprintf("The %s has been cut down for %d more ticks.", obj.Name, st.DepleteTimer)) } else { sess.WriteLine(fmt.Sprintf("The %s is depleted for %d more ticks.", obj.Name, st.DepleteTimer)) diff --git a/internal/game/game.go b/internal/game/game.go index 8151877..cc187a7 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -100,7 +100,8 @@ func (g *Game) handleGameCommand(sess *net.Session, input string) { return } - if p, ok := sess.Player.(*player.Player); ok { + p, _ := sess.Player.(*player.Player) + if p != nil { g.cancelRest(p.Name) } @@ -153,7 +154,13 @@ func (g *Game) handleGameCommand(sess *net.Session, input string) { } case "attack", "kill": if len(args) == 0 { - sess.WriteLine("Attack what?") + target := g.resolveDefaultMob(p.RoomID) + if target == "" { + sess.WriteLine("Attack what?") + break + } + g.doAttack(sess, target) + return } else { g.doAttack(sess, strings.Join(args, " ")) return @@ -209,7 +216,13 @@ func (g *Game) handleGameCommand(sess *net.Session, input string) { } case "mine", "chop", "fish", "cut", "use", "pull", "push": if len(args) == 0 { - sess.WriteLine(fmt.Sprintf("%s what?", cmd)) + target := g.resolveDefaultTarget(p.RoomID, cmd) + if target == "" { + sess.WriteLine(fmt.Sprintf("%s what?", cmd)) + break + } + g.StartAction(sess, cmd, target) + return } else { g.StartAction(sess, cmd, strings.Join(args, " ")) return |
