aboutsummaryrefslogtreecommitdiff
path: root/internal/game/action_fletch.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-18 18:50:54 -0400
committerhistoria <[not public]>2026-06-18 18:50:54 -0400
commit17e7725f252c7f5d3be2e9c37d62751c631f196f (patch)
tree40698c5c95cb21302adbb8cd021bd8d049b6fce1 /internal/game/action_fletch.go
parentfae979b75e37b6ad57e57062a1b637a37ec87174 (diff)
downloadthehouseoficarus-17e7725f252c7f5d3be2e9c37d62751c631f196f.tar.gz
feat: fletching added including zero-time bolt feathering, which probably will break lots of things
Diffstat (limited to 'internal/game/action_fletch.go')
-rw-r--r--internal/game/action_fletch.go215
1 files changed, 215 insertions, 0 deletions
diff --git a/internal/game/action_fletch.go b/internal/game/action_fletch.go
new file mode 100644
index 0000000..734453b
--- /dev/null
+++ b/internal/game/action_fletch.go
@@ -0,0 +1,215 @@
+package game
+
+import (
+ "fmt"
+
+ "thehouseoficarus/internal/action"
+ "thehouseoficarus/internal/engine"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/player"
+)
+
+func (g *Game) startFletchAction(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int) {
+ if p.Flags == nil {
+ p.Flags = make(map[string]any)
+ }
+ p.Flags["last_fletch"] = recipe.ID
+ g.AccountStore.SaveCharacter(p)
+
+ if recipe.Wait <= 0 {
+ g.doInstantFletch(sess, p, recipe, count)
+ return
+ }
+
+ g.startTimedFletch(sess, p, recipe, count)
+}
+
+func (g *Game) doInstantFletch(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int) {
+ outDef, _ := g.ItemStore.Load(recipe.Output)
+ outputQty := recipe.OutputQty
+ if outputQty <= 0 {
+ outputQty = 1
+ }
+
+ batches := 0
+ totalOutput := 0
+ for {
+ if !g.canDoFletchRecipe(p, recipe) {
+ break
+ }
+
+ placed := false
+ if outDef != nil && outDef.Stackable {
+ for i := 0; i < 28; i++ {
+ slot := p.InvSlot(i)
+ if slot != nil && slot.ItemID == recipe.Output {
+ recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ slot.Quantity += outputQty
+ placed = true
+ break
+ }
+ }
+ }
+ if !placed {
+ recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ freeSlot := p.FirstFreeSlot()
+ if freeSlot == -1 {
+ break
+ }
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: recipe.Output, Quantity: outputQty})
+ }
+
+ if recipe.XP > 0 {
+ if newLevel := p.AddSkillXP(player.Fletching, recipe.XP); newLevel > 0 {
+ sess.WriteLine(g.colorize(sess, "level_up", fmt.Sprintf("*** You are now level %d fletching! ***", newLevel)))
+ }
+ }
+
+ batches++
+ totalOutput += outputQty
+
+ if count > 0 {
+ count--
+ if count <= 0 {
+ break
+ }
+ }
+ }
+
+ if batches == 0 {
+ sess.WriteLine("You don't have the materials for that.")
+ return
+ }
+
+ g.AccountStore.SaveCharacter(p)
+
+ outputName := recipe.Output
+ if outDef != nil {
+ outputName = outDef.Name
+ }
+
+ msg := fmt.Sprintf("You fletch %d %s.", totalOutput, outputName)
+ if p.OptionBool("xp_drops") && recipe.XP > 0 {
+ totalXP := recipe.XP * batches
+ abbr := player.SkillAbbr[player.Fletching]
+ msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", totalXP, abbr))
+ }
+ sess.WriteLine(msg)
+}
+
+func (g *Game) startTimedFletch(sess *net.Session, p *player.Player, recipe *action.RecipeDef, count int) {
+ outDef, _ := g.ItemStore.Load(recipe.Output)
+ outputName := recipe.Output
+ if outDef != nil {
+ outputName = outDef.Name
+ }
+
+ p.BackgroundAction = &action.Action{
+ Type: "fletch",
+ TargetID: recipe.ID,
+ TargetName: outputName,
+ Data: map[string]any{
+ "recipe_id": recipe.ID,
+ "phase": 0,
+ "wait": recipe.Wait,
+ "remaining": count,
+ },
+ WaitLeft: engine.ToTicks(1),
+ }
+ p.BackgroundActionState = &ActionState{Type: ActionFletching, TargetName: outputName}
+
+ sess.WriteLine(fmt.Sprintf("\nYou begin fletching %s.", outputName))
+}
+
+func (g *Game) advanceFletch(sess *net.Session, p *player.Player) {
+ recipeID, _ := p.BackgroundAction.Data["recipe_id"].(string)
+ phase, _ := p.BackgroundAction.Data["phase"].(int)
+ wait, _ := p.BackgroundAction.Data["wait"].(float64)
+ remaining, _ := p.BackgroundAction.Data["remaining"].(int)
+
+ recipe := g.loadProductionRecipe(recipeID)
+ if recipe == nil {
+ g.CancelBackgroundAction(p)
+ return
+ }
+
+ if phase == 0 {
+ p.BackgroundAction.Data["phase"] = 1
+ p.BackgroundAction.WaitLeft = engine.ToTicks(wait)
+ return
+ }
+
+ if !g.canDoFletchRecipe(p, recipe) {
+ sess.WriteLine("\nYou've run out of fletching materials.")
+ g.CancelBackgroundAction(p)
+ return
+ }
+
+ outDef, _ := g.ItemStore.Load(recipe.Output)
+ outputQty := recipe.OutputQty
+ if outputQty <= 0 {
+ outputQty = 1
+ }
+
+ placed := false
+ if outDef != nil && outDef.Stackable {
+ for i := 0; i < 28; i++ {
+ slot := p.InvSlot(i)
+ if slot != nil && slot.ItemID == recipe.Output {
+ recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ slot.Quantity += outputQty
+ placed = true
+ break
+ }
+ }
+ }
+ if !placed {
+ recipe.ConsumeAll(p.HasItem, p.RemoveItem)
+ freeSlot := p.FirstFreeSlot()
+ if freeSlot == -1 {
+ sess.WriteLine("\nYour inventory is too full!")
+ g.CancelBackgroundAction(p)
+ return
+ }
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: recipe.Output, Quantity: outputQty})
+ }
+
+ if recipe.XP > 0 {
+ if newLevel := p.AddSkillXP(player.Fletching, recipe.XP); newLevel > 0 {
+ sess.WriteLine(g.colorize(sess, "level_up", fmt.Sprintf("*** You are now level %d fletching! ***", newLevel)))
+ }
+ }
+ g.AccountStore.SaveCharacter(p)
+
+ outputName := recipe.Output
+ if outDef != nil {
+ outputName = outDef.Name
+ }
+ msg := recipe.Message
+ if msg == "" {
+ msg = fmt.Sprintf("You fletch %s.", outputName)
+ }
+ if p.OptionBool("xp_drops") && recipe.XP > 0 {
+ abbr := player.SkillAbbr[player.Fletching]
+ msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", recipe.XP, abbr))
+ }
+ sess.WriteLine(msg)
+
+ if remaining > 0 {
+ remaining--
+ p.BackgroundAction.Data["remaining"] = remaining
+ if remaining <= 0 {
+ sess.WriteLine("\nYou've finished fletching.")
+ g.CancelBackgroundAction(p)
+ return
+ }
+ }
+
+ if !g.canContinueProduction(p, recipe) {
+ sess.WriteLine("\nYou've run out of fletching materials.")
+ g.CancelBackgroundAction(p)
+ return
+ }
+
+ p.BackgroundAction.WaitLeft = engine.ToTicks(wait)
+}