/** * Check state changes related to movement */ void BehaviorStandard::checkMove() { // dying enemies can't move if (e->stats.cur_state == ENEMY_DEAD || e->stats.cur_state == ENEMY_CRITDEAD) return; // stunned enemies can't act if (e->stats.effects.stun) return; // handle not being in combat and (not patrolling waypoints or waiting at waypoint) if (!e->stats.hero_ally && !e->stats.in_combat && (e->stats.waypoints.empty() || e->stats.waypoint_pause_ticks > 0) && (!e->stats.wander || e->stats.wander_pause_ticks > 0)) { if (e->stats.cur_state == ENEMY_MOVE) { e->newState(ENEMY_STANCE); } // currently enemies only move while in combat or patrolling return; } // clear current space to allow correct movement e->map->collider.unblock(e->stats.pos.x, e->stats.pos.y); // update direction if (e->stats.facing) { if (++e->stats.turn_ticks > e->stats.turn_delay) { // if blocked, face in pathfinder direction instead if (!e->map->collider.line_of_movement(e->stats.pos.x, e->stats.pos.y, e->stats.hero_pos.x, e->stats.hero_pos.y, e->stats.movement_type)) { // if a path is returned, target first waypoint std::vector<Point> path; e->map->collider.compute_path(e->stats.pos, pursue_pos, path, e->stats.movement_type); if(!path.empty()) pursue_pos = path.back(); } if(fleeing) e->stats.direction = calcDirection(pursue_pos, e->stats.pos); else e->stats.direction = calcDirection(e->stats.pos, pursue_pos); e->stats.turn_ticks = 0; } } // try to start moving if (e->stats.cur_state == ENEMY_STANCE) { checkMoveStateStance(); } // already moving else if (e->stats.cur_state == ENEMY_MOVE) { checkMoveStateMove(); } // if patrolling waypoints and has reached a waypoint, cycle to the next one if (!e->stats.waypoints.empty()) { Point waypoint = e->stats.waypoints.front(); Point pos = e->stats.pos; // if the patroller is close to the waypoint if (abs(waypoint.x - pos.x) < UNITS_PER_TILE/2 && abs(waypoint.y - pos.y) < UNITS_PER_TILE/2) { e->stats.waypoints.pop(); e->stats.waypoints.push(waypoint); e->stats.waypoint_pause_ticks = e->stats.waypoint_pause; } } // if a wandering enemy reaches its destination early, reset wander_ticks if (e->stats.wander) { Point pos = e->stats.pos; if (abs(pursue_pos.x - pos.x) < UNITS_PER_TILE/2 && abs(pursue_pos.y - pos.y) < UNITS_PER_TILE/2) { e->stats.wander_ticks = 0; } if (e->stats.wander_ticks == 0 && e->stats.wander_pause_ticks == 0) { e->stats.wander_pause_ticks = rand() % 60; } } // re-block current space to allow correct movement e->map->collider.block(e->stats.pos.x, e->stats.pos.y, e->stats.hero_ally); }
////////////////////////////////////////////////////////////////////////////// // 뱀파이어 타일 핸들러 ////////////////////////////////////////////////////////////////////////////// void BloodyBreaker::execute(Vampire* pVampire, ZoneCoord_t X, ZoneCoord_t Y, VampireSkillSlot* pVampireSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << "begin " << endl; SkillType_t SkillType = getSkillType(); // Knowledge of Blood 가 있다면 hit bonus 10 int HitBonus = 0; if (pVampire->hasRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_BLOOD ) ) { RankBonus* pRankBonus = pVampire->getRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_BLOOD); Assert(pRankBonus != NULL); HitBonus = pRankBonus->getPoint(); } try { SkillInput input(pVampire); SkillOutput output; computeOutput(input, output); Dir_t Dir = getDirectionToPosition(pVampire->getX(), pVampire->getY(), X, Y); // 강제로 knockback시킬 확률 // bool bForceKnockback = rand()%100 < output.ToHit; Player* pPlayer = pVampire->getPlayer(); Zone* pZone = pVampire->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); VSRect rect(1, 1, pZone->getWidth()-2, pZone->getHeight()-2); if (!rect.ptInRect(X, Y )) { executeSkillFailException(pVampire, SkillType); return; } GCSkillToTileOK1 _GCSkillToTileOK1; GCSkillToTileOK2 _GCSkillToTileOK2; // GCSkillToTileOK3 _GCSkillToTileOK3; // GCSkillToTileOK4 _GCSkillToTileOK4; GCSkillToTileOK5 _GCSkillToTileOK5; // GCSkillToTileOK6 _GCSkillToTileOK6; SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); int RequiredMP = decreaseConsumeMP(pVampire, pSkillInfo); bool bManaCheck = hasEnoughMana(pVampire, RequiredMP); bool bTimeCheck = verifyRunTime(pVampireSkillSlot); bool bRangeCheck = verifyDistance(pVampire, X, Y, pSkillInfo->getRange()); if (bManaCheck && bTimeCheck && bRangeCheck ) { // 마나를 떨어뜨린다. decreaseMana(pVampire, RequiredMP, _GCSkillToTileOK1); // 좌표와 방향을 구한다. ZoneCoord_t myX = pVampire->getX(); ZoneCoord_t myY = pVampire->getY(); Dir_t dir = calcDirection(myX, myY, X, Y); list<Creature*> cList; // knockback 때문에 recursive 하게 데미지를 먹는 경우가 있다. // 그래서 제일 먼쪽에 있는 마스크부터 체크한다. for (int i = 21; i >= 0; i-- ) { int tileX = myX + m_pBloodyBreakerMask[Dir][i].x; int tileY = myY + m_pBloodyBreakerMask[Dir][i].y; // 현재 타일이 존 내부이고, 안전지대가 아니라면 맞을 가능성이 있다. if (rect.ptInRect(tileX, tileY)) { // 타일을 받아온다. Tile& tile = pZone->getTile(tileX, tileY); list<Creature*> targetList; if (tile.hasCreature(Creature::MOVE_MODE_WALKING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_WALKING); targetList.push_back(pCreature); } if (tile.hasCreature(Creature::MOVE_MODE_FLYING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_FLYING); targetList.push_back(pCreature); } if (tile.hasCreature(Creature::MOVE_MODE_BURROWING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_BURROWING); targetList.push_back(pCreature); } list<Creature*>::iterator itr = targetList.begin(); for(; itr != targetList.end(); itr++) { Creature* pTargetCreature = (*itr); Assert(pTargetCreature != NULL); if (!canAttack(pVampire, pTargetCreature ) || pTargetCreature->isFlag(Effect::EFFECT_CLASS_COMA) ) { continue; } if (pTargetCreature != pVampire) { bool bPK = verifyPK(pVampire, pTargetCreature); bool bRaceCheck = pTargetCreature->isSlayer() || pTargetCreature->isMonster() || pTargetCreature->isOusters(); bool bZoneLevelCheck = checkZoneLevelToHitTarget(pTargetCreature); bool bHitRoll = false;//HitRoll::isSuccessMagic(pVampire, pSkillInfo, pVampireSkillSlot, HitBonus); int EnemyLevel = 0; if (pTargetCreature->isSlayer() ) { Slayer* pSlayer = dynamic_cast<Slayer*>(pTargetCreature); EnemyLevel = pSlayer->getHighestSkillDomainLevel(); } else if (pTargetCreature->isOusters() ) { Ousters* pOusters = dynamic_cast<Ousters*>(pTargetCreature); EnemyLevel = pOusters->getLevel(); } else if (pTargetCreature->isMonster() ) { Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); EnemyLevel = pMonster->getLevel(); } // min : 20, max : 100 int hitRatio = max(20, 50 + pVampire->getLevel() - EnemyLevel + HitBonus); bHitRoll = (rand()%100) < hitRatio; if (bPK && bRaceCheck && bZoneLevelCheck && bHitRoll) { Damage_t Damage = 0; bool bForceKnockback = rand() & 1; Damage += computeMagicDamage(pTargetCreature, output.Damage, SkillType, true, pVampire); ObjectID_t targetObjectID = pTargetCreature->getObjectID(); cList.push_back(pTargetCreature); _GCSkillToTileOK1.addCListElement(targetObjectID); _GCSkillToTileOK2.addCListElement(targetObjectID); _GCSkillToTileOK5.addCListElement(targetObjectID); // 일단 맞는 놈이 받을 패킷은 널 상태로 한 채로, 데미지를 준다. setDamage(pTargetCreature, Damage, pVampire, SkillType, NULL, &_GCSkillToTileOK1); computeAlignmentChange(pTargetCreature, Damage, pVampire, NULL, &_GCSkillToTileOK1); increaseAlignment(pVampire, pTargetCreature, _GCSkillToTileOK1); // 크리티컬 히트라면 상대방을 뒤로 물러나게 한다. if (bForceKnockback) { knockbackCreature(pZone, pTargetCreature, pVampire->getX(), pVampire->getY()); } if (pTargetCreature->isDead()) { int exp = computeCreatureExp(pTargetCreature, KILL_EXP); shareVampExp(pVampire, exp, _GCSkillToTileOK1); } } } } } } // 공격자의 아이템 내구성을 떨어뜨린다. decreaseDurability(pVampire, NULL, pSkillInfo, &_GCSkillToTileOK1, NULL); _GCSkillToTileOK1.setSkillType(SkillType); _GCSkillToTileOK1.setCEffectID(0); _GCSkillToTileOK1.setX(X); _GCSkillToTileOK1.setY(Y); _GCSkillToTileOK1.setRange(dir); _GCSkillToTileOK1.setDuration(0); _GCSkillToTileOK2.setObjectID(pVampire->getObjectID()); _GCSkillToTileOK2.setSkillType(SkillType); _GCSkillToTileOK2.setX(X); _GCSkillToTileOK2.setY(Y); _GCSkillToTileOK2.setRange(dir); _GCSkillToTileOK2.setDuration(0); _GCSkillToTileOK5.setObjectID(pVampire->getObjectID()); _GCSkillToTileOK5.setSkillType(SkillType); _GCSkillToTileOK5.setX(X); _GCSkillToTileOK5.setY(Y); _GCSkillToTileOK5.setRange(dir); _GCSkillToTileOK5.setDuration(0); pPlayer->sendPacket(&_GCSkillToTileOK1); // 이 기술에 의해 영향을 받는 놈들에게 패킷을 보내줘야 한다. for (list<Creature*>::const_iterator itr = cList.begin(); itr != cList.end(); itr++) { Creature * pTargetCreature = *itr; Assert(pTargetCreature != NULL); if (pTargetCreature->isPC()) { _GCSkillToTileOK2.clearList(); // HP의 변경사항을 패킷에다 기록한다. HP_t targetHP = 0; if (pTargetCreature->isSlayer()) { targetHP = (dynamic_cast<Slayer*>(pTargetCreature))->getHP(ATTR_CURRENT); } else if (pTargetCreature->isVampire()) { targetHP = (dynamic_cast<Vampire*>(pTargetCreature))->getHP(ATTR_CURRENT); } else if (pTargetCreature->isOusters()) { targetHP = (dynamic_cast<Ousters*>(pTargetCreature))->getHP(ATTR_CURRENT); } _GCSkillToTileOK2.addShortData(MODIFY_CURRENT_HP, targetHP); // 아이템의 내구력을 떨어뜨린다. decreaseDurability(NULL, pTargetCreature, pSkillInfo, NULL, &_GCSkillToTileOK2); // 패킷을 보내준다. pTargetCreature->getPlayer()->sendPacket(&_GCSkillToTileOK2); } else if (pTargetCreature->isMonster()) { // 당근 적으로 인식한다. Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); pMonster->addEnemy(pVampire); } } cList.push_back(pVampire); pZone->broadcastPacket(myX, myY, &_GCSkillToTileOK5 , cList); // set Next Run Time pVampireSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pVampire, SkillType, NULL); } } catch (Throwable & t) { executeSkillFailException(pVampire, SkillType); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end " << endl; __END_CATCH }
void Avatar::handlePower(std::vector<ActionData> &action_queue) { bool blocking = false; for (unsigned i=0; i<action_queue.size(); i++) { ActionData &action = action_queue[i]; const Power &power = powers->getPower(action.power); if (power.new_state == POWSTATE_BLOCK) blocking = true; if (action.power != 0 && (stats.cooldown_ticks == 0 || action.instant_item)) { FPoint target = action.target; // check requirements if ((stats.cur_state == AVATAR_ATTACK || stats.cur_state == AVATAR_HIT) && !action.instant_item) continue; if (!stats.canUsePower(power, action.power)) continue; if (power.requires_los && !mapr->collider.line_of_sight(stats.pos.x, stats.pos.y, target.x, target.y)) continue; if (power.requires_empty_target && !mapr->collider.is_empty(target.x, target.y)) continue; if (hero_cooldown[action.power] > 0) continue; if (!powers->hasValidTarget(action.power, &stats, target)) continue; // automatically target the selected enemy with melee attacks if (power.type == POWTYPE_FIXED && power.starting_pos == STARTING_POS_MELEE && enemy_pos.x != -1 && enemy_pos.y != -1) { target = enemy_pos; } // draw a target on the ground if we're attacking if (!power.buff && !power.buff_teleport && power.type != POWTYPE_TRANSFORM && power.new_state != POWSTATE_BLOCK) { if (target_anim) { target_pos = target; target_visible = true; target_anim->reset(); } lock_cursor = true; } else { curs->setCursor(CURSOR_NORMAL); } if (power.new_state != POWSTATE_INSTANT) { current_power = action.power; act_target = target; attack_anim = power.attack_anim; } // is this a power that requires changing direction? if (power.face) { stats.direction = calcDirection(stats.pos, target); } switch (power.new_state) { case POWSTATE_ATTACK: // handle attack powers stats.cur_state = AVATAR_ATTACK; break; case POWSTATE_BLOCK: // handle blocking stats.cur_state = AVATAR_BLOCK; powers->activate(action.power, &stats, target); hero_cooldown[action.power] = power.cooldown; stats.refresh_stats = true; break; case POWSTATE_INSTANT: // handle instant powers powers->activate(action.power, &stats, target); hero_cooldown[action.power] = power.cooldown; break; } } } stats.blocking = blocking; }
////////////////////////////////////////////////////////////////////// // // ThunderFlash::execute() // ////////////////////////////////////////////////////////////////////// void ThunderFlash::execute(Slayer* pSlayer, ZoneCoord_t X, ZoneCoord_t Y, SkillSlot* pSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " begin" << endl; Assert(pSlayer != NULL); Assert(pSkillSlot != NULL); try { Player* pPlayer = pSlayer->getPlayer(); Zone* pZone = pSlayer->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); // 무장하고 있는 무기가 널이거나, SWORD가 아니라면 사용할 수 없다. Item* pItem = pSlayer->getWearItem(Slayer::WEAR_RIGHTHAND); if (pItem == NULL || pItem->getItemClass() != Item::ITEM_CLASS_SWORD) { executeSkillFailException(pSlayer, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end" << endl; return; } bool bIncreaseExp = pSlayer->isRealWearingEx(Slayer::WEAR_RIGHTHAND); GCSkillToTileOK1 _GCSkillToTileOK1; GCSkillToTileOK2 _GCSkillToTileOK2; GCSkillToTileOK5 _GCSkillToTileOK5; SkillType_t SkillType = pSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); SkillDomainType_t DomainType = pSkillInfo->getDomainType(); SkillLevel_t SkillLevel = pSkillSlot->getExpLevel(); int RequiredMP = (int)pSkillInfo->getConsumeMP(); bool bManaCheck = hasEnoughMana(pSlayer, RequiredMP); bool bTimeCheck = verifyRunTime(pSkillSlot); bool bRangeCheck = verifyDistance(pSlayer, X, Y, pSkillInfo->getRange()); // 마나가 있어야 하고, 시간과 거리 체크에 성공하고, if (bManaCheck && bTimeCheck && bRangeCheck) { // MP를 떨어뜨린다. decreaseMana(pSlayer, RequiredMP, _GCSkillToTileOK1); // 좌표와 방향을 구한다. ZoneCoord_t myX = pSlayer->getX(); ZoneCoord_t myY = pSlayer->getY(); Dir_t dir = calcDirection(myX, myY, X, Y); SkillInput input(pSlayer, pSkillSlot); SkillOutput output; computeOutput(input, output); Damage_t SkillDamage = output.Damage; Damage_t Damage = 0; bool bHit = false; Level_t maxEnemyLevel = 0; uint EnemyNum = 0; VSRect rect(1, 1, pZone->getWidth()-2, pZone->getHeight()-2); list<Creature*> cList; for (int count=0; count<4; count++) { int tileX = X + m_pThunderFlashMask[count].x; int tileY = Y + m_pThunderFlashMask[count].y; // 현재 타일이 존 내부이고, 안전지대가 아니라면, 맞을 확률이 있다. if (rect.ptInRect(tileX, tileY)) { // 타일을 받아온다. Tile& tile = pZone->getTile(tileX, tileY); list<Creature*> targetList; if (tile.hasCreature(Creature::MOVE_MODE_WALKING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_WALKING); targetList.push_back(pCreature); } list<Creature*>::iterator itr = targetList.begin(); for(; itr != targetList.end(); itr++) { Creature* pTargetCreature = (*itr); Assert(pTargetCreature != NULL); bool bMoveModeCheck = (pTargetCreature->getMoveMode() == Creature::MOVE_MODE_WALKING) ? true : false; bool bHitRoll = HitRoll::isSuccess(pSlayer, pTargetCreature, SkillLevel/2); //bool bCanHit = canHit(pSlayer, pTargetCreature, SkillType); bool bCanHit = true; bool bPK = verifyPK(pSlayer, pTargetCreature); bool bRaceCheck = pTargetCreature->isSlayer() || pTargetCreature->isNPC(); bool bZoneLevelCheck = checkZoneLevelToHitTarget(pTargetCreature); if (bMoveModeCheck && bHitRoll && bCanHit && bPK && !bRaceCheck && bZoneLevelCheck) { CheckCrossCounter(pSlayer, pTargetCreature, Damage, pSkillInfo->getRange()); bool bCriticalHit = false; Damage = computeDamage(pSlayer, pTargetCreature, SkillLevel/5, bCriticalHit) + SkillDamage; ObjectID_t targetObjectID = pTargetCreature->getObjectID(); cList.push_back(pTargetCreature); _GCSkillToTileOK1.addCListElement(targetObjectID); _GCSkillToTileOK2.addCListElement(targetObjectID); _GCSkillToTileOK5.addCListElement(targetObjectID); // 일단 맞는 놈이 받을 패킷은 널 상태로 한 채로, 데미지를 준다. setDamage(pTargetCreature, Damage, pSlayer, SkillType, NULL, &_GCSkillToTileOK1); computeAlignmentChange(pTargetCreature, Damage, pSlayer, NULL, &_GCSkillToTileOK1); increaseAlignment(pSlayer, pTargetCreature, _GCSkillToTileOK1); // 크리티컬 히트라면 상대방을 뒤로 물러나게 한다. if (bCriticalHit) { knockbackCreature(pZone, pTargetCreature, pSlayer->getX(), pSlayer->getY()); } // 슬레이어가 아닐 경우에만 맞은 것으로 간주한다. if (!pTargetCreature->isSlayer()) { bHit = true; if (maxEnemyLevel < pTargetCreature->getLevel() ) maxEnemyLevel = pTargetCreature->getLevel(); EnemyNum++; } } } //for (; itr != objectList.end(); itr++) } // if (rect.ptInRect(tileX, tileY) && ... } // for (int count=0; count<3; count++) if (bHit) { if (bIncreaseExp) { shareAttrExp(pSlayer, Damage , 8, 1, 1, _GCSkillToTileOK1); increaseDomainExp(pSlayer, DomainType, pSkillInfo->getPoint(), _GCSkillToTileOK1, maxEnemyLevel, EnemyNum); increaseSkillExp(pSlayer, DomainType, pSkillSlot, pSkillInfo, _GCSkillToTileOK1); } } // 공격자 아이템 내구성 떨어트림. decreaseDurability(pSlayer, NULL, pSkillInfo, &_GCSkillToTileOK1, NULL); _GCSkillToTileOK1.setSkillType(SkillType); _GCSkillToTileOK1.setCEffectID(CEffectID); _GCSkillToTileOK1.setX(X); _GCSkillToTileOK1.setY(Y); _GCSkillToTileOK1.setRange(dir); _GCSkillToTileOK1.setDuration(0); _GCSkillToTileOK2.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK2.setSkillType(SkillType); _GCSkillToTileOK2.setX(X); _GCSkillToTileOK2.setY(Y); _GCSkillToTileOK2.setRange(dir); _GCSkillToTileOK2.setDuration(0); _GCSkillToTileOK5.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK5.setSkillType(SkillType); _GCSkillToTileOK5.setX(X); _GCSkillToTileOK5.setY(Y); _GCSkillToTileOK5.setRange(dir); _GCSkillToTileOK5.setDuration(0); pPlayer->sendPacket(&_GCSkillToTileOK1); // 이 기술에 의해 영향을 받는 놈들에게 패킷을 보내줘야 한다. for (list<Creature*>::const_iterator itr = cList.begin(); itr != cList.end(); itr++) { Creature * pTargetCreature = *itr; Assert(pTargetCreature != NULL); if (pTargetCreature->isPC()) { _GCSkillToTileOK2.clearList(); // HP의 변경사항을 패킷에다 기록한다. HP_t targetHP = 0; if (pTargetCreature->isSlayer()) targetHP = (dynamic_cast<Slayer*>(pTargetCreature))->getHP(ATTR_CURRENT); else if (pTargetCreature->isVampire()) targetHP = (dynamic_cast<Vampire*>(pTargetCreature))->getHP(ATTR_CURRENT); _GCSkillToTileOK2.addShortData(MODIFY_CURRENT_HP, targetHP); // 아이템의 내구력을 떨어뜨린다. decreaseDurability(NULL, pTargetCreature, pSkillInfo, NULL, &_GCSkillToTileOK2); // 패킷을 보내준다. pTargetCreature->getPlayer()->sendPacket(&_GCSkillToTileOK2); } else if (pTargetCreature->isMonster()) { // 당근 적으로 인식한다. Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); pMonster->addEnemy(pSlayer); } } cList.push_back(pSlayer); pZone->broadcastPacket(myX, myY, &_GCSkillToTileOK5 , cList); // set Next Run Time pSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pSlayer, getSkillType(), NULL); } } catch (Throwable & t) { executeSkillFailException(pSlayer, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end" << endl; __END_CATCH }
unsigned char calcDirection(const FPoint &src, const FPoint &dst) { return calcDirection(src.x, src.y, dst.x, dst.y); }
/** * Check state changes related to movement */ void BehaviorStandard::checkMove() { // dying enemies can't move if (e->stats.cur_state == ENEMY_DEAD || e->stats.cur_state == ENEMY_CRITDEAD) return; // stunned enemies can't act if (e->stats.effects.stun) return; // handle not being in combat and (not patrolling waypoints or waiting at waypoint) if (!e->stats.hero_ally && !e->stats.in_combat && (e->stats.waypoints.empty() || e->stats.waypoint_pause_ticks > 0) && (!e->stats.wander || e->stats.wander_pause_ticks > 0)) { if (e->stats.cur_state == ENEMY_MOVE) { e->newState(ENEMY_STANCE); } // currently enemies only move while in combat or patrolling return; } // clear current space to allow correct movement mapr->collider.unblock(e->stats.pos.x, e->stats.pos.y); // update direction if (e->stats.facing) { if (++e->stats.turn_ticks > e->stats.turn_delay) { // if blocked, face in pathfinder direction instead if (!mapr->collider.line_of_movement(e->stats.pos.x, e->stats.pos.y, pc->stats.pos.x, pc->stats.pos.y, e->stats.movement_type)) { // if a path is returned, target first waypoint bool recalculate_path = false; //if theres no path, it needs to be calculated if(path.empty()) recalculate_path = true; //if the target moved more than 1 tile away, recalculate if(calcDist(map_to_collision(prev_target), map_to_collision(pursue_pos)) > 1) recalculate_path = true; //if a collision ocurred then recalculate if(collided) recalculate_path = true; //add a 5% chance to recalculate on every frame. This prevents reclaulating lots of entities in the same frame chance_calc_path += 5; if(percentChance(chance_calc_path)) recalculate_path = true; //dont recalculate if we were blocked and no path was found last time //this makes sure that pathfinding calculation is not spammed when the target is unreachable and the entity is as close as its going to get if(!path_found && collided && !percentChance(chance_calc_path)) recalculate_path = false; else//reset the collision flag only if we dont want the cooldown in place collided = false; prev_target = pursue_pos; // target first waypoint if(recalculate_path) { chance_calc_path = -100; path.clear(); path_found = mapr->collider.compute_path(e->stats.pos, pursue_pos, path, e->stats.movement_type); } if(!path.empty()) { pursue_pos = path.back(); //if distance to node is lower than a tile size, the node is going to be passed and can be removed if(calcDist(e->stats.pos, pursue_pos) <= 64) path.pop_back(); } } else { path.clear(); } if(fleeing) e->stats.direction = calcDirection(pursue_pos, e->stats.pos); else e->stats.direction = calcDirection(e->stats.pos, pursue_pos); e->stats.turn_ticks = 0; } } // try to start moving if (e->stats.cur_state == ENEMY_STANCE) { checkMoveStateStance(); } // already moving else if (e->stats.cur_state == ENEMY_MOVE) { checkMoveStateMove(); } // if patrolling waypoints and has reached a waypoint, cycle to the next one if (!e->stats.waypoints.empty()) { FPoint waypoint = e->stats.waypoints.front(); FPoint pos = e->stats.pos; // if the patroller is close to the waypoint if (fabs(waypoint.x - pos.x) < 0.5 && fabs(waypoint.y - pos.y) < 0.5) { e->stats.waypoints.pop(); e->stats.waypoints.push(waypoint); e->stats.waypoint_pause_ticks = e->stats.waypoint_pause; } } // if a wandering enemy reaches its destination early, reset wander_ticks if (e->stats.wander) { FPoint pos = e->stats.pos; if (fabs(pursue_pos.x - pos.x) < 0.5 && fabs(pursue_pos.y - pos.y) < 0.5) { e->stats.wander_ticks = 0; } if (e->stats.wander_ticks == 0 && e->stats.wander_pause_ticks == 0) { e->stats.wander_pause_ticks = rand() % 60; } } // re-block current space to allow correct movement mapr->collider.block(e->stats.pos.x, e->stats.pos.y, e->stats.hero_ally); }
////////////////////////////////////////////////////////////////////////////// // 슬레이어 타일 핸들러 - AR이나 SMG를 들고 있을 경우 ////////////////////////////////////////////////////////////////////////////// void MoleShot::ARSMGexecute(Slayer* pSlayer, ZoneCoord_t X, ZoneCoord_t Y, SkillSlot* pSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " SGexecute Begin" << endl; Assert(pSlayer != NULL); Assert(pSkillSlot != NULL); try { Player* pPlayer = pSlayer->getPlayer(); Zone* pZone = pSlayer->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); GCSkillToTileOK1 _GCSkillToTileOK1; GCSkillToTileOK2 _GCSkillToTileOK2; GCSkillToTileOK3 _GCSkillToTileOK3; GCSkillToTileOK4 _GCSkillToTileOK4; GCSkillToTileOK5 _GCSkillToTileOK5; SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(getSkillType()); SkillDomainType_t DomainType = pSkillInfo->getDomainType(); Level_t SkillLevel = pSkillSlot->getExpLevel(); Item* pWeapon = pSlayer->getWearItem(Slayer::WEAR_RIGHTHAND); Assert(pWeapon != NULL); bool bIncreaseExp = pSlayer->isRealWearingEx(Slayer::WEAR_RIGHTHAND); int RequiredMP = (int)pSkillInfo->getConsumeMP(); bool bManaCheck = hasEnoughMana(pSlayer, RequiredMP); bool bTimeCheck = verifyRunTime(pSkillSlot); bool bRangeCheck = verifyDistance(pSlayer, X, Y, pWeapon->getRange()); bool bBulletCheck = (getRemainBullet(pWeapon) > 0) ? true : false; // 총알 숫자는 무조건 떨어뜨린다. Bullet_t RemainBullet = 0; if (bBulletCheck) { decreaseBullet(pWeapon); // 한발쓸때마다 저장할 필요 없다. by sigi. 2002.5.9 // pWeapon->save(pSlayer->getName(), STORAGE_GEAR, 0, Slayer::WEAR_RIGHTHAND, 0); RemainBullet = getRemainBullet(pWeapon); } // 데미지, 투힛 보너스, 좌표와 방향을 구한다. int ToHitBonus = 0; int DamageBonus = 0; int ToHitPenalty = 0; int DamagePenalty = 0; ZoneCoord_t myX = pSlayer->getX(); ZoneCoord_t myY = pSlayer->getY(); Dir_t dir = calcDirection(myX, myY, X, Y); bool bHit = false; // 한번이라도 맞았는가를 저장하기 위한 변수 Damage_t Damage = 0; // 마지막으로 입힌 데미지를 저장하기 위한 변수 // AR이나 SMG일 경우에는 2부터 시작해서 4까지의 splash 데미지를 입힌다. int Splash = 1 + pSkillSlot->getExpLevel()/30 + 1; if (bManaCheck && bTimeCheck && bRangeCheck && bBulletCheck) { decreaseMana(pSlayer, RequiredMP, _GCSkillToTileOK1); SkillInput input(pSlayer, pSkillSlot); SkillOutput output; computeOutput(input, output); // 음수 값이 돌아온다. ToHitPenalty = getPercentValue(pSlayer->getToHit(), output.ToHit); list<Creature*> cList; list<Creature*> creatureList; getSplashVictims(pZone, X, Y, Creature::CREATURE_CLASS_MAX, creatureList, Splash); Level_t maxEnemyLevel = 0; uint EnemyNum = 0; list<Creature*>::iterator itr = creatureList.begin(); for (; itr != creatureList.end(); itr++) { Creature* pTargetCreature = (*itr); Assert(pTargetCreature != NULL); ToHitBonus = computeArmsWeaponToHitBonus(pWeapon, myX, myY, pTargetCreature->getX(), pTargetCreature->getY()); DamageBonus = computeArmsWeaponDamageBonus(pWeapon, myX, myY, pTargetCreature->getX(), pTargetCreature->getY()); bool bInvokerCheck = (pTargetCreature->getObjectID() == pSlayer->getObjectID()) ? true : false; bool bRaceCheck = pTargetCreature->isSlayer() || pTargetCreature->isNPC(); bool bHitRoll = HitRoll::isSuccess(pSlayer, pTargetCreature, ToHitPenalty + ToHitBonus); bool bPK = verifyPK(pSlayer, pTargetCreature); bool bZoneLevelCheck = checkZoneLevelToHitTarget(pTargetCreature); if (pTargetCreature->isFlag(Effect::EFFECT_CLASS_NO_DAMAGE ) || pTargetCreature->isFlag(Effect::EFFECT_CLASS_COMA ) ) { bHitRoll = false; } if (!bInvokerCheck && !bRaceCheck && bHitRoll && bPK && bZoneLevelCheck) { bool bCriticalHit = false; // 데미지를 계산해서 페널티를 가한다. // 보너스는 멀티샷 페널티 때문에 음수가 될 수도 있다. Damage = computeDamage(pSlayer, pTargetCreature, SkillLevel/5, bCriticalHit); DamagePenalty = getPercentValue(Damage, output.Damage); Damage = max(0, Damage + DamagePenalty + DamageBonus); // 메인 타겟을 제외하고는, 스플래시 데미지를 입는데, // 스플래시 데미지는 일반 데미지의 50%다. if (pTargetCreature->getX() != X || pTargetCreature->getY() != Y) { Damage = Damage/2; } // 소드웨이브와는 달리 크로스 카운터 체크는 하지 않는다. ObjectID_t targetObjectID = pTargetCreature->getObjectID(); cList.push_back(pTargetCreature); _GCSkillToTileOK1.addCListElement(targetObjectID); _GCSkillToTileOK2.addCListElement(targetObjectID); _GCSkillToTileOK5.addCListElement(targetObjectID); setDamage(pTargetCreature, Damage, pSlayer, getSkillType(), NULL, &_GCSkillToTileOK1); computeAlignmentChange(pTargetCreature, Damage, pSlayer, NULL, &_GCSkillToTileOK1); // 크리티컬 히트라면 상대방을 뒤로 물러나게 한다. if (bCriticalHit) { knockbackCreature(pZone, pTargetCreature, pSlayer->getX(), pSlayer->getY()); } // 슬레이어가 아닐 경우에만 맞춘 걸로 간주한다. if (!pTargetCreature->isSlayer()) { bHit = true; if (maxEnemyLevel < pTargetCreature->getLevel() ) maxEnemyLevel = pTargetCreature->getLevel(); EnemyNum++; } } } if (bHit) { if (bIncreaseExp) { increaseDomainExp(pSlayer, DomainType , pSkillInfo->getPoint(), _GCSkillToTileOK1, maxEnemyLevel, EnemyNum); shareAttrExp(pSlayer, Damage , 1, 8, 1, _GCSkillToTileOK1); } increaseSkillExp(pSlayer, DomainType, pSkillSlot, pSkillInfo, _GCSkillToTileOK1); } _GCSkillToTileOK1.addShortData(MODIFY_BULLET, RemainBullet); decreaseDurability(pSlayer, NULL, pSkillInfo, &_GCSkillToTileOK1, NULL); _GCSkillToTileOK1.setSkillType(getSkillType()); _GCSkillToTileOK1.setCEffectID(CEffectID); _GCSkillToTileOK1.setX(X); _GCSkillToTileOK1.setY(Y); _GCSkillToTileOK1.setRange(dir); _GCSkillToTileOK1.setDuration(0); _GCSkillToTileOK2.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK2.setSkillType(getSkillType()); _GCSkillToTileOK2.setX(X); _GCSkillToTileOK2.setY(Y); _GCSkillToTileOK2.setRange(dir); _GCSkillToTileOK2.setDuration(0); _GCSkillToTileOK3.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK3.setSkillType(getSkillType()); _GCSkillToTileOK3.setX(X); _GCSkillToTileOK3.setY(Y); _GCSkillToTileOK4.setSkillType(getSkillType()); _GCSkillToTileOK4.setX(X); _GCSkillToTileOK4.setY(Y); _GCSkillToTileOK4.setDuration(0); _GCSkillToTileOK4.setRange(dir); _GCSkillToTileOK5.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK5.setSkillType(getSkillType()); _GCSkillToTileOK5.setX(X); _GCSkillToTileOK5.setY(Y); _GCSkillToTileOK5.setRange(dir); _GCSkillToTileOK5.setDuration(0); pPlayer->sendPacket(&_GCSkillToTileOK1); // 이 기술에 의해 영향을 받는 놈들에게 패킷을 보내줘야 한다. for(list<Creature*>::const_iterator itr = cList.begin(); itr != cList.end(); itr++) { Creature* pTargetCreature = *itr; Assert(pTargetCreature != NULL); if (pTargetCreature->isPC()) { _GCSkillToTileOK2.clearList(); HP_t targetHP = 0; if (pTargetCreature->isSlayer()) { targetHP = (dynamic_cast<Slayer*>(pTargetCreature))->getHP(); } else if (pTargetCreature->isVampire()) { targetHP = (dynamic_cast<Vampire*>(pTargetCreature))->getHP(); } _GCSkillToTileOK2.addShortData(MODIFY_CURRENT_HP, targetHP); // 아이템의 내구력을 떨어뜨린다. decreaseDurability(NULL, pTargetCreature, pSkillInfo, NULL, &_GCSkillToTileOK2); // 패킷을 보내준다. Player* pPlayer = pTargetCreature->getPlayer(); Assert(pPlayer != NULL); pPlayer->sendPacket(&_GCSkillToTileOK2); } else if (pTargetCreature->isMonster()) { // 당근 적으로 인식한다. Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); pMonster->addEnemy(pSlayer); } } cList.push_back(pSlayer); cList = pZone->broadcastSkillPacket(myX, myY, X, Y, &_GCSkillToTileOK5, cList); pZone->broadcastPacket(myX, myY, &_GCSkillToTileOK3 , cList); pZone->broadcastPacket(X, Y, &_GCSkillToTileOK4 , cList); pSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormalWithGun(pSlayer, getSkillType(), NULL, RemainBullet); } } catch (Throwable & t) { executeSkillFailException(pSlayer, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " SGexecute End" << endl; __END_CATCH }
/** * Apply basic power info to a new hazard. * * This can be called several times to combine powers. * Typically done when a base power can be modified by equipment * (e.g. ammo type affects the traits of powers that shoot) * * @param power_index The activated power ID * @param src_stats The StatBlock of the power activator * @param target Aim position in map coordinates * @param haz A newly-initialized hazard */ void PowerManager::initHazard(int power_index, StatBlock *src_stats, Point target, Hazard *haz) { //the hazard holds the statblock of its source haz->src_stats = src_stats; // Hazard attributes based on power source haz->crit_chance = src_stats->crit; haz->accuracy = src_stats->accuracy; // Hazard damage depends on equipped weapons and the power's optional damage_multiplier if (powers[power_index].base_damage == BASE_DAMAGE_MELEE) { haz->dmg_min = src_stats->dmg_melee_min; haz->dmg_max = src_stats->dmg_melee_max; } else if (powers[power_index].base_damage == BASE_DAMAGE_RANGED) { haz->dmg_min = src_stats->dmg_ranged_min; haz->dmg_max = src_stats->dmg_ranged_max; } else if (powers[power_index].base_damage == BASE_DAMAGE_MENT) { haz->dmg_min = src_stats->dmg_ment_min; haz->dmg_max = src_stats->dmg_ment_max; } //apply the multiplier haz->dmg_min = ceil(haz->dmg_min * powers[power_index].damage_multiplier / 100.0); haz->dmg_max = ceil(haz->dmg_max * powers[power_index].damage_multiplier / 100.0); // Only apply stats from powers that are not defaults // If we do this, we can init with multiple power layers // (e.g. base spell plus weapon type) if (powers[power_index].gfx_index != -1) { haz->sprites = gfx[powers[power_index].gfx_index]; } if (powers[power_index].rendered) { haz->rendered = powers[power_index].rendered; } if (powers[power_index].lifespan != 0) { haz->lifespan = powers[power_index].lifespan; } if (powers[power_index].frame_loop != 1) { haz->frame_loop = powers[power_index].frame_loop; } if (powers[power_index].frame_duration != 1) { haz->frame_duration = powers[power_index].frame_duration; } if (powers[power_index].frame_size.x != 0) { haz->frame_size.x = powers[power_index].frame_size.x; } if (powers[power_index].frame_size.y != 0) { haz->frame_size.y = powers[power_index].frame_size.y; } if (powers[power_index].frame_offset.x != 0) { haz->frame_offset.x = powers[power_index].frame_offset.x; } if (powers[power_index].frame_offset.y != 0) { haz->frame_offset.y = powers[power_index].frame_offset.y; } if (powers[power_index].directional) { haz->direction = calcDirection(src_stats->pos.x, src_stats->pos.y, target.x, target.y); } else if (powers[power_index].visual_random != 0) { haz->visual_option = rand() % powers[power_index].visual_random; } else if (powers[power_index].visual_option != 0) { haz->visual_option = powers[power_index].visual_option; } haz->floor = powers[power_index].floor; if (powers[power_index].speed > 0) { haz->base_speed = powers[power_index].speed; } if (powers[power_index].complete_animation) { haz->complete_animation = true; } // combat traits if (powers[power_index].no_attack) { haz->active = false; } if (powers[power_index].multitarget) { haz->multitarget = true; } if (powers[power_index].active_frame != -1) { haz->active_frame = powers[power_index].active_frame; } if (powers[power_index].radius != 0) { haz->radius = powers[power_index].radius; } if (powers[power_index].trait_armor_penetration) { haz->trait_armor_penetration = true; } haz->trait_crits_impaired = powers[power_index].trait_crits_impaired; if (powers[power_index].trait_elemental) { haz->trait_elemental = powers[power_index].trait_elemental; } // status effect durations // durations stack when combining powers (e.g. base power and weapon/ammo type) haz->bleed_duration += powers[power_index].bleed_duration; haz->stun_duration += powers[power_index].stun_duration; haz->slow_duration += powers[power_index].slow_duration; haz->immobilize_duration += powers[power_index].immobilize_duration; // steal effects haz->hp_steal += powers[power_index].hp_steal; haz->mp_steal += powers[power_index].mp_steal; // hazard starting position if (powers[power_index].starting_pos == STARTING_POS_SOURCE) { haz->pos.x = (float)src_stats->pos.x; haz->pos.y = (float)src_stats->pos.y; } else if (powers[power_index].starting_pos == STARTING_POS_TARGET) { haz->pos.x = (float)target.x; haz->pos.y = (float)target.y; } else if (powers[power_index].starting_pos == STARTING_POS_MELEE) { haz->pos = calcVector(src_stats->pos, src_stats->direction, src_stats->melee_range); } // pre/post power effects if (powers[power_index].post_power != -1) { haz->post_power = powers[power_index].post_power; } if (powers[power_index].wall_power != -1) { haz->wall_power = powers[power_index].wall_power; } // if equipment has special powers, apply it here (if it hasn't already been applied) if (!haz->equipment_modified && powers[power_index].allow_power_mod) { if (powers[power_index].base_damage == BASE_DAMAGE_MELEE && src_stats->melee_weapon_power != -1) { haz->equipment_modified = true; initHazard(src_stats->melee_weapon_power, src_stats, target, haz); } else if (powers[power_index].base_damage == BASE_DAMAGE_MENT && src_stats->mental_weapon_power != -1) { haz->equipment_modified = true; initHazard(src_stats->mental_weapon_power, src_stats, target, haz); } else if (powers[power_index].base_damage == BASE_DAMAGE_RANGED && src_stats->ranged_weapon_power != -1) { haz->equipment_modified = true; initHazard(src_stats->ranged_weapon_power, src_stats, target, haz); } } }
void Creep::update(float deltaTime) { m_fStateTime += deltaTime; m_healthBarFrame = m_health * 16 / m_maxHealth - 1; m_healthBarFrame = m_healthBarFrame < 0 ? 0 : m_healthBarFrame; m_timeToShowHealthBar -= deltaTime; if (FlagUtil::isFlagSet(m_creepCondition, ELECTRIFIED)) { if (m_electrifiedTime > 0) { m_electrifiedTime -= deltaTime; } else { m_creepCondition = FlagUtil::removeFlag(m_creepCondition, ELECTRIFIED); m_speed = m_initialSpeed; if (FlagUtil::isFlagSet(m_creepCondition, FROZEN)) { m_speed /= 3; } } } if (FlagUtil::isFlagSet(m_creepCondition, POISONED)) { if (m_poisonedTime > 0) { bool isFrozen = FlagUtil::isFlagSet(m_creepCondition, FROZEN); m_poisonedTime -= deltaTime; m_poisonTime += deltaTime; while (m_poisonTime > TIME_FOR_POISON_DAMAGE) { m_poisonTime -= TIME_FOR_POISON_DAMAGE; takeDamage(m_poisonDamage, Damage_Type::ACID); } if (m_isGrowingDueToPoison) { m_fWidth += deltaTime * (isFrozen ? 0.1f : 0.5f); m_fHeight += deltaTime * (isFrozen ? 0.1f : 0.5f); if (m_fWidth > m_maxSize) { m_fWidth = m_maxSize; m_fHeight = m_maxSize; m_isGrowingDueToPoison = false; } } else { m_fWidth -= deltaTime * (isFrozen ? 0.1f : 0.5f); m_fHeight -= deltaTime * (isFrozen ? 0.1f : 0.5f); if (m_fWidth < m_halfSize) { m_fWidth = m_halfSize; m_fHeight = m_halfSize; m_isGrowingDueToPoison = true; } } } else { m_creepCondition = FlagUtil::removeFlag(m_creepCondition, POISONED); m_fWidth = m_defaultSize; m_fHeight = m_defaultSize; } resetBounds(m_fWidth * SIZE_TO_BOUNDS_RATIO, m_fHeight * SIZE_TO_BOUNDS_RATIO); } if (FlagUtil::isFlagSet(m_creepCondition, FROZEN)) { if (m_frozenTime > 0) { m_frozenTime -= deltaTime; m_fRed += m_frozenRecoveryRate * deltaTime; m_fGreen = m_fRed; } else { thaw(); } } else if (FlagUtil::isFlagSet(m_creepCondition, ON_FIRE)) { if (m_burnTime > 0) { m_burnTime -= deltaTime; } else { m_creepCondition = FlagUtil::removeFlag(m_creepCondition, ON_FIRE); m_creepCondition = FlagUtil::setFlag(m_creepCondition, FIRE_RECOVERY); } } else if (FlagUtil::isFlagSet(m_creepCondition, FIRE_RECOVERY)) { m_fRed += 1.5f * deltaTime; m_fGreen = m_fRed; m_fBlue = m_fRed; if (m_fRed > 1) { m_creepCondition = FlagUtil::removeFlag(m_creepCondition, FIRE_RECOVERY); resetColor(); } } m_deltaX = m_velocity->getX() * deltaTime; m_deltaY = m_velocity->getY() * deltaTime; m_position->add(m_deltaX, m_deltaY); m_direction = calcDirection(); updateBounds(); }
void Gps::update() { float angle = 0.0; /* 4msecごとの車体角度の変化量 (度) */ float radius = 0.0; /* 4msecごとの車体の描く円の半径 */ float el = 0.0; /* 4msec間のエンコーダー値の変化量(left) */ float er = 0.0; /* 4msec間のエンコーダー値の変化量(right) */ float currEl = 0.0; /* 現在のエンコーダー値(left) */ float currEr = 0.0; /* 現在のエンコーダー値(right) */ timeCounter++; /********* 値取得 *********/ /* エンコーダー値を取得 */ motorL.setCount(); motorR.setCount(); currEl = motorL.getCount(); currEr = motorR.getCount(); /* 4msec間のエンコーダー値の変化量を計算 */ el = currEl - prevEl; er = currEr - prevEr; /********* 値取得終了 *********/ /********* 座標計算 ***********/ angle = calcAngle(el, er); /* 曲がった角度を計算 */ radius = calcRadius(el, angle); /* 走行体の描く円の半径を計算 */ calcCoordinates(angle, radius, el); /* 座標の更新 */ calcDirection(angle); /* 現在向いている方向の更新 */ /********* 座標計算終了 *******/ /******* prevEl,Erの更新 ******/ prevEl = currEl; prevEr = currEr; /**** prevEl,Erの更新の終了****/ /************ 座標補正 *****************/ //lcd.clear(); //lcd.putf("d", ); //lcd .disp(); /* if(timeCounter%50 !=0) { xHistory += mXCoordinate; yHistory += mYCoordinate; mDirectionHistory += mDirection; } else { xHistory = (xHistory+mXCoordinate)/timeCounter; yHistory = (yHistory + mYCoordinate)/timeCounter; mDirectionHistory = (mDirectionHistory + mDirection)/timeCounter; if(!gAngleTraceFlag) adjustPosition(xHistory, yHistory, mDirectionHistory); xHistory = 0; yHistory = 0; mDirectionHistory = 0; timeCounter = 0; } */ prevEl = currEl; prevEr = currEr; }