package game import ( "fmt" "math/rand" "strings" "thehouseoficarus/internal/behavior" "thehouseoficarus/internal/engine" "thehouseoficarus/internal/net" "thehouseoficarus/internal/object" "thehouseoficarus/internal/player" ) func (g *Game) startUse(sess *net.Session, p *player.Player, obj *object.ObjectDef) { cfg := obj.Use if cfg == nil { sess.WriteLine("You can't use this.") return } for itemID := range cfg.Consume { if !p.HasItem(itemID) { defName := itemID if def, err := g.ItemStore.Load(itemID); err == nil { defName = g.itemColorize(sess, def, def.Name) } sess.WriteLine(fmt.Sprintf("You need %s to use this.", defName)) return } } if cfg.Success == nil && p.FirstFreeSlot() == -1 { sess.WriteLine("Your inventory is full.") return } 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", TargetID: obj.ID, TargetName: obj.Name, WaitLeft: engine.ToTicks(1), Data: &behavior.UseData{Cfg: cfg, Step: 0}, } sess.WriteLine(fmt.Sprintf("%s", cfg.StartMessage)) } func (g *Game) advanceUse(sess *net.Session, p *player.Player) { d, ok := p.Action.Data.(*behavior.UseData) if !ok || d == nil || d.Cfg == nil { g.cancelAction(p) return } cfg := d.Cfg for itemID, qty := range cfg.Consume { if !p.HasItem(itemID) { defName := itemID if def, err := g.ItemStore.Load(itemID); err == nil { defName = g.itemColorize(sess, def, def.Name) } sess.WriteLine(fmt.Sprintf("You've run out of %s.", defName)) g.cancelAction(p) return } if !p.RemoveItem(itemID, qty) { sess.WriteLine(fmt.Sprintf("You need %d of %s.", qty, itemID)) g.cancelAction(p) return } } if cfg.Success != nil { skillLevel := p.Level(player.SkillName(cfg.Skill)) chance := behavior.SuccessChance(*cfg.Success, skillLevel, cfg.Level) if rand.Float64() >= chance { if cfg.FailMessage != "" { sess.WriteLine(cfg.FailMessage) } p.Action.WaitLeft = engine.ToTicks(cfg.TicksPerCycle) return } } freeSlot := p.FirstFreeSlot() if freeSlot == -1 { sess.WriteLine("Your inventory is full.") g.cancelAction(p) return } qty := cfg.Reward.Quantity if qty <= 0 { qty = 1 } p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: cfg.Reward.ItemID, Quantity: qty}) if cfg.XP > 0 { g.awardSkillXP(sess, p, player.SkillName(cfg.Skill), cfg.XP) } g.AccountStore.SaveCharacter(p) itemName := cfg.Reward.ItemID rewardDef, _ := g.ItemStore.Load(cfg.Reward.ItemID) if rewardDef != nil { itemName = rewardDef.Name } 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.TicksPerCycle) }