multiple archives, call init.py from base.zip
[Tsunagari.git] / src / world.cpp
blobeb7c17f345e61d4b349d21c6f80d46c2b9303a42
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 "log.h"
11 #include "python.h"
12 #include "resourcer.h"
13 #include "timeout.h"
14 #include "window.h"
15 #include "world.h"
16 #include "xml.h"
18 #define ASSERT(x) if (!(x)) return false
20 static World* globalWorld = NULL;
22 World* World::instance()
24 return globalWorld;
27 World::World()
28 : music(new Music()), player(NULL)
30 globalWorld = this;
31 pythonSetGlobal("World", this);
34 World::~World()
38 bool World::init()
40 if (!processDescriptor()) // Try to load in descriptor.
41 return false;
43 if (!player.init(playerentity))
44 return false;
45 player.setPhase("down");
47 if (onLoadScript.size()) {
48 pythonSetGlobal("Player", (Entity*)&player);
49 Resourcer* rc = Resourcer::instance();
50 rc->runPythonScript(onLoadScript);
53 view.reset(new Viewport(viewport));
54 view->trackEntity(&player);
56 Area* area = getArea(entry.area);
57 if (!area)
58 return false;
59 focusArea(area, entry.coords);
60 return true;
63 void World::buttonDown(const Gosu::Button btn)
65 area->buttonDown(btn);
68 void World::buttonUp(const Gosu::Button btn)
70 area->buttonUp(btn);
73 void World::draw()
75 GameWindow& window = GameWindow::instance();
76 Gosu::Graphics& graphics = window.graphics();
78 drawLetterbox();
79 drawAreaBorders();
80 graphics.pushTransform(getTransform());
81 area->draw();
82 graphics.popTransform();
85 bool World::needsRedraw() const
87 return area->needsRedraw();
90 void World::update(unsigned long dt)
92 area->update(dt);
93 updateTimeouts();
96 Area* World::getArea(const std::string& filename)
98 AreaMap::iterator entry = areas.find(filename);
99 if (entry != areas.end())
100 return entry->second;
102 Area* newArea = new AreaTMX(view.get(), &player, music.get(), filename);
104 if (!newArea->init())
105 newArea = NULL;
106 areas[filename] = newArea;
107 return newArea;
110 Area* World::getFocusedArea()
112 return area;
115 void World::focusArea(Area* area, int x, int y, double z)
117 focusArea(area, vicoord(x, y, z));
120 void World::focusArea(Area* area, vicoord playerPos)
122 // Log::info("World", area->getDescriptor() + ": focused");
123 this->area = area;
124 player.setArea(area);
125 player.setTileCoords(playerPos);
126 view->setArea(area);
127 area->focus();
130 std::string World::getAreaLoadScript()
132 return onAreaLoadScript;
135 bool World::processDescriptor()
137 XMLRef doc;
138 XMLNode root;
140 Resourcer* rc = Resourcer::instance();
141 ASSERT(doc = rc->getXMLDoc("world.conf", "world.dtd"));
142 ASSERT(root = doc->root()); // <world>
144 for (XMLNode child = root.childrenNode(); child; child = child.next()) {
145 if (child.is("info")) {
146 ASSERT(processInfo(child));
148 else if (child.is("init")) {
149 ASSERT(processInit(child));
151 else if (child.is("script")) {
152 ASSERT(processScript(child));
154 else if (child.is("input")) {
155 ASSERT(processInput(child));
159 if (conf.moveMode == TURN &&
160 (conf.persistInit == 0 || conf.persistCons == 0)) {
161 Log::fatal("world.conf", "\"input->persist\" option required in TURN mode.");
162 return false;
165 return true;
168 bool World::processInfo(XMLNode node)
170 for (node = node.childrenNode(); node; node = node.next()) {
171 if (node.is("name")) {
172 name = node.content();
173 GameWindow::instance().setCaption(Gosu::widen(name));
174 } else if (node.is("author")) {
175 author = node.content();
176 } else if (node.is("version")) {
177 version = atof(node.content().c_str());
180 return true;
183 bool World::processInit(XMLNode node)
185 for (node = node.childrenNode(); node; node = node.next()) {
186 if (node.is("area")) {
187 entry.area = node.content();
189 else if (node.is("player")) {
190 playerentity = node.content();
192 else if (node.is("mode")) {
193 std::string str = node.content();
194 if (str == "turn")
195 conf.moveMode = TURN;
196 else if (str == "tile")
197 conf.moveMode = TILE;
198 else if (str == "notile")
199 conf.moveMode = NOTILE;
201 else if (node.is("coords")) {
202 if (!node.intAttr("x", &entry.coords.x) ||
203 !node.intAttr("y", &entry.coords.y) ||
204 !node.doubleAttr("z", &entry.coords.z))
205 return false;
207 else if (node.is("viewport")) {
208 if (!node.intAttr("width", &viewport.x) ||
209 !node.intAttr("height", &viewport.y))
210 return false;
213 return true;
216 bool World::processScript(XMLNode node)
218 for (node = node.childrenNode(); node; node = node.next()) {
219 if (node.is("on_init")) {
220 std::string filename = node.content();
221 if (rc->resourceExists(filename)) {
222 onLoadScript = filename;
224 else {
225 Log::err("world.conf",
226 std::string("script not found: ") + filename);
227 return false;
229 } else if (node.is("on_area_init")) {
230 std::string filename = node.content();
231 if (rc->resourceExists(filename)) {
232 onAreaLoadScript = filename;
234 else {
235 Log::err("world.conf",
236 std::string("script not found: ") + filename);
237 return false;
241 return true;
244 bool World::processInput(XMLNode node)
246 for (node = node.childrenNode(); node; node = node.next()) {
247 if (node.is("persist")) {
248 if (!node.intAttr("init", &conf.persistInit) ||
249 !node.intAttr("cons", &conf.persistCons))
250 return false;
253 return true;
256 void World::drawLetterbox()
258 rvec2 sz = view->getPhysRes();
259 rvec2 lb = rvec2(0.0, 0.0);
260 lb -= view->getLetterboxOffset();
261 Gosu::Color black = Gosu::Color::BLACK;
263 drawRect(0, sz.x, 0, lb.y, black, 1000);
264 drawRect(0, sz.x, sz.y - lb.y, sz.y, black, 1000);
265 drawRect(0, lb.x, 0, sz.y, black, 1000);
266 drawRect(sz.x - lb.x, sz.x, 0, sz.y, black, 1000);
269 void World::drawAreaBorders()
271 Gosu::Color black = Gosu::Color::BLACK;
272 rvec2 sz = view->getPhysRes();
273 rvec2 scale = view->getScale();
274 rvec2 virtScroll = view->getMapOffset();
275 rvec2 padding = view->getLetterboxOffset();
277 rvec2 physScroll = virtScroll;
278 physScroll *= scale;
279 physScroll += padding;
280 physScroll *= -1;
282 bool loopX = area->loopsInX();
283 bool loopY = area->loopsInY();
285 if (!loopX && physScroll.x > 0) {
286 // Boxes on left-right.
287 drawRect(0, physScroll.x, 0, sz.y, black, 500);
288 drawRect(sz.x - physScroll.x, sz.x, 0, sz.y, black, 500);
290 if (!loopY && physScroll.y > 0) {
291 // Boxes on top-bottom.
292 drawRect(0, sz.x, 0, physScroll.y, black, 500);
293 drawRect(0, sz.x, sz.y - physScroll.y, sz.y, black, 500);
297 void World::drawRect(double x1, double x2, double y1, double y2,
298 Gosu::Color c, double z)
300 GameWindow& window = GameWindow::instance();
301 window.graphics().drawQuad(
302 x1, y1, c,
303 x2, y1, c,
304 x2, y2, c,
305 x1, y2, c,
310 Gosu::Transform World::getTransform()
312 rvec2 scale = view->getScale();
313 rvec2 scroll = view->getMapOffset();
314 rvec2 padding = view->getLetterboxOffset();
315 Gosu::Transform t = { {
316 scale.x, 0, 0, 0,
317 0, scale.y, 0, 0,
318 0, 0, 1, 0,
319 scale.x * -scroll.x - padding.x,
320 scale.y * -scroll.y - padding.y, 0, 1
321 } };
322 return t;
325 void exportWorld()
327 using namespace boost::python;
329 class_<World> ("World", no_init)
330 .def("area", &World::getArea,
331 return_value_policy<reference_existing_object>())
332 .def("focus",
333 static_cast<void (World::*) (Area*,int,int,double)>
334 (&World::focusArea))