1 /*********************************
2 ** Tsunagari Tile Engine **
4 ** Copyright 2011-2012 OmegaSDG **
5 *********************************/
7 #include <Gosu/Utility.hpp>
12 #include "resourcer.h"
18 #define ASSERT(x) if (!(x)) return false
20 static World
* globalWorld
= NULL
;
22 World
* World::instance()
28 : music(new Music()), player(NULL
)
31 pythonSetGlobal("World", this);
40 if (!processDescriptor()) // Try to load in descriptor.
43 if (!player
.init(playerentity
))
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
);
59 focusArea(area
, entry
.coords
);
63 void World::buttonDown(const Gosu::Button btn
)
65 area
->buttonDown(btn
);
68 void World::buttonUp(const Gosu::Button btn
)
75 GameWindow
& window
= GameWindow::instance();
76 Gosu::Graphics
& graphics
= window
.graphics();
80 graphics
.pushTransform(getTransform());
82 graphics
.popTransform();
85 bool World::needsRedraw() const
87 return area
->needsRedraw();
90 void World::update(unsigned long dt
)
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())
106 areas
[filename
] = newArea
;
110 Area
* World::getFocusedArea()
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");
124 player
.setArea(area
);
125 player
.setTileCoords(playerPos
);
130 std::string
World::getAreaLoadScript()
132 return onAreaLoadScript
;
135 bool World::processDescriptor()
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.");
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());
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();
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
))
207 else if (node
.is("viewport")) {
208 if (!node
.intAttr("width", &viewport
.x
) ||
209 !node
.intAttr("height", &viewport
.y
))
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
;
225 Log::err("world.conf",
226 std::string("script not found: ") + filename
);
229 } else if (node
.is("on_area_init")) {
230 std::string filename
= node
.content();
231 if (rc
->resourceExists(filename
)) {
232 onAreaLoadScript
= filename
;
235 Log::err("world.conf",
236 std::string("script not found: ") + filename
);
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
))
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
;
279 physScroll
+= padding
;
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(
310 Gosu::Transform
World::getTransform()
312 rvec2 scale
= view
->getScale();
313 rvec2 scroll
= view
->getMapOffset();
314 rvec2 padding
= view
->getLetterboxOffset();
315 Gosu::Transform t
= { {
319 scale
.x
* -scroll
.x
- padding
.x
,
320 scale
.y
* -scroll
.y
- padding
.y
, 0, 1
327 using namespace boost::python
;
329 class_
<World
> ("World", no_init
)
330 .def("area", &World::getArea
,
331 return_value_policy
<reference_existing_object
>())
333 static_cast<void (World::*) (Area
*,int,int,double)>