package game import ( "fmt" "strings" "thehouseoficarus/internal/combat" "thehouseoficarus/internal/engine" "thehouseoficarus/internal/net" "thehouseoficarus/internal/object" "thehouseoficarus/internal/player" ) func (g *Game) doMove(sess *net.Session, dir string, multiplier float64) { p := sess.Player exitDir := g.World.ResolveExit(dir) if exitDir == "" { sess.WriteLine("Go where?") return } room, err := g.World.LoadRoom(p.RoomID) if err != nil { sess.WriteLine("You can't move from here.") return } exitDef, ok := room.Exits[exitDir] if !ok { sess.WriteLine("You can't go that way.") return } if exitDef.Condition != nil && !g.checkCondition(sess, exitDef.Condition) { msg := exitDef.BlockedMessage if msg == "" { msg = fmt.Sprintf("The way %s is blocked.", exitDir) } sess.WriteLine(msg) return } targetID := exitDef.Room _, err = g.World.LoadRoom(targetID) if err != nil { sess.WriteLine("That path seems blocked.") return } inCombat := combat.GetCombat(p.Name) != nil if !inCombat && p.Action != nil { g.cancelAction(p) } ticks := g.moveTicks(p, multiplier) p.MoveDirection = string(exitDir) p.MoveTarget = targetID if ticks <= 0 { g.completeMove(sess, p) return } p.MoveTicks = ticks p.ActionState = &ActionState{Type: ActionMoving, Direction: string(exitDir)} if inCombat && p.OptionBool("run_countdown") { if p.MoveTicks > 1 { sess.WriteLine(fmt.Sprintf("Running in %d ticks...", p.MoveTicks)) } else { sess.WriteLine("Running next tick!") } } } var gracefulItems = map[string]bool{ "graceful_hat": true, "graceful_torso": true, "graceful_legs": true, "graceful_gloves": true, "graceful_boots": true, "graceful_cape": true, } func (g *Game) moveTicks(p *player.Player, multiplier float64) int { if id, ok := p.Equipment[object.SlotBack]; ok && id == "cape_of_agility" { return 0 } count := 0 for _, id := range p.Equipment { if gracefulItems[id] { count++ } } base := 2.0 - float64(count)*0.15 if count == 6 { base -= 0.1 } base *= multiplier return engine.ToTicks(base) - 1 } func (g *Game) completeMove(sess *net.Session, p *player.Player) { exitDir := p.MoveDirection targetID := p.MoveTarget p.ClearMoveState() if combat.GetCombat(p.Name) != nil { g.stopCombat(p.Name) } p.Action = nil oldRoom := p.RoomID p.RoomID = targetID g.AccountStore.SaveCharacter(p) g.World.SeedGroundItems(p.RoomID) g.seedRoomMobs(p.RoomID) g.seedRoomObjects(p.RoomID) if g.Hub != nil { for _, other := range g.Hub.PlayersInRoom(oldRoom) { if other != sess { other.WriteLine(fmt.Sprintf("\n%s leaves to the %s.", p.Name, exitDir)) } } g.Hub.EnterRoom(sess, targetID) for _, other := range g.Hub.PlayersInRoom(targetID) { if other != sess { other.WriteLine(fmt.Sprintf("\n%s arrives.", p.Name)) } } } sess.WriteLine(fmt.Sprintf("\nYou walk %s.", g.colorize(sess, "direction", exitDir))) p.ActionState = &ActionState{Type: ActionMoving, Direction: exitDir} if p.OptionBool("description") { g.doLook(sess) } else { targetRoom, _ := g.World.LoadRoom(targetID) if targetRoom != nil { sess.WriteLine(g.colorize(sess, "room_name", targetRoom.Name)) } } if p.OptionBool("automap") { g.doMap(sess) } g.runEnterSteps(sess, targetID) g.checkAggro(sess) } func (g *Game) seedRoomMobs(roomID int) { room, err := g.World.LoadRoom(roomID) if err != nil || len(room.Mobs) == 0 { return } g.MobStore.SeedMobs(roomID, room.Mobs) } func (g *Game) executeMove(sess *net.Session, args []string, rawInput string) { g.doMove(sess, strings.TrimSpace(rawInput), 1.0) } func (g *Game) seedRoomObjects(roomID int) { room, err := g.World.LoadRoom(roomID) if err != nil { return } var ids []string for _, obj := range room.Objects { ids = append(ids, obj.ID) } g.World.EnsureObjectStates(roomID, ids) for _, obj := range room.Objects { def, err := g.ObjectStore.Load(obj.ID) if err != nil { continue } g.World.SetObjName(roomID, obj.ID, def.Name) if len(obj.WanderRooms) > 0 { g.World.SetObjWander(roomID, obj.ID, obj.WanderRooms, obj.WanderInterval) } if def.IsGatherable() && def.Gather.DepleteTimer > 0 { g.World.SetObjDepleteTimer(roomID, obj.ID, def.Gather.DepleteTimer) } } }