1 extern "C" {
2 #include <lua.h>
3 #include <lualib.h>
4 #include <lauxlib.h>
5 }
6 
7 #include "core/gl_text.h"
8 #include "core/console.h"
9 #include "script/script.h"
10 #include "gui/gui.h"
11 #include "audio/audio.h"
12 #include "engine.h"
13 #include "gameflow.h"
14 #include "game.h"
15 #include "world.h"
16 
17 #include <assert.h>
18 #include <string.h>
19 #include <vector>
20 
21 typedef struct gameflow_action_s
22 {
23     int16_t      m_opcode;
24     uint16_t     m_operand;
25 } gameflow_action_t;
26 
27 struct gameflow_s
28 {
29     int                             m_currentGameID;
30     int                             m_currentLevelID;
31 
32     int                             m_nextGameID;
33     int                             m_nextLevelID;
34 
35     char                            m_currentLevelName[LEVEL_NAME_MAX_LEN];
36     char                            m_currentLevelPath[MAX_ENGINE_PATH];
37     char                            m_secretsTriggerMap[GF_MAX_SECRETS];
38 
39     std::vector<gameflow_action_t>    m_actions;
40 } global_gameflow;
41 
42 typedef struct level_info_s
43 {
44     int num_levels = 0;
45     char name[LEVEL_NAME_MAX_LEN];
46     char path[MAX_ENGINE_PATH];
47     char pic[MAX_ENGINE_PATH];
48 }level_info_t, *level_info_p;
49 
50 
51 bool Gameflow_GetLevelInfo(level_info_p info, int game_id, int level_id);
52 bool Gameflow_GetFMVPath(level_info_p info, int fmv_id);
53 bool Gameflow_SetGameInternal(int game_id, int level_id);
54 
55 
Gameflow_Init()56 void Gameflow_Init()
57 {
58     global_gameflow.m_nextGameID = -1;
59     global_gameflow.m_nextLevelID = -1;
60     memset(global_gameflow.m_currentLevelName, 0, sizeof(global_gameflow.m_currentLevelName));
61     memset(global_gameflow.m_currentLevelPath, 0, sizeof(global_gameflow.m_currentLevelPath));
62     memset(global_gameflow.m_secretsTriggerMap, 0, sizeof(global_gameflow.m_secretsTriggerMap));
63     global_gameflow.m_actions.clear();
64 }
65 
66 
Gameflow_Send(int opcode,int operand)67 bool Gameflow_Send(int opcode, int operand)
68 {
69     gameflow_action_t act;
70 
71     act.m_opcode = opcode;
72     act.m_operand = operand;
73     global_gameflow.m_actions.push_back(act);
74 
75     return true;
76 }
77 
78 
Gameflow_ProcessCommands()79 void Gameflow_ProcessCommands()
80 {
81     level_info_t info;
82     for(; !Engine_IsVideoPlayed() && !global_gameflow.m_actions.empty(); global_gameflow.m_actions.pop_back())
83     {
84         gameflow_action_t &it = global_gameflow.m_actions.back();
85         switch(it.m_opcode)
86         {
87             case GF_OP_LEVELCOMPLETE:
88                 if(World_GetPlayer())
89                 {
90                     luaL_dostring(engine_lua, "saved_inventory = getItems(player);");
91                 }
92                 if(Gameflow_SetGameInternal(global_gameflow.m_currentGameID, global_gameflow.m_currentLevelID + 1) && World_GetPlayer())
93                 {
94                     luaL_dostring(engine_lua, "if(saved_inventory ~= nil) then\n"
95                                                   "removeAllItems(player);\n"
96                                                   "for k, v in pairs(saved_inventory) do\n"
97                                                       "addItem(player, k, v);\n"
98                                                   "end;\n"
99                                                   "saved_inventory = nil;\n"
100                                               "end;");
101                 }
102                 break;
103 
104             case GF_OP_SETTRACK:
105                 Audio_StreamPlay(it.m_operand);
106                 break;
107 
108             case GF_OP_STARTFMV:
109                 if(Gameflow_GetFMVPath(&info, it.m_operand))
110                 {
111                     Engine_PlayVideo(info.path);
112                 }
113                 break;
114 
115             case GF_NOENTRY:
116                 continue;
117 
118             default:
119                 //Con_Printf("Unimplemented gameflow opcode: %i", global_gameflow.m_actions[i].m_opcode);
120                 break;
121         };   // end switch(gameflow_manager.Operand)
122     }
123 
124     if(global_gameflow.m_nextGameID >= 0)
125     {
126         Gameflow_SetGameInternal(global_gameflow.m_nextGameID, global_gameflow.m_nextLevelID);
127         global_gameflow.m_nextGameID = -1;
128         global_gameflow.m_nextLevelID = -1;
129     }
130 }
131 
132 
Gameflow_SetMap(const char * filePath,int game_id,int level_id)133 bool Gameflow_SetMap(const char* filePath, int game_id, int level_id)
134 {
135     level_info_t info;
136     if(Gameflow_GetLevelInfo(&info, game_id, level_id))
137     {
138         level_id = (level_id <= info.num_levels) ? (level_id) : (1);
139         if(!Gui_LoadScreenAssignPic(info.pic))
140         {
141             Gui_LoadScreenAssignPic("resource/graphics/legal");
142         }
143     }
144 
145     strncpy(global_gameflow.m_currentLevelPath, filePath, MAX_ENGINE_PATH);
146     global_gameflow.m_currentGameID = game_id;
147     global_gameflow.m_currentLevelID = level_id;
148 
149     return Engine_LoadMap(filePath);
150 }
151 
152 
Gameflow_SetGame(int game_id,int level_id)153 bool Gameflow_SetGame(int game_id, int level_id)
154 {
155     global_gameflow.m_nextGameID = game_id;
156     global_gameflow.m_nextLevelID = level_id;
157     return true;
158 }
159 
160 
Gameflow_GetLevelInfo(level_info_p info,int game_id,int level_id)161 bool Gameflow_GetLevelInfo(level_info_p info, int game_id, int level_id)
162 {
163     int top = lua_gettop(engine_lua);
164 
165     lua_getglobal(engine_lua, "gameflow_params");
166     if(!lua_istable(engine_lua, -1))
167     {
168         lua_settop(engine_lua, top);
169         return false;
170     }
171 
172     lua_rawgeti(engine_lua, -1, game_id);
173     if(!lua_istable(engine_lua, -1))
174     {
175         lua_settop(engine_lua, top);
176         return false;
177     }
178 
179     lua_getfield(engine_lua, -1, "title");
180     strncpy(info->pic, lua_tostring(engine_lua, -1), MAX_ENGINE_PATH);
181     lua_pop(engine_lua, 1);
182 
183     lua_getfield(engine_lua, -1, "numlevels");
184     info->num_levels = lua_tointeger(engine_lua, -1);
185     lua_pop(engine_lua, 1);
186 
187     lua_getfield(engine_lua, -1, "levels");
188     if(!lua_istable(engine_lua, -1))
189     {
190         lua_settop(engine_lua, top);
191         return false;
192     }
193 
194     level_id = (level_id <= info->num_levels) ? (level_id) : (1);
195     lua_rawgeti(engine_lua, -1, level_id);
196     if(!lua_istable(engine_lua, -1))
197     {
198         lua_settop(engine_lua, top);
199         return false;
200     }
201 
202     lua_getfield(engine_lua, -1, "name");
203     strncpy(info->name, lua_tostring(engine_lua, -1), LEVEL_NAME_MAX_LEN);
204     lua_pop(engine_lua, 1);
205 
206     lua_getfield(engine_lua, -1, "filepath");
207     strncpy(info->path, lua_tostring(engine_lua, -1), MAX_ENGINE_PATH);
208     lua_pop(engine_lua, 1);
209 
210     lua_getfield(engine_lua, -1, "picpath");
211     strncpy(info->pic, lua_tostring(engine_lua, -1), MAX_ENGINE_PATH);
212     lua_pop(engine_lua, 1);
213 
214     lua_pop(engine_lua, 1);   // level_id
215     lua_pop(engine_lua, 1);   // levels
216 
217     lua_pop(engine_lua, 1);   // game_id
218     lua_settop(engine_lua, top);
219 
220     return true;
221 }
222 
223 
Gameflow_GetFMVPath(level_info_p info,int fmv_id)224 bool Gameflow_GetFMVPath(level_info_p info, int fmv_id)
225 {
226     int top = lua_gettop(engine_lua);
227 
228     lua_getglobal(engine_lua, "gameflow_params");
229     if(!lua_istable(engine_lua, -1))
230     {
231         lua_settop(engine_lua, top);
232         return false;
233     }
234 
235     lua_rawgeti(engine_lua, -1, global_gameflow.m_currentGameID);
236     if(!lua_istable(engine_lua, -1))
237     {
238         lua_settop(engine_lua, top);
239         return false;
240     }
241 
242     lua_getfield(engine_lua, -1, "fmv");
243     if(!lua_istable(engine_lua, -1))
244     {
245         lua_settop(engine_lua, top);
246         return false;
247     }
248 
249     lua_rawgeti(engine_lua, -1, fmv_id);
250     if(!lua_istable(engine_lua, -1))
251     {
252         lua_settop(engine_lua, top);
253         return false;
254     }
255 
256     lua_getfield(engine_lua, -1, "name");
257     strncpy(info->name, lua_tostring(engine_lua, -1), LEVEL_NAME_MAX_LEN);
258     lua_pop(engine_lua, 1);
259 
260     lua_getfield(engine_lua, -1, "filepath");
261     strncpy(info->path, lua_tostring(engine_lua, -1), MAX_ENGINE_PATH);
262     lua_pop(engine_lua, 1);
263 
264     lua_pop(engine_lua, 1);   // fmv_id
265     lua_pop(engine_lua, 1);   // fmv
266 
267     lua_pop(engine_lua, 1);   // game_id
268     lua_settop(engine_lua, top);
269 
270     return true;
271 }
272 
273 
Gameflow_SetGameInternal(int game_id,int level_id)274 bool Gameflow_SetGameInternal(int game_id, int level_id)
275 {
276     level_info_t info;
277     if(Gameflow_GetLevelInfo(&info, game_id, level_id))
278     {
279         level_id = (level_id <= info.num_levels) ? (level_id) : (1);
280         if(!Gui_LoadScreenAssignPic(info.pic))
281         {
282             Gui_LoadScreenAssignPic("resource/graphics/legal");
283         }
284 
285         global_gameflow.m_currentGameID = game_id;
286         global_gameflow.m_currentLevelID = level_id;
287         strncpy(global_gameflow.m_currentLevelName, info.name, LEVEL_NAME_MAX_LEN);
288         strncpy(global_gameflow.m_currentLevelPath, info.path, MAX_ENGINE_PATH);
289         return Engine_LoadMap(info.path);
290     }
291 
292     return false;
293 }
294 
295 
Gameflow_GetCurrentLevelPathLocal()296 const char *Gameflow_GetCurrentLevelPathLocal()
297 {
298     return global_gameflow.m_currentLevelPath + strlen(Engine_GetBasePath());
299 }
300 
301 
Gameflow_GetCurrentGameID()302 uint8_t Gameflow_GetCurrentGameID()
303 {
304     return global_gameflow.m_currentGameID;
305 }
306 
307 
Gameflow_GetCurrentLevelID()308 uint8_t Gameflow_GetCurrentLevelID()
309 {
310     return global_gameflow.m_currentLevelID;
311 }
312 
313 
Gameflow_ResetSecrets()314 void Gameflow_ResetSecrets()
315 {
316     memset(global_gameflow.m_secretsTriggerMap, 0, GF_MAX_SECRETS * sizeof(*global_gameflow.m_secretsTriggerMap));
317 }
318 
319 
Gameflow_SetSecretStateAtIndex(int index,int value)320 void Gameflow_SetSecretStateAtIndex(int index, int value)
321 {
322     assert((index >= 0) && index <= (GF_MAX_SECRETS));
323     global_gameflow.m_secretsTriggerMap[index] = (char)value; ///@FIXME should not cast.
324 }
325 
326 
Gameflow_GetSecretStateAtIndex(int index)327 int Gameflow_GetSecretStateAtIndex(int index)
328 {
329     assert((index >= 0) && index <= (GF_MAX_SECRETS));
330     return global_gameflow.m_secretsTriggerMap[index];
331 }