refactor: add complex sex options
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
"version": "0.1.0",
|
||||
"sources": [
|
||||
{ "dir": "src" },
|
||||
{ "dir": "src/Common" },
|
||||
{ "dir": "src/Localization" },
|
||||
{ "dir": "src/Rated" },
|
||||
{ "dir": "src/Rules" },
|
||||
{ "dir": "src/Sources" },
|
||||
|
||||
+3
-1
@@ -1,7 +1,9 @@
|
||||
# Structure
|
||||
|
||||
- `src` – The source code.
|
||||
- `src` – The source code main folder, containing program entry points.
|
||||
- `Common` – Types and functions associated with multiple ‘namespaces’.
|
||||
- `Database` – The flat-file database containing all static data from the source books.
|
||||
- `Localization` – Types and functions related to different languages in the app and the source books.
|
||||
- `Rated` – All data types for entries with *ratings* (attributes, skills or combat techniques) and related types and logic.
|
||||
- `Rules` – All data types for rule entries.
|
||||
- `Sources` – Types and functions dealing with data related to source books: Source books themselves, references, availability by book and errata.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
module ActivatableAndSkill = struct
|
||||
type t =
|
||||
| Advantage of int
|
||||
| Disadvantage of int
|
||||
| SpecialAbility of int
|
||||
| Spell of int
|
||||
| LiturgicalChant of int
|
||||
end
|
||||
@@ -0,0 +1,105 @@
|
||||
type binary_handling = { as_male : bool; as_female : bool }
|
||||
|
||||
type t =
|
||||
| Male
|
||||
| Female
|
||||
| BalThani of binary_handling
|
||||
| Tsajana of binary_handling
|
||||
| Custom of { binary_handling : binary_handling; name : string }
|
||||
|
||||
type prerequisite = Male | Female
|
||||
|
||||
let matches (sex : t) (prerequisite : prerequisite) =
|
||||
match (prerequisite, sex) with
|
||||
| Male, Male | Female, Female -> true
|
||||
| Male, Female | Female, Male -> false
|
||||
| Male, BalThani { as_male = as_sex; _ }
|
||||
| Male, Tsajana { as_male = as_sex; _ }
|
||||
| Male, Custom { binary_handling = { as_male = as_sex; _ }; _ }
|
||||
| Female, BalThani { as_female = as_sex; _ }
|
||||
| Female, Tsajana { as_female = as_sex; _ }
|
||||
| Female, Custom { binary_handling = { as_female = as_sex; _ }; _ } ->
|
||||
as_sex
|
||||
|
||||
module Decode = struct
|
||||
open Json.Decode
|
||||
|
||||
let binary_handling json =
|
||||
{
|
||||
as_male = json |> field "as_male" bool;
|
||||
as_female = json |> field "as_female" bool;
|
||||
}
|
||||
|
||||
let make =
|
||||
oneOf
|
||||
[
|
||||
string
|
||||
|> map (function
|
||||
| "Male" -> (Male : t)
|
||||
| "Female" -> Female
|
||||
| str ->
|
||||
JsonStatic.raise_unknown_variant ~variant_name:"Sex"
|
||||
~invalid:str);
|
||||
field "type" string
|
||||
|> andThen (function
|
||||
| "BalThani" ->
|
||||
field "value" (field "binary_handling" binary_handling)
|
||||
|> map (fun opt -> BalThani opt)
|
||||
| "Tsajana" ->
|
||||
field "value" (field "binary_handling" binary_handling)
|
||||
|> map (fun opt -> Tsajana opt)
|
||||
| "Custom" ->
|
||||
field "value" (fun json ->
|
||||
Custom
|
||||
{
|
||||
binary_handling =
|
||||
json |> field "binary_handling" binary_handling;
|
||||
name = json |> field "name" string;
|
||||
})
|
||||
| str ->
|
||||
JsonStatic.raise_unknown_variant ~variant_name:"Sex"
|
||||
~invalid:str);
|
||||
]
|
||||
|
||||
let make_prerequisite =
|
||||
string
|
||||
|> map (function
|
||||
| "Male" -> (Male : prerequisite)
|
||||
| "Female" -> Female
|
||||
| str ->
|
||||
JsonStatic.raise_unknown_variant ~variant_name:"Sex" ~invalid:str)
|
||||
end
|
||||
|
||||
module Encode = struct
|
||||
open Json.Encode
|
||||
|
||||
let binary_handling { as_male; as_female } =
|
||||
object_ [ ("as_male", bool as_male); ("as_female", bool as_female) ]
|
||||
|
||||
let make (sex : t) =
|
||||
match sex with
|
||||
| Male -> string "Male"
|
||||
| Female -> string "Female"
|
||||
| BalThani bin ->
|
||||
object_
|
||||
[
|
||||
("type", string "BalThani");
|
||||
("value", object_ [ ("binary_handling", binary_handling bin) ]);
|
||||
]
|
||||
| Tsajana bin ->
|
||||
object_
|
||||
[
|
||||
("type", string "BalThani");
|
||||
("value", object_ [ ("binary_handling", binary_handling bin) ]);
|
||||
]
|
||||
| Custom { binary_handling = bin; name } ->
|
||||
object_
|
||||
[
|
||||
("type", string "BalThani");
|
||||
( "value",
|
||||
object_
|
||||
[
|
||||
("binary_handling", binary_handling bin); ("name", string name);
|
||||
] );
|
||||
]
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
type binary_handling = {
|
||||
as_male : bool;
|
||||
(** Defines if the sex should be treated as male when checking prerequisites. *)
|
||||
as_female : bool;
|
||||
(** Defines if the sex should be treated as female when checking prerequisites. *)
|
||||
}
|
||||
(** Defines how a non-binary sex should be treated when checking prerequisites. *)
|
||||
|
||||
(** A possible character's sex which may be binary or non-binary. If a sex is
|
||||
non-binary, options how to handle binary prerequisites must be specified. *)
|
||||
type t =
|
||||
| Male
|
||||
| Female
|
||||
| BalThani of binary_handling
|
||||
| Tsajana of binary_handling
|
||||
| Custom of { binary_handling : binary_handling; name : string }
|
||||
|
||||
(** A binary sex prerequisite*)
|
||||
type prerequisite = Male | Female
|
||||
|
||||
val matches : t -> prerequisite -> bool
|
||||
(** [matches sex prerequisite] validates a binary sex prerequisite against a
|
||||
character's sex. *)
|
||||
|
||||
module Decode : sig
|
||||
val make : t Json.Decode.decoder
|
||||
|
||||
val make_prerequisite : prerequisite Json.Decode.decoder
|
||||
end
|
||||
|
||||
module Encode : sig
|
||||
val make : t Json.Encode.encoder
|
||||
end
|
||||
@@ -0,0 +1,64 @@
|
||||
module Order = struct
|
||||
type t = string list
|
||||
|
||||
let from_list def xs = match xs with [] -> [ def ] | prefs -> prefs
|
||||
|
||||
let to_list xs = xs
|
||||
|
||||
let preferred = List.hd
|
||||
end
|
||||
|
||||
module Supported = struct
|
||||
type t = { id : string; name : string; region : string }
|
||||
|
||||
let system_locale_to_id supported_locales system_locale =
|
||||
(system_locale |> Js.String.split "-" |> fun arr -> arr.(0))
|
||||
|> fun systemLocaleStart ->
|
||||
StrMap.find
|
||||
(fun { id; _ } -> Js.String.startsWith systemLocaleStart id)
|
||||
supported_locales
|
||||
|> Option.option "en-US" (fun locale -> locale.id)
|
||||
|
||||
module Decode = struct
|
||||
open Json.Decode
|
||||
open JsonStrict
|
||||
|
||||
type multilingual = {
|
||||
id : string;
|
||||
name : string;
|
||||
region : string;
|
||||
isMissingImplementation : bool option;
|
||||
}
|
||||
|
||||
let multilingual json =
|
||||
{
|
||||
id = json |> field "id" string;
|
||||
name = json |> field "name" string;
|
||||
region = json |> field "region" string;
|
||||
isMissingImplementation =
|
||||
json |> optionalField "isMissingImplementation" bool;
|
||||
}
|
||||
|> Option.ensure (fun { isMissingImplementation; _ } ->
|
||||
Option.dis isMissingImplementation)
|
||||
|
||||
let make_assoc json =
|
||||
let open Option.Infix in
|
||||
json |> multilingual <&> fun multilingual ->
|
||||
( multilingual.id,
|
||||
{
|
||||
id = multilingual.id;
|
||||
name = multilingual.name;
|
||||
region = multilingual.region;
|
||||
} )
|
||||
|
||||
let make_strmap json =
|
||||
json |> list make_assoc |> Option.catOptions |> StrMap.fromList
|
||||
end
|
||||
end
|
||||
|
||||
(* let filterBySupported def supportedLocales order =
|
||||
order
|
||||
|> Ley_List.filter (Ley_Function.flip Ley_StrMap.member supportedLocales)
|
||||
|> function
|
||||
| [] -> [ def ]
|
||||
| order -> order *)
|
||||
@@ -19,21 +19,24 @@ module Order : sig
|
||||
order of possible locales. *)
|
||||
end
|
||||
|
||||
(* module Supported : sig
|
||||
type t = { id : string; name : string; region : string }
|
||||
(** A language supported in Optolith. It's id is it's IETF language tag
|
||||
(BCP47), and it features the languages name as well as the region name
|
||||
defined in the standard. *)
|
||||
module Supported : sig
|
||||
type t = {
|
||||
id : string; (** The IETF language tag (BCP47). *)
|
||||
name : string;
|
||||
region : string;
|
||||
}
|
||||
(** A language supported in Optolith. It features the language's identifier
|
||||
and name as well as the region name defined in the BCP47 standard. *)
|
||||
|
||||
val systemLocaleToId : t Ley_StrMap.t -> string -> string
|
||||
val system_locale_to_id : t StrMap.t -> string -> string
|
||||
(** Derive the default locale's id from the system locale's id. *)
|
||||
|
||||
module Decode : sig
|
||||
val map : t Ley_StrMap.t Json.Decode.decoder
|
||||
val make_strmap : t StrMap.t Json.Decode.decoder
|
||||
end
|
||||
end
|
||||
|
||||
val filterBySupported : string -> Supported.t Ley_StrMap.t -> Order.t -> Order.t
|
||||
(* val filterBySupported : string -> Supported.t Ley_StrMap.t -> Order.t -> Order.t
|
||||
(** [filterBySupported defaultLocale supportedLocales localeOrder] filters the
|
||||
order of locales set by the user by the locales that are supported. If this
|
||||
causes the order to be empty, a default locale will be used. *) *)
|
||||
@@ -1 +1,6 @@
|
||||
let raise_unknown_variant ~variant_name ~invalid =
|
||||
raise
|
||||
(Json.Decode.DecodeError
|
||||
("Unknown variant tag of variant \"" ^ variant_name ^ "\": " ^ invalid))
|
||||
|
||||
type 'a make_assoc = Locale.Order.t -> (int * 'a) option Json.Decode.decoder
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
(** Utility types for static data JSON decoders. *)
|
||||
(** Utility types and functions for static data JSON decoders. *)
|
||||
|
||||
val raise_unknown_variant : variant_name:string -> invalid:string -> 'a
|
||||
(** Utility function for throwing an exception if a string is not part of a
|
||||
variant. *)
|
||||
|
||||
type 'a make_assoc = Locale.Order.t -> (int * 'a) option Json.Decode.decoder
|
||||
(** A decoder returning a pair that can be used to build an [IntMap]. *)
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
module Order = struct
|
||||
type t = string list
|
||||
|
||||
let from_list def xs = match xs with [] -> [ def ] | prefs -> prefs
|
||||
|
||||
let to_list xs = xs
|
||||
|
||||
let preferred = List.hd
|
||||
end
|
||||
|
||||
(* module Supported = struct
|
||||
type t = { id : string; name : string; region : string }
|
||||
|
||||
module Decode = struct
|
||||
type multilingual = {
|
||||
id : string;
|
||||
name : string;
|
||||
region : string;
|
||||
isMissingImplementation : bool option;
|
||||
}
|
||||
[@@decco]
|
||||
|
||||
let makeAssoc multilingual =
|
||||
match multilingual.isMissingImplementation with
|
||||
| None | Some false ->
|
||||
Some
|
||||
( multilingual.id,
|
||||
{
|
||||
id = multilingual.id;
|
||||
name = multilingual.name;
|
||||
region = multilingual.region;
|
||||
} )
|
||||
| Some true -> None
|
||||
|
||||
let assoc json = json |> multilingual_decode |. Belt.Result.map makeAssoc
|
||||
|
||||
let map json =
|
||||
Json.Decode.(
|
||||
json |> list assoc |> Ley_Option.catOptions |> Ley_StrMap.fromList)
|
||||
end
|
||||
|
||||
let systemLocaleToId supportedLocales systemLocale =
|
||||
(systemLocale |> Js.String.split "-" |> fun arr -> arr.(0))
|
||||
|> fun systemLocaleStart ->
|
||||
Ley_StrMap.find
|
||||
(fun { id; _ } -> Js.String.startsWith systemLocaleStart id)
|
||||
supportedLocales
|
||||
|> Ley_Option.option "en-US" (fun locale -> locale.id)
|
||||
end
|
||||
|
||||
let filterBySupported def supportedLocales order =
|
||||
order
|
||||
|> Ley_List.filter (Ley_Function.flip Ley_StrMap.member supportedLocales)
|
||||
|> function
|
||||
| [] -> [ def ]
|
||||
| order -> order *)
|
||||
Reference in New Issue
Block a user