From 3184397ccf2e230024d5d8da7f80368c7e35f4e6 Mon Sep 17 00:00:00 2001 From: Max Wilson Date: Sat, 25 Nov 2023 20:32:22 -0800 Subject: [PATCH] Add deity filtering (but needs more data) --- index.html | 2 +- main.sass | 35 ++++++++++++++++-- src/UI/Components/PriestSpells.fs | 40 ++++++++++++++++++--- src/UI/Components/PriestSpellsView.fs | 51 +++++++++++++++++++-------- 4 files changed, 106 insertions(+), 22 deletions(-) diff --git a/index.html b/index.html index 08be214..8b7099b 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ - RPG Utils + Shining Sword diff --git a/main.sass b/main.sass index 666128c..02edbaf 100644 --- a/main.sass +++ b/main.sass @@ -11,7 +11,12 @@ html color-scheme: dark light body - min-height: 100vh + height: 100svh + position: absolute // prevent margin collapse, to prevent unneeded scrollbars + width: 100% + +#feliz-app, #feliz-app > div, #feliz-app > div > div + max-height: 100vh img, picture, svg, video display: block @@ -19,6 +24,10 @@ img, picture, svg, video // ShiningSword-specific logic here +ul + list-style-position: inside + margin-left: 1rem + .slideFromTop animation: fromTop 0.5s ease-in-out .slideFromLeft @@ -86,6 +95,7 @@ img, picture, svg, video transform: scale(0) .hasMargins + box-sizing: border-box margin: 3px .header @@ -109,9 +119,30 @@ img, picture, svg, video display: none .srcLink max-width: 6rem - float: right + position: absolute + right: 3px background-color: white +.horizontalStack + display: flex + width: calc(100% - 6px) + gap: 10px + > * + flex: 1 + max-height: 100% + +.scrollParent + display: flex + flex-direction: column + align-items: flex-start + max-height: 100% + +.scrollable + overflow: auto + flex: 1 + align-self: stretch + .mainPage // page styling for most pages in ShiningSword: display a basic layout with a header, some data items and links @extend .fadeIn @extend .hasMargins + max-height: calc(100vh - 6px) diff --git a/src/UI/Components/PriestSpells.fs b/src/UI/Components/PriestSpells.fs index 1fd5728..2c05395 100644 --- a/src/UI/Components/PriestSpells.fs +++ b/src/UI/Components/PriestSpells.fs @@ -24,7 +24,7 @@ let consolidateSpells spheres = let consolidateSpheres (spells: Spell list) spheres = let spells = spells |> List.map (fun spell -> spell.name, spell) |> Map.ofList spheres |> List.map (fun sphere -> { sphere with spells = sphere.spells |> List.map (fun spell -> spells.[spell.name]) }) -let spheres = """ +let spheresData = """ All: Bless 1, Combine 1, Detect Evil 1, Purify Food & Drink 1, Atonement 5 Animal: Animal Friendship 1, Invisibility to Animals 1, Locate Animals or Plants 1, Charm Person or Mammal 2, Messenger 2, Snake Charm 2, Speak With Animals 2, Hold Animal 3, Summon Insects 3, Animal Summoning I 4, Call Woodland Beings 4, @@ -54,6 +54,10 @@ Summoning: Abjure 4, Animal Summoning I 4, Call Woodland Beings 4, Animal Summon Sun: Light 1, Continual Light 3, Starshine 3, Moonbeam 5, Rainbow 5, Sunray 7 Weather: Faerie Fire 1, Obscurement 2, Call Lightning 3, Control Temperature 10' Radius 4, Protection From Lightning 4, Control Winds 5, Rainbow 5, Weather Summoning 6, Control Weather 7 """ +let deityData = """ + Isis: All, Protection, Necromantic* + Osiris: Necromantic, Summoning +""" module private Parser = // #load @"c:\code\rpg\src\Core\Common.fs" // #load @"c:\code\rpg\src\Core\CQRS.fs" @@ -88,6 +92,17 @@ module private Parser = | Sphere(lhs, OWS (Spheres(rhs, rest))) -> Some(lhs::rhs, rest) | Sphere(v, rest) -> Some([v], rest) | _ -> None + let (|SphereRef|_|) = function + | Word(name, Char('*', rest)) -> Some({ sphere = name; access = Minor }, rest) + | Word(name, rest) -> Some({ sphere = name; access = Major }, rest) + | _ -> None + let rec (|SphereRefs|_|) = pack <| function + | SphereRef(lhs, OWSStr "," (SphereRefs(rhs, rest))) -> Some(lhs :: rhs, rest) + | SphereRef(v, rest) -> Some([v], rest) + | _ -> None + let rec (|Deity|_|) = function + | NameChunk(name, OWSStr ":" (SphereRefs(spheres, rest))) -> Some({ name = name; spheres = spheres }, rest) + | _ -> None // let partial (|Recognizer|_|) txt = match ParseArgs.Init txt with | Recognizer(v, _) -> v // let partialR (|Recognizer|_|) txt = match ParseArgs.Init txt with | Recognizer(v, (input, pos)) -> v, input.input.Substring pos // partial (|Spheres|_|) spheres |> List.collect _.spells |> List.filter (fun spell -> spell.name = "Chariot of Sustarre") @@ -100,7 +115,7 @@ module Storage = let key = "Spheres" let cacheRead, cacheInvalidate = Cache.create() let read (): Sphere list = - cacheRead (thunk2 read key (fun () -> Packrat.parser Parser.(|Spheres|_|) (spheres.Trim()) |> fun spheres -> spheres |> consolidateSpheres (consolidateSpells spheres))) + cacheRead (thunk2 read key (fun () -> Packrat.parser Parser.(|Spheres|_|) (spheresData.Trim()) |> fun spheres -> spheres |> consolidateSpheres (consolidateSpells spheres))) let write (v: Sphere list) = write key v cacheInvalidate() @@ -116,7 +131,7 @@ module Storage = let key = "Deities" let cacheRead, cacheInvalidate = Cache.create() let read (): Deity list = - cacheRead (thunk2 read key (thunk [])) + cacheRead (thunk2 read key (fun () -> deityData.Trim().Split("\r") |> List.ofArray |> List.map (Packrat.parser Parser.(|Deity|_|)))) let write (v: Deity list) = write key v cacheInvalidate() @@ -142,5 +157,22 @@ let filteredSpells (filter: string) (model: Model) = | "" -> model.options.spells | filter -> let fragments = filter.Split(' ') |> List.ofArray - model.options.spells |> List.filter (fun spell -> fragments |> List.every (fun fragment -> String.containsIgnoreCase (spell.ToString()) fragment)) + let grantorsBySphere = + [ + for sphere in model.options.spheres do + let grantors = [ + for d in model.options.deities do + match d.spheres |> List.tryFind (fun s -> s.sphere = sphere.name) with + | Some v -> d.name, v.access + | None -> () + ] + sphere.name, grantors + ] + |> Map.ofList + let isMatch (spell: Spell) fragment = + if String.containsIgnoreCase(spell.ToString()) fragment then true + else spell.spheres |> List.exists (fun sphere -> grantorsBySphere[sphere] |> List.exists (fun (deity, access) -> String.containsIgnoreCase deity fragment && (spell.level <= 3 || access = Major))) + + let matchingDeities spell = model.options.deities |> List.filter (fun deity -> fragments |> List.exists (fun fragment -> String.containsIgnoreCase deity.name fragment)) + model.options.spells |> List.filter (fun spell -> fragments |> List.every (isMatch spell)) diff --git a/src/UI/Components/PriestSpellsView.fs b/src/UI/Components/PriestSpellsView.fs index bb6458d..bdf93f4 100644 --- a/src/UI/Components/PriestSpellsView.fs +++ b/src/UI/Components/PriestSpellsView.fs @@ -8,22 +8,43 @@ open UI.PriestSpells let View() = let model, dispatch = React.useElmishSimple init update let filter, setFilter = React.useState "" - Html.div [ - Html.h1 "Priest Spells" - Html.input [ - prop.value filter - prop.placeholder "Spell name, sphere or deity" - prop.onChange (fun txt -> setFilter txt) + class' "mainPage horizontalStack" Html.div [ + class' "scrollParent" Html.div [ + Html.h1 "Priest Spells" + Html.input [ + prop.value filter + prop.placeholder "Spell name, sphere or deity" + prop.onChange (fun txt -> setFilter txt) + ] + class' "scrollable" Html.ul [ + let spells = filteredSpells filter model |> List.groupBy _.level |> List.sortBy fst + for level, spells in spells do + let ordinalToText = function + | 1 -> "1st" + | 2 -> "2nd" + | 3 -> "3rd" + | n -> sprintf "%dth" n + Html.h2 [prop.text (ordinalToText level)] + for spell in spells do + Html.li [ + Html.span [ + prop.text ($"""{spell.name} ({spell.spheres |> String.join "/"})""") + ] + Html.span [ + prop.text (match model.picks.TryFind(spell.name) with Some(n) -> $" ({n})" | None -> "") + ] + ] + ] ] - Html.ul [ - for spell in filteredSpells filter model do - Html.li [ - Html.span [ - prop.text (spell.ToString()) + Html.div [ + Html.h2 "Gods" + Html.ul [ + for deity in model.options.deities do + Html.li [prop.text deity.name] + Html.ul [ + for sphere in deity.spheres do + Html.li [prop.text (sphere.sphere + if sphere.access = Minor then "*" else "")] ] - Html.span [ - prop.text (match model.picks.TryFind(spell.name) with Some(n) -> $" ({n})" | None -> "") - ] - ] + ] ] ]