Files
axyz/src/Game/Systems/Weapon/AssaultWeaponBehaviour.cpp
T
Tobias Dahl 5879cb0de4 Merge pull request #219 from teamfisk/FixAssaultWeaponReload
Reload for assault is only done once now.
2016-03-14 03:48:59 +01:00

523 lines
19 KiB
C++

#include "Systems/Weapon/AssaultWeaponBehaviour.h"
void AssaultWeaponBehaviour::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cWeapon, double dt)
{
Field<double> fireCooldown = cWeapon["FireCooldown"];
fireCooldown = glm::max(0.0, fireCooldown - dt);
WeaponBehaviour::UpdateComponent(entity, cWeapon, dt);
}
void AssaultWeaponBehaviour::UpdateWeapon(ComponentWrapper cWeapon, WeaponInfo& wi, double dt)
{
CheckBoost(cWeapon, wi);
// Start reloading automatically if at 0 mag ammo
Field<int> magAmmo = cWeapon["MagazineAmmo"];
if (m_ConfigAutoReload && magAmmo <= 0) {
//OnReload(cWeapon, wi);
// Send an input command instead of reloading immediately so the server
// has a chance to catch up.
if (IsClient) {
Events::InputCommand e;
e.Player = wi.Player;
e.PlayerID = -1;
e.Command = "Reload";
e.Value = 1;
m_EventBroker->Publish(e);
e.Value = 0;
m_EventBroker->Publish(e);
}
}
// Only start reloading once we're done firing
Field<bool> reloadQueued = cWeapon["ReloadQueued"];
Field<double> fireCooldown = cWeapon["FireCooldown"];
Field<bool> isReloading = cWeapon["IsReloading"];
if (reloadQueued && fireCooldown <= 0) {
reloadQueued = fireCooldown;
isReloading = true;
}
// Decrement reload timer
Field<double> reloadTimer = cWeapon["ReloadTimer"];
if (isReloading) {
reloadTimer = glm::max(0.0, reloadTimer - dt);
}
// Handle reloading
if (isReloading && reloadTimer <= 0.0) {
Field<int> magSize = cWeapon["MagazineSize"];
Field<int> ammo = cWeapon["Ammo"];
int usedAmmo = glm::max(0, *magSize - *magAmmo);
magAmmo = glm::clamp(*ammo + *magAmmo, 0, *magSize);
ammo = glm::max(0, *ammo - usedAmmo);
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) {
}
// 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);
}
}
}
// 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("MovementBlendAssault");
if (blend.Valid()) {
(Field<double>)blend["Blend"]["Weight"] = animationWeight;
}
}
// Fire if we're able to fire
if (canFire(cWeapon, wi)) {
fireBullet(cWeapon, wi);
}
}
void AssaultWeaponBehaviour::OnPrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi)
{
cWeapon["TriggerHeld"] = true;
if (canFire(cWeapon, wi)) {
fireBullet(cWeapon, wi);
}
}
void AssaultWeaponBehaviour::OnCeasePrimaryFire(ComponentWrapper cWeapon, WeaponInfo& wi)
{
cWeapon["TriggerHeld"] = false;
}
void AssaultWeaponBehaviour::OnReload(ComponentWrapper cWeapon, WeaponInfo& wi)
{
Field<bool> reloadQueued = cWeapon["ReloadQueued"];
Field<bool> isReloading = cWeapon["IsReloading"];
if (reloadQueued || isReloading) {
return;
}
Field<int> magAmmo = cWeapon["MagazineAmmo"];
Field<int> magSize = cWeapon["MagazineSize"];
if (magAmmo >= magSize) {
return;
}
Field<int> ammo = cWeapon["Ammo"];
if (ammo <= 0) {
return;
}
Field<double> reloadTime = cWeapon["ReloadTime"];
Field<double> reloadTimer = cWeapon["ReloadTimer"];
// Start reload
reloadQueued = true;
reloadTimer = reloadTime;
// Play animation
playAnimationAndReturn(wi.FirstPersonEntity, "ActionBlend", "Reload");
// Third person anim
Events::AutoAnimationBlend b1;
b1.RootNode = wi.ThirdPersonPlayerModel;
b1.NodeName = "Reload";
b1.Restart = true;
b1.Start = true;
m_EventBroker->Publish(b1);
// 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 AssaultWeaponBehaviour::OnEquip(ComponentWrapper cWeapon, WeaponInfo& wi)
{
cWeapon["FireCooldown"] = (double)cWeapon["EquipTime"];
if (wi.ThirdPersonPlayerModel.Valid()) {
Events::AutoAnimationBlend b1;
b1.RootNode = wi.ThirdPersonPlayerModel;
b1.NodeName = "AssaultWeapon";
b1.SingleLevelBlend = true;
m_EventBroker->Publish(b1);
}
}
void AssaultWeaponBehaviour::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 AssaultWeaponBehaviour::fireBullet(ComponentWrapper cWeapon, WeaponInfo& wi)
{
cWeapon["FireCooldown"] = 60.0 / (double)cWeapon["RPM"];
// Ammo
Field<int> magAmmo = cWeapon["MagazineAmmo"];
if (magAmmo <= 0) {
return;
} else {
magAmmo -= 1;
}
// 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 = 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;
}
}
}
// 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<glm::vec3>)ray["Transform"]["Scale"]).x(distance);
((Field<glm::vec3>)ray["Transform"]["Position"]).z(-distance/2.f);
}
}
//MuzzleFlash
EntityWrapper muzzleFlashSpawner = weaponModelEntity.FirstChildByName("WeaponMuzzleFlash");
if (muzzleFlashSpawner.Valid()) {
std::uniform_real_distribution<float> randomSpreadAngle(0.f, 3.1415f*2);
EntityWrapper flash = SpawnerSystem::Spawn(muzzleFlashSpawner, muzzleFlashSpawner);
if (flash.Valid()) {
((Field<glm::vec3>)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);
}
}
// 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 = 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, "ActionBlend", "Fire");
// Third person anim
Events::AutoAnimationBlend b1;
b1.RootNode = wi.ThirdPersonPlayerModel;
b1.NodeName = "Fire";
b1.Restart = true;
b1.Start = true;
m_EventBroker->Publish(b1);
// Sound
Events::PlaySoundOnEntity e;
e.Emitter = wi.Player;
e.FilePath = "Audio/weapon/Assault/AssaultWeaponFire.wav";
m_EventBroker->Publish(e);
}
bool AssaultWeaponBehaviour::canFire(ComponentWrapper cWeapon, WeaponInfo& wi)
{
bool triggerHeld = cWeapon["TriggerHeld"];
bool cooldownPassed = (double)cWeapon["FireCooldown"] <= 0.0;
bool isNotReloading = !(bool)cWeapon["IsReloading"];
return triggerHeld && cooldownPassed && isNotReloading;
}
bool AssaultWeaponBehaviour::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;
}
// Deal damage!
Events::PlayerDamage ePlayerDamage;
ePlayerDamage.Inflictor = wi.Player;
ePlayerDamage.Victim = victim;
ePlayerDamage.Damage = damage;
m_EventBroker->Publish(ePlayerDamage);
return damage > 0;
}
void AssaultWeaponBehaviour::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;
}
}
}
}
}
}
}
}