Events as Scripts (with a use case)

● ARCHIVED · READ-ONLY
Started by kmph 1 posts View original ↗
  1. Why such a plugin?

    The Event Editor is easy to use, however, it is not powerful enough to support more complex events. Its many drawbacks like the need to emulate 'and' conditions by nesting 'if's and 'or' conditions by labels, lack of support for creating a dynamic list of choices, lack of support for designating faces shown by a `Show Text` command from variables, lack of support for subroutines, etc., etc. Finally, writing things down may be faster and more convenient for some folks than clicking everything out.

    Imagine you want your party leader to speak, but you don't know who's the party leader compile-time. Imagine you want to implement a noticeboard where quests hang; you wish to show the player choices which quest to read, but different quest will become available or unavailable as the game progresses. All of this would be a pain to implement in the editor.

    While the script calls are released ( link ), this list is nevertheless incomplete as it ommits necessary details like the need to set the game interpreter to the correct wait mode after '$gameMessage.add()'. Also having to call a few API functions to display a text or to show choices is cumbersome.

    My plugin (WiP, obviously) aims to abstract all of this away and provide a (relatively) easy-to-use way to call event commands from scripts.

    Use case:

    First how would you implement such a notice board with my plugin:

    Code:
    var SC = SC || {}; // Your own namespace for your subroutines
    
    (function() {
       
       SC.leaderSpeak = function(cntxt, lines) {
           
           var leaderName;
           var leaderFaceset;
           var leaderFaceno;
           switch($gameVariables._data[1]) {
           case 0: leaderName = 'Harold'; leaderFaceset = 'Actor1'; leaderFaceno = 0; break
           case 1: leaderName = 'Therese'; leaderFaceset = 'Actor1'; leaderFaceno = 7; break
           case 2: leaderName = 'Marsha'; leaderFaceset = 'Actor3'; leaderFaceno = 7; break
           case 3: leaderName = 'Lucius'; leaderFaceset = 'Actor2'; leaderFaceno = 6; break
           }
           
           cntxt.showText(
               leaderFaceset, leaderFaceno,
               cntxt.BackgroundWindow, cntxt.TextBottom,
               ['\\C[7]'+leaderName+':\\C[0]'].concat(lines)
           )
       }
       
       SC.textBlank = function(cntxt, lines) {
           cntxt.showText(cntxt.NoImage, 0, cntxt.BackgroundWindow, cntxt.TextBottom, lines)
       }
    
       GZKM.eventCommands['noticeboard'] = function*() {
           
           var quests = ($gameVariables._data[2] = $gameVariables._data[2] || [
               // Completed quest will become no longer available and not shown on the noticeboard
               {name: 'Bat slaying', available: true, active: false},
               {name: 'Potion collecting', available: true, active: false},
               // This quest will become available when the story progresses much further
               {name: 'Kill The Mighty Dragon', available: false, active: false}
           ])
           
           var availableQuests = quests.filter(function(q){return q.available})
           var questNames = availableQuests.map(function(q){return q.name})
           
           if(availableQuests.some(function(q){return !q.active}))
               SC.leaderSpeak(this, ['It looks like there are new quests', 'on the noticeboard!'])
           else SC.leaderSpeak(this, ['Let’s see the requests again…'])
           
           this.showChoices(this.BackgroundWindow, this.ChoicesRight, questNames, this.NoDefault, this.CancelBranch)
           yield
           
           if(this.choice == this.CancelBranch) return
           
           var chosenQuest = availableQuests[this.choice]
           chosenQuest.active = true // this will even be saved when the game is saved!
           switch(chosenQuest.name) {
           case 'Bat slaying':
               SC.textBlank(this, ['The bats have overrun my attic!', 'Righteous Heroes, please help!', '    --Lucresia'])
               break
           case 'Potion collecting':
               SC.textBlank(this, ['I will pay handsomely anyone who brings me 20 potions.', '    --Shopkeeper'])
               break
           case 'Kill The Mighty Dragon':
               SC.textBlank(this, [
                   'You’ve come far, heroes, but your end is nigh!',
                   'I challenge you to come to my cave and try and kill me!',
                   'You will surely die! Mwahwahwahwahwa!',
                   '    --Dragon the Mighty and Evil'
               ]); yield
               SC.leaderSpeak(this, ['I think we should prepare well, team,', 'for I believe this will be our', 'final boss battle!'])
           }
           return
       }
       
    })()

    A few words of explanation. My plugin exposes the object GZKM.eventCommands, whose properties will be function generators executed when you call a plugin command with the name of the property. So, with the above code, you could make the noticeboard functional just like this:



    These function generators will be bound to an object that exposes functions like showText() or setMovementRoute() and constants like ThisEvent or BackgroundWindow.

    Why generators? Well, the problem is that the script must release control after certain commands like showChoices(), so if I was to allow putting any command afterwards, I had to provide some way to ensure that when the user selects some choice then the control will go to any command after the showChoices() command rather then running the script from the beginning.

    Smaller events can also be written in in-event scripts like this:



    (sorry for this bizzarre architecture, I'm not a very experienced programmer... Suggestions are welcome.)

    Below comes a list of implemented commands:

    Show Text:

    Code:
    this.showText(faceset, faceNo, background, position, textLines)

    faceset: the name of the faceset like 'Actor1' or if there is to be no image an empty string or this.NoImage
    faceNo: ignored if faceset is this.NoImage or ''
    background: this.BackgroundWindow, this.BackgroundDim, this.BackgroundTransparent (or, respectively, 0, 1, 2)
    position: this.TextTop, this.TextMiddle, this.TextBottom (or, respectively, 0, 1, 2)
    textLines: An array of strings to be displayed, one string per line.

    You must do a 'yield' after calling this unless you immediately afterwards call showChoices, inputNumber or selectItem, in which case it may be useful to 'yield' after this latter command.

    Show Choices:

    Code:
    this.showChoices(background, position, choices, default_choice, on_cancel)

    background: this.BackgroundWindow, this.BackgroundDim, this.BackgroundTransparent (or, respectively, 0, 1, 2)
    position: this.ChoicesLeft, this.ChoicesMiddle, this.ChoicesRight (or, respectively, 0, 1, 2)
    choices: An array of strings, each representing one choice.
    default_choice: An index of the above array or this.NoDefault (alternatively: -1)
    on_cancel: An index of the above array or this.CancelBranch (alternatively: -2) or this.CancelDisallow (alternatively: -1)

    Must yield after calling this. When control returns you will have the selected choice in this.choice. This will be either an index of the choices array or this.CancelBranch (or -2) if this was allowed.

    Input Number:

    Code:
    this.inputNumber(variable, digits)

    variable: variable index which the number will be written to.
    digits: how many digits is the number allowed to have.

    Must yield after calling this.

    Select Item:

    Code:
    this.selectItem(variable, itemCategory)

    variable: variable index which the item will be written to.
    itemCategory: either of this.RegularItem, this.KeyItem, this.HiddenItemA, this.HiddenItemB (or, respectively, 1, 2, 3, 4)

    Must yield after calling this.

    Show Scrolling Text:

    Code:
    this.showScrollingText(speed, fastForward, textLines)

    speed: integer, as in the editor.
    fastForward: true or false.
    textLines: array of strings, one string <=> one line shown.

    Switches, Variables, Items, Stats etc:

    Not (yet) implemented. In most cases there's little to abstract away, anyway. I wonder if I shouldn't simply document RMMV API in this section. (document more throroughly than the aforemented sheet).

    If you need the current instance of Game_Interpreter (holds the current MapID and EventID), it is this.gameInterpr.

    Flow Control:

    Would be pointless to provide wrappers for this. JS is much more powerful. (OK, labels and exit event processin should prob be exceptions)

    Transfer Player:

    Code:
    this.transferPlayer(mapId, x, y, direction, fade)

    mapId, x, y: the destination.
    direction: Either of this.DirectionRetain, this.DirectionDown, this.DirectionLeft, this.DirectionRight, this.DirectionUp. Or, respectively, 0, 2, 4, 6, 8.
    fade: Either of this.FadeBlack, this.FadeWhite, this.FadeNone. Or, respectively, 0, 1, 2.

    Must yield after calling this.

    Set Vehicle Location:

    Code:
    this.setVehicleLocation(mapId, x, y, vehicle)

    mapId, x, y: the destination.
    vehicle: either of this.Boat, this.Ship, this.Airship (or, respectively, 0, 1, 2)

    Set Event Location:

    Code:
    this.setEventLocation(x, y, dir, ev)
    x, y: destination.
    dir: direction. Either of this.DirectionRetain, this.DirectionDown, this.DirectionLeft, this.DirectionRight, this.DirectionUp. Or, respectively, 0, 2, 4, 6, 8.
    ev: event to move. Either event ID or this.ThisEvent (or, alternatively, 0)

    Code:
    this.swapEventLocations(dir, thisEvent, otherEvent)
    dir: as above.
    thisEvent: event to move, as above.
    otherEvent: event to swap with, syntax as above.

    Scroll Map:

    Code:
    this.scrollMap(dir, speed, dist)

    dir: this.DirectionDown, this.DirectionLeft, this.DirectionRight or this.DirectionUp. Or, respectively, 2, 4, 6 or 8.
    speed: this.Slower8, this.Slower4, this.Slower2, this.NormalSpeed, this.Faster2, this.Faster4; or, respectively, 1, 2, 3, 4, 5, 6.
    dist: distance, an integer, as in the editor.

    Now you don't yield after calling this since it is possible to, for example, show a text while the screen is scrolling. If you must ensure the previous scrolling has already ended (for example to call this function again), you can call this:

    Code:
    this.mustWaitForScrolling()

    If this function returns true you must yield.

    Set Movement Route:

    Code:
    this.setMovementRoute(ev, repeat, skippable, wait, moveCommands)

    ev: event whose movement route is to be set: this.Player (or -1), this.ThisEvent (or 0), or event ID.
    repeat: true or false.
    skippable: true or false.
    wait: true or false.
    moveCommands: An array of move commands, see below.

    If wait is true you must yield after calling this.

    move commands:

    this.MoveDown, this.MoveLeft, this.MoveRight, this.MoveUp, this.MoveLowerLeft, this.MoveLowerRight, this.MoveUpperLeft, this.MoveUpperRight, this.MoveAtRandom, this.MoveTowardPlayer, this.MoveAwayFromPlayer, this.MoveForward, this.MoveBackward, this.Jump(dx, dy), this.MovementWait(how_many_frames), this.TurnDown, this.TurnLeft, this.TurnRight, this.TurnUp, this.TurnRightHand, this.TurnLeftHand, this.TurnOpposite, this.TurnRightOrLeftHand, this.TurnAtRandom, this.TurnTowardPlayer, this.TurnAwayFromPlayer, this.SwitchOn(switchIndex), this.SwitchOff(switchIndex), this.Speed(this.Slower8 or this.Slower4 or this.Slower2 or this.NormalSpeed or this.Faster2 or this.Faster4 or, respectively, 1 or 2 or 3 or 4 or 5 or 6), this.Frequency(this.LowestFrequency or this.LowFrequency or this.NormalFrequency or this.HigherFrequency or this.HighestFrequency or, respectively, 1 or 2 or 3 or 4 or 5), this.WalkingAnimationOn, this.WalkingAnimationOff, this.SteppingAnimationOn, this.SteppingAnimationOff, this.DirectionFixOn, this.DirectionFixOff, this.ThroughOn, this.ThroughOff, this.TransparentOn, this.TransparentOff, this.ChangeImage(spriteset, spriteNo), this.ChangeOpacity(opacity), this.ChangeBlendMode(this.BlendNormal or this.BlendAdditive or this.BlendMultiply or this.BlendScreen or, respectively, 0 or 1 or 2 or 3), this.MovementPlaySE(name, pan, pitch, volume), this.MovementScript(script_as_string)

    Example:

    Code:
    this.setMovementRoute(this.ThisEvent, false, false, true, [this.Jump(-3, -3), this.MovementWait(15), this.MoveDown, this.TurnRight])

    Get On/Off Vehicle:

    Code:
    this.getOnOffVehicle()

    (nothing to abstract away in this case actually, provided just for consistency)

    Change Transparency:

    Code:
    this.changeTransparency(transparency)

    transparency: true or false

    (nothing to abstract away in this case actually, provided just for consistency)

    ChangePlayerFollowers:

    Code:
    this.changePlayerFollowers(followers)

    followers: true or false

    Gather Followers:

    Code:
    this.gatherFollowers()

    Must yield after calling this.

    Show Animation:

    Code:
    this.showAnimation(ev, wait, anim)

    ev: event; this.Player (or -1), this.ThisEvent (or 0), or event ID.
    wait: true or false.
    anim: animation ID.

    If wait is true then must yield after calling this.

    Show Balloon Icon:

    Code:
    this.showBalloonIcon(ev, wait, balloon)
    ev: event; this.Player (or -1), this.ThisEvent (or 0), or event ID.
    wait: true or false.
    balloon: Either of: this.Exclamation, this.Question, this.MusicNote, this.Heart, this.Anger, this.Sweat, this.Cobweb, this.Silence, this.LightBulb, this.Zzz, this.UserDefined1, this.UserDefined2, this.UserDefined3, this.UserDefined4, this.UserDefined5 or, respectively, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 or 15

    Must yield if wait is true.

    Erase Event:

    Code:
    this.eraseEvent()

    Picure, Screen, Audio & Video:

    Not yet implemented. Usually there's little to abstract away here, but for consistency I think I'll eventually provide wrappers nevertheless.

    System Settings, Map, Battle:

    Not yet implemented.

    Scene Control:

    Not yet implemented. DO NOT TRY TO SAVE GAME WHILE IN THE GENERATOR FUNCTION because I haven't figured it out how to allow this without breaking things yet.

    Scripts:

    Would be pointless to implement this.

    Final Notes:
    1) This plugin ommits some sanity checks the editor provides, you are responsible for calling everything with correct arguments and in correct circumstances.
    2) Work in progress, I do not guarantee it will work as expected. Probably buggy.

    The script (at last):

    Code:
    /*:
    * @plugindesc GZKM_ScriptEventCommands
    *
    * @author GZKM
    *
    * @help
    */
    
    var GZKM = GZKM || {};
    
    (function() {
    
       GZKM.ev = {}
       GZKM.cntxt = {}
       
       GZKM.runEvent = function(gameInterpr, eventBody) {
           var id = gameInterpr._eventId.toString()
           var context = (this.cntxt[id] = this.cntxt[id] || {
               gameInterpr: gameInterpr,
               
               NoImage: '',
               BackgroundWindow: 0, BackgroundDim: 1, BackgroundTransparent: 2,
               TextTop: 0, TextMiddle: 1, TextBottom: 2,
               showText: function(faceset, faceNo, background, position, textLines) {
                   $gameMessage.setFaceImage(faceset, faceNo)
                   $gameMessage.setBackground(background)
                   $gameMessage.setPositionType(position)
                   for(var i = 0; i < textLines.length; i++)
                       $gameMessage.add(textLines[i])
                   this.gameInterpr.setWaitMode('message')
               },
               
               ChoicesLeft: 0, ChoicesMiddle: 1, ChoicesRight: 2,
               NoDefault: -1, CancelBranch: -2, CancelDisallow: -1,
               showChoices: function(background, position, choices, default_choice, on_cancel) {
                   $gameMessage.setChoices(choices, default_choice, on_cancel)
                   $gameMessage.setChoiceBackground(background)
                   $gameMessage.setChoicePositionType(position)
                   $gameMessage.setChoiceCallback(function(n) {
                       this.choice = n
                   }.bind(this))
                   this.gameInterpr.setWaitMode('message')
               },
               
               ThisEvent: 0, Player: -1,
               MoveDown: 1, MoveLeft: 2, MoveRight: 3, MoveUp: 4,
               MoveLowerLeft: 5, MoveLowerRight: 6, MoveUpperLeft: 7, MoveUpperRight: 8,
               MoveAtRandom: 9, MoveTowardPlayer: 10, MoveAwayFromPlayer: 11,
               MoveForward: 12, MoveBackward: 13,
               Jump: function(x, y) {return [14, [x, y]]}, MovementWait: function(f) {return [15, [f]]},
               TurnDown: 16, TurnLeft: 17, TurnRight: 18, TurnUp: 19,
               TurnRightHand: 20, TurnLeftHand: 21, TurnOpposite: 22,
               TurnRightOrLeftHand: 23, TurnAtRandom: 24, TurnTowardPlayer: 25, TurnAwayFromPlayer: 26,
               SwitchOn: function(s) {return [27, [s]]}, SwitchOff: function(s) {return [28, [s]]},
               Slower8: 1, Slower4: 2, Slower2: 3, NormalSpeed: 4, Faster2: 5, Faster4: 6,
               LowestFrequency: 1, LowFrequency: 2, NormalFrequency: 3, HigherFrequency: 4, HighestFrequency: 5,
               Speed: function(s) {return [29, [s]]}, Frequency: function(f) {return [30, [f]]},
               WalkingAnimationOn: 31, WalkingAnimationOff: 32, SteppingAnimationOn: 33, SteppringAnimationOff: 34,
               DirectionFixOn: 35, DirectionFixOff: 36, ThroughOn: 37, ThroughOff: 38, TransparentOn: 39, TransparentOff: 40,
               ChangeImage: function(spriteset, spriteNo) {return [41, [spriteset, spriteNo]]},
               ChangeOpacity: function(opacity) {return [42, [opacity]]},
               BlendNormal: 0, BlendAdditive: 1, BlendMultiply: 2, BlendScreen: 3,
               ChangeBlendMode: function(blendmode) {return [43, [blendmode]]},
               MovementPlaySE: function(name, pan, pitch, volume) {return [44, [{name: name, pan: pan, pitch: pitch, volume: volume}]]},
               MovementScript: function(scriptAsString) {return [45, [scriptAsString]]},
               setMovementRoute: function(ev, repeat, skippable, wait, moveCommands) { // ev: albo ID innego eventu
                   
                   moveRoute = {
                       list: moveCommands.concat(0).map(function(command){
                           if(Object.prototype.toString.call(command) === '[object Array]')
                               return {code: command[0], parameters: command[1], indend: null}
                           else return {code: command, indend: null}
                       }),
                       skippable: skippable,
                       repeat: repeat,
                       wait: wait
                   }
                   
                   $gameMap.refreshIfNeeded()
                   this.gameInterpr._character = this.gameInterpr.character(ev)
                   if (this.gameInterpr._character) {
                       this.gameInterpr._character.forceMoveRoute(moveRoute)
                       if (moveRoute.wait) {
                           this.gameInterpr.setWaitMode('route') // Must yield in that case
                       }
                   }
               },
               
               inputNumber: function(variable, digits) {
                   $gameMessage.setNumberInput(variable, digits)
                   this.gameInterpr.setWaitMode('message')
               },
               
               RegularItem: 1, KeyItem: 2, HiddenItemA: 3, HiddenItemB: 4,
               selectItem: function(variable, itemCategory) {
                   $gameMessage.setItemChoice(variable, itemCategory)
                   this.gameInterpr.setWaitMode('message')
               },
               
               showScrollingText: function(speed, fastForward, textLines) {
                   $gameMessage.setScroll(speed, fastForward);
                   for(var i = 0; i < textLines.length; i++)
                       $gameMessage.add(textLines[i])
                   this.gameInterpr.setWaitMode('message')
               },
               
               DirectionRetain: 0, DirectionDown: 2, DirectionLeft: 4, DirectionRight: 6, DirectionUp: 8,
               FadeBlack: 0, FadeWhite: 1, FadeNone: 2,
               transferPlayer: function(mapId, x, y, direction, fade) {
                   $gamePlayer.reserveTransfer(mapId, x, y, direction, fade);
                   this.setWaitMode('transfer');
               },
               
               Boat: 0, Ship: 1, Airship: 2,
               setVehicleLocation: function(mapId, x, y, vehicle) {
                   $gameMap.vehicle(vehicle).setLocation(mapId, x, y);
               },
               
               setEventLocation: function(x, y, dir, thisEvent) {
                   var character = this.gameInterpr.character(thisEvent)
                   character.locate(x, y)
                   if(dir > 0)
                       character.setDirection(dir)
               },
               swapEventLocations: function(dir, thisEvent, otherEvent) {
                   var character = this.gameInterpr.character(thisEvent)
                   character.swap(this.gameInterpr.character(otherEvent))
                   if(dir > 0)
                       character.setDirection(dir)
               },
               
               mustWaitForScrolling: function() {
                   if ($gameMap.isScrolling()) {
                       this.gameInterpr.setWaitMode('scroll')
                       return true // must yield if returns true
                   } else return false
               },
               scrollMap: function(dir, speed, dist) {
                   $gameMap.startScroll(dir, dist, speed);
               },
               
               getOnOffVehicle: function() {
                   $gamePlayer.getOnOffVehicle()
               },
               
               changeTransparency: function(transparency) {
                   $gamePlayer.setTransparent(transparency);
               },
               
               changePlayerFollowers: function(followers) {
                   if (followers)
                       $gamePlayer.showFollowers()
                   else
                       $gamePlayer.hideFollowers()
                   $gamePlayer.refresh()
               },
               
               gatherFollowers: function() {
                   $gamePlayer.gatherFollowers()
                   this.gameInterpr.setWaitMode('gather')
               },
               
               showAnimation: function(ev, wait, anim) {
                   this.gameInterpr._character = this.gameInterpr.character(ev)
                   this.gameInterpr._character.requestAnimation(anim)
                   if (wait)
                       this.gameInterpr.setWaitMode('animation'); // must yield in that case
               },
               
               Exclamation: 1, Question: 2, MusicNote: 3, Heart: 4, Anger: 5, Sweat: 6, Cobweb: 7, Silence: 8, LightBulb: 9, Zzz: 10,
               UserDefined1: 11, UserDefined2: 12, UserDefined3: 13, UserDefined4: 14, UserDefined5: 15,
               showBalloonIcon: function(ev, wait, balloon) {
                   this.gameInterpr._character = this.gameInterpr.character(ev)
                   this.gameInterpr._character.requestBalloon(balloon)
                   if (wait)
                       this.gameInterpr.setWaitMode('balloon'); // must yield in that case
               },
               
               eraseEvent: function() {
                   if (this.gameInterpr.isOnCurrentMap())
                       $gameMap.eraseEvent(this.gameInterpr._eventId);
               },
               
               eventWait: function(duration) {
                   this.gameInterpr.wait(duration)
               }
               
           })
           var ev = (this.ev[id] = this.ev[id] || eventBody.bind(context)())
           
           if(ev.next().done)
               GZKM.ev[id] = GZKM.cntxt[id] = false
           else
               gameInterpr._index--
       }
       
       GZKM.eventCommands = {
           ScriptEvent1: function*() {
               this.showScrollingText(3, false, ['asdf', 'jdkflse'])
               return;
           }
       }
       
       GZKM.eventScripts = {}
       
       GZKM.scriptEvent = function(gameInterpr, commands) {
           while(gameInterpr.currentCommand().code != 355)
               gameInterpr._index--
           
           var key = [gameInterpr._mapId, gameInterpr._eventId, gameInterpr._index]
           if(!this.eventScripts[key])
               this.eventScripts[key] = commands
           this.runEvent(gameInterpr, this.eventScripts[key])
       }
    
        var _Game_Interpreter_pluginCommand =
                Game_Interpreter.prototype.pluginCommand
        Game_Interpreter.prototype.pluginCommand = function(command, args) {
            _Game_Interpreter_pluginCommand.call(this, command, args)
           
            if(command in GZKM.eventCommands) {
               GZKM.runEvent(this, GZKM.eventCommands[command])
           }
       }
    })()
    /*:
    * @plugindesc GZKM_ScriptEventCommands
    *
    * @author GZKM
    *
    * @help
    */
    
    var GZKM = GZKM || {};
    
    (function() {
    
       GZKM.ev = {}
       GZKM.cntxt = {}
       
       GZKM.runEvent = function(gameInterpr, eventBody) {
           var id = gameInterpr._eventId.toString()
           var context = (this.cntxt[id] = this.cntxt[id] || {
               gameInterpr: gameInterpr,
               
               NoImage: '',
               BackgroundWindow: 0, BackgroundDim: 1, BackgroundTransparent: 2,
               TextTop: 0, TextMiddle: 1, TextBottom: 2,
               showText: function(faceset, faceNo, background, position, textLines) {
                   $gameMessage.setFaceImage(faceset, faceNo)
                   $gameMessage.setBackground(background)
                   $gameMessage.setPositionType(position)
                   for(var i = 0; i < textLines.length; i++)
                       $gameMessage.add(textLines[i])
                   this.gameInterpr.setWaitMode('message')
               },
               
               ChoicesLeft: 0, ChoicesMiddle: 1, ChoicesRight: 2,
               NoDefault: -1, CancelBranch: -2, CancelDisallow: -1,
               showChoices: function(background, position, choices, default_choice, on_cancel) {
                   $gameMessage.setChoices(choices, default_choice, on_cancel)
                   $gameMessage.setChoiceBackground(background)
                   $gameMessage.setChoicePositionType(position)
                   $gameMessage.setChoiceCallback(function(n) {
                       this.choice = n
                   }.bind(this))
                   this.gameInterpr.setWaitMode('message')
               },
               
               ThisEvent: 0, Player: -1,
               MoveDown: 1, MoveLeft: 2, MoveRight: 3, MoveUp: 4,
               MoveLowerLeft: 5, MoveLowerRight: 6, MoveUpperLeft: 7, MoveUpperRight: 8,
               MoveAtRandom: 9, MoveTowardPlayer: 10, MoveAwayFromPlayer: 11,
               MoveForward: 12, MoveBackward: 13,
               Jump: function(x, y) {return [14, [x, y]]}, MovementWait: function(f) {return [15, [f]]},
               TurnDown: 16, TurnLeft: 17, TurnRight: 18, TurnUp: 19,
               TurnRightHand: 20, TurnLeftHand: 21, TurnOpposite: 22,
               TurnRightOrLeftHand: 23, TurnAtRandom: 24, TurnTowardPlayer: 25, TurnAwayFromPlayer: 26,
               SwitchOn: function(s) {return [27, [s]]}, SwitchOff: function(s) {return [28, [s]]},
               Slower8: 1, Slower4: 2, Slower2: 3, NormalSpeed: 4, Faster2: 5, Faster4: 6,
               LowestFrequency: 1, LowFrequency: 2, NormalFrequency: 3, HigherFrequency: 4, HighestFrequency: 5,
               Speed: function(s) {return [29, [s]]}, Frequency: function(f) {return [30, [f]]},
               WalkingAnimationOn: 31, WalkingAnimationOff: 32, SteppingAnimationOn: 33, SteppringAnimationOff: 34,
               DirectionFixOn: 35, DirectionFixOff: 36, ThroughOn: 37, ThroughOff: 38, TransparentOn: 39, TransparentOff: 40,
               ChangeImage: function(spriteset, spriteNo) {return [41, [spriteset, spriteNo]]},
               ChangeOpacity: function(opacity) {return [42, [opacity]]},
               BlendNormal: 0, BlendAdditive: 1, BlendMultiply: 2, BlendScreen: 3,
               ChangeBlendMode: function(blendmode) {return [43, [blendmode]]},
               MovementPlaySE: function(name, pan, pitch, volume) {return [44, [{name: name, pan: pan, pitch: pitch, volume: volume}]]},
               MovementScript: function(scriptAsString) {return [45, [scriptAsString]]},
               setMovementRoute: function(ev, repeat, skippable, wait, moveCommands) { // ev: albo ID innego eventu
                   
                   moveRoute = {
                       list: moveCommands.concat(0).map(function(command){
                           if(Object.prototype.toString.call(command) === '[object Array]')
                               return {code: command[0], parameters: command[1], indend: null}
                           else return {code: command, indend: null}
                       }),
                       skippable: skippable,
                       repeat: repeat,
                       wait: wait
                   }
                   
                   $gameMap.refreshIfNeeded()
                   this.gameInterpr._character = this.gameInterpr.character(ev)
                   if (this.gameInterpr._character) {
                       this.gameInterpr._character.forceMoveRoute(moveRoute)
                       if (moveRoute.wait) {
                           this.gameInterpr.setWaitMode('route') // Must yield in that case
                       }
                   }
               },
               
               inputNumber: function(variable, digits) {
                   $gameMessage.setNumberInput(variable, digits)
                   this.gameInterpr.setWaitMode('message')
               },
               
               RegularItem: 1, KeyItem: 2, HiddenItemA: 3, HiddenItemB: 4,
               selectItem: function(variable, itemCategory) {
                   $gameMessage.setItemChoice(variable, itemCategory)
                   this.gameInterpr.setWaitMode('message')
               },
               
               showScrollingText: function(speed, fastForward, textLines) {
                   $gameMessage.setScroll(speed, fastForward);
                   for(var i = 0; i < textLines.length; i++)
                       $gameMessage.add(textLines[i])
                   this.gameInterpr.setWaitMode('message')
               },
               
               DirectionRetain: 0, DirectionDown: 2, DirectionLeft: 4, DirectionRight: 6, DirectionUp: 8,
               FadeBlack: 0, FadeWhite: 1, FadeNone: 2,
               transferPlayer: function(mapId, x, y, direction, fade) {
                   $gamePlayer.reserveTransfer(mapId, x, y, direction, fade);
                   this.setWaitMode('transfer');
               },
               
               Boat: 0, Ship: 1, Airship: 2,
               setVehicleLocation: function(mapId, x, y, vehicle) {
                   $gameMap.vehicle(vehicle).setLocation(mapId, x, y);
               },
               
               setEventLocation: function(x, y, dir, thisEvent) {
                   var character = this.gameInterpr.character(thisEvent)
                   character.locate(x, y)
                   if(dir > 0)
                       character.setDirection(dir)
               },
               swapEventLocations: function(dir, thisEvent, otherEvent) {
                   var character = this.gameInterpr.character(thisEvent)
                   character.swap(this.gameInterpr.character(otherEvent))
                   if(dir > 0)
                       character.setDirection(dir)
               },
               
               mustWaitForScrolling: function() {
                   if ($gameMap.isScrolling()) {
                       this.gameInterpr.setWaitMode('scroll')
                       return true // must yield if returns true
                   } else return false
               },
               scrollMap: function(dir, speed, dist) {
                   $gameMap.startScroll(dir, dist, speed);
               },
               
               getOnOffVehicle: function() {
                   $gamePlayer.getOnOffVehicle()
               },
               
               changeTransparency: function(transparency) {
                   $gamePlayer.setTransparent(transparency);
               },
               
               changePlayerFollowers: function(followers) {
                   if (followers)
                       $gamePlayer.showFollowers()
                   else
                       $gamePlayer.hideFollowers()
                   $gamePlayer.refresh()
               },
               
               gatherFollowers: function() {
                   $gamePlayer.gatherFollowers()
                   this.gameInterpr.setWaitMode('gather')
               },
               
               showAnimation: function(ev, wait, anim) {
                   this.gameInterpr._character = this.gameInterpr.character(ev)
                   this.gameInterpr._character.requestAnimation(anim)
                   if (wait)
                       this.gameInterpr.setWaitMode('animation'); // must yield in that case
               },
               
               Exclamation: 1, Question: 2, MusicNote: 3, Heart: 4, Anger: 5, Sweat: 6, Cobweb: 7, Silence: 8, LightBulb: 9, Zzz: 10,
               UserDefined1: 11, UserDefined2: 12, UserDefined3: 13, UserDefined4: 14, UserDefined5: 15,
               showBalloonIcon: function(ev, wait, balloon) {
                   this.gameInterpr._character = this.gameInterpr.character(ev)
                   this.gameInterpr._character.requestBalloon(balloon)
                   if (wait)
                       this.gameInterpr.setWaitMode('balloon'); // must yield in that case
               },
               
               eraseEvent: function() {
                   if (this.gameInterpr.isOnCurrentMap())
                       $gameMap.eraseEvent(this.gameInterpr._eventId);
               },
               
               eventWait: function(duration) {
                   this.gameInterpr.wait(duration)
               }
               
           })
           var ev = (this.ev[id] = this.ev[id] || eventBody.bind(context)())
           
           if(ev.next().done)
               GZKM.ev[id] = GZKM.cntxt[id] = false
           else
               gameInterpr._index--
       }
       
       GZKM.eventCommands = {
           ScriptEvent1: function*() {
               this.showScrollingText(3, false, ['asdf', 'jdkflse'])
               return;
           }
       }
       
       GZKM.eventScripts = {}
       
       GZKM.scriptEvent = function(gameInterpr, commands) {
           while(gameInterpr.currentCommand().code != 355)
               gameInterpr._index--
           
           var key = [gameInterpr._mapId, gameInterpr._eventId, gameInterpr._index]
           if(!this.eventScripts[key])
               this.eventScripts[key] = commands
           this.runEvent(gameInterpr, this.eventScripts[key])
       }
    
        var _Game_Interpreter_pluginCommand =
                Game_Interpreter.prototype.pluginCommand
        Game_Interpreter.prototype.pluginCommand = function(command, args) {
            _Game_Interpreter_pluginCommand.call(this, command, args)
           
            if(command in GZKM.eventCommands) {
               GZKM.runEvent(this, GZKM.eventCommands[command])
           }
       }
    })()