diff options
| author | historia <[not public]> | 2026-06-14 22:38:23 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-14 22:38:23 -0400 |
| commit | a6f0ad79e2e3a5b7c11eef1ffc3233f3a2766f77 (patch) | |
| tree | 89601a502bbfcb50302cf03f09b6febc6040eeb0 /internal/game/action_gather.go | |
| parent | 1aba2bdd23aebed4032333e74aa553c40a31fcbd (diff) | |
| download | thehouseoficarus-a6f0ad79e2e3a5b7c11eef1ffc3233f3a2766f77.tar.gz | |
feat: fractional ticks and server game_speed implemented. potentially insanely janky.
Diffstat (limited to 'internal/game/action_gather.go')
| -rw-r--r-- | internal/game/action_gather.go | 140 |
1 files changed, 107 insertions, 33 deletions
diff --git a/internal/game/action_gather.go b/internal/game/action_gather.go index 201a619..512be81 100644 --- a/internal/game/action_gather.go +++ b/internal/game/action_gather.go @@ -29,13 +29,13 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje if cfg.DepletedMessage != "" { msg := cfg.DepletedMessage if p.OptionBool("depletion") { - msg += fmt.Sprintf(" (%d ticks to respawn)", st.DepleteTimer) + msg += fmt.Sprintf(" (%d ticks to respawn)", int(st.DepleteTimer)) } sess.WriteLine(msg) } else if cfg.DepleteTimer > 0 { - sess.WriteLine(fmt.Sprintf("The %s has been cut down for %d more ticks.", obj.Name, st.DepleteTimer)) + sess.WriteLine(fmt.Sprintf("The %s has been cut down for %d more ticks.", obj.Name, int(st.DepleteTimer))) } else { - sess.WriteLine(fmt.Sprintf("The %s is depleted for %d more ticks.", obj.Name, st.DepleteTimer)) + sess.WriteLine(fmt.Sprintf("The %s is depleted for %d more ticks.", obj.Name, int(st.DepleteTimer))) } return } @@ -43,7 +43,7 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje wait := cfg.BaseWait if len(cfg.Tools) > 0 { - toolSpeed := -1 + var toolSpeed float64 = -1 if itemID, ok := p.Equipment[object.SlotMainHand]; ok { def, err := g.ItemStore.Load(itemID) if err == nil { @@ -107,6 +107,39 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje return } + toolName := "" + if itemID, ok := p.Equipment[object.SlotMainHand]; ok { + if def, err := g.ItemStore.Load(itemID); err == nil { + for _, t := range cfg.Tools { + if def.ToolType == t { + toolName = def.Name + break + } + } + } + } + if toolName == "" { + for _, slot := range p.Inventory { + if slot == nil || slot.Quantity <= 0 { + continue + } + def, err := g.ItemStore.Load(slot.ItemID) + if err != nil { + continue + } + for _, t := range cfg.Tools { + if def.ToolType == t { + toolName = def.Name + break + } + } + if toolName != "" { + break + } + } + } + p.ActionState = &ActionState{Type: ActionGathering, TargetName: obj.Name, ToolName: toolName} + data := map[string]any{ "behavior_id": obj.BehaviorID, "instance_key": g.World.ObjStateKey(st.RoomID, st.DefID, st.Index), @@ -135,7 +168,7 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { } step := p.Action.Data["step"].(int) - wait := p.Action.Data["effective_wait"].(int) + wait := p.Action.Data["effective_wait"].(float64) instanceKey := p.Action.Data["instance_key"].(string) _, shared := p.Action.Data["deplete_timer"] @@ -153,21 +186,21 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { } sess.WriteLine(fmt.Sprintf("\n%s", cfg.GatherMsg)) p.Action.Data["step"] = 1 - p.Action.WaitLeft = wait + p.Action.WaitLeft = g.computeTicks(wait) return } if step == 2 { st := g.World.GetObjStateByKey(instanceKey) if st != nil && st.Depleted { - p.Action.WaitLeft = 3 + p.Action.WaitLeft = g.computeTicks(3) return } if cfg.RespawnMsg != "" { sess.WriteLine(fmt.Sprintf("\n%s", cfg.RespawnMsg)) } p.Action.Data["step"] = 0 - p.Action.WaitLeft = wait + p.Action.WaitLeft = g.computeTicks(wait) return } @@ -231,23 +264,30 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { g.checkBirdNest(sess, p, cfg, freeSlot) - if shared { - st := g.World.GetObjStateByKey(instanceKey) - if st != nil && st.SharedTimer <= 0 { - g.depleteSharedTree(sess, p, st, cfg, instanceKey) - return - } + if shared { + st := g.World.GetObjStateByKey(instanceKey) + if st != nil && st.SharedTimer <= 0 { + g.pendingDepletions = append(g.pendingDepletions, pendingDepletion{ + instanceKey: instanceKey, + behaviorID: behaviorID, + targetName: p.Action.TargetName, + playerNames: []string{p.Name}, + }) + p.Action.Data["step"] = 2 + p.Action.WaitLeft = g.computeTicks(1) + return } + } if !shared && drop.Depletes { - delay := cfg.RespawnTimer + delay := g.computeTicks(cfg.RespawnTimer) if delay <= 0 { delay = 10 } st := g.World.GetObjStateByKey(instanceKey) if st != nil { st.Depleted = true - st.DepleteTimer = delay + st.DepleteTimer = float64(delay) } for _, other := range g.Hub.PlayersInRoom(p.RoomID) { @@ -265,7 +305,7 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { } p.Action.Data["step"] = 2 - p.Action.WaitLeft = 1 + p.Action.WaitLeft = g.computeTicks(1) return } @@ -275,7 +315,7 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { return } p.Action.Data["step"] = 0 - p.Action.WaitLeft = wait + p.Action.WaitLeft = g.computeTicks(wait) return } } @@ -287,7 +327,7 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { return } p.Action.Data["step"] = 0 - p.Action.WaitLeft = wait + p.Action.WaitLeft = g.computeTicks(wait) } func filterDropsByLevel(drops []action.DropEntry, level int) []action.DropEntry { @@ -303,34 +343,68 @@ func filterDropsByLevel(drops []action.DropEntry, level int) []action.DropEntry return eligible } -func (g *Game) depleteSharedTree(sess *net.Session, p *player.Player, st *world.ObjState, cfg *action.GatherConfig, instanceKey string) { +func (g *Game) depleteSharedTree(st *world.ObjState, cfg *action.GatherConfig, targetName string, playerNames []string) { st.Depleted = true - st.DepleteTimer = cfg.RespawnTimer + st.DepleteTimer = float64(g.computeTicks(cfg.RespawnTimer)) st.SharedTimer = 0 msg := cfg.ExhaustedMessage if msg == "" { - msg = fmt.Sprintf("The %s falls to the ground!", p.Action.TargetName) + msg = fmt.Sprintf("The %s falls to the ground!", targetName) } - sess.WriteLine(fmt.Sprintf("\n%s", msg)) - - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other == sess { + for _, sess := range g.Hub.PlayersInRoom(st.RoomID) { + op, ok := sess.Player.(*player.Player) + if !ok || op.Action == nil || op.Action.Type != "gather" { continue } - op, ok := other.Player.(*player.Player) - if !ok || op.Action == nil || op.Action.Type != "gather" { + if op.Action.Data["instance_key"] != g.World.ObjStateKey(st.RoomID, st.DefID, st.Index) { + continue + } + isDepleter := false + for _, name := range playerNames { + if op.Name == name { + isDepleter = true + break + } + } + if isDepleter { + sess.WriteLine(fmt.Sprintf("\n%s", msg)) continue } - if op.Action.Data["instance_key"] == instanceKey { - other.WriteLine(fmt.Sprintf("\n%s", msg)) - g.CancelAction(op) + sess.WriteLine(fmt.Sprintf("\n%s", msg)) + g.CancelAction(op) + } +} + +func (g *Game) flushPendingDepletions() { + if len(g.pendingDepletions) == 0 { + return + } + + merged := make(map[string]*pendingDepletion) + for _, pd := range g.pendingDepletions { + if existing, ok := merged[pd.instanceKey]; ok { + existing.playerNames = append(existing.playerNames, pd.playerNames...) + } else { + copy := pd + merged[pd.instanceKey] = © + } + } + + for _, pd := range merged { + st := g.World.GetObjStateByKey(pd.instanceKey) + if st == nil || st.Depleted { + continue + } + cfg, err := g.BehaviorStore.LoadGather(pd.behaviorID) + if err != nil { + continue } + g.depleteSharedTree(st, cfg, pd.targetName, pd.playerNames) } - p.Action.Data["step"] = 2 - p.Action.WaitLeft = 1 + g.pendingDepletions = nil } func (g *Game) checkBirdNest(sess *net.Session, p *player.Player, cfg *action.GatherConfig, currentDropSlot int) { |
