00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <cassert>
00022
00023 #include "game-server/being.hpp"
00024
00025 #include "defines.h"
00026 #include "game-server/attackzone.hpp"
00027 #include "game-server/collisiondetection.hpp"
00028 #include "game-server/eventlistener.hpp"
00029 #include "game-server/mapcomposite.hpp"
00030 #include "game-server/effect.hpp"
00031 #include "utils/logger.h"
00032
00033 Being::Being(ThingType type):
00034 Actor(type),
00035 mAction(STAND),
00036 mSpeed(0),
00037 mDirection(0),
00038 mHpRegenTimer(0)
00039 {
00040 Attribute attr = { 0, 0 };
00041 mAttributes.resize(NB_BEING_ATTRIBUTES, attr);
00042
00043 for (int i = BASE_ELEM_BEGIN; i < BASE_ELEM_END; ++i)
00044 {
00045 mAttributes[i].base = 100;
00046 }
00047 }
00048
00049 int Being::damage(Actor *, const Damage &damage)
00050 {
00051 if (mAction == DEAD)
00052 return 0;
00053
00054 int HPloss = damage.base;
00055 if (damage.delta)
00056 {
00057 HPloss += rand() / (RAND_MAX / (damage.delta + 1));
00058 }
00059
00060 int hitThrow = rand()%(damage.cth + 1);
00061 int evadeThrow = rand()%(getModifiedAttribute(BASE_ATTR_EVADE) + 1);
00062 if (evadeThrow > hitThrow)
00063 {
00064 HPloss = 0;
00065 }
00066
00067
00068
00069 int mod1 = getModifiedAttribute(BASE_ELEM_BEGIN + damage.element);
00070 HPloss = HPloss * (mod1 / 100);
00071
00072 int mod2 = 0;
00073 switch (damage.type)
00074 {
00075 case DAMAGE_PHYSICAL:
00076 mod2 = getModifiedAttribute(BASE_ATTR_PHY_RES);
00077 HPloss = HPloss - mod2;
00078 break;
00079 case DAMAGE_MAGICAL:
00080 mod2 = getModifiedAttribute(BASE_ATTR_MAG_RES);
00081 HPloss = HPloss / (mod2 + 1);
00082 break;
00083 default:
00084 break;
00085 }
00086
00087 if (HPloss < 0) HPloss = 0;
00088
00089 mHitsTaken.push_back(HPloss);
00090 Attribute &HP = mAttributes[BASE_ATTR_HP];
00091 LOG_DEBUG("Being " << getPublicID() << " suffered "<<HPloss<<" damage. HP: "<<HP.base + HP.mod<<"/"<<HP.base);
00092 HP.mod -= HPloss;
00093 if (HPloss != 0) modifiedAttribute(BASE_ATTR_HP);
00094
00095 return HPloss;
00096 }
00097
00098 void Being::died()
00099 {
00100 if (mAction == DEAD)
00101 return;
00102
00103 LOG_DEBUG("Being " << getPublicID() << " died.");
00104 setAction(DEAD);
00105
00106 clearDestination();
00107
00108 for (Listeners::iterator i = mListeners.begin(),
00109 i_end = mListeners.end(); i != i_end;)
00110 {
00111 const EventListener &l = **i;
00112 ++i;
00113 if (l.dispatch->died) l.dispatch->died(&l, this);
00114 }
00115 }
00116
00117 void Being::setDestination(const Point &dst)
00118 {
00119 mDst = dst;
00120 raiseUpdateFlags(UPDATEFLAG_NEW_DESTINATION);
00121 mPath.clear();
00122 }
00123
00124 void Being::move()
00125 {
00126 mOld = getPosition();
00127
00128 if (mActionTime > 100)
00129 {
00130
00131 mActionTime -= 100;
00132 return;
00133 }
00134
00135 int tileSX = mOld.x / 32, tileSY = mOld.y / 32;
00136 int tileDX = mDst.x / 32, tileDY = mDst.y / 32;
00137 if (tileSX == tileDX && tileSY == tileDY)
00138 {
00139
00140 setPosition(mDst);
00141 mActionTime = 0;
00142 return;
00143 }
00144
00145 Map *map = getMap()->getMap();
00146
00147
00148
00149
00150
00151
00152
00153
00154 for (std::list<PATH_NODE>::iterator pathIterator = mPath.begin();
00155 pathIterator != mPath.end(); pathIterator++)
00156 {
00157 if (!map->getWalk(pathIterator->x, pathIterator->y, getWalkMask()))
00158 {
00159 mPath.clear();
00160 break;
00161 }
00162 }
00163
00164 if (mPath.empty())
00165 {
00166
00167
00168 mPath = map->findPath(tileSX, tileSY, tileDX, tileDY, getWalkMask());
00169 }
00170
00171 if (mPath.empty())
00172 {
00173
00174 mDst = mOld;
00175 mActionTime = 0;
00176 return;
00177 }
00178
00179 PATH_NODE prev(tileSX, tileSY);
00180 Point pos;
00181 do
00182 {
00183 PATH_NODE next = mPath.front();
00184 mPath.pop_front();
00185
00186 mActionTime += (prev.x != next.x && prev.y != next.y)
00187 ? mSpeed * 362 / 256 : mSpeed;
00188 if (mPath.empty())
00189 {
00190
00191 pos = mDst;
00192 break;
00193 }
00194
00195 pos.x = next.x * 32 + 16;
00196 pos.y = next.y * 32 + 16;
00197 }
00198 while (mActionTime < 100);
00199 setPosition(pos);
00200
00201 mActionTime = mActionTime > 100 ? mActionTime - 100 : 0;
00202
00203 if (mAction == WALK || mAction == STAND)
00204 {
00205 mAction = (mActionTime) ? WALK : STAND;
00206 }
00207 }
00208
00209 int Being::directionToAngle(int direction)
00210 {
00211 switch (direction)
00212 {
00213 case DIRECTION_UP: return 90;
00214 case DIRECTION_DOWN: return 270;
00215 case DIRECTION_RIGHT: return 180;
00216 case DIRECTION_LEFT:
00217 default: return 0;
00218 }
00219 }
00220
00221 void Being::performAttack(const Damage &damage, const AttackZone *attackZone)
00222 {
00223 Point ppos = getPosition();
00224 const int attackAngle = directionToAngle(getDirection());
00225
00226 std::list<Being *> victims;
00227
00228 LOG_DEBUG("Direction:"<<getDirection()<<
00229 " range:"<<attackZone->range<<
00230 " angle:"<<attackZone->angle);
00231
00232 Point attPos, attSize, defPos, defSize;
00233 if (attackZone->shape == ATTZONESHAPE_RECT)
00234 {
00235 if (getDirection() == DIRECTION_UP)
00236 {
00237 attPos.x = ppos.x - attackZone->angle;
00238 attPos.y = ppos.y - attackZone->range;
00239 attSize.x = attackZone->angle * 2;
00240 attSize.y = attackZone->range;
00241 }
00242 if (getDirection() == DIRECTION_DOWN)
00243 {
00244 attPos.x = ppos.x - attackZone->angle;
00245 attPos.y = ppos.y;
00246 attSize.x = attackZone->angle * 2;
00247 attSize.y = attackZone->range;
00248 }
00249 if (getDirection() == DIRECTION_RIGHT)
00250 {
00251 attPos.x = ppos.x;
00252 attPos.y = ppos.y - attackZone->angle;
00253 attSize.x = attackZone->range;
00254 attSize.y = attackZone->angle * 2;
00255 }
00256 if (getDirection() == DIRECTION_LEFT)
00257 {
00258 attPos.x = ppos.x - attackZone->range;
00259 attPos.y = ppos.y - attackZone->angle;
00260 attSize.x = attackZone->range;
00261 attSize.y = attackZone->angle * 2;
00262 }
00263
00264
00265
00266 Effects::show(26, getMap(), Point(attPos.x + attSize.x / 2, attPos.y + attSize.y / 2));
00267 }
00268
00269 for (BeingIterator
00270 i(getMap()->getAroundActorIterator(this, attackZone->range)); i; ++i)
00271 {
00272 Being *b = *i;
00273
00274 if (b == this)
00275 continue;
00276
00277 const ThingType type = b->getType();
00278 if (type != OBJECT_CHARACTER && type != OBJECT_MONSTER)
00279 continue;
00280
00281 if (getMap()->getPvP() == PVP_NONE &&
00282 type == OBJECT_CHARACTER &&
00283 getType() == OBJECT_CHARACTER)
00284 continue;
00285
00286 LOG_DEBUG("Attack Zone:" << attPos.x << ":" << attPos.y <<
00287 " " << attSize.x << "x" << attSize.y);
00288 LOG_DEBUG("Defender Zone:" << defPos.x << ":" << defPos.y <<
00289 " " << defSize.x << "x" << defSize.y);
00290
00291 const Point &opos = b->getPosition();
00292
00293 switch (attackZone->shape)
00294 {
00295 case ATTZONESHAPE_CONE:
00296 if (Collision::diskWithCircleSector(
00297 opos, b->getSize(),
00298 ppos, attackZone->range,
00299 attackZone->angle / 2, attackAngle)
00300 )
00301 {
00302 victims.push_back(b);
00303 }
00304 break;
00305 case ATTZONESHAPE_RECT:
00306 defPos.x = opos.x - b->getSize();
00307 defPos.y = opos.y - b->getSize();
00308 defSize.x = b->getSize() * 2;
00309 defSize.y = b->getSize() * 2;
00310 if (Collision::rectWithRect(attPos, attSize, defPos, defSize))
00311 {
00312 victims.push_back(b);
00313 }
00314 break;
00315 default:
00316 break;
00317 }
00318 }
00319
00320 if (attackZone->multiTarget)
00321 {
00322
00323 for (std::list<Being *>::iterator i = victims.begin();
00324 i != victims.end();
00325 i++)
00326 {
00327 (*i)->damage(this, damage);
00328 }
00329 }
00330 else
00331 {
00332
00333 Being* closestVictim = NULL;
00334 int closestDistance = INT_MAX;
00335 for (std::list<Being *>::iterator i = victims.begin();
00336 i != victims.end();
00337 i++)
00338 {
00339 Point opos = (*i)->getPosition();
00340 int distance = abs(opos.x - ppos.x) + abs(opos.y - ppos.y);
00341
00342
00343
00344
00345 if (distance < closestDistance)
00346 {
00347 closestVictim = (*i);
00348 closestDistance = distance;
00349 }
00350 }
00351 if (closestVictim) closestVictim->damage(this, damage);
00352 }
00353 }
00354
00355 void Being::setAction(Action action)
00356 {
00357 mAction = action;
00358 if (action != Being::ATTACK &&
00359 action != Being::WALK)
00360 {
00361 raiseUpdateFlags(UPDATEFLAG_ACTIONCHANGE);
00362 }
00363 }
00364
00365 void Being::applyModifier(int attr, int amount, int duration, int lvl)
00366 {
00367 if (duration)
00368 {
00369 AttributeModifier mod;
00370 mod.attr = attr;
00371 mod.value = amount;
00372 mod.duration = duration;
00373 mod.level = lvl;
00374 mModifiers.push_back(mod);
00375 }
00376 mAttributes[attr].mod += amount;
00377 modifiedAttribute(attr);
00378 }
00379
00380 void Being::dispellModifiers(int level)
00381 {
00382 AttributeModifiers::iterator i = mModifiers.begin();
00383 while (i != mModifiers.end())
00384 {
00385 if (i->level && i->level <= level)
00386 {
00387 mAttributes[i->attr].mod -= i->value;
00388 modifiedAttribute(i->attr);
00389 i = mModifiers.erase(i);
00390 continue;
00391 }
00392 ++i;
00393 }
00394 }
00395
00396 int Being::getModifiedAttribute(int attr) const
00397 {
00398 int res = mAttributes[attr].base + mAttributes[attr].mod;
00399 return res <= 0 ? 0 : res;
00400 }
00401
00402 void Being::update()
00403 {
00404 int oldHP = getModifiedAttribute(BASE_ATTR_HP);
00405 int newHP = oldHP;
00406 int maxHP = getAttribute(BASE_ATTR_HP);
00407
00408
00409 if (mAction != DEAD && ++mHpRegenTimer >= TICKS_PER_HP_REGENERATION)
00410 {
00411 mHpRegenTimer = 0;
00412 newHP += getModifiedAttribute(BASE_ATTR_HP_REGEN);
00413 }
00414
00415 if (newHP > maxHP)
00416 {
00417 newHP = maxHP;
00418 }
00419
00420 if (newHP != oldHP)
00421 {
00422 applyModifier(BASE_ATTR_HP, newHP - oldHP);
00423 raiseUpdateFlags(UPDATEFLAG_HEALTHCHANGE);
00424 }
00425
00426
00427 AttributeModifiers::iterator i = mModifiers.begin();
00428 while (i != mModifiers.end())
00429 {
00430 --i->duration;
00431 if (!i->duration)
00432 {
00433 mAttributes[i->attr].mod -= i->value;
00434 modifiedAttribute(i->attr);
00435 i = mModifiers.erase(i);
00436 continue;
00437 }
00438 ++i;
00439 }
00440
00441
00442 if (getModifiedAttribute(BASE_ATTR_HP) <= 0 && mAction != DEAD)
00443 {
00444 died();
00445 }
00446 }