let it leave me like a long breath

let it dissipate or fade in the background

(no subject)

Profile

xax: purple-orange {11/3 knotwork star, pointed down (Default)
howling howling howling

Nav

  • Recent Entries
  • Archive
  • Reading
  • Tags
  • Memories
  • Profile

Tags

  • art - 2 uses
  • asteroid garden - 4 uses
  • code - 19 uses
  • demos - 1 use
  • dreams - 5 uses
  • ff7 fangame - 23 uses
  • fic prompts - 13 uses
  • gamedev challenge - 82 uses
  • hell game - 76 uses
  • nanowrimo - 11 uses
  • plants - 9 uses
  • process - 52 uses
  • programming - 51 uses
  • screenshots - 5 uses
  • writing log - 83 uses

May 2025

S M T W T F S
    123
45678 910
1112131415 1617
18192021222324
25262728293031
  • Mar. 24th, 2016
  • xax: purple-orange {11/3 knotwork star, pointed down (Default)
    [personal profile] xax
    Tags:
    • code,
    • hell game,
    • process,
    • programming
    posted @ 02:27 am

    today i kind of worked on hell game! marking up sex scenes mostly.

    now i'm gonna talk about some of the code

    so the sex scenes are stored internally as just lists of events: here's a narration event w/ some prose inside, here's a dialog event, here's an inventory-changing event, etc.

    actually let's take a step back from that: the events are currently processed server-side, in haskell code. there's a ~request api~ that sends 1. the current formation of yr body, in the form of a JSON node tree, and 2. the data of the mob whose event yr triggering, in a somewhat-standardized mob format. (it also needs to send your noun gender but that doesn't really come up much currently.) the json gets parsed and (when successful) gets referred back to haskell code that basically goes "now generate the scene that happens when the player in this state interacts w/ the mob in this state"; these events are where the bulk of the content in the game is (by which i mean sex scenes).

    so these scene functions are all basically in a form of scene :: BodyGlob -> {mob data} -> [Event]. (where BodyGlob is the name of the type that has the player's parsed body data, and 'mod data' is just whatever that mob's data type looks like, since it's different for each one.) except that's actually not enough, since, uh, that's a linear stream of events and we kinda need a way to branch -- mostly on player choice; if there are branches on player body or on mob state, well, we have the data right there we can just do the branch in haskell code. but we need a way to encode a player choice, "go this way", "go that way". we could theoretically just nest events in events, but that seems... awkward.

    so for a while i just had a bunch of disconnected event functions like that, each with its own fragment of code -- here's the sex scene where you're a snake; here's the opening dialog if you approach when you're a centaur; here's the optional fisting scene -- and then kind of write in Choices events, which were just Choices [(String, String)], where the first string was some kinda text label actually presented as dialog ("yea lets fuck") and the second string was a scene identifier.

    so in the javascript -- the output of this api isn't a bunch of linear events. it's actually a lookup table: a list of events that are keyed by a scene identifier, and choices are made by looking up the scene identifier and running whatever event it matches.

    but i'm not actually writing scene identifiers for all these scenes. for a while i considered doing that manually, just taking all these scene fragments and making a lookup table and then filling out each choice with w/e scene identifier it needs.

    EXCEPT. this is kinda the thing haskell introductions talk about: okay so your scene identifiers are strings, right? but not all strings are valid scene identifiers. so what happens if you typo one, somewhere? what if you forget to include a scene in the scene index? whoops then at runtime when somebody is actually playing the game an event just kinda breaks. so maybe there's a way to elevate that runtime error into a compilation error, and a way to enforce that every scene referenced in a choice gets linked in the index?

    so to cut this whimsical exploration into haskell short i'll just give you the answer b/c it's late and i'm tired. here's what i did:

    Event becomes Event a, which is used only in the Choices constructor, which becomes Choices [(String, a)]. this also destroys the JSON instance for events, but that's okay: we duplicate event and call it RawEvent, and that has a Choices' constructor that still uses strings. that gets the JSON instance. the point of this is so that we can make a scene index type for each mob and use that.

    this also means all our mob scene functions turn into scene :: BodyGlob -> {MobData} -> [Event a]. we make a type alias for that, and a class to bind all of this together:

    type MarkedScene i mob = BodyGlob -> mob -> [Event i]
    type SceneBloc = (String, [RawEvent])
    
    class Scene i mob | i -> mob where
        link :: i -> MarkedScene i mob
        display :: i -> String

    where link demands we have a valid scene to present for each value in the index type (that's i), and display is used to render out the index type to dumb strings so we can actually run the events on the javascript side. these two functions in concert let us write a function like this:
    sceneDisplay :: Scene i mob => BodyGlob -> mob -> MarkedScene i mob -> [SceneBloc]
    

    which outputs the scene index as the final output of the api.

    (you might think that, if we can enumerate the index type, then we don't actually need to provide a scene to that function. that's right, except there's another concern i haven't mentioned yet, which is that we also don't want to overgenerate text. all this text is gonna be shuttled over from my server and it doesn't need to be every possible scene -- a lot of which are actually incoherent, like, we don't want to parse the "you are a snake" sex scene if yr a centaur. so no matter the body the vast majority of the scenes are gonna be unused or incoherent. so what we can do is pass in the root scene, the one that should run when a mob is first activated. and then, since all the branching happens through player choice, we can scan through it, extract all the scene labels that actually turn up in Choices, and output a result that includes the ones that are actually reachable at the point when the player triggers the mob.)

    so the function definition for sceneDisplay actually ends up looking like this:

    sceneDisplay :: Scene i mob => BodyGlob -> mob -> MarkedScene i mob -> [SceneBloc]
    sceneDisplay b m root = baseTable ++ snd (expandSceneFlow b m baseIndices baseTable)
        where
            baseTable = pure ("__activate", displayEvent <$> root b m)
            baseIndices = choiceTargets $ root b m
    
    expandSceneFlow :: Scene i mob => BodyGlob -> mob -> [i] -> [SceneBloc] -> ([i], [SceneBloc])
    expandSceneFlow _ _ [] scenekey = ([], scenekey)
    expandSceneFlow b m is scenekey = let
            (is', scenekey') = first concat $ unzip $ foo `mapMaybe` is
        in
            expandSceneFlow b m is' (scenekey ++ scenekey')
        where
            -- foo :: Scene i mob => i -> Maybe ([i], SceneBloc)
            foo i = case lookup (display i) scenekey of
                Just _ -> Nothing
                Nothing -> let
                        evs = (link i) b m
                    in
                        Just $ (choiceTargets evs, (display i, displayEvent <$> evs))
    
    choiceTargets :: [Event i] -> [i]
    choiceTargets = ...
    
    displayEvent :: Scene i mob => Event i -> RawEvent
    displayEvent = ...
    

    where we accumulate a scene index based on which events are actually reachable from the initial "__activate" event.

    (the ideal is still to not run anything server-side, but this is still reasonable to do i think, since after all if it's in javascript it's probably not gonna be lazy still.)

    anyway so that's what i did for hell game. well, 90% of it; i still need to hook some stuff up. and actually finish the sex scenes. there aren't many CLEVER TECHNICAL CONCERNS left to handle in hell game; it's mostly just... content. writing. lots and lots of writing.

    • Previous Entry
    • Add Memory
    • Share This Entry
    • Next Entry
    • Reply
Page generated Jan. 23rd, 2026 02:15 am
Powered by Dreamwidth Studios

Style Credit

  • Style: (No Theme) for vertical