bool BattleManagerScreen::GetVecBetween( ICastEntity* from, ICastEntity* to, kmVec2& distVec ) { CastWorldModel* world = CastWorldModel::get(); if( !world->isValid(from) || !world->isValid(to) ) return false; kmVec2 pFrom; pFrom.x = pFrom.y = 0; GameEntityView* fromView = getViewForEntity(from); if( fromView != NULL ) { pFrom.x = fromView->getPositionX(); pFrom.y = fromView->getPositionY(); } kmVec2 pTo; pTo.x = pTo.y = 0; GameEntityView* toView = getViewForEntity(to); if( toView != NULL ) { pTo.x = toView->getPositionX(); pTo.y = toView->getPositionY(); }else { CCLog("uhoh.."); } kmVec2Subtract( &distVec, &pFrom, &pTo ); kmVec2Scale(&distVec, &distVec, GAME_UNIT_CONVERSION ); //safe to operate on same vector return true; }
/** * Reflects a vector about a given surface normal. The surface normal is * assumed to be of unit length. */ kmVec2* kmVec2Reflect(kmVec2* pOut, const kmVec2* pIn, const kmVec2* normal) { kmVec2 tmp; kmVec2Scale(&tmp, normal, 2.0f * kmVec2Dot(pIn, normal)); kmVec2Subtract(pOut, pIn, &tmp); return pOut; }
/** * Returns the point mid-way between two others */ kmVec2* kmVec2MidPointBetween(kmVec2* pOut, const kmVec2* v1, const kmVec2* v2) { kmVec2 diff; kmVec2Subtract(&diff, v2, v1); kmVec2Normalize(&diff, &diff); kmVec2Scale(&diff, &diff, kmVec2DistanceBetween(v1, v2) * 0.5); kmVec2Add(pOut, v1, &diff); return pOut; }
bool BattleManagerScreen::GetEntityPosition( ICastEntity* entity, kmVec2& pos ) { GameEntityView* view = getViewForEntity( entity ); if( view != NULL ) { pos.x = view->getPositionX(); pos.y = view->getPositionY(); kmVec2Scale(&pos, &pos, GAME_UNIT_CONVERSION); return true; } return false; }
void BattleManagerScreen::onEntityEffectEvent( CCObject* e ) { GameEntityEffectEvt* evt = dynamic_cast<GameEntityEffectEvt*>(e); if(!evt) return; if( evt->name.compare("Death Grip") == 0 ) { CCLog("death grip"); kmVec2 pOrigin; kmVec2 pTarget; if( !GetEntityPosition(evt->origin, pOrigin) || !GetEntityPosition(evt->target, pTarget) ) { CCLog("aborting death grip evt due to invalid target(s)"); } kmVec2 toTarget; kmVec2Subtract(&toTarget, &pTarget, &pOrigin); kmVec2 u_toTarget; kmVec2Normalize(&u_toTarget, &toTarget); float leashDistance = 1; //one game unit kmVec2 dv; kmVec2Scale(&dv, &u_toTarget, leashDistance ); kmVec2 posEnd; kmVec2Add(&posEnd, &pOrigin, &dv); //convert back to screen coordinates kmVec2Scale(&posEnd, &posEnd, VIEW_UNIT_CONVERSION); GameEntityView* tView = getViewForEntity(evt->target); tView->setPosition(posEnd.x, posEnd.y); } }
bool BattleManagerScreen::GetEntitiesInRadius( kmVec2 p, float r, std::vector<ICastEntity*>& entities ) { bool found = false; float upscale = VIEW_UNIT_CONVERSION; kmVec2Scale( &p, &p, upscale ); r *= VIEW_UNIT_CONVERSION; //convert to pixels float rSq = r*r; for( int i=0; i< m_allEntities.size(); i++) { kmVec2 ePos; ePos.x = m_allEntities[i].view->getPositionX(); ePos.y = m_allEntities[i].view->getPositionY(); float distSq = 0; if( ePos.x == p.x ) //handle simple cases first { distSq = ePos.y - p.y; distSq *= distSq; }else if( ePos.y == p.y ) { distSq = ePos.x - p.x; distSq *= distSq; }else { kmVec2 dist; kmVec2Subtract( &dist, &p, &ePos ); distSq = kmVec2LengthSq(&dist); } CCLog("ent %d in radius dist %f", i, sqrt(distSq)); if( distSq <= rSq ) { entities.push_back( m_allEntities[i].model ); found = true; } } return found; }
kglt::Vec2 operator*(float lhs, const kglt::Vec2& rhs) { kglt::Vec2 result; kmVec2Scale(&result, &rhs, lhs); return result; }
void BattleManagerScreen::enemyMovementAI( int enemyIdx, float dt ) { float speed = 75.0f; //5 pixels/sec EntityPair& enemy = m_enemies[enemyIdx]; std::vector<kmVec2> impulses; //x, y std::vector<float> impulseWeights; //TODO: select from closest player EntityPair& player = m_players[0]; CCSize eSize = enemy.view->getContentSize(); CCPoint ePos = enemy.view->getPosition(); ePos.x += eSize.width/2; ePos.y += eSize.height/2; //convert to center origin CCSize pSize = player.view->getContentSize(); CCPoint pPos = player.view->getPosition(); pPos.x += pSize.width/2; pPos.y += pSize.height/2; //convert to center origin float playerLeash = pSize.width * 1.1f; float playerLeashSq = playerLeash*playerLeash; //impulse towards the player kmVec2 toPlayer = { pPos.x - ePos.x, pPos.y - ePos.y }; kmVec2 u_toPlayer; kmVec2Normalize( &u_toPlayer, &toPlayer); if( kmVec2LengthSq( &toPlayer ) < (playerLeashSq * 0.75f) ) { //impulse away from player (too close) kmVec2 u_fromPlayer; kmVec2Scale(&u_fromPlayer, &u_toPlayer, -1*0.75f); //flip the 'to player' vector impulses.push_back(u_fromPlayer); impulseWeights.push_back(100); //vastly overweigh the impulse to the player }else { if( kmVec2LengthSq(&toPlayer) > playerLeashSq ) { //kmVec2Scale(&u_toPlayer, &u_toPlayer, 0.51f); //impulse towards player impulses.push_back(u_toPlayer); impulseWeights.push_back(100); } } //add impulses away from other enemies for( int i=0; i< m_enemies.size(); i++) { if( i == enemyIdx ) continue; CCSize nSize = m_enemies[i].view->getContentSize(); CCPoint nPos = m_enemies[i].view->getPosition(); nPos.x += nSize.width/2; nPos.y += nSize.height/2; kmVec2 toNeighbor = { nPos.x - ePos.x, nPos.y - ePos.y }; float neighborLeash = nSize.width; if( kmVec2LengthSq(&toNeighbor) < neighborLeash * neighborLeash ) { kmVec2 u_toNeighbor; kmVec2Normalize(&u_toNeighbor, &toNeighbor); kmVec2 u_fromNeighbor; kmVec2Scale(&u_fromNeighbor, &u_toNeighbor, -1); impulses.push_back(u_fromNeighbor); impulseWeights.push_back(50); } } if( impulses.size() == 0 ) return; //blend impulses kmVec2 finalImpulse = impulses[0]; //zero always valid because its the impulse to the player float finalImpulseWeight = impulseWeights[0]; for( int i=1; i< impulses.size(); i++) { float w1 = finalImpulseWeight; float w2 = impulseWeights[i]; float wTot = w1 + w2; kmVec2 blend; blend.x = (finalImpulse.x * w1 / wTot) + (impulses[i].x * w2 / wTot); blend.y = (finalImpulse.y * w1 / wTot) + (impulses[i].y * w2 / wTot); //run-length summation finalImpulseWeight = wTot; finalImpulse = blend; } kmVec2 scaledImpulse; kmVec2Scale(&scaledImpulse, &finalImpulse, speed * dt); //CCLog("impulse %.4f", kmVec2Length(& finalImpulse)); if( kmVec2Length(& finalImpulse) < (0.5f) ) { //CCLog("ignore impulse %.4f", kmVec2Length(& finalImpulse)); return; //ignore very small changes to avoid leash jitter } ePos.x += scaledImpulse.x; ePos.y += scaledImpulse.y; ePos.x -= eSize.width/2; ePos.y -= eSize.height/2; //back to original anchor coords enemy.view->setPosition(ePos); }