diff options
| author | historia <[not public]> | 2026-06-29 18:13:33 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-29 18:13:33 -0400 |
| commit | 7d76673fa0707d204c7d084c8f90c8133c22a31a (patch) | |
| tree | 6fc832b9f3fde6ed455abb535f6e28b0e5db1fd9 /internal/game/sys_triggers.go | |
| parent | feb80b7dde200d4121cd9f9c583a08f9869c5588 (diff) | |
| download | thehouseoficarus-7d76673fa0707d204c7d084c8f90c8133c22a31a.tar.gz | |
feat: migrated map (and bfs) to full 3D instead of individual 2D maps on different planes.
Diffstat (limited to 'internal/game/sys_triggers.go')
| -rw-r--r-- | internal/game/sys_triggers.go | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/internal/game/sys_triggers.go b/internal/game/sys_triggers.go new file mode 100644 index 0000000..cd1bbd3 --- /dev/null +++ b/internal/game/sys_triggers.go @@ -0,0 +1,307 @@ +package game + +import ( + "fmt" + "strings" + + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" +) + +func (g *Game) TriggerSeqTick() { + g.processPlayerTriggerSequences() + g.processWorldTriggerSequences() +} + +func (g *Game) TransientMobTick() { + if g.Hub == nil { + return + } + for _, inst := range g.MobStore.AllInstances() { + if inst.Owner == "" || inst.HP <= 0 { + continue + } + if !inst.DespawnOnLeave { + continue + } + + g.charsMu.Lock() + ownerSess := g.loggedInChars[inst.Owner] + g.charsMu.Unlock() + if ownerSess == nil || ownerSess.Player == nil { + continue + } + + ownerRoom := ownerSess.Player.RoomID + allowed := inst.DespawnRooms + if len(allowed) == 0 { + allowed = []int{inst.HomeRoomID} + } + inAllowed := false + for _, r := range allowed { + if ownerRoom == r { + inAllowed = true + break + } + } + if inAllowed { + inst.DespawnCounter = inst.DespawnTickCount() + continue + } + inst.DecrementDespawnCounter() + if inst.DespawnCounter <= 0 { + g.MobStore.RemoveInstance(inst.InstanceID) + for _, sess := range g.Hub.PlayersInRoom(inst.RoomID) { + sess.WriteLine(g.colorize(sess, "broadcast", + fmt.Sprintf("%s fades away.", mobDisplayName(inst, true)))) + } + } + } +} + +func (g *Game) processPlayerTriggerSequences() { + seqs := g.TriggerStore.SnapshotPlayerSeqs() + for _, seq := range seqs { + g.charsMu.Lock() + sess := g.loggedInChars[seq.PlayerName] + g.charsMu.Unlock() + if sess == nil || sess.Player == nil { + g.TriggerStore.RemovePlayerSeq(seq.PlayerName, seq.TriggerID) + continue + } + p := sess.Player + if p.RoomID != seq.RoomID { + continue + } + + seq.Wait-- + if seq.Wait > 0 { + continue + } + + var jobs []world.TriggerStep + for len(seq.Steps) > 0 && seq.Wait <= 0 { + step := seq.Steps[0] + seq.Steps = seq.Steps[1:] + jobs = append(jobs, step) + if len(seq.Steps) > 0 { + seq.Wait = seq.Steps[0].Delay + } + } + + for _, step := range jobs { + s := step + g.executeTriggerStep(sess, p, &s, seq.RoomID, seq.FlagValue) + } + + if len(seq.Steps) == 0 { + g.TriggerStore.RemovePlayerSeq(seq.PlayerName, seq.TriggerID) + if p.RoomID == seq.RoomID && sess.State == net.StateGame { + g.writePrompt(sess) + } + } + } +} + +func (g *Game) processWorldTriggerSequences() { + seqs := g.TriggerStore.SnapshotWorldSeqs() + for _, seq := range seqs { + seq.Wait-- + if seq.Wait > 0 { + continue + } + + var jobs []world.TriggerStep + for len(seq.Steps) > 0 && seq.Wait <= 0 { + step := seq.Steps[0] + seq.Steps = seq.Steps[1:] + jobs = append(jobs, step) + if len(seq.Steps) > 0 { + seq.Wait = seq.Steps[0].Delay + } + } + + for _, step := range jobs { + g.executeWorldTriggerStep(&step, seq.RoomID, seq.FlagValue) + } + + if len(seq.Steps) == 0 { + g.TriggerStore.RemoveWorldSeq(seq.TriggerID) + } + } +} + +func (g *Game) executeTriggerStep(sess *net.Session, p *player.Player, step *world.TriggerStep, roomID int, flagValue any) { + seqSpec := g.resolveColor(sess, "sequence") + broadcastSpec := g.resolveColor(sess, "broadcast") + playerMode := g.colorMode(sess) + if step.Message != "" { + msg := expandTemplate(step.Message, p.Name, flagValue) + msg = color.ExpandTagsDefault(playerMode, seqSpec, msg) + sess.WriteLine(msg) + } + if step.Broadcast != "" && g.Hub != nil { + msg := expandTemplate(step.Broadcast, p.Name, flagValue) + for _, other := range g.Hub.PlayersInRoom(roomID) { + otherMode := g.colorMode(other) + rendered := color.ExpandTagsDefault(otherMode, broadcastSpec, msg) + other.WriteLine(g.colorize(other, "broadcast", rendered)) + } + } + if step.BroadcastGlobal != "" && g.Hub != nil { + msg := expandTemplate(step.BroadcastGlobal, p.Name, flagValue) + for _, other := range g.Hub.AllSessions() { + if other.Player != nil { + otherMode := g.colorMode(other) + rendered := color.ExpandTagsDefault(otherMode, broadcastSpec, msg) + other.WriteLine(g.colorize(other, "broadcast", rendered)) + } + } + } + if len(step.SetFlags) > 0 { + g.Flags.SetAll(step.SetFlags) + } + if len(step.SetPlayerFlags) > 0 { + for k, v := range step.SetPlayerFlags { + g.setPlayerFlag(p, k, v) + } + g.AccountStore.SaveCharacter(p) + } + if step.GiveItem != "" { + slot := p.FirstFreeSlot() + if slot == -1 { + sess.WriteLine("Your inventory is too full to receive that.") + } else { + p.SetInvSlot(slot, &player.InventorySlot{ItemID: step.GiveItem, Quantity: 1}) + g.AccountStore.SaveCharacter(p) + } + } + if step.TakeItem != "" { + if p.HasItem(step.TakeItem) { + p.RemoveItem(step.TakeItem, 1) + g.AccountStore.SaveCharacter(p) + } + } + if step.Heal > 0 { + p.HP += step.Heal + if maxHP := p.MaxHP(); p.HP > maxHP { + p.HP = maxHP + } + g.AccountStore.SaveCharacter(p) + sess.WriteLine(fmt.Sprintf("You regain %d hitpoints.", step.Heal)) + } + if step.SpawnMob != nil { + g.spawnTriggerMob(sess, p, step.SpawnMob, roomID) + } + if step.DespawnMob != "" { + g.despawnTriggerMobs(step.DespawnMob, p.Name) + } + if step.Teleport > 0 { + g.teleportPlayer(sess, p, step.Teleport) + } +} + +func (g *Game) executeWorldTriggerStep(step *world.TriggerStep, roomID int, flagValue any) { + if step.Broadcast != "" && g.Hub != nil { + msg := expandTemplate(step.Broadcast, "", flagValue) + broadcastSpec := g.resolveColor(nil, "broadcast") + for _, other := range g.Hub.PlayersInRoom(roomID) { + otherMode := g.colorMode(other) + rendered := color.ExpandTagsDefault(otherMode, broadcastSpec, msg) + other.WriteLine(g.colorize(other, "broadcast", rendered)) + } + } + if step.BroadcastGlobal != "" && g.Hub != nil { + msg := expandTemplate(step.BroadcastGlobal, "", flagValue) + broadcastSpec := g.resolveColor(nil, "broadcast") + for _, other := range g.Hub.AllSessions() { + if other.Player != nil { + otherMode := g.colorMode(other) + rendered := color.ExpandTagsDefault(otherMode, broadcastSpec, msg) + other.WriteLine(g.colorize(other, "broadcast", rendered)) + } + } + } + if len(step.SetFlags) > 0 { + g.Flags.SetAll(step.SetFlags) + } + if step.SpawnMob != nil { + g.spawnWorldTriggerMob(step.SpawnMob, roomID) + } + if step.DespawnMob != "" { + g.despawnTriggerMobs(step.DespawnMob, "") + } +} + +func (g *Game) spawnTriggerMob(sess *net.Session, p *player.Player, cfg *world.SpawnMobConfig, roomID int) { + if cfg.ID == "" { + return + } + def, err := g.MobStore.LoadDef(cfg.ID) + if err != nil { + return + } + inst := g.MobStore.SpawnTransient(def, cfg, roomID, p.Name) + if inst == nil { + return + } + inst.ResetDespawnCounter(engine.ToTicks(cfg.DespawnTicks)) + sess.WriteLine(g.colorize(sess, "broadcast", mobDisplayName(inst, true)+" appears!")) +} + +func (g *Game) spawnWorldTriggerMob(cfg *world.SpawnMobConfig, roomID int) { + if cfg.ID == "" { + return + } + def, err := g.MobStore.LoadDef(cfg.ID) + if err != nil { + return + } + inst := g.MobStore.SpawnTransient(def, cfg, roomID, "") + if inst == nil { + return + } + inst.ResetDespawnCounter(engine.ToTicks(cfg.DespawnTicks)) + if g.Hub != nil { + for _, sess := range g.Hub.PlayersInRoom(roomID) { + sess.WriteLine(g.colorize(sess, "broadcast", + mobDisplayName(inst, true)+" appears!")) + } + } +} + +func (g *Game) despawnTriggerMobs(mobID string, owner string) { + for _, inst := range g.MobStore.AllInstances() { + if inst.DefID != mobID { + continue + } + if owner != "" && inst.Owner != owner { + continue + } + if inst.HP <= 0 { + g.MobStore.RemoveInstance(inst.InstanceID) + continue + } + if g.Hub != nil { + for _, sess := range g.Hub.PlayersInRoom(inst.RoomID) { + sess.WriteLine(g.colorize(sess, "broadcast", + fmt.Sprintf("%s vanishes.", mobDisplayName(inst, true)))) + } + } + g.MobStore.RemoveInstance(inst.InstanceID) + } +} + +func expandTemplate(tmpl string, playerName string, flagValue any) string { + s := tmpl + if playerName != "" { + s = strings.ReplaceAll(s, "%p", playerName) + } + if flagValue != nil { + s = strings.ReplaceAll(s, "%v", fmt.Sprint(flagValue)) + } + return s +} |
