Compare commits

..

4 Commits

15 changed files with 15626 additions and 4415 deletions

8
generate.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -ex
go run ./horsegen "$@"
go generate ./horse/...
go fmt ./...
go test ./...

17
horse/character.kk Normal file
View File

@@ -0,0 +1,17 @@
module horse/character
import horse/game-id
pub struct character-detail
character-id: character-id
name: string
pub fun detail(
c: character-id,
?character/show: (character-id) -> string
): character-detail
Character-detail(c, c.show)
pub fun character-detail/show(d: character-detail): string
val Character-detail(Character-id(id), name) = d
name ++ " (ID " ++ id.show ++ ")"

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

View File

@@ -4,8 +4,11 @@ module horse/global/character
import std/core/vector import std/core/vector
import std/core-extras import std/core-extras
import std/data/rb-map
import horse/game-id
pub import horse/character
// Character identity. // Enumeration of all characters for type-safe programming.
pub type character pub type character
Special-Week Special-Week
Silence-Suzuka Silence-Suzuka
@@ -58,8 +61,62 @@ pub type character
Nice-Nature Nice-Nature
King-Halo King-Halo
// The list of all characters in order by ID, for easy iterating. // Get the character ID for a character.
pub val character/all = [ pub fun character-id(c: character): character-id
match c
Special-Week -> Character-id(1001)
Silence-Suzuka -> Character-id(1002)
Tokai-Teio -> Character-id(1003)
Maruzensky -> Character-id(1004)
Fuji-Kiseki -> Character-id(1005)
Oguri-Cap -> Character-id(1006)
Gold-Ship -> Character-id(1007)
Vodka -> Character-id(1008)
Daiwa-Scarlet -> Character-id(1009)
Taiki-Shuttle -> Character-id(1010)
Grass-Wonder -> Character-id(1011)
Hishi-Amazon -> Character-id(1012)
Mejiro-McQueen -> Character-id(1013)
El-Condor-Pasa -> Character-id(1014)
TM-Opera-O -> Character-id(1015)
Narita-Brian -> Character-id(1016)
Symboli-Rudolf -> Character-id(1017)
Air-Groove -> Character-id(1018)
Agnes-Digital -> Character-id(1019)
Seiun-Sky -> Character-id(1020)
Tamamo-Cross -> Character-id(1021)
Fine-Motion -> Character-id(1022)
Biwa-Hayahide -> Character-id(1023)
Mayano-Top-Gun -> Character-id(1024)
Manhattan-Cafe -> Character-id(1025)
Mihono-Bourbon -> Character-id(1026)
Mejiro-Ryan -> Character-id(1027)
Hishi-Akebono -> Character-id(1028)
Rice-Shower -> Character-id(1030)
Agnes-Tachyon -> Character-id(1032)
Admire-Vega -> Character-id(1033)
Inari-One -> Character-id(1034)
Winning-Ticket -> Character-id(1035)
Eishin-Flash -> Character-id(1037)
Curren-Chan -> Character-id(1038)
Kawakami-Princess -> Character-id(1039)
Gold-City -> Character-id(1040)
Sakura-Bakushin-O -> Character-id(1041)
Sweep-Tosho -> Character-id(1044)
Super-Creek -> Character-id(1045)
Smart-Falcon -> Character-id(1046)
Tosen-Jordan -> Character-id(1048)
Narita-Taishin -> Character-id(1050)
Nishino-Flower -> Character-id(1051)
Haru-Urara -> Character-id(1052)
Matikanefukukitaru -> Character-id(1056)
Meisho-Doto -> Character-id(1058)
Mejiro-Dober -> Character-id(1059)
Nice-Nature -> Character-id(1060)
King-Halo -> Character-id(1061)
// List of all characters in ID order for easy iterating.
pub val all = [
Special-Week, Special-Week,
Silence-Suzuka, Silence-Suzuka,
Tokai-Teio, Tokai-Teio,
@@ -112,429 +169,172 @@ pub val character/all = [
King-Halo, King-Halo,
] ]
// Get the character for a character ID. val name2id: rbmap<string, character-id> = rb-map/empty()
// Generally, these are four digit numbers in the range 1000-1999. .set("Special Week", Character-id(1001))
pub fun character/from-id(id: int): maybe<character> .set("Silence Suzuka", Character-id(1002))
match id .set("Tokai Teio", Character-id(1003))
1001 -> Just(Special-Week) .set("Maruzensky", Character-id(1004))
1002 -> Just(Silence-Suzuka) .set("Fuji Kiseki", Character-id(1005))
1003 -> Just(Tokai-Teio) .set("Oguri Cap", Character-id(1006))
1004 -> Just(Maruzensky) .set("Gold Ship", Character-id(1007))
1005 -> Just(Fuji-Kiseki) .set("Vodka", Character-id(1008))
1006 -> Just(Oguri-Cap) .set("Daiwa Scarlet", Character-id(1009))
1007 -> Just(Gold-Ship) .set("Taiki Shuttle", Character-id(1010))
1008 -> Just(Vodka) .set("Grass Wonder", Character-id(1011))
1009 -> Just(Daiwa-Scarlet) .set("Hishi Amazon", Character-id(1012))
1010 -> Just(Taiki-Shuttle) .set("Mejiro McQueen", Character-id(1013))
1011 -> Just(Grass-Wonder) .set("El Condor Pasa", Character-id(1014))
1012 -> Just(Hishi-Amazon) .set("T.M. Opera O", Character-id(1015))
1013 -> Just(Mejiro-McQueen) .set("Narita Brian", Character-id(1016))
1014 -> Just(El-Condor-Pasa) .set("Symboli Rudolf", Character-id(1017))
1015 -> Just(TM-Opera-O) .set("Air Groove", Character-id(1018))
1016 -> Just(Narita-Brian) .set("Agnes Digital", Character-id(1019))
1017 -> Just(Symboli-Rudolf) .set("Seiun Sky", Character-id(1020))
1018 -> Just(Air-Groove) .set("Tamamo Cross", Character-id(1021))
1019 -> Just(Agnes-Digital) .set("Fine Motion", Character-id(1022))
1020 -> Just(Seiun-Sky) .set("Biwa Hayahide", Character-id(1023))
1021 -> Just(Tamamo-Cross) .set("Mayano Top Gun", Character-id(1024))
1022 -> Just(Fine-Motion) .set("Manhattan Cafe", Character-id(1025))
1023 -> Just(Biwa-Hayahide) .set("Mihono Bourbon", Character-id(1026))
1024 -> Just(Mayano-Top-Gun) .set("Mejiro Ryan", Character-id(1027))
1025 -> Just(Manhattan-Cafe) .set("Hishi Akebono", Character-id(1028))
1026 -> Just(Mihono-Bourbon) .set("Rice Shower", Character-id(1030))
1027 -> Just(Mejiro-Ryan) .set("Agnes Tachyon", Character-id(1032))
1028 -> Just(Hishi-Akebono) .set("Admire Vega", Character-id(1033))
1030 -> Just(Rice-Shower) .set("Inari One", Character-id(1034))
1032 -> Just(Agnes-Tachyon) .set("Winning Ticket", Character-id(1035))
1033 -> Just(Admire-Vega) .set("Eishin Flash", Character-id(1037))
1034 -> Just(Inari-One) .set("Curren Chan", Character-id(1038))
1035 -> Just(Winning-Ticket) .set("Kawakami Princess", Character-id(1039))
1037 -> Just(Eishin-Flash) .set("Gold City", Character-id(1040))
1038 -> Just(Curren-Chan) .set("Sakura Bakushin O", Character-id(1041))
1039 -> Just(Kawakami-Princess) .set("Sweep Tosho", Character-id(1044))
1040 -> Just(Gold-City) .set("Super Creek", Character-id(1045))
1041 -> Just(Sakura-Bakushin-O) .set("Smart Falcon", Character-id(1046))
1044 -> Just(Sweep-Tosho) .set("Tosen Jordan", Character-id(1048))
1045 -> Just(Super-Creek) .set("Narita Taishin", Character-id(1050))
1046 -> Just(Smart-Falcon) .set("Nishino Flower", Character-id(1051))
1048 -> Just(Tosen-Jordan) .set("Haru Urara", Character-id(1052))
1050 -> Just(Narita-Taishin) .set("Matikanefukukitaru", Character-id(1056))
1051 -> Just(Nishino-Flower) .set("Meisho Doto", Character-id(1058))
1052 -> Just(Haru-Urara) .set("Mejiro Dober", Character-id(1059))
1056 -> Just(Matikanefukukitaru) .set("Nice Nature", Character-id(1060))
1058 -> Just(Meisho-Doto) .set("King Halo", Character-id(1061))
1059 -> Just(Mejiro-Dober)
1060 -> Just(Nice-Nature)
1061 -> Just(King-Halo)
_ -> Nothing
// Get the ID for a character. // Get the character ID that has the given exact name.
pub fun character/character-id(c: character): int // If no character matches the name, the result is an invalid ID.
match c pub fun from-name(name: string): character-id
Special-Week -> 1001 name2id.lookup(name).default(Character-id(0))
Silence-Suzuka -> 1002
Tokai-Teio -> 1003
Maruzensky -> 1004
Fuji-Kiseki -> 1005
Oguri-Cap -> 1006
Gold-Ship -> 1007
Vodka -> 1008
Daiwa-Scarlet -> 1009
Taiki-Shuttle -> 1010
Grass-Wonder -> 1011
Hishi-Amazon -> 1012
Mejiro-McQueen -> 1013
El-Condor-Pasa -> 1014
TM-Opera-O -> 1015
Narita-Brian -> 1016
Symboli-Rudolf -> 1017
Air-Groove -> 1018
Agnes-Digital -> 1019
Seiun-Sky -> 1020
Tamamo-Cross -> 1021
Fine-Motion -> 1022
Biwa-Hayahide -> 1023
Mayano-Top-Gun -> 1024
Manhattan-Cafe -> 1025
Mihono-Bourbon -> 1026
Mejiro-Ryan -> 1027
Hishi-Akebono -> 1028
Rice-Shower -> 1030
Agnes-Tachyon -> 1032
Admire-Vega -> 1033
Inari-One -> 1034
Winning-Ticket -> 1035
Eishin-Flash -> 1037
Curren-Chan -> 1038
Kawakami-Princess -> 1039
Gold-City -> 1040
Sakura-Bakushin-O -> 1041
Sweep-Tosho -> 1044
Super-Creek -> 1045
Smart-Falcon -> 1046
Tosen-Jordan -> 1048
Narita-Taishin -> 1050
Nishino-Flower -> 1051
Haru-Urara -> 1052
Matikanefukukitaru -> 1056
Meisho-Doto -> 1058
Mejiro-Dober -> 1059
Nice-Nature -> 1060
King-Halo -> 1061
// Get the name of a character. // Get the name for a character.
pub fun character/show(c: character): string // If no character matches the ID, the result is the numeric ID.
match c pub fun show(c: character-id): string
Special-Week -> "Special Week" match c.game-id
Silence-Suzuka -> "Silence Suzuka" 1001 -> "Special Week"
Tokai-Teio -> "Tokai Teio" 1002 -> "Silence Suzuka"
Maruzensky -> "Maruzensky" 1003 -> "Tokai Teio"
Fuji-Kiseki -> "Fuji Kiseki" 1004 -> "Maruzensky"
Oguri-Cap -> "Oguri Cap" 1005 -> "Fuji Kiseki"
Gold-Ship -> "Gold Ship" 1006 -> "Oguri Cap"
Vodka -> "Vodka" 1007 -> "Gold Ship"
Daiwa-Scarlet -> "Daiwa Scarlet" 1008 -> "Vodka"
Taiki-Shuttle -> "Taiki Shuttle" 1009 -> "Daiwa Scarlet"
Grass-Wonder -> "Grass Wonder" 1010 -> "Taiki Shuttle"
Hishi-Amazon -> "Hishi Amazon" 1011 -> "Grass Wonder"
Mejiro-McQueen -> "Mejiro McQueen" 1012 -> "Hishi Amazon"
El-Condor-Pasa -> "El Condor Pasa" 1013 -> "Mejiro McQueen"
TM-Opera-O -> "T.M. Opera O" 1014 -> "El Condor Pasa"
Narita-Brian -> "Narita Brian" 1015 -> "T.M. Opera O"
Symboli-Rudolf -> "Symboli Rudolf" 1016 -> "Narita Brian"
Air-Groove -> "Air Groove" 1017 -> "Symboli Rudolf"
Agnes-Digital -> "Agnes Digital" 1018 -> "Air Groove"
Seiun-Sky -> "Seiun Sky" 1019 -> "Agnes Digital"
Tamamo-Cross -> "Tamamo Cross" 1020 -> "Seiun Sky"
Fine-Motion -> "Fine Motion" 1021 -> "Tamamo Cross"
Biwa-Hayahide -> "Biwa Hayahide" 1022 -> "Fine Motion"
Mayano-Top-Gun -> "Mayano Top Gun" 1023 -> "Biwa Hayahide"
Manhattan-Cafe -> "Manhattan Cafe" 1024 -> "Mayano Top Gun"
Mihono-Bourbon -> "Mihono Bourbon" 1025 -> "Manhattan Cafe"
Mejiro-Ryan -> "Mejiro Ryan" 1026 -> "Mihono Bourbon"
Hishi-Akebono -> "Hishi Akebono" 1027 -> "Mejiro Ryan"
Rice-Shower -> "Rice Shower" 1028 -> "Hishi Akebono"
Agnes-Tachyon -> "Agnes Tachyon" 1030 -> "Rice Shower"
Admire-Vega -> "Admire Vega" 1032 -> "Agnes Tachyon"
Inari-One -> "Inari One" 1033 -> "Admire Vega"
Winning-Ticket -> "Winning Ticket" 1034 -> "Inari One"
Eishin-Flash -> "Eishin Flash" 1035 -> "Winning Ticket"
Curren-Chan -> "Curren Chan" 1037 -> "Eishin Flash"
Kawakami-Princess -> "Kawakami Princess" 1038 -> "Curren Chan"
Gold-City -> "Gold City" 1039 -> "Kawakami Princess"
Sakura-Bakushin-O -> "Sakura Bakushin O" 1040 -> "Gold City"
Sweep-Tosho -> "Sweep Tosho" 1041 -> "Sakura Bakushin O"
Super-Creek -> "Super Creek" 1044 -> "Sweep Tosho"
Smart-Falcon -> "Smart Falcon" 1045 -> "Super Creek"
Tosen-Jordan -> "Tosen Jordan" 1046 -> "Smart Falcon"
Narita-Taishin -> "Narita Taishin" 1048 -> "Tosen Jordan"
Nishino-Flower -> "Nishino Flower" 1050 -> "Narita Taishin"
Haru-Urara -> "Haru Urara" 1051 -> "Nishino Flower"
Matikanefukukitaru -> "Matikanefukukitaru" 1052 -> "Haru Urara"
Meisho-Doto -> "Meisho Doto" 1056 -> "Matikanefukukitaru"
Mejiro-Dober -> "Mejiro Dober" 1058 -> "Meisho Doto"
Nice-Nature -> "Nice Nature" 1059 -> "Mejiro Dober"
King-Halo -> "King Halo" 1060 -> "Nice Nature"
1061 -> "King Halo"
x -> "character " ++ x.show
// Compare two characters. fun character/index(c: character-id): int
pub fip fun character/order2(a: character, b: character): order2<character> match c.game-id
match (a, b) 1001 -> 0
(Special-Week, Special-Week) -> Eq2(Special-Week) 1002 -> 1
(Special-Week, b') -> Lt2(Special-Week, b') 1003 -> 2
(a', Special-Week) -> Gt2(Special-Week, a') 1004 -> 3
(Silence-Suzuka, Silence-Suzuka) -> Eq2(Silence-Suzuka) 1005 -> 4
(Silence-Suzuka, b') -> Lt2(Silence-Suzuka, b') 1006 -> 5
(a', Silence-Suzuka) -> Gt2(Silence-Suzuka, a') 1007 -> 6
(Tokai-Teio, Tokai-Teio) -> Eq2(Tokai-Teio) 1008 -> 7
(Tokai-Teio, b') -> Lt2(Tokai-Teio, b') 1009 -> 8
(a', Tokai-Teio) -> Gt2(Tokai-Teio, a') 1010 -> 9
(Maruzensky, Maruzensky) -> Eq2(Maruzensky) 1011 -> 10
(Maruzensky, b') -> Lt2(Maruzensky, b') 1012 -> 11
(a', Maruzensky) -> Gt2(Maruzensky, a') 1013 -> 12
(Fuji-Kiseki, Fuji-Kiseki) -> Eq2(Fuji-Kiseki) 1014 -> 13
(Fuji-Kiseki, b') -> Lt2(Fuji-Kiseki, b') 1015 -> 14
(a', Fuji-Kiseki) -> Gt2(Fuji-Kiseki, a') 1016 -> 15
(Oguri-Cap, Oguri-Cap) -> Eq2(Oguri-Cap) 1017 -> 16
(Oguri-Cap, b') -> Lt2(Oguri-Cap, b') 1018 -> 17
(a', Oguri-Cap) -> Gt2(Oguri-Cap, a') 1019 -> 18
(Gold-Ship, Gold-Ship) -> Eq2(Gold-Ship) 1020 -> 19
(Gold-Ship, b') -> Lt2(Gold-Ship, b') 1021 -> 20
(a', Gold-Ship) -> Gt2(Gold-Ship, a') 1022 -> 21
(Vodka, Vodka) -> Eq2(Vodka) 1023 -> 22
(Vodka, b') -> Lt2(Vodka, b') 1024 -> 23
(a', Vodka) -> Gt2(Vodka, a') 1025 -> 24
(Daiwa-Scarlet, Daiwa-Scarlet) -> Eq2(Daiwa-Scarlet) 1026 -> 25
(Daiwa-Scarlet, b') -> Lt2(Daiwa-Scarlet, b') 1027 -> 26
(a', Daiwa-Scarlet) -> Gt2(Daiwa-Scarlet, a') 1028 -> 27
(Taiki-Shuttle, Taiki-Shuttle) -> Eq2(Taiki-Shuttle) 1030 -> 28
(Taiki-Shuttle, b') -> Lt2(Taiki-Shuttle, b') 1032 -> 29
(a', Taiki-Shuttle) -> Gt2(Taiki-Shuttle, a') 1033 -> 30
(Grass-Wonder, Grass-Wonder) -> Eq2(Grass-Wonder) 1034 -> 31
(Grass-Wonder, b') -> Lt2(Grass-Wonder, b') 1035 -> 32
(a', Grass-Wonder) -> Gt2(Grass-Wonder, a') 1037 -> 33
(Hishi-Amazon, Hishi-Amazon) -> Eq2(Hishi-Amazon) 1038 -> 34
(Hishi-Amazon, b') -> Lt2(Hishi-Amazon, b') 1039 -> 35
(a', Hishi-Amazon) -> Gt2(Hishi-Amazon, a') 1040 -> 36
(Mejiro-McQueen, Mejiro-McQueen) -> Eq2(Mejiro-McQueen) 1041 -> 37
(Mejiro-McQueen, b') -> Lt2(Mejiro-McQueen, b') 1044 -> 38
(a', Mejiro-McQueen) -> Gt2(Mejiro-McQueen, a') 1045 -> 39
(El-Condor-Pasa, El-Condor-Pasa) -> Eq2(El-Condor-Pasa) 1046 -> 40
(El-Condor-Pasa, b') -> Lt2(El-Condor-Pasa, b') 1048 -> 41
(a', El-Condor-Pasa) -> Gt2(El-Condor-Pasa, a') 1050 -> 42
(TM-Opera-O, TM-Opera-O) -> Eq2(TM-Opera-O) 1051 -> 43
(TM-Opera-O, b') -> Lt2(TM-Opera-O, b') 1052 -> 44
(a', TM-Opera-O) -> Gt2(TM-Opera-O, a') 1056 -> 45
(Narita-Brian, Narita-Brian) -> Eq2(Narita-Brian) 1058 -> 46
(Narita-Brian, b') -> Lt2(Narita-Brian, b') 1059 -> 47
(a', Narita-Brian) -> Gt2(Narita-Brian, a') 1060 -> 48
(Symboli-Rudolf, Symboli-Rudolf) -> Eq2(Symboli-Rudolf) 1061 -> 49
(Symboli-Rudolf, b') -> Lt2(Symboli-Rudolf, b') _ -> -99999999
(a', Symboli-Rudolf) -> Gt2(Symboli-Rudolf, a')
(Air-Groove, Air-Groove) -> Eq2(Air-Groove)
(Air-Groove, b') -> Lt2(Air-Groove, b')
(a', Air-Groove) -> Gt2(Air-Groove, a')
(Agnes-Digital, Agnes-Digital) -> Eq2(Agnes-Digital)
(Agnes-Digital, b') -> Lt2(Agnes-Digital, b')
(a', Agnes-Digital) -> Gt2(Agnes-Digital, a')
(Seiun-Sky, Seiun-Sky) -> Eq2(Seiun-Sky)
(Seiun-Sky, b') -> Lt2(Seiun-Sky, b')
(a', Seiun-Sky) -> Gt2(Seiun-Sky, a')
(Tamamo-Cross, Tamamo-Cross) -> Eq2(Tamamo-Cross)
(Tamamo-Cross, b') -> Lt2(Tamamo-Cross, b')
(a', Tamamo-Cross) -> Gt2(Tamamo-Cross, a')
(Fine-Motion, Fine-Motion) -> Eq2(Fine-Motion)
(Fine-Motion, b') -> Lt2(Fine-Motion, b')
(a', Fine-Motion) -> Gt2(Fine-Motion, a')
(Biwa-Hayahide, Biwa-Hayahide) -> Eq2(Biwa-Hayahide)
(Biwa-Hayahide, b') -> Lt2(Biwa-Hayahide, b')
(a', Biwa-Hayahide) -> Gt2(Biwa-Hayahide, a')
(Mayano-Top-Gun, Mayano-Top-Gun) -> Eq2(Mayano-Top-Gun)
(Mayano-Top-Gun, b') -> Lt2(Mayano-Top-Gun, b')
(a', Mayano-Top-Gun) -> Gt2(Mayano-Top-Gun, a')
(Manhattan-Cafe, Manhattan-Cafe) -> Eq2(Manhattan-Cafe)
(Manhattan-Cafe, b') -> Lt2(Manhattan-Cafe, b')
(a', Manhattan-Cafe) -> Gt2(Manhattan-Cafe, a')
(Mihono-Bourbon, Mihono-Bourbon) -> Eq2(Mihono-Bourbon)
(Mihono-Bourbon, b') -> Lt2(Mihono-Bourbon, b')
(a', Mihono-Bourbon) -> Gt2(Mihono-Bourbon, a')
(Mejiro-Ryan, Mejiro-Ryan) -> Eq2(Mejiro-Ryan)
(Mejiro-Ryan, b') -> Lt2(Mejiro-Ryan, b')
(a', Mejiro-Ryan) -> Gt2(Mejiro-Ryan, a')
(Hishi-Akebono, Hishi-Akebono) -> Eq2(Hishi-Akebono)
(Hishi-Akebono, b') -> Lt2(Hishi-Akebono, b')
(a', Hishi-Akebono) -> Gt2(Hishi-Akebono, a')
(Rice-Shower, Rice-Shower) -> Eq2(Rice-Shower)
(Rice-Shower, b') -> Lt2(Rice-Shower, b')
(a', Rice-Shower) -> Gt2(Rice-Shower, a')
(Agnes-Tachyon, Agnes-Tachyon) -> Eq2(Agnes-Tachyon)
(Agnes-Tachyon, b') -> Lt2(Agnes-Tachyon, b')
(a', Agnes-Tachyon) -> Gt2(Agnes-Tachyon, a')
(Admire-Vega, Admire-Vega) -> Eq2(Admire-Vega)
(Admire-Vega, b') -> Lt2(Admire-Vega, b')
(a', Admire-Vega) -> Gt2(Admire-Vega, a')
(Inari-One, Inari-One) -> Eq2(Inari-One)
(Inari-One, b') -> Lt2(Inari-One, b')
(a', Inari-One) -> Gt2(Inari-One, a')
(Winning-Ticket, Winning-Ticket) -> Eq2(Winning-Ticket)
(Winning-Ticket, b') -> Lt2(Winning-Ticket, b')
(a', Winning-Ticket) -> Gt2(Winning-Ticket, a')
(Eishin-Flash, Eishin-Flash) -> Eq2(Eishin-Flash)
(Eishin-Flash, b') -> Lt2(Eishin-Flash, b')
(a', Eishin-Flash) -> Gt2(Eishin-Flash, a')
(Curren-Chan, Curren-Chan) -> Eq2(Curren-Chan)
(Curren-Chan, b') -> Lt2(Curren-Chan, b')
(a', Curren-Chan) -> Gt2(Curren-Chan, a')
(Kawakami-Princess, Kawakami-Princess) -> Eq2(Kawakami-Princess)
(Kawakami-Princess, b') -> Lt2(Kawakami-Princess, b')
(a', Kawakami-Princess) -> Gt2(Kawakami-Princess, a')
(Gold-City, Gold-City) -> Eq2(Gold-City)
(Gold-City, b') -> Lt2(Gold-City, b')
(a', Gold-City) -> Gt2(Gold-City, a')
(Sakura-Bakushin-O, Sakura-Bakushin-O) -> Eq2(Sakura-Bakushin-O)
(Sakura-Bakushin-O, b') -> Lt2(Sakura-Bakushin-O, b')
(a', Sakura-Bakushin-O) -> Gt2(Sakura-Bakushin-O, a')
(Sweep-Tosho, Sweep-Tosho) -> Eq2(Sweep-Tosho)
(Sweep-Tosho, b') -> Lt2(Sweep-Tosho, b')
(a', Sweep-Tosho) -> Gt2(Sweep-Tosho, a')
(Super-Creek, Super-Creek) -> Eq2(Super-Creek)
(Super-Creek, b') -> Lt2(Super-Creek, b')
(a', Super-Creek) -> Gt2(Super-Creek, a')
(Smart-Falcon, Smart-Falcon) -> Eq2(Smart-Falcon)
(Smart-Falcon, b') -> Lt2(Smart-Falcon, b')
(a', Smart-Falcon) -> Gt2(Smart-Falcon, a')
(Tosen-Jordan, Tosen-Jordan) -> Eq2(Tosen-Jordan)
(Tosen-Jordan, b') -> Lt2(Tosen-Jordan, b')
(a', Tosen-Jordan) -> Gt2(Tosen-Jordan, a')
(Narita-Taishin, Narita-Taishin) -> Eq2(Narita-Taishin)
(Narita-Taishin, b') -> Lt2(Narita-Taishin, b')
(a', Narita-Taishin) -> Gt2(Narita-Taishin, a')
(Nishino-Flower, Nishino-Flower) -> Eq2(Nishino-Flower)
(Nishino-Flower, b') -> Lt2(Nishino-Flower, b')
(a', Nishino-Flower) -> Gt2(Nishino-Flower, a')
(Haru-Urara, Haru-Urara) -> Eq2(Haru-Urara)
(Haru-Urara, b') -> Lt2(Haru-Urara, b')
(a', Haru-Urara) -> Gt2(Haru-Urara, a')
(Matikanefukukitaru, Matikanefukukitaru) -> Eq2(Matikanefukukitaru)
(Matikanefukukitaru, b') -> Lt2(Matikanefukukitaru, b')
(a', Matikanefukukitaru) -> Gt2(Matikanefukukitaru, a')
(Meisho-Doto, Meisho-Doto) -> Eq2(Meisho-Doto)
(Meisho-Doto, b') -> Lt2(Meisho-Doto, b')
(a', Meisho-Doto) -> Gt2(Meisho-Doto, a')
(Mejiro-Dober, Mejiro-Dober) -> Eq2(Mejiro-Dober)
(Mejiro-Dober, b') -> Lt2(Mejiro-Dober, b')
(a', Mejiro-Dober) -> Gt2(Mejiro-Dober, a')
(Nice-Nature, Nice-Nature) -> Eq2(Nice-Nature)
(Nice-Nature, b') -> Lt2(Nice-Nature, b')
(a', Nice-Nature) -> Gt2(Nice-Nature, a')
(King-Halo, King-Halo) -> Eq2(King-Halo)
// Character equality.
pub fun character/(==)(a: character, b: character): bool
match (a, b)
(Special-Week, Special-Week) -> True
(Silence-Suzuka, Silence-Suzuka) -> True
(Tokai-Teio, Tokai-Teio) -> True
(Maruzensky, Maruzensky) -> True
(Fuji-Kiseki, Fuji-Kiseki) -> True
(Oguri-Cap, Oguri-Cap) -> True
(Gold-Ship, Gold-Ship) -> True
(Vodka, Vodka) -> True
(Daiwa-Scarlet, Daiwa-Scarlet) -> True
(Taiki-Shuttle, Taiki-Shuttle) -> True
(Grass-Wonder, Grass-Wonder) -> True
(Hishi-Amazon, Hishi-Amazon) -> True
(Mejiro-McQueen, Mejiro-McQueen) -> True
(El-Condor-Pasa, El-Condor-Pasa) -> True
(TM-Opera-O, TM-Opera-O) -> True
(Narita-Brian, Narita-Brian) -> True
(Symboli-Rudolf, Symboli-Rudolf) -> True
(Air-Groove, Air-Groove) -> True
(Agnes-Digital, Agnes-Digital) -> True
(Seiun-Sky, Seiun-Sky) -> True
(Tamamo-Cross, Tamamo-Cross) -> True
(Fine-Motion, Fine-Motion) -> True
(Biwa-Hayahide, Biwa-Hayahide) -> True
(Mayano-Top-Gun, Mayano-Top-Gun) -> True
(Manhattan-Cafe, Manhattan-Cafe) -> True
(Mihono-Bourbon, Mihono-Bourbon) -> True
(Mejiro-Ryan, Mejiro-Ryan) -> True
(Hishi-Akebono, Hishi-Akebono) -> True
(Rice-Shower, Rice-Shower) -> True
(Agnes-Tachyon, Agnes-Tachyon) -> True
(Admire-Vega, Admire-Vega) -> True
(Inari-One, Inari-One) -> True
(Winning-Ticket, Winning-Ticket) -> True
(Eishin-Flash, Eishin-Flash) -> True
(Curren-Chan, Curren-Chan) -> True
(Kawakami-Princess, Kawakami-Princess) -> True
(Gold-City, Gold-City) -> True
(Sakura-Bakushin-O, Sakura-Bakushin-O) -> True
(Sweep-Tosho, Sweep-Tosho) -> True
(Super-Creek, Super-Creek) -> True
(Smart-Falcon, Smart-Falcon) -> True
(Tosen-Jordan, Tosen-Jordan) -> True
(Narita-Taishin, Narita-Taishin) -> True
(Nishino-Flower, Nishino-Flower) -> True
(Haru-Urara, Haru-Urara) -> True
(Matikanefukukitaru, Matikanefukukitaru) -> True
(Meisho-Doto, Meisho-Doto) -> True
(Mejiro-Dober, Mejiro-Dober) -> True
(Nice-Nature, Nice-Nature) -> True
(King-Halo, King-Halo) -> True
_ -> False
fip fun character/index(^c: character): int
match c
Special-Week -> 0
Silence-Suzuka -> 1
Tokai-Teio -> 2
Maruzensky -> 3
Fuji-Kiseki -> 4
Oguri-Cap -> 5
Gold-Ship -> 6
Vodka -> 7
Daiwa-Scarlet -> 8
Taiki-Shuttle -> 9
Grass-Wonder -> 10
Hishi-Amazon -> 11
Mejiro-McQueen -> 12
El-Condor-Pasa -> 13
TM-Opera-O -> 14
Narita-Brian -> 15
Symboli-Rudolf -> 16
Air-Groove -> 17
Agnes-Digital -> 18
Seiun-Sky -> 19
Tamamo-Cross -> 20
Fine-Motion -> 21
Biwa-Hayahide -> 22
Mayano-Top-Gun -> 23
Manhattan-Cafe -> 24
Mihono-Bourbon -> 25
Mejiro-Ryan -> 26
Hishi-Akebono -> 27
Rice-Shower -> 28
Agnes-Tachyon -> 29
Admire-Vega -> 30
Inari-One -> 31
Winning-Ticket -> 32
Eishin-Flash -> 33
Curren-Chan -> 34
Kawakami-Princess -> 35
Gold-City -> 36
Sakura-Bakushin-O -> 37
Sweep-Tosho -> 38
Super-Creek -> 39
Smart-Falcon -> 40
Tosen-Jordan -> 41
Narita-Taishin -> 42
Nishino-Flower -> 43
Haru-Urara -> 44
Matikanefukukitaru -> 45
Meisho-Doto -> 46
Mejiro-Dober -> 47
Nice-Nature -> 48
King-Halo -> 49
// Create the table of all pair affinities. // Create the table of all pair affinities.
// The affinity is the value at a.index*count + b.index. // The affinity is the value at a.index*count + b.index.
@@ -544,7 +344,7 @@ extern global/create-pair-table(): vector<int>
val global/pair-table = global/create-pair-table() val global/pair-table = global/create-pair-table()
// Base affinity between a pair using the global ruleset. // Base affinity between a pair using the global ruleset.
pub fun global/pair-affinity(a: character, b: character): int pub fun global/pair-affinity(a: character-id, b: character-id): int
global/pair-table.at(a.index * 50 + b.index).default(0) global/pair-table.at(a.index * 50 + b.index).default(0)
// Create the table of all trio affinities. // Create the table of all trio affinities.
@@ -555,5 +355,5 @@ extern global/create-trio-table(): vector<int>
val global/trio-table = global/create-trio-table() val global/trio-table = global/create-trio-table()
// Base affinity for a trio using the global ruleset. // Base affinity for a trio using the global ruleset.
pub fun global/trio-affinity(a: character, b: character, c: character): int pub fun global/trio-affinity(a: character-id, b: character-id, c: character-id): int
global/trio-table.at(a.index * 50 * 50 + b.index * 50 + c.index).default(0) global/trio-table.at(a.index * 50 * 50 + b.index * 50 + c.index).default(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 module horse/trainee
import std/data/rb-map import horse/movement
// Aptitudes of an umamusume being trained. // Details of a trainee.
pub struct uma pub struct trainee-detail
turf: aptitudes turf: level
dirt: aptitudes dirt: level
sprint: aptitudes sprint: level
mile: aptitudes mile: level
medium: aptitudes medium: level
long: aptitudes long: level
front-runner: aptitudes front-runner: level
pace-chaser: aptitudes pace-chaser: level
late-surger: aptitudes late-surger: level
end-closer: aptitudes end-closer: level
// 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"

View File

@@ -5,67 +5,55 @@ module horse/{{ $.Region }}/character
import std/core/vector import std/core/vector
import std/core-extras import std/core-extras
import std/data/rb-map
import horse/game-id
pub import horse/character
// Character identity. // Enumeration of all characters for type-safe programming.
pub type character pub type character
{{- range $uma := $.Characters }} {{- range $uma := $.Characters }}
{{ kkenum $uma.Name }} {{ kkenum $uma.Name }}
{{- end }} {{- end }}
// The list of all characters in order by ID, for easy iterating. // Get the character ID for a character.
pub val character/all = [ pub fun character-id(c: character): character-id
match c
{{- range $uma := $.Characters }}
{{ kkenum $uma.Name }} -> Character-id({{ $uma.ID }})
{{- end }}
// List of all characters in ID order for easy iterating.
pub val all = [
{{- range $uma := $.Characters }} {{- range $uma := $.Characters }}
{{ kkenum $uma.Name }}, {{ kkenum $uma.Name }},
{{- end }} {{- end }}
] ]
// Get the character for a character ID. val name2id: rbmap<string, character-id> = rb-map/empty()
// Generally, these are four digit numbers in the range 1000-1999. {{- range $uma := $.Characters }}
pub fun character/from-id(id: int): maybe<character> .set({{ printf "%q" $uma.Name }}, Character-id({{ $uma.ID}}))
match id {{- end }}
// Get the character ID that has the given exact name.
// If no character matches the name, the result is an invalid ID.
pub fun from-name(name: string): character-id
name2id.lookup(name).default(Character-id(0))
// Get the name for a character.
// If no character matches the ID, the result is the numeric ID.
pub fun show(c: character-id): string
match c.game-id
{{- range $uma := $.Characters }} {{- range $uma := $.Characters }}
{{ $uma.ID }} -> Just( {{- kkenum $uma.Name -}} ) {{ $uma.ID }} -> {{ printf "%q" $uma.Name }}
{{- end }} {{- end }}
_ -> Nothing x -> "character " ++ x.show
// Get the ID for a character. fun character/index(c: character-id): int
pub fun character/character-id(c: character): int match c.game-id
match c
{{- range $uma := $.Characters }} {{- range $uma := $.Characters }}
{{ kkenum $uma.Name }} -> {{ $uma.ID }} {{ $uma.ID }} -> {{ $uma.Index }}
{{- end }}
// Get the name of a character.
pub fun character/show(c: character): string
match c
{{- range $uma := $.Characters }}
{{ kkenum $uma.Name }} -> {{ printf "%q" $uma.Name }}
{{- end }}
// Compare two characters.
pub fip fun character/order2(a: character, b: character): order2<character>
match (a, b)
{{- range $uma := $.Characters }}{{ $e := kkenum $uma.Name }}
( {{- $e }}, {{ $e -}} ) -> Eq2( {{- $e -}} )
{{- if ne $uma.ID $.MaxID }}
( {{- $e }}, b') -> Lt2( {{- $e }}, b')
(a', {{ $e -}} ) -> Gt2( {{- $e }}, a')
{{- end }}
{{- end }}
// Character equality.
pub fun character/(==)(a: character, b: character): bool
match (a, b)
{{- range $uma := $.Characters }}{{ $e := kkenum $uma.Name }}
( {{- $e }}, {{ $e -}} ) -> True
{{- end }}
_ -> False
fip fun character/index(^c: character): int
match c
{{- range $uma := $.Characters }}
{{ kkenum $uma.Name }} -> {{ $uma.Index }}
{{- end }} {{- end }}
_ -> -99999999
// Create the table of all pair affinities. // Create the table of all pair affinities.
// The affinity is the value at a.index*count + b.index. // The affinity is the value at a.index*count + b.index.
@@ -87,7 +75,7 @@ extern global/create-pair-table(): vector<int>
val global/pair-table = global/create-pair-table() val global/pair-table = global/create-pair-table()
// Base affinity between a pair using the global ruleset. // Base affinity between a pair using the global ruleset.
pub fun global/pair-affinity(a: character, b: character): int pub fun global/pair-affinity(a: character-id, b: character-id): int
global/pair-table.at(a.index * {{ $.Count }} + b.index).default(0) global/pair-table.at(a.index * {{ $.Count }} + b.index).default(0)
// Create the table of all trio affinities. // Create the table of all trio affinities.
@@ -114,7 +102,7 @@ extern global/create-trio-table(): vector<int>
val global/trio-table = global/create-trio-table() val global/trio-table = global/create-trio-table()
// Base affinity for a trio using the global ruleset. // Base affinity for a trio using the global ruleset.
pub fun global/trio-affinity(a: character, b: character, c: character): int pub fun global/trio-affinity(a: character-id, b: character-id, c: character-id): int
global/trio-table.at(a.index * {{ $.Count }} * {{ $.Count }} + b.index * {{ $.Count }} + c.index).default(0) global/trio-table.at(a.index * {{ $.Count }} * {{ $.Count }} + b.index * {{ $.Count }} + c.index).default(0)
{{- end }} {{- end }}

View File

@@ -93,15 +93,6 @@ func ExecSkill(t *template.Template, region string, kk, g io.Writer, groups []Na
return err return err
} }
func ExecSkillGroupKK(t *template.Template, region string, w io.Writer, g []NamedID[SkillGroup], s []Skill) error {
data := struct {
Region string
Groups []NamedID[SkillGroup]
Skills []Skill
}{region, g, s}
return t.ExecuteTemplate(w, "koka-skill-group", &data)
}
const wordSeps = " ,!?/-+();#○☆♡'=♪∀゚∴" const wordSeps = " ,!?/-+();#○☆♡'=♪∀゚∴"
var ( var (
@@ -159,6 +150,7 @@ func kkenum(name string) string {
} }
name = strings.ToUpper(name[:1]) + name[1:] name = strings.ToUpper(name[:1]) + name[1:]
if !unicode.IsLetter(rune(name[0])) { if !unicode.IsLetter(rune(name[0])) {
//lint:ignore ST1005 proper name
panic(fmt.Errorf("Koka enum variant %q (from %q) starts with a non-letter", name, orig)) panic(fmt.Errorf("Koka enum variant %q (from %q) starts with a non-letter", name, orig))
} }
for _, c := range name { for _, c := range name {

View File

@@ -179,21 +179,22 @@ func SkillGroups(ctx context.Context, db *sqlitex.Pool) ([]NamedID[SkillGroup],
} }
type Skill struct { type Skill struct {
ID int ID int
Name string Name string
Description string Description string
GroupID int GroupID int
GroupName string GroupName string
Rarity int Rarity int
GroupRate int GroupRate int
GradeValue int GradeValue int
WitCheck bool WitCheck bool
Activations [2]SkillActivation Activations [2]SkillActivation
SPCost int SPCost int
InheritID int InheritID int
UniqueOwner string UniqueOwnerID int
IconID int UniqueOwner string
Index int IconID int
Index int
} }
type SkillActivation struct { type SkillActivation struct {
@@ -303,11 +304,12 @@ func Skills(ctx context.Context, db *sqlitex.Pool) ([]Skill, error) {
}, },
}, },
}, },
SPCost: stmt.ColumnInt(47), SPCost: stmt.ColumnInt(47),
InheritID: stmt.ColumnInt(48), InheritID: stmt.ColumnInt(48),
UniqueOwner: stmt.ColumnText(49), UniqueOwnerID: stmt.ColumnInt(49),
IconID: stmt.ColumnInt(50), UniqueOwner: stmt.ColumnText(50),
Index: stmt.ColumnInt(51), IconID: stmt.ColumnInt(51),
Index: stmt.ColumnInt(52),
} }
r = append(r, s) r = append(r, s)
} }

View File

@@ -20,7 +20,7 @@ func main() {
region string region string
) )
flag.StringVar(&mdb, "mdb", os.ExpandEnv(`$USERPROFILE\AppData\LocalLow\Cygames\Umamusume\master\master.mdb`), "`path` to Umamusume master.mdb") flag.StringVar(&mdb, "mdb", os.ExpandEnv(`$USERPROFILE\AppData\LocalLow\Cygames\Umamusume\master\master.mdb`), "`path` to Umamusume master.mdb")
flag.StringVar(&out, "o", `.\horse`, "`dir`ectory for output files") flag.StringVar(&out, "o", `horse`, "`dir`ectory for output files")
flag.StringVar(&region, "region", "global", "region the database is for (global, jp)") flag.StringVar(&region, "region", "global", "region the database is for (global, jp)")
flag.Parse() flag.Parse()
@@ -110,16 +110,11 @@ func main() {
return err return err
} }
gf, err := os.Create(filepath.Join(out, region, "skill.go")) gf, err := os.Create(filepath.Join(out, region, "skill.go"))
slog.Info("write skills")
return ExecSkill(t, region, sf, gf, sg, skills)
})
eg.Go(func() error {
sf, err := os.Create(filepath.Join(out, region, "skill-group.kk"))
if err != nil { if err != nil {
return err return err
} }
slog.Info("write skill groups") slog.Info("write skills")
return ExecSkillGroupKK(t, region, sf, sg, skills) return ExecSkill(t, region, sf, gf, sg, skills)
}) })
if err := eg.Wait(); err != nil { if err := eg.Wait(); err != nil {
slog.Error("generate", slog.Any("err", err)) slog.Error("generate", slog.Any("err", err))

View File

@@ -1,347 +1,233 @@
{{- define "koka-skill-group" -}}
module horse/{{ $.Region }}/skill-group
// Automatically generated with horsegen; DO NOT EDIT
// Skill groups.
// A skill group may contain white, circle, double-circle, gold, and purple skills
// for the same effect.
// Sparks that grant skills refer to a skill group.
pub type skill-group
{{- range $g := $.Groups }}
{{ kkenum $g.Name }}
{{- end }}
// Map a skill group to its ID.
pub fip fun skill-group/group-id(^sg: skill-group): int
match sg
{{- range $g := $.Groups }}
{{ kkenum $g.Name }} -> {{ $g.ID }}
{{- end }}
// Get the skill group for an ID.
pub fip(1) fun skill-group/from-id(^id: int): maybe<skill-group>
match id
{{- range $g := $.Groups }}
{{ $g.ID }} -> Just( {{- kkenum $g.Name -}} )
{{- end }}
_ -> Nothing
// Get the name for a skill group.
// Skill group names are the name of the base skill in the group.
pub fun skill-group/show(sg: skill-group): string
match sg
{{- range $g := $.Groups }}
{{ kkenum $g.Name }} -> {{ printf "%q" $g.Name }}
{{- end }}
// Compare two skill groups by ID order.
pub fip fun skill-group/order2(a: skill-group, b: skill-group): order2<skill-group>
match cmp(a.group-id, b.group-id)
Lt -> Lt2(a, b)
Eq -> Eq2(a)
Gt -> Gt2(a, b)
pub fun skill-group/(==)(a: skill-group, b: skill-group): bool
a.group-id == b.group-id
{{- end -}}
{{- define "koka-skill" -}} {{- define "koka-skill" -}}
module horse/{{ $.Region }}/skill module horse/{{ $.Region }}/skill
// Automatically generated with horsegen; DO NOT EDIT // Automatically generated with horsegen; DO NOT EDIT
import std/data/rb-map
import std/num/decimal import std/num/decimal
pub import horse/{{ $.Region }}/skill-group import horse/game-id
import horse/movement
pub import horse/skill
// Skill instances. // Enumeration of all skills for type-safe programming.
pub type skill pub type skill
{{- range $s := $.Skills }} {{- range $s := $.Skills }}
{{ kkenum $s.Name }}{{ if ne $s.InheritID 0 }}-Inherit{{ end -}} {{ kkenum $s.Name }}{{ if $s.InheritID }}-Inherit{{ end }}
{{- end }} {{- end }}
// Map a skill to its ID. // Get the skill ID for a skill.
pub fip fun skill/skill-id(^s: skill): int pub fun skill-id(s: skill): skill-id
match s match s
{{- range $s := $.Skills }} {{- range $s := $.Skills }}
{{ kkenum $s.Name }}{{ if ne $s.InheritID 0 }}-Inherit{{ end }} -> {{ $s.ID }} {{ kkenum $s.Name }}{{ if $s.InheritID }}-Inherit{{ end }} -> Skill-id({{ $s.ID }})
{{- end }} {{- end }}
// Get the skill for an ID. // List of all skills in ID order for easy iterating.
pub fip(1) fun skill/from-id(^id: int): maybe<skill> pub val all = [
match id {{- range $s := $.Skills }}
{{ kkenum $s.Name }}{{ if $s.InheritID }}-Inherit{{ end }},
{{- end }}
]
val name2id: rbmap<string, skill-id> = rb-map/empty()
{{- range $s := $.Skills }}
.set({{ printf "%q" $s.Name }}{{ if $s.InheritID }} ++ " (Inherited)"{{ end }}, Skill-id({{ $s.ID }}))
{{- end }}
// Get the skill ID that has the given exact name.
// Inherited skills have `" (Inherited)"` appended to their names.
// If no skill matches the name, the result is an invalid ID.
pub fun from-name(name: string): skill-id
name2id.lookup(name).default(Skill-id(0))
// Get the name for a skill.
// Inherited skills have `" (Inherited)"` appended to their names.
// If no skill matches the ID, the result is the numeric ID.
pub fun show(s: skill-id): string
match s.game-id
{{- range $s := $.Skills }} {{- range $s := $.Skills }}
{{ $s.ID }} -> Just( {{- kkenum $s.Name -}}{{ if ne $s.InheritID 0 }}-Inherit{{ end -}} ) {{ $s.ID }} -> {{ printf "%q" $s.Name }}{{ if $s.InheritID }} ++ " (Inherited)"{{ end }}
{{- end }}
x -> "skill " ++ x.show
// Get the description for a skill.
// If no skill matches the ID, the result is the empty string.
pub fun description(s: skill-id): string
match s.game-id
{{- range $s := $.Skills }}
{{ $s.ID }} -> {{ printf "%q" $s.Description }}
{{- end }}
_ -> ""
// Get the skill group ID for a skill.
// If no skill matches the ID, the result is an invalid ID.
pub fun group(s: skill-id): skill-group-id
match s.game-id
{{- range $s := $.Skills }}
{{ $s.ID }} -> Skill-group-id( {{- $s.GroupID -}} )
{{- end }}
_ -> Skill-group-id(0)
// Get the rarity of a skill.
// If no skill matches the ID, the result is Common.
pub fun rarity(s: skill-id): rarity
match s.game-id
{{- range $s := $.Skills }}
{{ $s.ID }} -> {{ if eq $s.Rarity 1 }}Common{{ else if eq $s.Rarity 2 }}Rare{{ else if eq $s.Rarity 3 }}Unique-Low{{ else if eq $s.Rarity 4 }}Unique-Upgraded{{ else if eq $s.Rarity 5 }}Unique{{ else }}??? $s.Rarity={{ $s.Rarity }}{{ end }}
{{- end }}
_ -> Common
// Get the group rate of a skill.
// If no skill matches the ID, the result is 0.
pub fun group-rate(s: skill-id): int
match s.game-id
{{- range $s := $.Skills }}
{{ $s.ID }} -> {{ $s.GroupRate }}
{{- end }}
_ -> 0
// Get the grade value of a skill.
// If no skill matches the ID, the result is 0.
pub fun grade-value(s: skill-id): int
match s.game-id
{{- range $s := $.Skills }}
{{ $s.ID }} -> {{ $s.GradeValue }}
{{- end }}
_ -> 0
// Get whether a skill is a wit check.
// If no skill matches the ID, the result is False.
pub fun wit-check(s: skill-id): bool
match s.game-id
{{- range $s := $.Skills }}
{{ $s.ID }} -> {{ if $s.WitCheck }}True{{ else }}False{{ end }}
{{- end }}
_ -> False
// Get the activations of a skill.
// If no skill matches the ID, the result is an empty list.
pub fun activations(s: skill-id): list<activation>
match s.game-id
{{- range $s := $.Skills }}
{{ $s.ID }} -> [
{{- range $a := $s.Activations }}
{{- if $a.Condition }}
Activation(
precondition = {{ printf "%q" $a.Precondition }},
condition = {{ printf "%q" $a.Condition }},
duration = {{ $a.Duration }}.decimal{{ if gt $a.Duration 0 }}(-4){{ end }},
cooldown = {{ $a.Cooldown }}.decimal{{ if gt $a.Cooldown 0 }}(-4){{ end }},
abilities = [
{{- range $abil := $a.Abilities }}
{{- if $abil.Type }}
Ability(
ability-type = {{ if eq $abil.Type 1 }}Passive-Speed({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 2 }}Passive-Stamina({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 3 }}Passive-Power({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 4 }}Passive-Guts({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 5 }}Passive-Wit({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 6 }}Great-Escape
{{- else if eq $abil.Type 8 }}Vision({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 9 }}HP({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 10 }}Gate-Delay({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 13 }}Frenzy({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 21 }}Current-Speed({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 27 }}Target-Speed({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 28 }}Lane-Speed({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 31 }}Accel({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 35 }}Lane-Change({{ $abil.Value }}.decimal(-4))
{{- else }}??? $abil.Type={{$abil.Type}}
{{- end }},
value-usage = {{ if eq $abil.ValueUsage 1 }}Direct
{{- else if eq $abil.ValueUsage 3 }}Team-Speed
{{- else if eq $abil.ValueUsage 4 }}Team-Stamina
{{- else if eq $abil.ValueUsage 5 }}Team-Power
{{- else if eq $abil.ValueUsage 6 }}Team-Guts
{{- else if eq $abil.ValueUsage 7 }}Team-Wit
{{- else if eq $abil.ValueUsage 8 }}Multiply-Random
{{- else if eq $abil.ValueUsage 9 }}Multiply-Random2
{{- else if eq $abil.ValueUsage 10 }}Climax
{{- else if eq $abil.ValueUsage 13 }}Max-Stat
{{- else if eq $abil.ValueUsage 14 }}Passive-Count
{{- else if eq $abil.ValueUsage 19 }}Front-Distance-Add
{{- else if eq $abil.ValueUsage 20 }}Midrace-Side-Block-Time
{{- else if eq $abil.ValueUsage 22 }}Speed-Scaling
{{- else if eq $abil.ValueUsage 23 }}Speed-Scaling2
{{- else if eq $abil.ValueUsage 24 }}Arc-Global-Potential
{{- else if eq $abil.ValueUsage 25 }}Max-Lead-Distance
{{- else }}??? $abil.ValueUsage={{ $abil.ValueUsage }}
{{- end }},
target = {{ if eq $abil.Target 1}}Self
{{- else if eq $abil.Target 4 }}Sympathizers
{{- else if eq $abil.Target 4 }}In-View
{{- else if eq $abil.Target 4 }}Frontmost
{{- else if eq $abil.Target 9 }}Ahead({{ $abil.TargetValue }})
{{- else if eq $abil.Target 10 }}Behind({{ $abil.TargetValue }})
{{- else if eq $abil.Target 4 }}All-Teammates
{{- else if eq $abil.Target 18 }}Style({{ if eq $abil.TargetValue 1 }}Front-Runner{{ else if eq $abil.TargetValue 2 }}Pace-Chaser{{ else if eq $abil.TargetValue 3 }}Late-Surger{{ else if eq $abil.TargetValue 4 }}End-Closer{{ else }}??? $abil.TargetValue={{ $abil.TargetValue }}{{ end }})
{{- else if eq $abil.Target 19 }}Rushing-Ahead({{ $abil.TargetValue }})
{{- else if eq $abil.Target 20 }}Rushing-Behind({{ $abil.TargetValue }})
{{- else if eq $abil.Target 21 }}Rushing-Style({{ if eq $abil.TargetValue 1 }}Front-Runner{{ else if eq $abil.TargetValue 2 }}Pace-Chaser{{ else if eq $abil.TargetValue 3 }}Late-Surger{{ else if eq $abil.TargetValue 4 }}End-Closer{{ else }}??? $abil.TargetValue={{ $abil.TargetValue }}{{ end }})
{{- else if eq $abil.Target 22 }}Specific-Character(Character-id({{ $abil.TargetValue }}))
{{- else if eq $abil.Target 23 }}Triggering
{{- end }}
),
{{- end }}
{{- end }}
]
),
{{- end }}
{{- end }}
]
{{- end }}
_ -> Nil
// Get the owner of a unique skill.
// If the skill is not unique, or if there is no skill with the given ID,
// the result is Nothing.
pub fun unique-owner(s: skill-id): maybe<trainee-id>
match s.game-id
{{- range $s := $.Skills }}
{{- if $s.UniqueOwnerID }}
{{ $s.ID }} -> Just(Trainee-id({{ $s.UniqueOwnerID }}))
{{- end }}
{{- end }} {{- end }}
_ -> Nothing _ -> Nothing
// Get the name of a skill. // Get the SP cost of a skill.
// Inherited skills have the same names as their original counterparts. // If there is no skill with the given ID, the result is 0.
pub fun skill/show(s: skill): string pub fun sp-cost(s: skill-id): int
match s match s.game-id
{{- range $s := $.Skills }} {{- range $s := $.Skills }}
{{ kkenum $s.Name }}{{ if ne $s.InheritID 0 }}-Inherit{{ end }} -> {{ printf "%q" $s.Name }} {{ $s.ID }} -> {{ $s.SPCost }}
{{- end }} {{- end }}
_ -> 0
// Compare two skills by ID order. // Get the icon ID of a skill.
pub fip fun skill/order2(a: skill, b: skill): order2<skill> // If there is no skill with the given ID, the result is an invalid ID.
match cmp(a.skill-id, b.skill-id) pub fun icon-id(s: skill-id): skill-icon-id
Lt -> Lt2(a, b) match s.game-id
Eq -> Eq2(a) {{- range $s := $.Skills }}
Gt -> Gt2(a, b) {{ $s.ID }} -> Skill-icon-id({{ $s.IconID }})
{{- end }}
_ -> Skill-icon-id(0)
pub fun skill/(==)(a: skill, b: skill): bool // Get the name for a skill group.
a.skill-id == b.skill-id // Skill group names are the name of the base skill in the group.
// If there is no skill group with the given ID, the result is the numeric ID.
// Get the skills in a skill group. pub fun skill-group/show(sg: skill-group-id): string
pub fun skill-group/skills(g: skill-group): list<skill> match sg.game-id
match g
{{- range $g := $.Groups }} {{- range $g := $.Groups }}
{{ kkenum $g.Name }} -> [ {{- range $s := index $.Related $g.ID }}{{ kkenum $s.Name }}{{ if ne $s.InheritID 0 }}-Inherit{{ end }}, {{ end }}] {{ $g.ID }} -> {{- printf "%q" $g.Name -}}
{{- end }} {{- end }}
x -> "skill group " ++ x.show
// Get complete skill info. // Get the list of skills in a skill group.
pub fun skill/detail(^s: skill): skill-detail pub fun skill-group/skills(sg: skill-group-id): list<skill-id>
match s match sg.game-id
{{- range $s := $.Skills }} {{- range $g := $.Groups }}
{{ kkenum $s.Name }}{{ if ne $s.InheritID 0 }}-Inherit{{ end }} -> {{ template "kk-render-skill-detail" $s }} {{ $g.ID }} -> [ {{- range $s := index $.Related $g.ID }}Skill-id({{ $s.ID }}), {{ end -}} ]
{{- end }} {{- end }}
_ -> Nil
// Details about a skill. {{- end }}
pub struct skill-detail
skill-id: int
name: string
description: string
group: maybe<skill-group>
rarity: rarity
group-rate: int
grade-value: int
wit-check: bool
activations: list<activation>
sp-cost: int
icon-id: int
// Automatically generated.
// Shows a string representation of the `skill-detail` type.
pub fun skill-detail/show(this : skill-detail) : e string
match this
Skill-detail(skill-id, name, description, group, rarity, group-rate, grade-value, wit-check, activations, sp-cost, icon-id) -> "Skill-detail(skill-id: " ++ skill-id.show ++ ", name: " ++ name.show ++ ", description: " ++ description.show ++ ", group: " ++ group.show ++ ", rarity: " ++ rarity.show ++ ", group-rate: " ++ group-rate.show ++ ", grade-value: " ++ grade-value.show ++ ", wit-check: " ++ wit-check.show ++ ", activations: " ++ activations.show ++ ", sp-cost: " ++ sp-cost.show ++ ", icon-id: " ++ icon-id.show ++ ")"
// Skill rarity.
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): string
match a
Activation("", condition, duration, _, abilities) | duration <= 0.decimal -> condition ++ " -> " ++ abilities.show
Activation("", condition, duration, cooldown, abilities) | cooldown >= 500.decimal -> condition ++ " -> " ++ abilities.show ++ " for " ++ duration.show ++ "s"
Activation("", condition, duration, cooldown, abilities) -> condition ++ " -> " ++ abilities.show ++ " for " ++ duration.show ++ "s on " ++ cooldown.show ++ "s cooldown"
Activation(precondition, condition, duration, _, abilities) | duration <= 0.decimal -> precondition ++ " -> " ++ condition ++ " -> " ++ abilities.show
Activation(precondition, condition, duration, cooldown, abilities) | cooldown >= 500.decimal -> precondition ++ " -> " ++ condition ++ " -> " ++ abilities.show ++ " for " ++ duration.show ++ "s"
Activation(precondition, condition, duration, cooldown, abilities) -> precondition ++ "-> " ++ condition ++ " -> " ++ abilities.show ++ " for " ++ duration.show ++ "s on " ++ cooldown.show ++ "s cooldown"
// Effects of activating a skill.
pub struct ability
ability-type: ability-type
value-usage: value-usage
target: target
pub fun ability/show(a: ability): string
match a
Ability(t, Direct, Self) -> t.show
Ability(t, Direct, target) -> t.show ++ " " ++ target.show
Ability(t, v, Self) -> t.show ++ " scaling by " ++ v.show
Ability(t, v, target) -> t.show ++ " " ++ target.show ++ " scaling by " ++ v.show
// Target of a skill activation effect.
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(rate: decimal)
Target-Speed(rate: decimal)
Lane-Speed(rate: decimal)
Accel(rate: decimal)
Lane-Change(rate: decimal)
pub fun ability-type/show(a: ability-type): string
match a
Passive-Speed(bonus) -> "passive " ++ bonus.show ++ " Speed"
Passive-Stamina(bonus) -> "passive " ++ bonus.show ++ " Stamina"
Passive-Power(bonus) -> "passive " ++ bonus.show ++ " Power"
Passive-Guts(bonus) -> "passive " ++ bonus.show ++ " Guts"
Passive-Wit(bonus) -> "passive " ++ bonus.show ++ " Wit"
Great-Escape -> "enable Great Escape style"
Vision(bonus) -> bonus.show ++ " vision"
HP(rate) | rate >= 0.decimal -> 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) -> show(rate * 100.decimal) ++ "% current speed"
Target-Speed(rate) -> show(rate * 100.decimal) ++ "% target speed"
Lane-Speed(rate) -> show(rate * 100.decimal) ++ "% lane speed"
Accel(rate) -> show(rate * 100.decimal) ++ "% acceleration"
Lane-Change(rate) -> rate.show ++ " course width movement"
// Special scaling for skill activation effects.
pub type value-usage
Direct
Team-Speed
Team-Stamina
Team-Power
Team-Guts
Team-Wit
Multiply-Random
pub fun value-usage/show(v: value-usage): string
match v
Direct -> "no scaling"
Team-Speed -> "team's Speed"
Team-Stamina -> "team's Stamina"
Team-Power -> "team's Power"
Team-Guts -> "team's Guts"
Team-Wit -> "team's Wit"
Multiply-Random -> "random multiplier (0×, 0.02×, or 0.04×)"
// Who a skill activation targets.
pub type target
Self
In-View
Ahead(limit: int)
Behind(limit: int)
Style(style: style)
Rushing-Ahead(limit: int)
Rushing-Behind(limit: int)
Rushing-Style(style: style)
pub fun target/show(t: target): string
match t
Self -> "self"
In-View -> "others in field of view"
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"
Style(Front-Runner) -> "other Front Runners"
Style(Pace-Chaser) -> "other Pace Chasers"
Style(Late-Surger) -> "other Late Surgers"
Style(End-Closer) -> "other End Closers"
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(Front-Runner) -> "rushing Front Runners"
Rushing-Style(Pace-Chaser) -> "rushing Pace Chasers"
Rushing-Style(Late-Surger) -> "rushing Late Surgers"
Rushing-Style(End-Closer) -> "rushing End Closers"
// Running style for skill targets.
{{- /* TODO(zeph): there is definitely a better place for this to live */}}
pub type style
Front-Runner
Pace-Chaser
Late-Surger
End-Closer
{{- end -}}
{{ define "kk-render-skill-detail" }}
{{- /* Call with Skill structure as argument. */ -}}
Skill-detail(skill-id = {{ $.ID -}}
, name = {{ printf "%q" $.Name -}}
, description = {{ printf "%q" $.Description -}}
, group = {{ if ne $.GroupName "" }}Just({{ kkenum $.GroupName }}){{ else }}Nothing{{ end -}}
, rarity = {{ if eq $.Rarity 1 }}Common{{ else if eq $.Rarity 2 }}Rare{{ else if eq $.Rarity 3 }}Unique-Low{{ else if eq $.Rarity 4 }}Unique-Upgraded{{ else if eq $.Rarity 5 }}Unique{{ else }}??? $.Rarity={{ $.Rarity }}{{ end -}}
, group-rate = {{ $.GroupRate -}}
, grade-value = {{ $.GradeValue -}}
, wit-check = {{ if $.WitCheck }}True{{ else }}False{{ end -}}
, activations = [
{{- range $a := $.Activations -}}
{{- if ne $a.Condition "" -}}
Activation(precondition = {{ printf "%q" $a.Precondition -}}
, condition = {{ printf "%q" $a.Condition -}}
, duration = {{ $a.Duration -}}{{ if gt $a.Duration 0 }}.decimal(-4){{ else }}.decimal{{ end -}}
, cooldown = {{ $a.Cooldown -}}{{ if gt $a.Cooldown 0 }}.decimal(-4){{ else }}.decimal{{ end -}}
, abilities = [
{{- range $abil := $a.Abilities -}}
{{- if ne $abil.Type 0 -}}
Ability(ability-type =
{{- if eq $abil.Type 1 -}}Passive-Speed({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 2 -}}Passive-Stamina({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 3 -}}Passive-Power({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 4 -}}Passive-Guts({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 5 -}}Passive-Wit({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 6 -}}Great-Escape
{{- else if eq $abil.Type 8 -}}Vision({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 9 -}}HP({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 10 -}}Gate-Delay({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 13 -}}Frenzy({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 21 -}}Current-Speed({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 27 -}}Target-Speed({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 28 -}}Lane-Speed({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 31 -}}Accel({{ $abil.Value }}.decimal(-4))
{{- else if eq $abil.Type 35 -}}Lane-Change({{ $abil.Value }}.decimal(-4))
{{- else -}}??? $abil.Type={{$abil.Type}}
{{- end -}}
, value-usage =
{{- if eq $abil.ValueUsage 1 -}}Direct
{{- else if eq $abil.ValueUsage 3 -}}Team-Speed
{{- else if eq $abil.ValueUsage 4 -}}Team-Stamina
{{- else if eq $abil.ValueUsage 5 -}}Team-Power
{{- else if eq $abil.ValueUsage 6 -}}Team-Guts
{{- else if eq $abil.ValueUsage 7 -}}Team-Wit
{{- else if eq $abil.ValueUsage 8 -}}Multiply-Random
{{- else -}}??? $abil.ValueUsage={{ $abil.ValueUsage }}
{{- end -}}
, target =
{{- if eq $abil.Target 1 -}}Self
{{- else if eq $abil.Target 4 -}}In-View
{{- else if eq $abil.Target 9 -}}Ahead({{ $abil.TargetValue }})
{{- else if eq $abil.Target 10 -}}Behind({{ $abil.TargetValue }})
{{- else if eq $abil.Target 18 -}}Style({{ if eq $abil.TargetValue 1 }}Front-Runner{{ else if eq $abil.TargetValue 2 }}Pace-Chaser{{ else if eq $abil.TargetValue 3 }}Late-Surger{{ else if eq $abil.TargetValue 4 }}End-Closer{{ else }}??? $abil.TargetValue={{ $abil.TargetValue }}{{ end }})
{{- else if eq $abil.Target 19 -}}Rushing-Ahead({{ $abil.TargetValue }})
{{- else if eq $abil.Target 20 -}}Rushing-Behind({{ $abil.TargetValue }})
{{- else if eq $abil.Target 21 -}}Rushing-Style({{ if eq $abil.TargetValue 1 }}Front-Runner{{ else if eq $abil.TargetValue 2 }}Pace-Chaser{{ else if eq $abil.TargetValue 3 }}Late-Surger{{ else if eq $abil.TargetValue 4 }}End-Closer{{ else }}??? $abil.TargetValue={{ $abil.TargetValue }}{{ end }})
{{- end -}}
),
{{- end -}}
{{- end -}}
]),
{{- end -}}
{{- end -}}
], sp-cost = {{ $.SPCost -}}
, icon-id = {{ $.IconID -}}
)
{{- end -}}

View File

@@ -21,6 +21,7 @@ WITH skill_names AS (
), card_unique AS ( ), card_unique AS (
SELECT DISTINCT SELECT DISTINCT
ss.skill_id1 AS unique_id, ss.skill_id1 AS unique_id,
card_name.id AS owner_id,
card_name.name card_name.name
FROM card_data card FROM card_data card
JOIN card_name ON card.id = card_name.id JOIN card_name ON card.id = card_name.id
@@ -81,6 +82,7 @@ SELECT
d.target_value_2_3, d.target_value_2_3,
IFNULL(p.need_skill_point, 0) AS sp_cost, IFNULL(p.need_skill_point, 0) AS sp_cost,
d.unique_skill_id_1, d.unique_skill_id_1,
COALESCE(u.owner_id, iu.owner_id, 0) AS unique_owner_id,
COALESCE(u.name, iu.name, '') AS unique_owner, COALESCE(u.name, iu.name, '') AS unique_owner,
d.icon_id, d.icon_id,
ROW_NUMBER() OVER (ORDER BY d.id) - 1 AS "index" ROW_NUMBER() OVER (ORDER BY d.id) - 1 AS "index"