diff options
Diffstat (limited to 'elm/cyoa/src')
-rw-r--r-- | elm/cyoa/src/Game.elm | 145 | ||||
-rw-r--r-- | elm/cyoa/src/Main.elm | 15 | ||||
-rw-r--r-- | elm/cyoa/src/Types.elm | 48 | ||||
-rw-r--r-- | elm/cyoa/src/View.elm | 111 |
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 |