diff options
author | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2008-10-09 19:42:13 +0000 |
---|---|---|
committer | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2008-10-09 19:42:13 +0000 |
commit | 3fe1772b1e00344365e3cf8204225be19925b9e5 (patch) | |
tree | 0f66dddac8e14787096c01368611efa53f453134 | |
parent | 8cc0423b0c0aaa5dd9e91f673a691e5e634988c1 (diff) | |
download | mana-3fe1772b1e00344365e3cf8204225be19925b9e5.tar.gz mana-3fe1772b1e00344365e3cf8204225be19925b9e5.tar.bz2 mana-3fe1772b1e00344365e3cf8204225be19925b9e5.tar.xz mana-3fe1772b1e00344365e3cf8204225be19925b9e5.zip |
Merged the movement branch into trunk
I consider this the only way forward. In my tests this code isn't actually
doing worse than what was there before. Of course some cases are a bit broken,
and I'm open to any kind of feedback so that we can fix those issues.
-rw-r--r-- | ChangeLog | 66 | ||||
-rw-r--r-- | src/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/animatedsprite.cpp | 26 | ||||
-rw-r--r-- | src/being.cpp | 230 | ||||
-rw-r--r-- | src/being.h | 110 | ||||
-rw-r--r-- | src/beingmanager.cpp | 15 | ||||
-rw-r--r-- | src/game.cpp | 9 | ||||
-rw-r--r-- | src/gui/minimap.cpp | 7 | ||||
-rw-r--r-- | src/gui/playerbox.cpp | 7 | ||||
-rw-r--r-- | src/gui/updatewindow.cpp | 4 | ||||
-rw-r--r-- | src/gui/viewport.cpp | 91 | ||||
-rw-r--r-- | src/gui/viewport.h | 11 | ||||
-rw-r--r-- | src/localplayer.cpp | 63 | ||||
-rw-r--r-- | src/map.cpp | 35 | ||||
-rw-r--r-- | src/map.h | 12 | ||||
-rw-r--r-- | src/monster.cpp | 1 | ||||
-rw-r--r-- | src/net/beinghandler.cpp | 370 | ||||
-rw-r--r-- | src/net/playerhandler.cpp | 7 | ||||
-rw-r--r-- | src/npc.cpp | 10 | ||||
-rw-r--r-- | src/particleemitter.cpp | 5 | ||||
-rw-r--r-- | src/player.cpp | 11 | ||||
-rw-r--r-- | src/position.cpp | 47 | ||||
-rw-r--r-- | src/position.h | 60 | ||||
-rw-r--r-- | src/resources/spritedef.cpp | 5 | ||||
-rw-r--r-- | src/simpleanimation.cpp | 5 | ||||
-rw-r--r-- | src/vector.cpp | 30 | ||||
-rw-r--r-- | src/vector.h | 68 |
28 files changed, 573 insertions, 742 deletions
@@ -1,10 +1,76 @@ +2008-10-07 Bjørn Lindeijer <bjorn@lindeijer.nl> + + * src/gui/updatewindow.cpp, src/particleemitter.cpp, + src/simpleanimation.cpp, src/resources/spritedef.cpp: Fixed some + compiler warnings. This probably also fixed the logging of several + error messages. + +2008-09-30 Bjørn Lindeijer <bjorn@lindeijer.nl> + + * src/being.cpp: Fixed conversion of positions in the path to pixel + coordinates. Solves issues with keyboard walking and other + strangeness. + * src/localplayer.cpp, src/gui/playerbox.cpp, src/npc.cpp, + src/player.cpp, src/being.cpp, src/being.h: Fixed updating of player + direction and animation. Also fixed position of name, particle effects + and the position of the player sprite in the player box. + * src/map.cpp: Fixed ordering of sprites and tiles on the fringe + layer. + +2008-09-29 Bjørn Lindeijer <bjorn@lindeijer.nl> + + * src/localplayer.cpp, src/being.cpp, src/being.h: Reenabled keyboard + walking (though its offset is broken) and make the player walk to the + exact destination when reaching the end of the path. + * src/being.cpp, src/animatedsprite.cpp: Corrected the location at + which the player sprite is drawn to match with the clicking location. + Might be a workaround for a bug elsewhere, I'm not sure yet. + +2008-09-28 Bjørn Lindeijer <bjorn@lindeijer.nl> + + * src/localplayer.cpp, src/gui/viewport.h, src/gui/viewport.cpp, + src/position.cpp, src/position.h, src/being.cpp, src/CMakeLists.txt, + src/Makefile.am, src/being.h: Added printing and drawing of paths + beings are taking and fixed an issue in adjustPath (interpreting tiles + as pixels) that caused beings to stop moving. + +2008-09-26 Bjørn Lindeijer <bjorn@lindeijer.nl> + + * src/net/beinghandler.cpp: Fixed interpretation of being speed. + 2008-09-14 Roderic Morris <roderic@ccs.neu.edu> * src/gui/widgets/tabbedarea.cpp, src/gui/widgets/tabbedarea.h: Fix to avoid guichan bug and call logic in tabbed area children. +2008-09-09 Bjørn Lindeijer <bjorn@lindeijer.nl> + + * src/being.cpp, src/net/beinghandler.cpp, src/being.h: Take into + account the speed sent by the server. Currently interpreted as pixels + per second, but this seems to be a bit too slow. + * src/localplayer.cpp: Re-enabled moving by mouse. Shows how utterly + broken the thing still is, but makes testing easier. + +2008-09-08 Bjørn Lindeijer <bjorn@lindeijer.nl> + + * src/vector.cpp, src/being.cpp, src/CMakeLists.txt, src/vector.h, + src/Makefile.am: Made sure at least something happens. + +2008-09-07 Bjørn Lindeijer <bjorn@lindeijer.nl> + + * src/map.cpp, src/position.h, src/being.cpp, src/CMakeLists.txt, + src/net/beinghandler.cpp, src/vector.h, src/map.h, src/Makefile.am, + src/being.h: Some late night fiddling around. It isn't doing anything + yet, but no time left to figure out why. + 2008-09-04 Bjørn Lindeijer <bjorn@lindeijer.nl> + * src/localplayer.cpp, src/game.cpp, src/map.cpp, src/gui/viewport.h, + src/gui/viewport.cpp, src/gui/minimap.cpp, src/beingmanager.cpp, + src/npc.cpp, src/player.cpp, src/being.cpp, src/monster.cpp, + src/net/beinghandler.cpp, src/net/playerhandler.cpp, src/vector.h, + src/map.h, src/being.h: Almost completely disabled old style movement. + Clearing the road for a new movement system. * src/map.cpp, src/gui/setup.cpp, src/gui/shop.cpp, src/gui/windowcontainer.cpp, src/gui/skill.cpp, src/beingmanager.cpp, src/flooritemmanager.cpp, src/channelmanager.cpp, src/being.cpp, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aee99d2e..688f8787 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -390,6 +390,8 @@ SET(SRCS particleemitter.h player.cpp player.h + position.cpp + position.h properties.h serverinfo.h shopitem.cpp @@ -402,6 +404,7 @@ SET(SRCS textparticle.cpp textparticle.h tileset.h + vector.cpp vector.h ) diff --git a/src/Makefile.am b/src/Makefile.am index e7752477..e1de3a34 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,8 +7,8 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ gui/widgets/layout.h \ gui/widgets/resizegrip.cpp \ gui/widgets/resizegrip.h \ - gui/widgets/tab.cpp \ - gui/widgets/tab.h \ + gui/widgets/tab.cpp \ + gui/widgets/tab.h \ gui/widgets/tabbedarea.cpp \ gui/widgets/tabbedarea.h \ gui/box.h \ @@ -342,6 +342,8 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ particleemitter.h \ player.cpp \ player.h \ + position.cpp \ + position.h \ properties.h \ serverinfo.h \ shopitem.cpp \ @@ -354,6 +356,7 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ textparticle.cpp \ textparticle.h \ tileset.h \ + vector.cpp \ vector.h # set the include path found by configure diff --git a/src/animatedsprite.cpp b/src/animatedsprite.cpp index 466779fd..13596a70 100644 --- a/src/animatedsprite.cpp +++ b/src/animatedsprite.cpp @@ -160,9 +160,7 @@ bool AnimatedSprite::draw(Graphics* graphics, int posX, int posY) const { if (!mFrame || !mFrame->image) - { return false; - } return graphics->drawImage(mFrame->image, posX + mFrame->offsetX, @@ -177,9 +175,7 @@ AnimatedSprite::setDirection(SpriteDirection direction) mDirection = direction; if (!mAction) - { return; - } Animation *animation = mAction->getAnimation(mDirection); @@ -192,26 +188,12 @@ AnimatedSprite::setDirection(SpriteDirection direction) } } -int -AnimatedSprite::getWidth() const +int AnimatedSprite::getWidth() const { - if (mFrame) - { - return mFrame->image->getWidth(); - } - else { - return 0; - } + return mFrame ? mFrame->image->getWidth() : 0; } -int -AnimatedSprite::getHeight() const +int AnimatedSprite::getHeight() const { - if (mFrame) - { - return mFrame->image->getHeight(); - } - else { - return 0; - } + return mFrame ? mFrame->image->getHeight() : 0; } diff --git a/src/being.cpp b/src/being.cpp index cf2e3772..7b77ed5a 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -43,27 +43,27 @@ #include "utils/dtor.h" #include "utils/tostring.h" +namespace { +const bool debug_movement = true; +} + int Being::instances = 0; ImageSet *Being::emotionSet = NULL; Being::Being(int id, int job, Map *map): - mX(0), mY(0), mEmotion(0), mEmotionTime(0), mAttackSpeed(350), - mWalkTime(0), mAction(STAND), mJob(job), mId(id), - mWalkSpeed(150), - mSpeedModifier(1024), mSpriteDirection(DIRECTION_DOWN), mDirection(DOWN), mMap(NULL), mEquippedWeapon(NULL), mSpeechTime(0), - mPx(0), mPy(0), mSprites(VECTOREND_SPRITE, NULL), mSpriteIDs(VECTOREND_SPRITE, 0), - mSpriteColors(VECTOREND_SPRITE, "") + mSpriteColors(VECTOREND_SPRITE, ""), + mWalkSpeed(100) { setMap(map); @@ -106,27 +106,36 @@ Being::~Being() delete mSpeechBubble; } -void Being::setPositionInPixels(int x, int y) +void Being::setPosition(const Vector &pos) { - mMap->freeTile(mX / 32, mY / 32, getBlockType()); - mX = x; - mY = y; - mMap->blockTile(x / 32, y / 32, getBlockType()); + mPos = pos; + mDest = pos; } -void Being::adjustCourse(Uint16 srcX, Uint16 srcY, Uint16 dstX, Uint16 dstY) +void Being::adjustCourse(int srcX, int srcY, int dstX, int dstY) { - if (!mMap || (mX == dstX && mY == dstY)) - { + if (debug_movement) + printf("%p adjustCourse(%d, %d, %d, %d)\n", + (void*) this, srcX, srcY, dstX, dstY); + + mDest.x = dstX; + mDest.y = dstY; + + // Find a path to the destination when it is at least a tile away + if (mMap && fabsf((mDest - mPos).length()) > 32) { + setPath(mMap->findPath((int) mPos.x / 32, (int) mPos.y / 32, + dstX / 32, dstY / 32, getWalkMask())); + } else { setPath(Path()); - return; } + // TODO: Evaluate the implementation of this method + /* if (mX / 32 == dstX / 32 && mY / 32 == dstY / 32) { // The being is already on the last tile of the path. Path p; - p.push_back(PATH_NODE(dstX, dstY)); + p.push_back(Position(dstX, dstY)); setPath(p); return; } @@ -173,7 +182,7 @@ void Being::adjustCourse(Uint16 srcX, Uint16 srcY, Uint16 dstX, Uint16 dstY) p1_length = mMap->getMetaTile(dstX / 32, dstY / 32)->Gcost; p1_dist[p1_size - 1] = p1_length; } - p1.push_back(PATH_NODE(dstX, dstY)); + p1.push_back(Position(dstX, dstY)); if (mX / 32 == srcX / 32 && mY / 32 == srcY / 32) { @@ -248,19 +257,24 @@ void Being::adjustCourse(Uint16 srcX, Uint16 srcY, Uint16 dstX, Uint16 dstY) assert(bestLength > 0); setPath(p1, p1_length * 1024 / bestLength); delete[] p1_dist; + */ } -void Being::adjustCourse(Uint16 srcX, Uint16 srcY) +void Being::adjustCourse(int srcX, int srcY) { + if (debug_movement) + printf("%p adjustCourse(%d, %d)\n", (void*) this, srcX, srcY); + if (!mPath.empty()) - { - adjustCourse(srcX, srcY, mPath.back().x, mPath.back().y); - } + adjustCourse(srcX, srcY, mPath.back().x * 32, mPath.back().y * 32); } -void Being::setDestination(Uint16 destX, Uint16 destY) +void Being::setDestination(int destX, int destY) { - adjustCourse(mX, mY, destX, destY); + if (debug_movement) + printf("%p setDestination(%d, %d)\n", (void*) this, destX, destY); + + adjustCourse((int) mPos.x, (int) mPos.y, destX, destY); } void Being::clearPath() @@ -268,34 +282,10 @@ void Being::clearPath() mPath.clear(); } -void Being::setPath(const Path &path, int mod) +void Being::setPath(const Path &path) { + std::cout << this << " New path: " << path << std::endl; mPath = path; - mSpeedModifier = mod >= 512 ? (mod <= 2048 ? mod : 2048) : 512; // TODO: tune bounds - - int sz = mPath.size(); - if (sz > 1) - { - // The path contains intermediate steps, so avoid going through tile - // centers for them. Instead, interpolate the tile offset. - int sx = mX & 31, sy = mY & 31; - int dx = (mPath.back().x & 31) - sx; - int dy = (mPath.back().y & 31) - sy; - Path::iterator j = mPath.begin(); - for (int i = 0; i < sz - 1; ++i) - { - j->x |= sx + dx * (i + 1) / (sz - 1); - j->y |= sy + dy * (i + 1) / (sz - 1); - ++j; - } - } - - if (mAction != WALK && mAction != DEAD) - { - mWalkTime = tick_time; - mStepTime = 0; - nextStep(); - } } void Being::setSprite(int slot, int id, const std::string &color) @@ -339,7 +329,8 @@ void Being::takeDamage(int amount) // Show damage number particleEngine->addTextSplashEffect(damage, 255, 255, 255, font, - mPx + 16, mPy + 16); + (int) mPos.x + 16, + (int) mPos.y + 16); } void Being::handleAttack() @@ -349,11 +340,9 @@ void Being::handleAttack() void Being::setMap(Map *map) { - // Remove sprite from potential previous map if (mMap) { - mMap->freeTile(mX / 32, mY / 32, getBlockType()); mMap->removeSprite(mSpriteIterator); } @@ -363,7 +352,6 @@ void Being::setMap(Map *map) if (mMap) { mSpriteIterator = mMap->addSprite(this); - mMap->blockTile(mX / 32, mY / 32, getBlockType()); } // Clear particle effect list because child particles became invalid @@ -447,21 +435,13 @@ void Being::setDirection(Uint8 direction) SpriteDirection dir; if (mFaceDirection & UP) - { dir = DIRECTION_UP; - } else if (mFaceDirection & RIGHT) - { dir = DIRECTION_RIGHT; - } else if (mFaceDirection & DOWN) - { dir = DIRECTION_DOWN; - } else - { dir = DIRECTION_LEFT; - } mSpriteDirection = dir; for (int i = 0; i < VECTOREND_SPRITE; i++) @@ -471,57 +451,48 @@ void Being::setDirection(Uint8 direction) } } -void Being::nextStep() -{ - if (mPath.empty()) - { - setAction(STAND); - return; - } - - PATH_NODE node = mPath.front(); - mPath.pop_front(); - - mStepX = node.x - mX; - mStepY = node.y - mY; - - int dir = 0, dx = std::abs(mStepX), dy = std::abs(mStepY); - if (dx * 2 > dy) - dir |= mStepX > 0 ? RIGHT : LEFT; - if (dy * 2 > dx) - dir |= mStepY > 0 ? DOWN : UP; - - setDirection(dir); - - if (!mMap->getWalk(node.x / 32, node.y / 32)) - { - setAction(STAND); - return; - } - - setPositionInPixels(node.x, node.y); - setAction(WALK); - mWalkTime += mStepTime / 10; - mStepTime = mWalkSpeed * (int)std::sqrt((double)mStepX * mStepX + (double)mStepY * mStepY) * - mSpeedModifier / (32 * 1024); -} - void Being::logic() { - // Determine whether the being should take another step - if (mAction == WALK && get_elapsed_time(mWalkTime) >= mStepTime) - { - nextStep(); + const Vector dest = (mPath.empty()) ? + mDest : Vector(mPath.front().x * 32 + 16, + mPath.front().y * 32 + 16); + + Vector dir = dest - mPos; + const float length = dir.length(); + + // When we're over 2 pixels from our destination, move to it + // TODO: Should be possible to make it even pixel exact, but this solves + // the jigger caused by moving too far. + if (length > 2.0f) { + const float speed = mWalkSpeed / 100.0f; + dir /= (length / speed); + mPos += dir; + + if (mAction != WALK) + setAction(WALK); + + // Update the player sprite direction + int direction = 0; + const float dx = std::abs(dir.x); + const float dy = std::abs(dir.y); + if (dx * 2 > dy) + direction |= (dir.x > 0) ? RIGHT : LEFT; + if (dy * 2 > dx) + direction |= (dir.y > 0) ? DOWN : UP; + setDirection(direction); + } + else if (!mPath.empty()) { + // TODO: Pop as soon as there is a direct unblocked line to the next + // point on the path. + mPath.pop_front(); + } else if (mAction == WALK) { + setAction(STAND); } // Reduce the time that speech is still displayed if (mSpeechTime > 0) mSpeechTime--; - // Update pixel coordinates - mPx = mX - 16 + getXOffset(); - mPy = mY - 16 + getYOffset(); - if (mEmotion != 0) { mEmotionTime--; @@ -543,7 +514,7 @@ void Being::logic() for (std::list<Particle *>::iterator i = mChildParticleEffects.begin(); i != mChildParticleEffects.end();) { - (*i)->setPosition((float)mPx + 16.0f, (float)mPy + 32.0f); + (*i)->setPosition(mPos.x, mPos.y); if (!(*i)->isAlive()) { (*i)->kill(); @@ -557,14 +528,16 @@ void Being::logic() void Being::draw(Graphics *graphics, int offsetX, int offsetY) const { - int px = mPx + offsetX; - int py = mPy + offsetY; + int px = (int) mPos.x + offsetX; + int py = (int) mPos.y + offsetY; for (int i = 0; i < VECTOREND_SPRITE; i++) { if (mSprites[i] != NULL) { - mSprites[i]->draw(graphics, px, py); + // TODO: Eventually, we probably should fix all sprite offsets so + // that this translation isn't necessary anymore. + mSprites[i]->draw(graphics, px - 16, py - 32); } } } @@ -574,18 +547,18 @@ void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) if (!mEmotion) return; - const int px = mPx + offsetX + 3; - const int py = mPy + offsetY - 60; + const int px = (int) mPos.x + offsetX + 3; + const int py = (int) mPos.y + offsetY - 60; const int emotionIndex = mEmotion - 1; - if ( emotionIndex >= 0 && emotionIndex < (int) emotionSet->size() ) + if (emotionIndex >= 0 && emotionIndex < (int) emotionSet->size()) graphics->drawImage(emotionSet->get(emotionIndex), px, py); } void Being::drawSpeech(Graphics *graphics, int offsetX, int offsetY) { - int px = mPx + offsetX; - int py = mPy + offsetY; + int px = (int) mPos.x + offsetX; + int py = (int) mPos.y + offsetY; // Draw speech above this being if (mSpeechTime > 0) @@ -605,32 +578,7 @@ Being::Type Being::getType() const return UNKNOWN; } -int Being::getOffset(int step) const -{ - // Check whether we're walking in the requested direction - if (mAction != WALK || step == 0) { - return 0; - } - - int offset = (get_elapsed_time(mWalkTime) * std::abs(step)) / mStepTime; - - // We calculate the offset _from_ the _target_ location - offset -= std::abs(step); - if (offset > 0) { - offset = 0; - } - - // Going into negative direction? Invert the offset. - if (step < 0) { - offset = -offset; - } - - return offset; -} - - -int -Being::getWidth() const +int Being::getWidth() const { if (mSprites[BASE_SPRITE]) { @@ -641,9 +589,7 @@ Being::getWidth() const } } - -int -Being::getHeight() const +int Being::getHeight() const { if (mSprites[BASE_SPRITE]) { diff --git a/src/being.h b/src/being.h index 9d04f383..567a0d98 100644 --- a/src/being.h +++ b/src/being.h @@ -29,9 +29,11 @@ #include <SDL_types.h> #include <vector> +#include "position.h" #include "sprite.h" #include "map.h" #include "animatedsprite.h" +#include "vector.h" #define NR_HAIR_STYLES 8 #define NR_HAIR_COLORS 10 @@ -46,24 +48,6 @@ class ImageSet; class Particle; class SpeechBubble; -/** - * A position along a being's path. - */ -struct PATH_NODE -{ - /** - * Constructor. - */ - PATH_NODE(unsigned short x, unsigned short y): - x(x), y(y) - { } - - unsigned short x; - unsigned short y; -}; -typedef std::list<PATH_NODE> Path; -typedef Path::iterator PathIterator; - class Being : public Sprite { public: @@ -113,11 +97,9 @@ class Being : public Sprite enum { DOWN = 1, LEFT = 2, UP = 4, RIGHT = 8 }; std::string mName; /**< Name of character */ - Uint16 mX, mY; /**< Pixel coordinates of tile center */ Uint8 mEmotion; /**< Currently showing emotion */ Uint8 mEmotionTime; /**< Time until emotion disappears */ Uint16 mAttackSpeed; /**< Attack speed */ - Uint16 mWalkTime; Action mAction; /**< Action the being is performing */ Uint16 mJob; /**< Job (player job, npc, monster, creature ) */ @@ -139,17 +121,17 @@ class Being : public Sprite /** * Sets a new destination for this being to walk to. */ - void setDestination(Uint16 destX, Uint16 destY); + void setDestination(int x, int y); /** * Adjusts course to expected stat point. */ - void adjustCourse(Uint16, Uint16); + void adjustCourse(int, int); /** * Adjusts course to expected start and end points. */ - void adjustCourse(Uint16, Uint16, Uint16, Uint16); + void adjustCourse(int, int, int, int); /** * Puts a "speech balloon" above this being for the specified amount @@ -195,12 +177,6 @@ class Being : public Sprite setSprite(int slot, int id, const std::string &color = ""); /** - * Makes this being take the next step of his path. - */ - virtual void - nextStep(); - - /** * Performs being logic. */ virtual void @@ -231,15 +207,14 @@ class Being : public Sprite /** * Gets the walk speed. + * @see setWalkSpeed(int) */ - Uint16 - getWalkSpeed() const { return mWalkSpeed; } + int getWalkSpeed() const { return mWalkSpeed; } /** - * Sets the walk speed. + * Sets the walk speed (in pixels per second). */ - void - setWalkSpeed(Uint16 speed) { mWalkSpeed = speed; } + void setWalkSpeed(int speed) { mWalkSpeed = speed; } /** * Gets the being id. @@ -269,8 +244,6 @@ class Being : public Sprite */ bool isAlive() { return mAction != DEAD; } - int getWalkTime() { return mWalkTime; } - /** * Returns the direction the being is facing. */ @@ -287,57 +260,49 @@ class Being : public Sprite * * @see Sprite::draw(Graphics, int, int) */ - virtual void - draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; /** * Returns the pixel X coordinate. */ - int - getPixelX() const { return mPx; } + int getPixelX() const { return (int) mPos.x; } /** * Returns the pixel Y coordinate. * * @see Sprite::getPixelY() */ - int - getPixelY() const { return mPy; } + int getPixelY() const { return (int) mPos.y; } /** - * sets the position in pixels using pixel coordinates + * Sets the position of this being. */ - void setPositionInPixels(int x, int y); + void setPosition(const Vector &pos); /** - * sets the position in pixels using tile coordinates - */ - void setPositionInTiles(int x, int y) - { setPositionInPixels(x * 32 + 16, y * 32 + 16); } - - /** - * Get the current X pixel offset. + * Overloaded method provided for convenience. + * + * @see setPosition(const Vector &pos) */ - int - getXOffset() const { return getOffset(mStepX); } + void setPosition(float x, float y, float z = 0.0f) + { + setPosition(Vector(x, y, z)); + } /** - * Get the current Y pixel offset. + * Returns the position of this being. */ - int - getYOffset() const { return getOffset(mStepY); } + const Vector &getPosition() const { return mPos; } /** - * Returns the horizontal size of the current base sprite of the being + * Returns the horizontal size of the current base sprite of the being. */ - virtual int - getWidth() const; + virtual int getWidth() const; /** - * Returns the vertical size of the current base sprite of the being + * Returns the vertical size of the current base sprite of the being. */ - virtual int - getHeight() const; + virtual int getHeight() const; /** * Returns the required size of a target cursor for this being. @@ -351,16 +316,22 @@ class Being : public Sprite void controlParticle(Particle *particle); /** - * Gets the way the object is blocked by other objects + * Gets the way the object is blocked by other objects. */ virtual unsigned char getWalkMask() const { return 0x00; } //can walk through everything + /** + * Returns the path this being is following. An empty path is returned + * when this being isn't following any path currently. + */ + const Path &getPath() const { return mPath; } + protected: /** * Sets the new path for this being. */ - void setPath(const Path &path, int mod = 1024); + void setPath(const Path &path); /** * Gets the way the object blocks pathfinding for other objects @@ -369,8 +340,6 @@ class Being : public Sprite { return Map::BLOCKTYPE_NONE; } Uint16 mId; /**< Unique being id */ - Uint16 mWalkSpeed; /**< Walking speed */ - Uint16 mSpeedModifier; /**< Modifier to keep course on sync (1024 = normal speed) */ Uint8 mSpriteDirection; /**< Facing direction */ Uint8 mDirection; /**< Walking direction */ Map *mMap; /**< Map on which this being resides */ @@ -382,7 +351,6 @@ class Being : public Sprite Path mPath; std::string mSpeech; Uint32 mSpeechTime; - Sint32 mPx, mPy; /**< Pixel coordinates */ std::vector<AnimatedSprite*> mSprites; std::vector<int> mSpriteIDs; @@ -390,13 +358,13 @@ class Being : public Sprite std::list<Particle *> mChildParticleEffects; private: - int getOffset(int step) const; - // Speech Bubble components SpeechBubble *mSpeechBubble; - Sint16 mStepX, mStepY; - Uint16 mStepTime; + int mWalkSpeed; /**< Walking speed (pixels/sec) */ + + Vector mPos; + Vector mDest; static int instances; /**< Number of Being instances */ static ImageSet *emotionSet; /**< Emoticons used by beings */ diff --git a/src/beingmanager.cpp b/src/beingmanager.cpp index a58c97e7..abb23f5e 100644 --- a/src/beingmanager.cpp +++ b/src/beingmanager.cpp @@ -39,8 +39,9 @@ class FindBeingFunctor bool operator() (Being *being) { Uint16 other_y = y + ((being->getType() == Being::NPC) ? 1 : 0); - return (being->mX / 32 == x && - (being->mY / 32 == y || being->mY / 32 == other_y) && + const Vector &pos = being->getPosition(); + return ((int) pos.x / 32 == x && + ((int) pos.y / 32 == y || (int) pos.y / 32 == other_y) && being->mAction != Being::DEAD && (type == Being::UNKNOWN || being->getType() == type)); } @@ -165,7 +166,8 @@ Being* BeingManager::findNearestLivingBeing(Uint16 x, Uint16 y, int maxdist, for (BeingIterator i = mBeings.begin(); i != mBeings.end(); i++) { Being *being = (*i); - int d = abs(being->mX - x) + abs(being->mY - y); + const Vector &pos = being->getPosition(); + int d = abs((int) pos.x - x) + abs((int) pos.y - y); if ((being->getType() == type || type == Being::UNKNOWN) && (d < dist || closestBeing == NULL) // it is closer @@ -185,13 +187,14 @@ Being* BeingManager::findNearestLivingBeing(Being *aroundBeing, int maxdist, { Being *closestBeing = NULL; int dist = 0; - int x = aroundBeing->mX; - int y = aroundBeing->mY; + const Vector &aroundBeingPos = aroundBeing->getPosition(); for (BeingIterator i = mBeings.begin(); i != mBeings.end(); i++) { Being *being = (*i); - int d = abs(being->mX - x) + abs(being->mY - y); + const Vector &pos = being->getPosition(); + int d = abs((int) pos.x - aroundBeingPos.x) + + abs((int) pos.y - aroundBeingPos.y); if ((being->getType() == type || type == Being::UNKNOWN) && (d < dist || closestBeing == NULL) // it is closer diff --git a/src/game.cpp b/src/game.cpp index e2343f39..fb434be2 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -580,8 +580,9 @@ void Game::handleInput() switch (tKey) { case KeyboardConfig::KEY_PICKUP: { - Uint16 x = player_node->mX / 32; - Uint16 y = player_node->mY / 32; + const Vector &pos = player_node->getPosition(); + Uint16 x = (int) pos.x / 32; + Uint16 y = (int) pos.y / 32; FloorItem *item = floorItemManager->findByCoordinates(x, y); @@ -754,7 +755,9 @@ void Game::handleInput() // Get the state of the keyboard keys keyboard.refreshActiveKeys(); - Uint16 x = player_node->mX / 32, y = player_node->mY / 32; + const Vector &pos = player_node->getPosition(); + Uint16 x = (int) pos.x / 32; + Uint16 y = (int) pos.y / 32; unsigned char direction = 0; diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index 4e5664d6..ca6f4fd7 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -113,11 +113,12 @@ void Minimap::draw(gcn::Graphics *graphics) break; } - int offset = (dotSize - 1) / 2; + const int offset = (dotSize - 1) / 2; + const Vector &pos = being->getPosition(); graphics->fillRectangle(gcn::Rectangle( - being->mX / 64 + getPadding() - offset, - being->mY / 64 + getTitleBarHeight() - offset, + (int) pos.x / 64 + getPadding() - offset, + (int) pos.y / 64 + getTitleBarHeight() - offset, dotSize, dotSize)); } } diff --git a/src/gui/playerbox.cpp b/src/gui/playerbox.cpp index 2c633b72..e9237110 100644 --- a/src/gui/playerbox.cpp +++ b/src/gui/playerbox.cpp @@ -82,10 +82,9 @@ PlayerBox::draw(gcn::Graphics *graphics) if (mPlayer) { // Draw character - int x, y, bs; - bs = getFrameSize(); - x = getWidth() / 2 - 16 + bs; - y = getHeight() / 2 + bs; + const int bs = getFrameSize(); + const int x = getWidth() / 2 + bs; + const int y = getHeight() - bs - 8; mPlayer->draw(static_cast<Graphics*>(graphics), x, y); } } diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index abae69f6..10d0c826 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -60,9 +60,9 @@ unsigned long fadler32(FILE *file) // Calculate Adler-32 checksum char *buffer = (char*) malloc(fileSize); - fread(buffer, 1, fileSize, file); + const size_t read = fread(buffer, 1, fileSize, file); unsigned long adler = adler32(0L, Z_NULL, 0); - adler = adler32(adler, (Bytef*) buffer, fileSize); + adler = adler32(adler, (Bytef*) buffer, read); free(buffer); return adler; diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index 9677f81a..80dcf489 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -58,7 +58,6 @@ Viewport::Viewport(): mShowDebugPath(false), mVisibleNames(false), mPlayerFollowMouse(false), - mWalkTime(0), mLocalWalkTime(-1) { setOpaque(false); @@ -91,9 +90,9 @@ Viewport::Viewport(): true, Being::TC_LARGE); } -void -Viewport::loadTargetCursor(std::string filename, int width, int height, - bool outRange, Being::TargetCursorSize size) +void Viewport::loadTargetCursor(const std::string &filename, + int width, int height, + bool outRange, Being::TargetCursorSize size) { assert(size >= Being::TC_SMALL); assert(size < Being::NUM_TC); @@ -125,7 +124,7 @@ Viewport::loadTargetCursor(std::string filename, int width, int height, Viewport::~Viewport() { delete mPopupMenu; - + config.removeListener("visiblenames", this); for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++) @@ -137,14 +136,12 @@ Viewport::~Viewport() } } -void -Viewport::setMap(Map *map) +void Viewport::setMap(Map *map) { mMap = map; } -void -Viewport::draw(gcn::Graphics *gcnGraphics) +void Viewport::draw(gcn::Graphics *gcnGraphics) { static int lastTick = tick_time; @@ -163,8 +160,9 @@ Viewport::draw(gcn::Graphics *gcnGraphics) int midTileX = (graphics->getWidth() + mScrollCenterOffsetX) / 2; int midTileY = (graphics->getHeight() + mScrollCenterOffsetX) / 2; - int player_x = player_node->mX - midTileX + player_node->getXOffset(); - int player_y = player_node->mY - midTileY + player_node->getYOffset(); + const Vector &playerPos = player_node->getPosition(); + const int player_x = (int) playerPos.x - midTileX; + const int player_y = (int) playerPos.y - midTileY; if (mScrollLaziness < 1) mScrollLaziness = 1; // Avoids division by zero @@ -203,8 +201,10 @@ Viewport::draw(gcn::Graphics *gcnGraphics) }; // Don't move camera so that the end of the map is on screen - int viewXmax = mMap->getWidth() * 32 - graphics->getWidth(); - int viewYmax = mMap->getHeight() * 32 - graphics->getHeight(); + const int viewXmax = + mMap->getWidth() * mMap->getTileWidth() - graphics->getWidth(); + const int viewYmax = + mMap->getHeight() * mMap->getTileHeight() - graphics->getHeight(); if (mMap) { if (mViewX < 0) { @@ -227,10 +227,11 @@ Viewport::draw(gcn::Graphics *gcnGraphics) mMap->draw(graphics, (int) mViewX, (int) mViewY); drawTargetCursor(graphics); // TODO: Draw the cursor with the sprite drawTargetName(graphics); - if (mShowDebugPath) { mMap->drawCollision(graphics, (int) mViewX, (int) mViewY); +#if 0 drawDebugPath(graphics); +#endif } } @@ -239,19 +240,21 @@ Viewport::draw(gcn::Graphics *gcnGraphics) for (BeingIterator i = beings.begin(); i != beings.end(); i++) { (*i)->drawSpeech(graphics, -(int) mViewX, -(int) mViewY); - if(mVisibleNames) + if (mVisibleNames) (*i)->drawName(graphics, -(int) mViewX, -(int) mViewY); - else if((*i) == mSelectedBeing) + else if ((*i) == mSelectedBeing) (*i)->drawName(graphics, -(int) mViewX, -(int) mViewY); (*i)->drawEmotion(graphics, -(int) mViewX, -(int) mViewY); + + if (mShowDebugPath && !(*i)->getPath().empty()) + drawPath(graphics, (*i)->getPath()); } // Draw contained widgets WindowContainer::draw(gcnGraphics); } -void -Viewport::logic() +void Viewport::logic() { WindowContainer::logic(); @@ -262,13 +265,11 @@ Viewport::logic() Uint8 button = SDL_GetMouseState(&mouseX, &mouseY); if (mPlayerFollowMouse && button & SDL_BUTTON(1) && - mWalkTime != player_node->mWalkTime && get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) { mLocalWalkTime = tick_time; player_node->setDestination(mouseX + (int) mViewX, mouseY + (int) mViewY); - mWalkTime = player_node->mWalkTime; } for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++) @@ -278,8 +279,7 @@ Viewport::logic() } } -void -Viewport::drawTargetCursor(Graphics *graphics) +void Viewport::drawTargetCursor(Graphics *graphics) { // Draw target marker if needed Being *target = player_node->getTarget(); @@ -288,9 +288,11 @@ Viewport::drawTargetCursor(Graphics *graphics) // Calculate target circle position // Find whether target is in range - int rangeX = abs(target->mX - player_node->mX); - int rangeY = abs(target->mY - player_node->mY); - int attackRange = player_node->getAttackRange(); + const Vector &dist = + target->getPosition() - player_node->getPosition(); + const int rangeX = abs((int) dist.x); + const int rangeY = abs((int) dist.y); + const int attackRange = player_node->getAttackRange(); // Get the correct target cursors graphic Being::TargetCursorSize cursorSize = target->getTargetCursorSize(); @@ -313,8 +315,7 @@ Viewport::drawTargetCursor(Graphics *graphics) } } -void -Viewport::drawTargetName(Graphics *graphics) +void Viewport::drawTargetName(Graphics *graphics) { // Draw target marker if needed Being *target = player_node->getTarget(); @@ -331,8 +332,7 @@ Viewport::drawTargetName(Graphics *graphics) } } -void -Viewport::drawDebugPath(Graphics *graphics) +void Viewport::drawDebugPath(Graphics *graphics) { // Get the current mouse position int mouseX, mouseY; @@ -340,13 +340,20 @@ Viewport::drawDebugPath(Graphics *graphics) const int mouseTileX = (mouseX + (int) mViewX) / 32; const int mouseTileY = (mouseY + (int) mViewY) / 32; + const Vector &playerPos = player_node->getPosition(); Path debugPath = mMap->findPath( - player_node->mX / 32, player_node->mY / 32, + (int) playerPos.x / 32, + (int) playerPos.y / 32, mouseTileX, mouseTileY, 0xFF); + drawPath(graphics, debugPath); +} + +void Viewport::drawPath(Graphics *graphics, const Path &path) +{ graphics->setColor(gcn::Color(255, 0, 0)); - for (PathIterator i = debugPath.begin(); i != debugPath.end(); i++) + for (Path::const_iterator i = path.begin(); i != path.end(); ++i) { int squareX = i->x * 32 - (int) mViewX + 12; int squareY = i->y * 32 - (int) mViewY + 12; @@ -358,8 +365,7 @@ Viewport::drawDebugPath(Graphics *graphics) } } -void -Viewport::mousePressed(gcn::MouseEvent &event) +void Viewport::mousePressed(gcn::MouseEvent &event) { // Check if we are alive and kickin' if (!mMap || !player_node || player_node->mAction == Being::DEAD) @@ -435,13 +441,12 @@ Viewport::mousePressed(gcn::MouseEvent &event) } } -void -Viewport::mouseDragged(gcn::MouseEvent &event) +void Viewport::mouseDragged(gcn::MouseEvent &event) { if (!mMap || !player_node) return; - if (mPlayerFollowMouse && mWalkTime == player_node->mWalkTime + if (mPlayerFollowMouse && get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) { mLocalWalkTime = tick_time; @@ -450,31 +455,27 @@ Viewport::mouseDragged(gcn::MouseEvent &event) } } -void -Viewport::mouseReleased(gcn::MouseEvent &event) +void Viewport::mouseReleased(gcn::MouseEvent &event) { mPlayerFollowMouse = false; } -void -Viewport::showPopup(int x, int y, Item *item) +void Viewport::showPopup(int x, int y, Item *item) { mPopupMenu->showPopup(x, y, item); } -void -Viewport::optionChanged(const std::string &name) +void Viewport::optionChanged(const std::string &name) { mScrollLaziness = (int) config.getValue("ScrollLaziness", 32); mScrollRadius = (int) config.getValue("ScrollRadius", 32); if (name == "visiblenames") { - mVisibleNames = config.getValue("visiblenames", 1); + mVisibleNames = config.getValue("visiblenames", 1); } } -void -Viewport::mouseMoved(gcn::MouseEvent &event) +void Viewport::mouseMoved(gcn::MouseEvent &event) { // Check if we are on the map if (!mMap || !player_node) diff --git a/src/gui/viewport.h b/src/gui/viewport.h index 82587938..5dedcea8 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -145,9 +145,10 @@ class Viewport : public WindowContainer, public gcn::MouseListener, private: /** - * Helper function for loading target cursors + * Helper function for loading target cursors. */ - void loadTargetCursor(std::string filename, int width, int height, + void loadTargetCursor(const std::string &filename, + int width, int height, bool outRange, Being::TargetCursorSize size); /** @@ -166,6 +167,11 @@ class Viewport : public WindowContainer, public gcn::MouseListener, */ void drawDebugPath(Graphics *graphics); + /** + * Draws the given path. + */ + void drawPath(Graphics *graphics, const Path &path); + Map *mMap; /**< The current map. */ @@ -191,7 +197,6 @@ class Viewport : public WindowContainer, public gcn::MouseListener, SimpleAnimation *mTargetCursorOutRange[Being::NUM_TC]; bool mPlayerFollowMouse; - int mWalkTime; int mLocalWalkTime; /**< Timestamp before the next walk can be sent. */ PopupMenu *mPopupMenu; /**< Popup menu. */ diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 00ea6a49..f596a8d9 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -87,14 +87,16 @@ void LocalPlayer::logic() } // Show XP messages - if(!mExpMessages.empty()) + if (!mExpMessages.empty()) { if (mExpMessageTime == 0) { + const Vector &pos = getPosition(); particleEngine->addTextRiseFadeOutEffect(mExpMessages.front(), 0, 128, 255, speechFont, - mPx + 16, mPy - 16); + (int) pos.x + 16, + (int) pos.y - 16); mExpMessages.pop_front(); mExpMessageTime = 30; } @@ -106,6 +108,8 @@ void LocalPlayer::logic() void LocalPlayer::nextStep() { + // TODO: Fix picking up when reaching target (this method is obsolete) + // TODO: Fix holding walking button to keep walking smoothly if (mPath.empty()) { if (mPickUpTarget) @@ -118,8 +122,6 @@ void LocalPlayer::nextStep() walk(mWalkingDir); } } - - Player::nextStep(); } bool LocalPlayer::checkInviteRights(const std::string &guildName) @@ -165,8 +167,7 @@ void LocalPlayer::setInvItem(int index, int id, int amount) mInventory->setItem(index, id, amount); } -void -LocalPlayer::moveInvItem(Item *item, int newIndex) +void LocalPlayer::moveInvItem(Item *item, int newIndex) { // special case, the old and new cannot copy over each other. if (item->getInvIndex() == newIndex) @@ -218,13 +219,12 @@ void LocalPlayer::splitItem(Item *item, int quantity) Net::GameServer::Player::moveItem( item->getInvIndex(), newIndex, quantity); } - } void LocalPlayer::pickUp(FloorItem *item) { - int dx = item->getX() - mX / 32; - int dy = item->getY() - mY / 32; + int dx = item->getX() - (int) getPosition().x / 32; + int dy = item->getY() - (int) getPosition().y / 32; if (dx * dx + dy * dy < 4) { int id = item->getId(); @@ -238,13 +238,16 @@ void LocalPlayer::pickUp(FloorItem *item) void LocalPlayer::walk(unsigned char dir) { + // TODO: Evaluate the implementation of this method if (!mMap || !dir) return; + const Vector &pos = getPosition(); + if (mAction == WALK && !mPath.empty()) { // Just finish the current action, otherwise we get out of sync - Being::setDestination(mX, mY); + Being::setDestination(pos.x, pos.y); return; } @@ -259,19 +262,23 @@ void LocalPlayer::walk(unsigned char dir) dx += 32; // Prevent skipping corners over colliding tiles - if (dx && !mMap->getWalk((mX + dx) / 32, mY / 32, getWalkMask())) - dx = 16 - mX % 32; - if (dy && !mMap->getWalk(mX / 32, (mY + dy) / 32, getWalkMask())) - dy = 16 - mY % 32; + if (dx && !mMap->getWalk(((int) pos.x + dx) / 32, + (int) pos.y / 32, getWalkMask())) + dx = 16 - (int) pos.x % 32; + if (dy && !mMap->getWalk((int) pos.x / 32, + ((int) pos.y + dy) / 32, getWalkMask())) + dy = 16 - (int) pos.y % 32; // Choose a straight direction when diagonal target is blocked - if (dx && dy && !mMap->getWalk((mX + dx) / 32, (mY + dy) / 32, getWalkMask())) - dx = 16 - mX % 32; + if (dx && dy && !mMap->getWalk((pos.x + dx) / 32, + (pos.y + dy) / 32, getWalkMask())) + dx = 16 - (int) pos.x % 32; // Walk to where the player can actually go - if ((dx || dy) && mMap->getWalk((mX + dx) / 32, (mY + dy) / 32, getWalkMask())) + if ((dx || dy) && mMap->getWalk(((int) pos.x + dx) / 32, + ((int) pos.y + dy) / 32, getWalkMask())) { - setDestination(mX + dx, mY + dy); + setDestination((int) pos.x + dx, (int) pos.y + dy); } else if (dir) { @@ -284,10 +291,20 @@ void LocalPlayer::walk(unsigned char dir) void LocalPlayer::setDestination(Uint16 x, Uint16 y) { // Fix coordinates so that the player does not seem to dig into walls. - int tx = x / 32, ty = y / 32, fx = x % 32, fy = y % 32; - if (fx != 16 && !mMap->getWalk(tx + fx / 16 * 2 - 1, ty, getWalkMask())) fx = 16; - if (fy != 16 && !mMap->getWalk(tx, ty + fy / 16 * 2 - 1, getWalkMask())) fy = 16; - if (fx != 16 && fy != 16 && !mMap->getWalk(tx + fx / 16 * 2 - 1, ty + fy / 16 * 2 - 1, getWalkMask())) fx = 16; + const int tx = x / 32; + const int ty = y / 32; + int fx = x % 32; + int fy = y % 32; + + if (fx != 16 && !mMap->getWalk(tx + fx / 16 * 2 - 1, ty, getWalkMask())) + fx = 16; + if (fy != 16 && !mMap->getWalk(tx, ty + fy / 16 * 2 - 1, getWalkMask())) + fy = 16; + if (fx != 16 && fy != 16 && !mMap->getWalk(tx + fx / 16 * 2 - 1, + ty + fy / 16 * 2 - 1, + getWalkMask())) + fx = 16; + x = tx * 32 + fx; y = ty * 32 + fy; @@ -375,7 +392,6 @@ void LocalPlayer::attack() return; mLastAction = tick_time; - mWalkTime = tick_time; setAction(ATTACK); @@ -449,7 +465,6 @@ const struct LocalPlayer::SkillInfo& LocalPlayer::getSkillInfo(int skill) { return skills[skill]; } - } void LocalPlayer::setExperience(int skill, int current, int next) diff --git a/src/map.cpp b/src/map.cpp index 888ea3a8..e10e4fad 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -113,7 +113,7 @@ void MapLayer::draw(Graphics *graphics, // If drawing the fringe layer, make sure all sprites above this row of // tiles have been drawn if (mIsFringeLayer) { - while (si != sprites.end() && (*si)->getPixelY() <= y * 32 - 32) { + while (si != sprites.end() && (*si)->getPixelY() <= y * 32) { (*si)->draw(graphics, -scrollX, -scrollY); si++; } @@ -383,35 +383,6 @@ void Map::blockTile(int x, int y, BlockType type) } } -void Map::freeTile(int x, int y, BlockType type) -{ - if (type == BLOCKTYPE_NONE || x < 0 || y < 0 || x >= mWidth || y >= mHeight) - { - return; - } - - int tileNum = x + y * mWidth; - - if ((--mOccupation[type][tileNum]) <= 0) - { - switch (type) - { - case BLOCKTYPE_WALL: - mMetaTiles[tileNum].blockmask &= (BLOCKMASK_WALL xor 0xff); - break; - case BLOCKTYPE_CHARACTER: - mMetaTiles[tileNum].blockmask &= (BLOCKMASK_CHARACTER xor 0xff); - break; - case BLOCKTYPE_MONSTER: - mMetaTiles[tileNum].blockmask &= (BLOCKMASK_MONSTER xor 0xff); - break; - default: - // shut up! - break; - } - } -} - bool Map::getWalk(int x, int y, char walkmask) const { // You can't walk outside of the map @@ -450,7 +421,7 @@ static int const basicCost = 100; Path Map::findPath(int startX, int startY, int destX, int destY, unsigned char walkmask, int maxCost) { // Path to be built up (empty by default) - std::list<PATH_NODE> path; + std::list<Position> path; // Declare open list, a list with open tiles sorted on F cost std::priority_queue<Location> openList; @@ -612,7 +583,7 @@ Path Map::findPath(int startX, int startY, int destX, int destY, unsigned char w while (pathX != startX || pathY != startY) { // Add the new path node to the start of the path list - path.push_front(PATH_NODE(pathX, pathY)); + path.push_front(Position(pathX, pathY)); // Find out the next parent MetaTile *tile = getMetaTile(pathX, pathY); @@ -27,6 +27,7 @@ #include <list> #include <vector> +#include "position.h" #include "properties.h" class AmbientOverlay; @@ -37,8 +38,6 @@ class Particle; class Sprite; class Tileset; -struct PATH_NODE; - typedef std::vector<Tileset*> Tilesets; typedef std::list<Sprite*> Sprites; typedef Sprites::iterator SpriteIterator; @@ -189,11 +188,6 @@ class Map : public Properties void blockTile(int x, int y, BlockType type); /** - * Marks a tile as unoccupied - */ - void freeTile(int x, int y, BlockType type); - - /** * Gets walkability for a tile with a blocking bitmask. When called * without walkmask, only blocks against colliding tiles. */ @@ -226,8 +220,8 @@ class Map : public Properties /** * Find a path from one location to the next. */ - std::list<PATH_NODE> - findPath(int startX, int startY, int destX, int destY, unsigned char walkmask, int maxCost = 20); + Path findPath(int startX, int startY, int destX, int destY, + unsigned char walkmask, int maxCost = 20); /** * Adds a sprite to the map. diff --git a/src/monster.cpp b/src/monster.cpp index 1561b2ea..396d0c06 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -55,7 +55,6 @@ Monster::Monster(Uint16 id, Uint16 job, Map *map): Monster::~Monster() { - if (mMap) mMap->freeTile(mX / 32, mY / 32, getBlockType()); } Being::Type diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp index b1c502e1..7e37aa27 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -48,18 +48,6 @@ const int EMOTION_TIME = 150; /**< Duration of emotion icon */ BeingHandler::BeingHandler() { static const Uint16 _messages[] = { - //SMSG_BEING_VISIBLE, - //SMSG_BEING_MOVE, - //SMSG_BEING_REMOVE, - //SMSG_BEING_ACTION, - //SMSG_BEING_LEVELUP, - //SMSG_BEING_EMOTION, - //SMSG_BEING_CHANGE_LOOKS, - //SMSG_BEING_NAME_RESPONSE, - //SMSG_PLAYER_UPDATE_1, - //SMSG_PLAYER_UPDATE_2, - //SMSG_PLAYER_MOVE, - //0x0119, GPMSG_BEING_ATTACK, GPMSG_BEING_ENTER, GPMSG_BEING_LEAVE, @@ -74,15 +62,6 @@ BeingHandler::BeingHandler() void BeingHandler::handleMessage(MessageIn &msg) { - /* - Uint32 id; - Uint16 job, speed; - Uint16 headBottom, headTop, headMid; - Sint16 param1; - Sint8 type; - Being *srcBeing, *dstBeing; - */ - switch (msg.getId()) { case GPMSG_BEING_ENTER: @@ -106,322 +85,6 @@ void BeingHandler::handleMessage(MessageIn &msg) case GPMSG_BEING_LOOKS_CHANGE: handleBeingLooksChangeMessage(msg); break; - - - /* - case SMSG_BEING_VISIBLE: - case SMSG_BEING_MOVE: - // Information about a being in range - id = msg.readInt32(); - speed = msg.readInt16(); - msg.readInt16(); // unknown - msg.readInt16(); // unknown - msg.readInt16(); // option - job = msg.readInt16(); // class - - dstBeing = beingManager->findBeing(id); - - if (!dstBeing) - { - // Being with id >= 110000000 and job 0 are better - // known as ghosts, so don't create those. - if (job == 0 && id >= 110000000) - { - break; - } - - dstBeing = beingManager->createBeing(id, job); - } - else if (msg.getId() == 0x0078) - { - dstBeing->clearPath(); - dstBeing->mFrame = 0; - dstBeing->mWalkTime = tick_time; - dstBeing->setAction(Being::STAND); - } - - // Prevent division by 0 when calculating frame - if (speed == 0) { speed = 150; } - - dstBeing->setWalkSpeed(speed); - dstBeing->mJob = job; - dstBeing->setHairStyle(msg->readInt16()); - dstBeing->setSprite( - Being::WEAPON_SPRITE, msg->readInt16()); - dstBeing->setSprite( - Being::BOTTOMCLOTHES_SPRITE, msg->readInt16()); - - if (msg.getId() == SMSG_BEING_MOVE) - { - msg.readInt32(); // server tick - } - - msg->readInt16(); // shield - headTop = msg->readInt16(); - headMid = msg->readInt16(); - dstBeing->setSprite(Being::HAT_SPRITE, headTop); - dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid); - dstBeing->setHairColor(msg->readInt16()); - msg->readInt16(); // unknown - msg->readInt16(); // head dir - msg->readInt16(); // guild - msg->readInt16(); // unknown - msg->readInt16(); // unknown - msg->readInt16(); // manner - msg->readInt16(); // karma - msg->readInt8(); // unknown - dstBeing->setSex(1 - msg->readInt8()); // sex - - if (msg.getId() == SMSG_BEING_MOVE) - { - //Uint16 srcX, srcY, dstX, dstY; - //msg.readCoordinatePair(srcX, srcY, dstX, dstY); - //dstBeing->setAction(Being::STAND); - //dstBeing->mX = srcX; - //dstBeing->mY = srcY; - //dstBeing->setDestination(dstX, dstY); - } - else - { - //Uint8 dir; - //msg->readCoordinates(dstBeing->mX, dstBeing->mY, dir); - //dstBeing->setDirection(dir); - } - - msg.readInt8(); // unknown - msg.readInt8(); // unknown - msg.readInt8(); // unknown / sit - break; - - case SMSG_BEING_REMOVE: - // A being should be removed or has died - dstBeing = beingManager->findBeing(msg.readInt32()); - - if (!dstBeing) - break; - - if (msg.readInt8() == 1) - { - dstBeing->setAction(Being::DEAD); - } - else - { - beingManager->destroyBeing(dstBeing); - } - - if (dstBeing == player_node->getTarget()) - { - player_node->stopAttack(); - } - break; - - case SMSG_BEING_ACTION: - srcBeing = beingManager->findBeing(msg.readInt32()); - dstBeing = beingManager->findBeing(msg.readInt32()); - msg.readInt32(); // server tick - msg.readInt32(); // src speed - msg.readInt32(); // dst speed - param1 = msg.readInt16(); - msg.readInt16(); // param 2 - type = msg.readInt8(); - msg.readInt16(); // param 3 - - switch (type) - { - case 0: // Damage - if (dstBeing) { - dstBeing->takeDamage(param1); - } - if (srcBeing) { - srcBeing->handleAttack(dstBeing, param1); - } - break; - - case 2: // Sit - if (srcBeing) { - srcBeing->mFrame = 0; - srcBeing->setAction(Being::SIT); - } - break; - - case 3: // Stand up - if (srcBeing) { - srcBeing->mFrame = 0; - srcBeing->setAction(Being::STAND); - } - break; - } - break; - - case SMSG_BEING_LEVELUP: - id = (Uint32) msg->readInt32(); - - if (id == player_node->getId()) { - logger->log("Level up"); - sound.playSfx("sfx/levelup.ogg"); - } - else { - logger->log("Someone else went level up"); - } - Particle *levelupFX; - if (msg->readInt32() == 0) { // type - levelupFX = particleEngine->addEffect( - "graphics/particles/levelup.particle.xml", 0, 0); - } - else { - levelupFX = particleEngine->addEffect( - "graphics/particles/skillup.particle.xml", 0, 0); - } - beingManager->findBeing(id)->controlParticle(levelupFX); - break; - - case SMSG_BEING_EMOTION: - if (!(dstBeing = beingManager->findBeing(msg.readInt32()))) - { - break; - } - - dstBeing->mEmotion = msg.readInt8(); - dstBeing->mEmotionTime = EMOTION_TIME; - break; - - case SMSG_BEING_CHANGE_LOOKS: - { - if (!(dstBeing = beingManager->findBeing(msg.readInt32()))) - { - break; - } - - int type = msg.readInt8(); - int id = msg.readInt8(); - - switch (type) { - case 1: - dstBeing->setHairStyle(id); - break; - case 2: - dstBeing->setSprite(Being::WEAPON_SPRITE, id); - break; - case 3: // Change lower headgear for eAthena, pants for us - dstBeing->setSprite( - Being::BOTTOMCLOTHES_SPRITE, id); - break; - case 4: // Change upper headgear for eAthena, hat for us - dstBeing->setSprite( - Being::HAT_SPRITE, id); - break; - case 5: // Change middle headgear for eathena, armor for us - dstBeing->setSprite( - Being::TOPCLOTHES_SPRITE, id); - break; - case 6: - dstBeing->setHairColor(id); - break; - default: - logger->log("SMSG_BEING_CHANGE_LOOKS: unsupported type: " - "%d, id: %d", type, id); - break; - } - } - break; - - case SMSG_BEING_NAME_RESPONSE: - if ((dstBeing = beingManager->findBeing(msg.readInt32()))) - { - dstBeing->setName(msg.readString(24)); - } - break; - - case SMSG_PLAYER_UPDATE_1: - case SMSG_PLAYER_UPDATE_2: - case SMSG_PLAYER_MOVE: - // An update about a player, potentially including movement. - id = msg.readInt32(); - speed = msg.readInt16(); - msg.readInt16(); // option 1 - msg.readInt16(); // option 2 - msg.readInt16(); // option - job = msg.readInt16(); - - dstBeing = beingManager->findBeing(id); - - if (!dstBeing) - { - dstBeing = beingManager->createBeing(id, job); - } - - dstBeing->setWalkSpeed(speed); - dstBeing->mJob = job; - dstBeing->setHairStyle(msg->readInt16()); - dstBeing->setSprite( - Being::WEAPON_SPRITE, msg->readInt16()); - msg->readInt16(); // item id 2 - headBottom = msg->readInt16(); - - if (msg.getId() == SMSG_PLAYER_MOVE) - { - msg.readInt32(); // server tick - } - - headTop = msg.readInt16(); - headMid = msg.readInt16(); - dstBeing->setHairColor(msg.readInt16()); - msg.readInt16(); // unknown - msg.readInt16(); // head dir - msg.readInt32(); // guild - msg.readInt32(); // emblem - msg.readInt16(); // manner - msg.readInt8(); // karma - dstBeing->setSex(1 - msg.readInt8()); // sex - dstBeing->setSprite( - Being::BOTTOMCLOTHES_SPRITE, headBottom); - dstBeing->setSprite(Being::HAT_SPRITE, headTop); - dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid); - - if (msg.getId() == SMSG_PLAYER_MOVE) - { - //Uint16 srcX, srcY, dstX, dstY; - //msg.readCoordinatePair(srcX, srcY, dstX, dstY); - //dstBeing->mX = srcX; - //dstBeing->mY = srcY; - //dstBeing->setDestination(dstX, dstY); - } - else - { - //Uint8 dir; - //msg->readCoordinates(dstBeing->mX, dstBeing->mY, dir); - //dstBeing->setDirection(dir); - } - - msg.readInt8(); // unknown - msg.readInt8(); // unknown - - if (msg.getId() == SMSG_PLAYER_UPDATE_1) - { - if (msg.readInt8() == 2) - { - dstBeing->setAction(Being::SIT); - } - } - else if (msg.getId() == SMSG_PLAYER_MOVE) - { - msg.readInt8(); // unknown - } - - msg.readInt8(); // Lv - msg.readInt8(); // unknown - - dstBeing->mWalkTime = tick_time; - dstBeing->mFrame = 0; - break; - - case 0x0119: - // Change in players look - logger->log("0x0119 %i %i %i %x %i", msg->readInt32(), - msg->readInt16(), msg->readInt16(), msg->readInt16(), - msg->readInt8()); - break; - */ } } @@ -453,8 +116,7 @@ static void handleLooks(Player *being, MessageIn &msg) } } -void -BeingHandler::handleBeingEnterMessage(MessageIn &msg) +void BeingHandler::handleBeingEnterMessage(MessageIn &msg) { int type = msg.readInt8(); int id = msg.readInt16(); @@ -481,7 +143,8 @@ BeingHandler::handleBeingEnterMessage(MessageIn &msg) Player *p = static_cast< Player * >(being); int hs = msg.readInt8(), hc = msg.readInt8(); p->setHairStyle(hs, hc); - p->setGender(msg.readInt8() == GENDER_MALE ? GENDER_MALE : GENDER_FEMALE); + p->setGender(msg.readInt8() == GENDER_MALE ? + GENDER_MALE : GENDER_FEMALE); handleLooks(p, msg); } break; @@ -498,7 +161,7 @@ BeingHandler::handleBeingEnterMessage(MessageIn &msg) return; } - being->setPositionInPixels(px, py); + being->setPosition(px, py); being->setDestination(px, py); being->setAction(action); } @@ -518,7 +181,17 @@ void BeingHandler::handleBeingsMoveMessage(MessageIn &msg) int id = msg.readInt16(); int flags = msg.readInt8(); Being *being = beingManager->findBeing(id); - int sx = 0, sy = 0, dx = 0, dy = 0, speed = 0; + int sx = 0; + int sy = 0; + int dx = 0; + int dy = 0; + int speed = 0; + + printf("handleBeingsMoveMessage for %p (%s | %s)\n", + (void*) being, + (flags & MOVING_POSITION) ? "pos" : "", + (flags & MOVING_DESTINATION) ? "dest" : ""); + if (flags & MOVING_POSITION) { Uint16 sx2, sy2; @@ -543,12 +216,19 @@ void BeingHandler::handleBeingsMoveMessage(MessageIn &msg) } if (speed) { - being->setWalkSpeed(speed * 10); + /* The speed on the server is the cost of moving from one tile to + * the next. Beings get 1000 cost units per second. The speed is + * transferred as devided by 10, so that slower speeds fit in a + * byte. Here we convert the speed to pixels per second. + */ + const float tilesPerSecond = 100.0f / speed; + being->setWalkSpeed((int) (tilesPerSecond * 32)); } - if (abs(being->getPixelX() - sx) + abs(being->getPixelY() - sy) > 4 * 32) + if (abs(being->getPixelX() - sx) + + abs(being->getPixelY() - sy) > 4 * 32) { // Too large a desynchronization. - being->setPositionInPixels(sx, sy); + being->setPosition(sx, sy); being->setDestination(dx, dy); } else if (!(flags & MOVING_POSITION)) diff --git a/src/net/playerhandler.cpp b/src/net/playerhandler.cpp index ea581095..3c0a1835 100644 --- a/src/net/playerhandler.cpp +++ b/src/net/playerhandler.cpp @@ -302,11 +302,12 @@ PlayerHandler::handleMapChangeMessage(MessageIn &msg) current_npc = 0; - const float scrollOffsetX = x - player_node->mX; - const float scrollOffsetY = y - player_node->mY; + const Vector &playerPos = player_node->getPosition(); + const float scrollOffsetX = x - (int) playerPos.x; + const float scrollOffsetY = y - (int) playerPos.y; player_node->setAction(Being::STAND); - player_node->setPositionInPixels(x, y); + player_node->setPosition(x, y); logger->log("Adjust scrolling by %d,%d", (int) scrollOffsetX, (int) scrollOffsetY); diff --git a/src/npc.cpp b/src/npc.cpp index c2b266ff..a7302e0d 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -69,15 +69,15 @@ NPC::getType() const return Being::NPC; } -void -NPC::drawName(Graphics *graphics, Sint32 offsetX, Sint32 offsetY) +void NPC::drawName(Graphics *graphics, Sint32 offsetX, Sint32 offsetY) { - int px = mPx + offsetX; - int py = mPy + offsetY; + const Vector &pos = getPosition(); + const int px = (int) pos.x + offsetX; + const int py = (int) pos.y + offsetY; graphics->setFont(speechFont); graphics->setColor(gcn::Color(200, 200, 255)); - graphics->drawText(mName, px + 15, py + 30, gcn::Graphics::CENTER); + graphics->drawText(mName, px, py, gcn::Graphics::CENTER); } void diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp index f03490ac..23c6879e 100644 --- a/src/particleemitter.cpp +++ b/src/particleemitter.cpp @@ -195,7 +195,7 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * if (!img) { - logger->log("No image at index " + (index)); + logger->log("No image at index %d", index); continue; } @@ -218,8 +218,7 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * if (!img) { - logger->log("No image at index " + - (start)); + logger->log("No image at index %d", start); continue; } diff --git a/src/player.cpp b/src/player.cpp index 49f0221d..97c60789 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -46,7 +46,6 @@ Player::Player(int id, int job, Map *map): Player::~Player() { - if (mMap) mMap->freeTile(mX / 32, mY / 32, getBlockType()); } Being::Type @@ -55,15 +54,15 @@ Player::getType() const return PLAYER; } -void -Player::drawName(Graphics *graphics, int offsetX, int offsetY) +void Player::drawName(Graphics *graphics, int offsetX, int offsetY) { - int px = mPx + offsetX; - int py = mPy + offsetY; + const Vector &pos = getPosition(); + const int px = (int) pos.x + offsetX; + const int py = (int) pos.y + offsetY; graphics->setFont(speechFont); graphics->setColor(gcn::Color(255, 255, 255)); - graphics->drawText(mName, px + 15, py + 30, gcn::Graphics::CENTER); + graphics->drawText(mName, px, py, gcn::Graphics::CENTER); } void Player::setGender(Gender gender) diff --git a/src/position.cpp b/src/position.cpp new file mode 100644 index 00000000..334079bb --- /dev/null +++ b/src/position.cpp @@ -0,0 +1,47 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "position.h" + +std::ostream& operator <<(std::ostream &os, const Position &p) +{ + os << "(" << p.x << ", " << p.y << ")"; + return os; +} + +std::ostream& operator <<(std::ostream &os, const Path &path) +{ + Path::const_iterator i = path.begin(); + + os << "("; + while (i != path.end()) + { + os << *i; + ++i; + if (i != path.end()) + os << ", "; + } + os << ")"; + + return os; +} diff --git a/src/position.h b/src/position.h new file mode 100644 index 00000000..d1aa2ee6 --- /dev/null +++ b/src/position.h @@ -0,0 +1,60 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: being.h 4570 2008-09-04 20:59:34Z b_lindeijer $ + */ + +#ifndef TMW_POSITION_H +#define TMW_POSITION_H + +#include <list> +#include <iostream> + +/** + * A position along a being's path. + */ +struct Position +{ + /** + * Constructor. + */ + Position(int x, int y): + x(x), y(y) + { } + + int x; + int y; +}; + +typedef std::list<Position> Path; +typedef Path::iterator PathIterator; + +/** + * Appends a string representation of a position to the output stream. + */ +std::ostream& operator <<(std::ostream &os, const Position &p); + +/** + * Appends a string representation of a path (sequence of positions) to the + * output stream. + */ +std::ostream& operator <<(std::ostream &os, const Path &path); + +#endif // TMW_POSITION_H diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index 9d98184a..dcfee165 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -232,7 +232,7 @@ SpriteDef::loadAnimation(xmlNodePtr animationNode, if (!img) { - logger->log("No image at index " + (index + variant_offset)); + logger->log("No image at index %d", index + variant_offset); continue; } @@ -255,8 +255,7 @@ SpriteDef::loadAnimation(xmlNodePtr animationNode, if (!img) { - logger->log("No image at index " + - (start + variant_offset)); + logger->log("No image at index %d", start + variant_offset); continue; } diff --git a/src/simpleanimation.cpp b/src/simpleanimation.cpp index 18e732ef..f425d3c1 100644 --- a/src/simpleanimation.cpp +++ b/src/simpleanimation.cpp @@ -68,7 +68,7 @@ SimpleAnimation::SimpleAnimation(xmlNodePtr animationNode): if (!img) { - logger->log("No image at index " + (index)); + logger->log("No image at index %d", index); continue; } @@ -91,8 +91,7 @@ SimpleAnimation::SimpleAnimation(xmlNodePtr animationNode): if (!img) { - logger->log("No image at index " + - (start)); + logger->log("No image at index %d", start); continue; } diff --git a/src/vector.cpp b/src/vector.cpp new file mode 100644 index 00000000..88092c9b --- /dev/null +++ b/src/vector.cpp @@ -0,0 +1,30 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: vector.h 4592 2008-09-07 20:38:52Z b_lindeijer $ + */ + +#include "vector.h" + +std::ostream& operator <<(std::ostream &os, const Vector &v) +{ + os << "Vector(" << v.x << ", " << v.y << ", " << v.z << ")"; + return os; +} diff --git a/src/vector.h b/src/vector.h index 7a5da241..7251eff0 100644 --- a/src/vector.h +++ b/src/vector.h @@ -24,6 +24,10 @@ #ifndef _TMW_VECTOR_H_ #define _TMW_VECTOR_H_ +#include <math.h> + +#include <iostream> + /** * Vector class. Represents either a 3D point in space, a velocity or a force. * Provides several convenient operator overloads. @@ -43,7 +47,7 @@ class Vector /** * Constructor. */ - Vector(float x, float y, float z): + Vector(float x, float y, float z = 0.0f): x(x), y(y), z(z) @@ -71,11 +75,12 @@ class Vector /** * In-place scale vector operator. */ - void operator*=(float c) + Vector &operator*=(float c) { x *= c; y *= c; z *= c; + return *this; } /** @@ -89,6 +94,17 @@ class Vector } /** + * In-place scale vector operator. + */ + Vector &operator/=(float c) + { + x /= c; + y /= c; + z /= c; + return *this; + } + + /** * Add vector operator. */ Vector operator+(const Vector &v) const @@ -101,11 +117,12 @@ class Vector /** * In-place add vector operator. */ - void operator+=(const Vector &v) + Vector &operator+=(const Vector &v) { x += v.x; y += v.y; z += v.z; + return *this; } /** @@ -121,14 +138,55 @@ class Vector /** * In-place substract vector operator. */ - void operator-=(const Vector &v) + Vector &operator-=(const Vector &v) { x -= v.x; y -= v.y; z -= v.z; + return *this; + } + + /** + * Returns the length of this vector. This method does a relatively + * slow square root. + */ + float length() const + { + return sqrtf(x * x + y * y + z * z); + } + + /** + * Returns the squared length of this vector. Avoids the square root. + */ + float squaredLength() const + { + return x * x + y * y + z * z; + } + + /** + * Returns the manhattan length of this vector. + */ + float manhattanLength() const + { + return fabsf(x) + fabsf(y) + fabsf(z); + } + + /** + * Returns a normalized version of this vector. This is a unit vector + * running parallel to it. + */ + Vector normalized() const + { + const float l = length(); + return Vector(x / l, y / l, z / l); } float x, y, z; }; -#endif +/** + * Appends a string representation of a vector to the output stream. + */ +std::ostream& operator <<(std::ostream &os, const Vector &v); + +#endif // _TMW_VECTOR_H_ |