bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) { // Store the player for future reference m_PlayerEntities[e.PlayerID] = e.Player; m_PlayerIDs[e.Player.ID] = e.PlayerID; // When a player is actually spawned (since the actual spawning is handled on the server) // Hack should be moved. // TODO: Set the player name to whatever EntityWrapper playerName = e.Player.FirstChildByName("PlayerName"); if (playerName.Valid()) { playerName["Text"]["Content"] = e.PlayerName; } if (!IsClient) { return false; } // Set the camera to the correct entity EntityWrapper cameraEntity = e.Player.FirstChildByName("Camera"); bool outOfBodyExperience = ResourceManager::Load<ConfigFile>("Config.ini")->Get<bool>("Debug.OutOfBodyExperience", false); if (cameraEntity.Valid() && !outOfBodyExperience) { Events::SetCamera e; e.CameraEntity = cameraEntity; m_EventBroker->Publish(e); Events::LockMouse lock; m_EventBroker->Publish(lock); } return true; }
void DefenderWeaponBehaviour::spawnTracers(ComponentWrapper cWeapon, WeaponInfo& wi, std::vector<glm::vec2> pattern) { EntityWrapper flashSpawner = getRelevantWeaponEntity(wi).FirstChildByName("WeaponMuzzleFlash"); if (flashSpawner.Valid()) { std::uniform_real_distribution<float> randomSpreadAngle(0.f, 3.1415f*2); EntityWrapper flash = SpawnerSystem::Spawn(flashSpawner, flashSpawner); if (flash.Valid()) { ((Field<glm::vec3>)flash["Transform"]["Orientation"]).z(randomSpreadAngle(m_RandomEngine)); } } EntityWrapper muzzle = getRelevantWeaponEntity(wi).FirstChildByName("WeaponMuzzleRay"); if (!muzzle.Valid()) { return; } EntityWrapper camera = wi.Player.FirstChildByName("Camera"); if (!camera.Valid()) { return; } float spreadAngle = glm::radians((float)cWeapon["SpreadAngle"]); for (auto& pellet : pattern) { glm::quat pelletRotation = glm::quat(TransformSystem::AbsoluteOrientationEuler(camera)) * glm::quat(glm::vec3(pellet.y, pellet.x, 0) * spreadAngle); glm::vec3 cameraPosition = TransformSystem::AbsolutePosition(camera); glm::vec3 direction = pelletRotation * glm::vec3(0, 0, -1); float distance; glm::vec3 hitPosition; if (Collision::EntityFirstHitByRay(Ray(cameraPosition, direction), m_CollisionOctree, distance, hitPosition) != boost::none) { // don't spawn ray if you "miss the world" EntityWrapper ray = SpawnerSystem::Spawn(muzzle); if (ray.Valid()) { ComponentWrapper cTransform = ray["Transform"]; Field<glm::vec3> rayOrigin = cTransform["Position"]; Field<glm::vec3> rayOrientation = cTransform["Orientation"]; Field<glm::vec3> rayScale = cTransform["Scale"]; glm::vec3 muzzlePosition = TransformSystem::AbsolutePosition(muzzle); glm::quat muzzleOrientation = TransformSystem::AbsoluteOrientation(muzzle); glm::vec3 muzzleToHit = hitPosition - muzzlePosition; rayOrigin = muzzlePosition + (muzzleToHit/2.f); glm::vec3 lookVector = glm::normalize(-muzzleToHit); float pitch = std::asin(-lookVector.y); float yaw = std::atan2(lookVector.x, lookVector.z); glm::quat orientation = glm::quat(glm::vec3(pitch, yaw, 0)); rayOrientation = glm::eulerAngles(orientation); rayScale.x(glm::length(muzzleToHit)); } } } }
glm::vec3 TransformSystem::AbsoluteOrientationEuler(EntityWrapper entity) { glm::vec3 orientation; while (entity.Valid()) { ComponentWrapper transform = entity["Transform"]; orientation += (Field<glm::vec3>)transform["Orientation"]; entity = entity.Parent(); } return orientation; }
glm::quat TransformSystem::AbsoluteOrientation(EntityWrapper entity) { if (!entity.Valid()) { return glm::quat(); } auto cacheIt = OrientationCache.find(entity); ComponentWrapper cTransform = entity["Transform"]; ComponentWrapper::SubscriptProxy cTransformOrientation = cTransform["Orientation"]; if (cacheIt != OrientationCache.end() && !cTransformOrientation.Dirty(DirtySetType::Transform)) { return cacheIt->second; } else { EntityWrapper parent = entity.Parent(); // Calculate orientation glm::quat orientation = AbsoluteOrientation(parent) * glm::quat((const glm::vec3&)cTransformOrientation); // Cache it OrientationCache[entity] = orientation; RecalculatedOrientations++; // Unset dirty flag cTransformOrientation.SetDirty(DirtySetType::Transform, false); return orientation; } }
glm::vec3 TransformSystem::AbsoluteScale(EntityWrapper entity) { if (!entity.Valid()) { return glm::vec3(1.f); } auto cacheIt = ScaleCache.find(entity); ComponentWrapper cTransform = entity["Transform"]; ComponentWrapper::SubscriptProxy cTransformScale = cTransform["Scale"]; if (cacheIt != ScaleCache.end() && !cTransformScale.Dirty(DirtySetType::Transform)) { return cacheIt->second; } else { EntityWrapper parent = entity.Parent(); // Calculate scale glm::vec3 scale = AbsoluteScale(parent) * (const glm::vec3&)cTransformScale; // Cache it ScaleCache[entity] = scale; RecalculatedPositions++; // Unset dirty flag cTransformScale.SetDirty(DirtySetType::Transform, false); return scale; } }
void PlayerSpawnSystem::Update(double dt) { // If there are no CapturePointGameMode components we will just spawn immediately. // Should be able to support older maps with this. // TODO: In the future we might want to return instead, to avoid spawning in the menu for instance. auto pool = m_World->GetComponents("CapturePointGameMode"); if (pool != nullptr && pool->size() > 0) { // Take the first CapturePointGameMode component found. ComponentWrapper& modeComponent = *pool->begin(); // Increase timer. Field<double> timer = modeComponent["RespawnTime"]; timer += dt; if (m_DbgConfigForceRespawn) { (Field<double>)modeComponent["MaxRespawnTime"] = m_ForcedRespawnTime; } double maxRespawnTime = (double)modeComponent["MaxRespawnTime"]; EntityWrapper spectatorCam = m_World->GetFirstEntityByName("SpectatorCamera"); if (spectatorCam.Valid()) { EntityWrapper HUD = spectatorCam.FirstChildByName("SpectatorHUD"); if (HUD.Valid()) { EntityWrapper respawnTimer = spectatorCam.FirstChildByName("RespawnTimer"); if (respawnTimer.Valid()) { //Update respawn time in the HUD element. respawnTimer["Text"]["Content"] = std::to_string(1 + (int)(maxRespawnTime - timer)); } } } if (timer < maxRespawnTime) { return; } // If respawn time has passed, we spawn all players that have requested to be spawned. timer = 0; } // If there are no spawn requests, return immediately, if we are client the SpawnRequests should always be empty. if (m_SpawnRequests.size() == 0) { return; } auto playerSpawns = m_World->GetComponents("PlayerSpawn"); if (playerSpawns == nullptr) { return; } int numSpawnedPlayers = 0; int playersSpectating = 0; const int numRequestsToHandle = (int)m_SpawnRequests.size(); for (auto it = m_SpawnRequests.begin(); it != m_SpawnRequests.end(); ++it) { // It is valid if they didn't pick class yet // but don't spawn anything, goto next spawnrequest. if (it->Class == PlayerClass::None) { ++playersSpectating; continue; } for (auto& cPlayerSpawn : *playerSpawns) { EntityWrapper spawner(m_World, cPlayerSpawn.EntityID); if (!spawner.HasComponent("Spawner")) { continue; } // If the spawner has a team affiliation, check it if (spawner.HasComponent("Team")) { auto cSpawnerTeam = spawner["Team"]; if ((int)cSpawnerTeam["Team"] != it->Team) { // If they somehow has a valid class as spectator, don't spawn them. if (it->Team == cSpawnerTeam["Team"].Enum("Spectator")) { ++playersSpectating; break; } else { continue; } } } // TODO: Choose a different spawner depending on class picked? std::string playerEntityFile; if (it->Class == PlayerClass::Assault) { playerEntityFile = (const std::string&)cPlayerSpawn["AssaultFile"]; } else if (it->Class == PlayerClass::Defender) { playerEntityFile = (const std::string&)cPlayerSpawn["DefenderFile"]; } else if (it->Class == PlayerClass::Sniper) { playerEntityFile = (const std::string&)cPlayerSpawn["SniperFile"]; } // Spawn the player! EntityWrapper player = SpawnerSystem::SpawnEntityFile(playerEntityFile, spawner, EntityWrapper::Invalid, "Player"); // Set the player team affiliation player["Team"]["Team"] = it->Team; // Publish a PlayerSpawned event Events::PlayerSpawned e; e.PlayerID = it->PlayerID; e.Player = player; e.Spawner = spawner; m_EventBroker->Publish(e); ++numSpawnedPlayers; it = m_SpawnRequests.erase(it); break; } if (it == m_SpawnRequests.end()) { break; } } if (numSpawnedPlayers != numRequestsToHandle - playersSpectating) { LOG_DEBUG("%i players were supposed to be spawned, but only %i was successfully.", numRequestsToHandle - playersSpectating, numSpawnedPlayers); } else { std::string dbg = numSpawnedPlayers != 0 ? std::to_string(numSpawnedPlayers) + " players were spawned. " : ""; dbg += playersSpectating != 0 ? std::to_string(playersSpectating) + " players are spectating/picking class. " : ""; LOG_DEBUG(dbg.c_str()); } }
void DefenderWeaponBehaviour::CheckBoost(ComponentWrapper cWeapon, WeaponInfo& wi) { // Only check ammo client side if (!IsClient) { return; } // Only handle ammo check for the local player if (wi.Player != LocalPlayer) { return; } // Make sure the player isn't checking from the grave if (!wi.Player.Valid()) { return; } // 3D-pick middle of screen Rectangle viewport = m_Renderer->GetViewportSize(); glm::vec2 centerScreen(viewport.Width / 2, viewport.Height / 2); // TODO: Some horizontal spread PickData pickData = m_Renderer->Pick(centerScreen); EntityWrapper victim(m_World, pickData.Entity); if (!victim.Valid()) { return; } // Don't let us somehow shoot ourselves in the foot if (victim == LocalPlayer) { return; } // Only care about players being hit if (!victim.HasComponent("Player")) { victim = victim.FirstParentWithComponent("Player"); } if (!victim.Valid()) { return; } // If friendly fire, reduce damage to 0 (needed to make Boosts, Ammosharing work) if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) { EntityWrapper friendlyBoostHudSpawner = wi.FirstPersonPlayerModel.FirstChildByName("FriendlyBoostAttachment"); if (friendlyBoostHudSpawner.Valid()) { EntityWrapper assaultBoost = victim.FirstChildByName("BoostAssault"); EntityWrapper defenderBoost = victim.FirstChildByName("BoostDefender"); EntityWrapper sniperBoost = victim.FirstChildByName("BoostSniper"); auto children = m_World->GetDirectChildren(friendlyBoostHudSpawner.ID); if (children.first == children.second) { if (friendlyBoostHudSpawner.HasComponent("Spawner")) { EntityWrapper friendlyBoostHud = SpawnerSystem::Spawn(friendlyBoostHudSpawner, friendlyBoostHudSpawner); if (friendlyBoostHud.Valid()) { EntityWrapper assaultBoostEntity = friendlyBoostHud.FirstChildByName("AssaultBoost"); if (assaultBoostEntity.Valid()) { EntityWrapper active = assaultBoostEntity.FirstChildByName("Active"); if (active.HasComponent("Text")) { if (assaultBoost.Valid()) { (Field<bool>)active["Text"]["Visible"] = true; } else { (Field<bool>)active["Text"]["Visible"] = false; } } } EntityWrapper defenderBoostEntity = friendlyBoostHud.FirstChildByName("DefenderBoost"); if (defenderBoostEntity.Valid()) { EntityWrapper active = defenderBoostEntity.FirstChildByName("Active"); if (active.HasComponent("Text")) { if (defenderBoost.Valid()) { (Field<bool>)active["Text"]["Visible"] = true; } else { (Field<bool>)active["Text"]["Visible"] = false; } } } EntityWrapper sniperBoostEntity = friendlyBoostHud.FirstChildByName("SniperBoost"); if (sniperBoostEntity.Valid()) { EntityWrapper active = sniperBoostEntity.FirstChildByName("Active"); if (active.HasComponent("Text")) { if (sniperBoost.Valid()) { (Field<bool>)active["Text"]["Visible"] = true; } else { (Field<bool>)active["Text"]["Visible"] = false; } } } } } } else { EntityWrapper friendlyBoostHud = friendlyBoostHudSpawner.FirstChildByName("FriendlyBoostHUD"); if (friendlyBoostHud.Valid()) { if (friendlyBoostHud.HasComponent("Lifetime")) { (Field<double>)friendlyBoostHud["Lifetime"]["Lifetime"] = 0.5; } EntityWrapper assaultBoostEntity = friendlyBoostHud.FirstChildByName("AssaultBoost"); if (assaultBoostEntity.Valid()) { EntityWrapper active = assaultBoostEntity.FirstChildByName("Active"); if (active.HasComponent("Text")) { if (assaultBoost.Valid()) { (Field<bool>)active["Text"]["Visible"] = true; } else { (Field<bool>)active["Text"]["Visible"] = false; } } } EntityWrapper defenderBoostEntity = friendlyBoostHud.FirstChildByName("DefenderBoost"); if (defenderBoostEntity.Valid()) { EntityWrapper active = defenderBoostEntity.FirstChildByName("Active"); if (active.HasComponent("Text")) { if (defenderBoost.Valid()) { (Field<bool>)active["Text"]["Visible"] = true; } else { (Field<bool>)active["Text"]["Visible"] = false; } } } EntityWrapper sniperBoostEntity = friendlyBoostHud.FirstChildByName("SniperBoost"); if (sniperBoostEntity.Valid()) { EntityWrapper active = sniperBoostEntity.FirstChildByName("Active"); if (active.HasComponent("Text")) { if (sniperBoost.Valid()) { (Field<bool>)active["Text"]["Visible"] = true; } else { (Field<bool>)active["Text"]["Visible"] = false; } } } } } } } }
void DefenderWeaponBehaviour::fireShell(ComponentWrapper cWeapon, WeaponInfo& wi) { cWeapon["FireCooldown"] = 60.0 / (double)cWeapon["RPM"]; // Stop reloading Field<bool> isReloading = cWeapon["IsReloading"]; isReloading = false; // Ammo Field<int> magAmmo = cWeapon["MagazineAmmo"]; if (magAmmo <= 0) { return; } else { magAmmo -= 1; } // We can't really do any valuable calculations without a valid camera if (!m_CurrentCamera.Valid()) { return; } int numPellets = cWeapon["NumPellets"]; // Create a spread pattern std::vector<glm::vec2> pattern; // The first pellet is always centered pattern.push_back(glm::vec2(0, 0)); // Any additional pellets form circles around the middle int numOuterPellets = numPellets - 1; float angleIncrement = glm::two_pi<float>() / numOuterPellets; for (int i = 0; i < numOuterPellets; ++i) { float angle = angleIncrement * i; glm::vec2 pellet = glm::vec2(glm::cos(angle), glm::sin(angle)); pattern.push_back(pellet); } // Deal damage (clientside) dealDamage(cWeapon, wi, pattern); // Spawn tracers spawnTracers(cWeapon, wi, pattern); // View punch if (IsClient) { EntityWrapper camera = wi.Player.FirstChildByName("Camera"); if (camera.Valid()) { Field<glm::vec3> cameraOrientation = camera["Transform"]["Orientation"]; float viewPunch = cWeapon["ViewPunch"]; float maxTravelAngle = (const float&)cWeapon["MaxTravelAngle"]; Field<float> currentTravel = cWeapon["CurrentTravel"]; if (currentTravel < maxTravelAngle) { float change = viewPunch; if (currentTravel + change > maxTravelAngle) { change = maxTravelAngle - currentTravel; } cameraOrientation.x(cameraOrientation.x() + change); currentTravel += change; } } } // Play animation playAnimationAndReturn(wi.FirstPersonEntity, "FinalBlend", "Fire"); playAnimationAndReturn(wi.ThirdPersonPlayerModel, "FinalBlend", "Fire"); // Sound Events::PlaySoundOnEntity e; e.Emitter = wi.Player; e.FilePath = "Audio/weapon/Blast.wav"; m_EventBroker->Publish(e); }
bool DefenderWeaponBehaviour::OnInputCommand(ComponentWrapper cWeapon, WeaponInfo& wi, const Events::InputCommand& e) { if (e.Command == "SpecialAbility") { EntityWrapper attachment = wi.Player.FirstChildByName("ShieldAttachment"); if (attachment.Valid()) { if (e.Value > 0) { if(wi.Player.Valid()) { if(wi.Player.HasComponent("ShieldAbility")) { (Field<bool>)wi.Player["ShieldAbility"]["Active"] = true; } } if (IsServer) { SpawnerSystem::Spawn(attachment, attachment); } EntityWrapper backAttachment = attachment.FirstChildByName("Back"); if (backAttachment.Valid()) { Events::AutoAnimationBlend eDeployShieldAttachement; eDeployShieldAttachement.RootNode = backAttachment; eDeployShieldAttachement.NodeName = "Deploy"; eDeployShieldAttachement.Restart = true; eDeployShieldAttachement.Start = true; m_EventBroker->Publish(eDeployShieldAttachement); } EntityWrapper frontAttachment = attachment.FirstChildByName("Front"); if (frontAttachment.Valid()) { Events::AutoAnimationBlend eDeployShieldAttachement; eDeployShieldAttachement.RootNode = frontAttachment; eDeployShieldAttachement.NodeName = "Deploy"; eDeployShieldAttachement.Restart = true; eDeployShieldAttachement.Start = true; m_EventBroker->Publish(eDeployShieldAttachement); } if (IsClient) { EntityWrapper root = wi.FirstPersonEntity; if (root.Valid()) { EntityWrapper animationNode = root.FirstChildByName("Shield"); if (animationNode.Valid()) { Events::AutoAnimationBlend eShieldBlend; eShieldBlend.RootNode = root; eShieldBlend.NodeName = "Shield"; eShieldBlend.Restart = true; eShieldBlend.Start = true; m_EventBroker->Publish(eShieldBlend); } } } else { // server only EntityWrapper root = wi.ThirdPersonPlayerModel; if (root.Valid()) { Events::AutoAnimationBlend eShieldActivateBlend; eShieldActivateBlend.RootNode = root; eShieldActivateBlend.NodeName = "ActivateShield"; eShieldActivateBlend.Restart = true; eShieldActivateBlend.Start = true; m_EventBroker->Publish(eShieldActivateBlend); EntityWrapper animationNode = root.FirstChildByName("ActivateShield"); if (animationNode.Valid()) { Events::AutoAnimationBlend eShieldIdleBlend; eShieldIdleBlend.RootNode = root; eShieldIdleBlend.NodeName = "ShieldFront"; eShieldIdleBlend.Restart = true; eShieldIdleBlend.Start = true; eShieldIdleBlend.AnimationEntity = animationNode; m_EventBroker->Publish(eShieldIdleBlend); } } } } else { attachment.DeleteChildren(); if (wi.Player.Valid()) { if (wi.Player.HasComponent("ShieldAbility")) { (Field<bool>)wi.Player["ShieldAbility"]["Active"] = false; } } if (IsClient) { EntityWrapper root = wi.FirstPersonEntity; if (root.Valid()) { EntityWrapper animationNode = root.FirstChildByName("ActionBlend"); if (animationNode.Valid()) { Events::AutoAnimationBlend eFireBlend; eFireBlend.RootNode = root; eFireBlend.NodeName = "ActionBlend"; eFireBlend.Restart = true; eFireBlend.Start = true; m_EventBroker->Publish(eFireBlend); } } } else { // server only EntityWrapper root = wi.ThirdPersonPlayerModel; if (root.Valid()) { Events::AutoAnimationBlend eShieldDeactivateBlend; eShieldDeactivateBlend.RootNode = root; eShieldDeactivateBlend.NodeName = "ActivateShield"; eShieldDeactivateBlend.Restart = true; eShieldDeactivateBlend.Start = true; eShieldDeactivateBlend.Reverse = true; m_EventBroker->Publish(eShieldDeactivateBlend); EntityWrapper animationNode = root.FirstChildByName("ActivateShield"); if (animationNode.Valid()) { Events::AutoAnimationBlend eActionBlend; eActionBlend.RootNode = root; eActionBlend.NodeName = "ActionBlend"; eActionBlend.AnimationEntity = animationNode; m_EventBroker->Publish(eActionBlend); } } } } } } return false; }
void DefenderWeaponBehaviour::UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt) { CheckBoost(cWeapon, wi); // Decrement reload timer Field<double> reloadTimer = cWeapon["ReloadTimer"]; reloadTimer = glm::max(0.0, reloadTimer - dt); // Start reloading automatically if at 0 mag ammo Field<int> magAmmo = cWeapon["MagazineAmmo"]; if (m_ConfigAutoReload && magAmmo <= 0) { OnReload(cWeapon, wi); } // Handle reloading Field<bool> isReloading = cWeapon["IsReloading"]; if (isReloading && reloadTimer <= 0.0) { double reloadTime = cWeapon["ReloadTime"]; Field<int> magSize = cWeapon["MagazineSize"]; Field<int> ammo = cWeapon["Ammo"]; if (magAmmo < magSize && ammo > 0) { ammo -= 1; magAmmo += 1; reloadTimer = reloadTime; Events::PlaySoundOnEntity e; e.Emitter = wi.Player; e.FilePath = "Audio/weapon/Zoom.wav"; m_EventBroker->Publish(e); } else { isReloading = false; playAnimationAndReturn(wi.FirstPersonEntity, "FinalBlend", "ReloadEnd"); if (wi.ThirdPersonPlayerModel.Valid()) { Events::AutoAnimationBlend eFireIdle; eFireIdle.Duration = 0.2; eFireIdle.RootNode = wi.ThirdPersonPlayerModel; eFireIdle.NodeName = "Fire"; eFireIdle.Start = false; m_EventBroker->Publish(eFireIdle); } } } // Restore view angle if (IsClient) { Field<float> currentTravel = cWeapon["CurrentTravel"]; Field<float> returnSpeed = cWeapon["ViewReturnSpeed"]; if (currentTravel > 0) { float change = returnSpeed * dt; currentTravel = glm::max(0.f, currentTravel - change); EntityWrapper camera = wi.Player.FirstChildByName("Camera"); if (camera.Valid()) { Field<glm::vec3> cameraOrientation = camera["Transform"]["Orientation"]; cameraOrientation.x(cameraOrientation.x() - change); } } } // Fire if we're able to fire if (canFire(cWeapon, wi)) { fireShell(cWeapon, wi); } }