fose_example_plugin source. This is also includes relevant to some of my work.
#include "fose/PluginAPI.h"#include "fose/CommandTable.h"#include "fose/GameRTTI.h"#include "fose/GameAPI.h"#include "fose/ParamInfos.h"#include "fose/GameObjects.h"#include "fose/GameInterface.h"#include "fose/GameExtraData.h"
The header file CommandTable.h contains these lines
extern CommandTable g_consoleCommands;extern CommandTable g_scriptCommands;
How exactly do I access those two in my main.cpp?
Here is a sample of how I might use it:
CommandInfo* pCI = g_consoleCommands.GetStart();
It produces these linker errors
1>main.obj : error LNK2019: unresolved external symbol "public: struct CommandInfo * __thiscall CommandTable::GetStart(void)" (?GetStart@CommandTable@@QAEPAUCommandInfo@@XZ) referenced in function "int __cdecl FindGameCmd(char *,bool)" (?FindGameCmd@@YAHPAD_N@Z)1>main.obj : error LNK2001: unresolved external symbol "class CommandTable g_consoleCommands" (?g_consoleCommands@@3VCommandTable@@A)
My last problem is related to calling a script/console command of my own and most importantly an existing script/console command of the game.
I could not use the CommandTable due to the linker errors so I resorted to a work around.
int FindGameCmd(char* nzName, bool Console = false) { CommandInfo* pCI = g_consoleCommands.GetStart(); UInt32 nCmdListBegin = CmdRange.Start; UInt32 nCmdListEnd = CmdRange.End; CommandInfo* pCmdInfo = (CommandInfo*)nCmdListBegin; int nCmdIndex = 0; int nCmdCount = 300; for(int iii = 0; iii < nCmdCount; ++iii) { if( iii >= nCmdCount) { Console_Print("Search Limit Reached. Failed."); break; } size_t found = std::string::npos; std::string strCmdNameShort (pCmdInfo->shortName); found = strCmdNameShort.find(nzName); if(found != std::string::npos) { nCmdIndex = iii; Console_Print("name: %s index: %d via: short-name", pCmdInfo->shortName, nCmdIndex); break; } found = std::string::npos; std::string strCmdNameLong (pCmdInfo->longName); found = strCmdNameLong.find(nzName); if (found != std::string::npos) { nCmdIndex = iii; Console_Print("name: %s index: %d via: long-name", pCmdInfo->longName, nCmdIndex); break; } pCmdInfo = (CommandInfo*)(nCmdListBegin + (iii * sizeof(CommandInfo))); } return nCmdIndex-1;}
Later I show how I use the returned int (nCmdIndex) in my calculations to get a correct CommandInfo pointer for my target command.
Briefly I will share the basics of what I know.
From my understand there are at least 3 locations you will have in your source that relate to a custom command you make.
1. You Define it.
// Simple Example
DEFINE_COMMAND_PLUGIN(ExamplePlugin_Test, "test", 0, 0, NULL);
// Example with requirement of a ref. DEFINE_COMMAND_PLUGIN(ExamplePlugin_Test, "test. ref required. e.g. player.YourCommand", 1, 0, NULL);
The fourth parameter species how many arguments are expected.// Three ArgumentsDEFINE_COMMAND_PLUGIN(ExamplePlugin_Test, "test. ref required. e.g. player.YourCommand", 1, 3, NULL);
Lastly if you noticed in the previous examples the last parameter is NULL. To set your ParamInfo you need a collection of ParamTypes. You can check with
the CommandTable.h for the ParamTypes already enumerated.
FOSE has common ParamInfos setup already. see CommandList.h
Example of a Custom
static ParamInfo kParams_PlaceAtMe[4] = { {"ObjectID:ref", kParamType_TESObject, 0}, {"Count:int", kParamType_Integer, 0}, {"Distance:float", kParamType_Float, 1}, {"Direction:int", kParamType_Integer, 1} // 1 = optional};// Usage. Notice the 4 matches the size of our PlaceAtMeDEFINE_COMMAND_PLUGIN(ExamplePlugin_Test, "test", 0, 4, kParams_PlaceAtMe);
2. The Guts
The guts of the function should be right after the ifdef #RUNTIME line and not after the corresponding #endif. However the DEFINITON is after the #endif
// Minimum needed inside your command .
bool Cmd_ExamplePlugin_Test_Execute(COMMAND_ARGS){*result = 0;return true;}
3. Register the CMD in the FOSEPlugin_Load
// register with a code-range in mind before releasing. The FOSE makers appreciate it if you let them know how many commands you need. The
OpCodeBase value may also be different
// depending on who else has already contacted FOSE makers for space.
fose->SetOpcodeBase(0x2100); fose->RegisterCommand(&kCommandInfo_ExamplePlugin_Test); fose->RegisterCommand(&kCommandInfo_ExamplePlugin_TestOther);
Lastly I will state some things I am unclear in my understanding of.
COMMAND_ARGS contains a void* named arg1. There are some ExtractArg functions to retrieve the arguments. But those aren't good enough in my case. While I
can access the existing arguments using the Extraction method,
how would I add to the arguments?
First you may ask why would I want to? It is because the COMMAND_ARGS passed into my custom command are relevant to that command and not something like
PlaceAtMe. I could define my custom command to take the same arguments and paramInfo as PlaceAtMe but that isn't the point.
// I set a CommandInfo pointer to the address of my target function so that I can call execute();
CommandInfo* pCmdInfo = (CommandInfo*)(CmdRange.Start + (nCmdIndex * + sizeof(CommandInfo)));// the PASS_COMMAND_ARGS takes everything in COMMAND_ARGS and passes it in.pCmdInfo->execute(PASS_COMMAND_ARGS);
The larger picture is simply this:bool Cmd_ExamplePlugin_Test_Execute(COMMAND_ARGS){int nCmdIndex = FindGameCmd("PlaceAtMe", true);CommandInfo* pCmdInfo = (CommandInfo*)(CmdRange.Start + (nCmdIndex * + sizeof(CommandInfo)));paramInfo = kParams_PlaceAtMe; // Again there is no PackArgs or ConstructArgs. The following byte array is important for placeatme to work. BYTE baArgs [18]={0x01, 0x20, 0x0A, 0x00, 0x02, 0x00, 0x72, 0x01, 0x00, 0x6E, 0x01, 0x00, 0x00, 0x00, // count? 0x00, 0x00, 0x00, 0x00};// Another thing I don't understand is why references/TESObjects aren't part of the arguments in arg1. // Also why can't we just pass in a pointer to a Struct matching the format of kParams for the args?arg1 = &baArgs;// Depending on your custom command the scriptObj's number of references could be less than two.// However for PlaceAtMe two references are involved. One reference is the object we are placing at. The second reference is the object we are placing.scriptObj->info.numRefs = 2;// varItemBase will be our toPlace reference variable. // varItemBase may need to be moved out of your custom command's scope.Script::RefVariable varItemBase;// pDupeForm can be swapped withvarItemBase.form = pDupeForm;//varIdx 0 is the first reference id variable. the reference your mouse has highlighted would be this .// varIdx 1 is the reference id you want to placevarItemBase.varIdx = 1;scriptObj->refs.AddAt(&varItemBase, 1);pCmdInfo->execute(PASS_COMMAND_ARGS);return true;}
Phew that was a lot. So with a for loop, how exactly would I call execute x amount of times, and everytime actually work like the first?