aboutsummaryrefslogtreecommitdiff
path: root/internal/game/action_steal.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/action_steal.go')
-rw-r--r--internal/game/action_steal.go477
1 files changed, 0 insertions, 477 deletions
diff --git a/internal/game/action_steal.go b/internal/game/action_steal.go
deleted file mode 100644
index 65c684c..0000000
--- a/internal/game/action_steal.go
+++ /dev/null
@@ -1,477 +0,0 @@
-package game
-
-import (
- "fmt"
- "math/rand"
- "sort"
- "strconv"
- "strings"
-
- "thehouseoficarus/internal/action"
- "thehouseoficarus/internal/combat"
- "thehouseoficarus/internal/engine"
- "thehouseoficarus/internal/net"
- "thehouseoficarus/internal/object"
- "thehouseoficarus/internal/player"
- "thehouseoficarus/internal/world"
-)
-
-func (g *Game) executeSteal(sess *net.Session, args []string, rawInput string) {
- p := sess.Player
- g.cancelAction(p)
- if len(args) == 0 {
- g.doSteal(sess, "")
- } else {
- g.doSteal(sess, strings.Join(args, " "))
- }
-}
-
-var stallGuardTalk = &action.TalkConfig{
- Nodes: map[string]action.TalkNode{
- "start": {
- Message: "Caught you red-handed! You have three options, thief.",
- Options: []action.TalkOption{
- {Text: "\"I'll pay a fine. (500 credits)\"", Goto: "bribe", Condition: &action.Condition{MinCredits: 500}},
- {Text: "\"Take me to jail.\"", Goto: "jail"},
- {Text: "\"You'll have to catch me first!\"", Goto: "fight"},
- },
- },
- "bribe": {
- Message: "Smart choice. Hand over 500 credits and we'll forget this happened.",
- Action: &action.NodeAction{Cost: 500},
- Options: []action.TalkOption{{Text: "\"Fine, take it.\"", End: true}},
- },
- "jail": {
- Message: "Off to the detention cell with you!",
- Action: &action.NodeAction{Teleport: 162},
- Options: []action.TalkOption{{Text: "(You are dragged away)", End: true}},
- },
- "fight": {
- Message: "Then defend yourself!",
- Action: &action.NodeAction{SetFlags: map[string]any{"guard_hostile": true}},
- Options: []action.TalkOption{{Text: "(The guard attacks!)", End: true}},
- },
- },
-}
-
-var stealSuccess = action.SuccessFormula{
- Base: 0.5,
- PerLevel: 0.03,
- Cap: 0.95,
-}
-
-func (g *Game) doSteal(sess *net.Session, input string) {
- p := sess.Player
-
- if combat.GetCombat(p.Name) != nil {
- sess.WriteLine("You can't do that during combat!")
- return
- }
-
- g.cancelAction(p)
-
- targetType, mob, objDef, errMsg := g.resolveStealTarget(sess, p, input)
- if errMsg != "" {
- sess.WriteLine(errMsg)
- return
- }
-
- g.startSteal(sess, p, targetType, mob, objDef)
-}
-
-func (g *Game) resolveStealTarget(sess *net.Session, p *player.Player, input string) (targetType string, mob *world.MobInstance, objDef *object.ObjectDef, errMsg string) {
- input = strings.ToLower(strings.TrimSpace(input))
- instanceIdx := -1
- searchName := input
-
- if dotPos := strings.Index(input, "."); dotPos > 0 {
- if n, err := strconv.Atoi(input[:dotPos]); err == nil && n > 0 {
- instanceIdx = n - 1
- searchName = input[dotPos+1:]
- }
- }
-
- roomMobs := g.MobStore.MobsInRoom(p.RoomID)
- var stealMobs []*world.MobInstance
- for _, m := range roomMobs {
- if m.StealTable != "" {
- stealMobs = append(stealMobs, m)
- }
- }
-
- roomObjs := g.World.AllObjInstances(p.RoomID)
- var stealObjs []*object.ObjectDef
- for _, st := range roomObjs {
- def, err := g.ObjectStore.Load(st.DefID)
- if err != nil || def.StealTable == "" {
- continue
- }
- stealObjs = append(stealObjs, def)
- }
-
- if input == "" {
- if len(stealMobs) == 0 && len(stealObjs) == 0 {
- return "", nil, nil, "There's nothing here to steal from."
- }
-
- uniqueMobDefs := make(map[string]bool)
- for _, m := range stealMobs {
- uniqueMobDefs[m.DefID] = true
- }
-
- if len(stealMobs) > 0 && len(stealObjs) == 0 {
- if len(uniqueMobDefs) <= 1 {
- return "mob", stealMobs[0], nil, ""
- }
- }
- if len(stealObjs) > 0 && len(stealMobs) == 0 {
- uniqueObjDefs := make(map[string]bool)
- for _, o := range stealObjs {
- uniqueObjDefs[o.ID] = true
- }
- if len(uniqueObjDefs) <= 1 {
- return "object", nil, stealObjs[0], ""
- }
- }
- return "", nil, nil, "Steal from what?"
- }
-
- var matchingMobs []*world.MobInstance
- for _, m := range stealMobs {
- if m.MatchQuality(searchName) >= world.MatchPrefix {
- matchingMobs = append(matchingMobs, m)
- }
- }
- sort.Slice(matchingMobs, func(i, j int) bool {
- return matchingMobs[i].InstanceID < matchingMobs[j].InstanceID
- })
-
- if len(matchingMobs) > 0 {
- uniqueDefs := make(map[string]bool)
- for _, m := range matchingMobs {
- uniqueDefs[m.DefID] = true
- }
- if len(uniqueDefs) > 1 && instanceIdx < 0 {
- return "", nil, nil, "Which one?"
- }
- if instanceIdx >= 0 && instanceIdx < len(matchingMobs) {
- return "mob", matchingMobs[instanceIdx], nil, ""
- }
- if len(matchingMobs) == 1 {
- return "mob", matchingMobs[0], nil, ""
- }
- for i := range matchingMobs {
- if instanceIdx == i || instanceIdx < 0 {
- return "mob", matchingMobs[i], nil, ""
- }
- }
- }
-
- var matchingObjs []*object.ObjectDef
- for _, o := range stealObjs {
- if object.WordPrefixMatch(searchName, o.Name) {
- matchingObjs = append(matchingObjs, o)
- }
- }
- if len(matchingObjs) > 1 {
- return "", nil, nil, "Which one?"
- }
- if len(matchingObjs) == 1 {
- return "object", nil, matchingObjs[0], ""
- }
-
- return "", nil, nil, "There's nothing here to steal from."
-}
-
-func (g *Game) startSteal(sess *net.Session, p *player.Player, targetType string, mob *world.MobInstance, objDef *object.ObjectDef) {
- var stealTable string
- var stealLevel int
- var stealXP int
- var stealSpeed float64
- var targetName string
- var targetID string
- var mobInstanceID string
- var objDefID string
- var guardMob string
- guardWatching := false
-
- if targetType == "mob" {
- stealTable = mob.StealTable
- stealLevel = mob.StealLevel
- stealXP = mob.StealXP
- stealSpeed = mob.StealSpeed
- targetName = mob.Name
- targetID = mob.InstanceID
- mobInstanceID = mob.InstanceID
-
- if stealTable == "" {
- sess.WriteLine(fmt.Sprintf("You can't steal from the %s.", targetName))
- return
- }
- if thievingLevel := p.Level(player.Thieving); stealLevel > 0 && thievingLevel < stealLevel {
- sess.WriteLine(fmt.Sprintf("You need level %d thieving to steal from the %s.", stealLevel, targetName))
- return
- }
- if mob.HP <= 0 {
- sess.WriteLine("That is already dead.")
- return
- }
- if combat.IsMobInCombat(mob.InstanceID) {
- sess.WriteLine(fmt.Sprintf("The %s is busy.", targetName))
- return
- }
- } else {
- stealTable = objDef.StealTable
- stealLevel = objDef.StealLevel
- stealXP = objDef.StealXP
- stealSpeed = objDef.StealSpeed
- targetName = objDef.Name
- targetID = objDef.ID
- objDefID = objDef.ID
- guardMob = objDef.GuardMob
-
- if stealTable == "" {
- sess.WriteLine(fmt.Sprintf("You can't steal from the %s.", targetName))
- return
- }
- if thievingLevel := p.Level(player.Thieving); stealLevel > 0 && thievingLevel < stealLevel {
- sess.WriteLine(fmt.Sprintf("You need level %d thieving to steal from the %s.", stealLevel, targetName))
- return
- }
-
- if guardMob != "" {
- guard := g.findGuardInRoom(p.RoomID, guardMob)
- if guard != nil {
- timerKey := fmt.Sprintf("%d:%s", p.RoomID, objDefID)
- if _, exists := g.guardWatchTimers[timerKey]; !exists {
- g.guardWatchTimers[timerKey] = 0
- }
- guardWatching = g.isGuardWatching(p.RoomID, objDefID)
- }
- }
- }
-
- if p.FirstFreeSlot() == -1 {
- sess.WriteLine("Your inventory is too full!")
- return
- }
-
- sess.WriteLine(fmt.Sprintf("You attempt to steal from the %s...", targetName))
-
- g.broadcastAction(sess, "\n%s glances around the room.", p.Name)
-
- p.Action = &action.Action{
- Type: action.TypeSteal,
- TargetID: targetID,
- TargetName: targetName,
- WaitLeft: engine.ToTicks(stealSpeed),
- Data: &action.StealData{
- TargetType: targetType,
- StealTable: stealTable,
- StealLevel: stealLevel,
- StealXP: stealXP,
- StealSpeed: stealSpeed,
- TargetName: targetName,
- MobInstanceID: mobInstanceID,
- ObjDefID: objDefID,
- GuardMob: guardMob,
- GuardWatching: guardWatching,
- },
- }
-}
-
-func (g *Game) advanceSteal(sess *net.Session, p *player.Player) {
- d := p.Action.Data.(*action.StealData)
- targetType := d.TargetType
- stealTable := d.StealTable
- stealLevel := d.StealLevel
- stealXP := d.StealXP
- stealSpeed := d.StealSpeed
- targetName := d.TargetName
- guardMob := d.GuardMob
- guardWatching := d.GuardWatching
-
- if targetType == "mob" {
- mobInstanceID := d.MobInstanceID
- mob := g.MobStore.GetInstance(mobInstanceID)
- if mob == nil || mob.HP <= 0 || mob.RoomID != p.RoomID {
- sess.WriteLine("Your target is gone.")
- g.cancelAction(p)
- return
- }
- if combat.IsMobInCombat(mob.InstanceID) {
- sess.WriteLine(fmt.Sprintf("The %s is busy.", targetName))
- g.cancelAction(p)
- return
- }
- d.StealLevel = mob.StealLevel
- d.StealXP = mob.StealXP
- d.StealSpeed = mob.StealSpeed
- stealLevel = mob.StealLevel
- stealXP = mob.StealXP
- stealSpeed = mob.StealSpeed
- } else {
- objDefID := d.ObjDefID
- found := false
- for _, st := range g.World.AllObjInstances(p.RoomID) {
- if st.DefID == objDefID && !st.Depleted {
- found = true
- break
- }
- }
- if !found {
- sess.WriteLine("Your target is gone.")
- g.cancelAction(p)
- return
- }
- objDef, err := g.ObjectStore.Load(objDefID)
- if err == nil {
- d.StealLevel = objDef.StealLevel
- d.StealXP = objDef.StealXP
- d.StealSpeed = objDef.StealSpeed
- stealLevel = objDef.StealLevel
- stealXP = objDef.StealXP
- stealSpeed = objDef.StealSpeed
- d.GuardMob = objDef.GuardMob
- guardMob = objDef.GuardMob
-
- if guardMob != "" {
- guard := g.findGuardInRoom(p.RoomID, guardMob)
- if guard != nil {
- timerKey := fmt.Sprintf("%d:%s", p.RoomID, objDefID)
- if _, exists := g.guardWatchTimers[timerKey]; !exists {
- g.guardWatchTimers[timerKey] = 0
- }
- guardWatching = g.isGuardWatching(p.RoomID, objDefID)
- d.GuardWatching = guardWatching
- }
- }
- }
- }
-
- level := p.Level(player.Thieving)
- chance := action.SuccessChance(stealSuccess, level, stealLevel)
- if guardWatching {
- chance *= 0.5
- if chance < 0.05 {
- chance = 0.05
- }
- }
-
- if rand.Float64() < chance {
- dt, err := action.LoadDropTable(g.DataDir, stealTable)
- if err != nil || len(dt.Drops) == 0 {
- sess.WriteLine("You steal nothing of value.")
- } else {
- drop := action.ResolveDrop(g.DataDir, dt.Drops)
- if drop == nil || drop.ItemID == "" {
- sess.WriteLine("You steal nothing of value.")
- } else {
- g.giveStealLoot(sess, p, drop)
- }
- }
-
- if stealXP > 0 {
- g.awardSkillXP(sess, p, player.Thieving, stealXP)
- }
-
- g.AccountStore.SaveCharacter(p)
-
- if p.FirstFreeSlot() == -1 {
- sess.WriteLine("Your inventory is full.")
- g.cancelAction(p)
- return
- }
-
- p.Action.WaitLeft = engine.ToTicks(stealSpeed)
- } else {
- if targetType == "mob" {
- mobInstanceID := d.MobInstanceID
- mob := g.MobStore.GetInstance(mobInstanceID)
- sess.WriteLine(fmt.Sprintf("The %s notices you! They attack!", targetName))
- g.cancelAction(p)
- if mob != nil && mob.HP > 0 {
- g.startCombat(sess, p, mob)
- }
- } else {
- if guardMob != "" && guardWatching {
- guard := g.findGuardInRoom(p.RoomID, guardMob)
- if guard != nil {
- sess.WriteLine("You fumble and the Guard spots you!")
- g.cancelAction(p)
- g.startStealGuardTalk(sess, p, guard)
- return
- }
- }
- sess.WriteLine("You fail to steal anything.")
- p.Action.WaitLeft = engine.ToTicks(stealSpeed)
- }
- }
-}
-
-func (g *Game) giveStealLoot(sess *net.Session, p *player.Player, drop *action.DropEntry) {
- qty := drop.Quantity
- if qty <= 0 {
- qty = 1
- }
-
- if drop.ItemID == "credits" {
- p.Credits += qty
- sess.WriteLine(g.colorize(sess, "credits_pickup", fmt.Sprintf("You steal %d credits.", qty)))
- return
- }
-
- name := drop.ItemID
- lootDef, _ := g.ItemStore.Load(drop.ItemID)
- if lootDef != nil {
- name = lootDef.Name
- }
-
- freeSlot := p.FirstFreeSlot()
- if freeSlot == -1 {
- g.World.AddGroundItem(p.RoomID, drop.ItemID, qty)
- sess.WriteLine(fmt.Sprintf("You steal %s but your inventory is full. It falls to the ground.", g.itemColorize(sess, lootDef, name)))
- return
- }
-
- p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: drop.ItemID, Quantity: qty})
- sess.WriteLine(fmt.Sprintf("You steal %s.", g.itemColorize(sess, lootDef, name)))
-}
-
-func (g *Game) findGuardInRoom(roomID int, guardDefID string) *world.MobInstance {
- mobs := g.MobStore.MobsInRoom(roomID)
- for _, m := range mobs {
- if m.DefID == guardDefID && m.HP > 0 {
- return m
- }
- }
- return nil
-}
-
-const guardWatchDuration = 8
-const guardLookAwayDuration = 4
-const guardCycleLength = guardWatchDuration + guardLookAwayDuration
-
-func (g *Game) isGuardWatching(roomID int, objDefID string) bool {
- key := fmt.Sprintf("%d:%s", roomID, objDefID)
- counter := g.guardWatchTimers[key]
- return counter%guardCycleLength < guardWatchDuration
-}
-
-func (g *Game) startStealGuardTalk(sess *net.Session, p *player.Player, guardMob *world.MobInstance) {
- cfg := stallGuardTalk
- node, ok := cfg.Nodes["start"]
- if !ok {
- sess.WriteLine("The Guard glares at you but says nothing.")
- return
- }
-
- p.Action = &action.Action{
- Type: "talk",
- TargetID: guardMob.DefID,
- TargetName: guardMob.Name,
- Data: &action.TalkData{Node: "start", Cfg: cfg, StealGuard: true},
- }
-
- g.showTalkNode(sess, node)
-}