diff --git a/assets b/assets index 85d96f6f..298641b1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 85d96f6f3d2269e46962492f9713ec67c54e8ece +Subproject commit 298641b1afe313ebde00d77a0c4d75e6c6a0533d diff --git a/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h b/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h index 9e7d4991..a32d65f1 100644 --- a/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h +++ b/include/Game/Systems/Weapon/DefenderWeaponBehaviour.h @@ -27,9 +27,12 @@ private: std::random_device m_RandomDevice; std::mt19937 m_RandomEngine; + + // Weapon functions void fireShell(ComponentWrapper cWeapon, WeaponInfo& wi); - void dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi, glm::vec3 direction, double damage); + void dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi, const std::vector& pattern); + void spawnTracers(ComponentWrapper cWeapon, WeaponInfo& wi, std::vector pattern); bool canFire(ComponentWrapper cWeapon, WeaponInfo& wi); // Utility diff --git a/resources/Schema/Components/DefenderWeapon.xml b/resources/Schema/Components/DefenderWeapon.xml index b01955bc..9c3861cd 100755 --- a/resources/Schema/Components/DefenderWeapon.xml +++ b/resources/Schema/Components/DefenderWeapon.xml @@ -1,14 +1,14 @@ - 8 + 9999 8 64 64 90 - 0.174533 + 10 0.174533 - 10 + 9 120 0.03 0.2 diff --git a/resources/Schema/Components/DefenderWeapon.xsd b/resources/Schema/Components/DefenderWeapon.xsd index 53200952..4968fa5d 100755 --- a/resources/Schema/Components/DefenderWeapon.xsd +++ b/resources/Schema/Components/DefenderWeapon.xsd @@ -36,7 +36,7 @@ Damage dealt if all shotgun pellets hit - Spread angle in radians + The spread angle radius. Maximum vertical aim travel angle in radians diff --git a/resources/Schema/Entities/10Degrees.xml b/resources/Schema/Entities/10Degrees.xml new file mode 100644 index 00000000..0039ca82 --- /dev/null +++ b/resources/Schema/Entities/10Degrees.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + Models/Core/UnitQuad.mesh + + Textures/Effects/Ray.png + + + + + + + + + + + + + + Models/Core/UnitQuad.mesh + + Textures/Effects/Ray.png + + + + + + + + + + + + + + + + + + + + + + + Models/Core/UnitQuad.mesh + + Textures/Effects/Ray.png + + + + + + + + + + + + + + Models/Core/UnitQuad.mesh + + Textures/Effects/Ray.png + + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/DefenderShield.xml b/resources/Schema/Entities/DefenderShield.xml index 5cb72116..845f7d90 100755 --- a/resources/Schema/Entities/DefenderShield.xml +++ b/resources/Schema/Entities/DefenderShield.xml @@ -1,39 +1,95 @@ - + - - Deploy - Idle - 0 - - - Models/Characters/Defender/DefenderShield.mesh - - + + + - + - - ActivateDeactiveShieldF - - 1 - + + Deploy + Idle + 0 + + + + Models/Characters/Defender/DefenderShieldBack.mesh + + false + true + false + false + false + false + - + + + + + ActivateDeactiveShieldF + true + + + + + + + + + ShieldFrontF + + + + + + - + - - ShieldFrontF - 1 - + + Deploy + Idle + 0 + + + Models/Characters/Defender/DefenderShieldFront.mesh + + false + true + false + false + false + false + - + + + + + ActivateDeactiveShieldF + true + + + + + + + + + ShieldFrontF + + + + + + diff --git a/resources/Schema/Entities/HitTest.xml b/resources/Schema/Entities/HitTest.xml new file mode 100644 index 00000000..4649072b --- /dev/null +++ b/resources/Schema/Entities/HitTest.xml @@ -0,0 +1,27 @@ + + + + + + 10 + + + + 1 + true + true + + + Models/Core/UnitSphere.mesh + + false + true + + + + + + + + + diff --git a/resources/Schema/Entities/MovementTest.xml b/resources/Schema/Entities/MovementTest.xml index 6a3fe47f..604aaca4 100644 --- a/resources/Schema/Entities/MovementTest.xml +++ b/resources/Schema/Entities/MovementTest.xml @@ -116,7 +116,7 @@ - + @@ -164,6 +164,99 @@ + + + + + + + + + + + + + + + + + + + + Models/Core/UnitQuad.mesh + + Textures/Effects/Ray.png + + + + + + + + + + + + + + Models/Core/UnitQuad.mesh + + Textures/Effects/Ray.png + + + + + + + + + + + + + + + + + + + + + + + Models/Core/UnitQuad.mesh + + Textures/Effects/Ray.png + + + + + + + + + + + + + + Models/Core/UnitQuad.mesh + + Textures/Effects/Ray.png + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/RayBlue.xml b/resources/Schema/Entities/RayBlue.xml index 83adbe51..64ff3619 100644 --- a/resources/Schema/Entities/RayBlue.xml +++ b/resources/Schema/Entities/RayBlue.xml @@ -3,7 +3,7 @@ - 0.10000000149011612 + 10 diff --git a/src/Engine/Core/EntityWrapper.cpp b/src/Engine/Core/EntityWrapper.cpp index a85127ad..83a31fa7 100644 --- a/src/Engine/Core/EntityWrapper.cpp +++ b/src/Engine/Core/EntityWrapper.cpp @@ -27,7 +27,7 @@ void EntityWrapper::AttachComponent(const char* componentName) EntityWrapper EntityWrapper::Parent() { - if (this->World == nullptr || this->ID == EntityID_Invalid) { + if (!Valid()) { return EntityWrapper::Invalid; } else { return EntityWrapper(this->World, this->World->GetParent(this->ID)); @@ -36,6 +36,10 @@ EntityWrapper EntityWrapper::Parent() EntityWrapper EntityWrapper::FirstParentByName(const std::string& parentEntityName) { + if (!Valid()) { + return EntityWrapper::Invalid; + } + EntityWrapper entity = *this; while (entity.Parent().Valid()) { entity = entity.Parent(); @@ -48,6 +52,10 @@ EntityWrapper EntityWrapper::FirstParentByName(const std::string& parentEntityNa EntityWrapper EntityWrapper::FirstChildByName(const std::string& name) { + if (!Valid()) { + return EntityWrapper::Invalid; + } + return firstChildByNameRecursive(name, this->ID); } @@ -78,6 +86,10 @@ EntityWrapper EntityWrapper::FirstLevelChildByName(const std::string& name) EntityWrapper EntityWrapper::FirstParentWithComponent(const std::string& componentType) { + if (!Valid()) { + return EntityWrapper::Invalid; + } + EntityWrapper entity = *this; while (entity.Parent().Valid()) { entity = entity.Parent(); diff --git a/src/Engine/Rendering/DrawFinalPass.cpp b/src/Engine/Rendering/DrawFinalPass.cpp index d03b0c2b..a538f818 100644 --- a/src/Engine/Rendering/DrawFinalPass.cpp +++ b/src/Engine/Rendering/DrawFinalPass.cpp @@ -1232,7 +1232,7 @@ void DrawFinalPass::DrawToDepthStencilBuffer(std::listSkeleton->GetTPose(); } - glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); } else { if (lastShader != m_FillDepthStencilBufferProgram->GetHandle()) { diff --git a/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp b/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp index 835b5378..ca12ec91 100644 --- a/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp +++ b/src/Game/Systems/Weapon/DefenderWeaponBehaviour.cpp @@ -190,60 +190,50 @@ void DefenderWeaponBehaviour::fireShell(ComponentWrapper cWeapon, WeaponInfo& wi magAmmo -= 1; } - int numPellets = cWeapon["NumPellets"]; - float spreadAngle = cWeapon["SpreadAngle"]; - std::uniform_real_distribution randomSpreadAngle(-spreadAngle, spreadAngle); - - // Calculate pellet angles - // HACK: Random for now? - // TODO: Make distribution even for each quadrant - std::vector pelletAngles; - for (int i = 0; i < numPellets; i++) { - pelletAngles.push_back(glm::vec2(randomSpreadAngle(m_RandomEngine), randomSpreadAngle(m_RandomEngine))); + // We can't really do any valuable calculations without a valid camera + if (!m_CurrentCamera.Valid()) { + return; } - double pelletDamage = (double)cWeapon["BaseDamage"] / numPellets; + int numPellets = cWeapon["NumPellets"]; + + // Create a spread pattern + std::vector 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() / 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()) { - glm::vec3& cameraOrientation = camera["Transform"]["Orientation"]; - float viewPunch = cWeapon["ViewPunch"]; - float maxTravelAngle = cWeapon["MaxTravelAngle"]; - float& currentTravel = cWeapon["CurrentTravel"]; - if (currentTravel < maxTravelAngle) { - float change = viewPunch; - if (currentTravel + change > maxTravelAngle) { - change = maxTravelAngle - currentTravel; - } - cameraOrientation.x += change; - currentTravel += change; - } - } - } - - // Tracers - EntityWrapper weaponModelEntity; - if (wi.Player == LocalPlayer) { - weaponModelEntity = wi.FirstPersonEntity; - } else { - weaponModelEntity = wi.ThirdPersonEntity; - } - if (weaponModelEntity.Valid()) { - EntityWrapper spawner = weaponModelEntity.FirstChildByName("WeaponMuzzle"); - for (auto& angles : pelletAngles) { - glm::vec3 direction = Transform::AbsoluteOrientation(spawner) * glm::quat(glm::vec3(angles, 0.f)) * glm::vec3(0, 0, -1); - float distance = traceRayDistance(Transform::AbsolutePosition(spawner), direction); - EntityWrapper ray = SpawnerSystem::Spawn(spawner); - ((glm::vec3&)ray["Transform"]["Scale"]).z = (distance / 100.f); - glm::vec3& orientation = ray["Transform"]["Orientation"]; - orientation.x += angles.x; - orientation.y += angles.y; - glm::vec3 trajectory = direction * distance; - dealDamage(cWeapon, wi, direction, pelletDamage); - } - } + //if (IsClient) { + // EntityWrapper camera = wi.Player.FirstChildByName("Camera"); + // if (camera.Valid()) { + // glm::vec3& cameraOrientation = camera["Transform"]["Orientation"]; + // float viewPunch = cWeapon["ViewPunch"]; + // float maxTravelAngle = cWeapon["MaxTravelAngle"]; + // float& currentTravel = cWeapon["CurrentTravel"]; + // if (currentTravel < maxTravelAngle) { + // float change = viewPunch; + // if (currentTravel + change > maxTravelAngle) { + // change = maxTravelAngle - currentTravel; + // } + // cameraOrientation.x += change; + // currentTravel += change; + // } + // } + //} // Play animation playAnimationAndReturn(wi.FirstPersonEntity, "FinalBlend", "Fire"); @@ -255,7 +245,35 @@ void DefenderWeaponBehaviour::fireShell(ComponentWrapper cWeapon, WeaponInfo& wi m_EventBroker->Publish(e); } -void DefenderWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi, glm::vec3 direction, double damage) +void DefenderWeaponBehaviour::spawnTracers(ComponentWrapper cWeapon, WeaponInfo& wi, std::vector pattern) +{ + EntityWrapper muzzle = getRelevantWeaponEntity(wi).FirstChildByName("WeaponMuzzle"); + if (!muzzle.Valid()) { + return; + } + + float spreadAngle = glm::radians((float)cWeapon["SpreadAngle"]); + + for (auto& pellet : pattern) { + glm::quat pelletRotation = glm::quat(Transform::AbsoluteOrientationEuler(wi.Player.FirstChildByName("Camera"))) * glm::quat(glm::vec3(pellet.y, pellet.x, 0) * spreadAngle); + glm::vec3 origin = Transform::AbsolutePosition(wi.Player.FirstChildByName("Camera")); + glm::vec3 direction = pelletRotation * glm::vec3(0, 0, -1); + float distance = traceRayDistance(origin, direction); + EntityWrapper ray = SpawnerSystem::Spawn(muzzle); + if (ray.Valid()) { + ComponentWrapper cTransform = ray["Transform"]; + glm::vec3& position = cTransform["Position"]; + glm::vec3& orientation = cTransform["Orientation"]; + glm::vec3& scale = cTransform["Scale"]; + + position = Transform::AbsolutePosition(wi.Player.FirstChildByName("Camera")); + orientation = glm::eulerAngles(pelletRotation); + scale.z = distance; + } + } +} + +void DefenderWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& wi, const std::vector& pattern) { // Only deal damage client side if (!IsClient) { @@ -271,47 +289,76 @@ void DefenderWeaponBehaviour::dealDamage(ComponentWrapper cWeapon, WeaponInfo& w if (!wi.Player.Valid()) { return; } - - glm::vec3 maxRange = direction * 2.f; - EntityWrapper camera = wi.Player.FirstChildByName("Camera"); - glm::vec3 cameraPosition = Transform::AbsolutePosition(camera); - if (!camera.Valid()) { - return; - } - Rectangle screenResolution = m_Renderer->GetViewportSize(); - glm::vec2 centerScreen = glm::vec2(screenResolution.Width / 2, screenResolution.Height / 2); - glm::vec2 screenCoords = cameraFromEntity(m_CurrentCamera).WorldToScreen(cameraPosition + maxRange, m_Renderer->GetViewportSize()); - PickData pickData = m_Renderer->Pick(centerScreen + screenCoords); - EntityWrapper victim(m_World, pickData.Entity); - if (!victim.Valid()) { - return; + + // Convert the spread angle to screen coordinates, taking FOV into account + Rectangle res = m_Renderer->GetViewportSize(); + float spreadAngle = glm::radians((float)cWeapon["SpreadAngle"]); + float nearClip = (double)m_CurrentCamera["Camera"]["NearClip"]; + float farClip = (double)m_CurrentCamera["Camera"]["FarClip"]; + float yFOV = glm::radians((double)m_CurrentCamera["Camera"]["FOV"]); + //float yRefFOV = glm::radians(59.f); + //float yRef = glm::tan(yRefFOV) * nearClip; + //float yRatio = yRef / (glm::tan(yFOV) * nearClip); + //float yMax = (yRatio / 2.f) * spreadAngle * (res.Height / 2.f); + float yRefFOV = glm::radians(59.f); + float yRef = glm::tan(yRefFOV) * nearClip; + float yRatio = yRef / (glm::tan(yFOV) * nearClip); + float yMax = yRatio * (glm::tan(spreadAngle) * farClip); + + LOG_DEBUG("Ratio: %f", yRatio); + LOG_DEBUG("fatClip: %f", farClip); + LOG_DEBUG("yMax: %f", yMax); + double pelletDamage = (double)cWeapon["BaseDamage"] / pattern.size(); + + // Pick! + std::unordered_map damageSum; + glm::vec2 screenCenter(res.Width / 2.f, res.Height / 2.f); + for (auto& pellet : pattern) { + glm::vec2 pickCoord = screenCenter + (pellet * glm::vec2(yMax, yMax)); + PickData pick = m_Renderer->Pick(pickCoord); + + EntityWrapper victim(m_World, pick.Entity); + if (!victim.Valid()) { + continue; + } + + // Temp hit decal + EntityWrapper hit = ResourceManager::Load("Schema/Entities/HitTest.xml")->MergeInto(m_World); + hit["Transform"]["Position"] = pick.Position; + + // Don't let us shoot ourselves in the foot somehow + if (victim == LocalPlayer) { + continue; + } + + // Only care about players being hit + if (!victim.HasComponent("Player")) { + victim = victim.FirstParentWithComponent("Player"); + if (!victim.Valid()) { + continue; + } + } + + // Check for friendly fire + if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) { + // TODO: Ammo sharing + continue; + } + + damageSum[victim] += pelletDamage; + ((glm::vec3&)hit["Model"]["Color"]).b = 1.f; } - // Don't let us shoot ourselves in the foot somehow - if (victim == LocalPlayer) { - return; + // Deal damage! + for (auto& kv : damageSum) { + // Deal damage! + Events::PlayerDamage ePlayerDamage; + ePlayerDamage.Inflictor = wi.Player; + ePlayerDamage.Victim = kv.first; + ePlayerDamage.Damage = kv.second; + m_EventBroker->Publish(ePlayerDamage); + LOG_DEBUG("Dealt %f damage to #%i", kv.second, kv.first.ID); } - - // Only care about players being hit - if (!victim.HasComponent("Player")) { - victim = victim.FirstParentWithComponent("Player"); - } - if (!victim.Valid()) { - return; - } - - // Check for friendly fire - if ((ComponentInfo::EnumType)victim["Team"]["Team"] == (ComponentInfo::EnumType)wi.Player["Team"]["Team"]) { - return; - } - - // Deal damage! - Events::PlayerDamage ePlayerDamage; - ePlayerDamage.Inflictor = wi.Player; - ePlayerDamage.Victim = victim; - ePlayerDamage.Damage = damage; - m_EventBroker->Publish(ePlayerDamage); - LOG_DEBUG("Damage: %f", damage); } bool DefenderWeaponBehaviour::canFire(ComponentWrapper cWeapon, WeaponInfo& wi)