bind World
[Tsunagari.git] / src / world.cpp
blob5fb07c9fca0e7620b340ebbeaac0a7b60a336bf2
1 /*********************************
2 ** Tsunagari Tile Engine **
3 ** world.cpp **
4 ** Copyright 2011-2012 OmegaSDG **
5 *********************************/
7 #include <Gosu/Utility.hpp>
9 #include "area-tmx.h"
10 #include "common.h"
11 #include "log.h"
12 #include "python.h"
13 #include "resourcer.h"
14 #include "timeout.h"
15 #include "window.h"
16 #include "world.h"
17 #include "xml.h"
19 #define ASSERT(x) if (!(x)) return false
21 static World* globalWorld = NULL;
23 World* World::instance()
25 return globalWorld;
28 World::World()
29 : player(NULL)
31 globalWorld = this;
32 pythonSetGlobal("World", this);
35 World::~World()
39 bool World::init()
41 if (!processDescriptor()) // Try to load in descriptor.
42 return false;
44 music.reset(new Music());
46 if (!player.init(playerentity))
47 return false;
48 player.setPhase("down");
50 if (onLoadScript.size()) {
51 pythonSetGlobal("Player", (Entity*)&player);
52 Resourcer* rc = Resourcer::instance();
53 rc->runPythonScript(onLoadScript);
56 view = new Viewport(viewport);
57 view->trackEntity(&player);
59 Area* area = getArea(entry.area);
60 if (!area)
61 return false;
62 focusArea(area, entry.coords);
63 return true;
66 void World::buttonDown(const Gosu::Button btn)
68 area->buttonDown(btn);
71 void World::buttonUp(const Gosu::Button btn)
73 area->buttonUp(btn);
76 void World::draw()
78 GameWindow& window = GameWindow::instance();
79 Gosu::Graphics& graphics = window.graphics();
81 drawLetterbox();
82 drawAreaBorders();
83 graphics.pushTransform(getTransform());
84 area->draw();
85 graphics.popTransform();
88 bool World::needsRedraw() const
90 return area->needsRedraw();
93 void World::update(unsigned long dt)
95 area->update(dt);
96 updateTimeouts();
99 Area* World::getArea(const std::string& filename)
101 AreaMap::iterator entry = areas.find(filename);
102 if (entry != areas.end())
103 return entry->second;
105 Area* newArea = new AreaTMX(view, &player, music.get(), filename);
107 if (!newArea->init())
108 newArea = NULL;
109 areas[filename] = newArea;
110 return newArea;
113 Area* World::getFocusedArea()
115 return area;
118 void World::focusArea(Area* area, int x, int y, double z)
120 focusArea(area, vicoord(x, y, z));
123 void World::focusArea(Area* area, vicoord playerPos)
125 // Log::info("World", area->getDescriptor() + ": focused");
126 this->area = area;
127 player.setArea(area);
128 player.setTileCoords(playerPos);
129 view->setArea(area);
130 area->focus();
133 std::string World::getAreaLoadScript()
135 return onAreaLoadScript;
138 bool World::processDescriptor()
140 XMLRef doc;
141 XMLNode root;
143 Resourcer* rc = Resourcer::instance();
144 ASSERT(doc = rc->getXMLDoc("world.conf", "world.dtd"));
145 ASSERT(root = doc->root()); // <world>
147 for (XMLNode child = root.childrenNode(); child; child = child.next()) {
148 if (child.is("info")) {
149 ASSERT(processInfo(child));
151 else if (child.is("init")) {
152 ASSERT(processInit(child));
154 else if (child.is("script")) {
155 ASSERT(processScript(child));
157 else if (child.is("input")) {
158 ASSERT(processInput(child));
162 return true;
165 bool World::processInfo(XMLNode node)
167 for (node = node.childrenNode(); node; node = node.next()) {
168 if (node.is("name")) {
169 name = node.content();
170 GameWindow::instance().setCaption(Gosu::widen(name));
171 } else if (node.is("author")) {
172 author = node.content();
173 } else if (node.is("version")) {
174 version = atof(node.content().c_str());
177 return true;
180 bool World::processInit(XMLNode node)
182 for (node = node.childrenNode(); node; node = node.next()) {
183 if (node.is("area")) {
184 entry.area = node.content();
186 else if (node.is("player")) {
187 playerentity = node.content();
189 else if (node.is("mode")) {
190 std::string str = node.content();
191 if (str == "turn")
192 conf.moveMode = TURN;
193 else if (str == "tile")
194 conf.moveMode = TILE;
195 else if (str == "notile")
196 conf.moveMode = NOTILE;
198 else if (node.is("coords")) {
199 if (!node.intAttr("x", &entry.coords.x) ||
200 !node.intAttr("y", &entry.coords.y) ||
201 !node.doubleAttr("z", &entry.coords.z))
202 return false;
204 else if (node.is("viewport")) {
205 if (!node.intAttr("width", &viewport.x) ||
206 !node.intAttr("height", &viewport.y))
207 return false;
210 return true;
213 bool World::processScript(XMLNode node)
215 for (node = node.childrenNode(); node; node = node.next()) {
216 if (node.is("on_init")) {
217 std::string filename = node.content();
218 if (rc->resourceExists(filename)) {
219 onLoadScript = filename;
221 else {
222 Log::err("world.conf",
223 std::string("script not found: ") + filename);
224 return false;
226 } else if (node.is("on_area_init")) {
227 std::string filename = node.content();
228 if (rc->resourceExists(filename)) {
229 onAreaLoadScript = filename;
231 else {
232 Log::err("world.conf",
233 std::string("script not found: ") + filename);
234 return false;
238 return true;
241 bool World::processInput(XMLNode node)
243 for (node = node.childrenNode(); node; node = node.next()) {
244 if (node.is("persist")) {
245 if (!node.intAttr("init", &conf.persistInit) ||
246 !node.intAttr("cons", &conf.persistCons))
247 return false;
250 return true;
253 void World::drawLetterbox()
255 rvec2 sz = view->getPhysRes();
256 rvec2 lb = rvec2(0.0, 0.0);
257 lb -= view->getLetterboxOffset();
258 Gosu::Color black = Gosu::Color::BLACK;
260 drawRect(0, sz.x, 0, lb.y, black, 1000);
261 drawRect(0, sz.x, sz.y - lb.y, sz.y, black, 1000);
262 drawRect(0, lb.x, 0, sz.y, black, 1000);
263 drawRect(sz.x - lb.x, sz.x, 0, sz.y, black, 1000);
266 void World::drawAreaBorders()
268 Gosu::Color black = Gosu::Color::BLACK;
269 rvec2 sz = view->getPhysRes();
270 rvec2 scale = view->getScale();
271 rvec2 virtScroll = view->getMapOffset();
272 rvec2 padding = view->getLetterboxOffset();
274 rvec2 physScroll = virtScroll;
275 physScroll *= scale;
276 physScroll += padding;
277 physScroll *= -1;
279 bool loopX = area->loopsInX();
280 bool loopY = area->loopsInY();
282 if (!loopX && physScroll.x > 0) {
283 // Boxes on left-right.
284 drawRect(0, physScroll.x, 0, sz.y, black, 500);
285 drawRect(sz.x - physScroll.x, sz.x, 0, sz.y, black, 500);
287 if (!loopY && physScroll.y > 0) {
288 // Boxes on top-bottom.
289 drawRect(0, sz.x, 0, physScroll.y, black, 500);
290 drawRect(0, sz.x, sz.y - physScroll.y, sz.y, black, 500);
294 void World::drawRect(double x1, double x2, double y1, double y2,
295 Gosu::Color c, double z)
297 GameWindow& window = GameWindow::instance();
298 window.graphics().drawQuad(
299 x1, y1, c,
300 x2, y1, c,
301 x2, y2, c,
302 x1, y2, c,
307 Gosu::Transform World::getTransform()
309 rvec2 scale = view->getScale();
310 rvec2 scroll = view->getMapOffset();
311 rvec2 padding = view->getLetterboxOffset();
312 Gosu::Transform t = { {
313 scale.x, 0, 0, 0,
314 0, scale.y, 0, 0,
315 0, 0, 1, 0,
316 scale.x * -scroll.x - padding.x,
317 scale.y * -scroll.y - padding.y, 0, 1
318 } };
319 return t;
322 void exportWorld()
324 using namespace boost::python;
326 class_<World> ("World", no_init)
327 .def("area", &World::getArea,
328 return_value_policy<reference_existing_object>())
329 .def("focus",
330 static_cast<void (World::*) (Area*,int,int,double)>
331 (&World::focusArea))