Site Logo


Sparen's Danmakufu ph3 Tutorials Lesson 25 - Understanding and Manipulating @Event

Part 1: What will be covered in this lesson?

In these two lessons (25 and 26), I will cover Events in Danmakufu. This lesson will function as an introduction to Events in general, with parallels drawn from other programming languages, and will also serve as a refresher for the existing events in Danmakufu that are handled automatically by the system, as well as potential uses of these built-in Events.

These two guides will assume that you are familiar with Common Data and will also assume that you are familiar with scripts in Danmakufu in general.

Part 2: What Events have we already seen?

We've been mentioning Events on and off throughout these guides. Back in Lesson 6, we discussed @Event briefly in the context of setting up a boss's lifebar, timer, and spell bonus. Here we reviewed EV_REQUEST_LIFE, EV_REQUEST_TIMER, and EV_REQUEST_SPELL_SCORE, which are used exclusively for Single scripts that handle boss attacks. In Lesson 7, we added EV_REQUEST_IS_DURABLE_SPELL to this list.

In Lesson 19, we covered the Events called in the default system script: EV_GAIN_SPELL and EV_START_BOSS_SPELL. We'll be taking a closer look at this later due to the way Danmakufu opens another script in the System file.

Finally, in Lesson 23, we discussed the automatic Pause events and how to work with these.

All of the events we have currently discussed are but a subset of the full list. There are plenty of built-in events that can be browsed at the Official Danmakufu ph3 Documentation: 'イベント(@Event)' on the sidebar. We'll be discussing the majority of these in this guide, the next one (on EV_USER), and the player script creation guide.

So we have all of these events. What are they, and how do they work exactly?

Part 3: What are Events?

Consider the following scenario:

Suppose we are on a website with only a single button on it smack in the middle. We move the mouse around, we attempt to scroll, etc. But nothing happens. But then we click the button.

When the button is clicked, the website responds and replaces the contents of the screen with cat videos. The clicking of the button is an event - specifically, the Javascript onclick event. Not surprisingly, the function call or code bound to the button's onclick event triggers when you, well, click the button.

Wikipedia's page on Events states that "an event is an action or occurrence recognized by software, often originating asynchronously from the external environment, that may be handled by the software." Translated into English, that means that an event is some kind of action that does not have a set time to execute, and is handled by the software. In Danmakufu, events are automatically called when needed, and for all scripts currently running, the @Event routine is checked to see if there is any code to execute with respect to the given event.

CHECKPOINT: At which points in time do events NOT fire/execute?

Part 4: How do I use Danmakufu's built-in Events?

So let's discuss usage of the built-in Events. For example, let's say that we want to have an Extend system based on player graze. We can use EV_GRAZE for this - when this event happens to be called, run some code to handle the Extends. Of course, you can have an EV_GRAZE check in multiple scripts or just one, and it will trigger in all of them. But as long as the player isn't grazing, the event will never fire.

Many of the event we've seen before fire at a specific time, but asynchronously with regards to the overall program flow. The @Event routine will run any code in the case block. This means that as long as you know the list of supported events in Danmakufu and under what conditions they fire, you can hook any supported code you want to the occurrence of a given event. Want to have the player automatically bomb when they are hit? In a player script, call the spellcard (if they have any left) using EV_HIT. Want to implement a continue menu? EV_PLAYER_SHOOTDOWN is the event that runs when the player is hit when they are out of lives and fail to deathbomb.

One big usage is to do specific things on spell capture. For example, see the following code (see Default_System.txt for a similar version):

    alternative(GetEventType())
    case(EV_GAIN_SPELL) {
        let objScene = GetEnemyBossSceneObjectID();
        let score = truncate(ObjEnemyBossScene_GetInfo(objScene, INFO_SPELL_SCORE) / 10) * 10;
        TGainSpell(score); //task for drawing spellcard bonus on screen
    }

In the above code, on the GAIN_SPELL event, we set the ones digit of the spellcard bonus to 0 (typically, the ones digit is reserved for # continues currently used, hence the purging of the ones digit from spellcard bonuses above) and then run a task to display the spellcard bonus in text on the screen. If we want to, we can handle all kinds of things here as well - applying the bonus to the player's score, toggling various settings, etc. It's up to you as for what you want to put in the event block.

There are many ways to utilize the built-in events in Danmakufu. Many of these will be discussed alongside player scripts, but with what you know now, it shouldn't be too hard to play sound effects on certain events, etc.

Of course, all this being said, the true flexibility of events is in NotifyEventAll and User Defined Events - the topic of the next lesson.

Part 5: How do I handle Event Arguments?

Back when we discussed the Single script events, we used a function called SetScriptResult() with a value. For example, case(EV_REQUEST_LIFE){SetScriptResult(2500);} to set the boss's HP to 2500 units. In this case, the REQUEST_LIFE event requires a return value that MUST be passed back using SetScriptResult() inside the case block.

There are a number of events that require return values using the above method. But there are also events that provide values. These can be acquired using GetEventArgument(), which we will see a lot of in the next lesson.

For example, when we discuss custom items, we will need to handle EV_GET_ITEM, where GetEventArgument(0) returns the type of item picked up by the player.

Part 6: What things should I be careful with when using Events?

To close this lesson, we will discuss pitfalls and things to avoid when working with events.

First and foremost, it is important to keep track of where you are handling events. Are you handling x Event in the System script? In the stage? Both? Keep track of where you're handling events and they will be much easier to debug afterwards. Personally, I recommend keeping track of all events in your stage script with the exception of System-specific events. For communication with Background scripts and other scripts, it will typically be best to minimize usage of @Event and utilize CommonData instead, though this is up to the scripter.

Finally, restrictions. The most pressing are EV_PAUSE_ENTER and EV_PAUSE_LEAVE. These are triggered by user input regardless of whether the main game is playing or a replay is playing. Therefore, usage of rand, creation of bullets, and creation of enemies is explicitly banned in the case blocks for these events. Failing to uphold these design constraints will result in replay desync on pause.

In addition to the above, there are many events that are meant for use in player scripts. Usage of these events outside of player scripts can have unforseen consequences. For example, EV_GRAZE is expressly meant for usage inside player scripts (for particle effects and sound effects). Therefore, it is designed to fire at most once per frame. This means it will fire once if the player grazes one bullet in that frame, and will also fire once if the player grazes twenty bullets in that frame. Keep this in mind when designing graze-based systems.

Finally, as a note for the next lesson, calling events within events when using arguments is bad practice. This is because calling an event within an existing event will overwrite GetEventArgument() for the outer event.

Quiz: Events

1) Let's say that we have a Stage, a Player, and a System script. Each has a case block for EV_GRAZE. Suppose we have a graze-based extend system, where getting 1000 graze provides the player with a bomb. However, when running the game, grazing a single bullet seems to be adding more than one point to this extend system. What is the likely cause for the problem and a potential solution?

A. EV_GRAZE increments at most once per frame; Don't use EV_GRAZE
B. Points are being incremented in multiple scripts; Remove duplicate incrementation code
C. Intended Behavior

2) In a script whose gimmick is to steal a player's bomb if they time out a spellcard, which of the following events would be most effectively utilized for the removal of the player bomb if the scripter chose to utilize events for this problem?

A. EV_TIMEOUT
B. EV_END_BOSS_STEP
C. EV_REQUEST_TIMER

Summary

  • Events are called asynchronously
  • Danmakufu Events can receive arguments and return values
  • Danmakufu Events are often meant to be used in certain script types and may not function properly in other script types

Sources and External Resources

N/A