1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2008 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 // -----------------------------------------------------------------------
26 
27 #include "script_machine/script_world.h"
28 
29 #include <iostream>
30 #include <typeinfo>
31 #include <sstream>
32 #include <string>
33 #include <boost/filesystem/operations.hpp>
34 #include <boost/filesystem/path.hpp>
35 
36 #include "modules/module_sel.h"
37 #include "script_machine/luabind_event_system.h"
38 #include "script_machine/luabind_graphics_object.h"
39 #include "script_machine/luabind_graphics_system.h"
40 #include "script_machine/luabind_machine.h"
41 #include "script_machine/luabind_system.h"
42 #include "script_machine/luabind_utility.h"
43 #include "script_machine/script_machine.h"
44 #include "systems/base/system.h"
45 
46 extern "C" {
47 #include "lua.h"
48 #include "lualib.h"
49 #include "lauxlib.h"
50 }
51 
52 #include <luabind/luabind.hpp>
53 
54 using namespace std;
55 using namespace luabind;
56 namespace fs = boost::filesystem;
57 
ScriptWorld()58 ScriptWorld::ScriptWorld() {
59   L = lua_open();
60   luaopen_base(L);
61   luaopen_string(L);
62   luaopen_table(L);
63   InitializeLuabind(L);
64 
65   luabind::globals(L)["World"] = this;
66 }
67 
~ScriptWorld()68 ScriptWorld::~ScriptWorld() { lua_close(L); }
69 
LoadToplevelFile(const std::string & lua_file)70 void ScriptWorld::LoadToplevelFile(const std::string& lua_file) {
71   script_dir_ = fs::path(lua_file).branch_path();
72 
73   if (luaL_dofile(L, lua_file.c_str())) {
74     ostringstream oss;
75     oss << "Error while running script: " << lua_file << " ("
76         << lua_tostring(L, -1) << ")";
77     throw std::runtime_error(oss.str());
78   }
79 }
80 
Import(const std::string & file_name)81 void ScriptWorld::Import(const std::string& file_name) {
82   fs::path script_path(script_dir_ / file_name);
83 
84   if (!fs::exists(script_path)) {
85     ostringstream oss;
86     oss << "Could not read script file: " << script_path;
87     throw std::runtime_error(oss.str());
88   }
89 
90   if (luaL_dofile(L, script_path.string().c_str())) {
91     ostringstream oss;
92     oss << "Error while running script: " << script_path << " ("
93         << lua_tostring(L, -1) << ")";
94     throw std::runtime_error(oss.str());
95   }
96 }
97 
Regname() const98 std::string ScriptWorld::Regname() const {
99   ScriptMachine* machine =
100       luabind::object_cast<ScriptMachine*>(luabind::globals(L)["Machine"]);
101   if (machine) {
102     return machine->system().Regname();
103   } else {
104     throw std::logic_error("No machine!?");
105   }
106 }
107 
SetDecisionList(luabind::object table)108 void ScriptWorld::SetDecisionList(luabind::object table) {
109   decisions_.clear();
110   for (luabind::iterator itr(table), end; itr != end; ++itr) {
111     boost::optional<std::string> v = object_cast_nothrow<std::string>(*itr);
112 
113     if (v) {
114       decisions_.push_back(*v);
115     }
116   }
117 
118   ScriptMachine* machine =
119       luabind::object_cast<ScriptMachine*>(luabind::globals(L)["Machine"]);
120   if (machine) {
121     machine->SetDecisionList(decisions_);
122   }
123 }
124 
Error(const std::string & error_message)125 void ScriptWorld::Error(const std::string& error_message) {
126   ScriptMachine* machine =
127       luabind::object_cast<ScriptMachine*>(luabind::globals(L)["Machine"]);
128   if (machine)
129     machine->Halt();
130 
131   cerr << "ERROR: " << error_message << endl;
132 }
133 
AddHandler(int scene,int lineNo,luabind::object handler)134 void ScriptWorld::AddHandler(int scene, int lineNo, luabind::object handler) {
135   ScriptMachine* machine =
136       luabind::object_cast<ScriptMachine*>(luabind::globals(L)["Machine"]);
137   if (machine) {
138     machine->AddLineAction(
139         scene, lineNo, boost::bind(&ScriptWorld::RunHandler, handler));
140   }
141 }
142 
SetDecisionHandler(luabind::object obj)143 void ScriptWorld::SetDecisionHandler(luabind::object obj) {
144   luabind::globals(L)["DecisionHandler"] = obj;
145 }
146 
MakeDecision(const std::vector<std::string> & decisions)147 std::string ScriptWorld::MakeDecision(
148     const std::vector<std::string>& decisions) {
149   luabind::object handler = luabind::globals(L)["DecisionHandler"];
150 
151   if (type(handler) == LUA_TFUNCTION) {
152     object table = newtable(L);
153     for (int i = 0; i < decisions.size(); ++i) {
154       settable(table, i, decisions[i]);
155     }
156 
157     luabind::object ret = handler(table);
158     if (type(ret) == LUA_TSTRING) {
159       return object_cast<std::string>(ret);
160     }
161   }
162 
163   return "";
164 }
165 
InitializeMachine(ScriptMachine & machine)166 void ScriptWorld::InitializeMachine(ScriptMachine& machine) {
167   luabind::globals(L)["Machine"] = &machine;
168   luabind::globals(L)["System"] = &(machine.system());
169 }
170 
171 // static
InitializeLuabind(lua_State * L)172 void ScriptWorld::InitializeLuabind(lua_State* L) {
173   using namespace luabind;
174 
175   open(L);
176   module(L)[
177     // High level interface
178     class_<ScriptWorld>("World")
179         .def("import", &ScriptWorld::Import)
180         .def("regname", &ScriptWorld::Regname)
181         .def("setDecisionList", &ScriptWorld::SetDecisionList)
182         .def("error", &ScriptWorld::Error)
183         .def("addHandler", &ScriptWorld::AddHandler)
184         .def("setDecisionHandler", &ScriptWorld::SetDecisionHandler),
185     register_utility(),
186     register_machine(),
187     register_system(),
188     register_event_system(),
189     register_graphics_system(),
190     register_graphics_object()
191   ];
192 }
193 
194 // static
RunHandler(luabind::object handler)195 void ScriptWorld::RunHandler(luabind::object handler) {
196   try {
197     luabind::call_function<void>(handler);
198   }
199   catch (const luabind::error& e) {
200     lua_State* state = e.state();
201     std::cerr << lua_tostring(state, -1) << endl;
202   }
203 }
204