diff options
Diffstat (limited to 'internal/game')
| -rw-r--r-- | internal/game/act.go | 10 | ||||
| -rw-r--r-- | internal/game/act_clean.go | 2 | ||||
| -rw-r--r-- | internal/game/act_combine.go | 157 | ||||
| -rw-r--r-- | internal/game/act_fletch.go | 2 | ||||
| -rw-r--r-- | internal/game/act_gather.go | 25 | ||||
| -rw-r--r-- | internal/game/act_state.go | 8 | ||||
| -rw-r--r-- | internal/game/act_use.go | 28 | ||||
| -rw-r--r-- | internal/game/act_use_interaction.go | 23 | ||||
| -rw-r--r-- | internal/game/cmd_use.go | 32 | ||||
| -rw-r--r-- | internal/game/core_production.go | 7 | ||||
| -rw-r--r-- | internal/game/production_engine.go | 31 |
11 files changed, 264 insertions, 61 deletions
diff --git a/internal/game/act.go b/internal/game/act.go index aa25625..c8101ab 100644 --- a/internal/game/act.go +++ b/internal/game/act.go @@ -236,9 +236,13 @@ func (g *Game) AdvanceActions() { g.advanceObstacle(sess, p) case behavior.TypeTalk: g.advanceTalk(sess, p) - case behavior.TypeTriggerModule: - g.advanceTriggerModule(sess, p) - default: + case behavior.TypeTriggerModule: + g.advanceTriggerModule(sess, p) + case behavior.TypeUseInteraction: + g.advanceUseInteraction(sess, p) + case behavior.TypeCombine: + g.advanceCombine(sess, p) + default: if productionActionTypes[string(p.Action.Type)] { g.advanceProduction(sess, p) } diff --git a/internal/game/act_clean.go b/internal/game/act_clean.go index 021a07c..7647f4f 100644 --- a/internal/game/act_clean.go +++ b/internal/game/act_clean.go @@ -71,7 +71,7 @@ func (g *Game) advanceClean(sess *net.Session, p *player.Player) { } g.AccountStore.SaveCharacter(p) - msg := g.resolveCraftMessage(sess, craft.Message, "You clean the %i1.", craft, matchItem.ID, nil, p.HasItem) + msg := g.resolveCraftMessage(sess, craft.SuccessMessage, "You clean the %i1.", craft, matchItem.ID, nil, p.HasItem) if p.OptionBool("xp_drops") && craft.XP > 0 { msg += g.formatXpDropSingle(sess, p, player.Pharmacy, craft.XP) } diff --git a/internal/game/act_combine.go b/internal/game/act_combine.go new file mode 100644 index 0000000..8937d38 --- /dev/null +++ b/internal/game/act_combine.go @@ -0,0 +1,157 @@ +package game + +import ( + "thehouseoficarus/internal/behavior" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/item" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +func (g *Game) startCombine(sess *net.Session, p *player.Player, item *item.ItemDef, craft *item.CraftDef, count int) { + g.cancelAction(p) + g.cancelBgAction(p) + + if !craftHasAllItemsQty(craft, p.CountItem) { + sess.WriteLine("You don't have the required materials.") + g.reprompt(sess) + return + } + + outDef, _ := g.ItemStore.Load(item.ID) + outputName := item.ID + if outDef != nil { + outputName = outDef.Name + } + + startMsg := g.resolveCraftMessage(sess, craft.StartMessage, "You start combining %i1.", craft, item.ID, nil, p.HasItem) + endMsg := g.resolveCraftMessage(sess, craft.EndMessage, "You've finished combining.", craft, item.ID, nil, p.HasItem) + + p.Action = &behavior.Action{ + Type: behavior.TypeCombine, + TargetID: item.ID, + TargetName: outputName, + Data: &behavior.CombineData{ + ItemID: item.ID, + Phase: 0, + StepIndex: 0, + Remaining: count, + StartMessage: startMsg, + EndMessage: endMsg, + Wait: craft.Wait, + }, + WaitLeft: engine.ToTicks(1), + } + + g.broadcastAction(sess, "%s starts combining.", p.Name) +} + +func (g *Game) advanceCombine(sess *net.Session, p *player.Player) { + d, ok := p.Action.Data.(*behavior.CombineData) + if !ok { + g.cancelAction(p) + return + } + + item := g.loadCraftItem(d.ItemID) + if item == nil { + g.cancelAction(p) + return + } + craft := item.FirstCraft() + + switch d.Phase { + case 0: + if d.StartMessage != "" { + sess.WriteLine(d.StartMessage) + } + d.Phase = 1 + if len(craft.Steps) > 0 { + p.Action.WaitLeft = engine.ToTicks(craft.Steps[0].Delay) + } else { + firstWait := craft.Wait + if firstWait <= 0 { + firstWait = 4 + } + p.Action.WaitLeft = engine.ToTicks(firstWait) + } + return + + case 1: + if len(craft.Steps) > 0 && d.StepIndex < len(craft.Steps) { + step := craft.Steps[d.StepIndex] + sess.WriteLine(g.resolveCraftVar(sess, step.Message, craft, item.ID, nil, p.HasItem)) + d.StepIndex++ + if d.StepIndex < len(craft.Steps) { + p.Action.WaitLeft = engine.ToTicks(craft.Steps[d.StepIndex].Delay) + return + } + } + d.Phase = 2 + p.Action.WaitLeft = engine.ToTicks(1) + return + + case 2: + if !craftHasAllItemsQty(craft, p.CountItem) { + if d.EndMessage != "" { + sess.WriteLine(d.EndMessage) + } + g.cancelAction(p) + return + } + + consumeIngredients(craft, p.HasItem, p.RemoveItem) + + outputQty := craft.OutputQty + if outputQty <= 0 { + outputQty = 1 + } + + outDef, _ := g.ItemStore.Load(item.ID) + placed := false + if outDef != nil && outDef.Stackable { + for i := 0; i < 28; i++ { + slot := p.InvSlot(i) + if slot != nil && slot.ItemID == item.ID { + slot.Quantity += outputQty + placed = true + break + } + } + } + if !placed { + freeSlot := p.FirstFreeSlot() + if freeSlot == -1 { + sess.WriteLine("Your inventory is too full!") + g.cancelAction(p) + return + } + p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: item.ID, Quantity: outputQty}) + } + + g.AccountStore.SaveCharacter(p) + + if d.Remaining > 0 { + d.Remaining-- + if d.Remaining <= 0 { + if d.EndMessage != "" { + sess.WriteLine(d.EndMessage) + } + g.cancelAction(p) + return + } + } + + if !craftHasAllItemsQty(craft, p.CountItem) { + if d.EndMessage != "" { + sess.WriteLine(d.EndMessage) + } + g.cancelAction(p) + return + } + + d.Phase = 0 + d.StepIndex = 0 + p.Action.WaitLeft = engine.ToTicks(1) + } +} diff --git a/internal/game/act_fletch.go b/internal/game/act_fletch.go index 800bc8e..ce3a525 100644 --- a/internal/game/act_fletch.go +++ b/internal/game/act_fletch.go @@ -188,7 +188,7 @@ func (g *Game) advanceFletch(sess *net.Session, p *player.Player) { } g.AccountStore.SaveCharacter(p) - msg := g.resolveCraftMessage(sess, c.Message, "You fletch a %n.", c, def.ID, nil, p.HasItem) + msg := g.resolveCraftMessage(sess, c.SuccessMessage, "You fletch a %n.", c, def.ID, nil, p.HasItem) if p.OptionBool("xp_drops") && c.XP > 0 { msg += g.formatXpDropSingle(sess, p, skill, c.XP) } diff --git a/internal/game/act_gather.go b/internal/game/act_gather.go index ab59de0..c34c6ae 100644 --- a/internal/game/act_gather.go +++ b/internal/game/act_gather.go @@ -106,7 +106,12 @@ func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.Obje verb = "chopping" } - g.broadcastAction(sess, "%s starts %s the %s.", p.Name, verb, obj.Name) + if cfg.BroadcastMessage != "" { + bcMsg := strings.ReplaceAll(cfg.BroadcastMessage, "%o", obj.Name) + g.broadcastAction(sess, bcMsg, p.Name) + } else { + g.broadcastAction(sess, "%s starts %s the %s.", p.Name, verb, obj.Name) + } d := &behavior.GatherData{ ObjDefID: obj.ID, @@ -158,7 +163,7 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { return } } - sess.WriteLine(fmt.Sprintf("%s", cfg.GatherMsg)) + sess.WriteLine(fmt.Sprintf("%s", cfg.GatherMessage)) d.Step = 1 p.Action.WaitLeft = engine.ToTicks(wait) return @@ -220,12 +225,14 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { } g.AccountStore.SaveCharacter(p) - msg := drop.Message - if msg == "" { - msg = fmt.Sprintf("You manage to get some %s.", g.itemColorize(sess, itemDef, itemName)) - } else { - msg = color.ExpandTags(g.colorMode(sess), msg) - } + msg := drop.SuccessMessage + coloredName := g.itemColorize(sess, itemDef, itemName) + if msg == "" { + msg = fmt.Sprintf("You manage to get some %s.", coloredName) + } else { + msg = strings.ReplaceAll(msg, "%n", coloredName) + msg = color.ExpandTags(g.colorMode(sess), msg) + } if xp > 0 && p.OptionBool("xp_drops") { msg += g.formatXpDropSingle(sess, p, player.SkillName(cfg.Skill), xp) } @@ -294,7 +301,7 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { } } - sess.WriteLine(cfg.FailMsg) + sess.WriteLine(cfg.FailMessage) if p.FirstFreeSlot() == -1 { sess.WriteLine(g.colorize(sess, "error", "Your inventory is too full!")) g.cancelAction(p) diff --git a/internal/game/act_state.go b/internal/game/act_state.go index 051390f..c4ccaba 100644 --- a/internal/game/act_state.go +++ b/internal/game/act_state.go @@ -81,9 +81,11 @@ func (g *Game) playerActionDisplay(p *player.Player) string { return "mixing some " + a.TargetName case behavior.TypeConstruct: return "constructing some " + a.TargetName - case behavior.TypeTriggerModule: - return "triggering " + a.TargetName - } + case behavior.TypeTriggerModule: + return "triggering " + a.TargetName + case behavior.TypeUseInteraction: + return "using " + a.TargetName + } } if a := p.BackgroundAction; a != nil { diff --git a/internal/game/act_use.go b/internal/game/act_use.go index 0d8582d..1e9d8e9 100644 --- a/internal/game/act_use.go +++ b/internal/game/act_use.go @@ -3,6 +3,7 @@ package game import ( "fmt" "math/rand" + "strings" "thehouseoficarus/internal/behavior" "thehouseoficarus/internal/engine" @@ -34,7 +35,11 @@ func (g *Game) startUse(sess *net.Session, p *player.Player, obj *object.ObjectD return } - g.broadcastAction(sess, "%s uses the %s.", p.Name, obj.Name) + if cfg.BroadcastMessage != "" { + g.broadcastAction(sess, strings.ReplaceAll(cfg.BroadcastMessage, "%o", obj.Name), p.Name) + } else { + g.broadcastAction(sess, "%s uses the %s.", p.Name, obj.Name) + } p.Action = &behavior.Action{ Type: "use", @@ -44,7 +49,7 @@ func (g *Game) startUse(sess *net.Session, p *player.Player, obj *object.ObjectD Data: &behavior.UseData{Cfg: cfg, Step: 0}, } - sess.WriteLine(fmt.Sprintf("%s", cfg.Message)) + sess.WriteLine(fmt.Sprintf("%s", cfg.StartMessage)) } func (g *Game) advanceUse(sess *net.Session, p *player.Player) { @@ -76,8 +81,8 @@ func (g *Game) advanceUse(sess *net.Session, p *player.Player) { skillLevel := p.Level(player.SkillName(cfg.Skill)) chance := behavior.SuccessChance(*cfg.Success, skillLevel, cfg.Level) if rand.Float64() >= chance { - if cfg.FailMsg != "" { - sess.WriteLine(cfg.FailMsg) + if cfg.FailMessage != "" { + sess.WriteLine(cfg.FailMessage) } p.Action.WaitLeft = engine.ToTicks(cfg.Wait) return @@ -107,17 +112,30 @@ func (g *Game) advanceUse(sess *net.Session, p *player.Player) { if rewardDef != nil { itemName = rewardDef.Name } - line := fmt.Sprintf("You make a %s.", g.itemColorize(sess, rewardDef, itemName)) + coloredName := g.itemColorize(sess, rewardDef, itemName) + + var line string + if cfg.SuccessMessage != "" { + line = strings.ReplaceAll(cfg.SuccessMessage, "%n", coloredName) + } else { + line = fmt.Sprintf("You make a %s.", coloredName) + } if cfg.XP > 0 && p.OptionBool("xp_drops") { line += g.formatXpDropSingle(sess, p, player.SkillName(cfg.Skill), cfg.XP) } sess.WriteLine(line) if p.FirstFreeSlot() == -1 { + if cfg.EndMessage != "" { + sess.WriteLine(cfg.EndMessage) + } sess.WriteLine("Your inventory is full.") g.cancelAction(p) return } + if cfg.EndMessage != "" { + sess.WriteLine(cfg.EndMessage) + } p.Action.WaitLeft = engine.ToTicks(cfg.Wait) } diff --git a/internal/game/act_use_interaction.go b/internal/game/act_use_interaction.go new file mode 100644 index 0000000..b96aaa3 --- /dev/null +++ b/internal/game/act_use_interaction.go @@ -0,0 +1,23 @@ +package game + +import ( + "thehouseoficarus/internal/behavior" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +func (g *Game) advanceUseInteraction(sess *net.Session, p *player.Player) { + d, ok := p.Action.Data.(*behavior.UseInteractionData) + if !ok { + g.cancelAction(p) + return + } + if d.Message != "" { + sess.WriteLine(d.Message) + } + if d.Action != nil { + g.applyNodeAction(sess, d.Action) + } + g.broadcastAction(sess, "%s uses the %s.", p.Name, d.ObjName) + g.cancelAction(p) +} diff --git a/internal/game/cmd_use.go b/internal/game/cmd_use.go index 033fd62..1989a3e 100644 --- a/internal/game/cmd_use.go +++ b/internal/game/cmd_use.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "thehouseoficarus/internal/behavior" "thehouseoficarus/internal/item" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" @@ -97,13 +98,17 @@ func (g *Game) useRoomObject(sess *net.Session, p *player.Player, input string) if ui.Condition != nil && !g.checkCondition(sess, ui.Condition) { continue } - if ui.Message != "" { - sess.WriteLine(ui.Message) + g.cancelAction(p) + p.Action = &behavior.Action{ + Type: behavior.TypeUseInteraction, + TargetName: def.Name, + WaitLeft: 1, + Data: &behavior.UseInteractionData{ + Message: ui.Message, + Action: ui.Action, + ObjName: def.Name, + }, } - if ui.Action != nil { - g.applyNodeAction(sess, ui.Action) - } - g.broadcastAction(sess, "%s uses the %s.", p.Name, def.Name) return true } if len(def.UseInteractions) > 0 { @@ -286,11 +291,16 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName, if ui.Condition != nil && !g.checkCondition(sess, ui.Condition) { continue } - if ui.Message != "" { - sess.WriteLine(ui.Message) - } - if ui.Action != nil { - g.applyNodeAction(sess, ui.Action) + g.cancelAction(p) + p.Action = &behavior.Action{ + Type: behavior.TypeUseInteraction, + TargetName: def.Name, + WaitLeft: 1, + Data: &behavior.UseInteractionData{ + Message: ui.Message, + Action: ui.Action, + ObjName: def.Name, + }, } return } diff --git a/internal/game/core_production.go b/internal/game/core_production.go index da9466f..e6a62d4 100644 --- a/internal/game/core_production.go +++ b/internal/game/core_production.go @@ -80,13 +80,6 @@ var productionTypes = map[string]productionTypeInfo{ SuccessMessage: "You craft a %n.", EndMessage: "You've finished crafting.", }, - "combine": { - ActionType: behavior.TypeCombine, - DisplayVerb: "combining", - StartMessage: "You start combining %i1.", - SuccessMessage: "You produce %n.", - EndMessage: "You've finished combining.", - }, "fletching": { ActionType: behavior.TypeFletch, DisplayVerb: "fletching", diff --git a/internal/game/production_engine.go b/internal/game/production_engine.go index 048e152..1d54714 100644 --- a/internal/game/production_engine.go +++ b/internal/game/production_engine.go @@ -113,8 +113,8 @@ func (g *Game) startProduction(sess *net.Session, p *player.Player, item *item.I ItemID: item.ID, Phase: 0, Wait: wait, - StartMsg: startMsg, - EndMsg: endMsg, + StartMessage: startMsg, + EndMessage: endMsg, Remaining: count, }, WaitLeft: engine.ToTicks(1), @@ -129,6 +129,11 @@ func (g *Game) startProductionFromItem(sess *net.Session, p *player.Player, item return } + if craft.Type == "combine" { + g.startCombine(sess, p, item, craft, count) + return + } + _, stationName := g.findStation(p.RoomID, craft.Station) if stationName == "" { stationName = "inventory" @@ -177,8 +182,8 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool { itemID := d.ItemID phase := d.Phase wait := d.Wait - startMsg := d.StartMsg - endMsg := d.EndMsg + startMsg := d.StartMessage + endMsg := d.EndMessage remaining := d.Remaining item := g.loadCraftItem(itemID) @@ -192,27 +197,11 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool { if startMsg != "" { sess.WriteLine(startMsg) } - if len(craft.Steps) > 0 { - d.NextStepIndex = 0 - d.StepsAccumulatedTicks = 0 - } d.Phase = 1 p.Action.WaitLeft = engine.ToTicks(wait) return true } - if stepsIdx := d.NextStepIndex; stepsIdx < len(craft.Steps) { - accTicks := d.StepsAccumulatedTicks - accTicks += wait - d.StepsAccumulatedTicks = accTicks - for i := stepsIdx; i < len(craft.Steps); i++ { - if accTicks >= craft.Steps[i].Tick { - sess.WriteLine(g.resolveCraftVar(sess, craft.Steps[i].Message, craft, item.ID, nil, p.HasItem)) - d.NextStepIndex = i + 1 - } - } - } - skill := craft.EffectiveSkill() skillLevel := p.Level(player.SkillName(skill)) chance := 1.0 @@ -278,7 +267,7 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool { } else { defaultSuccess = "You produce %n." } - msg := g.resolveCraftMessage(sess, craft.Message, defaultSuccess, craft, item.ID, byproducts, p.HasItem) + msg := g.resolveCraftMessage(sess, craft.SuccessMessage, defaultSuccess, craft, item.ID, byproducts, p.HasItem) if p.OptionBool("xp_drops") && craft.XP > 0 { msg += g.formatXpDropSingle(sess, p, player.SkillName(skill), craft.XP) } |
