#include "Systems/Weapon/SidearmWeaponBehaviour.h" void SidearmWeaponBehaviour::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt) { Field cooldown = cWeapon["FireCooldown"]; if (cooldown > 0) { cooldown -= dt; if (cooldown < 0) { cooldown = 0; } } WeaponBehaviour::UpdateComponent(entity, cWeapon, dt); } void SidearmWeaponBehaviour::UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt) { CheckAmmo(cWeapon, wi); // Start reloading automatically if at 0 mag ammo Field magAmmo = cWeapon["MagazineAmmo"]; if (m_ConfigAutoReload && magAmmo <= 0) { OnReload(cWeapon, wi); } // Only start reloading once we're done firing Field reloadQueued = cWeapon["ReloadQueued"]; Field fireCooldown = cWeapon["FireCooldown"]; Field isReloading = cWeapon["IsReloading"]; if (reloadQueued && fireCooldown <= 0) { reloadQueued = fireCooldown; isReloading = true; } // Decrement reload timer Field reloadTimer = cWeapon["ReloadTimer"]; if (isReloading) { reloadTimer = glm::max(0.0, reloadTimer - dt); } // Handle reloading if (isReloading && reloadTimer <= 0.0) { Field magSize = cWeapon["MagazineSize"]; magAmmo = magSize; isReloading = false; if (wi.FirstPersonEntity.Valid()) { wi.FirstPersonEntity.FirstChildByName("ViewModel")["Model"]["Visible"] = true; } if (wi.ThirdPersonEntity.Valid()) { wi.ThirdPersonEntity["Model"]["Visible"] = true; } } double reloadTime = cWeapon["ReloadTime"]; if (isReloading && reloadTimer <= reloadTime / 2) { } // Update first person run animation ComponentWrapper cPlayer = wi.Player["Player"]; ComponentWrapper cPhysics = wi.Player["Physics"]; const float& movementSpeed = cPlayer["MovementSpeed"]; float speed = glm::length((const glm::vec3&)cPhysics["Velocity"]); float animationWeight = glm::min(speed, movementSpeed) / movementSpeed; EntityWrapper rootNode = wi.FirstPersonEntity; if (rootNode.Valid()) { EntityWrapper blend = rootNode.FirstChildByName("MovementBlendSidearm"); if (blend.Valid()) { (Field)blend["Blend"]["Weight"] = animationWeight; } } // Restore view angle if (IsClient) { Field currentTravel = cWeapon["CurrentTravel"]; Field 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 cameraOrientation = camera["Transform"]["Orientation"]; cameraOrientation.x(cameraOrientation.x() - change); } } } if ((bool)cWeapon["Automatic"] && canFire(cWeapon)) { fireBullet(cWeapon, wi); } } void SidearmWeaponBehaviour::OnPrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi) { cWeapon["TriggerHeld"] = true; if (canFire(cWeapon)) { fireBullet(cWeapon, wi); } } void SidearmWeaponBehaviour::OnCeasePrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi) { cWeapon["TriggerHeld"] = false; } void SidearmWeaponBehaviour::OnReload(ComponentWrapper cWeapon, WeaponInfo& wi) { Field reloadQueued = cWeapon["ReloadQueued"]; Field isReloading = cWeapon["IsReloading"]; if (reloadQueued || isReloading) { return; } Field magAmmo = cWeapon["MagazineAmmo"]; Field magSize = cWeapon["MagazineSize"]; if (magAmmo >= magSize) { return; } double reloadTime = cWeapon["ReloadTime"]; Field reloadTimer = cWeapon["ReloadTimer"]; // Start reload reloadQueued = true; reloadTimer = reloadTime; // Play animation playAnimationAndReturn(wi.FirstPersonEntity, "ActionBlend", "Reload"); // Third person anim Events::AutoAnimationBlend eReloadBlend; eReloadBlend.RootNode = wi.ThirdPersonPlayerModel; eReloadBlend.NodeName = "Reload"; eReloadBlend.Restart = true; eReloadBlend.Start = true; m_EventBroker->Publish(eReloadBlend); // Spawn explosion effect if (wi.FirstPersonEntity.Valid()) { if (IsClient) { EntityWrapper reloadEffectSpawner = wi.FirstPersonEntity.FirstChildByName("ReloadSpawner"); if (reloadEffectSpawner.Valid()) { reloadEffectSpawner.DeleteChildren(); SpawnerSystem::Spawn(reloadEffectSpawner, reloadEffectSpawner); } } wi.FirstPersonEntity.FirstChildByName("ViewModel")["Model"]["Visible"] = false; } if (wi.ThirdPersonEntity.Valid()) { if (IsServer) { EntityWrapper reloadEffectSpawner = wi.ThirdPersonEntity.FirstChildByName("ReloadSpawner"); if (reloadEffectSpawner.Valid()) { reloadEffectSpawner.DeleteChildren(); SpawnerSystem::Spawn(reloadEffectSpawner, reloadEffectSpawner); } } wi.ThirdPersonEntity["Model"]["Visible"] = false; } // Sound Events::PlaySoundOnEntity e; e.Emitter = wi.Player; e.FilePath = "Audio/weapon/Assault/AssaultWeaponReload.wav"; m_EventBroker->Publish(e); } void SidearmWeaponBehaviour::OnEquip(ComponentWrapper cWeapon, WeaponInfo& wi) { cWeapon["FireCooldown"] = (double)cWeapon["EquipTime"]; if(wi.ThirdPersonPlayerModel.Valid()) { Events::AutoAnimationBlend b1; b1.RootNode = wi.ThirdPersonPlayerModel; b1.NodeName = "SidearmWeapon"; b1.SingleLevelBlend = true; m_EventBroker->Publish(b1); } } void SidearmWeaponBehaviour::OnHolster(ComponentWrapper cWeapon, WeaponInfo& wi) { // Make sure the trigger is released if weapon is holstered while firing cWeapon["TriggerHeld"] = false; // Cancel any reload cWeapon["IsReloading"] = false; cWeapon["ReloadTimer"] = 0.0; } void SidearmWeaponBehaviour::fireBullet(ComponentWrapper cWeapon, WeaponInfo& wi) { cWeapon["FireCooldown"] = 60.0 / (double)cWeapon["RPM"]; // Ammo Field magAmmo = cWeapon["MagazineAmmo"]; if (magAmmo <= 0) { return; } else { magAmmo -= 1; } // View punch if (IsClient) { EntityWrapper camera = wi.Player.FirstChildByName("Camera"); if (camera.Valid()) { Field cameraOrientation = camera["Transform"]["Orientation"]; float viewPunch = cWeapon["ViewPunch"]; float maxTravelAngle = cWeapon["MaxTravelAngle"]; Field currentTravel = cWeapon["CurrentTravel"]; if (currentTravel < maxTravelAngle) { float change = viewPunch; if (currentTravel + change > maxTravelAngle) { change = maxTravelAngle - currentTravel; } cameraOrientation.x(cameraOrientation.x() + change); currentTravel += change; } } } // Get weapon model based on current person EntityWrapper weaponModelEntity = getRelevantWeaponEntity(wi); if (!weaponModelEntity.Valid()) { return; } // Tracer EntityWrapper tracerSpawner = weaponModelEntity.FirstChildByName("WeaponMuzzleRay"); if (tracerSpawner.Valid()) { glm::vec3 origin = TransformSystem::AbsolutePosition(tracerSpawner); glm::vec3 direction = glm::quat(TransformSystem::AbsoluteOrientationEuler(tracerSpawner)) * glm::vec3(0, 0, -1); float distance = traceRayDistance(origin, direction); EntityWrapper ray = SpawnerSystem::Spawn(tracerSpawner, tracerSpawner); if (ray.Valid()) { ((Field)ray["Transform"]["Scale"]).x(distance); ((Field)ray["Transform"]["Position"]).z(-distance/2.f); } } //Flash EntityWrapper flashSpawner = weaponModelEntity.FirstChildByName("WeaponMuzzleFlash"); if (flashSpawner.Valid()) { std::uniform_real_distribution randomSpreadAngle(0.f, 3.1415f*2); EntityWrapper flash = SpawnerSystem::Spawn(flashSpawner, flashSpawner); if(flash.Valid()){ ((Field)flash["Transform"]["Orientation"]).z(randomSpreadAngle(m_RandomEngine)); } } // Deal damage if (dealDamage(cWeapon, wi)) { // Show hit marker EntityWrapper hitMarkerSpawner = wi.Player.FirstChildByName("HitMarkerSpawner"); if (hitMarkerSpawner.Valid()) { SpawnerSystem::Spawn(hitMarkerSpawner, hitMarkerSpawner); Events::PlaySoundOnEntity e; e.Emitter = wi.Player; e.FilePath = "Audio/weapon/hitclick.wav"; m_EventBroker->Publish(e); } } // Play animation playAnimationAndReturn(wi.FirstPersonEntity, "ActionBlend", "Fire"); // Third person anim if (wi.ThirdPersonPlayerModel.Valid()) { Events::AutoAnimationBlend b1; b1.RootNode = wi.ThirdPersonPlayerModel; b1.NodeName = "Fire"; b1.Restart = true; b1.Start = true; m_EventBroker->Publish(b1); } } bool SidearmWeaponBehaviour::canFire(ComponentWrapper cWeapon) { bool triggerHeld = cWeapon["TriggerHeld"]; bool cooldownPassed = (double)cWeapon["FireCooldown"] <= 0.0; bool isNotReloading = !(bool)cWeapon["IsReloading"]; return triggerHeld && cooldownPassed && isNotReloading; } bool SidearmWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi) { // Only deal damage client side if (!IsClient) { return false; } // Only handle damage for the local player if (wi.Player != LocalPlayer) { return false; } // Make sure the player isn't shooting from the grave if (!wi.Player.Valid()) { return false; } // 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 false; } // Don't let us somehow shoot ourselves in the foot if (victim == LocalPlayer) { return false; } // Only care about players being hit if (!victim.HasComponent("Player")) { victim = victim.FirstParentWithComponent("Player"); } if (!victim.Valid()) { return false; } double damage = cWeapon["BaseDamage"]; // 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"]) { damage = 0; bool gaveAmmo = false; if (victim.HasComponent("AssaultWeapon")) { int magazineSize = victim["AssaultWeapon"]["MagazineSize"]; int magazineAmmo = victim["AssaultWeapon"]["MagazineAmmo"]; int maxAmmo = victim["AssaultWeapon"]["MaxAmmo"]; int ammo = victim["AssaultWeapon"]["Ammo"]; if (magazineAmmo < magazineSize) { gaveAmmo = true; } else if (ammo < maxAmmo) { gaveAmmo = true; } } else if (victim.HasComponent("DefenderWeapon")) { int magazineSize = victim["DefenderWeapon"]["MagazineSize"]; int magazineAmmo = victim["DefenderWeapon"]["MagazineAmmo"]; int maxAmmo = victim["DefenderWeapon"]["MaxAmmo"]; int ammo = victim["DefenderWeapon"]["Ammo"]; if (magazineAmmo < magazineSize) { gaveAmmo = true; } else if (ammo < maxAmmo) { gaveAmmo = true; } } if (gaveAmmo) { EntityWrapper effectSpawner = wi.FirstPersonEntity.FirstChildByName("AmmoShareEffectSpawner"); if (effectSpawner.Valid()) { if (effectSpawner.HasComponent("Spawner")) { SpawnerSystem::Spawn(effectSpawner, effectSpawner); } } } } spawnTracer(cWeapon, wi); // Deal damage! Events::PlayerDamage ePlayerDamage; ePlayerDamage.Inflictor = wi.Player; ePlayerDamage.Victim = victim; ePlayerDamage.Damage = damage; m_EventBroker->Publish(ePlayerDamage); return damage > 0; } void SidearmWeaponBehaviour::spawnTracer(ComponentWrapper cWeapon, WeaponInfo& wi) { EntityWrapper muzzle = getRelevantWeaponEntity(wi).FirstChildByName("WeaponMuzzle"); if (!muzzle.Valid()) { return; } EntityWrapper camera = wi.Player.FirstChildByName("Camera"); if (!camera.Valid()) { return; } glm::vec3 cameraPosition = TransformSystem::AbsolutePosition(camera); glm::vec3 direction = glm::vec3(0, 0, -1); float distance; glm::vec3 hitPosition; Collision::EntityFirstHitByRay(Ray(cameraPosition, direction), m_CollisionOctree, distance, hitPosition); EntityWrapper ray = SpawnerSystem::Spawn(muzzle); if (ray.Valid()) { ComponentWrapper cTransform = ray["Transform"]; Field rayOrigin = cTransform["Position"]; Field rayOrientation = cTransform["Orientation"]; Field rayScale = cTransform["Scale"]; glm::vec3 muzzlePosition = TransformSystem::AbsolutePosition(muzzle); glm::quat muzzleOrientation = TransformSystem::AbsoluteOrientation(muzzle); rayOrigin = muzzlePosition; glm::vec3 muzzleToHit = hitPosition - muzzlePosition; 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.z(glm::length(muzzleToHit)); } } /* void SidearmWeaponBehaviour::giveAmmo(ComponentWrapper cWeapon, WeaponInfo& wi, EntityWrapper receiver) { bool gaveAmmo = false; if (receiver.HasComponent("AssaultWeapon")) { int magazineSize = receiver["AssaultWeapon"]["MagazineSize"]; int& magazineAmmo = receiver["AssaultWeapon"]["MagazineAmmo"]; int maxAmmo = receiver["AssaultWeapon"]["MaxAmmo"]; int& ammo = receiver["AssaultWeapon"]["Ammo"]; if(magazineAmmo < magazineSize) { magazineAmmo += 1; gaveAmmo = true; } else if (ammo < maxAmmo) { ammo += 1; gaveAmmo = true; } } else if (receiver.HasComponent("DefenderWeapon")) { int magazineSize = receiver["DefenderWeapon"]["MagazineSize"]; int& magazineAmmo = receiver["DefenderWeapon"]["MagazineAmmo"]; int maxAmmo = receiver["DefenderWeapon"]["MaxAmmo"]; int& ammo = receiver["DefenderWeapon"]["Ammo"]; if (magazineAmmo < magazineSize) { magazineAmmo += 1; gaveAmmo = true; } else if (ammo < maxAmmo) { ammo += 1; gaveAmmo = true; } } if(gaveAmmo) { EntityWrapper effectSpawner = wi.FirstPersonEntity.FirstChildByName("AmmoShareEffectSpawner"); if(effectSpawner.Valid()) { if (effectSpawner.HasComponent("Spawner")) { SpawnerSystem::Spawn(effectSpawner, effectSpawner); } } } }*/ void SidearmWeaponBehaviour::CheckAmmo(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()) { RemoveFrindlyAmmoHUD(wi); return; } // Don't let us somehow shoot ourselves in the foot if (victim == LocalPlayer) { RemoveFrindlyAmmoHUD(wi); return; } // Only care about players being hit if (!victim.HasComponent("Player")) { victim = victim.FirstParentWithComponent("Player"); } if (!victim.Valid()) { RemoveFrindlyAmmoHUD(wi); 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"]) { int magazineAmmo = 0; int ammo = 0; if (victim.HasComponent("AssaultWeapon")) { magazineAmmo = (int)victim["AssaultWeapon"]["MagazineAmmo"]; ammo = (int)victim["AssaultWeapon"]["Ammo"]; } else if (victim.HasComponent("DefenderWeapon")) { magazineAmmo = (int)victim["DefenderWeapon"]["MagazineAmmo"]; ammo = (int)victim["DefenderWeapon"]["Ammo"]; } EntityWrapper friendlyAmmoHudSpawner = wi.FirstPersonPlayerModel.FirstChildByName("FriendlyAmmoAttachment"); if (friendlyAmmoHudSpawner.Valid()) { auto children = m_World->GetDirectChildren(friendlyAmmoHudSpawner.ID); if (children.first == children.second) { if (friendlyAmmoHudSpawner.HasComponent("Spawner")) { EntityWrapper friendlyAmmoHud = SpawnerSystem::Spawn(friendlyAmmoHudSpawner, friendlyAmmoHudSpawner); if (friendlyAmmoHud.Valid()) { EntityWrapper textEntity = friendlyAmmoHud.FirstChildByName("MagazineAmmo"); if (textEntity.Valid()) { if (textEntity.HasComponent("Text")) { (Field)textEntity["Text"]["Content"] = std::to_string(magazineAmmo); } } EntityWrapper ammoTextEntity = friendlyAmmoHud.FirstChildByName("Ammo"); if (ammoTextEntity.Valid()) { if (ammoTextEntity.HasComponent("Text")) { (Field)ammoTextEntity["Text"]["Content"] = std::to_string(ammo); } } } } } else { EntityWrapper textEntity = friendlyAmmoHudSpawner.FirstChildByName("MagazineAmmo"); if (textEntity.Valid()) { if (textEntity.HasComponent("Text")) { (Field)textEntity["Text"]["Content"] = std::to_string(magazineAmmo); } } EntityWrapper ammoTextEntity = friendlyAmmoHudSpawner.FirstChildByName("Ammo"); if (ammoTextEntity.Valid()) { if (ammoTextEntity.HasComponent("Text")) { (Field)ammoTextEntity["Text"]["Content"] = std::to_string(ammo); } } } } } } void SidearmWeaponBehaviour::RemoveFrindlyAmmoHUD(WeaponInfo& wi) { EntityWrapper friendlyAmmoHudSpawner = wi.FirstPersonPlayerModel.FirstChildByName("FriendlyAmmoAttachment"); if (friendlyAmmoHudSpawner.Valid()) { friendlyAmmoHudSpawner.DeleteChildren(); } }