WIP multi-weapon behaviours

This commit is contained in:
2016-02-25 16:24:38 +01:00
parent 0c60b637c3
commit bb76c9d248
36 changed files with 1251 additions and 613 deletions
+6 -2
View File
@@ -16,7 +16,7 @@
#include "Game/Systems/PickupSpawnSystem.h"
#include "Game/Systems/AmmoPickupSystem.h"
#include "Game/Systems/DamageIndicatorSystem.h"
#include "Game/Systems/Weapon/WeaponSystem.h"
#include "Game/Systems/Weapon/DefenderWeaponBehaviour.h"
#include "Rendering/AnimationSystem.h"
#include "Game/Systems/HealthHUDSystem.h"
#include "Rendering/BoneAttachmentSystem.h"
@@ -98,6 +98,10 @@ Game::Game(int argc, char* argv[])
m_NetworkClient->Connect(m_NetworkAddress, m_NetworkPort);
m_Renderer->SetWindowTitle(m_Renderer->WindowTitle() + " CLIENT");
}
} else {
// If network is disabled, pretend we're a server
m_IsClient = true;
m_IsServer = true;
}
// Create Octrees
@@ -120,7 +124,7 @@ Game::Game(int argc, char* argv[])
m_SystemPipeline->AddSystem<PlayerMovementSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<SpawnerSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<PlayerSpawnSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<WeaponSystem>(updateOrderLevel, m_Renderer, m_OctreeCollision);
m_SystemPipeline->AddSystem<DefenderWeaponBehaviour>(updateOrderLevel, m_Renderer, m_OctreeCollision);
m_SystemPipeline->AddSystem<LifetimeSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<CapturePointSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<CapturePointHUDSystem>(updateOrderLevel);
@@ -13,6 +13,7 @@ bool MultiplayerSnapshotFilter::FilterComponent(EntityWrapper entity, SharedComp
component.Info.Name == "Transform"
|| component.Info.Name == "Physics"
|| component.Info.Name == "AssaultWeapon"
|| component.Info.Name == "DefenderWeapon"
|| component.Info.Name == "Animation"
|| component.Info.Name == "AnimationOffset"
|| entity.Name() == "PlayerName"
+1 -1
View File
@@ -13,7 +13,7 @@ DamageIndicatorSystem::DamageIndicatorSystem(SystemParams params)
}
void DamageIndicatorSystem::Update(double dt) {
if (!IsServer) {
if (!IsServer && LocalPlayer.Valid()) {
for (auto& iter = updateDamageIndicatorVector.begin(); iter != updateDamageIndicatorVector.end(); iter++) {
if (!iter->spriteEntity.Valid()) {
updateDamageIndicatorVector.erase(iter);
@@ -1,13 +1,5 @@
#include "Systems/Weapon/AssaultWeaponBehaviour.h"
AssaultWeaponBehaviour::AssaultWeaponBehaviour(SystemParams systemParams, IRenderer* renderer, Octree<EntityAABB>* collisionOctree, EntityWrapper player)
: WeaponBehaviour(systemParams, renderer, collisionOctree, player)
{
m_FirstPersonModel = m_Player.FirstChildByName("Hands");
m_ThirdPersonModel = m_Player.FirstChildByName("PlayerModel");
EVENT_SUBSCRIBE_MEMBER(m_EAnimationComplete, &AssaultWeaponBehaviour::OnAnimationComplete);
}
void AssaultWeaponBehaviour::Fire()
{
m_TimeSinceLastFire = 0.0;
@@ -36,7 +28,7 @@ void AssaultWeaponBehaviour::Reload()
return;
}
// Don't reload if we're completly out of ammo
// Don't reload if we're completely out of ammo
if (ammo == 0) {
playEmptySound();
m_TimeSinceLastFire = -0.0f; // HACK: To make empty sound play with interval
@@ -56,14 +48,14 @@ void AssaultWeaponBehaviour::Update(double dt)
{
if (m_Reloading) {
m_ReloadTimer -= dt;
// Re-enable glow on reload impersonator half-way through the animation
// Re-enable glow on reload impostor half-way through the animation
if (IsClient) {
if (m_ReloadTimer <= (double)m_Player["AssaultWeapon"]["ReloadTime"] / 2.0) {
if (m_FirstPersonReloadImpersonator.Valid()) {
m_FirstPersonReloadImpersonator["Model"]["GlowMap"] = true;
if (m_FirstPersonReloadImpostor.Valid()) {
m_FirstPersonReloadImpostor["Model"]["GlowMap"] = true;
}
if (m_ThirdPersonReloadImpersonator.Valid()) {
m_ThirdPersonReloadImpersonator["Model"]["GlowMap"] = true;
if (m_ThirdPersonReloadImpostor.Valid()) {
m_ThirdPersonReloadImpostor["Model"]["GlowMap"] = true;
}
}
}
@@ -96,21 +88,6 @@ void AssaultWeaponBehaviour::Update(double dt)
}
}
bool AssaultWeaponBehaviour::OnAnimationComplete(Events::AnimationComplete& e)
{
if (e.Entity != m_FirstPersonModel) {
return false;
}
//if (e.Name == "ShootRifle") {
// if (!m_Firing) {
// playIdleAnimation();
// }
//}
return true;
}
bool AssaultWeaponBehaviour::hasAmmo()
{
ComponentWrapper& cAssaultWeapon = m_Player["AssaultWeapon"];
@@ -177,7 +154,6 @@ void AssaultWeaponBehaviour::spawnTracer()
float AssaultWeaponBehaviour::traceRayDistance(glm::vec3 origin, glm::vec3 direction)
{
// TODO: Cast a ray and size tracer appropriately
float distance;
glm::vec3 pos;
auto entity = Collision::EntityFirstHitByRay(Ray(origin, direction), m_CollisionOctree, distance, pos);
@@ -215,6 +191,12 @@ void AssaultWeaponBehaviour::playEmptySound()
void AssaultWeaponBehaviour::viewPunch()
{
// Since we send absolute client orientations to server, running this server side would
// cause aim desync.
if (!IsClient) {
return;
}
EntityWrapper playerCamera = m_Player.FirstChildByName("Camera");
if (!playerCamera.Valid()) {
return;
@@ -323,8 +305,8 @@ void AssaultWeaponBehaviour::playReloadAnimation()
EntityWrapper firstPersonWeaponModel = m_Player.FirstChildByName("WeaponModel");
EntityWrapper reloadSpawner = m_Player.FirstChildByName("FirstPersonReloadSpawner");
if (IsClient) {
m_FirstPersonReloadImpersonator = SpawnerSystem::Spawn(reloadSpawner, reloadSpawner);
firstPersonWeaponModel["Model"].Copy(m_FirstPersonReloadImpersonator["Model"]);
m_FirstPersonReloadImpostor = SpawnerSystem::Spawn(reloadSpawner, reloadSpawner);
firstPersonWeaponModel["Model"].Copy(m_FirstPersonReloadImpostor["Model"]);
}
firstPersonWeaponModel["Model"]["Visible"] = false;
}
@@ -332,8 +314,8 @@ void AssaultWeaponBehaviour::playReloadAnimation()
EntityWrapper thirdPersonWeaponModel = m_Player.FirstChildByName("ThirdPersonWeaponModel");
EntityWrapper reloadSpawner = m_Player.FirstChildByName("ThirdPersonReloadSpawner");
if (IsClient) {
m_ThirdPersonReloadImpersonator = SpawnerSystem::Spawn(reloadSpawner, reloadSpawner);
thirdPersonWeaponModel["Model"].Copy(m_ThirdPersonReloadImpersonator["Model"]);
m_ThirdPersonReloadImpostor = SpawnerSystem::Spawn(reloadSpawner, reloadSpawner);
thirdPersonWeaponModel["Model"].Copy(m_ThirdPersonReloadImpostor["Model"]);
}
thirdPersonWeaponModel["Model"]["Visible"] = false;
}
@@ -371,7 +353,7 @@ bool AssaultWeaponBehaviour::shoot(double damage)
return false;
}
// Don't let us shoot ourselves in the foot
// Don't let us shoot ourselves in the foot somehow
if (victim == LocalPlayer) {
return false;
}
@@ -0,0 +1,188 @@
#include "Systems/Weapon/DefenderWeaponBehaviour.h"
void DefenderWeaponBehaviour::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt)
{
(double&)cWeapon["TimeSinceLastFire"] += dt;
WeaponBehaviour::UpdateComponent(entity, cWeapon, dt);
}
void DefenderWeaponBehaviour::UpdateWeapon(WeaponInfo& wi, double dt)
{
ComponentWrapper cWeapon = wi.GetComponent();
bool isFiring = cWeapon["IsFiring"];
bool cooldownPassed = (double)cWeapon["TimeSinceLastFire"] >= (60.0 / (double)cWeapon["RPM"]);
bool isNotShielding = wi.Player.ChildrenWithComponent("Shield").size() == 0;
if (isFiring && cooldownPassed && isNotShielding) {
fireShell(wi);
}
}
void DefenderWeaponBehaviour::OnPrimaryFire(WeaponInfo& wi)
{
ComponentWrapper cWeapon = wi.GetComponent();
cWeapon["IsFiring"] = true;
bool cooldownPassed = (double)cWeapon["TimeSinceLastFire"] >= (60.0 / (double)cWeapon["RPM"]);
bool isNotShielding = wi.Player.ChildrenWithComponent("Shield").size() == 0;
if (cooldownPassed && isNotShielding) {
fireShell(wi);
}
}
void DefenderWeaponBehaviour::OnCeasePrimaryFire(WeaponInfo& wi)
{
ComponentWrapper cWeapon = wi.GetComponent();
cWeapon["IsFiring"] = false;
}
bool DefenderWeaponBehaviour::OnInputCommand(WeaponInfo& wi, const Events::InputCommand& e)
{
if (e.Command == "SpecialAbility" && IsServer) {
EntityWrapper attachment = wi.Player.FirstChildByName("ShieldAttachment");
if (attachment.Valid()) {
if (e.Value > 0) {
SpawnerSystem::Spawn(attachment, attachment);
} else {
attachment.DeleteChildren();
}
}
}
return false;
}
bool DefenderWeaponBehaviour::OnSetCamera(const Events::SetCamera& e)
{
m_CurrentCamera = e.CameraEntity;
return true;
}
void DefenderWeaponBehaviour::fireShell(WeaponInfo& wi)
{
ComponentWrapper cWeapon = wi.GetComponent();
cWeapon["TimeSinceLastFire"] = 0.0;
int numPellets = cWeapon["NumPellets"];
float spreadAngle = cWeapon["SpreadAngle"];
std::uniform_real_distribution<float> randomSpreadAngle(-spreadAngle, spreadAngle);
// Calculate pellet angles
// HACK: Random for now?
// TODO: Make distribution even for each quadrant
std::vector<glm::vec2> pelletAngles;
for (int i = 0; i < numPellets; i++) {
pelletAngles.push_back(glm::vec2(randomSpreadAngle(m_RandomEngine), randomSpreadAngle(m_RandomEngine)));
LOG_DEBUG("%f %f", pelletAngles[i].x, pelletAngles[i].y);
}
double pelletDamage = (double)cWeapon["BaseDamage"] / numPellets;
// 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(wi, direction, pelletDamage);
}
}
}
void DefenderWeaponBehaviour::dealDamage(WeaponInfo& wi, glm::vec3 direction, double damage)
{
// Only deal damage client side
if (!IsClient) {
return;
}
// Only handle shooting for the local player
if (wi.Player != LocalPlayer) {
return;
}
// Make sure the player isn't shooting from the grave
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;
}
// Don't let us shoot ourselves in the foot somehow
if (victim == LocalPlayer) {
return;
}
// 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);
}
float DefenderWeaponBehaviour::traceRayDistance(glm::vec3 origin, glm::vec3 direction)
{
float distance;
glm::vec3 pos;
auto entity = Collision::EntityFirstHitByRay(Ray(origin, direction), m_CollisionOctree, distance, pos);
if (entity) {
return distance;
} else {
return 100.f;
}
}
Camera DefenderWeaponBehaviour::cameraFromEntity(EntityWrapper camera)
{
ComponentWrapper cTransform = camera["Transform"];
ComponentWrapper cCamera = camera["Camera"];
Camera cam(
(float)m_Renderer->Resolution().Width / m_Renderer->Resolution().Height,
(double)cCamera["FOV"],
(double)cCamera["NearClip"],
(double)cCamera["FarClip"]
);
cam.SetPosition(cTransform["Position"]);
cam.SetOrientation(glm::quat((const glm::vec3&)cTransform["Orientation"]));
return cam;
}
@@ -13,7 +13,7 @@ WeaponSystem::WeaponSystem(SystemParams params, IRenderer* renderer, Octree<Enti
void WeaponSystem::Update(double dt)
{
// TODO: Clear inactive weapons
}
void WeaponSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cPlayer, double dt)
@@ -72,20 +72,64 @@ bool WeaponSystem::OnInputCommand(Events::InputCommand& e)
void WeaponSystem::selectWeapon(EntityWrapper player, ComponentInfo::EnumType slot)
{
std::vector<EntityWrapper> weaponAttachments = player.ChildrenWithComponent("WeaponAttachment");
// Find the weapon attachments matching the slot selected
EntityWrapper firstPersonAttachment;
EntityWrapper thirdPersonAttachment;
for (auto& attachment : weaponAttachments) {
ComponentWrapper cWeaponAttachment = attachment["WeaponAttachment"];
if ((ComponentInfo::EnumType)cWeaponAttachment["Slot"] == slot) {
ComponentWrapper::SubscriptProxy person = cWeaponAttachment["Person"];
if (person == person.Enum("FirstPerson")) {
firstPersonAttachment = attachment;
} else if (person == person.Enum("ThirdPerson")) {
thirdPersonAttachment = attachment;
}
}
}
if (firstPersonAttachment.Valid() && thirdPersonAttachment.Valid()) {
LOG_WARNING("No weapon attachment found for slot %i of player #%i", slot, player.ID);
return;
}
// TODO: Delete old weapons
// Spawn the weapon(s)
EntityWrapper firstPersonWeapon;
EntityWrapper thirdPersonWeapon;
if (firstPersonAttachment.Valid()) {
firstPersonWeapon = SpawnerSystem::Spawn(firstPersonAttachment, firstPersonAttachment);
}
if (thirdPersonAttachment.Valid()) {
firstPersonWeapon = SpawnerSystem::Spawn(thirdPersonAttachment, thirdPersonAttachment);
}
// Create the correct behaviour
if (firstPersonWeapon.Valid()) {
if (firstPersonWeapon.HasComponent("AssaultWeapon") {
}
}
// Primary
if (slot == 1) {
// TODO: if class...
if (m_ActiveWeapons.count(player) == 0) {
m_ActiveWeapons.insert(std::make_pair(player, std::make_shared<AssaultWeaponBehaviour>(m_SystemParams, m_Renderer, m_CollisionOctree, player)));
} else {
//m_ActiveWeapons.erase(player);
}
nextBehaviour = std::make_shared<AssaultWeaponBehaviour>(m_SystemParams, m_Renderer, m_CollisionOctree, player);
}
// Secondary
if (slot == 2) {
//m_ActiveWeapons[player] = std::make_shared<PistolWeaponBehaviour>();
}
if (nextBehaviour != nullptr) {
// TODO: Destroy previous behaviour and make new
if (m_ActiveWeapons.count(player) == 0) {
m_ActiveWeapons[player] = nextBehaviour;
}
}
}
bool WeaponSystem::OnPlayerSpawned(Events::PlayerSpawned& e)