horse, horsegen: redesign approach for koka

This commit is contained in:
2026-01-27 08:34:11 -05:00
parent 0126101b1b
commit e890108591
11 changed files with 14312 additions and 4449 deletions

49
horse/game-id.kk Normal file
View File

@@ -0,0 +1,49 @@
module horse/game-id
// Game ID for characters, cards, skills, races, &c.
// Values for different categories may overlap.
alias game-id = int
// Specific game ID types.
// I've already made mistakes with ID categories and I haven't even committed this file yet.
// Game ID for characters.
// Generally numbers in the range 1000-9999.
pub struct character-id
game-id: game-id
// Game ID for trainees, i.e. costume instances of characters.
// Generally a character ID with two digits appended.
pub struct trainee-id
game-id: game-id
// Game ID for skills.
pub struct skill-id
game-id: game-id
// Game ID for skill groups.
pub struct skill-group-id
game-id: game-id
// Game ID for skill icons.
pub struct skill-icon-id
game-id: game-id
// order2 comparison between any game ID types.
pub inline fun order2(x: a, y: a, ?a/game-id: (a) -> game-id): order2<a>
match x.game-id.cmp(y.game-id)
Lt -> Lt2(x, y)
Eq -> Eq2(x)
Gt -> Gt2(x, y)
// Comparison between any game ID types.
pub inline fun cmp(x: a, y: a, ?a/game-id: (a) -> game-id): order
x.game-id.cmp(y.game-id)
// Equality between any game ID types.
pub inline fun (==)(x: a, y: a, ?a/game-id: (a) -> game-id): bool
x.game-id == y.game-id
// Check whether a game ID is valid, i.e. nonzero.
pub inline fun is-valid(x: a, ?a/game-id: (a) -> game-id): bool
x.game-id != 0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

104
horse/movement.kk Normal file
View File

@@ -0,0 +1,104 @@
module horse/movement
// Running styles.
pub type style
Front-Runner
Pace-Chaser
Late-Surger
End-Closer
// Automatically generated.
// Equality comparison of the `style` type.
pub fun style/(==)(this : style, other : style) : e bool
match (this, other)
(Front-Runner, Front-Runner) -> True
(Pace-Chaser, Pace-Chaser) -> True
(Late-Surger, Late-Surger) -> True
(End-Closer, End-Closer) -> True
(_, _) -> False
// Shows a string representation of the `style` type.
pub fun style/show(this : style) : e string
match this
Front-Runner -> "Front Runner"
Pace-Chaser -> "Pace Chaser"
Late-Surger -> "Late Surger"
End-Closer -> "End Closer"
// Starting aptitude levels.
pub type level
G
F
E
D
C
B
A
S
// Automatically generated.
// Comparison of the `level` type.
pub fun level/cmp(this : level, other : level) : e order
match (this, other)
(G, G) -> Eq
(G, _) -> Lt
(_, G) -> Gt
(F, F) -> Eq
(F, _) -> Lt
(_, F) -> Gt
(E, E) -> Eq
(E, _) -> Lt
(_, E) -> Gt
(D, D) -> Eq
(D, _) -> Lt
(_, D) -> Gt
(C, C) -> Eq
(C, _) -> Lt
(_, C) -> Gt
(B, B) -> Eq
(B, _) -> Lt
(_, B) -> Gt
(A, A) -> Eq
(A, _) -> Lt
(_, A) -> Gt
(S, S) -> Eq
// Automatically generated.
// Fip comparison of the `level` type.
pub fun level/order2(this : level, other : level) : order2<level>
match (this, other)
(G, G) -> Eq2(G)
(G, other') -> Lt2(G, other')
(this', G) -> Gt2(G, this')
(F, F) -> Eq2(F)
(F, other') -> Lt2(F, other')
(this', F) -> Gt2(F, this')
(E, E) -> Eq2(E)
(E, other') -> Lt2(E, other')
(this', E) -> Gt2(E, this')
(D, D) -> Eq2(D)
(D, other') -> Lt2(D, other')
(this', D) -> Gt2(D, this')
(C, C) -> Eq2(C)
(C, other') -> Lt2(C, other')
(this', C) -> Gt2(C, this')
(B, B) -> Eq2(B)
(B, other') -> Lt2(B, other')
(this', B) -> Gt2(B, this')
(A, A) -> Eq2(A)
(A, other') -> Lt2(A, other')
(this', A) -> Gt2(A, this')
(S, S) -> Eq2(S)
// Automatically generated.
// Shows a string representation of the `level` type.
pub fun level/show(this : level) : string
match this
G -> "G"
F -> "F"
E -> "E"
D -> "D"
C -> "C"
B -> "B"
A -> "A"
S -> "S"

226
horse/skill.kk Normal file
View File

@@ -0,0 +1,226 @@
module horse/skill
// This module contains skill-related definitions
// common to all versions of the game.
import std/num/decimal
import horse/game-id
import horse/movement
// Full details about a skill.
pub struct skill-detail
skill-id: skill-id
name: string
description: string
group-id: skill-group-id
rarity: rarity
group-rate: int
grade-value: int
wit-check: bool
activations: list<activation>
owner: maybe<trainee-id>
sp-cost: int
icon-id: skill-icon-id
pub fun detail(
s: skill-id,
?skill/show: (skill-id) -> string,
?skill/description: (skill-id) -> string,
?skill/group: (skill-id) -> skill-group-id,
?skill/rarity: (skill-id) -> rarity,
?skill/group-rate: (skill-id) -> int,
?skill/grade-value: (skill-id) -> int,
?skill/wit-check: (skill-id) -> bool,
?skill/activations: (skill-id) -> list<activation>,
?skill/unique-owner: (skill-id) -> maybe<trainee-id>,
?skill/sp-cost: (skill-id) -> int,
?skill/icon-id: (skill-id) -> skill-icon-id
): skill-detail
Skill-detail(
s,
s.show,
s.description,
s.group,
s.rarity,
s.group-rate,
s.grade-value,
s.wit-check,
s.activations,
s.unique-owner,
s.sp-cost,
s.icon-id
)
pub fun skill-detail/show(d: skill-detail, ?character/show: (character-id) -> string, ?trainee/show: (trainee-id) -> string): string
val Skill-detail(Skill-id(id), name, desc, _, rarity, _, grade-value, wit-check, activations, owner, sp-cost, _) = d
val r = name ++ " (ID " ++ id.show ++ "): " ++ desc ++ " " ++ activations.map(activation/show).join(". ") ++ (if wit-check then ". Wit check. " else ". No wit check. ") ++ rarity.show ++ " costing " ++ sp-cost.show ++ " SP, worth " ++ grade-value.show ++ " grade value."
match owner
Nothing -> r
Just(owner-id) -> match owner-id.show
"" -> r ++ " Unique skill of trainee with ID " ++ owner-id.show ++ "."
owner-name -> r ++ " Unique skill of " ++ owner-name ++ "."
// Skill rarity levels.
pub type rarity
Common // white
Rare // gold
Unique-Low // 1*/2* unique
Unique-Upgraded // 3*+ unique on a trainee upgraded from 1*/2*
Unique // base 3* unique
pub fun rarity/show(r: rarity): string
match r
Common -> "Common"
Rare -> "Rare"
Unique-Low -> "Unique (1\u2606/2\u2606)"
Unique-Upgraded -> "Unique (3\u2606+ from 1\u2606/2\u2606 upgraded)"
Unique -> "Unique (3\u2606+)"
// Condition and precondition logic.
pub alias condition = string
// Activation conditions and effects.
// A skill has one or two activations.
pub struct activation
precondition: condition
condition: condition
duration: decimal // seconds
cooldown: decimal // seconds
abilities: list<ability> // one to three elements
pub fun activation/show(a: activation, ?character/show: (character-id) -> string): string
match a
Activation("", condition, duration, _, abilities) | !duration.is-pos -> condition ++ " -> " ++ abilities.show
Activation("", condition, duration, cooldown, abilities) | cooldown >= 500.decimal -> condition ++ " -> for " ++ duration.show ++ "s, " ++ abilities.show
Activation("", condition, duration, cooldown, abilities) -> condition ++ " -> for " ++ duration.show ++ "s on " ++ cooldown.show ++ "s cooldown, " ++ abilities.show
Activation(precondition, condition, duration, _, abilities) | !duration.is-pos -> precondition ++ " -> " ++ condition ++ " -> " ++ abilities.show
Activation(precondition, condition, duration, cooldown, abilities) | cooldown >= 500.decimal -> precondition ++ " -> " ++ condition ++ " -> for " ++ duration.show ++ "s, " ++ abilities.show
Activation(precondition, condition, duration, cooldown, abilities) -> precondition ++ " -> " ++ condition ++ " -> for " ++ duration.show ++ "s on " ++ cooldown.show ++ "s cooldown, " ++ abilities.show
// Effects of activating a skill.
pub struct ability
ability-type: ability-type
value-usage: value-usage
target: target
pub fun ability/show(a: ability, ?character/show: (character-id) -> string): string
match a
Ability(t, Direct, Self) -> t.show
Ability(t, Direct, target) -> t.show ++ " " ++ target.show
Ability(t, v, Self) -> t.show ++ " " ++ v.show
Ability(t, v, target) -> t.show ++ " " ++ target.show ++ " " ++ v.show
// Skill ability effects.
pub type ability-type
Passive-Speed(bonus: decimal)
Passive-Stamina(bonus: decimal)
Passive-Power(bonus: decimal)
Passive-Guts(bonus: decimal)
Passive-Wit(bonus: decimal)
Great-Escape
Vision(bonus: decimal)
HP(rate: decimal)
Gate-Delay(rate: decimal)
Frenzy(add: decimal)
Current-Speed(add: decimal)
Target-Speed(add: decimal)
Lane-Speed(add: decimal)
Accel(add: decimal)
Lane-Change(add: decimal)
pub fun ability-type/show(a: ability-type): string
match a
Passive-Speed(bonus) -> bonus.show ++ " Speed"
Passive-Stamina(bonus) -> bonus.show ++ " Stamina"
Passive-Power(bonus) -> bonus.show ++ " Power"
Passive-Guts(bonus) -> bonus.show ++ " Guts"
Passive-Wit(bonus) -> bonus.show ++ " Wit"
Great-Escape -> "enable Great Escape style"
Vision(bonus) -> bonus.show ++ " vision"
HP(rate) | rate.is-pos -> show(rate * 100.decimal) ++ "% HP recovery"
HP(rate) -> show(rate * 100.decimal) ++ "% HP loss"
Gate-Delay(rate) -> rate.show ++ "× gate delay"
Frenzy(add) -> add.show ++ "s longer Rushed"
Current-Speed(rate) -> rate.show ++ "m/s current speed"
Target-Speed(rate) -> rate.show ++ "m/s target speed"
Lane-Speed(rate) -> rate.show ++ "m/s lane change speed"
Accel(rate) -> rate.show ++ "m/s² acceleration"
Lane-Change(rate) -> rate.show ++ " course width movement"
// Special scaling types for skill abilities.
pub type value-usage
Direct
Team-Speed
Team-Stamina
Team-Power
Team-Guts
Team-Wit
Multiply-Random
Multiply-Random2
Climax
Max-Stat
Passive-Count
Front-Distance-Add
Midrace-Side-Block-Time
Speed-Scaling
Speed-Scaling2
Arc-Global-Potential
Max-Lead-Distance
pub fun value-usage/show(v: value-usage): string
match v
Direct -> "with no scaling"
Team-Speed -> "scaling with team Speed"
Team-Stamina -> "scaling with team Stamina"
Team-Power -> "scaling with team Power"
Team-Guts -> "scaling with team Guts"
Team-Wit -> "scaling with team Wit"
Multiply-Random -> "scaling with a random multiplier (0×, 0.02×, or 0.04×)"
Multiply-Random2 -> "scaling with a random multiplier (0×, 0.02×, or 0.04×)"
Climax -> "scaling with the number of races won during training"
Max-Stat -> "scaling with the value of the user's highest stat"
Passive-Count -> "scaling with the number of Passive skills activated"
Front-Distance-Add -> "scaling with distance from the leader"
Midrace-Side-Block-Time -> "scaling with mid-race phase blocked side time"
Speed-Scaling -> "scaling with overall speed"
Speed-Scaling2 -> "scaling with overall speed"
Arc-Global-Potential -> "scaling with L'Arc global potential"
Max-Lead-Distance -> "scaling with the distance of the longest lead obtained in the first two thirds of the race"
// Who a skill ability targets.
pub type target
Self
Sympathizers
In-View
Frontmost(limit: int)
Ahead(limit: int)
Behind(limit: int)
All-Teammates
Style(style: style)
Rushing-Ahead(limit: int)
Rushing-Behind(limit: int)
Rushing-Style(style: style)
Specific-Character(who: character-id)
Triggering
pub fun target/show(t: target, ?character/show: (character-id) -> string): string
match t
Self -> "self"
Sympathizers -> "others with Sympathy"
In-View -> "others in field of view"
Frontmost(limit) -> "frontmost " ++ limit.show
Ahead(limit) | limit >= 18 -> "others ahead"
Ahead(limit) -> "next " ++ limit.show ++ " others ahead"
Behind(limit) | limit >= 18 -> "others behind"
Behind(limit) -> "next " ++ limit.show ++ " others behind"
All-Teammates -> "all teammates"
Style(s) -> "other " ++ s.show ++ "s"
Rushing-Ahead(limit) | limit >= 18 -> "others rushing ahead"
Rushing-Ahead(limit) -> "next " ++ limit.show ++ " others rushing ahead"
Rushing-Behind(limit) | limit >= 18 -> "others rushing behind"
Rushing-Behind(limit) -> "next " ++ limit.show ++ " others rushing behind"
Rushing-Style(s) -> "rushing " ++ s.show ++ "s"
Specific-Character(who) -> match who.show
"" -> "character with ID " ++ who.show
name -> name
Triggering -> "whosoever triggered this skill"

View File

@@ -1,70 +1,16 @@
module horse/trainee
import std/data/rb-map
import horse/movement
// Aptitudes of an umamusume being trained.
pub struct uma
turf: aptitudes
dirt: aptitudes
sprint: aptitudes
mile: aptitudes
medium: aptitudes
long: aptitudes
front-runner: aptitudes
pace-chaser: aptitudes
late-surger: aptitudes
end-closer: aptitudes
// Aptitude level distribution.
pub alias aptitudes = rbmap<level, float64>
// Starting aptitude levels.
pub type level
G
F
E
D
C
B
A
S
// Automatically generated.
// Fip comparison of the `level` type.
pub fun level/order2(this : level, other : level) : order2<level>
match (this, other)
(G, G) -> Eq2(G)
(G, other') -> Lt2(G, other')
(this', G) -> Gt2(G, this')
(F, F) -> Eq2(F)
(F, other') -> Lt2(F, other')
(this', F) -> Gt2(F, this')
(E, E) -> Eq2(E)
(E, other') -> Lt2(E, other')
(this', E) -> Gt2(E, this')
(D, D) -> Eq2(D)
(D, other') -> Lt2(D, other')
(this', D) -> Gt2(D, this')
(C, C) -> Eq2(C)
(C, other') -> Lt2(C, other')
(this', C) -> Gt2(C, this')
(B, B) -> Eq2(B)
(B, other') -> Lt2(B, other')
(this', B) -> Gt2(B, this')
(A, A) -> Eq2(A)
(A, other') -> Lt2(A, other')
(this', A) -> Gt2(A, this')
(S, S) -> Eq2(S)
// Automatically generated.
// Shows a string representation of the `level` type.
pub fun level/show(this : level) : string
match this
G -> "G"
F -> "F"
E -> "E"
D -> "D"
C -> "C"
B -> "B"
A -> "A"
S -> "S"
// Details of a trainee.
pub struct trainee-detail
turf: level
dirt: level
sprint: level
mile: level
medium: level
long: level
front-runner: level
pace-chaser: level
late-surger: level
end-closer: level