{-# OPTIONS_GHC -Wno-unused-imports #-}

-- |
-- [Next Chapter: Asset Refs]("Mischief.Assets.Tutorial.Refs")
--
-- [Main Page]("Mischief.Assets")
module Mischief.Assets.Tutorial.Introduction
  ( -- * Chapter 1: Introduction to Assets
    -- $intro

    -- * Defining an Asset
    -- $defining

    -- * Loading the Asset
    -- $loading

    -- * Hot-Reloading the Asset
    -- $hot

    -- * Integrating Assets Into a Game
    -- $integrating

    -- * [Next Chapter: Asset Refs]("Mischief.Assets.Tutorial.Refs")
  )
where

import Control.Monad
import Mischief.Assets
import Mischief.ECS

-- $intro
-- This chapter will walk you through creating and loading your first asset, as well as hot-reloading it and integrating it into the ECS.
--
-- We'll start by creating an app:
--
-- @
-- import Mischief.Assets
-- import Mischief.ECS
--
-- main :: 'IO' ()
-- main = do
--   app <- 'newApp'
--   'addPlugin' \@MyPlugin app
--   'runApp' app
--
-- data MyPlugin
--
-- instance 'Plugin' MyPlugin
-- @
--
-- It's good practice to set an /asset source/ at the start of your program. This will be the place where you'll be loading and saving assets.
--
-- @
-- app <- 'newApp'
-- 'setAssetSource' \"assets/\" app
-- ...
-- @
--
-- Make sure to create an @assets@ folder. This is generally relative to where you're executing the program from.

-- $defining
-- For this tutorial we'll use @.kdl@ (pronounced '\cuddle'\), as it is the official file format used by Mischief. You can read more about it [here](https://kdl.dev/spec/).
--
-- We'll define an asset that looks like this:
--
-- @
-- heal {
--   amount 5
-- }
-- @
--
-- Just a simple @heal@ node with an @amount@ child containing an integer.
--
-- Create a @heal.kdl@ file in your assets folder and copy the above content into it.
--
-- Next, we need a type in Haskell to represent that data. @Component@ is necessary for all assets.
-- @KdlAsset@ is a tool we use to conver the kdl into this form for us.
--
-- @
-- newtype Heal = Heal {amount :: 'Int'}
--   deriving ('Generic', 'KdlAsset', 'Show', 'Component')
-- @
--
-- Now to write the actual @Asset@ instance:
--
-- @
-- instance 'Asset' Heal where
--   type 'Saveable' Heal = 'False'
--   'loadAsset' = 'loadKdl' \"heal\"
-- @
--
-- We do two things here: First, we set @Saveable@ to False. We just don't care about saving this asset. Second,
-- we tell it how to actually load the asset. We use the @loadKdl@ function provided by the earlier @KdlAsset@ instance.
-- We need to also give it the name of the node we want it to load. In our case that's \"heal\".

-- $loading
-- We can load an asset using the @load@ function! Let's write a system that does that.
--
-- @
-- loadHeal :: 'System' ()
-- loadHeal = 'load_' \@Heal \"heal.kdl\"
-- @
--
-- Since assets are loaded aysnchronously, we also need a way to react to the asset being loaded if we want to print it and
-- check that everything's working. We can do that using the @OnLoad@ event:
--
-- @
-- onLoadHeal :: 'OnLoad' Heal -> 'System' ()
-- onLoadHeal ('OnLoad' e) =
--   ['q'|e. Heal|]
--     & 'qinfo' (\\h -\> ['i'|Loaded #{h}|])
--     & 'query_'
-- @
--
-- And of course, we need to set these to run:
--
-- @
-- instance 'Plugin' MyPlugin where
--   'init' = do
--     'systems' loadHeal
--       & 'schedule' \@'Startup'
--
--     'spawn_' $ 'Observer' onLoadHeal
-- @
--
-- When running your app, you should now see this printed to your screen:
--
-- @
-- [INFO] Loaded Heal {amount = 5}
-- @

-- $hot
-- Pretty annoying that we need to restart our program after modifying our asset in order to load it again..
--
-- But that's why we have hot-reloading! Just add the @FileWatcherPlugin@ as a dependency to your plugin:
--
-- @
-- instance 'Plugin' MyPlugin where
--   'deps' = ['dep' \@'FileWatcherPlugin']
-- @
--
-- Note that, on Windows, you may need to run your project with the @-fthreaded@ flag for this to work properly.
--
-- Now, while running your app, try changing the amount in the @heal.kdl@ file. You'll see the new value printed
-- to the screen immediately!

-- $integrating
-- Let's spawn a few players, each having a @Health@ component:
--
-- @
-- newtype Health = Health 'Int' deriving ('Component')
--
-- spawnPlayers :: 'System' ()
-- spawnPlayers = do
--   'spawn_' (Name \"A\", Health 20)
--   'spawn_' (Name \"B\", Health 30)
--   'spawn_' (Name \"C\", Health 60)
-- @
--
-- Now let's write a system that, when it runs, increases each player's health by the value currently loaded inside Heal:
--
-- @
-- healPlayers :: 'System' ()
-- healPlayers = 'query_' $ do
--   Heal heal <- 'qasset' \@Heal \"heal.kdl\"
--   (player, name, Health hp) \<- ['q'|Entity, Name, Health|]
--   'qlift' $ do
--     'insert' (Health $ hp + heal) player
--     'info' ['i'|Player #{name} is now at #{hp + heal} hp|]
-- @
--
-- Inside a query, we are grabbing each player's name and health, along with the Heal asset. We are then inserting the updated health
-- on the player and printing it.
--
-- All that's left is scheduling this to run at a fixed interval:
--
-- @
-- import "Mischief.ECS.Interval" qualified as Interval
-- @
--
-- @
-- instance 'Plugin' MyPlugin where
--   'init' = do
--     'systems' spawnPlayers
--       & 'schedule' \@'Startup'
--
--     'void' $ Interval.'Mischief.ECS.Interval.start' 1000000 healPlayers
-- @
--
-- Note that we don't need to manually load the file anymore. Asset queries are /active/ by default,
-- meaning they will trigger a load for us if the asset we're asking for is not currently loaded.
--
-- You should see a bunch of these printed to your screen:
--
-- @
-- [Info] Player A is now at 24 hp
-- [Info] Player B is now at 34 hp
-- [Info] Player C is now at 64 hp
-- @
--
-- Try toying with the heal amount in the file, and see how that affects the amount players heal each second!