about summary refs log tree commit diff stats
path: root/elm/cyoa/src
diff options
context:
space:
mode:
Diffstat (limited to 'elm/cyoa/src')
-rw-r--r--elm/cyoa/src/Game.elm145
-rw-r--r--elm/cyoa/src/Main.elm15
-rw-r--r--elm/cyoa/src/Types.elm48
-rw-r--r--elm/cyoa/src/View.elm111
4 files changed, 319 insertions, 0 deletions
diff --git a/elm/cyoa/src/Game.elm b/elm/cyoa/src/Game.elm
new file mode 100644
index 0000000..22b1e8a
--- /dev/null
+++ b/elm/cyoa/src/Game.elm
@@ -0,0 +1,145 @@
+module Game exposing (..)
+
+import Types exposing (..)
+
+--| Initial game state with a simple adventure
+init : Model
+init =
+    { currentScene = SceneId "start"
+    , inventory = []
+    , scenes =
+        [ { id = SceneId "start"
+          , title = "The Mysterious Mansion"
+          , description = "You stand before an old mansion. The door is locked, but you notice a rusty key on the ground nearby."
+          , choices =
+              [ { text = "Pick up the key"
+                , nextScene = SceneId "start"
+                , requiredItem = Nothing
+                , consumesItem = False
+                }
+              , { text = "Try the door"
+                , nextScene = SceneId "inside"
+                , requiredItem = Just (ItemId "rusty_key")
+                , consumesItem = True
+                }
+              ]
+          , items = [ { id = ItemId "rusty_key", name = "Rusty Key", description = "An old, rusty key that might fit the mansion's door." } ]
+          }
+        , { id = SceneId "inside"
+          , title = "Inside the Mansion"
+          , description = "You're inside the mansion. The air is musty and old. There's a dusty book on a table and a locked chest in the corner."
+          , choices =
+              [ { text = "Examine the book"
+                , nextScene = SceneId "book"
+                , requiredItem = Nothing
+                , consumesItem = False
+                }
+              , { text = "Try to open the chest"
+                , nextScene = SceneId "chest"
+                , requiredItem = Nothing
+                , consumesItem = False
+                }
+              ]
+          , items = []
+          }
+        , { id = SceneId "book"
+          , title = "The Dusty Book"
+          , description = "The book contains a map of the mansion. You notice a small key drawn in the corner of one page."
+          , choices =
+              [ { text = "Take the key"
+                , nextScene = SceneId "inside"
+                , requiredItem = Nothing
+                , consumesItem = False
+                }
+              , { text = "Return to the main room"
+                , nextScene = SceneId "inside"
+                , requiredItem = Nothing
+                , consumesItem = False
+                }
+              ]
+          , items = [ { id = ItemId "small_key", name = "Small Key", description = "A small key that might fit the chest." } ]
+          }
+        , { id = SceneId "chest"
+          , title = "The Locked Chest"
+          , description = "The chest is locked with a small lock. You need a key to open it."
+          , choices =
+              [ { text = "Use the small key"
+                , nextScene = SceneId "treasure"
+                , requiredItem = Just (ItemId "small_key")
+                , consumesItem = True
+                }
+              , { text = "Return to the main room"
+                , nextScene = SceneId "inside"
+                , requiredItem = Nothing
+                , consumesItem = False
+                }
+              ]
+          , items = []
+          }
+        , { id = SceneId "treasure"
+          , title = "The Treasure"
+          , description = "Inside the chest, you find a valuable gem! Your adventure has been successful."
+          , choices =
+              [ { text = "Start a new adventure"
+                , nextScene = SceneId "start"
+                , requiredItem = Nothing
+                , consumesItem = False
+                }
+              ]
+          , items = [ { id = ItemId "gem", name = "Valuable Gem", description = "A beautiful gem that sparkles in the light." } ]
+          }
+        ]
+    }
+
+--| Find a scene by its ID
+findScene : SceneId -> List Scene -> Maybe Scene
+findScene targetId scenes =
+    List.filter (\scene -> scene.id == targetId) scenes
+        |> List.head
+
+--| Find an item by its ID
+findItem : ItemId -> List Item -> Maybe Item
+findItem targetId items =
+    List.filter (\item -> item.id == targetId) items
+        |> List.head
+
+--| Check if player has an item
+hasItem : ItemId -> List Item -> Bool
+hasItem itemId inventory =
+    List.any (\item -> item.id == itemId) inventory
+
+--| Update the game state based on messages
+update : Msg -> Model -> Model
+update msg model =
+    case msg of
+        ChooseChoice choice ->
+            case choice.requiredItem of
+                Just requiredItemId ->
+                    if hasItem requiredItemId model.inventory then
+                        let
+                            newInventory =
+                                if choice.consumesItem then
+                                    List.filter (\item -> item.id /= requiredItemId) model.inventory
+                                else
+                                    model.inventory
+                        in
+                        { model
+                            | currentScene = choice.nextScene
+                            , inventory = newInventory
+                        }
+                    else
+                        model
+
+                Nothing ->
+                    { model | currentScene = choice.nextScene }
+
+        PickupItem item ->
+            { model | inventory = item :: model.inventory }
+
+        UseItem item _ ->
+            if hasItem item.id model.inventory then
+                { model
+                    | inventory = List.filter (\i -> i.id /= item.id) model.inventory
+                }
+            else
+                model 
\ No newline at end of file
diff --git a/elm/cyoa/src/Main.elm b/elm/cyoa/src/Main.elm
new file mode 100644
index 0000000..d739d22
--- /dev/null
+++ b/elm/cyoa/src/Main.elm
@@ -0,0 +1,15 @@
+module Main exposing (main)
+
+import Browser
+import Types exposing (Model, Msg)
+import Game exposing (init, update)
+import View exposing (view)
+
+--| Main entry point
+main : Program () Model Msg
+main =
+    Browser.sandbox
+        { init = init
+        , update = update
+        , view = view
+        } 
\ No newline at end of file
diff --git a/elm/cyoa/src/Types.elm b/elm/cyoa/src/Types.elm
new file mode 100644
index 0000000..46c6cdb
--- /dev/null
+++ b/elm/cyoa/src/Types.elm
@@ -0,0 +1,48 @@
+module Types exposing (..)
+
+--| Core game types for our Choose Your Own Adventure game
+
+--| A unique identifier for items
+type ItemId
+    = ItemId String
+
+--| A unique identifier for scenes
+type SceneId
+    = SceneId String
+
+--| Represents an item in the game
+type alias Item =
+    { id : ItemId
+    , name : String
+    , description : String
+    }
+
+--| Represents a choice/action the player can take
+type alias Choice =
+    { text : String
+    , nextScene : SceneId
+    , requiredItem : Maybe ItemId
+    , consumesItem : Bool
+    }
+
+--| Represents a scene in the game
+type alias Scene =
+    { id : SceneId
+    , title : String
+    , description : String
+    , choices : List Choice
+    , items : List Item
+    }
+
+--| The complete game state
+type alias Model =
+    { currentScene : SceneId
+    , inventory : List Item
+    , scenes : List Scene
+    }
+
+--| Messages that can occur in the game
+type Msg
+    = ChooseChoice Choice
+    | PickupItem Item
+    | UseItem Item SceneId 
\ No newline at end of file
diff --git a/elm/cyoa/src/View.elm b/elm/cyoa/src/View.elm
new file mode 100644
index 0000000..090a89a
--- /dev/null
+++ b/elm/cyoa/src/View.elm
@@ -0,0 +1,111 @@
+module View exposing (..)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (onClick)
+import Types exposing (..)
+import Game exposing (findScene)
+
+--| Main view function
+view : Model -> Html Msg
+view model =
+    div [ class "game-container" ]
+        [ viewScene model
+        , viewInventory model.inventory
+        ]
+
+--| View the current scene
+viewScene : Model -> Html Msg
+viewScene model =
+    case findScene model.currentScene model.scenes of
+        Just scene ->
+            div [ class "scene" ]
+                [ h1 [] [ text scene.title ]
+                , p [] [ text scene.description ]
+                , viewItems scene.items
+                , viewChoices scene.choices model.inventory
+                ]
+
+        Nothing ->
+            div [] [ text "Scene not found!" ]
+
+--| View available items in the current scene
+viewItems : List Item -> Html Msg
+viewItems items =
+    if List.isEmpty items then
+        div [] []
+    else
+        div [ class "items" ]
+            [ h2 [] [ text "Items in this area:" ]
+            , ul []
+                (List.map
+                    (\item ->
+                        li []
+                            [ button
+                                [ onClick (PickupItem item)
+                                , class "item-button"
+                                ]
+                                [ text ("Pick up " ++ item.name) ]
+                            ]
+                    )
+                    items
+                )
+            ]
+
+--| View available choices/actions
+viewChoices : List Choice -> List Item -> Html Msg
+viewChoices choices inventory =
+    div [ class "choices" ]
+        [ h2 [] [ text "What will you do?" ]
+        , ul []
+            (List.map
+                (\choice ->
+                    li []
+                        [ viewChoice choice inventory ]
+                )
+                choices
+            )
+        ]
+
+--| View a single choice/action
+viewChoice : Choice -> List Item -> Html Msg
+viewChoice choice inventory =
+    case choice.requiredItem of
+        Just requiredItemId ->
+            if Game.hasItem requiredItemId inventory then
+                button
+                    [ onClick (ChooseChoice choice)
+                    , class "choice-button"
+                    ]
+                    [ text choice.text ]
+            else
+                button
+                    [ class "choice-button disabled"
+                    , disabled True
+                    ]
+                    [ text (choice.text ++ " (requires an item)") ]
+
+        Nothing ->
+            button
+                [ onClick (ChooseChoice choice)
+                , class "choice-button"
+                ]
+                [ text choice.text ]
+
+--| View the player's inventory
+viewInventory : List Item -> Html Msg
+viewInventory items =
+    div [ class "inventory" ]
+        [ h2 [] [ text "Inventory" ]
+        , if List.isEmpty items then
+            p [] [ text "Your inventory is empty" ]
+          else
+            ul []
+                (List.map
+                    (\item ->
+                        li []
+                            [ text (item.name ++ " - " ++ item.description) ]
+                    )
+                    items
+                )
+        ] 
\ No newline at end of file