diff options
| author | historia <[not public]> | 2026-06-19 02:51:05 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-19 02:51:05 -0400 |
| commit | dea4a06764c507699cf9adcd1fd0a572d227aff0 (patch) | |
| tree | 25769809d34db70cf0bec7d8c6d3422c23780a55 /internal | |
| parent | 2ae4a052fd1c15c3c6ba0f1dd324d896371aaffc (diff) | |
| download | thehouseoficarus-dea4a06764c507699cf9adcd1fd0a572d227aff0.tar.gz | |
feat: scavenge/scrap system started
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/game/action.go | 2 | ||||
| -rw-r--r-- | internal/game/action_identify.go | 80 | ||||
| -rw-r--r-- | internal/game/action_state.go | 7 | ||||
| -rw-r--r-- | internal/game/cmd_id.go | 144 | ||||
| -rw-r--r-- | internal/game/game.go | 6 |
5 files changed, 236 insertions, 3 deletions
diff --git a/internal/game/action.go b/internal/game/action.go index 641f4dd..2d30010 100644 --- a/internal/game/action.go +++ b/internal/game/action.go @@ -233,6 +233,8 @@ func (g *Game) AdvanceActions() { g.advanceStoke(sess, p) case "search": g.advanceSearch(sess, p) + case "identify": + g.advanceIdentify(sess, p) default: if productionActionTypes[p.Action.Type] { g.advanceProduction(sess, p) diff --git a/internal/game/action_identify.go b/internal/game/action_identify.go new file mode 100644 index 0000000..3f63e62 --- /dev/null +++ b/internal/game/action_identify.go @@ -0,0 +1,80 @@ +package game + +import ( + "fmt" + + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +func (g *Game) advanceIdentify(sess *net.Session, p *player.Player) { + junkID := p.Action.Data["junk_id"].(string) + xpPer := p.Action.Data["xp_per"].(int) + + scrapCount := p.CountItem("scrap_metal") + if scrapCount == 0 { + sess.WriteLine("You don't have any scrap metal.") + g.CancelAction(p) + return + } + + scavLevel := p.Level(player.Scavenging) + multiplier := junkMultiplier(junkID, scavLevel) + totalJunk := scrapCount * multiplier + + hasExistingStack := false + for i := 0; i < 28; i++ { + slot := p.InvSlot(i) + if slot != nil && slot.ItemID == junkID { + hasExistingStack = true + break + } + } + if !hasExistingStack && p.FirstFreeSlot() == -1 { + sess.WriteLine("Your inventory is too full!") + g.CancelAction(p) + return + } + + for i := 0; i < 28; i++ { + slot := p.InvSlot(i) + if slot != nil && slot.ItemID == "scrap_metal" { + p.SetInvSlot(i, nil) + } + } + + placed := false + for i := 0; i < 28; i++ { + slot := p.InvSlot(i) + if slot != nil && slot.ItemID == junkID { + slot.Quantity += totalJunk + placed = true + break + } + } + if !placed { + freeSlot := p.FirstFreeSlot() + p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: junkID, Quantity: totalJunk}) + } + + totalXP := scrapCount * xpPer + junkName := junkID + if def, err := g.ItemStore.Load(junkID); err == nil { + junkName = def.Name + } + + msg := fmt.Sprintf("The altar hums. You identify %d scrap metal into %d %s.", scrapCount, totalJunk, junkName) + + if newLevel := p.AddSkillXP(player.Scavenging, totalXP); newLevel > 0 { + sess.WriteLine(g.colorize(sess, "level_up", + fmt.Sprintf("*** You are now level %d scavenging! ***", newLevel))) + } + + if p.OptionBool("xp_drops") && totalXP > 0 { + msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp scv)", totalXP)) + } + sess.WriteLine(msg) + + g.AccountStore.SaveCharacter(p) + g.CancelAction(p) +} diff --git a/internal/game/action_state.go b/internal/game/action_state.go index 1935703..0dc3d32 100644 --- a/internal/game/action_state.go +++ b/internal/game/action_state.go @@ -22,8 +22,9 @@ const ( ActionProducing ActionType = "producing" ActionFletching ActionType = "fletching" ActionEating ActionType = "eating" - ActionCleaning ActionType = "cleaning" - ActionMixing ActionType = "mixing" + ActionCleaning ActionType = "cleaning" + ActionMixing ActionType = "mixing" + ActionIdentifying ActionType = "identifying" ) type ActionState struct { @@ -79,6 +80,8 @@ func (a *ActionState) Description() string { return "cleaning herbs" case ActionMixing: return "mixing " + a.TargetName + case ActionIdentifying: + return "identifying scrap at " + a.TargetName } return "" } diff --git a/internal/game/cmd_id.go b/internal/game/cmd_id.go new file mode 100644 index 0000000..5ea146d --- /dev/null +++ b/internal/game/cmd_id.go @@ -0,0 +1,144 @@ +package game + +import ( + "fmt" + + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +type altarConfig struct { + AltarID string + IdentifierID string + JunkID string + LevelReq int + XPPer int +} + +var altarConfigs = []altarConfig{ + {"solar_altar", "solar_identifier", "solarjunk", 1, 5}, + {"hydro_altar", "hydro_identifier", "hydrojunk", 14, 8}, + {"cosmic_altar", "cosmic_identifier", "cosmicjunk", 27, 10}, + {"eco_altar", "eco_identifier", "ecojunk", 28, 12}, + {"chaos_altar", "chaos_identifier", "chaosjunk", 35, 11}, + {"bio_altar", "bio_identifier", "biojunk", 44, 16}, + {"nature_altar", "nature_identifier", "naturejunk", 44, 16}, + {"law_altar", "law_identifier", "lawjunk", 54, 18}, + {"death_altar", "death_identifier", "deathjunk", 65, 22}, + {"blood_altar", "blood_identifier", "bloodjunk", 77, 28}, +} + +type junkMultiplierEntry struct { + Level int + Multiplier int +} + +var junkMultipliers = map[string][]junkMultiplierEntry{ + "solarjunk": { + {1, 1}, {22, 2}, {44, 3}, {66, 4}, {88, 5}, {99, 6}, + }, + "hydrojunk": { + {14, 1}, {36, 2}, {66, 3}, {88, 4}, + }, + "cosmicjunk": { + {27, 1}, {56, 2}, {78, 3}, + }, + "ecojunk": { + {28, 1}, {56, 2}, {78, 3}, + }, + "chaosjunk": { + {35, 1}, {66, 2}, {88, 3}, + }, + "biojunk": { + {44, 1}, {78, 2}, {99, 3}, + }, + "naturejunk": { + {44, 1}, {78, 2}, {99, 3}, + }, + "lawjunk": { + {54, 1}, {88, 2}, + }, + "deathjunk": { + {65, 1}, {99, 2}, + }, + "bloodjunk": { + {77, 1}, {99, 2}, + }, +} + +func junkMultiplier(junkID string, level int) int { + entries, ok := junkMultipliers[junkID] + if !ok { + return 1 + } + mult := 1 + for _, e := range entries { + if level >= e.Level { + mult = e.Multiplier + } + } + return mult +} + +func (g *Game) doIdentify(sess *net.Session, input string) { + p := sess.Player.(*player.Player) + g.CancelAction(p) + + var found altarConfig + foundAny := false + for _, ac := range altarConfigs { + if defID, _ := g.findStation(p.RoomID, []string{ac.AltarID}); defID != "" { + found = ac + foundAny = true + break + } + } + if !foundAny { + sess.WriteLine("There is no altar here.") + return + } + + identifierName := found.IdentifierID + if def, err := g.ItemStore.Load(found.IdentifierID); err == nil { + identifierName = def.Name + } + if !p.HasItem(found.IdentifierID) { + sess.WriteLine(fmt.Sprintf("You need a %s to use this altar.", identifierName)) + return + } + + scrapCount := p.CountItem("scrap_metal") + if scrapCount == 0 { + sess.WriteLine("You don't have any scrap metal.") + return + } + + scavLevel := p.Level(player.Scavenging) + if scavLevel < found.LevelReq { + junkName := found.JunkID + if def, err := g.ItemStore.Load(found.JunkID); err == nil { + junkName = def.Name + } + sess.WriteLine(fmt.Sprintf("You need level %d scavenging to identify %s.", found.LevelReq, junkName)) + return + } + + altarName := found.AltarID + if def, err := g.ObjectStore.Load(found.AltarID); err == nil { + altarName = def.Name + } + + p.Action = &action.Action{ + Type: "identify", + TargetID: found.AltarID, + TargetName: altarName, + WaitLeft: 1, + Data: map[string]any{ + "junk_id": found.JunkID, + "xp_per": found.XPPer, + "level_req": found.LevelReq, + }, + } + p.ActionState = &ActionState{Type: ActionIdentifying, TargetName: altarName} +} diff --git a/internal/game/game.go b/internal/game/game.go index 15236f4..dff78d5 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -149,7 +149,8 @@ func classifyCommand(cmd string) CommandClass { "attack", "kill", "north", "n", "south", "s", "east", "e", "west", "w", "up", "u", "down", "d", - "quit", "use", "burn", "stoke", "search", "walk", "cook", "smelt", "smith", "craft", "mix": + "quit", "use", "burn", "stoke", "search", "walk", "cook", "smelt", "smith", "craft", "mix", + "id", "identify": return ClassActive case "eat", "fletch", "clean": return ClassFree @@ -397,6 +398,9 @@ func (g *Game) executeCommand(sess *net.Session, cmd string, args []string, rawI case "mix": g.doMix(sess, strings.Join(args, " ")) return + case "id", "identify": + g.doIdentify(sess, strings.Join(args, " ")) + return case "talk", "speak", "ask": g.CancelAction(p) if len(args) == 0 { |
