Area.tiles => Area.tile
[Tsunagari.git] / src / area.cpp
blobc53ef5acc391577aaa43401af4f9149dffd939b7
1 /*********************************
2 ** Tsunagari Tile Engine **
3 ** area.cpp **
4 ** Copyright 2011-2012 OmegaSDG **
5 *********************************/
7 #include <algorithm>
8 #include <math.h>
9 #include <vector>
11 #include <boost/foreach.hpp>
12 #include <boost/shared_ptr.hpp>
13 #include <Gosu/Graphics.hpp>
14 #include <Gosu/Image.hpp>
15 #include <Gosu/Math.hpp>
16 #include <Gosu/Timing.hpp>
18 #include "area.h"
19 #include "common.h"
20 #include "entity.h"
21 #include "log.h"
22 #include "python.h"
23 #include "resourcer.h"
24 #include "tile.h"
25 #include "window.h"
26 #include "world.h"
28 #define ASSERT(x) if (!(x)) return false
30 /* NOTE: In the TMX map format used by Tiled, tileset tiles start counting
31 their Y-positions from 0, while layer tiles start counting from 1. I
32 can't imagine why the author did this, but we have to take it into
33 account.
36 template<class T>
37 static T wrap(T min, T value, T max)
39 while (value < min)
40 value += max;
41 return value % max;
44 Area::Area(Viewport* view,
45 Player* player,
46 Music* music,
47 const std::string& descriptor)
48 : view(view),
49 player(player),
50 music(music),
51 colorOverlay(0, 0, 0, 0),
52 dim(0, 0, 0),
53 tileDim(0, 0),
54 loopX(false), loopY(false),
55 beenFocused(false),
56 redraw(true),
57 descriptor(descriptor)
61 Area::~Area()
65 bool Area::init()
67 // Abstract method.
68 return false;
71 void Area::focus()
73 if (!beenFocused) {
74 beenFocused = true;
75 runOnLoads();
78 music->setIntro(musicIntro);
79 music->setLoop(musicLoop);
81 if (onFocusScripts.size()) {
82 BOOST_FOREACH(const std::string& script, onFocusScripts) {
83 pythonSetGlobal("Area", this);
84 Resourcer* rc = Resourcer::instance();
85 rc->runPythonScript(script);
90 void Area::buttonDown(const Gosu::Button btn)
92 if (btn == Gosu::kbRight)
93 player->startMovement(ivec2(1, 0));
94 else if (btn == Gosu::kbLeft)
95 player->startMovement(ivec2(-1, 0));
96 else if (btn == Gosu::kbUp)
97 player->startMovement(ivec2(0, -1));
98 else if (btn == Gosu::kbDown)
99 player->startMovement(ivec2(0, 1));
100 else if (btn == Gosu::kbSpace)
101 player->useTile();
104 void Area::buttonUp(const Gosu::Button btn)
106 if (btn == Gosu::kbRight)
107 player->stopMovement(ivec2(1, 0));
108 else if (btn == Gosu::kbLeft)
109 player->stopMovement(ivec2(-1, 0));
110 else if (btn == Gosu::kbUp)
111 player->stopMovement(ivec2(0, -1));
112 else if (btn == Gosu::kbDown)
113 player->stopMovement(ivec2(0, 1));
116 void Area::draw()
118 updateTileAnimations();
119 drawTiles();
120 drawEntities();
121 drawColorOverlay();
122 redraw = false;
125 bool Area::needsRedraw() const
127 if (redraw)
128 return true;
129 if (player->needsRedraw())
130 return true;
132 // Do any on-screen tile types need to update their animations?
133 const icube tiles = visibleTiles();
134 for (int z = tiles.z1; z < tiles.z2; z++) {
135 for (int y = tiles.y1; y < tiles.y2; y++) {
136 for (int x = tiles.x1; x < tiles.x2; x++) {
137 const Tile& tile = getTile(x, y, z);
138 const TileType* type = tile.getType();
139 if (type && type->needsRedraw())
140 return true;
144 return false;
147 void Area::requestRedraw()
149 redraw = true;
152 void Area::update(unsigned long dt)
154 pythonSetGlobal("Area", this);
155 player->update(dt);
157 if (onUpdateScripts.size()) {
158 BOOST_FOREACH(const std::string& script, onUpdateScripts) {
159 pythonSetGlobal("Area", this);
160 Resourcer* rc = Resourcer::instance();
161 rc->runPythonScript(script);
165 view->update(dt);
166 music->update();
169 AreaPtr Area::reset()
171 World* world = World::instance();
172 AreaPtr newSelf = world->getArea(descriptor, GETAREA_ALWAYS_CREATE);
173 if (world->getFocusedArea().get() == this) {
174 vicoord c = player->getTileCoords_vi();
175 world->focusArea(newSelf, c);
177 return newSelf;
180 void Area::setColorOverlay(int r, int g, int b, int a)
182 using namespace Gosu;
184 if (0 <= r && r < 256 &&
185 0 <= g && g < 256 &&
186 0 <= b && b < 256 &&
187 0 <= a && a < 256) {
188 Color::Channel ac = (Color::Channel)a;
189 Color::Channel rc = (Color::Channel)r;
190 Color::Channel gc = (Color::Channel)g;
191 Color::Channel bc = (Color::Channel)b;
192 colorOverlay = Color(ac, rc, gc, bc);
193 redraw = true;
195 else {
196 PyErr_Format(PyExc_ValueError,
197 "Area::color_overlay() arguments must be "
198 "between 0 and 255");
204 const Tile& Area::getTile(int x, int y, int z) const
206 if (loopX)
207 x = wrap(0, x, dim.x);
208 if (loopY)
209 y = wrap(0, y, dim.y);
210 return map[z][y][x];
213 const Tile& Area::getTile(int x, int y, double z) const
215 return getTile(x, y, depthIndex(z));
218 const Tile& Area::getTile(icoord phys) const
220 return getTile(phys.x, phys.y, phys.z);
223 const Tile& Area::getTile(vicoord virt) const
225 return getTile(virt2phys(virt));
228 Tile& Area::getTile(int x, int y, int z)
230 if (loopX)
231 x = wrap(0, x, dim.x);
232 if (loopY)
233 y = wrap(0, y, dim.y);
234 return map[z][y][x];
237 Tile& Area::getTile(int x, int y, double z)
239 return getTile(x, y, depthIndex(z));
242 Tile& Area::getTile(icoord phys)
244 return getTile(phys.x, phys.y, phys.z);
247 Tile& Area::getTile(vicoord virt)
249 return getTile(virt2phys(virt));
252 TileType& Area::getTileType(int idx)
254 return tileTypes[idx];
259 ivec3 Area::getDimensions() const
261 return dim;
264 ivec2 Area::getTileDimensions() const
266 return tileDim;
269 icube Area::visibleTileBounds() const
271 rvec2 screen = view->getVirtRes();
272 rvec2 off = view->getMapOffset();
274 int x1 = (int)floor(off.x / tileDim.x);
275 int y1 = (int)floor(off.y / tileDim.y);
276 int x2 = (int)ceil((screen.x + off.x) / tileDim.x);
277 int y2 = (int)ceil((screen.y + off.y) / tileDim.y);
279 return icube(x1, y1, 0, x2, y2, dim.z);
282 icube Area::visibleTiles() const
284 icube cube = visibleTileBounds();
285 if (!loopX) {
286 cube.x1 = std::max(cube.x1, 0);
287 cube.x2 = std::min(cube.x2, dim.x);
289 if (!loopY) {
290 cube.y1 = std::max(cube.y1, 0);
291 cube.y2 = std::min(cube.y2, dim.y);
293 return cube;
296 bool Area::inBounds(int x, int y, int z) const
298 return ((loopX || (0 <= x && x < dim.x)) &&
299 (loopY || (0 <= y && y < dim.y)) &&
300 0 <= z && z < dim.z);
303 bool Area::inBounds(int x, int y, double z) const
305 return inBounds(x, y, depthIndex(z));
308 bool Area::inBounds(icoord phys) const
310 return inBounds(phys.x, phys.y, phys.z);
313 bool Area::inBounds(vicoord virt) const
315 return inBounds(virt2phys(virt));
320 bool Area::loopsInX() const
322 return loopX;
325 bool Area::loopsInY() const
327 return loopY;
330 const std::string Area::getDescriptor() const
332 return descriptor;
337 vicoord Area::phys2virt_vi(icoord phys) const
339 return vicoord(phys.x, phys.y, indexDepth(phys.z));
342 rcoord Area::phys2virt_r(icoord phys) const
344 return rcoord(
345 (double)phys.x * tileDim.x,
346 (double)phys.y * tileDim.y,
347 indexDepth(phys.z)
351 icoord Area::virt2phys(vicoord virt) const
353 return icoord(virt.x, virt.y, depthIndex(virt.z));
356 icoord Area::virt2phys(rcoord virt) const
358 return icoord(
359 (int)(virt.x / tileDim.x),
360 (int)(virt.y / tileDim.y),
361 depthIndex(virt.z)
365 rcoord Area::virt2virt(vicoord virt) const
367 return rcoord(
368 (double)virt.x * tileDim.x,
369 (double)virt.y * tileDim.y,
370 virt.z
374 vicoord Area::virt2virt(rcoord virt) const
376 return vicoord(
377 (int)virt.x / tileDim.x,
378 (int)virt.y / tileDim.y,
379 virt.z
384 int Area::depthIndex(double depth) const
386 return depth2idx.find(depth)->second;
389 double Area::indexDepth(int idx) const
391 return idx2depth[idx];
396 void Area::runOnLoads()
398 Resourcer* rc = Resourcer::instance();
399 World* world = World::instance();
400 std::string onAreaLoadScript = world->getAreaLoadScript();
401 if (onAreaLoadScript.size()) {
402 pythonSetGlobal("Area", this);
403 rc->runPythonScript(onAreaLoadScript);
405 BOOST_FOREACH(const std::string& script, onLoadScripts) {
406 pythonSetGlobal("Area", this);
407 rc->runPythonScript(script);
411 void Area::updateTileAnimations()
413 const int millis = GameWindow::instance().time();
414 BOOST_FOREACH(TileType& type, tileTypes)
415 type.anim.updateFrame(millis);
418 void Area::drawTiles() const
420 const icube tiles = visibleTiles();
421 for (int z = tiles.z1; z < tiles.z2; z++) {
422 double depth = idx2depth[z];
423 for (int y = tiles.y1; y < tiles.y2; y++) {
424 for (int x = tiles.x1; x < tiles.x2; x++) {
425 const Tile& tile = getTile(x, y, z);
426 drawTile(tile, x, y, depth);
432 void Area::drawTile(const Tile& tile, int x, int y, double depth) const
434 const TileType* type = (TileType*)tile.parent;
435 if (type) {
436 const Gosu::Image* img = type->anim.frame();
437 if (img)
438 img->draw((double)x*img->width(),
439 (double)y*img->height(), depth);
443 void Area::drawEntities()
445 player->draw();
448 void Area::drawColorOverlay()
450 if (colorOverlay.alpha() != 0) {
451 GameWindow& window = GameWindow::instance();
452 Gosu::Color c = colorOverlay;
453 int x = window.width();
454 int y = window.height();
455 window.graphics().drawQuad(
456 0, 0, c,
457 x, 0, c,
458 x, y, c,
459 0, y, c,
465 boost::python::tuple Area::pyGetDimensions()
467 using namespace boost::python;
469 list zs;
470 BOOST_FOREACH(double dep, idx2depth)
471 zs.append(dep);
472 return make_tuple(dim.x, dim.y, zs);
475 void exportArea()
477 using namespace boost::python;
479 class_<Area>("Area", no_init)
480 .add_property("descriptor", &Area::getDescriptor)
481 .add_property("dimensions", &Area::pyGetDimensions)
482 .def("redraw", &Area::requestRedraw)
483 .def("tile",
484 static_cast<Tile& (Area::*) (int, int, double)>
485 (&Area::getTile),
486 return_value_policy<reference_existing_object>())
487 .def("in_bounds",
488 static_cast<bool (Area::*) (int, int, double) const>
489 (&Area::inBounds))
490 .def("get_tile_type", &Area::getTileType,
491 return_value_policy<reference_existing_object>())
492 .def("reset", &Area::reset)
493 .def("color_overlay", &Area::setColorOverlay)