Skip to content

Commit

Permalink
Add deity filtering (but needs more data)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxWilson committed Nov 26, 2023
1 parent 5490b0b commit 82f1aab
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 25 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<title>RPG Utils</title>
<title>Shining Sword</title>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" type="image/png" href="./img/favicon-32x32.png" sizes="32x32" />
Expand Down
35 changes: 33 additions & 2 deletions main.sass
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,23 @@ 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
max-width: 100%

// ShiningSword-specific logic here
ul
list-style-position: inside
margin-left: 1rem

.slideFromTop
animation: fromTop 0.5s ease-in-out
.slideFromLeft
Expand Down Expand Up @@ -86,6 +95,7 @@ img, picture, svg, video
transform: scale(0)

.hasMargins
box-sizing: border-box
margin: 3px

.header
Expand All @@ -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)
3 changes: 2 additions & 1 deletion src/UI/CommonUI.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ let divWrap (className: string) element =
prop.className className
prop.children [element]
]

// make crlf in string constants work the same way in dev and in prod
let normalizeCRLF (str: string) = str.Replace("\r\n", "\n").Replace("\r", "\n")
exception UserFacingException of msg:string
let informUserOfError msg = UserFacingException msg |> raise

Expand Down
61 changes: 56 additions & 5 deletions src/UI/Components/PriestSpells.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module UI.PriestSpells

open CommonUI
#nowarn "40" // Disabling the recursive initialization warning for this file because the parser is recursive, but isn't doing anything weird like calling ctor args during construction.

type SphereName = string
Expand All @@ -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 = normalizeCRLF """
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,
Expand Down Expand Up @@ -54,6 +54,22 @@ 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 = normalizeCRLF """
Great Spirit: all, animal, elemental, healing, plant, protection, sun, weather
Sun: sun, all, healing*, protection*
Moon: all, protection, healing, charm*, creation*
Earth: all, animal, plant, elemental*, summoning*, weather*
Morning Star: all, creation, healing, animal*, divination*, plant*, protection*
Wind: all, combat, elemental, weather
Fire: all, combat, divination, elemental, guardian*, necromantic*, summoning*
Thunder: all, divination, protection, guardian*, healing*, weather*
Raven: all, animal, charm
Coyote: all, animal, summoning, charm
Snake: all, animal, charm, healing, protection
Osiris: all, astral, charm*, combat*, guardian, healing, necromantic, protection
Isis: all, astral, charm, combat, creation, divination, elemental, guardian, healing, necromantic*, protection, sun
"""

module private Parser =
// #load @"c:\code\rpg\src\Core\Common.fs"
// #load @"c:\code\rpg\src\Core\CQRS.fs"
Expand Down Expand Up @@ -88,6 +104,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
| Names(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")
Expand All @@ -100,7 +127,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()
Expand All @@ -116,7 +143,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("\n") |> List.ofArray |> List.map (Packrat.parser Parser.(|Deity|_|))))
let write (v: Deity list) =
write key v
cacheInvalidate()
Expand All @@ -142,5 +169,29 @@ 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 -> String.equalsIgnoreCase 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)))
model.options.spells |> List.filter (fun spell -> fragments |> List.every (isMatch spell))

let filteredDeities (filter: string) (model: Model) =
match filter.Trim() with
| "" -> model.options.deities
| filter ->
let fragments = filter.Split(' ') |> List.ofArray
let matchingDeities = model.options.deities |> List.filter (fun deity -> fragments |> List.exists (fun fragment -> String.containsIgnoreCase deity.name fragment || deity.spheres |> List.exists (fun sphere -> String.containsIgnoreCase sphere.sphere fragment)))
match matchingDeities with
| [] -> model.options.deities // they must not be trying to filter by deity
| lst -> lst
49 changes: 33 additions & 16 deletions src/UI/Components/PriestSpellsView.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,39 @@ 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.span [
prop.text (match model.picks.TryFind(spell.name) with Some(n) -> $" ({n})" | None -> "")
]
]
Html.div [
Html.h2 "Gods"
Html.ul [
for deity in filteredDeities filter model do
Html.li [prop.text (deity.name + ": " + String.join ", " [for sphere in deity.spheres -> (sphere.sphere + if sphere.access = Minor then "*" else "")])]
]
]
]

0 comments on commit 82f1aab

Please sign in to comment.