///===================================================== /// ///===================================================== void AISystem::DefendStrategy() const{ static IntVec2 defenceLocation(48, 9); for (Actors::const_iterator actorIter = Actor::s_actorsOnMap.cbegin(); actorIter != Actor::s_actorsOnMap.cend(); ++actorIter){ Actor* actor = actorIter->second; if (actor->m_isDead) continue; if (actor->IsPlayer()){ break; } else{ NPC* npc = (NPC*)actor; if (npc->m_plannedBehavior->m_name == "Wander" && CalcDistanceSquared(npc->m_position, defenceLocation) > (10 * 10)){ //if we're far away from the defence location and wandering, move toward the defence location DefendLocationBehavior* defendLocationBehavior = (DefendLocationBehavior*)npc->FindBehaviorByName("DefendLocation"); RECOVERABLE_ASSERT(defendLocationBehavior != nullptr); if (defendLocationBehavior != nullptr){ defendLocationBehavior->m_defenceLocation = defenceLocation; npc->m_plannedBehavior = defendLocationBehavior; } } } } }
///--------------------------------------------------------------------------------- /// fast ///--------------------------------------------------------------------------------- inline bool Disc2D::IsPointInside( const Vector2& point ) const { float distanceToPointSquared = CalcDistanceSquared( m_center, point ); float radiusSquared = m_radius * m_radius; bool isInside = distanceToPointSquared < radiusSquared; return isInside; }
///===================================================== /// ///===================================================== bool TeleportBehavior::Think(){ if ((m_NPC->m_health / m_NPC->m_maxHealth) > m_healthThreshold){ m_NPC->m_ignoredBehaviors.push_back(this); m_NPC->PlanNextThink(false); return m_NPC->Think(false); } std::vector<Tile*>& airTiles = m_NPC->m_map->GetAllTilesOfType(TileType::Air, true); if (airTiles.empty()) return false; std::vector<Tile*>::const_iterator tileIter = airTiles.cbegin() + GetRandomIntLessThan(airTiles.size()); Tile* tile = *tileIter; while (CalcDistanceSquared(tile->m_position, m_NPC->m_position) <= (5 * 5)){ airTiles.erase(tileIter); if (airTiles.empty()) return false; tileIter = airTiles.cbegin() + GetRandomIntLessThan(airTiles.size()); tile = *tileIter; } m_NPC->MoveToLocation(tile->m_position); m_NPC->TakeDamage(-(int)((float)m_NPC->m_maxHealth * m_healthThreshold), m_NPC); if (m_numUses > 0) --m_numUses; if (m_NPC->CanSeePlayer()) s_theMessageBar->m_messages.push_back(m_NPC->m_name + " " + m_phrase + " and disappears!"); return true; }
///--------------------------------------------------------------------------------- /// ///--------------------------------------------------------------------------------- inline float CalcDistance( const IntVector2& positionA, const IntVector2& positionB ) { return sqrtf( (float) CalcDistanceSquared( positionA, positionB ) ); }
///--------------------------------------------------------------------------------- /// very slow ///--------------------------------------------------------------------------------- float CalcDistance( const Vector2& positionA, const Vector2& positionB ) { float distSquared = CalcDistanceSquared( positionA, positionB ); float dist = sqrtf( distSquared ); return dist; }
///===================================================== /// //assumes NPC has RetreatToAllyBehavior ///===================================================== RememberedActor* AISystem::FindAllyToRetreatTo(NPC& retreatingNPC, bool retreatToBuffers, bool findClosest) const{ RetreatToAllyBehavior* retreatToAllyBehavior = (RetreatToAllyBehavior*)retreatingNPC.FindBehaviorByName("RetreatToAlly"); retreatToAllyBehavior->m_avoidEnemies = !retreatToBuffers && findClosest; if (retreatToAllyBehavior == nullptr){ RECOVERABLE_ERROR(); return nullptr; } RememberedActor* prevTargetAlly = retreatingNPC.m_targetAlly; RememberedActor* allyToRetreatTo = nullptr; int bestDistanceSquared = findClosest ? INT_MAX : -1; for (std::vector<Actor*>::const_iterator actorIter = retreatingNPC.m_visibleActors.cbegin(); actorIter != retreatingNPC.m_visibleActors.cend(); ++actorIter){ Actor* actor = *actorIter; FATAL_ASSERT(actor != nullptr); RECOVERABLE_ASSERT(actor != &retreatingNPC); if (retreatToBuffers){ if (actor->IsPlayer()) continue; NPC* npc = (NPC*)actor; AOEBuffBehavior* buffBehavior = (AOEBuffBehavior*)npc->FindBehaviorByName("AOEBuff"); if (buffBehavior == nullptr || buffBehavior->m_hasBeenUsed) continue; } if (retreatingNPC.GetFactionStatus(actor->m_faction->m_factionID, actor->m_ID) > 0){ //is ally int distanceSquared = CalcDistanceSquared(actor->m_position, retreatingNPC.m_position); if ((findClosest && distanceSquared < bestDistanceSquared) || (!findClosest && distanceSquared > bestDistanceSquared)){ RememberedActor remActor = RememberedActor(*actor); retreatingNPC.m_targetAlly = &remActor; IntVec2 possibleMoveLocation = retreatToAllyBehavior->CalcNextPathStep(); if ((possibleMoveLocation.x != -1) && (retreatToBuffers || !findClosest || CalcDistanceSquared(possibleMoveLocation, retreatingNPC.m_targetEnemy->m_position) > CalcDistanceSquared(retreatingNPC.m_position, retreatingNPC.m_targetEnemy->m_position))){ //only retreat if the first step would take us further away from the enemy in the case of findFurthest bestDistanceSquared = distanceSquared; if (allyToRetreatTo == nullptr) allyToRetreatTo = new RememberedActor(remActor); else *allyToRetreatTo = remActor; } } } } if (allyToRetreatTo == nullptr){ //we didn't find anyone to retreat to, so let's see if we remember any nearby allies for (std::map<int, RememberedActor>::const_iterator actorIter = retreatingNPC.m_previouslyVisibleActors.cbegin(); actorIter != retreatingNPC.m_previouslyVisibleActors.cend(); ++actorIter){ const RememberedActor& rememberedActorData = actorIter->second; if (Entity::s_turnCount - 5 > rememberedActorData.m_turn) continue; if (retreatToBuffers && !rememberedActorData.m_canAOEBuff){ continue; } if (retreatingNPC.GetFactionStatus(rememberedActorData.m_factionID, actorIter->first) > 0){ //is ally int distanceSquared = CalcDistanceSquared(rememberedActorData.m_position, retreatingNPC.m_position); if ((findClosest && distanceSquared < bestDistanceSquared) || (!findClosest && distanceSquared > bestDistanceSquared)){ retreatingNPC.m_targetAlly = (RememberedActor*)&rememberedActorData; IntVec2 possibleMoveLocation = retreatToAllyBehavior->CalcNextPathStep(); if ((possibleMoveLocation.x != -1) && (retreatToBuffers || !findClosest || CalcDistanceSquared(possibleMoveLocation, retreatingNPC.m_targetEnemy->m_position) > CalcDistanceSquared(retreatingNPC.m_position, retreatingNPC.m_targetEnemy->m_position))){ //only retreat if the first step would take us further away from the enemy in the case of findFurthest bestDistanceSquared = distanceSquared; if (allyToRetreatTo == nullptr) allyToRetreatTo = new RememberedActor(rememberedActorData); else *allyToRetreatTo = rememberedActorData; } } } } } retreatingNPC.m_targetAlly = prevTargetAlly; return allyToRetreatTo; }
///===================================================== /// ///===================================================== void AISystem::AttackStrategy(const Player& player) const{ for (Actors::const_iterator actorIter = Actor::s_actorsOnMap.cbegin(); actorIter != Actor::s_actorsOnMap.cend(); ++actorIter){ Actor* actor = actorIter->second; if (actor->m_isDead || actor->GetFactionStatus(player.m_faction->m_factionID, player.m_ID) > 0) continue; if (actor->IsPlayer()){ break; } NPC* npc = (NPC*)actor; //if we are a healer, we are next to an enemy, and there are nearby allies, try to run away if (npc->FindBehaviorByName("HealAlly") != nullptr && (npc->m_plannedBehavior->m_behaviorType == BehaviorType::Attack)){ if (npc->m_targetAlly != nullptr && npc->m_targetAlly->m_turn == Entity::s_turnCount && npc->m_targetEnemy->m_turn == Entity::s_turnCount && CalcDistanceSquared(npc->m_position, npc->m_targetEnemy->m_position) <= 8){ BaseAIBehavior* retreatToAllyBehavior = npc->FindBehaviorByName("RetreatToAlly"); if (retreatToAllyBehavior != nullptr){ RememberedActor* allyToRetreatTo = FindAllyToRetreatTo(*npc, false, true); if (allyToRetreatTo != nullptr){ //smartly retreat toward an ally if we can delete npc->m_targetAlly; npc->m_targetAlly = allyToRetreatTo; npc->m_plannedBehavior = retreatToAllyBehavior; continue; } } RetreatBehavior* retreatBehavior = (RetreatBehavior*)npc->FindBehaviorByName("Retreat"); if (retreatBehavior != nullptr){ retreatBehavior->m_retreatCount = 0; npc->m_plannedBehavior = retreatBehavior; //otherwise, dumbly retreat continue; } continue; } } //if a lot of monsters want to chase/attack, we should too as long as we know where the player is if (npc->m_targetEnemy != nullptr && npc->m_targetEnemy->m_isPlayer){ if (npc->m_plannedBehavior->m_behaviorType != BehaviorType::Attack && npc->m_plannedBehavior->m_behaviorType != BehaviorType::Aide){ //if we're far away and a bunch of monsters want to chase, get a bit closer BaseAIBehavior* chaseBehavior = npc->FindBehaviorByName("Chase"); RECOVERABLE_ASSERT(chaseBehavior != nullptr); if (chaseBehavior != nullptr){ npc->m_plannedBehavior = chaseBehavior; continue; } } } //try to group up for buffing if (npc->m_plannedBehavior->m_name == "AOEBuff" || (npc->m_plannedBehavior->m_behaviorType != BehaviorType::Attack && npc->m_plannedBehavior->m_behaviorType != BehaviorType::Aide && npc->m_plannedBehavior->m_name != "PickUpItem")){ BaseAIBehavior* retreatToAllyBehavior = npc->FindBehaviorByName("RetreatToAlly"); if (retreatToAllyBehavior != nullptr){ AOEBuffBehavior* buffBehavior = (AOEBuffBehavior*)npc->FindBehaviorByName("AOEBuff"); if (buffBehavior == nullptr){ RememberedActor* allyToRetreatTo = FindAllyToRetreatTo(*npc, true, true); //try to move to a buff-caster if (allyToRetreatTo != nullptr){ delete npc->m_targetAlly; npc->m_targetAlly = allyToRetreatTo; npc->m_plannedBehavior = retreatToAllyBehavior; continue; } } else if (buffBehavior->m_hasBeenUsed == false && npc->m_plannedBehavior->m_name != "AOEBuff"){ RememberedActor* allyToRetreatTo = FindAllyToRetreatTo(*npc, false, false); //try to move to someone who we can buff if (allyToRetreatTo != nullptr){ delete npc->m_targetAlly; npc->m_targetAlly = allyToRetreatTo; npc->m_plannedBehavior = retreatToAllyBehavior; continue; } } } } } }
primitiveType CalcDistance( const Vector4< primitiveType >& positionA, const Vector4< primitiveType >& positionB ) { primitiveType distSquared = CalcDistanceSquared( positionA, positionB ); primitiveType dist = sqrt( distSquared ); return dist; }
//#define collisions ///========================================================================================================================================== /// Fireworks ///========================================================================================================================================== void FireworksEmitter::Update(double deltaSeconds){ double currentTime = GetCurrentSeconds(); #if defined collisions Vec3s initialPositions; Vec3s finalPositions; #endif //update existing particles for (Particles::iterator particleIter = m_particles.begin(); particleIter != m_particles.end();){ Particle* particle = *particleIter; //delete expired particles if (particle->m_expirationTime < currentTime){ delete particle; particleIter = m_particles.erase(particleIter); continue; } #if defined collisions initialPositions.push_back(particle->m_position); #endif particle->m_velocity.y -= m_acceleration * (float)deltaSeconds; particle->Update(deltaSeconds); #if defined collisions finalPositions.push_back(particle->m_position); #endif ++particleIter; } #if defined collisions int count1 = 0; int count2 = 0; for (Particles::iterator particleIter = m_particles.begin(); particleIter != m_particles.end();){ Particle* particle = *particleIter; Vec3 p1 = initialPositions.at(count1); if (CalcDistanceSquared(p1, m_position) < 0.5f){ ++particleIter; ++count1; continue; } Vec3 p2 = finalPositions.at(count1); count2 = ++count1; float R1 = 0.00000001f; for (Particles::iterator particleIter2 = ++particleIter; particleIter2 != m_particles.end(); ++particleIter2, ++count2){ Particle* particle2 = *particleIter2; Vec3 q1 = initialPositions.at(count2); if (CalcDistanceSquared(q1, m_position) < 0.5f){ continue; } Vec3 q2 = finalPositions.at(count2); float R2 = 0.00000001f; Vec3 x0(q1 - p1); Vec3 e(q2 - q1 - (p2 - p1)); float R = R1 + R2; float collisionTest = (DotProduct(e, x0) * DotProduct(e, x0)) - (DotProduct(e, e) * (DotProduct(x0, x0) - (R * R))); if (collisionTest >= 0.0f){ //did collide float collisionTime = (-DotProduct(e, x0) - sqrt(collisionTest)) / DotProduct(e, e); if (collisionTime < 0.0f) collisionTime = 0.0f; particle2->m_color = RGBAchars::YELLOW; particle->m_color = RGBAchars::YELLOW; particle->m_position = initialPositions.at(count1 - 1); particle2->m_position = initialPositions.at(count2); Vec3 collisionNormal = particle->m_position - particle2->m_position; float length = collisionNormal.Normalize(); if (length <= 0.001f) continue; float v1Parallel = DotProduct(particle->m_velocity, collisionNormal); Vec3 v1Perp = particle->m_velocity - v1Parallel * collisionNormal; float v2Parallel = DotProduct(particle2->m_velocity, collisionNormal); Vec3 v2Perp = particle2->m_velocity - v2Parallel * collisionNormal; float e = 0.7f; Vec3 v1ParralelFinal = (0.5f * (v1Parallel + v2Parallel + e * (v2Parallel - v1Parallel))) * collisionNormal; Vec3 v2ParralelFinal = (0.5f * (v1Parallel + v2Parallel - e * (v2Parallel - v1Parallel))) * collisionNormal; particle->m_velocity = v1ParralelFinal + v1Perp; particle2->m_velocity = v2ParralelFinal + v2Perp; } } } #endif //add new particles m_timeSinceParticleEmission += deltaSeconds; if (m_timeSinceParticleEmission >= m_timeBetweenParticleEmissions){ AddParticles(); m_timeSinceParticleEmission -= m_timeBetweenParticleEmissions; } }