diff options
| author | historia <[not public]> | 2026-06-19 13:28:06 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-19 13:28:06 -0400 |
| commit | 3a58125d14bb307f861b38c6c5b0a63babde7e08 (patch) | |
| tree | bd89e866447d55e6dffd447d8ef5fabf9b8fbe9d /internal/game/action_agility.go | |
| parent | 575a71dcfed3b9fa44af244836f638a23df05a9a (diff) | |
| download | thehouseoficarus-3a58125d14bb307f861b38c6c5b0a63babde7e08.tar.gz | |
feat: all skills kind of implemented, random prototype content added
Diffstat (limited to 'internal/game/action_agility.go')
| -rw-r--r-- | internal/game/action_agility.go | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/internal/game/action_agility.go b/internal/game/action_agility.go new file mode 100644 index 0000000..089f026 --- /dev/null +++ b/internal/game/action_agility.go @@ -0,0 +1,180 @@ +package game + +import ( + "fmt" + "math/rand" + + "thehouseoficarus/internal/action" + "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 = &action.Action{ + Type: "obstacle", + TargetID: info.CourseID, + TargetName: info.CourseName, + WaitLeft: 0, + Data: map[string]any{ + "course_id": info.CourseID, + "phase": 0, + "obstacle_index": info.ObstacleIndex, + "total_obstacles": info.TotalObstacles, + "next_room": info.NextRoom, + "start_room": info.StartRoom, + "obstacle_xp": info.ObstacleXP, + "completion_xp": info.CompletionXP, + "fail_chance": failChance, + "fail_damage_min": info.FailDamage[0], + "fail_damage_max": info.FailDamage[1], + "messages": msgs, + "ticks_per_phase": info.TicksPerPhase, + "required_level": info.RequiredLevel, + }, + } + + p.ActionState = &ActionState{ + Type: ActionTraversing, + Verb: gerund, + TargetName: info.CourseName, + } + + if g.Hub != nil { + for _, other := range g.Hub.PlayersInRoom(p.RoomID) { + if other != sess && other.Player != nil { + other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s starts %s on the %s.", p.Name, gerund, info.CourseName))) + } + } + } +} + +func (g *Game) advanceObstacle(sess *net.Session, p *player.Player) { + data := p.Action.Data + phase := data["phase"].(int) + messages := data["messages"].([]any) + ticksPerPhase := data["ticks_per_phase"].(float64) + + if phase >= len(messages) { + g.CancelAction(p) + return + } + + msg := messages[phase].(string) + sess.WriteLine(g.colorize(sess, "broadcast", msg)) + + if phase == 1 { + failChance := data["fail_chance"].(float64) + if rand.Float64() < failChance { + g.obstacleFail(sess, p, data) + return + } + } + + nextPhase := phase + 1 + + if nextPhase >= len(messages) { + obstacleXP := data["obstacle_xp"].(int) + completionXP := data["completion_xp"].(int) + nextRoom := data["next_room"].(int) + startRoom := data["start_room"].(int) + obstacleIndex := data["obstacle_index"].(int) + totalObstacles := data["total_obstacles"].(int) + courseID := data["course_id"].(string) + + if obstacleXP > 0 { + if newLevel := p.AddSkillXP(player.Agility, obstacleXP); newLevel > 0 { + sess.WriteLine(g.colorize(sess, "level_up", + fmt.Sprintf("*** You are now level %d agility! ***", newLevel))) + } + 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 { + if newLevel := p.AddSkillXP(player.Agility, completionXP); newLevel > 0 { + sess.WriteLine(g.colorize(sess, "level_up", + fmt.Sprintf("*** You are now level %d agility! ***", newLevel))) + } + 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 + } + + data["phase"] = nextPhase + p.Action.WaitLeft = engine.ToTicks(ticksPerPhase) +} + +func (g *Game) obstacleFail(sess *net.Session, p *player.Player, data map[string]any) { + startRoom := data["start_room"].(int) + failMin := data["fail_damage_min"].(int) + failMax := data["fail_damage_max"].(int) + + damage := failMin + if failMax > failMin { + damage = failMin + rand.Intn(failMax-failMin+1) + } + + sess.WriteLine(g.colorize(sess, "damage", "You slip and fall!")) + + p.HP -= damage + if p.HP < 1 { + p.HP = 1 + } + sess.WriteLine(g.colorize(sess, "damage", + 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 +} |
