From eb7ddd289c5afafcb4da16a9d1cb098a1ab3d1d3 Mon Sep 17 00:00:00 2001 From: nev6502 Date: Fri, 12 Jun 2020 20:44:44 -0600 Subject: [PATCH] Work done on the scripting engine Probably going to need some discussion --- code/REDALERT/MapScript.cpp | 633 ++++++++++++++++++++++++++++++++---- 1 file changed, 572 insertions(+), 61 deletions(-) diff --git a/code/REDALERT/MapScript.cpp b/code/REDALERT/MapScript.cpp index 8fc67d4..a18da70 100644 --- a/code/REDALERT/MapScript.cpp +++ b/code/REDALERT/MapScript.cpp @@ -4,91 +4,602 @@ #include "FUNCTION.H" #include "MapScript.h" -static int Script_GiveMoney(lua_State* L) { - int cashToGive = lua_tointeger(L, -1); - PlayerPtr->Refund_Money(cashToGive); - return 1; +/*********************************************************************************************** + * Script_GiveCreditsToPlayer - Gives a given player a given amount of credits * + * * + * SCRIPT INPUT: houseType (int) - The player (house) index to count buildings for * + * cashToGive (int) - The amount of cold hard credits to give the player * + * * + * SCRIPT OUTPUT: result (number) - The amount of the given buildings for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + * HISTORY: * + * 6/11/2020 - JM Created * + * 6/12/2020 - JJ Added player (house) index input * + *=============================================================================================*/ +static int Script_GiveCreditsToPlayer(lua_State* L) { + int houseType = lua_tointeger(L, 1); + int cashToGive = lua_tointeger(L, 2); + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType) { + house->Refund_Money(cashToGive); + } + + } + + return 1; } +/*********************************************************************************************** + * Script_NumBuildingTypeForPlayer - Returns the amount of specific buildings for a player * + * * + * SCRIPT INPUT: houseType (int) - The player (house) index to count buildings for * + * structType (int) - The structure index to count buildings for * + * * + * SCRIPT OUTPUT: result (number) - The amount of the given buildings for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ static int Script_NumBuildingTypeForPlayer(lua_State* L) { - int houseType = lua_tointeger(L, 1); - int structType = lua_tointeger(L, 2); - int result = 0; - for (int b_index = 0; b_index < Buildings.Count(); b_index++) { - BuildingClass* building = Buildings.Ptr(b_index); + int houseType = lua_tointeger(L, 1); + int structType = lua_tointeger(L, 2); + int result = 0; + for (int b_index = 0; b_index < Buildings.Count(); b_index++) { + BuildingClass* building = Buildings.Ptr(b_index); - if (building->Owner() == houseType) { - if (building->Class->Type == structType) { - result++; - } - } - } + if (building->Owner() == houseType) { + if (building->Class->Type == structType) { + result++; + } + } + } - lua_pushnumber(L, result); - return 1; + lua_pushnumber(L, result); + return 1; } +/*********************************************************************************************** + * Script_StartMissionTimer - Starts the in-game mission timer * + * * + * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ static int Script_StartMissionTimer(lua_State* L) { - int ret = lua_tointeger(L, -1); - Scen.MissionTimer = Scen.MissionTimer + (ret * (TICKS_PER_MINUTE / 10)); - Scen.MissionTimer.Start(); - Map.Redraw_Tab(); - return 1; + int ret = lua_tointeger(L, -1); + Scen.MissionTimer = Scen.MissionTimer + (ret * (TICKS_PER_MINUTE / 10)); + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + return 1; } +/*********************************************************************************************** + * Script_StopMissionTimer - Stops the in-game mission timer * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ static int Script_StopMissionTimer(lua_State* L) { - Scen.MissionTimer.Start(); - return 1; + Scen.MissionTimer.Start(); + return 1; } + +/*********************************************************************************************** + * Script_CreateTeam - Attempts to create team (as defined in map) * + * * + * SCRIPT INPUT: teamType (int) - The team index to attempt to create * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CreateTeam(lua_State* L) { + + const char* teamType = lua_tostring(L, -1); + + TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); + assert(teamPtr != NULL); + + ScenarioInit++; + teamPtr->Create_One_Of(); + ScenarioInit--; + + + + return 1; +} + +/*********************************************************************************************** + * Script_PlayMovie - Plays the given movie * + * * + * SCRIPT INPUT: ret (int) - The movie index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ static int Script_PlayMovie(lua_State* L) { - int ret = lua_tointeger(L, -1); - Play_Movie((VQType)ret); - return 1; + int ret = lua_tointeger(L, -1); + Play_Movie((VQType)ret); + return 1; } -static int Script_Speak(lua_State* L) { - int ret = lua_tointeger(L, -1); - Speak((VoxType)ret); - return 1; +/*********************************************************************************************** + * Script_TriggerText - Triggers text to display in-game * + * * + * SCRIPT INPUT: textIndex (int) - The text index to display (from tutorial.ini) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_TriggerText(lua_State* L) { + int textIndex = lua_tointeger(L, -1); + + // TODO: Implement colors / flags + + // JJQUESTION: This appears right but doesn't do anything. Is text broken at the moment? + if (textIndex > 0 && textIndex < sizeof(TutorialText)) { + Session.Messages.Add_Message(NULL, textIndex, (char*)TutorialText[textIndex], PCOLOR_GREEN, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + + return 1; } + + + + +/*********************************************************************************************** + * Script_DestroyTrigger - Reveals the entire map * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_DestroyTrigger(lua_State* L) { + if (!PlayerPtr->IsVisionary) { + PlayerPtr->IsVisionary = true; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + } + + return 1; +} + +/*********************************************************************************************** + * Script_AutoCreate - Allows house AI to begin creating autocreate teams * + * * + * Autocreation means that instead of a teamtype being * + * manually created by a Create Team trigger action, that * + * it will be subject to the autocreate function. Each * + * computer country can have its autocreation function * + * turned on through a trigger action, after which the * + * creation of the teamtypes that have autocreation turned * + * on will be subject to being created, at the whim of the * + * autocreate function. See [TeamTypes] Section for more * + * details. * + * - The Red Alert Single Player Mission Creation Guide * + * * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow autocreate for * + * createEnables (bool, default yes) - Allow autocreate on/off * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AutoCreate(lua_State* L) { + + + + int houseType = 0; + bool createEnabled = true; + + houseType = lua_tointeger(L, 1); + + if (lua_gettop(L) == 2) { + createEnabled = lua_toboolean(L, 2); + } + + if (createEnabled) { + HouseClass::As_Pointer((HousesType)houseType)->AlertTime = 0; + } + + if (houseType != HOUSE_NONE) { + HouseClass* specified_house = HouseClass::As_Pointer((HousesType)houseType); + specified_house->IsAlerted = createEnabled; + + } + + return 1; +} + +/*********************************************************************************************** + * Script_AllowWin - Allows a win...ire map * + * * + * Used when you want a specific objective * + * to be met before the mission is accomplished. In other words, if the * + * trigger with this action assigned to it hasn't been fired, but the * + * actual WIN trigger has been fired, the mission will not end until this * + * "allow win" trigger is fired. * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AllowWin(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + HouseClass::As_Pointer((HousesType)houseType)->Blockage++; + } + + return 1; +} + + +/*********************************************************************************************** + * Script_RevealAll - Reveals the entire map TODO: input player * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_RevealAll(lua_State* L) { + if (!PlayerPtr->IsVisionary) { + PlayerPtr->IsVisionary = true; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + } + + return 1; +} + +/*********************************************************************************************** + * Script_RevealCell - Reveals map around given waypoint TODO: input player * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_RevealCell(lua_State* L) { + int ret = lua_tointeger(L, -1); + + if (!PlayerPtr->IsVisionary) { + Map.Sight_From(Scen.Waypoint[ret], Rule.GapShroudRadius, PlayerPtr, false); + } + + return 1; +} + +/*********************************************************************************************** + * Script_RevealZone - Reveals area of map around given waypoint TODO: input player * + * * + * Reveal all cells of the zone that the specified waypoint is located in. This can be * + * used to reveal whole islands or bodies of water * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_RevealZone(lua_State* L) { + int ret = lua_tointeger(L, -1); + + if (!PlayerPtr->IsVisionary) { + int zone = Map[Scen.Waypoint[ret]].Zones[MZONE_CRUSHER]; + + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Map[cell].Zones[MZONE_CRUSHER] == zone) { + Map.Map_Cell(cell, PlayerPtr); + } + } + + } + + return 1; +} + + +/*********************************************************************************************** + * Script_PlaySound - Plays the given Sound * + * * + * SCRIPT INPUT: ret (int) - The sound index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_PlaySound(lua_State* L) { + int ret = lua_tointeger(L, -1); + + Sound_Effect((VocType)ret); + + return 1; +} + + +/*********************************************************************************************** + * Script_PlayMusic - Plays the given Music score. * + * * + * SCRIPT INPUT: ret (int) - The music index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_PlayMusic(lua_State* L) { + int ret = lua_tointeger(L, -1); + Theme.Play_Song((ThemeType)ret); + return 1; +} + + +/*********************************************************************************************** + * Script_PlaySpeech - Plays the given Speech audio * + * * + * SCRIPT INPUT: ret (int) - The speech index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_PlaySpeech(lua_State* L) { + int ret = lua_tointeger(L, -1); + Speak((VoxType)ret); + return 1; +} + + +/*********************************************************************************************** + * Script_SetBriefingText - Sets the text on the mission briefing screen * + * * + * SCRIPT INPUT: (char*); The input text * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ static int Script_SetBriefingText(lua_State* L) { - strcpy(Scen.BriefingText, lua_tostring(L, -1)); - return 1; + strcpy(Scen.BriefingText, lua_tostring(L, -1)); + return 1; } -/* -================== -MapScript::CallFunction -================== -*/ + +/*********************************************************************************************** + * MapScript::CallFunction - Calls a given function within the current script * + * * + * INPUT: functionName - The name of the function to call * + * * + * OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ void MapScript::CallFunction(const char* functionName) { - lua_getglobal(L, functionName); - lua_pcall(L, 0, 0, 0); + lua_getglobal(L, functionName); + lua_pcall(L, 0, 0, 0); } //BriefingText -/* -================== -MapScript::Init -================== -*/ -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)) { - L = NULL; - return false; - } - lua_register(L, "Play_Movie", Script_PlayMovie); - lua_register(L, "SetBriefingText", Script_SetBriefingText); - lua_register(L, "Speak", Script_Speak); - lua_register(L, "StartMissionTimer", Script_StartMissionTimer); - lua_register(L, "StopMissionTimer", Script_StopMissionTimer); - lua_register(L, "NumBuildingTypeForPlayer", Script_NumBuildingTypeForPlayer); - lua_register(L, "GiveMoney", Script_GiveMoney); - return true; +/*********************************************************************************************** + * MapScript::Init -- Loads and initializes script for a given map * + * * + * This routine is called at the beginning of a mission * + * * + * INPUT: mapName - The script path * + * * + * OUTPUT: bool; did the map script load and initialize? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +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)) { + L = NULL; + return false; + } + + /********************************************************************************************** + * Red Alert Vanilla Actions * + *=============================================================================================*/ + + // player wins! + // player loses. + // computer begins factory production. + lua_register(L, "CreateTeam", Script_CreateTeam); // computer creates a certain type of team + // destroy team (comment was redacted. was something offensive originally written?) + // all enemy units go into hunt mode (teams destroyed). + // player gets reinforcements + // (house that gets them is determined by + // the Reinforcement instance) + // Deploy drop zone smoke. + // Sell all buildings and go on rampage. + lua_register(L, "PlayMovie", Script_PlayMovie); // Play movie (temporarily suspend game). + lua_register(L, "TriggerText", Script_TriggerText); // Triggers a text message display. + lua_register(L, "DestroyTrigger", Script_DestroyTrigger); // Destroy specified trigger. + lua_register(L, "AutoCreate", Script_AutoCreate); // Computer to autocreate teams. + // Win if captured, lose if destroyed ( function does not exist ) + lua_register(L, "AllowWin", Script_AllowWin); // Allows winning if triggered. + + lua_register(L, "RevealAll", Script_RevealAll); // Reveal the entire map. + lua_register(L, "RevealCell", Script_RevealCell); // Reveal map around cell #. + lua_register(L, "RevealZone", Script_RevealZone); // Reveal all of specified zone. + lua_register(L, "PlaySound", Script_PlaySound); // Play sound effect. + lua_register(L, "PlayMusic", Script_PlayMusic); // Play musical score. + lua_register(L, "PlaySpeech", Script_PlaySpeech); // Play EVA speech. + // Force trigger to activate. + lua_register(L, "StartMissionTimer", Script_StartMissionTimer); // Start mission timer. + lua_register(L, "StopMissionTimer", Script_StopMissionTimer); // Stop mission timer. + + +/********************************************************************************************** +* Red Alert Vanilla Events * +*=============================================================================================*/ + +// player enters this square +// Spied by. +// Thieved by (raided or stolen vehicle). +// player discovers this object +// House has been discovered. +// player attacks this object +// player destroys this object +// Any object event will cause the trigger. +// all house's units destroyed +// all house's buildings destroyed +// all house's units & buildings destroyed +// house reaches this many credits +// Scenario elapsed time from start. +// Pre expired mission timer. +// Number of buildings destroyed. +// Number of units destroyed. +// No factories left. +// Civilian has been evacuated. +// Specified building has been built. +// Specified unit has been built. +// Specified infantry has been built. +// Specified aircraft has been built. +// Specified team member leaves map. +// Enters same zone as waypoint 'x'. +// Crosses horizontal trigger line. +// Crosses vertical trigger line. +// If specified global has been set. +// If specified global has been cleared. +// If all fake structures are gone. +// When power drops below 100%. +// All bridges destroyed. +// Check for building existing. + + +/********************************************************************************************** +* Utility Functions * +*=============================================================================================*/ + + lua_register(L, "SetBriefingText", Script_SetBriefingText); // Sets the [text] on the mission briefing screen + lua_register(L, "GiveCreditsToPlayer", Script_GiveCreditsToPlayer); // Give [player] [credits] + lua_register(L, "NumBuildingTypeForPlayer", Script_NumBuildingTypeForPlayer); // Number of buildings of [type] for given [player] + + +/********************************************************************************************** +* Script Globals * +*=============================================================================================*/ + + lua_pushnumber(L, PlayerPtr->ID); // Local (human) player + lua_setglobal(L, "_localPlayer"); + + + return true; }