Intro
I was tasked to write kragii worms in the game and informed that they’re small (10cm / 4 inches) long worms that burrow in ground and are drawn to farming fields and people. They’re dangerous and might eat harvest or people.
Special events build on top of the new system I explained in episode 2733. They are read from same API as regular news and need same ToJSON, FromJSON, ToDto and FromDto instances as regular news (for translating them data transfer objects and then into JSON for sending to client).
Loading
Starting from the API interface, the first real difference is when JSON stored into database is turned into NewsArticle. Two cases, where special news have available options added to them and regular news are left unchanged. These options tell player what choices they have when dealing with the situation and evaluated every time special event is loaded, because situation might have changed since special event got stored into database and available options might have changed.
addOptions (key, article) = case article of
Special news ->
(key, Special $ availableOptions news)
_ ->
(key, article)
availableOptions :: SpecialNews -> SpecialNews
availableOptions x =
case x of
KragiiWorms event _ choice ->
KragiiWorms event (eventOptions event) choice
eventOptions is one of the events defined in SpecialEvent type class that specifies two functions every special event has to have. eventOptions lists what options the event has currently available and resolveEvent resolves the event according to choice user might have made (hence Maybe in it).
Type class is parametrized with three types (imaginatively named to a, b and c). First is data type that holds information about special event (where it’s happening and to who for example), second one is one that tells all possible choices player has and third one lists various results that might occur when resolving the event. In this example they’re KragiiWormsEvent, KragiiWormsChoice and KragiiResults.
data KragiiWormsEvent = KragiiWormsEvent
{ kragiiWormsPlanetId :: Key Planet
, kragiiWormsPlanetName :: Text
, kragiiWormsSystemId :: Key StarSystem
, kragiiWormsSystemName :: Text
, kragiiWormsDate :: Int
}
data KragiiWormsChoice =
EvadeWorms
| AttackWorms
| TameWorms
data KragiiResults =
WormsStillPresent
| WormsRemoved
| WormsTamed
| CropsDestroyed (RawResource Biological)
| FarmersInjured
Definition of the SpecialEvent type class is shown below. Type signature of resolveEvent is gnarly because it’s reading and writing database.
class SpecialEvent a b c | a -> b, a -> c where
eventOptions :: a -> [UserOption b]
resolveEvent :: ( PersistQueryRead backend, PersistQueryWrite backend
, MonadIO m, BaseBackend backend ~ SqlBackend ) =>
(Key News, a) -> Maybe b -> ReaderT backend m (Maybe EventRemoval, [c])
One more piece we need is UserOption. This records options in a format that is useful in the client side. Each option player has are given title and explanation that are shown on UI.
data UserOption a =
UserOption { userOptionTitle :: Text
, userOptionExplanation :: [Text]
, userOptionChoice :: a
}