1 #ifndef _SCRIPTING_H
2 #define _SCRIPTING_H
3
4 #include "globalincs/globals.h"
5 #include "globalincs/pstypes.h"
6
7 #include "graphics/2d.h"
8 #include "scripting/ade.h"
9 #include "scripting/ade_args.h"
10 #include "scripting/lua/LuaFunction.h"
11 #include "utils/event.h"
12
13 //**********Scripting languages that are possible
14 #define SC_LUA (1<<0)
15
16 //*************************Scripting structs*************************
17 #define SCRIPT_END_LIST NULL
18
19 namespace scripting {
20 struct ScriptingDocumentation;
21 }
22
23 struct image_desc
24 {
25 char fname[MAX_FILENAME_LEN];
26 int handle;
27 };
28
29 struct script_function {
30 int language = 0;
31 luacpp::LuaFunction function;
32 };
33
34 //-WMC
35 struct script_hook
36 {
37 //Override
38 script_function override_function;
39
40 //Actual hook
41 script_function hook_function;
42 };
43
44 extern bool script_hook_valid(script_hook *hook);
45
46 //**********Main Conditional Hook stuff
47
48 #define MAX_HOOK_CONDITIONS 8
49
50 // Conditionals
51 enum ConditionalType {
52 CHC_NONE = -1,
53 CHC_MISSION = 0,
54 CHC_SHIP = 1,
55 CHC_SHIPCLASS = 2,
56 CHC_SHIPTYPE = 3,
57 CHC_STATE = 4,
58 CHC_CAMPAIGN = 5,
59 CHC_WEAPONCLASS = 6,
60 CHC_OBJECTTYPE = 7,
61 CHC_KEYPRESS = 8,
62 CHC_ACTION = 9,
63 CHC_VERSION = 10,
64 CHC_APPLICATION = 11,
65 };
66
67 //Actions
68 enum ConditionalActions : int32_t {
69 CHA_NONE = -1,
70 CHA_DEATH,
71 CHA_ONFRAME,
72 CHA_COLLIDESHIP,
73 CHA_COLLIDEWEAPON,
74 CHA_COLLIDEDEBRIS,
75 CHA_COLLIDEASTEROID,
76 CHA_HUDDRAW,
77 CHA_OBJECTRENDER,
78 CHA_SPLASHSCREEN,
79 CHA_GAMEINIT,
80 CHA_MISSIONSTART,
81 CHA_MISSIONEND,
82 CHA_MOUSEMOVED,
83 CHA_MOUSEPRESSED,
84 CHA_MOUSERELEASED,
85 CHA_KEYPRESSED,
86 CHA_KEYRELEASED,
87 CHA_ONSTATESTART,
88 CHA_ONSTATEEND,
89 CHA_ONWEAPONDELETE,
90 CHA_ONWPEQUIPPED,
91 CHA_ONWPFIRED,
92 CHA_ONWPSELECTED,
93 CHA_ONWPDESELECTED,
94 CHA_GAMEPLAYSTART,
95 CHA_ONTURRETFIRED,
96 CHA_PRIMARYFIRE,
97 CHA_SECONDARYFIRE,
98 CHA_ONSHIPARRIVE,
99 CHA_COLLIDEBEAM,
100 CHA_ONACTION,
101 CHA_ONACTIONSTOPPED,
102 CHA_MSGRECEIVED,
103 CHA_HUDMSGRECEIVED,
104 CHA_AFTERBURNSTART,
105 CHA_AFTERBURNEND,
106 CHA_BEAMFIRE,
107 CHA_SIMULATION,
108 CHA_LOADSCREEN,
109 CHA_CMISSIONACCEPT,
110 CHA_ONSHIPDEPART,
111 CHA_ONWEAPONCREATED,
112 CHA_ONWAYPOINTSDONE,
113 CHA_ONSUBSYSDEATH,
114 CHA_ONGOALSCLEARED,
115 CHA_ONBRIEFSTAGE,
116
117 // DO NOT ADD NEW HOOKS HERE
118 // There is a new Lua Hook API, see hook_api.h
119 // There you use either scripting::Hook for non-overridable or scripting::OverridableHook for overridable hooks
120 // while also having the option to document when the hook is called and what hook variables are set for it.
121
122 CHA_LAST = CHA_ONBRIEFSTAGE,
123 };
124
125 // management stuff
126 void scripting_state_init();
127 void scripting_state_close();
128 void scripting_state_do_frame(float frametime);
129
130 int32_t scripting_string_to_action(const char* action);
131 ConditionalType scripting_string_to_condition(const char* condition);
132
133 struct script_condition
134 {
135 ConditionalType condition_type = CHC_NONE;
136 SCP_string condition_string;
137 // stores values evaluated at hook load to optimize later condition checking.
138 // currently mostly olg done for the highest impact condition types, according to performance profiling
139 // the exact type of information stored varries between hook types.
140 // CHC_STATE, CHC_OBJECTTYPE - stores the value of enum matching the name requested by the condition string.
141 // CHC_SHIPCLASS, CHC_WEAPONCLASS - stores the index of the info object requested by the condition
142 // CHC_VERSION, CHC_APPLICATION - stores validity of the check in 1 for true or 0 for false, as the condition will not change after load.
143 // see ConditionedHook::AddCondition for exact implimentation
144 int condition_cached_value = -1;
145 };
146
147 struct script_action
148 {
149 int32_t action_type {CHA_NONE};
150 script_hook hook;
151 };
152
153 class ConditionedHook
154 {
155 public:
156 SCP_vector<script_action> Actions;
157 SCP_vector<script_condition> Conditions;
158 bool AddCondition(script_condition *sc);
159 bool AddAction(script_action *sa);
160
161 bool ConditionsValid(int action, class object *objp=NULL, int more_data = 0);
162 bool IsOverride(class script_state *sys, int action);
163 bool Run(class script_state* sys, int action);
164 };
165
166 //**********Main script_state function
167 class script_state
168 {
169 private:
170 char StateName[32];
171
172 int Langs;
173 struct lua_State *LuaState;
174 const struct script_lua_lib_list *LuaLibs;
175
176 //Utility variables
177 SCP_vector<image_desc> ScriptImages;
178 SCP_vector<ConditionedHook> ConditionalHooks;
179
180 SCP_vector<script_function> GameInitFunctions;
181
182 // Stores references to the Lua values for the hook variables. Uses a raw reference since we do not need the more
183 // advanced features of LuaValue
184 // values are a vector to provide a stack of values. This is necessary to ensure consistent behavior if a scripting
185 // hook is called from within another script (e.g. calls to createShip)
186 SCP_unordered_map<SCP_string, SCP_vector<luacpp::LuaReference>> HookVariableValues;
187 // ActiveActions lets code that might run scripting hooks know whether any scripts are even registered for it.
188 // AssayActions is responsible for keeping it up to date.
189 bool ActiveActions[ConditionalActions::CHA_LAST+1];
190
191 void ParseChunkSub(script_function& out_func, const char* debug_str=NULL);
192
193 void SetLuaSession(struct lua_State *L);
194
195 static void OutputLuaDocumentation(scripting::ScriptingDocumentation& doc,
196 const scripting::DocumentationErrorReporter& errorReporter);
197
198 //Internal Lua helper functions
199 void EndLuaFrame();
200
201 public:
202 //***Init/Deinit
203 script_state(const char *name);
204
205 script_state(const script_state&) = delete;
206 script_state& operator=(script_state &i) = delete;
207
208 ~script_state();
209
210 /**
211 * @brief Resets the lua state and frees all resources
212 */
213 void Clear();
214
215 //***Internal scripting stuff
216 int LoadBm(const char* name);
217 void UnloadImages();
218
GetLuaSession()219 lua_State *GetLuaSession(){return LuaState;}
220
221 //***Init functions for langs
222 int CreateLuaState();
223
224 //***Get data
225 scripting::ScriptingDocumentation OutputDocumentation(const scripting::DocumentationErrorReporter& errorReporter);
226
227 //***Moves data
228 //void MoveData(script_state &in);
229
230 template<typename T>
231 void SetHookVar(const char *name, char format, T&& value);
232 void SetHookObject(const char *name, object *objp);
233 void SetHookObjects(int num, ...);
234 void RemHookVar(const char *name);
235 void RemHookVars(std::initializer_list<SCP_string> names);
236
237 const SCP_unordered_map<SCP_string, SCP_vector<luacpp::LuaReference>>& GetHookVariableReferences();
238
239 //***Hook creation functions
240 template <typename T>
241 bool EvalStringWithReturn(const char* string, const char* format = nullptr, T* rtn = NULL,
242 const char* debug_str = nullptr);
243 bool EvalString(const char* string, const char* debug_str = nullptr);
244 void ParseChunk(script_hook *dest, const char* debug_str=NULL);
245 void ParseGlobalChunk(ConditionalActions hookType, const char* debug_str=nullptr);
246 bool ParseCondition(const char *filename="<Unknown>");
247 void AddConditionedHook(ConditionedHook hook);
248 void AssayActions();
249 bool IsActiveAction(ConditionalActions action_id);
250
251 void AddGameInitFunction(script_function func);
252
253 //***Hook running functions
254 template <typename T>
255 int RunBytecode(script_function& hd, char format = '\0', T* data = nullptr);
256 int RunBytecode(script_function& hd);
257 bool IsOverride(script_hook &hd);
258 int RunCondition(int condition, object* objp = nullptr, int more_data = 0);
259 bool IsConditionOverride(int action, object *objp=NULL);
260
261 void RunInitFunctions();
262
263 //*****Other functions
264 void EndFrame();
265
266 static script_state* GetScriptState(lua_State* L);
267
268 util::event<void, lua_State*> OnStateDestroy;
269 };
270
271 template<typename T>
SetHookVar(const char * name,char format,T && value)272 void script_state::SetHookVar(const char *name, char format, T&& value)
273 {
274 if(format == '\0')
275 return;
276
277 if(LuaState != nullptr)
278 {
279 char fmt[2] = {format, '\0'};
280 ::scripting::ade_set_args(LuaState, fmt, std::forward<T>(value));
281 auto reference = luacpp::UniqueLuaReference::create(LuaState);
282 lua_pop(LuaState, 1); // Remove object value from the stack
283
284 HookVariableValues[name].push_back(std::move(reference));
285 }
286 }
287
288 template <typename T>
EvalStringWithReturn(const char * string,const char * format,T * rtn,const char * debug_str)289 bool script_state::EvalStringWithReturn(const char* string, const char* format, T* rtn, const char* debug_str)
290 {
291 using namespace luacpp;
292
293 size_t string_size = strlen(string);
294 char lastchar = string[string_size - 1];
295
296 if (string[0] == '{') {
297 return false;
298 }
299
300 if (string[0] == '[' && lastchar != ']') {
301 return false;
302 }
303
304 size_t s_bufSize = string_size + 8;
305 std::string s;
306 s.reserve(s_bufSize);
307 if (string[0] != '[') {
308 if (rtn != nullptr) {
309 s = "return ";
310 }
311 s += string;
312 } else {
313 s.assign(string + 1, string + string_size);
314 }
315
316 SCP_string debug_name;
317 if (debug_str == nullptr) {
318 debug_name = "String: ";
319 debug_name += s;
320 } else {
321 debug_name = debug_str;
322 }
323
324 try {
325 auto function = LuaFunction::createFromCode(LuaState, s, debug_name);
326 function.setErrorFunction(LuaFunction::createFromCFunction(LuaState, scripting::ade_friendly_error));
327
328 try {
329 auto ret = function.call(LuaState);
330
331 if (rtn != nullptr && ret.size() >= 1) {
332 auto stack_start = lua_gettop(LuaState);
333
334 auto val = ret.front();
335 val.pushValue(LuaState);
336
337 scripting::internal::Ade_get_args_skip = stack_start;
338 scripting::internal::Ade_get_args_lfunction = true;
339 scripting::ade_get_args(LuaState, format, rtn);
340 }
341 } catch (const LuaException&) {
342 return false;
343 }
344 } catch (const LuaException& e) {
345 LuaError(GetLuaSession(), "%s", e.what());
346
347 return false;
348 }
349
350 return true;
351 }
352
353 template <typename T>
RunBytecode(script_function & hd,char format,T * data)354 int script_state::RunBytecode(script_function& hd, char format, T* data)
355 {
356 using namespace luacpp;
357
358 if (!hd.function.isValid()) {
359 return 1;
360 }
361
362 GR_DEBUG_SCOPE("Lua code");
363
364 try {
365 auto ret = hd.function.call(LuaState);
366
367 if (data != nullptr && ret.size() >= 1) {
368 auto stack_start = lua_gettop(LuaState);
369
370 auto val = ret.front();
371 val.pushValue(LuaState);
372
373 char fmt[2] = {format, '\0'};
374 scripting::internal::Ade_get_args_skip = stack_start;
375 scripting::internal::Ade_get_args_lfunction = true;
376 scripting::ade_get_args(LuaState, fmt, data);
377
378 // Reset stack again
379 lua_settop(LuaState, stack_start);
380 }
381 } catch (const LuaException&) {
382 return 0;
383 }
384
385 return 1;
386 }
387
388 //**********Script registration functions
389 void script_init();
390
391 //**********Script globals
392 extern class script_state Script_system;
393 extern bool Output_scripting_meta;
394 extern bool Output_scripting_json;
395
396 //*************************Conditional scripting*************************
397
398 #endif //_SCRIPTING_H
399