2013-08-29 11:45:22 +09:00
|
|
|
/*
|
|
|
|
Copyright (c) 2013 yvt
|
2013-09-05 00:52:03 +09:00
|
|
|
based on code of pysnip (c) Mathias Kaerlev 2011-2012.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
This file is part of OpenSpades.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
OpenSpades is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
OpenSpades is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
*/
|
2013-08-18 16:18:06 +09:00
|
|
|
|
|
|
|
#include "Weapon.h"
|
2017-09-11 20:59:39 +09:00
|
|
|
#include "GameProperties.h"
|
2017-01-08 18:02:21 +09:00
|
|
|
#include "IWorldListener.h"
|
|
|
|
#include "World.h"
|
2016-12-03 18:49:07 +09:00
|
|
|
#include <Core/Debug.h>
|
|
|
|
#include <Core/Exception.h>
|
2013-08-27 16:10:25 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
namespace spades {
|
|
|
|
namespace client {
|
2016-12-03 18:23:47 +09:00
|
|
|
Weapon::Weapon(World *w, Player *p)
|
|
|
|
: world(w),
|
|
|
|
owner(p),
|
|
|
|
time(0),
|
|
|
|
shooting(false),
|
2017-01-26 15:55:35 +09:00
|
|
|
shootingPreviously(false),
|
2016-12-03 18:23:47 +09:00
|
|
|
reloading(false),
|
|
|
|
nextShotTime(0.f),
|
|
|
|
reloadStartTime(-101.f),
|
2017-11-18 21:15:07 +00:00
|
|
|
reloadEndTime(-100.f),
|
2016-12-03 18:23:47 +09:00
|
|
|
lastDryFire(false) {
|
2013-08-18 16:18:06 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
Weapon::~Weapon() { SPADES_MARK_FUNCTION(); }
|
|
|
|
|
|
|
|
void Weapon::Restock() { stock = GetMaxStock(); }
|
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
void Weapon::Reset() {
|
|
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
shooting = false;
|
|
|
|
reloading = false;
|
|
|
|
ammo = GetClipSize();
|
|
|
|
stock = GetMaxStock();
|
|
|
|
time = 0.f;
|
|
|
|
nextShotTime = 0.f;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
void Weapon::SetShooting(bool b) { shooting = b; }
|
|
|
|
|
|
|
|
bool Weapon::IsReadyToShoot() {
|
2017-01-08 18:02:21 +09:00
|
|
|
return (ammo > 0 || !owner->IsLocalPlayer()) && time >= nextShotTime &&
|
|
|
|
(!reloading || IsReloadSlow());
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-21 00:28:57 +09:00
|
|
|
float Weapon::GetReloadProgress() {
|
|
|
|
return (time - reloadStartTime) / (reloadEndTime - reloadStartTime);
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
float Weapon::TimeToNextFire() { return nextShotTime - time; }
|
|
|
|
|
|
|
|
bool Weapon::FrameNext(float dt) {
|
2013-08-18 16:18:06 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2017-01-08 18:02:21 +09:00
|
|
|
bool ownerIsLocalPlayer = owner->IsLocalPlayer();
|
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
bool fired = false;
|
|
|
|
bool dryFire = false;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (shooting && (!reloading || IsReloadSlow())) {
|
2013-08-18 16:18:06 +09:00
|
|
|
// abort slow reload
|
|
|
|
reloading = false;
|
2017-01-08 18:02:21 +09:00
|
|
|
|
2017-01-26 15:55:35 +09:00
|
|
|
if (!shootingPreviously) {
|
|
|
|
nextShotTime = std::max(nextShotTime, time);
|
|
|
|
}
|
|
|
|
|
2017-01-08 18:02:21 +09:00
|
|
|
// Automatic operation of weapon.
|
|
|
|
if (time >= nextShotTime && (ammo > 0 || !ownerIsLocalPlayer)) {
|
2013-08-18 16:18:06 +09:00
|
|
|
fired = true;
|
2017-01-08 18:02:21 +09:00
|
|
|
|
|
|
|
// Consume an ammo.
|
2017-12-22 22:38:34 +09:00
|
|
|
if (ammo > 0) {
|
2017-01-08 18:02:21 +09:00
|
|
|
ammo--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (world->GetListener()) {
|
2013-08-18 16:18:06 +09:00
|
|
|
world->GetListener()->PlayerFiredWeapon(owner);
|
2017-01-08 18:02:21 +09:00
|
|
|
}
|
2017-01-26 15:55:35 +09:00
|
|
|
nextShotTime += GetDelay();
|
2016-12-03 18:23:47 +09:00
|
|
|
} else if (time >= nextShotTime) {
|
2013-08-18 16:18:06 +09:00
|
|
|
dryFire = true;
|
|
|
|
}
|
2017-01-26 15:55:35 +09:00
|
|
|
shootingPreviously = true;
|
|
|
|
} else {
|
|
|
|
shootingPreviously = false;
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
if (reloading) {
|
|
|
|
if (time >= reloadEndTime) {
|
2017-12-22 22:38:34 +09:00
|
|
|
// A reload was completed (non-slow-loading weapon), or a single shell was
|
|
|
|
// loaded (slow-loading weapon).
|
|
|
|
//
|
|
|
|
// For local players, the number of bullets/shells loaded onto the magazine is
|
|
|
|
// sent by the server. However, we still calculate it by ourselves for slow
|
|
|
|
// -loading weapons so the reloading animation and the number displayed on the
|
|
|
|
// screen is synchronized. (This is especially important on a slow connection.)
|
|
|
|
//
|
|
|
|
// For remote players, the server does not send any information regarding the
|
|
|
|
// number of bullets/shells loaded or held as stock. This is problematic for
|
|
|
|
// slow-loading weapons because we can't tell how many times the reloading
|
|
|
|
// animation has to be repeated. For now, we just assume a remote player has
|
|
|
|
// an infinite supply of ammo, but a limited number of bullets in a clip.
|
2013-08-18 16:18:06 +09:00
|
|
|
reloading = false;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (IsReloadSlow()) {
|
2017-12-22 22:38:34 +09:00
|
|
|
if (ammo < GetClipSize() && (stock > 0 || !ownerIsLocalPlayer)) {
|
2013-09-02 14:50:10 +09:00
|
|
|
ammo++;
|
|
|
|
stock--;
|
|
|
|
}
|
2013-09-02 15:08:45 +09:00
|
|
|
slowReloadLeftCount--;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (slowReloadLeftCount > 0)
|
2013-09-02 15:08:45 +09:00
|
|
|
Reload(false);
|
2016-12-03 18:23:47 +09:00
|
|
|
else if (world->GetListener())
|
|
|
|
world->GetListener()->PlayerReloadedWeapon(owner);
|
|
|
|
} else {
|
2017-01-08 18:02:21 +09:00
|
|
|
if (!ownerIsLocalPlayer) {
|
2017-12-22 22:38:34 +09:00
|
|
|
ammo = GetClipSize();
|
2013-08-31 02:50:51 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
if (world->GetListener())
|
2013-09-02 15:08:45 +09:00
|
|
|
world->GetListener()->PlayerReloadedWeapon(owner);
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
time += dt;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (dryFire && !lastDryFire) {
|
|
|
|
if (world->GetListener())
|
2013-08-18 16:18:06 +09:00
|
|
|
world->GetListener()->PlayerDryFiredWeapon(owner);
|
|
|
|
}
|
|
|
|
lastDryFire = dryFire;
|
|
|
|
return fired;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-31 02:50:51 +09:00
|
|
|
void Weapon::ReloadDone(int ammo, int stock) {
|
2013-08-31 14:57:29 +09:00
|
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
2013-08-31 02:50:51 +09:00
|
|
|
this->ammo = ammo;
|
|
|
|
this->stock = stock;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-31 14:57:29 +09:00
|
|
|
void Weapon::AbortReload() {
|
|
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
reloading = false;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2017-12-22 22:38:34 +09:00
|
|
|
void Weapon::Reload(bool initial) {
|
2013-08-18 16:18:06 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2017-12-22 22:38:34 +09:00
|
|
|
bool ownerIsLocalPlayer = owner->IsLocalPlayer();
|
|
|
|
|
2016-12-03 18:23:47 +09:00
|
|
|
if (reloading)
|
2013-08-18 16:18:06 +09:00
|
|
|
return;
|
2017-12-22 22:38:34 +09:00
|
|
|
|
|
|
|
// Is the clip already full?
|
2016-12-03 18:23:47 +09:00
|
|
|
if (ammo >= GetClipSize())
|
2013-08-18 16:18:06 +09:00
|
|
|
return;
|
2017-12-22 22:38:34 +09:00
|
|
|
|
|
|
|
if (ownerIsLocalPlayer) {
|
|
|
|
if (stock == 0)
|
|
|
|
return;
|
|
|
|
if (IsReloadSlow() && ammo > 0 && shooting)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (initial)
|
2013-09-02 15:08:45 +09:00
|
|
|
slowReloadLeftCount = stock - std::max(0, stock - GetClipSize() + ammo);
|
2013-08-18 16:18:06 +09:00
|
|
|
reloading = true;
|
|
|
|
shooting = false;
|
2013-08-21 00:28:57 +09:00
|
|
|
reloadStartTime = time;
|
2013-08-18 16:18:06 +09:00
|
|
|
reloadEndTime = time + GetReloadTime();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (world->GetListener())
|
2013-08-18 16:18:06 +09:00
|
|
|
world->GetListener()->PlayerReloadingWeapon(owner);
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-09-03 00:28:39 +09:00
|
|
|
void Weapon::ForceReloadDone() {
|
|
|
|
int newStock;
|
|
|
|
newStock = std::max(0, stock - GetClipSize() + ammo);
|
|
|
|
ammo += stock - newStock;
|
|
|
|
stock = newStock;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
class RifleWeapon3 : public Weapon {
|
2013-08-18 16:18:06 +09:00
|
|
|
public:
|
2016-12-03 18:23:47 +09:00
|
|
|
RifleWeapon3(World *w, Player *p) : Weapon(w, p) {}
|
2018-10-13 14:43:17 +09:00
|
|
|
std::string GetName() override { return "Rifle"; }
|
|
|
|
float GetDelay() override { return 0.5f; }
|
|
|
|
int GetClipSize() override { return 10; }
|
|
|
|
int GetMaxStock() override { return 50; }
|
|
|
|
float GetReloadTime() override { return 2.5f; }
|
|
|
|
bool IsReloadSlow() override { return false; }
|
|
|
|
WeaponType GetWeaponType() override { return RIFLE_WEAPON; }
|
|
|
|
int GetDamage(HitType type, float distance) override {
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (type) {
|
2013-08-18 16:18:06 +09:00
|
|
|
case HitTypeTorso: return 49;
|
|
|
|
case HitTypeHead: return 100;
|
|
|
|
case HitTypeArms: return 33;
|
|
|
|
case HitTypeLegs: return 33;
|
|
|
|
case HitTypeBlock: return 50;
|
2014-02-25 19:35:37 +09:00
|
|
|
default: SPAssert(false); return 0;
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
Vector3 GetRecoil() override {
|
2017-02-25 23:08:36 +09:00
|
|
|
return MakeVector3(0.025f, 0.05f, 0.f); // measured
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
float GetSpread() override { return 0.012f; } // measured (standing, crouched)
|
|
|
|
int GetPelletSize() override { return 1; }
|
2013-08-18 16:18:06 +09:00
|
|
|
};
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
class SMGWeapon3 : public Weapon {
|
2013-08-18 16:18:06 +09:00
|
|
|
public:
|
2016-12-03 18:23:47 +09:00
|
|
|
SMGWeapon3(World *w, Player *p) : Weapon(w, p) {}
|
2018-10-13 14:43:17 +09:00
|
|
|
std::string GetName() override { return "SMG"; }
|
|
|
|
float GetDelay() override { return 0.11f; }
|
|
|
|
int GetClipSize() override { return 30; }
|
|
|
|
int GetMaxStock() override { return 120; }
|
|
|
|
float GetReloadTime() override { return 2.5f; }
|
|
|
|
bool IsReloadSlow() override { return false; }
|
|
|
|
WeaponType GetWeaponType() override { return SMG_WEAPON; }
|
|
|
|
int GetDamage(HitType type, float distance) override {
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (type) {
|
2013-08-18 16:18:06 +09:00
|
|
|
case HitTypeTorso: return 29;
|
|
|
|
case HitTypeHead: return 75;
|
|
|
|
case HitTypeArms: return 18;
|
|
|
|
case HitTypeLegs: return 18;
|
2014-03-08 01:32:39 +09:00
|
|
|
case HitTypeBlock: return 35;
|
2014-02-25 19:35:37 +09:00
|
|
|
default: SPAssert(false); return 0;
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
Vector3 GetRecoil() override {
|
2017-02-25 23:08:36 +09:00
|
|
|
return MakeVector3(0.01f, 0.0125f, 0.f); // measured
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
float GetSpread() override { return 0.025f; } // measured (standing, crouched)
|
|
|
|
int GetPelletSize() override { return 1; }
|
2013-08-18 16:18:06 +09:00
|
|
|
};
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
class ShotgunWeapon3 : public Weapon {
|
2013-08-18 16:18:06 +09:00
|
|
|
public:
|
2016-12-03 18:23:47 +09:00
|
|
|
ShotgunWeapon3(World *w, Player *p) : Weapon(w, p) {}
|
2018-10-13 14:43:17 +09:00
|
|
|
std::string GetName() override { return "Shotgun"; }
|
|
|
|
float GetDelay() override { return 1.f; }
|
|
|
|
int GetClipSize() override { return 6; }
|
|
|
|
int GetMaxStock() override { return 48; }
|
|
|
|
float GetReloadTime() override { return 0.5f; }
|
|
|
|
bool IsReloadSlow() override { return true; }
|
|
|
|
WeaponType GetWeaponType() override { return SHOTGUN_WEAPON; }
|
|
|
|
int GetDamage(HitType type, float distance) override {
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (type) {
|
2013-08-18 16:18:06 +09:00
|
|
|
case HitTypeTorso: return 27;
|
|
|
|
case HitTypeHead: return 37;
|
|
|
|
case HitTypeArms: return 16;
|
|
|
|
case HitTypeLegs: return 16;
|
|
|
|
case HitTypeBlock:
|
2016-12-03 18:23:47 +09:00
|
|
|
// Actually, you cast a hit per pallet. This value is a guess, by the way.
|
|
|
|
// --GM
|
2013-08-27 12:07:20 +12:00
|
|
|
return 34;
|
2014-02-25 19:35:37 +09:00
|
|
|
default: SPAssert(false); return 0;
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
Vector3 GetRecoil() override {
|
2017-02-25 23:08:36 +09:00
|
|
|
return MakeVector3(0.05f, 0.1f, 0.f); // measured
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
float GetSpread() override { return 0.024f; }
|
|
|
|
int GetPelletSize() override { return 8; }
|
2013-08-18 16:18:06 +09:00
|
|
|
};
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
class RifleWeapon4 : public Weapon {
|
2013-08-27 12:07:20 +12:00
|
|
|
public:
|
2016-12-03 18:23:47 +09:00
|
|
|
RifleWeapon4(World *w, Player *p) : Weapon(w, p) {}
|
2018-10-13 14:43:17 +09:00
|
|
|
std::string GetName() override { return "Rifle"; }
|
|
|
|
float GetDelay() override { return 0.6f; }
|
|
|
|
int GetClipSize() override { return 8; }
|
|
|
|
int GetMaxStock() override { return 48; }
|
|
|
|
float GetReloadTime() override { return 2.5f; }
|
|
|
|
bool IsReloadSlow() override { return false; }
|
|
|
|
WeaponType GetWeaponType() override { return RIFLE_WEAPON; }
|
|
|
|
int GetDamage(HitType type, float distance) override {
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (type) {
|
2013-08-27 12:07:20 +12:00
|
|
|
// These are the 0.75 damage values.
|
2016-12-03 18:23:47 +09:00
|
|
|
// To be honest, we don't need this information, as the server decides the
|
|
|
|
// damage.
|
2013-08-27 12:07:20 +12:00
|
|
|
// EXCEPT for blocks, that is.
|
|
|
|
// --GM
|
|
|
|
case HitTypeTorso: return 49;
|
|
|
|
case HitTypeHead: return 100;
|
|
|
|
case HitTypeArms: return 33;
|
|
|
|
case HitTypeLegs: return 33;
|
|
|
|
case HitTypeBlock: return 50;
|
2014-02-25 19:35:37 +09:00
|
|
|
default: SPAssert(false); return 0;
|
2013-08-27 12:07:20 +12:00
|
|
|
}
|
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
Vector3 GetRecoil() override {
|
2016-12-03 18:23:47 +09:00
|
|
|
// FIXME: needs to measured
|
2013-08-27 12:07:20 +12:00
|
|
|
return MakeVector3(0.0001f, 0.075f, 0.f);
|
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
float GetSpread() override { return 0.004f; }
|
|
|
|
int GetPelletSize() override { return 1; }
|
2013-08-27 12:07:20 +12:00
|
|
|
};
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
class SMGWeapon4 : public Weapon {
|
2013-08-27 12:07:20 +12:00
|
|
|
public:
|
2016-12-03 18:23:47 +09:00
|
|
|
SMGWeapon4(World *w, Player *p) : Weapon(w, p) {}
|
2018-10-13 14:43:17 +09:00
|
|
|
std::string GetName() override { return "SMG"; }
|
|
|
|
float GetDelay() override { return 0.1f; }
|
|
|
|
int GetClipSize() override { return 30; }
|
|
|
|
int GetMaxStock() override { return 150; }
|
|
|
|
float GetReloadTime() override { return 2.5f; }
|
|
|
|
bool IsReloadSlow() override { return false; }
|
|
|
|
WeaponType GetWeaponType() override { return SMG_WEAPON; }
|
|
|
|
int GetDamage(HitType type, float distance) override {
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (type) {
|
2013-08-27 12:07:20 +12:00
|
|
|
case HitTypeTorso: return 29;
|
|
|
|
case HitTypeHead: return 75;
|
|
|
|
case HitTypeArms: return 18;
|
|
|
|
case HitTypeLegs: return 18;
|
|
|
|
case HitTypeBlock: return 34;
|
2014-02-25 19:35:37 +09:00
|
|
|
default: SPAssert(false); return 0;
|
2013-08-27 12:07:20 +12:00
|
|
|
}
|
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
Vector3 GetRecoil() override {
|
2016-12-03 18:23:47 +09:00
|
|
|
// FIXME: needs to measured
|
2013-08-27 12:07:20 +12:00
|
|
|
return MakeVector3(0.00005f, 0.0125f, 0.f);
|
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
float GetSpread() override { return 0.012f; }
|
|
|
|
int GetPelletSize() override { return 1; }
|
2013-08-27 12:07:20 +12:00
|
|
|
};
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
class ShotgunWeapon4 : public Weapon {
|
2013-08-27 12:07:20 +12:00
|
|
|
public:
|
2016-12-03 18:23:47 +09:00
|
|
|
ShotgunWeapon4(World *w, Player *p) : Weapon(w, p) {}
|
2018-10-13 14:43:17 +09:00
|
|
|
std::string GetName() override { return "Shotgun"; }
|
|
|
|
float GetDelay() override { return 0.8f; }
|
|
|
|
int GetClipSize() override { return 8; }
|
|
|
|
int GetMaxStock() override { return 48; }
|
|
|
|
float GetReloadTime() override { return 0.4f; }
|
|
|
|
bool IsReloadSlow() override { return true; }
|
|
|
|
WeaponType GetWeaponType() override { return SHOTGUN_WEAPON; }
|
|
|
|
int GetDamage(HitType type, float distance) override {
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (type) {
|
2013-08-27 12:07:20 +12:00
|
|
|
case HitTypeTorso: return 27;
|
|
|
|
case HitTypeHead: return 37;
|
|
|
|
case HitTypeArms: return 16;
|
|
|
|
case HitTypeLegs: return 16;
|
2016-12-03 18:23:47 +09:00
|
|
|
case HitTypeBlock: return 34;
|
2014-02-25 19:35:37 +09:00
|
|
|
default: SPAssert(false); return 0;
|
2013-08-27 12:07:20 +12:00
|
|
|
}
|
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
Vector3 GetRecoil() override {
|
2016-12-03 18:23:47 +09:00
|
|
|
// FIXME: needs to measured
|
2013-08-27 12:07:20 +12:00
|
|
|
return MakeVector3(0.0002f, 0.075f, 0.f);
|
|
|
|
}
|
2018-10-13 14:43:17 +09:00
|
|
|
float GetSpread() override { return 0.036f; }
|
|
|
|
int GetPelletSize() override { return 8; }
|
2013-08-27 12:07:20 +12:00
|
|
|
};
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2017-09-11 20:59:39 +09:00
|
|
|
Weapon *Weapon::CreateWeapon(WeaponType type, Player *p, const GameProperties &gp) {
|
2013-08-18 16:18:06 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2017-09-11 20:59:39 +09:00
|
|
|
switch (gp.protocolVersion) {
|
|
|
|
case ProtocolVersion::v075:
|
|
|
|
switch (type) {
|
|
|
|
case RIFLE_WEAPON: return new RifleWeapon3(p->GetWorld(), p);
|
|
|
|
case SMG_WEAPON: return new SMGWeapon3(p->GetWorld(), p);
|
|
|
|
case SHOTGUN_WEAPON: return new ShotgunWeapon3(p->GetWorld(), p);
|
|
|
|
default: SPInvalidEnum("type", type);
|
|
|
|
}
|
|
|
|
case ProtocolVersion::v076:
|
|
|
|
switch (type) {
|
|
|
|
case RIFLE_WEAPON: return new RifleWeapon4(p->GetWorld(), p);
|
|
|
|
case SMG_WEAPON: return new SMGWeapon4(p->GetWorld(), p);
|
|
|
|
case SHOTGUN_WEAPON: return new ShotgunWeapon4(p->GetWorld(), p);
|
|
|
|
default: SPInvalidEnum("type", type);
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
SPInvalidEnum("protocolVersion", gp.protocolVersion);
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|