diff options
author | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2009-03-22 19:45:03 +0100 |
---|---|---|
committer | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2009-03-22 19:45:56 +0100 |
commit | 0c43d04b438d41c277ae80402d4b4888db1a0b64 (patch) | |
tree | 3aaeb75ecd1bcbe85decedab5f1fa426fe0411e3 /src/being.cpp | |
parent | a7f5eaeb7f643658d356533a608f0f18d85b6d32 (diff) | |
parent | 401802c1d7a1b3d659bdc53a45d9a6292fc1121e (diff) | |
download | mana-0c43d04b438d41c277ae80402d4b4888db1a0b64.tar.gz mana-0c43d04b438d41c277ae80402d4b4888db1a0b64.tar.bz2 mana-0c43d04b438d41c277ae80402d4b4888db1a0b64.tar.xz mana-0c43d04b438d41c277ae80402d4b4888db1a0b64.zip |
Merged the tmwserv client with the eAthena client
This merge involved major changes on both sides, and as such took
several weeks. Lots of things are expected to be broken now, however, we
now have a single code base to improve and extend, which can be compiled
to support either eAthena or tmwserv.
In the coming months, the plan is to work towards a client that supports
both eAthena and tmwserv, without needing to be recompiled.
Conflicts:
Everywhere!
Diffstat (limited to 'src/being.cpp')
-rw-r--r-- | src/being.cpp | 315 |
1 files changed, 304 insertions, 11 deletions
diff --git a/src/being.cpp b/src/being.cpp index 43480eba..7f5a7d33 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -55,6 +55,11 @@ #include <cassert> #include <cmath> +namespace { +const bool debug_movement = true; +} + + #define BEING_EFFECTS_FILE "effects.xml" #define HAIR_FILE "hair.xml" @@ -69,21 +74,30 @@ static const int DEFAULT_WIDTH = 32; static const int DEFAULT_HEIGHT = 32; Being::Being(int id, int job, Map *map): - mJob(job), +#ifdef EATHENA_SUPPORT mX(0), mY(0), - mAction(STAND), mWalkTime(0), +#endif mEmotion(0), mEmotionTime(0), mAttackSpeed(350), + mAction(STAND), + mJob(job), mId(id), - mWalkSpeed(150), mDirection(DOWN), +#ifdef TMWSERV_SUPPORT + mSpriteDirection(DIRECTION_DOWN), +#endif mMap(NULL), mName(""), mIsGM(false), mParticleEffects(config.getValue("particleeffects", 1)), mEquippedWeapon(NULL), - mHairStyle(1), mHairColor(0), +#ifdef TMWSERV_SUPPORT + mHairStyle(0), +#else + mHairStyle(1), +#endif + mHairColor(0), mGender(GENDER_UNSPECIFIED), mSpeechTime(0), mPx(0), mPy(0), @@ -94,6 +108,11 @@ Being::Being(int id, int job, Map *map): mStatusParticleEffects(&mStunParticleEffects, false), mChildParticleEffects(&mStatusParticleEffects, false), mMustResetParticles(false), +#ifdef TMWSERV_SUPPORT + mWalkSpeed(100), +#else + mWalkSpeed(150), +#endif mUsedTargetCursor(NULL) { setMap(map); @@ -150,12 +169,188 @@ Being::~Being() delete mText; } +void Being::setPosition(const Vector &pos) +{ + mPos = pos; + mDest = pos; + mPath.clear(); +} + +#ifdef EATHENA_SUPPORT void Being::setDestination(Uint16 destX, Uint16 destY) { if (mMap) - setPath(mMap->findPath(mX, mY, destX, destY)); + setPath(mMap->findPath(mX, mY, destX, destY, getWalkMask())); +} +#endif + +#ifdef TMWSERV_SUPPORT +void Being::adjustCourse(int srcX, int srcY, int dstX, int 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()); + } + + // 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(Position(dstX, dstY)); + setPath(p); + return; + } + + Path p1; + int p1_size, p1_length; + Uint16 *p1_dist; + int onPath = -1; + if (srcX / 32 == dstX / 32 && srcY / 32 == dstY / 32) + { + p1_dist = new Uint16[1]; + p1_size = 1; + p1_dist[0] = 0; + p1_length = 0; + } + else + { + p1 = mMap->findPath(srcX / 32, srcY / 32, dstX / 32, dstY / 32, getWalkMask()); + if (p1.empty()) + { + // No path, but don't teleport since it could be user input. + setPath(p1); + return; + } + p1_size = p1.size(); + p1_dist = new Uint16[p1_size]; + int j = 0; + // Remove last tile so that it can be replaced by the exact destination. + p1.pop_back(); + for (Path::iterator i = p1.begin(), i_end = p1.end(); i != i_end; ++i) + { + // Get distance from source to tile i. + p1_dist[j] = mMap->getMetaTile(i->x, i->y)->Gcost; + // Check if the being is already walking on the path. + if (i->x == mX / 32 && i->y == mY / 32) + { + onPath = j; + } + // Do not set any offset for intermediate steps. + i->x = i->x * 32; + i->y = i->y * 32; + ++j; + } + p1_length = mMap->getMetaTile(dstX / 32, dstY / 32)->Gcost; + p1_dist[p1_size - 1] = p1_length; + } + p1.push_back(Position(dstX, dstY)); + + if (mX / 32 == srcX / 32 && mY / 32 == srcY / 32) + { + // The being is at the start of the path. + setPath(p1); + delete[] p1_dist; + return; + } + + if (onPath >= 0) + { + // The being is already on the path, but it needs to be slowed down. + for (int j = onPath; j >= 0; --j) + { + p1.pop_front(); + } + int r = p1_length - p1_dist[onPath]; // remaining length + assert(r > 0); + setPath(p1, p1_length * 1024 / r); + delete[] p1_dist; + return; + } + + Path bestPath; + int bestRating = -1, bestStart = 0, bestLength = 0; + int j = 0; + + for (Path::iterator i = p1.begin(), i_end = p1.end(); i != i_end; ++i) + { + // Look if it is worth passing by tile i. + Path p2 = mMap->findPath(mX / 32, mY / 32, i->x / 32, i->y / 32, getWalkMask()); + if (!p2.empty()) + { + int l1 = mMap->getMetaTile(i->x / 32, i->y / 32)->Gcost; + int l2 = p1_length - p1_dist[j]; + int r = l1 + l2 / 2; // TODO: tune rating formula + assert(r > 0); + if (bestRating < 0 || r < bestRating) + { + bestPath.swap(p2); + bestRating = r; + bestStart = j; + bestLength = l1 + l2; + } + } + ++j; + } + + if (bestRating < 0) + { + // Unable to reach the path? Still, don't teleport since it could be + // user input instead of server command. + setPath(p1); + delete[] p1_dist; + return; + } + + bestPath.pop_back(); + for (Path::iterator i = bestPath.begin(), i_end = bestPath.end(); i != i_end; ++i) + { + i->x = i->x * 32; + i->y = i->y * 32; + } + + // Concatenate paths. + for (int j = bestStart; j > 0; --j) + { + p1.pop_front(); + } + p1.splice(p1.begin(), bestPath); + + assert(bestLength > 0); + setPath(p1, p1_length * 1024 / bestLength); + delete[] p1_dist; + */ } +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 * 32, mPath.back().y * 32); +} + +void Being::setDestination(int destX, int destY) +{ + if (debug_movement) + printf("%p setDestination(%d, %d)\n", (void*) this, destX, destY); + + adjustCourse((int) mPos.x, (int) mPos.y, destX, destY); +} +#endif // TMWSERV_SUPPORT + void Being::clearPath() { mPath.clear(); @@ -164,12 +359,15 @@ void Being::clearPath() void Being::setPath(const Path &path) { mPath = path; - +#ifdef TMWSERV_SUPPORT + std::cout << this << " New path: " << path << std::endl; +#else if (mAction != WALK && mAction != DEAD) { nextStep(); mWalkTime = tick_time; } +#endif } void Being::setHairStyle(int style, int color) @@ -178,7 +376,7 @@ void Being::setHairStyle(int style, int color) mHairColor = color < 0 ? mHairColor : color % ColorDB::size(); } -void Being::setSprite(int slot, int id, std::string color) +void Being::setSprite(int slot, int id, const std::string &color) { assert(slot >= BASE_SPRITE && slot < VECTOREND_SPRITE); mSpriteIDs[slot] = id; @@ -245,7 +443,12 @@ void Being::takeDamage(int amount) // Show damage number particleEngine->addTextSplashEffect(damage, 255, 255, 255, font, +#ifdef TMWSERV_SUPPORT + (int) mPos.x + 16, + (int) mPos.y + 16); +#else mPx + 16, mPy + 16); +#endif effectManager->trigger(26, this); } @@ -255,11 +458,17 @@ void Being::showCrit() } +#ifdef TMWSERV_SUPPORT +void Being::handleAttack() +#else void Being::handleAttack(Being *victim, int damage) +#endif { setAction(Being::ATTACK); +#ifdef EATHENA_SUPPORT mFrame = 0; mWalkTime = tick_time; +#endif } void Being::setMap(Map *map) @@ -284,7 +493,7 @@ void Being::controlParticle(Particle *particle) mChildParticleEffects.addLocally(particle); } -void Being::setAction(Action action) +void Being::setAction(Action action, int attackType) { SpriteAction currentAction = ACTION_INVALID; @@ -340,8 +549,27 @@ void Being::setDirection(Uint8 direction) if (mDirection == direction) return; +#ifdef TMWSERV_SUPPORT + // if the direction does not change much, keep the common component + int mFaceDirection = mDirection & direction; + if (!mFaceDirection) + mFaceDirection = direction; + mDirection = direction; + + SpriteDirection dir; + if (mFaceDirection & UP) + dir = DIRECTION_UP; + else if (mFaceDirection & DOWN) + dir = DIRECTION_DOWN; + else if (mFaceDirection & RIGHT) + dir = DIRECTION_RIGHT; + else + dir = DIRECTION_LEFT; + mSpriteDirection = dir; +#else mDirection = direction; SpriteDirection dir = getSpriteDirection(); +#endif for (int i = 0; i < VECTOREND_SPRITE; i++) { @@ -350,6 +578,7 @@ void Being::setDirection(Uint8 direction) } } +#ifdef EATHENA_SUPPORT SpriteDirection Being::getSpriteDirection() const { SpriteDirection dir; @@ -397,7 +626,7 @@ void Being::nextStep() setDirection(dir); - if (mMap->tileCollides(pos.x, pos.y)) + if (!mMap->getWalk(pos.x, pos.y, getWalkMask())) { setAction(STAND); return; @@ -408,12 +637,13 @@ void Being::nextStep() setAction(WALK); mWalkTime += mWalkSpeed / 10; } +#endif void Being::logic() { // Reduce the time that speech is still displayed if (mSpeechTime > 0) - mSpeechTime--; + mSpeechTime--; // Remove text if speech boxes aren't being used if (mSpeechTime == 0 && mText) @@ -422,6 +652,42 @@ void Being::logic() mText = 0; } +#ifdef TMWSERV_SUPPORT + 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; + mPos += dir / (length / speed); + + 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 > dy) + direction |= (dir.x > 0) ? RIGHT : LEFT; + else + 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); + } +#else int oldPx = mPx; int oldPy = mPy; @@ -433,6 +699,7 @@ void Being::logic() { updateCoords(); } +#endif if (mEmotion != 0) { @@ -463,15 +730,23 @@ void Being::logic() } // Update particle effects +#ifdef TMWSERV_SUPPORT mChildParticleEffects.moveTo((float) mPx + 16.0f, (float) mPy + 32.0f); - +#else + mChildParticleEffects.moveTo(mPos.x, mPos.y); +#endif } void Being::draw(Graphics *graphics, int offsetX, int offsetY) const { +#ifdef TMWSERV_SUPPORT + int px = (int) mPos.x + offsetX; + int py = (int) mPos.y + offsetY; +#else int px = mPx + offsetX; int py = mPy + offsetY; +#endif if (mUsedTargetCursor) { @@ -482,7 +757,13 @@ void Being::draw(Graphics *graphics, int offsetX, int offsetY) const { if (mSprites[i]) { +#ifdef TMWSERV_SUPPORT + // 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); +#else mSprites[i]->draw(graphics, px, py); +#endif } } } @@ -492,8 +773,13 @@ void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) if (!mEmotion) return; +#ifdef TMWSERV_SUPPORT + const int px = (int) mPos.x + offsetX + 3; + const int py = (int) mPos.y + offsetY - 60; +#else const int px = mPx + offsetX + 3; const int py = mPy + offsetY - 60; +#endif const int emotionIndex = mEmotion - 1; if (emotionIndex >= 0 && emotionIndex <= EmoteDB::getLast()) @@ -502,8 +788,13 @@ void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) void Being::drawSpeech(int offsetX, int offsetY) { +#ifdef TMWSERV_SUPPORT + int px = (int) mPos.x + offsetX; + int py = (int) mPos.y + offsetY; +#else const int px = mPx + offsetX; const int py = mPy + offsetY; +#endif const int speech = (int) config.getValue("speech", NAME_IN_BUBBLE); // Draw speech above this being @@ -612,6 +903,7 @@ void Being::setStatusEffect(int index, bool active) } } +#ifdef EATHENA_SUPPORT int Being::getOffset(char pos, char neg) const { // Check whether we're walking in the requested direction @@ -637,6 +929,7 @@ int Being::getOffset(char pos, char neg) const return offset; } +#endif int Being::getWidth() const { |