Topic: Dialog Boxes: a component-based approach

Dialog Boxes: a component-based approach

it's been a while since nobody shares new objects... I decided to start a new thread here, and hopefully others will do the same. smile

revision 593 (download link) includes a new dialog box system (supporting avatars and text effects and transitions) that is very flexible, meaning that you can extend its functionality without modifying a single line of code!

it's possible to create dialog boxes that react to (and interact to) in-game events without needing to take care of bloody, gory details on "textout", offsets, etc. The method is to think as a dialog box as something purely abstract, and have other objects (its components) that take care of the complicated details. The result turned out to be clean and simple to use. smile

http://upload.surgeswarehouse.com/upload/s064.png

http://upload.surgeswarehouse.com/upload/Saturday24thofSeptember2011071637AM.png

//
// BASIC USAGE:
//
// Set $_dialogbox_text_id = <TEXT_ID> and $_dialogbox_avatar_id = <AVATAR_ID>
// before spawning '.dialogbox.default'. The DIALOGBOX_MESSAGE_<TEXTID> string,
// defined in the .lng file (in the languages/ folder) will be displayed along
// with the animation #<AVATAR_ID> of the DIALOGBOX_AVATAR sprite (defined in
// a .spr file of the sprites/ folder)
//
// The spawner object should have a state named '.dialogbox:on_destroy', which
// will be called when the dialog box gets destroyed. You may choose to take
// some action when this happens, or just 'return_to_previous_state'.
//
// If you don't want an avatar in your dialog box, just set $_dialogbox_text_id
// and spawn '.dialogbox.default[no_avatar]' instead.
// 
// Example:
//
// object foo {
//     requires 0.2.0
//     always_active // the spawner object should be always_active!
//
//     state main {
//         let $_dialogbox_text_id=0
//         let $_dialogbox_avatar_id=0
//         create_child .dialogbox.default
//         change_state freeze
//     }
//
//     state freeze {
//     }
//
//     state .dialogbox:on_destroy {
//         destroy
//     }
// }
//
//
// ADVANCED USAGE:
//
// If you want to create your own customized dialog box, script a new object
// called '.dialogbox.<PICK_A_NAME>'. Your customized dialog box may have
// any behavior you want. Here's what you need to do:
//
// 1. Upon creation, spawn the following objects:
//
//    a) .dialogbox.base.background
//    b) .dialogbox.base.avatar (optional)
//    c) .dialogbox.base.text
//    d) .dialogbox.base.button (optional)
//
//    These objects behave in a way that is documented in the base/ folder.
//    You may check that out, or read some examples below. These objects
//    provide default functionality.
//
//    This system is meant to be flexible enough, so if you need that, for
//    example, the dialog box behaves in a completely different manner,
//    you can replace its components by customized ones you write. For example,
//    you can replace '.dialogbox.base.background' by
//    '.dialogbox.base.background[slide_from_bottom]', and your dialog box will
//    suddenly become much richer.
//
//    You don't need to change existing code: just create new components, and
//    attach them to your objects. This is the beauty of the component-based
//    approach.
//
// 2. Your dialogbox must implement the following states:
//
//    a) .dialogbox.base.background:on_appear
//       Will be called when the dialog completes its "appearing" animation.
//       Normally you want to start writing the dialog text here. You do that
//       by changing the state of the text object to 'start'.
//
//    b) .dialogbox.base.background:on_disappear
//       Will be called when the dialog completes its "disappearing" animation.
//       Normally you will delete the object in this state (see #3).
//
//    c) .dialogbox.base.text:on_complete
//       Will be called when the full text is rendered to the screen.
//
// 3. Upon deletion, it's mandatory to change the state of its parent object to
//    '.dialogbox:on_destroy' !
//
// Whenever you want to show your dialogbox in the game, spawn the
// '.dialogbox.<DIALOGNAME>' object in the level. Your other scripts should NOT
// have any logic to handle dialog boxes other than that. The dialog boxes
// should take care of themselves.
//
// Upon deletion, the dialog box will change the state of its parent object to
// '.dialogbox:on_destroy'. You may choose to take some action when this state
// gets called, or maybe just 'return_to_previous_state'.
//

Simple example:

object balloon.example
{
    requires 0.2.0
    category Balloon
    annotation "An example balloon"

    state main
    {
        set_animation DIALOGBOX_BALLOON 0
        elliptical_trajectory 0 10 0 1 0
        on_player_collision display_balloon
    }

    state display_balloon
    {
        let $_dialogbox_text_id=1
        let $_dialogbox_avatar_id=1
        create_child .dialogbox.default
        change_state wait
    }

    state wait
    {
        disable_player_movement
    }

    state .dialogbox:on_destroy
    {
        enable_player_movement
        change_state activate_again
    }

    state activate_again
    {
        on_timeout 2.0 main
    }
}

Advanced example:

object balloon.example2
{
    requires 0.2.0
    category Balloon
    annotation "An example balloon (advanced)"

    state main
    {
        set_animation DIALOGBOX_BALLOON 0
        elliptical_trajectory 0 10 0 1 0
        on_player_collision display_balloon
    }

    state display_balloon
    {
        create_child .dialogbox.balloon.advancedexample // a customized dialog
        change_state wait
    }

    state wait
    {
        disable_player_movement
    }

    state .dialogbox:on_destroy
    {
        enable_player_movement
        change_state activate_again
    }

    state activate_again
    {
        on_timeout 2.0 main
    }
}

// --------------------------------------

object .dialogbox.balloon.advancedexample
{
    requires 0.2.0
    always_active
    detach_from_camera

    state main
    {
        let $step=0
        let $_dialogbox_text_id=1
        let $_dialogbox_avatar_id=1

        hide
        create_child .dialogbox.base.background[slide_from_top] 0 0 background
        create_child .dialogbox.base.avatar 0 0 avatar
        create_child .dialogbox.base.text[typewriter] 0 0 text
        create_child .dialogbox.base.button 0 0 button
        change_state wait
    }

    state wait
    {
    }

    state .dialogbox.base.background:on_appear
    {
        change_child_state text start
        change_state wait
    }

    state .dialogbox.base.background:on_disappear
    {
        change_child_state background destroy
        change_child_state avatar destroy
        change_child_state text destroy
        change_child_state button destroy
        change_parent_state .dialogbox:on_destroy
        destroy
    }

    state .dialogbox.base.text:on_complete
    {
        let $step+=1
        let $_dialogbox_text_id+=1
        let $_dialogbox_avatar_id+=1

        change_child_state button continue
        if $step<2 wants_to_refresh

        change_child_state button stop
        change_state completed
    }

    state wants_to_refresh
    {
        on_button_pressed fire1 refresh
    }

    state refresh
    {
        change_child_state text refresh
        change_child_state avatar refresh
        change_state wait
    }

    state completed
    {
        on_button_pressed fire1 disappear
    }

    state disappear
    {
        change_child_state background disappear
        change_state wait
    }
}

Tip: on the example above, replace the line

create_child .dialogbox.base.background[slide_from_top] 0 0 background

by

create_child .dialogbox.base.background[slide_from_bottom] 0 0 background

and see the magic occur. You can also design your own different effects. smile

Re: Dialog Boxes: a component-based approach

Most impressive! I experimented with the dialog boxes a bit before, but I was thinking that it would prolly be a smart move to skip the balloons during the tutorial level just showing the dialogs.