Write a Plugin that reads Notetags - Beginner

● ARCHIVED · READ-ONLY
Started by Aloe Guvner 21 posts Page 1 of 2 View original ↗
  1. Write a Plugin that reads Notetags - Beginner

    Happy Tutorial Tuesday!

    This tutorial will teach you how to read an actor's "race" (like Human, Goblin, Dwarf) from their notetag in the database, and display it in the Status Menu.

    What should you know before getting started?

    This is a beginner level tutorial, and almost everything will be explained in detail. That being said, it would help to have a basic understanding of the following concepts:
    • What a Javascript variable is
    • The sections of the database (Actor, Item, etc.)
    • The difference between assigning a value to a variable vs. checking for equality in a variable
      • i.e. `var myVar = 3` vs. `myVar === 3`
    • How to create folders and files on your computer
    ---------------------------------------------------------------
    What will you learn?

    You will learn how to:
    • Read basic notetag data
    • Alias a function
    • Create a new property on an actor
    • Write a script call that can be used in events
    • Create a plugin command
    • Slightly alter the Status Menu

    ---------------------------------------------------------------
    Let's get started!

    ---------------------------------------------------------------​

    RPG Maker MV version

    This tutorial is written using modern Javascript standards (ES2015+) that are available in MV v1.6+. For old versions of MV, be sure to use the old Javascript code that is provided. Look out for the sections labelled {{Old MV}}.

    If only one version of code is provided, it will work with any version.

    ---------------------------------------------------------------

    Setup

    This tutorial is best followed on a blank project with no plugins installed.
    If your code isn't working, you will know it is a mistake that you made rather than an incompatibility with another scripter's code. After you have tested your plugin in a blank project, then you can integrate it with existing projects.

    In order to write the code, you will need a text editor. Some recommendations are Notepad++, Visual Studio Code, Sublime Text, Atom, Brackets.

    Start by creating a blank Javascript file (.js file) named whatever you would like.

    ---------------------------------------------------------------
    Tutorial

    This tutorial will teach you how to read an actor's "race" (like Human, Goblin, Dwarf) from their notetag in the database, and display it in the Status Menu.

    1.)
    Add the following text to Harold's notetag in the database:
    Code:
    <race:Human>

    ---------------------------------------------------------------

    2.)
    Run the game in test mode. On the Title screen, press F8 to open the console, and we will discover the 'meta' property that is on all database objects. In the console, type the following and press enter:
    Code:
    $dataActors[1]

    Expand the data using the arrows, and you will see all of the data from the Database entry for Harold, including the "race" notetag you have added.

    Console Screenshot
    MetaNotetagConsole.png

    How did this get there?

    The RPG Maker MV developers added this `meta` property to make it easy to read notetag data.

    ---------------------------------------------------------------

    3.)
    Begin writing the plugin, specifically, the section to read the notetag data. In your .js file, write the following:

    Code:
    // Retain a reference to the original function
    const Game_Actor_setup = Game_Actor.prototype.setup;

    {{Old MV}}
    Replace `const` with `var`

    This creates an alias of the existing `setup` function for actors. In Javascript, you can completely overwrite functions; so by doing this, we will keep a reference to the original function.

    ---------------------------------------------------------------

    4.)
    Below that, we will define our new actor `setup` function.

    Code:
    Game_Actor.prototype.setup = function (actorId) {
       // Call the original function
       Game_Actor_setup.call(this, actorId);
    };

    We call the original function so that the normal setup of the actors still happens.

    ---------------------------------------------------------------

    5.)
    Now we can add one more line to our `setup` function to read from the `meta` data that we examined in step 2.

    Code:
    Game_Actor.prototype.setup = function (actorId) {
       // Call the original function
       Game_Actor_setup.call(this, actorId);
       this.race = $dataActors[actorId].meta.race || null;
    };

    The actor now has a `race` property on it that is set to whatever was written in the notetag!

    Let's stop for a quick Q & A about this.

    ---------------------------------------------------------------

    Q & A - Round 1
    Why did we go through the trouble of aliasing and calling the original function? Why not just copy the whole function from `rpg_objects.js` and add our line to the end?

    We could have, and this would have produced an identical result.

    However, by overwriting the `setup` function without keeping a reference to the original, we would have made our plugin incompatible with any other plugins that use the same `setup` function.

    What is the difference between 'Game' Actor and 'Data' Actor? When do we use one or the other?

    The 'Game' Actor is the living, changing actor. It represents all of the changeable data about the actor, such as HP, MP, and Level. This is saved/loaded to/from the savefiles.

    The 'Data' actor is completely static data that is defined in the editor Database and stored in the /data/Actors.json file. This data is read fresh from the json file every time the game is booted.

    What does `this` mean in Javascript?

    `this` is a variable that represents the context that a function is invoked in. In our case, it refers to the actor that is currently getting setup, so it could be Harold, Therese, etc.
    Here is a more thorough article explaining this concept.

    What does the `||` operator do?

    This is Javascript's `or` operator. We're using it to check whether the race notetag actually exists or not, and if it doesn't exist, then we set the value to `null`.

    What is `null` and why use it?

    `null` in Javascript is when the developer wants to explicitly declare that a variable/property has no value. We use it in this circumstance to avoid crashing the game if somebody didn't add a "race" notetag to the actor.

    ---------------------------------------------------------------

    6.)
    Now that "race" is added as a property on the actors, we can easily access it. As a quick example, we'll overwrite the `drawActorNickname` function in the Status screen to draw the actor's race instead of their nickname.

    Code:
    Window_Status.prototype.drawActorNickname = function (actor, x, y, width=270) {
       this.resetTextColor();
       // If the actor has a defined race, we'll draw it
       if (actor.race) {
          this.drawText(actor.race, x, y, width);
       }
    };

    {{Old MV}}
    Remove the `=270` from the function arguments and add this line as the first line of the function:
    Code:
    width = width || 270;

    Actor race in status menu
    HaroldStatusScreen.png

    Why include an "if" statement? What is it doing?

    Remember before, how if there was no "race" notetag on the actor, we saved the "race" value as `null`?
    This `if` conditional statement is checking whether the actor has a legitimate value for their race before drawing that text.

    ---------------------------------------------------------------

    7.)
    Create a script call that can change the race of the actor.

    Perhaps a mysterious actor starts with a race of "???" and is revealed later, or they change races over the course of the story (Human --> Werewolf). We will create a function here that can be called to change an actor's race.

    Code:
    Game_Actor.prototype.changeRace = function (newRace) {
       this.race = newRace;
    };

    This function takes one input, or `argument` of the new race for the actor, and updates the actor's "race" property to the new value.

    During the game, this function can now be called like this:
    Code:
    // Change the race of actor #1 (Harold) to 'Werewolf'
    $gameActors.actor(1).changeRace('Werewolf')
    Notice how the world 'Werewolf' had to be in quotes. This is because it is a string (text).

    ---------------------------------------------------------------
    8.)
    Create a plugin command that can change the race of the actor.

    We start by aliasing a reference to the `pluginCommand` function.

    Code:
    // Retain a reference to the original function
    const Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;

    {{Old MV}}
    Replace `const` with `var`

    Then, we overwrite the `pluginCommand` function but make sure to call the original function.

    Code:
    Game_Interpreter.prototype.pluginCommand = function (command, args) {
       Game_Interpreter_pluginCommand.call(this, command, args);
    };

    **It is very important to alias and call the original function. If you don't, you will break the plugin commands of all other plugins installed above this one in your projects.

    ---------------------------------------------------------------

    9.)
    Add the logic for our plugin command.

    Plugin commands are structured like this:
    Code:
    command arg[0] arg[1] arg[2] arg[3] arg[4] ... arg[n]

    So we decide that we will structure our command as:
    • command --> "ChangeRace"
    • arg[0] --> The actor ID to change
    • arg[1] --> The new race

    Hey, what's with the square brackets? Is this a new type of variable?

    The arguments are given to us in an 'array' format, which is like a list. Arrays are zero-indexed, which means the first item is considered to be a position 0.

    We can now modify the function to use our logic.

    Code:
    Game_Interpreter.prototype.pluginCommand = function (command, args) {
       Game_Interpreter_pluginCommand.call(this, command, args);
       // If the plugin command is ours
       if (command.toLowerCase() === "changerace") {
          // Grab the arguments
          const id = parseInt(args[0]);
          const newRace = args[1];
          // Change the actor's race
          $gameActors.actor(id).changeRace(newRace);
       }
    };

    {{Old MV}}
    Replace `const` with `var`

    Now, using a Plugin Command of:
    Code:
    ChangeRace 1 Werewolf

    Harold's race will be changed to 'Werewolf'.

    What is parseInt? Why didn't we have to include quotes in the race 'Werewolf' like before?

    The arguments are given to us already in a string format, so we need to ensure any numbers are actually converted to numbers (int means integer).

    What is `toLowerCase()`?

    This is a function we can use on strings to convert all letters to lower case. It makes the plugin command more friendly, because it's no longer case-sensitive.

    ---------------------------------------------------------------

    The final plugin that was created in this tutorial can be referenced here.
    The tutorial in its original format is available here.
  2. @Aloe Guvner I'm going to move this to Learning Javascript, it's better suited there.

    @Archeia @taarna23 Can one of you look at this?
  3. It looks fine to me but I think it'll disappear in Learning Javascript so I'll pin it.
  4. @Archeia If it would be better suited in tutorials, that's fine, I just felt since it was Javascript specific, it should go here. There are learning Javascript tutorials in this section as well.
  5. THANKS A LOT ALOE.... i will learn from here!! ^^
  6. Man I have read tutorials, I have watched many videos, and I have tried to understand how plugin commands are implimented. You broke this down so well that I can actually understand what I am doing now. Thank you for this Aloe. This is a game changer for me!
  7. I'm pretty sure I followed this tutorial to the letter, yet when I add my plugin into my game nothing shows up in the Parameters field of the plugin menu. Is it supposed to be that way, or am I doing something wrong?
  8. @Bridgeman This tutorial will not insert any plugin parameters. I think you understand something wrong.
    This is for a plugin to be able to read Actor note tags.
    You can see the entire plugin made in this tutorial here.
  9. Yeah, gotcha. Just a misunderstanding on my part.
  10. I usually go in Search from examples that people created
    But only a few people who write well describe this way. Thank you.
  11. Yes! This is eactly what I need next! After reading hiddenones camping item tutorial, I have been playing around with custom notetags. Currently I am only using custom notetags to build arrays of items as sub categories and saving the arrays to their own game variable. Seeing how easy it will be to give myself the ability to modify the meta data opens the door to take this even further. After landing on storing arrays in a game variable, one thought that keeps popping up is creating some kind of reputation or relationship system.
  12. This is really cool, I wish someone could do this for MZ plugins.
  13. ultimagicka said:
    This is really cool, I wish someone could do this for MZ plugins.
    There is no difference. MZ objects have the same meta property.
  14. Trihan said:
    There is no difference. MZ objects have the same meta property.
    I actually went through this tutorial earlier and got everything working in MZ till I got to the Plugin Command section. I just need to learn more about how to make/use plugin commands.
  15. ultimagicka said:
    Thank you, I have that link. What I wish there was is something that goes more into the Plugin creation instead of spending most of the doc talking about JavaScript.
    Oh, sorry! I just did a search and found no mention of PluginManager in that, I probably just misremembered... :kaoslp:

    There's some stuff near the bottom of the MV / MZ Script Call List in case that helps:
    E.g. this is the MZ example for "Define Plugin Commands" on that sheet:

    // Register command Open Equips for plugin Shortcuts.js PluginManager.registerCommand( 'Shortcuts', 'Open Equips', args => { const actorId = parseInt(args['Actor ID'], 10); if (actorId) { $gameParty.setMenuActor(actorId); SceneManager.push(Scene_Equip); } } );
    MZ also has the header directives @command etc. Those are described on that sheet as well. Plugin Commands are the only major difference between MV and MZ plugin structures, but of course you should also make sure you're familiar with the relevant parts of the core scripts that you want your plugin to modify~
  16. caethyril said:
    Oh, sorry! I just did a search and found no mention of PluginManager in that, I probably just misremembered... :kaoslp:

    There's some stuff near the bottom of the MV / MZ Script Call List in case that helps:
    E.g. this is the MZ example for "Define Plugin Commands" on that sheet:

    // Register command Open Equips for plugin Shortcuts.js PluginManager.registerCommand( 'Shortcuts', 'Open Equips', args => { const actorId = parseInt(args['Actor ID'], 10); if (actorId) { $gameParty.setMenuActor(actorId); SceneManager.push(Scene_Equip); } } );

    MZ also has the header directives @command etc. Those are described on that sheet as well. Plugin Commands are the only major difference between MV and MZ plugin structures, but of course you should also make sure you're familiar with the relevant parts of the core scripts that you want your plugin to modify~
    Another good resource that I have been looking at as well. Thank you. I will just have to do like you mentioned and dig into the core scripts as well as the plugins that came with MZ to get better acquainted with proper syntax and coding practices. Thank you again for your replies.
  17. How would I use notes I added to an event? I have seen it in a lot of plugins, but they are so convoluted with other logic that I can't really see through it :D
    My ultimate goal is to add some control into the events' life cycle at runtime, like in the Community Lighting plugin.
  18. @NomenNescio - for an event, you can get the standard notetag data as ev.event().meta, where ev is a Game_Event instance. Otherwise everything else works the same way. :kaohi:

    (Comment tags, i.e. plugin data written in a Comment command, are not the same as notetags. They have to be parsed manually, at a timing appropriate for whatever the tag is doing. If you want help with comment tags, consider starting a thread in Learning JavaScript.)