aboutsummaryrefslogtreecommitdiff
path: root/internal/game/act_agility.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-25 15:40:48 -0400
committerhistoria <[not public]>2026-06-25 15:40:48 -0400
commitabd612c15799f604e671e83dc7c410ed2b44185f (patch)
tree0597ca92350de73aac2a7cf26b2f2d6595dac0cc /internal/game/act_agility.go
parent2725e2927a1595c7b100d942d1f14146252adeb7 (diff)
downloadthehouseoficarus-abd612c15799f604e671e83dc7c410ed2b44185f.tar.gz
slop refactor
Diffstat (limited to 'internal/game/act_agility.go')
-rw-r--r--internal/game/act_agility.go162
1 files changed, 162 insertions, 0 deletions
diff --git a/internal/game/act_agility.go b/internal/game/act_agility.go
new file mode 100644
index 0000000..2136699
--- /dev/null
+++ b/internal/game/act_agility.go
@@ -0,0 +1,162 @@
+package game
+
+import (
+ "fmt"
+ "math/rand"
+
+ "thehouseoficarus/internal/behavior"
+ "thehouseoficarus/internal/engine"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/player"
+)
+
+func (g *Game) startObstacle(sess *net.Session, p *player.Player, info *ObstacleInfo) {
+ failChance := g.calcFailChance(p, info)
+
+ msgs := make([]any, len(info.Messages))
+ for i, m := range info.Messages {
+ msgs[i] = m
+ }
+
+ gerund := info.Verb + "ing"
+ if g, ok := verbGerund[info.Verb]; ok {
+ gerund = g
+ }
+
+ p.Action = &behavior.Action{
+ Type: behavior.TypeObstacle,
+ TargetID: info.CourseID,
+ TargetName: info.CourseName,
+ WaitLeft: 0,
+ Data: &behavior.ObstacleData{
+ CourseID: info.CourseID,
+ Phase: 0,
+ ObstacleIndex: info.ObstacleIndex,
+ TotalObstacles: info.TotalObstacles,
+ NextRoom: info.NextRoom,
+ StartRoom: info.StartRoom,
+ ObstacleXP: info.ObstacleXP,
+ CompletionXP: info.CompletionXP,
+ FailChance: failChance,
+ FailDamageMin: info.FailDamage[0],
+ FailDamageMax: info.FailDamage[1],
+ Messages: msgs,
+ TicksPerPhase: info.TicksPerPhase,
+ RequiredLevel: info.RequiredLevel,
+ },
+ }
+
+ g.broadcastAction(sess, "\n%s starts %s on the %s.", p.Name, gerund, info.CourseName)
+}
+
+func (g *Game) advanceObstacle(sess *net.Session, p *player.Player) {
+ d := p.Action.Data.(*behavior.ObstacleData)
+ phase := d.Phase
+ messages := d.Messages
+ ticksPerPhase := d.TicksPerPhase
+
+ if phase >= len(messages) {
+ g.cancelAction(p)
+ return
+ }
+
+ msg := messages[phase].(string)
+ sess.WriteLine(g.colorize(sess, "broadcast", msg))
+
+ if phase == 1 {
+ failChance := d.FailChance
+ if rand.Float64() < failChance {
+ g.obstacleFail(sess, p, d)
+ return
+ }
+ }
+
+ nextPhase := phase + 1
+
+ if nextPhase >= len(messages) {
+ obstacleXP := d.ObstacleXP
+ completionXP := d.CompletionXP
+ nextRoom := d.NextRoom
+ startRoom := d.StartRoom
+ obstacleIndex := d.ObstacleIndex
+ totalObstacles := d.TotalObstacles
+ courseID := d.CourseID
+
+ if obstacleXP > 0 {
+ g.awardSkillXP(sess, p, player.Agility, obstacleXP)
+ if p.OptionBool("xp_drops") {
+ sess.WriteLine(g.colorize(sess, "xp",
+ fmt.Sprintf("(+%dxp %s)", obstacleXP, player.SkillAbbr[player.Agility])))
+ }
+ }
+
+ isLastObstacle := obstacleIndex == totalObstacles-1
+
+ if isLastObstacle {
+ if completionXP > 0 {
+ g.awardSkillXP(sess, p, player.Agility, completionXP)
+ if p.OptionBool("xp_drops") {
+ sess.WriteLine(g.colorize(sess, "xp",
+ fmt.Sprintf("(+%dxp %s course bonus)", completionXP, player.SkillAbbr[player.Agility])))
+ }
+ }
+
+ lapKey := "agility_laps_" + courseID
+ laps := getPlayerFlagInt(p, lapKey) + 1
+ setPlayerFlag(p, lapKey, laps)
+
+ courseName := p.Action.TargetName
+ sess.WriteLine(g.colorize(sess, "broadcast",
+ fmt.Sprintf("Course complete! %s lap %d finished.", courseName, laps)))
+
+ g.cancelAction(p)
+ g.teleportPlayer(sess, p, startRoom)
+ } else {
+ g.cancelAction(p)
+ g.teleportPlayer(sess, p, nextRoom)
+ }
+
+ g.AccountStore.SaveCharacter(p)
+ return
+ }
+
+ d.Phase = nextPhase
+ p.Action.WaitLeft = engine.ToTicks(ticksPerPhase)
+}
+
+func (g *Game) obstacleFail(sess *net.Session, p *player.Player, d *behavior.ObstacleData) {
+ startRoom := d.StartRoom
+ failMin := d.FailDamageMin
+ failMax := d.FailDamageMax
+
+ damage := failMin
+ if failMax > failMin {
+ damage = failMin + rand.Intn(failMax-failMin+1)
+ }
+
+ sess.WriteLine(g.colorize(sess, "damage_taken", "You slip and fall!"))
+
+ p.HP -= damage
+ if p.HP < 1 {
+ p.HP = 1
+ }
+ sess.WriteLine(g.colorize(sess, "damage_taken",
+ fmt.Sprintf("You take %d damage. HP: %d/%d", damage, p.HP, p.MaxHP())))
+
+ g.AccountStore.SaveCharacter(p)
+ g.cancelAction(p)
+ g.teleportPlayer(sess, p, startRoom)
+}
+
+func (g *Game) calcFailChance(p *player.Player, info *ObstacleInfo) float64 {
+ level := p.Level(player.Agility)
+ required := info.RequiredLevel
+ chance := 0.30 - float64(level-required)*0.01
+ if chance < 0.05 {
+ chance = 0.05
+ }
+ if chance > 0.60 {
+ chance = 0.60
+ }
+ return chance
+}