kmBool kmRay2IntersectTriangle(const kmRay2* ray, const kmVec2* p1, const kmVec2* p2, const kmVec2* p3, kmVec2* intersection, kmVec2* normal_out) { kmVec2 intersect; kmVec2 final_intersect; kmVec2 normal; kmScalar distance = 10000.0f; kmBool intersected = KM_FALSE; if(kmRay2IntersectLineSegment(ray, p1, p2, &intersect)) { intersected = KM_TRUE; kmVec2 tmp; kmScalar this_distance = kmVec2Length(kmVec2Subtract(&tmp, &intersect, &ray->start)); if(this_distance < distance) { final_intersect.x = intersect.x; final_intersect.y = intersect.y; distance = this_distance; calculate_line_normal(*p1, *p2, &normal); } } if(kmRay2IntersectLineSegment(ray, p2, p3, &intersect)) { intersected = KM_TRUE; kmVec2 tmp; kmScalar this_distance = kmVec2Length(kmVec2Subtract(&tmp, &intersect, &ray->start)); if(this_distance < distance) { final_intersect.x = intersect.x; final_intersect.y = intersect.y; distance = this_distance; calculate_line_normal(*p2, *p3, &normal); } } if(kmRay2IntersectLineSegment(ray, p3, p1, &intersect)) { intersected = KM_TRUE; kmVec2 tmp; kmScalar this_distance = kmVec2Length(kmVec2Subtract(&tmp, &intersect, &ray->start)); if(this_distance < distance) { final_intersect.x = intersect.x; final_intersect.y = intersect.y; distance = this_distance; calculate_line_normal(*p3, *p1, &normal); } } if(intersected) { intersection->x = final_intersect.x; intersection->y = final_intersect.y; if(normal_out) { normal_out->x = normal.x; normal_out->y = normal.y; } } return intersected; }
void testVec2Length(void) { kmVec2 vec; vec.x = 4.0f; vec.y = 3.0f; CU_ASSERT(5.0f == kmVec2Length(&vec)); }
kmVec2* kmVec2Normalize(kmVec2* pOut, const kmVec2* pIn) { kmScalar l = 1.0f / kmVec2Length(pIn); kmVec2 v; v.x = pIn->x * l; v.y = pIn->y * l; pOut->x = v.x; pOut->y = v.y; return pOut; }
kmVec2* kmVec2Normalize(kmVec2* pOut, const kmVec2* pIn) { if (!pIn->x && !pIn->y) return kmVec2Assign(pOut, pIn); kmScalar l = 1.0f / kmVec2Length(pIn); kmVec2 v; v.x = pIn->x * l; v.y = pIn->y * l; pOut->x = v.x; pOut->y = v.y; return pOut; }
kmBool kmRay2IntersectBox(const kmRay2* ray, const kmVec2* p1, const kmVec2* p2, const kmVec2* p3, const kmVec2* p4, kmVec2* intersection, kmVec2* normal_out) { kmBool intersected = KM_FALSE; kmVec2 intersect, final_intersect, normal; kmScalar distance = 10000.0f; const kmVec2* points[4]; points[0] = p1; points[1] = p2; points[2] = p3; points[3] = p4; unsigned int i = 0; for(; i < 4; ++i) { const kmVec2* this_point = points[i]; const kmVec2* next_point = (i == 3) ? points[0] : points[i+1]; const kmVec2* other_point = (i == 3 || i == 0) ? points[1] : points[0]; if(kmRay2IntersectLineSegment(ray, this_point, next_point, &intersect)) { kmVec2 tmp; kmScalar this_distance = kmVec2Length(kmVec2Subtract(&tmp, &intersect, &ray->start)); kmVec2 this_normal; calculate_line_normal(*this_point, *next_point, *other_point, &this_normal); if(this_distance < distance && kmVec2Dot(&this_normal, &ray->dir) < 0.0f) { kmVec2Assign(&final_intersect, &intersect); distance = this_distance; intersected = KM_TRUE; kmVec2Assign(&normal, &this_normal); } } } if(intersected) { intersection->x = final_intersect.x; intersection->y = final_intersect.y; if(normal_out) { normal_out->x = normal.x; normal_out->y = normal.y; } } return intersected; }
/** * Returns the distance between the two points */ kmScalar kmVec2DistanceBetween(const kmVec2* v1, const kmVec2* v2) { kmVec2 diff; kmVec2Subtract(&diff, v2, v1); return fabs(kmVec2Length(&diff)); }
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); }
void BattleManagerScreen::PerformPlayerAi( GameEntity* player ) { //select ability std::vector<CastCommandState*> abilities; abilities = player->getAbilityList(); if( abilities.size() == 0 ) return; //no abilities, nothing to do CastCommandState* cast = abilities[ rand() % abilities.size() ]; ICastEntity* target = NULL; if( player->getTarget()->getEntityList().size() > 0 ) target = player->getTarget()->getEntityList()[0]; if( !CastWorldModel::get()->isValid( target ) ) { player->getTarget()->clearTargetEntities(); if( m_enemies.size() > 0 ) { //target closest enemy target = m_enemies[0].model; kmVec2 distVec; GetVecBetween(player, target, distVec); float closestEnemy = kmVec2Length( &distVec ); for( int i=1; i< m_enemies.size(); i++) { GetVecBetween(player, m_enemies[i].model, distVec); float distTo = kmVec2Length( &distVec ); if( distTo < closestEnemy ) { closestEnemy = distTo; target = m_enemies[i].model; } } player->getTarget()->addTargetEntity(target); } } kmVec2 toTarget; if( GetVecBetween(player, target, toTarget) && player->canCast() && cast->canAfford() ) { float dTargetSq = kmVec2LengthSq( &toTarget ); float range = cast->getRange(); float meleeRange = 1.0f; if( range*range >= dTargetSq ) { bool dontCast = false; if( cast->getName().compare("Death Grip") == 0 ) { //abort if in melee range already if( dTargetSq <= meleeRange*meleeRange ) { dontCast = true; //CCLog("target in melee range, abort death grip"); } } else if( cast->getName().compare("Curse of Weakness") == 0 ) { //abort if already has curse of weakness and time left greater than 2s GameEntity* geTarget = dynamic_cast<GameEntity*>(target); if( geTarget != NULL ) { float lt = geTarget->getDebuffTimeLeft("Curse of Weakness"); if( lt > 2 ) dontCast = true; } } #ifndef DISABLE_ATTACKS if( !dontCast ) { cast->startCast(); } #endif } } }