Added ability to attach Lua/MapScript callbacks to a trigger. Can be set to work with complex trigger actions.

Added scripts path to Lua so that Lua can find scripts within scripts/
This commit is contained in:
nev6502
2020-06-14 23:51:31 -06:00
parent fb4d3f272c
commit 4a7a3df2cc
6 changed files with 180 additions and 16 deletions
+113 -1
View File
@@ -72,6 +72,59 @@ static int Script_NumBuildingTypeForPlayer(lua_State* L) {
return 1;
}
/***********************************************************************************************
* Script_SetTriggerCallback - Adds a lua callback to an existing trigger *
* *
* SCRIPT INPUT: triggerName (string) - The ININame of the trigger via its class *
* callbackName (string) - The function name to be called when the actions *
* have been met *
* actionIndex (int) (optional) - 0 (default): always callback on any of the *
* given paths of the trigger *
* 1: only callback on first action *
* 2: only callback on second action *
* *
* SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player *
* *
* INPUT: lua_State - The current Lua state *
* *
* OUTPUT: int; Did the function run successfully? Return 1 *
* *
* WARNINGS: ? *
* *
*=============================================================================================*/
static int Script_SetTriggerCallback(lua_State* L) {
const char* triggerName = lua_tostring(L, 1);
const char* callbackName = lua_tostring(L, 2);
char actionIndex = 0;
// Optional actionIndex parameter
if (lua_gettop(L) == 3) {
actionIndex = lua_tointeger(L, 2);
}
// Find the trigger and set up callback
for (int t_index = 0; t_index < Triggers.Count(); t_index++) {
TriggerClass* trigger = Triggers.Ptr(t_index);
if (trigger != NULL) {
if (strcmp(trigger->Name(), triggerName) == 0) {
strncpy(trigger->MapScriptCallback,callbackName,sizeof(trigger->MapScriptCallback ) - 1);
trigger->MapScriptActionIndex = actionIndex;
break;
}
}
}
return 1;
}
/***********************************************************************************************
* Script_Win - The specified player wins *
@@ -779,11 +832,47 @@ static int Script_SetBriefingText(lua_State* L) {
* *
*=============================================================================================*/
void MapScript::CallFunction(const char* functionName) {
lua_getglobal(L, functionName);
lua_pcall(L, 0, 0, 0);
} //BriefingText
/***********************************************************************************************
* MapScript::setLuaPath - Sets Lua's path / extensions to search for scripts *
* *
* This helps Lua know where/how to look for files when using require(), otherwise we could *
* hard code path/extension as we did previously *
* *
* INPUT: pathvalue - See lua documentation RE: LUA_PATH to know how to deal with this *
* *
* OUTPUT: void *
* *
* WARNINGS: ? *
* *
*=============================================================================================*/
void MapScript::SetLuaPath( const char* input_path)
{
// Needs space to keep the initial paths
char new_path[1024];
// Set new GLOBALS.package.path
lua_getglobal(L, "package");
lua_getfield(L, -1, "path"); // get field "path" from table at top of stack (-1)
const char* cur_path = lua_tostring(L, -1); // grab path string from top of stack
sprintf(new_path, "%s;%s", cur_path, input_path);
lua_pop(L, 1);
lua_pushstring(L, new_path);
lua_setfield(L, -2, "path");
lua_pop(L, 1);
}
/***********************************************************************************************
* MapScript::Init -- Loads and initializes script for a given map *
* *
@@ -798,14 +887,35 @@ void MapScript::CallFunction(const char* functionName) {
*=============================================================================================*/
bool MapScript::Init(const char* mapName) {
char scriptName[256];
sprintf(scriptName, "scripts/%s.script", mapName);
L = luaL_newstate();
if (luaL_loadfile(L, scriptName) || lua_pcall(L, 0, 0, 0)) {
luaL_openlibs(L);
if (luaL_loadfile(L, scriptName)){
L = NULL;
return false;
}
// TODO: I've tried several combinations of paths (including "scripts/?"),
// but I can only get "require 'basefilename'" to work within a
// script, not "require 'basefilename.script'". This may be completely
// normal within lua, but it seems odd.
// This allows require() to work
SetLuaPath(";scripts/?.script;");
if (lua_pcall(L, 0, 0, 0)) {
L = NULL;
return false;
}
/**********************************************************************************************
* Red Alert Vanilla Actions *
*=============================================================================================*/
@@ -883,6 +993,8 @@ bool MapScript::Init(const char* mapName) {
lua_register(L, "GiveCreditsToPlayer", Script_GiveCreditsToPlayer); // Give [player] [credits]
lua_register(L, "NumBuildingTypeForPlayer", Script_NumBuildingTypeForPlayer); // Number of buildings of [type] for given [player]
lua_register(L, "SetTriggerCallback", Script_SetTriggerCallback); // Initiates a given [callback] on an existing [trigger]
/**********************************************************************************************
* Script Globals *
+5
View File
@@ -1,6 +1,9 @@
// MapScript.h
//
#ifndef MAPSCRIPT_H
#define MAPSCRIPT_H
// Lua is written in C, so compiler needs to know how to link its libraries
extern "C" {
# include "lua.h"
@@ -15,6 +18,8 @@ class MapScript {
public:
bool Init(const char* mapName);
void CallFunction(const char* functionName);
void SetLuaPath(const char* input_path);
private:
lua_State* L;
};
#endif
+11 -13
View File
@@ -60,7 +60,6 @@
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#include "MapScript.h"
#ifdef WIN32
#include "tcpip.h"
#include "ccdde.h"
@@ -111,7 +110,6 @@ bool Is_Mission_Counterstrike (char *file_name);
bool Is_Mission_Aftermath (char *file_name);
#endif
MapScript* mapScript = nullptr;
bool mapScriptPlayStart = false;
void Cmd_Map(void);
@@ -332,13 +330,13 @@ bool Start_Scenario(char * name, bool briefing)
}
// jmarshall: map scripts
if (mapScript) {
delete mapScript;
if (Scen.mapScript) {
delete Scen.mapScript;
}
mapScript = new MapScript();
if (!mapScript->Init(name)) {
delete mapScript;
mapScript = nullptr;
Scen.mapScript = new MapScript();
if (!Scen.mapScript->Init(name)) {
delete Scen.mapScript;
Scen.mapScript = nullptr;
}
else {
mapScriptPlayStart = true;
@@ -379,11 +377,11 @@ bool Start_Scenario(char * name, bool briefing)
// }
Theme.Stop();
// jmarshall - map scripts
if (mapScript) {
if (Scen.mapScript) {
Hide_Mouse();
VisiblePage.Clear();
Show_Mouse();
mapScript->CallFunction("map_briefing");
Scen.mapScript->CallFunction("map_briefing");
}
else if (briefing) {
Hide_Mouse();
@@ -430,16 +428,16 @@ bool Start_Scenario(char * name, bool briefing)
}
void Scenario_MapScriptFrame(void) {
if (!mapScript)
if (!Scen.mapScript)
return;
if (mapScriptPlayStart) {
mapScript->CallFunction("map_start");
Scen.mapScript->CallFunction("map_start");
mapScriptPlayStart = false;
return;
}
mapScript->CallFunction("map_frame");
Scen.mapScript->CallFunction("map_frame");
}
+8
View File
@@ -36,6 +36,8 @@
#ifndef SCENARIO_H
#define SCENARIO_H
#include "MapScript.h"
/*
** This class holds the information about the current game being played. This information is
@@ -328,6 +330,12 @@ class ScenarioClass {
#endif
bool warpIntoMap;
/*
** MapScript storage, to be accessed by triggers etc.
*/
MapScript* mapScript = nullptr;
};
+37 -2
View File
@@ -45,6 +45,7 @@
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#include "MapScript.h"
#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR)
@@ -228,6 +229,7 @@ bool TriggerClass::Spring(TEventType event, ObjectClass * obj, CELL cell, bool f
bool e1 = Class->Event1(Event1, event, Class->House, obj, forced);
bool e2 = false;
bool execute = false;
bool execute_mapScript = false;
/*
** Forced triggers must presume that the cell parameter is invalid. It then
@@ -295,6 +297,13 @@ bool TriggerClass::Spring(TEventType event, ObjectClass * obj, CELL cell, bool f
}
}
/*
** We need to execute this trigger, so is there an attached MapScript callback?
*/
if (Scen.mapScript != NULL && strlen(MapScriptCallback) > 0) {
execute_mapScript = true;
}
/*
** For linked trigger events, perform the action associated with the matching
** trigger event. Otherwise perform the action for both events.
@@ -302,19 +311,45 @@ bool TriggerClass::Spring(TEventType event, ObjectClass * obj, CELL cell, bool f
bool ok = false;
HousesType hh = Class->House;
if (Class->EventControl == MULTI_LINKED) {
if (e1 || forced) ok |= Class->Action1(hh, obj, ID, cell);
if (e2 && !forced) ok |= Class->Action2(hh, obj, ID, cell);
if (e1 || forced) {
ok |= Class->Action1(hh, obj, ID, cell);
// Execute MapScript Callback for action 1?
if (ok && execute_mapScript && MapScriptActionIndex <= 1) {
Scen.mapScript->CallFunction(MapScriptCallback);
}
}
if (e2 && !forced) {
ok |= Class->Action2(hh, obj, ID, cell);
// Execute MapScript Callback for action 2?
if (ok && execute_mapScript && MapScriptActionIndex != 1) {
Scen.mapScript->CallFunction(MapScriptCallback);
}
}
} else {
switch (Class->ActionControl) {
case MULTI_ONLY:
ok |= Class->Action1(hh, obj, ID, cell);
// Execute MapScript Callback for action 1?
if (ok && execute_mapScript && MapScriptActionIndex <= 1) {
Scen.mapScript->CallFunction(MapScriptCallback);
}
break;
default:
case MULTI_AND:
ok |= Class->Action1(hh, obj, ID, cell);
ok |= Class->Action2(hh, obj, ID, cell);
// Execute MapScript Callback for action?
if (ok && execute_mapScript) {
Scen.mapScript->CallFunction(MapScriptCallback);
}
break;
}
}
+6
View File
@@ -115,6 +115,12 @@ class TriggerClass {
** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load
*/
unsigned char SaveLoadPadding[32];
/*
** Store MapScript callback function and optional action index (because there can be multiple outcomes) -- JJ 6/14/2020
*/
char MapScriptCallback[32];
char MapScriptActionIndex;
};