Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ////////////////////////////////////
- // Creating ZScript Instructions ///
- ////////////////////////////////////////////////////////////////////////////
- //! Note: A ZASM instruction may have a label of no more than 79 chars. ///
- ////////////////////////////////////////////////////////////////////////////
- /* VARIABLES */
- To add variables to ZScript, for any of the ZS namespaces (e.g. 'Screen'), you must follow a process of
- adding the ZASM labels, to three files, the hooks for the instruction in the Global Symbols array,
- and the actual live instruction code to ffscript.cpp.
- You may wish to add a function to class FFScript along the way.
- /* STEPS */
- //////////////////////////////////////////////////
- /// 0. ZASM Instruction and ZScript Identifier ///
- //////////////////////////////////////////////////
- Before adding a new instruction, you must decide both the ZASM Label (the assembly label) that it will use.
- and the IDENTIFIER that the user will utilise when writing ZScript code.
- For the sake of the examples (below), we'll decide that we want to give Link a custom *hat*, and we will call the variable
- that holds the data for his hat as `Link->Hat`, with the ZASM Label LINKHAT.
- /////////////////////
- /// 1. ffscript.h ///
- /////////////////////
- Each new ZScript variable must have a unique ZASM register, defined in ffscript.h.
- A typical entry for these, looks like this:
- [example--
- #define LWPNX 0x0259
- --end example]
- This is the primary ZASM label for the ZScript instruction, lweapon->X, used by *Zelda.cpp*.
- That means that this is the token used by ZC, when processing the stack!
- To add a new instruction, either:
- (a)(1) Find the end of the list of instructions by seeking for `NUMVARIABLES`.
- (a)(2) Add a new definition above NUMVARIABLES, and addign it a value equal to
- the value of the (prior instruction +1).
- NOTE that these values are hexadecimal.
- (a)(3) Increase the value assigned to NUMVARIABLES by +1.
- As an example, we'll add the new variable, Link->Hat:
- Prior to adding it, we have:
- [example--
- #define NPCDATABEHAVIOUR 0x1318
- #define NUMVARIABLES 0x1319
- --end example]
- Here is the result after we add our new definition:
- [example--
- #define NPCDATABEHAVIOUR 0x1318
- #define LINKHAT 0x1319
- #define NUMVARIABLES 0x131A //Remember, these are HEX!
- --end example]
- --or--
- (b)(1) Find an unused variable *e.g., reserved space) and change the identifier of the define
- directive to the ZASM LABEL that you wish to use for your new variable.
- ////////////////////
- /// 2. ffasm.cpp ///
- ////////////////////
- There are two large arrays in ffasm.cpp. The first is for ZASM COMMANDS, and the second is for ZASM VARIABLES.
- For ZScript variables, you will want to append to the array,`script_variable variable_list[]`
- A typical entry in this table looks like this:
- [example--
- { "LWPNX", LWPNX, 0, 0 },
- --end example]
- The TERMINATION ENTRY in the table reads as follows:
- [example--
- { " ", -1, 0, 0 }
- --end example]
- To add your new variable:
- (a)(1) Duplicate the last entry in the table, PRIOR to the termination entry.
- (a)(2) Replace the string in the entry with a string that matches your ZASM label.
- (a)(3) Replace the old ZASM label , in this entry, with your new ZASm label.
- e.g., if you are adding a ZASM label of LINKHAT, your table entry will be:
- [example--
- { "LINKHAT", LINKHAT, 0, 0 },
- --end example]
- (a)(2)
- ////////////////////////////
- /// 3. parser/bytecode.h ///
- ////////////////////////////
- Next, we move into the parser files, starting with bytecode.h, where the actual bytecode IDs are located.
- These IDs are the literal value of each instruction in ZScript, stored in the stacks!
- When ZC processes a ZScript instruction, it follows this flowchart:
- 1. Read stack.
- 2. Match instruction to a definition in the bytecode.
- 3. Convert that literal to a string.
- 4. Match that string to a value in ffscript.h
- 5. The switch blocksw in ffscript.cpp execute that case value.
- To add your new variable, find the end of the bytecode, by seeking `LAST_BYTECODE`.
- NOTE: These values are DECIMAL, not hex.
- (a)(1) Duplicate the definition prior to LAST_BYTECODE.
- (a)(2) Append a new define after this, with a number one higher than the prior definition.
- (a)(3) Increment LAST_BYTECODE by one.
- Continuing our example, adding Link->Hat:
- Prior to adding it, we have:
- [example--
- #define NPCDATABEHAVIOUR 991
- #define LAST_BYTECODE 992
- --end example]
- Here is the result after we add our new definition:
- [example--
- #define NPCDATABEHAVIOUR 991
- #define LINKHAT 992
- #define LAST_BYTECODE 993 //Remember, these are DEC!
- --end example]
- //////////////////////////////
- /// 4. parser/bytecode.cpp ///
- //////////////////////////////
- Similar to ffasm.h, here we will be adding a case to return a string when the parser encounters a specific literal.
- To add a new variable, seek the function `string VarArgument::toString()`, and then, inside the switch block `switch(ID)`,
- add a new case, using our ZASM label as the case value, and returning the same as a string:
- [example--
- case LINLHAT: return "LINKHAT";
- --end example]
- ///////////////////////////////
- /// 5. Global Symbols Table ///
- ///////////////////////////////
- In the global symbols table for the namespace to which you are adding the variable--Link, in the case of
- our example--, we will next add a table entry into the array to convert to the correct ZASM instruction
- when the parser scans for tokens.
- At present, these tables all exist in GlobalSymbols.cpp.
- Entries in these tables typically exist in PAIRS: One for reading a variable, and one entry for writing to it.
- These are GETTER and SETTER entries, respectively, and a typical pair looks like this:
- [example--
- { "getHat", ZVARTYPEID_FLOAT, GETTER, LINKX, 1, { ZVARTYPEID_LINK, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} },
- { "setHat", ZVARTYPEID_VOID, SETTER, LINKX, 1, { ZVARTYPEID_LINK, ZVARTYPEID_FLOAT, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} },
- --end example]
- The FIELDS for these arrays are as follows:
- "string" : This is the token that the scanner will use when compiling.
- : Variables are prefaced by a lowercase "set" or "get", then the variable
- : identifier. Thus, "getHat" and "setHat" will become Hat (read, and write).
- : To add an array format (indexed) variable, append "[]" to the string.
- : Thus, "setVar[]" or "gerVar[]"
- Return Type : The return type of the variable.
- : For SETTERs, this is always ZVARTYPEID_VOID.
- : For GETTERs, you must specify the appropriate type.
- : The most common GETTER return types are ZVARTYPEID_FLOAT and ZVARTYPEID_BOOL.
- Instr. Type : Defines if the instruction is a SETTER or a GETTER.
- : The third type, FUNCTION, is used for ZASM COMMANDS.
- ZASM LABEL : The ZASM label (bytecode ID) into which the parser will convert the instruction.
- Num. Fields : The number of fields used by the variable.
- : For ordinary variables, this is always `1`.
- : For arrays, this is the number of indices in the array.
- ARGS : The inputs, and datatypes thereof, used by the variable, starting with its `this`.
- : In the examples above, both the SETTER and the GETTER have a `this` of Link.
- : Values of `-1` indicate no input.
- : In the examples above, the GETTER has NO args after its `this`.
- : In the examples above, the SETTER has ONE args after its `this`, with a TYPE of FLOAT.
- To add your new variable:
- (a)(1) Locate the correct table. For Link, seek the array `LinkSTable[] `.
- (a)(2) Add a GETTER entry:
- [example--
- { "getHat", ZVARTYPEID_FLOAT, GETTER, LINKX, 1, { ZVARTYPEID_LINK, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} },
- --end example]
- (a)(3) If the new variable is both read and write, then add a SETTER entry:
- [example--
- { "setHat", ZVARTYPEID_VOID, GETTER, LINKX, 1, { ZVARTYPEID_LINK, ZVARTYPEID_FLOAT, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} },
- --end example]
- ///////////////////////
- /// 6. ffscript.cpp ///
- ///////////////////////
- Now that we have completed our set-up, we can add the actual code that we will run when the user calls the new
- instruction in ZScript.
- For variables, in ffscript.cpp, we will be adding entries both for the GETTER, and (for r/w instructions), the SETTER
- in two separate switch blocks, using CASE labels.
- Each case in the switch uses the ZASM label.
- GETTER instructions reside inside the `switch(arg)` block of the function: `long get_register(const long arg)`
- All entries in this switch block must return a value to `ret`.
- Indexed variables (arrays)
- A typical single variable:
- [example--
- case LINKX:
- ret = long(Link.getX()) * 10000;
- break;
- --end example]
- A typical array:
- [example--
- case NPCDEFENSED:
- {
- int a = ri->d[0] / 10000; //The INDEX that we're accessing.
- ///A sanity check.
- if(GuyH::loadNPC(ri->guyref, "npc->Defense[]") != SH::_NoError ||
- BC::checkBounds(a, 0, (edefLAST255), "npc->Defense[]") != SH::_NoError)
- ret = -10000;
- else
- ret = GuyH::getNPC()->defense[a] * 10000;
- break;
- }
- --end example]
- NOTE: Almost all values are multiplied, or divided by 10000, as the literal values in ZScript
- are 'fixed' point types, with a factor of 10000. Thus, `1` in ZScript has a literal value
- of 10000, and we must manually convert to the correct values inside ffscript.cpp.
- SETTER instructions, likewise, reside inside the `switch(arg)` block of the function:
- `void set_register(const long arg, const long value)`
- For these, we always have at least one base arg passed to them as `value`.
- Additional args, such as for indexed variables (arrays), use ri->d[] registers (part of refInfo).
- Rentries in this switch block MAY NEVER return a value!
- A typical single variable:
- [example--
- case LINKX:
- Link.setX(value/10000);
- break;
- --end example]
- A typical array:
- [example--
- case NPCDEFENSED:
- {
- long a = ri->d[0] / 10000; //The index to which we are writing.
- //sanity check
- if(GuyH::loadNPC(ri->guyref, "npc->Defense") == SH::_NoError &&
- BC::checkBounds(a, 0, (edefLAST255), "npc->Defense") == SH::_NoError)
- GuyH::getNPC()->defense[a] = vbound((value / 10000),0,255);
- break;
- }
- --end example]
- Note that ZScript array sizes are automatically sanity bounded by the parser, so there is no need to
- specifically check that ri->d[n] is within the bounds of 0 and size-1.
- As internal/parser ZScript arrays are not true arrays, but merely a mask for accessing data in the FORM
- of an arraym we can also use specific cases to do different things:
- [example--
- case NPCDD:
- {
- long a = ri->d[0] / 10000; //the 'INDEX'
- if(GuyH::loadNPC(ri->guyref, "npc->Attributes") == SH::_NoError &&
- BC::checkBounds(a, 0, 15, "npc->Attributes") == SH::_NoError)
- //use the index to do different things
- switch(a){
- case 0: GuyH::getNPC()->dmisc1 = value / 10000; break;
- case 1: GuyH::getNPC()->dmisc2 = value / 10000; break;
- case 2: GuyH::getNPC()->dmisc3 = value / 10000; break;
- case 3: GuyH::getNPC()->dmisc4 = value / 10000; break;
- case 4: GuyH::getNPC()->dmisc5 = value / 10000; break;
- //truncated list of additional values
- default: break;
- }
- break;
- }
- --end example]
- Also noteworthy, is that there need be no direct correlation internally to what each index of your
- ZScript array represents. It is prefectly valid for one array to access radically different internal memory locations.
- /////////////////////
- /// ZASM COMMANDS ///
- ////////////////////////////////////////////////////
- Adding ZASM Commands (ZScript FUNCTIONS), is somewhat more complicated than adding variables, as you must manually
- specify the operation of data registers in the GlobalSymbols maps.
- Here is a BASIC OUTLINE on the process.
- //////////////////////////////////////////////////
- /// 0. ZASM Instruction and ZScript Identifier ///
- //////////////////////////////////////////////////
- As with adding basic instructions, you must decide both the ZASM Label (the assembly label) that it will use.
- and the IDENTIFIER for the function that the user will utilise when writing ZScript code.
- For the sake of the examples (below), we'll decide that we want to read the tiles in Link;s sprite.
- As Link's sprites are a multi-dimensional array, and we want to access it with one instruction, we'll be making
- Link->GetSpriteTile(int sprite, int indx), and Link->SetSpriteTile(int sprite, int index, int tile).
- The ZASM labels for these will be GETLINKSPRITETILES and SETLINKSCRIPTTILES.
- /////////////////////
- /// 1. ffscript.h ///
- /////////////////////
- To begin this process, we'll start in ffscript.h, inside of the enum `ASM_DEFINE`.
- This enumerated list contains the ZASM Labels for ALL ZASM COMMANDS.
- Note that you MUST keep these in order, and never change their order; and that you will later need to add
- the ZASM label to a 2D array in the SAME order as you list it in ASM_DEFINE.
- TO begin, seek for `NUMCOMMANDS`.
- Duplicate the entry prior to that, and replace the definition with our new ZASM Label.
- If the current entry for ASM_DEFINE looks like this:
- [example--
- FXZAPR,
- FXZAPV,
- GREYSCALER,
- GREYSCALEV,
- NUMCOMMANDS //0x013B
- --end example]
- It will look like *this*, after we add out new label:
- [example--
- FXZAPR,
- FXZAPV,
- GREYSCALER,
- GREYSCALEV,
- GETLINKSPRITETILES,
- SETLINKSPRITETILES,
- NUMCOMMANDS //0x013B
- --end example]
- ///////////////////
- // 2. ffasm.cpp ///
- ///////////////////
- Next, in our ASM code, we need to add the new ZASM COMMAND Label to the 2D array:
- `script_command command_list[NUMCOMMANDS+1]`
- When adding to ( script_command command_list[NUMCOMMANDS+1] ) list, in ffasm.cpp, the ORDER of listing
- any new parameter MUST MATCH the order in ( ASM_DEFINE ) in file ffscript.h!
- (This differs from script variables, which may be in any order.)
- This 2D array is matched AGAINST ADM_DEFINE to find the numeric literal value of COMMANDS.
- Here is an excerpt of this table:
- [example--
- { "MULTR", 2, 0, 0, 0},
- { "MULTV", 2, 0, 1, 0},
- { "DIVR", 2, 0, 0, 0},
- { "DIVV", 2, 0, 1, 0},
- { "WAITFRAME", 0, 0, 0, 0},
- { "GOTO", 1, 1, 0, 0},
- { "CHECKTRIG", 0, 0, 0, 0},
- { "WARP", 2, 1, 1, 0},
- { "COMPARER", 2, 0, 0, 0},
- { "COMPAREV", 2, 0, 1, 0},
- { "GOTOTRUE", 1, 1, 0, 0},
- { "GOTOFALSE", 1, 1, 0, 0},
- --end example]
- In this table, the first entry is a STRING of the ZASM Label.
- The second, is the number of commands used by its Opcode:
- Unary Opcodes use `1`
- Binary Opcodes use 2`
- All others use `0`.
- These determine if data is automatically pushed into sarg1 and sarg2.
- The third and fourth values are labeled `sarg1` and `sarg2`, and /**I BELIEVE that these are
- used to automatically POP those registers. **/
- /*This needs verification!*/
- The fourth value, is labeled `more`, and is not used by anything. /*I am UNSURE of its intended use. */
- Thus, you have entries consisting of:
- [example--
- { "ZASM_LABEL", push/num_args, sarg1/pop1, sarg2/pop2, more},
- --end example]
- When adding a new instruction, it is best either to use another instruction as its basis, or:
- (a) If you are using only one or two parameters, you can set `args to `1` or `2`.
- (b) If you are using three or more args, or no args, you can set `args` to `0` and directly read the stack.
- All of this depends on if you wish to create a basic Opcode, a Unary opcode, or a Binary Opcode in ByteCode.h.
- In any case, we are using two args for `Link->GetSpriteTile(int sprite, int index, int tile)` and three args for
- Link->SetSpriteTile(int sprite, int index, int tile). The former, also must return a value to a register
- for us to use.
- We'll define these in the `script_command command_list[NUMCOMMANDS+1]` array as:
- [example--
- { "GETLINKSPRITETILES", 1, 0, 0, 0}, //WIll use unary opcode
- { "SETLINKSPRITETILES", 0, 0, 0, 0}, //Will use Opcode
- --end example]
- ///Generic Setter ROutine for basic Opcode (not for Unary or Binary opcode types)
- void SetterFunction(const string& label, new Opcode* ocode, int numargs)
- {
- Function* function = getFunction(label);
- int label = function->getLabel();
- vector<Opcode *> code;
- Opcode *first = ocode();
- first->setLabel(label);
- code.push_back(first);
- POP_ARGS(numargs, EXP2);
- //pop pointer, and ignore it
- code.push_back(new OPopRegister(new VarArgument(NUL))); //Pop its `this` to NUL.
- code.push_back(new OPopRegister(new VarArgument(EXP2)));
- code.push_back(new OReturn());
- function->giveCode(code);
- }
- void GetterFunction(const string& label, new Opcode ocode, int numargs)
- {
- /////////////////////////////****************************
- We will append them to the bottom of the list.
- //Drawing
- Script_Drawing.cpp
- add function
- add opcode redirect with ZASM label
- GlobalSymbols.cpp
- Add table entry, with proper args values. The entry name should match the ZScript instruction.
- Add function to bind the name in the table to the O-Register
- Bytecode.cpp
- Add ORegister redirect that returns the ZASM instruction
- Bytecode.h
- Add class ORegister entry tat returns the opcode entry in Bytecode.cpp
- ffasm.cpp
- Add entry to script command list, using the ZASM label
- ffscript.cpp
- add case statement to switch(script_command) and ensure the args value matches that in GlobalSymbols.c[[
- add case entry to switch(scommand), and ensure it is in the script commands case list
- Both of these use the ZASM label
- ffscript.h
- Add the ZASM label to the ASM_DEFINE table.
- //Link
- Link.h
- Add a new public variable if needed, suc as:
- int linkdointhing
- (or)
- Find a public variable to use
- Add an empty function set (getter, setter) with the identifier that you desire, such as
- int getLinkDoingThing()
- void setLinkDoingThing(int value)
- Add a clear instruction in the Init function:
- linkdointhing = 0;
- Link.cpp
- Create a pair of setter/getter functions tied to the link class, such as:
- //Return the variable using a getter.
- int LinkClass::getLinkDoingThing()
- {
- return linkdointhing;
- }
- //Set the variable using a setter
- void LinkClass::setLinkDoingThing(int value)
- {
- linkdointhing=value;
- }
- ! You can constrain the min and max values written with: vbound(int value, int min, int max)
- //Set the variable using a setter
- void LinkClass::setLinkDoingThing(int value)
- {
- linkdointhing=vbound(value,0,3);
- }
- ffasm.cpp
- Add an entry to the ( variable_list[] ) table, and introduce your ZASM keyword:
- { "LINKDOTHING", LINKDOTHING, 0, 0 },
- The first value is a string, that is matched wit the assembly, to the second value, called by GlobalSymbols.cpp.
- ffscript.h
- Define the ZASM label and give it a unique numeric value. This is used by the wordtable to look it up.
- #define LINKDOTHING 0x0300
- If you added something new, be sure to increase the value of NUMVARIABLES.
- ffscript.cpp
- in the ( long get_register(const long arg) switch(arg) ) STATEMENT, under 'Link's Variables' add a GETTER instruction case:
- case LINKDOTHING:
- ret=Link.getLinkDoingThing*10000; //THis points to the LinkClass::getWarpSound() function
- //Be sure to work with the ZScript 10,000 multplier!
- break;
- in the ( long set_register(const long arg) switch(arg) ) STATEMENT, under 'Link's Variables' add a SETTER instruction case:
- case LINKDOTHING:
- Link.getLinkDoingThing(value/10000); //THis points to the LinkClass::getWarpSound() function
- //Be sure to work with the ZScript 10,000 multplier!
- break;
- /parser/ByteCode.h
- Add a new definition with a unique value using your ZASM label.
- This DOES NOT need to match the word table ID tat you set earlier.
- #define LINKDOTHING 401
- /parser/ByteCode.cpp
- in ( string VarArgument::toString() switch(ID) ) add a string redirect case for the ZASM label:
- case LINKDOTHING: //We the word table looks up the ZASM instruction and finds this...
- return "LINKDOTHING"; //it returns this string.
- /parser/GlobalSynbols.cpp
- in ( static AccessorTable LinkSTable[] ) add setter/getter entries.
- { "getDoThing", ScriptParser::TYPE_FLOAT, GETTER, PLAYWARPSOUND, 1, { ScriptParser::TYPE_LINK, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } },
- { "setDoThing", ScriptParser::TYPE_VOID, SETTER, PLAYWARPSOUND, 1, { ScriptParser::TYPE_LINK, ScriptParser::TYPE_FLOAT, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } },
- The first etry on each line is te ZScript command token. The chars 'set' or 'get' in lowercase are removed, so
- getDoThing / setDoThing i the Link table becomes Link->DoThing in ZScript.
- The second entry on eac line is the TYPE. Getters are typed by their return. Setters are void typed, but you
- must define the input type later.
- The third entry is the instruction type: SETTER, GETTER, or FUNCTION.
- The fourth, is the ZASM label.
- The fifth, is '1' unless you are creating a FUNCTION type.
- Following these, is a ScriptParser::Type. For Link->, this is always ScriptParser::TYPE_LINK
- Followin this, is the input parameters.
- For a return, these are always all -1.
- For a setter, the first is its value type (TYPE_FLOAT, or TYPE_BOOL).
- For a function, define each input parameter by its type.
- You're done.
- // Adding a Function
- //From DarkDragon regarding the stack:
- the ZScript calling convention is more or less equivalent to C's.
- The caller first pushes the return address onto the stack, then all of the function's explicit
- argument, and finally, if the function is a member function, the "this" pointer is implicitly
- pushed as the last argument.
- The return value of the function is by convention stored in EXP1.
- The callee, then, before it can return, must pop all of the arguments (including the "this" pointer)
- off of the stack. That's what the lines are doing that you've commented about. The ZASM instruction
- uses the top 4 entries of the stack, but leaves them untouched, so ZScript pops them off, then pops
- off the return address and jumps to it. You could use NUL instead of EXP2 for these pops if you wanted.
- The reason EXP2 is used as scratch space instead of EXP1 is simply to avoid accidentally tampering
- with the return value of the function.
- Addin a function requires some additional work, as follows:
- ffasm.cpp
- Add a new entry at the END of the table ( script_command command_list[NUMCOMMANDS+1] ) with the following format
- ZASMLABEL, number_of_args_for_the_assembler, has_arg_1, has_arg_2, more (?what's this now)
- These are used by the function: int set_argument(char *argbuf, ffscript **script, int com, int argument)
- Note that entries often have an 'R" and a 'V', meaning Register, and Value. These are effectively, ( ) and ( ).
- Keep a careful eye on the position of the function in this table, as it must match the position of their counterparts
- in ( enum ASM_DEFINE ) inside ffscript.h.
- ffscript.h
- Add an entry into the ( enum ASM_DEFINE ) table to match each entry that you added in ffasm.cpp. |
- These must be in the same sequence.
- ffscript.cpp
- Add a function to which you wish to pair each entry that you created in ffasm.cpp/ffscript.h
- ByteCode.cpp
- Add a string redirect, ( string OIdentifier::toString() ) in the list after the ( switch(ID) ) statement
- in ( string VarArgument::toString() )
- ByteCode.h
- Add a class definition, setting its identifier, public type (UnaryCode, BinaryCode, OpCode) and
- set its instructions. Return it new if desired.
- GlobalSymbols.cpp
- Insert the function into the table to which you desire to link it, based on the class to which you are assigning it.
- Set its type to ScriptParser::TYPE, its use to FUNCTION, its 'va' to '0' (this is for a script class variable,
- such as 'X' in 'Link->X'), its indices, its number_of_indices to -1 (these are for array script vars, such as
- ComboD[]), its type/class association, then, one entry for each of its inputs by type.
- Use -1 to fill in any unused inputs.
- In the vector for its class (e.g. ScreenSymbols::addSymbolsCode), create a routine for it, using the existing entries as
- examples. This may require writing a new opcode, or you may be able to use stack registers directly, depending on how many
- values you need to parse, and your return types.
- //Global
- //NPCs
- //Weapons
- //FFCs
- //Items
- //itemdata
- ref->info /The current, selected object int he struct is identified by *
- /// By ZoriaRPG
- /// Original version, (c) 8th December, 2016
- /// Rev 2 (c) 14th December, 216
- /// Revised version (c) 23rd October, 2018
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement