00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "game-server/monster.hpp"
00022
00023 #include "common/configuration.hpp"
00024 #include "game-server/character.hpp"
00025 #include "game-server/collisiondetection.hpp"
00026 #include "game-server/item.hpp"
00027 #include "game-server/mapcomposite.hpp"
00028 #include "game-server/state.hpp"
00029 #include "utils/logger.h"
00030
00031 #include <cmath>
00032
00033 ItemClass *MonsterClass::getRandomDrop() const
00034 {
00035 int p = rand() / (RAND_MAX / 10000);
00036 for (MonsterDrops::const_iterator i = mDrops.begin(),
00037 i_end = mDrops.end(); i != i_end; ++i)
00038 {
00039 p -= i->probability;
00040 if (p < 0)
00041 {
00042 return i->item;
00043 }
00044 }
00045 return NULL;
00046 }
00047
00048 struct MonsterTargetEventDispatch: EventDispatch
00049 {
00050 MonsterTargetEventDispatch()
00051 {
00052 typedef EventListenerFactory< Monster, &Monster::mTargetListener > Factory;
00053 removed = &Factory::create< Thing, &Monster::forgetTarget >::function;
00054 died = &Factory::create< Thing, &Monster::forgetTarget, Being >::function;
00055 }
00056 };
00057
00058 static MonsterTargetEventDispatch monsterTargetEventDispatch;
00059
00060 Monster::Monster(MonsterClass *specy):
00061 Being(OBJECT_MONSTER),
00062 mSpecy(specy),
00063 mCountDown(0),
00064 mTargetListener(&monsterTargetEventDispatch),
00065 mOwner(NULL),
00066 mOwnerTimer(0),
00067 mAttackTime(0),
00068 mCurrentAttack(NULL)
00069 {
00070 LOG_DEBUG("Monster spawned!");
00071
00072
00073 int mutation = specy->getMutation();
00074 for (int i = BASE_ATTR_BEGIN; i < BASE_ATTR_END; i++)
00075 {
00076 float attr = (float)specy->getAttribute(i);
00077 if (mutation)
00078 {
00079 attr *= (100 + (rand()%(mutation * 2)) - mutation) / 100.0f;
00080 }
00081 setAttribute(i, (int)std::ceil(attr));
00082 }
00083
00084 setSpeed(specy->getSpeed());
00085 setSize(specy->getSize());
00086
00087
00088 int dist = specy->getAttackDistance();
00089 mAttackPositions.push_back(AttackPosition(dist, 0, DIRECTION_LEFT));
00090 mAttackPositions.push_back(AttackPosition(-dist, 0, DIRECTION_RIGHT));
00091 mAttackPositions.push_back(AttackPosition(0, dist, DIRECTION_DOWN));
00092 mAttackPositions.push_back(AttackPosition(0, -dist, DIRECTION_UP));
00093 }
00094
00095 Monster::~Monster()
00096 {
00097
00098 for (std::map<Being *, int>::iterator i = mAnger.begin(),
00099 i_end = mAnger.end(); i != i_end; ++i)
00100 {
00101 i->first->removeListener(&mTargetListener);
00102 }
00103
00104
00105 if (getMap())
00106 {
00107 Point oldP = getPosition();
00108 getMap()->getMap()->freeTile(oldP.x / 32, oldP.y / 32, getBlockType());
00109 }
00110 }
00111
00112 void Monster::perform()
00113 {
00114
00115 if (mAction == ATTACK && mCurrentAttack)
00116 {
00117 if (mAttackTime == mCurrentAttack->aftDelay)
00118 {
00119
00120 Damage damage;
00121 damage.base = (int) (getModifiedAttribute(BASE_ATTR_PHY_ATK_MIN) * mCurrentAttack->damageFactor);
00122 damage.delta = (int) (getModifiedAttribute(BASE_ATTR_PHY_ATK_DELTA) * mCurrentAttack->damageFactor);
00123 damage.cth = getModifiedAttribute(BASE_ATTR_HIT);
00124 damage.element = mCurrentAttack->element;
00125 damage.type = mCurrentAttack->type;
00126 performAttack(damage, &mCurrentAttack->attackZone);
00127 }
00128 if (!mAttackTime)
00129 {
00130 setAction(STAND);
00131 }
00132 }
00133 }
00134
00135 void Monster::update()
00136 {
00137 Being::update();
00138
00139 if (mOwner && mOwnerTimer)
00140 {
00141 mOwnerTimer--;
00142 } else {
00143 mOwner = NULL;
00144 }
00145
00146
00147 if (mAction == DEAD)
00148 {
00149 mCountDown--;
00150 if (mCountDown <= 0)
00151 {
00152 GameState::enqueueRemove(this);
00153 }
00154 return;
00155 }
00156
00157 if (mAction == ATTACK)
00158 {
00159 mAttackTime--;
00160 return;
00161 }
00162
00163
00164 Being *bestAttackTarget = NULL;
00165 int bestTargetPriority = 0;
00166 Point bestAttackPosition;
00167 Direction bestAttackDirection = DIRECTION_DOWN;
00168
00169
00170 int aroundArea = Configuration::getValue("visualRange", 320);
00171 for (BeingIterator i(getMap()->getAroundBeingIterator(this, aroundArea)); i; ++i)
00172 {
00173
00174 if ((*i)->getType() != OBJECT_CHARACTER) continue;
00175
00176 Being *target = static_cast<Being *> (*i);
00177
00178
00179 if (target->getAction() == DEAD) continue;
00180
00181
00182 int targetPriority = 0;
00183 std::map<Being *, int, std::greater<Being *> >::iterator angerIterator;
00184 angerIterator = mAnger.find(target);
00185 if (angerIterator != mAnger.end())
00186 {
00187 targetPriority = angerIterator->second;
00188 }
00189 else if (mSpecy->isAggressive())
00190 {
00191 targetPriority = 1;
00192 }
00193 else
00194 {
00195 continue;
00196 }
00197
00198
00199 for (std::list<AttackPosition>::iterator j = mAttackPositions.begin();
00200 j != mAttackPositions.end();
00201 j++)
00202 {
00203 Point attackPosition = (*i)->getPosition();
00204 attackPosition.x += (*j).x;
00205 attackPosition.y += (*j).y;
00206
00207 int posPriority = calculatePositionPriority(attackPosition,
00208 targetPriority);
00209 if (posPriority > bestTargetPriority)
00210 {
00211 bestAttackTarget = target;
00212 bestTargetPriority = posPriority;
00213 bestAttackPosition = attackPosition;
00214 bestAttackDirection = (*j).direction;
00215 }
00216 }
00217 }
00218
00219
00220 if (bestAttackTarget)
00221 {
00222
00223 MonsterAttacks allAttacks = mSpecy->getAttacks();
00224 std::map<int, MonsterAttack *> workingAttacks;
00225 int prioritySum = 0;
00226
00227 for (MonsterAttacks::iterator i = allAttacks.begin();
00228 i != allAttacks.end();
00229 i++)
00230 {
00231 const int attackAngle = directionToAngle(bestAttackDirection);
00232
00233 if (Collision::diskWithCircleSector(
00234 bestAttackTarget->getPosition(), bestAttackTarget->getSize(),
00235 getPosition(), (*i)->attackZone.range, (*i)->attackZone.angle, attackAngle))
00236 {
00237 prioritySum += (*i)->priority;
00238 workingAttacks[prioritySum] = (*i);
00239 }
00240 }
00241 if (workingAttacks.empty() || !prioritySum)
00242 {
00243 setDestination(bestAttackPosition);
00244 }
00245 else
00246 {
00247
00248 setDestination(getPosition());
00249
00250 setDirection(bestAttackDirection);
00251
00252 mCurrentAttack = workingAttacks.upper_bound(rand()%prioritySum)->second;
00253 mAttackTime = mCurrentAttack->preDelay + mCurrentAttack->aftDelay;
00254 setAction(ATTACK);
00255 raiseUpdateFlags(UPDATEFLAG_ATTACK);
00256 }
00257 }
00258 else
00259 {
00260
00261 if (getPosition() == getDestination())
00262 {
00263 mCountDown--;
00264 if (mCountDown <= 0)
00265 {
00266 unsigned range = mSpecy->getStrollRange();
00267 if (range)
00268 {
00269 Point randomPos(rand() % (range * 2 + 1) - range + getPosition().x,
00270 rand() % (range * 2 + 1) - range + getPosition().y);
00271 setDestination(randomPos);
00272 mCountDown = 10 + rand() % 10;
00273 }
00274 }
00275 }
00276 }
00277 }
00278
00279 int Monster::calculatePositionPriority(Point position, int targetPriority)
00280 {
00281 Point thisPos = getPosition();
00282
00283 unsigned range = mSpecy->getTrackRange();
00284
00285
00286 if (thisPos.x / 32 == position.x / 32 &&
00287 thisPos.y / 32 == position.y / 32)
00288 {
00289 return targetPriority *= range;
00290 }
00291
00292 std::list<PATH_NODE> path;
00293 path = getMap()->getMap()->findPath(thisPos.x / 32, thisPos.y / 32,
00294 position.x / 32, position.y / 32,
00295 getWalkMask(),
00296 range);
00297
00298 if (path.empty() || path.size() >= range)
00299 {
00300 return 0;
00301 }
00302 else
00303 {
00304 return targetPriority * (range - path.size());
00305 }
00306 }
00307
00308 void Monster::forgetTarget(Thing *t)
00309 {
00310 Being *b = static_cast< Being * >(t);
00311 mAnger.erase(b);
00312 b->removeListener(&mTargetListener);
00313
00314 if (b->getType() == OBJECT_CHARACTER)
00315 {
00316 Character *c = static_cast< Character * >(b);
00317 mExpReceivers.erase(c);
00318 mLegalExpReceivers.erase(c);
00319 }
00320 }
00321
00322 int Monster::damage(Actor *source, const Damage &damage)
00323 {
00324 int HPLoss = Being::damage(source, damage);
00325 if (HPLoss && source && source->getType() == OBJECT_CHARACTER)
00326 {
00327 Character *s = static_cast< Character * >(source);
00328 std::pair< std::map< Being *, int >::iterator, bool > ib =
00329 mAnger.insert(std::make_pair(s, HPLoss));
00330
00331 if (ib.second)
00332 {
00333 s->addListener(&mTargetListener);
00334 }
00335 else
00336 {
00337 ib.first->second += HPLoss;
00338 }
00339
00340 std::list<size_t>::const_iterator iSkill;
00341 for (iSkill = damage.usedSkills.begin(); iSkill != damage.usedSkills.end(); ++iSkill)
00342 {
00343 if (*iSkill)
00344 {
00345 mExpReceivers[s].insert(*iSkill);
00346 if (!mOwnerTimer || mOwner == s || mOwner->getParty() == s->getParty())
00347 {
00348 mOwner = s;
00349 mLegalExpReceivers.insert(s);
00350 mOwnerTimer = KILLSTEAL_PROTECTION_TIME;
00351 }
00352 }
00353 }
00354 }
00355 return HPLoss;
00356 }
00357
00358 void Monster::died()
00359 {
00360 if (mAction == DEAD) return;
00361
00362 Being::died();
00363 mCountDown = 50;
00364
00365
00366 if (ItemClass *drop = mSpecy->getRandomDrop())
00367 {
00368 Item *item = new Item(drop, 1);
00369 item->setMap(getMap());
00370 item->setPosition(getPosition());
00371 GameState::enqueueInsert(item);
00372 }
00373
00374
00375 if (mExpReceivers.size() > 0)
00376 {
00377 std::map<Character *, std::set <size_t> > ::iterator iChar;
00378 std::set<size_t>::iterator iSkill;
00379
00380
00381 float expPerChar = (float)mSpecy->getExp() / mExpReceivers.size();
00382
00383 for (iChar = mExpReceivers.begin(); iChar != mExpReceivers.end(); iChar++)
00384 {
00385 Character *character = (*iChar).first;
00386 std::set<size_t> *skillSet = &(*iChar).second;
00387
00388 if (mLegalExpReceivers.find(character) == mLegalExpReceivers.end()
00389 || skillSet->size() < 1)
00390 {
00391 continue;
00392 }
00393 int expPerSkill = int(expPerChar / skillSet->size());
00394 for (iSkill = skillSet->begin(); iSkill != skillSet->end(); iSkill++)
00395 {
00396 character->receiveExperience(*iSkill, expPerSkill);
00397 }
00398 }
00399 }
00400 }
00401