Compare commits
1 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
eabba2b7fe |
|
|
@ -0,0 +1,129 @@
|
||||||
|
module Decoder exposing (..)
|
||||||
|
|
||||||
|
import Dict
|
||||||
|
import FastDict
|
||||||
|
import Json.Decode as D
|
||||||
|
import Json.Encode as E
|
||||||
|
|
||||||
|
|
||||||
|
type Information a
|
||||||
|
= Information
|
||||||
|
{ decoder : D.Decoder a
|
||||||
|
, encoder : a -> E.Value
|
||||||
|
, keys : List String
|
||||||
|
, default : a
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool : List String -> Bool -> Information Bool
|
||||||
|
bool keys default =
|
||||||
|
Information
|
||||||
|
{ decoder = D.bool
|
||||||
|
, encoder = E.bool
|
||||||
|
, keys = keys
|
||||||
|
, default = default
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dict : List String -> Dict.Dict comparable value -> (comparable -> String) -> (String -> comparable) -> (value -> E.Value) -> D.Decoder value -> Information (Dict.Dict comparable value)
|
||||||
|
dict keys default fromKey toKey fromValue valueDecoder =
|
||||||
|
Information
|
||||||
|
{ decoder =
|
||||||
|
D.keyValuePairs valueDecoder
|
||||||
|
|> D.map (List.map (Tuple.mapFirst toKey))
|
||||||
|
|> D.map Dict.fromList
|
||||||
|
, encoder = E.dict fromKey fromValue
|
||||||
|
, keys = keys
|
||||||
|
, default = default
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fastdict : List String -> FastDict.Dict comparable value -> (comparable -> String) -> (String -> comparable) -> (value -> E.Value) -> D.Decoder value -> Information (FastDict.Dict comparable value)
|
||||||
|
fastdict keys default fromKey toKey fromValue valueDecoder =
|
||||||
|
Information
|
||||||
|
{ decoder =
|
||||||
|
D.keyValuePairs valueDecoder
|
||||||
|
|> D.map (List.map (Tuple.mapFirst toKey))
|
||||||
|
|> D.map FastDict.fromList
|
||||||
|
, encoder =
|
||||||
|
FastDict.toList
|
||||||
|
>> List.map (Tuple.mapBoth fromKey fromValue)
|
||||||
|
>> E.object
|
||||||
|
, keys = keys
|
||||||
|
, default = default
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int : List String -> Int -> Information Int
|
||||||
|
int keys default =
|
||||||
|
Information
|
||||||
|
{ decoder = D.int
|
||||||
|
, encoder = E.int
|
||||||
|
, keys = keys
|
||||||
|
, default = default
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
list : List String -> List a -> (a -> E.Value) -> D.Decoder a -> Information (List a)
|
||||||
|
list keys default fromValue valueDecoder =
|
||||||
|
Information
|
||||||
|
{ decoder = D.list valueDecoder
|
||||||
|
, encoder = E.list fromValue
|
||||||
|
, keys = keys
|
||||||
|
, default = default
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| The website allows users to overwrite the pre-generated values.
|
||||||
|
Hence, if a value already exists, it is returned. If it does not, it will be
|
||||||
|
generated.
|
||||||
|
-}
|
||||||
|
lookUp : E.Value -> Information a -> a
|
||||||
|
lookUp value (Information { decoder, keys, default }) =
|
||||||
|
case D.decodeValue (D.at keys decoder) value of
|
||||||
|
Ok v ->
|
||||||
|
v
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
default
|
||||||
|
|
||||||
|
|
||||||
|
overwrite : a -> Information a -> E.Value -> E.Value
|
||||||
|
overwrite newValue (Information info) obj =
|
||||||
|
case info.keys of
|
||||||
|
[] ->
|
||||||
|
info.encoder newValue
|
||||||
|
|
||||||
|
head :: tail ->
|
||||||
|
case D.decodeValue (D.keyValuePairs D.value) obj of
|
||||||
|
Ok o ->
|
||||||
|
o
|
||||||
|
|> FastDict.fromList
|
||||||
|
|> FastDict.update head
|
||||||
|
(\mv ->
|
||||||
|
overwrite newValue
|
||||||
|
(Information
|
||||||
|
{ encoder = info.encoder
|
||||||
|
, decoder = info.decoder
|
||||||
|
, keys = tail
|
||||||
|
, default = info.default
|
||||||
|
}
|
||||||
|
)
|
||||||
|
(mv |> Maybe.withDefault (E.dict identity identity Dict.empty))
|
||||||
|
|> Just
|
||||||
|
)
|
||||||
|
|> FastDict.toCoreDict
|
||||||
|
|> E.dict identity identity
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
obj
|
||||||
|
|
||||||
|
|
||||||
|
string : List String -> String -> Information String
|
||||||
|
string keys default =
|
||||||
|
Information
|
||||||
|
{ encoder = E.string
|
||||||
|
, decoder = D.string
|
||||||
|
, keys = keys
|
||||||
|
, default = default
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,341 @@
|
||||||
|
module Defaults exposing (..)
|
||||||
|
|
||||||
|
{-| The Defaults module exposes config values. These values can be overwritten
|
||||||
|
but they are usually set by default.
|
||||||
|
-}
|
||||||
|
|
||||||
|
import FastDict as Dict
|
||||||
|
|
||||||
|
|
||||||
|
commonerOccupations : Dict.Dict String Int
|
||||||
|
commonerOccupations =
|
||||||
|
Dict.fromList
|
||||||
|
[ ( "Baker", 25 )
|
||||||
|
, ( "Shoemaker", 37 )
|
||||||
|
, ( "Furrier", 62 )
|
||||||
|
, ( "Household helper", 62 )
|
||||||
|
, ( "Tailor", 62 )
|
||||||
|
, ( "Barber", 87 )
|
||||||
|
, ( "Unlicensed Doctor", 87 )
|
||||||
|
, ( "Jeweler", 100 )
|
||||||
|
, ( "Old cloth seller", 100 )
|
||||||
|
, ( "Pastry baker", 125 )
|
||||||
|
, ( "Mason", 125 )
|
||||||
|
, ( "Carpenter", 137 )
|
||||||
|
, ( "Ostler", 150 )
|
||||||
|
, ( "Porter", 150 )
|
||||||
|
, ( "Weaver", 150 )
|
||||||
|
, ( "Chandler", 175 )
|
||||||
|
, ( "Mercer", 175 )
|
||||||
|
, ( "Cooper", 175 )
|
||||||
|
, ( "Cobbler", 180 )
|
||||||
|
, ( "Watercarrier", 212 )
|
||||||
|
, ( "Scabbardmaker", 212 )
|
||||||
|
, ( "Wine-seller", 225 )
|
||||||
|
, ( "Hatmaker", 237 )
|
||||||
|
, ( "Saddler", 250 )
|
||||||
|
, ( "Chicken butcher", 250 )
|
||||||
|
, ( "Pursemaker", 275 )
|
||||||
|
, ( "Soapmaker", 275 )
|
||||||
|
, ( "Goldsmith", 300 )
|
||||||
|
, ( "Butcher", 300 )
|
||||||
|
, ( "Farrier", 300 )
|
||||||
|
, ( "Fishmonger", 300 )
|
||||||
|
, ( "Beer seller", 350 )
|
||||||
|
, ( "Buckle Maker", 350 )
|
||||||
|
, ( "Plasterer", 350 )
|
||||||
|
, ( "Spice Merchant", 350 )
|
||||||
|
, ( "Hedge-Mage", 350 )
|
||||||
|
, ( "Painter", 375 )
|
||||||
|
, ( "Hunter", 400 )
|
||||||
|
, ( "Licensed doctor", 425 )
|
||||||
|
, ( "Roofer", 450 )
|
||||||
|
, ( "Locksmith", 475 )
|
||||||
|
, ( "Bather", 475 )
|
||||||
|
, ( "Ropemaker", 475 )
|
||||||
|
, ( "Inn", 500 )
|
||||||
|
, ( "Tanner", 500 )
|
||||||
|
, ( "Copyist", 500 )
|
||||||
|
, ( "Sculptor", 500 )
|
||||||
|
, ( "Rugmaker", 500 )
|
||||||
|
, ( "Dyer", 500 )
|
||||||
|
, ( "Harness-Maker", 500 )
|
||||||
|
, ( "Bleacher", 525 )
|
||||||
|
, ( "Hay Merchant", 575 )
|
||||||
|
, ( "Cutler", 575 )
|
||||||
|
, ( "Glovemaker", 600 )
|
||||||
|
, ( "Woodcarver", 600 )
|
||||||
|
, ( "Woodseller", 600 )
|
||||||
|
, ( "Herald", 600 )
|
||||||
|
, ( "Lorimer", 600 )
|
||||||
|
, ( "Penmaker", 650 )
|
||||||
|
, ( "Horse Trainer", 700 )
|
||||||
|
, ( "Bookbinder", 750 )
|
||||||
|
, ( "Stationer (Ink/Pen Maker)", 700 )
|
||||||
|
, ( "Spurrer", 800 )
|
||||||
|
, ( "Dung Carter", 800 )
|
||||||
|
, ( "Costermonger", 800 )
|
||||||
|
, ( "Fortune Teller", 900 )
|
||||||
|
, ( "Chirurgeon", 900 )
|
||||||
|
, ( "Silversmith", 920 )
|
||||||
|
, ( "Illuminator", 975 )
|
||||||
|
, ( "Coppersmith", 1100 )
|
||||||
|
, ( "Master of Hounds", 1200 )
|
||||||
|
, ( "Leech Collector", 1300 )
|
||||||
|
, ( "Tinsmith", 1400 )
|
||||||
|
, ( "Castellan", 1500 )
|
||||||
|
, ( "Bookseller", 1575 )
|
||||||
|
, ( "Composer", 1600 )
|
||||||
|
, ( "Limeburner", 1600 )
|
||||||
|
, ( "Ale-conner", 1700 )
|
||||||
|
, ( "Amanuensis", 1800 )
|
||||||
|
, ( "Gong Farmer", 1800 )
|
||||||
|
, ( "Coiner", 1900 )
|
||||||
|
, ( "Philosopher", 2000 )
|
||||||
|
, ( "Noble", 3000 )
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
type alias RaceData =
|
||||||
|
{ density : Int
|
||||||
|
, maturesAt : Int
|
||||||
|
, livesUntil : Int
|
||||||
|
, becomesParentUntil : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
races : Dict.Dict String RaceData
|
||||||
|
races =
|
||||||
|
Dict.fromList
|
||||||
|
[ ( "Human"
|
||||||
|
, { density = 800000
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 90
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Half-elf"
|
||||||
|
, { density = 100000
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 200
|
||||||
|
, becomesParentUntil = 160
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Dwarf"
|
||||||
|
, { density = 60000
|
||||||
|
, maturesAt = 50
|
||||||
|
, livesUntil = 350
|
||||||
|
, becomesParentUntil = 150
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Elf"
|
||||||
|
, { density = 35000
|
||||||
|
, maturesAt = 100
|
||||||
|
, livesUntil = 750
|
||||||
|
, becomesParentUntil = 500
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Half-orc"
|
||||||
|
, { density = 25000
|
||||||
|
, maturesAt = 14
|
||||||
|
, livesUntil = 75
|
||||||
|
, becomesParentUntil = 30
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Halfling"
|
||||||
|
, { density = 20000
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 65
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Dragonborn"
|
||||||
|
, { density = 10000
|
||||||
|
, maturesAt = 15
|
||||||
|
, livesUntil = 80
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Tiefling"
|
||||||
|
, { density = 10000
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 110
|
||||||
|
, becomesParentUntil = 45
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Gnome"
|
||||||
|
, { density = 10000
|
||||||
|
, maturesAt = 40
|
||||||
|
, livesUntil = 500
|
||||||
|
, becomesParentUntil = 200
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Aasimar"
|
||||||
|
, { density = 9000
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 160
|
||||||
|
, becomesParentUntil = 60
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Orc"
|
||||||
|
, { density = 8000
|
||||||
|
, maturesAt = 12
|
||||||
|
, livesUntil = 50
|
||||||
|
, becomesParentUntil = 30
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Goliath"
|
||||||
|
, { density = 7500
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 90
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Tabaxi"
|
||||||
|
, { density = 5000
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 90
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Hobgoblin"
|
||||||
|
, { density = 2500
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 90
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Lizardfolk"
|
||||||
|
, { density = 1500
|
||||||
|
, maturesAt = 14
|
||||||
|
, livesUntil = 60
|
||||||
|
, becomesParentUntil = 30
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Goblin"
|
||||||
|
, { density = 1000
|
||||||
|
, maturesAt = 8
|
||||||
|
, livesUntil = 60
|
||||||
|
, becomesParentUntil = 30
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Yuan-Ti Pureblood"
|
||||||
|
, { density = 1000
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 90
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Bugbear"
|
||||||
|
, { density = 1000
|
||||||
|
, maturesAt = 16
|
||||||
|
, livesUntil = 80
|
||||||
|
, becomesParentUntil = 35
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Tortle"
|
||||||
|
, { density = 1000
|
||||||
|
, maturesAt = 15
|
||||||
|
, livesUntil = 50
|
||||||
|
, becomesParentUntil = 30
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Warforged"
|
||||||
|
, { density = 1000
|
||||||
|
, maturesAt = 0
|
||||||
|
, livesUntil = 5000
|
||||||
|
, becomesParentUntil = 5000
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Kobold"
|
||||||
|
, { density = 800
|
||||||
|
, maturesAt = 6
|
||||||
|
, livesUntil = 120
|
||||||
|
, becomesParentUntil = 25
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Shifter"
|
||||||
|
, { density = 750
|
||||||
|
, maturesAt = 10
|
||||||
|
, livesUntil = 70
|
||||||
|
, becomesParentUntil = 30
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Triton"
|
||||||
|
, { density = 400
|
||||||
|
, maturesAt = 15
|
||||||
|
, livesUntil = 200
|
||||||
|
, becomesParentUntil = 110
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Firbolg"
|
||||||
|
, { density = 300
|
||||||
|
, maturesAt = 30
|
||||||
|
, livesUntil = 500
|
||||||
|
, becomesParentUntil = 300
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Kalashtar"
|
||||||
|
, { density = 250
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 90
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Aarakocra"
|
||||||
|
, { density = 250
|
||||||
|
, maturesAt = 3
|
||||||
|
, livesUntil = 30
|
||||||
|
, becomesParentUntil = 20
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Kenku"
|
||||||
|
, { density = 200
|
||||||
|
, maturesAt = 12
|
||||||
|
, livesUntil = 60
|
||||||
|
, becomesParentUntil = 36
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Changeling"
|
||||||
|
, { density = 50
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 90
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Gith"
|
||||||
|
, { density = 75
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 90
|
||||||
|
, becomesParentUntil = 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Air Genasi"
|
||||||
|
, { density = 10
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 120
|
||||||
|
, becomesParentUntil = 50
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Earth Genasi"
|
||||||
|
, { density = 10
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 120
|
||||||
|
, becomesParentUntil = 50
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Fire Genasi"
|
||||||
|
, { density = 10
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 120
|
||||||
|
, becomesParentUntil = 50
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, ( "Water Genasi"
|
||||||
|
, { density = 10
|
||||||
|
, maturesAt = 20
|
||||||
|
, livesUntil = 120
|
||||||
|
, becomesParentUntil = 50
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
module Generatable exposing (..)
|
module Generatable exposing (Generatable, content, fromGenerator, generateAndReturnFromSeed, generateFromSeed, generate, map, regenerate, setContent, removeContent, updateGenerator)
|
||||||
{-| The Generatable type contains information that can be stored, modified,
|
{-| The Generatable type contains information that can be stored, modified,
|
||||||
generated or regenerated.
|
generated or regenerated.
|
||||||
-}
|
-}
|
||||||
|
|
@ -26,6 +26,25 @@ fromGenerator : Random.Generator a -> Generatable a
|
||||||
fromGenerator gr =
|
fromGenerator gr =
|
||||||
ToBeGenerated gr
|
ToBeGenerated gr
|
||||||
|
|
||||||
|
{-| Get the generator with which new values can be generated.
|
||||||
|
-}
|
||||||
|
generator : Generatable a -> Random.Generator a
|
||||||
|
generator gen =
|
||||||
|
case gen of
|
||||||
|
ToBeGenerated gr ->
|
||||||
|
gr
|
||||||
|
|
||||||
|
Generated _ gr ->
|
||||||
|
gr
|
||||||
|
|
||||||
|
{-| Create a new Generatable from a given seed, and return the value.
|
||||||
|
-}
|
||||||
|
generateAndReturnFromSeed : Random.Seed -> Generatable a -> ( Generatable a, a )
|
||||||
|
generateAndReturnFromSeed seed gen =
|
||||||
|
case Random.step (generator gen) seed of
|
||||||
|
( new, _ ) ->
|
||||||
|
( Generated new (generator gen), new )
|
||||||
|
|
||||||
{-| Create a new generatable from a given seed.
|
{-| Create a new generatable from a given seed.
|
||||||
-}
|
-}
|
||||||
generateFromSeed : Random.Seed -> Generatable a -> Generatable a
|
generateFromSeed : Random.Seed -> Generatable a -> Generatable a
|
||||||
|
|
@ -65,23 +84,13 @@ already generated.
|
||||||
-}
|
-}
|
||||||
regenerate : (a -> msg) -> Generatable a -> Cmd msg
|
regenerate : (a -> msg) -> Generatable a -> Cmd msg
|
||||||
regenerate toMsg gen =
|
regenerate toMsg gen =
|
||||||
case gen of
|
Random.generate toMsg (generator gen)
|
||||||
ToBeGenerated gr ->
|
|
||||||
Random.generate toMsg gr
|
|
||||||
|
|
||||||
Generated _ gr ->
|
|
||||||
Random.generate toMsg gr
|
|
||||||
|
|
||||||
{-| Override the content with freshly generated content.
|
{-| Override the content with freshly generated content.
|
||||||
-}
|
-}
|
||||||
setContent : a -> Generatable a -> Generatable a
|
setContent : a -> Generatable a -> Generatable a
|
||||||
setContent x gen =
|
setContent x gen =
|
||||||
case gen of
|
Generated x (generator gen)
|
||||||
ToBeGenerated gr ->
|
|
||||||
Generated x gr
|
|
||||||
|
|
||||||
Generated _ gr ->
|
|
||||||
Generated x gr
|
|
||||||
|
|
||||||
{-| Remove any previously generated content from the generatable.
|
{-| Remove any previously generated content from the generatable.
|
||||||
-}
|
-}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
module Generator exposing (..)
|
||||||
|
|
||||||
|
{-| The Generator module helps generate values based on a given hash.
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Pick exposing (Generator)
|
||||||
|
|
||||||
|
|
||||||
|
commonerOccupation : Generator (Maybe String)
|
||||||
|
commonerOccupation =
|
||||||
|
Pick.inverseWeighted
|
||||||
|
[ ( "Baker", 25 )
|
||||||
|
, ( "Shoemaker", 37 )
|
||||||
|
, ( "Furrier", 62 )
|
||||||
|
, ( "Household helper", 62 )
|
||||||
|
, ( "Tailor", 62 )
|
||||||
|
, ( "Barber", 87 )
|
||||||
|
, ( "Unlicensed Doctor", 87 )
|
||||||
|
, ( "Jeweler", 100 )
|
||||||
|
, ( "Old cloth seller", 100 )
|
||||||
|
, ( "Pastry baker", 125 )
|
||||||
|
, ( "Mason", 125 )
|
||||||
|
, ( "Carpenter", 137 )
|
||||||
|
, ( "Ostler", 150 )
|
||||||
|
, ( "Porter", 150 )
|
||||||
|
, ( "Weaver", 150 )
|
||||||
|
, ( "Chandler", 175 )
|
||||||
|
, ( "Mercer", 175 )
|
||||||
|
, ( "Cooper", 175 )
|
||||||
|
, ( "Cobbler", 180 )
|
||||||
|
, ( "Watercarrier", 212 )
|
||||||
|
, ( "Scabbardmaker", 212 )
|
||||||
|
, ( "Wine-seller", 225 )
|
||||||
|
, ( "Hatmaker", 237 )
|
||||||
|
, ( "Saddler", 250 )
|
||||||
|
, ( "Chicken butcher", 250 )
|
||||||
|
, ( "Pursemaker", 275 )
|
||||||
|
, ( "Soapmaker", 275 )
|
||||||
|
, ( "Goldsmith", 300 )
|
||||||
|
, ( "Butcher", 300 )
|
||||||
|
, ( "Farrier", 300 )
|
||||||
|
, ( "Fishmonger", 300 )
|
||||||
|
, ( "Beer seller", 350 )
|
||||||
|
, ( "Buckle Maker", 350 )
|
||||||
|
, ( "Plasterer", 350 )
|
||||||
|
, ( "Spice Merchant", 350 )
|
||||||
|
, ( "Hedge-Mage", 350 )
|
||||||
|
, ( "Painter", 375 )
|
||||||
|
, ( "Hunter", 400 )
|
||||||
|
, ( "Licensed doctor", 425 )
|
||||||
|
, ( "Roofer", 450 )
|
||||||
|
, ( "Locksmith", 475 )
|
||||||
|
, ( "Bather", 475 )
|
||||||
|
, ( "Ropemaker", 475 )
|
||||||
|
, ( "Inn", 500 )
|
||||||
|
, ( "Tanner", 500 )
|
||||||
|
, ( "Copyist", 500 )
|
||||||
|
, ( "Sculptor", 500 )
|
||||||
|
, ( "Rugmaker", 500 )
|
||||||
|
, ( "Dyer", 500 )
|
||||||
|
, ( "Harness-Maker", 500 )
|
||||||
|
, ( "Bleacher", 525 )
|
||||||
|
, ( "Hay Merchant", 575 )
|
||||||
|
, ( "Cutler", 575 )
|
||||||
|
, ( "Glovemaker", 600 )
|
||||||
|
, ( "Woodcarver", 600 )
|
||||||
|
, ( "Woodseller", 600 )
|
||||||
|
, ( "Herald", 600 )
|
||||||
|
, ( "Lorimer", 600 )
|
||||||
|
, ( "Penmaker", 650 )
|
||||||
|
, ( "Horse Trainer", 700 )
|
||||||
|
, ( "Bookbinder", 750 )
|
||||||
|
, ( "Stationer (Ink/Pen Maker)", 700 )
|
||||||
|
, ( "Spurrer", 800 )
|
||||||
|
, ( "Dung Carter", 800 )
|
||||||
|
, ( "Costermonger", 800 )
|
||||||
|
, ( "Fortune Teller", 900 )
|
||||||
|
, ( "Chirurgeon", 900 )
|
||||||
|
, ( "Silversmith", 920 )
|
||||||
|
, ( "Illuminator", 975 )
|
||||||
|
, ( "Coppersmith", 1100 )
|
||||||
|
, ( "Master of Hounds", 1200 )
|
||||||
|
, ( "Leech Collector", 1300 )
|
||||||
|
, ( "Tinsmith", 1400 )
|
||||||
|
, ( "Castellan", 1500 )
|
||||||
|
, ( "Bookseller", 1575 )
|
||||||
|
, ( "Composer", 1600 )
|
||||||
|
, ( "Limeburner", 1600 )
|
||||||
|
, ( "Ale-conner", 1700 )
|
||||||
|
, ( "Amanuensis", 1800 )
|
||||||
|
, ( "Gong Farmer", 1800 )
|
||||||
|
, ( "Coiner", 1900 )
|
||||||
|
, ( "Philosopher", 2000 )
|
||||||
|
, ( "Noble", 3000 )
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
townName : Generator String
|
||||||
|
townName =
|
||||||
|
Pick.map2 (++)
|
||||||
|
(Pick.fromList
|
||||||
|
"Auden"
|
||||||
|
[ "Caia"
|
||||||
|
, "Ex Caia"
|
||||||
|
, "Gold"
|
||||||
|
, "Hirey"
|
||||||
|
, "Ideato"
|
||||||
|
, "Jairo"
|
||||||
|
, "Kalares"
|
||||||
|
, "Oosterdorp"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
(Pick.fromList
|
||||||
|
""
|
||||||
|
[ ""
|
||||||
|
, ""
|
||||||
|
, " City"
|
||||||
|
, " Town"
|
||||||
|
, "'s Creek"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
module Objects exposing (..)
|
||||||
|
|
||||||
|
{-| This module exposes all types that can be generated.
|
||||||
|
-}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
module Objects.District exposing (..)
|
||||||
|
{-| Districts are sections of a town. These are mostly segregated to keep the
|
||||||
|
design of the procedurally generated town simple.
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-| A district is a part of town. Usually, it is a neighbourhood.
|
||||||
|
-}
|
||||||
|
type District
|
||||||
|
= District {}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
module Objects.NPC exposing (..)
|
||||||
|
|
||||||
|
{-| The NPC is the character that is most ordinary.
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Hash exposing (Hash)
|
||||||
|
import Objects.TownName as Town
|
||||||
|
|
||||||
|
|
||||||
|
type NPC
|
||||||
|
= NPC { town : Town.TownName, id : Int }
|
||||||
|
|
||||||
|
|
||||||
|
fromTown : { town : Town.TownName, id : Int } -> NPC
|
||||||
|
fromTown =
|
||||||
|
NPC
|
||||||
|
|
||||||
|
|
||||||
|
toTown : NPC -> Town.TownName
|
||||||
|
toTown (NPC { town }) =
|
||||||
|
town
|
||||||
|
|
||||||
|
|
||||||
|
toId : NPC -> Int
|
||||||
|
toId (NPC { id }) =
|
||||||
|
id
|
||||||
|
|
||||||
|
|
||||||
|
toHash : NPC -> Hash
|
||||||
|
toHash (NPC { town, id }) =
|
||||||
|
Hash.fromString "NPC"
|
||||||
|
|> Hash.dependent (Town.toHash town)
|
||||||
|
|> Hash.dependent (Hash.fromInt id)
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
module Objects.Town exposing (..)
|
||||||
|
|
||||||
|
import Array exposing (Array)
|
||||||
|
import Hash
|
||||||
|
import Random
|
||||||
|
import Generatable exposing (Generatable)
|
||||||
|
import Objects.District as District exposing (District)
|
||||||
|
|
||||||
|
{-| The Msg is the update telling what adjustments are made to the model.
|
||||||
|
-}
|
||||||
|
type Msg
|
||||||
|
= GenerateSize
|
||||||
|
|
||||||
|
{-| The Town is the comprehensive model that contains all the information about
|
||||||
|
a town.
|
||||||
|
-}
|
||||||
|
type Town
|
||||||
|
= Town
|
||||||
|
{ districts : Generatable (Array (Generatable District))
|
||||||
|
, name : String
|
||||||
|
, size : TownSize
|
||||||
|
}
|
||||||
|
|
||||||
|
type TownSize
|
||||||
|
= Tiny
|
||||||
|
| Small
|
||||||
|
| Medium
|
||||||
|
| Average
|
||||||
|
| Big
|
||||||
|
| Huge
|
||||||
|
-- | Humongous
|
||||||
|
-- | Massive
|
||||||
|
-- | Colossal
|
||||||
|
|
||||||
|
{-| Determine the number of districts in the town based on its size.
|
||||||
|
-}
|
||||||
|
districtNumberGenerator : TownSize -> Random.Generator Int
|
||||||
|
districtNumberGenerator ts =
|
||||||
|
case ts of
|
||||||
|
Tiny ->
|
||||||
|
Random.int 1 2
|
||||||
|
|
||||||
|
Small ->
|
||||||
|
Random.int 5 8
|
||||||
|
|
||||||
|
Medium ->
|
||||||
|
Random.int 15 20
|
||||||
|
|
||||||
|
Average ->
|
||||||
|
Random.int 35 45
|
||||||
|
|
||||||
|
Big ->
|
||||||
|
Random.int 90 140
|
||||||
|
|
||||||
|
Huge ->
|
||||||
|
Random.int 250 470
|
||||||
|
|
||||||
|
{-| Init a new town.
|
||||||
|
-}
|
||||||
|
init : { name : String, size : TownSize } -> Town
|
||||||
|
init data =
|
||||||
|
Town
|
||||||
|
{ districts =
|
||||||
|
data.size
|
||||||
|
|> districtNumberGenerator
|
||||||
|
|> Random.map (always never)
|
||||||
|
|> Generatable.fromGenerator
|
||||||
|
, name = data.name
|
||||||
|
, size = data.size
|
||||||
|
}
|
||||||
|
|
||||||
|
{-| Get a town's name.
|
||||||
|
-}
|
||||||
|
name : Town -> String
|
||||||
|
name (Town town) = town.name
|
||||||
|
|
||||||
|
{-| Get a town's size.
|
||||||
|
-}
|
||||||
|
size : Town -> Maybe Int
|
||||||
|
size (Town town) =
|
||||||
|
Generatable.content town.size
|
||||||
|
|
||||||
|
{-| Get a town's size. If it isn't generated yet, generate it.
|
||||||
|
-}
|
||||||
|
sizeGenerate : Town -> ( Town, Int )
|
||||||
|
sizeGenerate ((Town t) as town) =
|
||||||
|
case Generatable.content t.size of
|
||||||
|
Just s ->
|
||||||
|
( town, s )
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
case Generatable.generateAndReturnFromSeed (sizeSeed town) t.size of
|
||||||
|
( newSize, s ) ->
|
||||||
|
( Town { t | size = newSize }, s )
|
||||||
|
|
||||||
|
{-| Generate a seed for the town's size.
|
||||||
|
-}
|
||||||
|
sizeSeed : Town -> Random.Seed
|
||||||
|
sizeSeed (Town town) =
|
||||||
|
Hash.fromString town.name
|
||||||
|
|> Hash.dependent (Hash.fromString "size")
|
||||||
|
|> Hash.toString
|
||||||
|
|> String.toInt
|
||||||
|
|> Maybe.withDefault 0
|
||||||
|
|> Random.initialSeed
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
module Objects.TownName exposing (TownName, fromString, toHash, toString)
|
||||||
|
|
||||||
|
import Hash exposing (Hash)
|
||||||
|
|
||||||
|
|
||||||
|
type TownName
|
||||||
|
= TownName String
|
||||||
|
|
||||||
|
|
||||||
|
{-| Decode a town name from a string.
|
||||||
|
-}
|
||||||
|
fromString : String -> TownName
|
||||||
|
fromString =
|
||||||
|
TownName
|
||||||
|
|
||||||
|
|
||||||
|
{-| Encode a town name into a string.
|
||||||
|
-}
|
||||||
|
toString : TownName -> String
|
||||||
|
toString (TownName name) =
|
||||||
|
name
|
||||||
|
|
||||||
|
|
||||||
|
{-| Hash a town name.
|
||||||
|
-}
|
||||||
|
toHash : TownName -> Hash
|
||||||
|
toHash (TownName name) =
|
||||||
|
Hash.dependent (Hash.fromString "town-name") (Hash.fromString name)
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
module Pick exposing (..)
|
||||||
|
|
||||||
|
{-| The pick module helps pick random values based on a given hash.
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Hash exposing (Hash)
|
||||||
|
import Random
|
||||||
|
|
||||||
|
|
||||||
|
type alias Generator a =
|
||||||
|
Random.Generator a
|
||||||
|
|
||||||
|
|
||||||
|
generate : Hash -> Generator a -> a
|
||||||
|
generate hash picker =
|
||||||
|
let
|
||||||
|
seed : Random.Seed
|
||||||
|
seed =
|
||||||
|
hash
|
||||||
|
|> Hash.toString
|
||||||
|
|> String.toInt
|
||||||
|
|> Maybe.withDefault 0
|
||||||
|
|> Random.initialSeed
|
||||||
|
in
|
||||||
|
Random.step
|
||||||
|
|
||||||
|
|
||||||
|
fromList : a -> List a -> Generator a
|
||||||
|
fromList head tail =
|
||||||
|
Random.uniform head tail
|
||||||
|
|
||||||
|
|
||||||
|
inverseWeighted : List ( a, Int ) -> Generator (Maybe a)
|
||||||
|
inverseWeighted items =
|
||||||
|
let
|
||||||
|
chanceSum : Float
|
||||||
|
chanceSum =
|
||||||
|
items
|
||||||
|
|> List.map Tuple.second
|
||||||
|
|> List.map toFloat
|
||||||
|
|> List.map ((/) 1)
|
||||||
|
|> List.sum
|
||||||
|
in
|
||||||
|
items
|
||||||
|
|> List.map (Tuple.mapSecond (\i -> 1 / toFloat i))
|
||||||
|
|> List.map (\( a, b ) -> ( b, a ))
|
||||||
|
|> List.map (Tuple.mapSecond Just)
|
||||||
|
|> weighted ( max 0 (1 - chanceSum), Nothing )
|
||||||
|
|
||||||
|
|
||||||
|
map : (a -> b) -> Generator a -> Generator b
|
||||||
|
map =
|
||||||
|
Random.map
|
||||||
|
|
||||||
|
|
||||||
|
map2 : (a -> b -> c) -> Generator a -> Generator b -> Generator c
|
||||||
|
map2 =
|
||||||
|
Random.map2
|
||||||
|
|
||||||
|
|
||||||
|
value : a -> Generator a
|
||||||
|
value =
|
||||||
|
Random.constant
|
||||||
|
|
||||||
|
|
||||||
|
weighted : ( Float, a ) -> List ( Float, a ) -> Generator a
|
||||||
|
weighted =
|
||||||
|
Random.weighted
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
module Seed.Town exposing (TownSeed, fromName, toHash, toSeed)
|
||||||
|
{-| The Town Seed helps generate different towns.
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Hash
|
||||||
|
import Random
|
||||||
|
|
||||||
|
type TownSeed = TownSeed String
|
||||||
|
|
||||||
|
fromName : String -> TownSeed
|
||||||
|
fromName name = TownSeed name
|
||||||
|
|
||||||
|
toHash : TownSeed -> Hash.Hash
|
||||||
|
toHash (TownSeed name) =
|
||||||
|
Hash.fromString name
|
||||||
|
|
||||||
|
toSeed : TownSeed -> Random.Seed
|
||||||
|
toSeed seed =
|
||||||
|
seed
|
||||||
|
|> toHash
|
||||||
|
|> Hash.toString
|
||||||
|
|> String.toInt
|
||||||
|
|> Maybe.withDefault 0
|
||||||
|
|> Random.initialSeed
|
||||||
Loading…
Reference in New Issue