Weapons and stuff pt. 2
This commit is contained in:
parent
f9e3e419f5
commit
9a9c182347
@ -134,8 +134,8 @@ set(CLIENT_ONLY_SOURCES
|
||||
"src/gui/font.cpp"
|
||||
"src/gui/menu.hpp"
|
||||
"src/gui/menu.cpp"
|
||||
"src/gui/use_target_hud.hpp"
|
||||
"src/gui/use_target_hud.cpp"
|
||||
"src/gui/player_hud.hpp"
|
||||
"src/gui/player_hud.cpp"
|
||||
"src/utils/files.cpp"
|
||||
)
|
||||
|
||||
@ -158,6 +158,8 @@ set(SERVER_ONLY_SOURCES
|
||||
"src/game/game.cpp"
|
||||
"src/game/human_character.hpp"
|
||||
"src/game/human_character.cpp"
|
||||
"src/game/item_instance.hpp"
|
||||
"src/game/item_instance.cpp"
|
||||
"src/game/mapinstance.hpp"
|
||||
"src/game/mapinstance.cpp"
|
||||
"src/game/marker.hpp"
|
||||
|
||||
@ -31,20 +31,25 @@ std::shared_ptr<assets::Item> assets::Item::LoadFromFile(const std::string& path
|
||||
|
||||
if (anim_type == "idle")
|
||||
item->idle_anim = anim_name;
|
||||
else if (anim_type == "raise")
|
||||
item->raise_anim = anim_name;
|
||||
else if (anim_type == "use" || anim_type == "fire")
|
||||
item->use_anim = anim_name;
|
||||
else if (anim_type == "aim")
|
||||
item->aim_anim = anim_name;
|
||||
else if (anim_type == "aiming")
|
||||
item->aiming_anim = anim_name;
|
||||
else if (anim_type == "reload")
|
||||
item->reload_anim = anim_name;
|
||||
else if (anim_type == "legs")
|
||||
item->legs_anim = anim_name;
|
||||
else
|
||||
throw std::runtime_error("Unknown item anim type " + anim_type);
|
||||
}
|
||||
else if (command == "model")
|
||||
{
|
||||
std::string model_name;
|
||||
iss >> model_name;
|
||||
item->model = CacheManager::GetModel("data/" + model_name + ".mdl");
|
||||
iss >> item->model_name;
|
||||
item->model = CacheManager::GetModel("data/" + item->model_name + ".mdl");
|
||||
}
|
||||
else if (command == "attach")
|
||||
{
|
||||
@ -117,6 +122,30 @@ std::shared_ptr<assets::Item> assets::Item::LoadFromFile(const std::string& path
|
||||
{
|
||||
iss >> item->fire_delay;
|
||||
}
|
||||
else if (command == "firesnd")
|
||||
{
|
||||
iss >> item->fire_snd;
|
||||
}
|
||||
else if (command == "firefx")
|
||||
{
|
||||
iss >> item->fire_fx >> item->fire_fx_loc;
|
||||
}
|
||||
else if (command == "dispersion")
|
||||
{
|
||||
iss >> item->dispersion_min >> item->dispersion_max >> item->dispersion_shot >> item->dispersion_decay;
|
||||
}
|
||||
else if (command == "slot")
|
||||
{
|
||||
iss >> item->slot;
|
||||
}
|
||||
else if (command == "twohanded")
|
||||
{
|
||||
item->twohanded = true;
|
||||
}
|
||||
else if (command == "walkspeedmult")
|
||||
{
|
||||
iss >> item->walk_speed_mult;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown item command: " + command);
|
||||
|
||||
@ -41,14 +41,23 @@ struct Item
|
||||
ItemType type = ITEM_NONE;
|
||||
std::string name;
|
||||
|
||||
std::string idle_anim;
|
||||
std::string use_anim; // use or fire
|
||||
|
||||
size_t slot = 0;
|
||||
|
||||
std::string model_name;
|
||||
std::shared_ptr<const assets::Model> model;
|
||||
|
||||
std::string bone;
|
||||
Transform bone_offset;
|
||||
|
||||
bool twohanded = false;
|
||||
|
||||
float walk_speed_mult = 1.0f;
|
||||
|
||||
std::string legs_anim;
|
||||
std::string raise_anim;
|
||||
std::string idle_anim;
|
||||
std::string use_anim; // use or fire
|
||||
|
||||
// consumable
|
||||
std::string action;
|
||||
|
||||
@ -58,9 +67,18 @@ struct Item
|
||||
std::string ammo_type;
|
||||
size_t clip_size = 0;
|
||||
size_t fire_delay = 0;
|
||||
float dispersion_min = 0.0f;
|
||||
float dispersion_max = 0.0f;
|
||||
float dispersion_shot = 0.0f;
|
||||
float dispersion_decay = 1.0f;
|
||||
|
||||
std::string aim_anim;
|
||||
std::string aiming_anim;
|
||||
std::string reload_anim;
|
||||
|
||||
std::string fire_snd;
|
||||
std::string fire_fx;
|
||||
std::string fire_fx_loc;
|
||||
|
||||
static std::shared_ptr<Item> LoadFromFile(const std::string& path);
|
||||
};
|
||||
|
||||
@ -241,6 +241,12 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
||||
iss >> pm_name;
|
||||
col_material = GetMaterialByName(pm_name);
|
||||
}
|
||||
else if (command == "loc")
|
||||
{
|
||||
std::string loc_name;
|
||||
iss >> loc_name;
|
||||
ParseTransform(iss, model->locations_[loc_name]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown command in model file: " + command);
|
||||
@ -306,3 +312,12 @@ bool assets::Model::GetParamFloat(const std::string& key, float& out) const
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Transform* assets::Model::GetLocation(const std::string& key) const
|
||||
{
|
||||
auto it = locations_.find(key);
|
||||
if (it == locations_.end())
|
||||
return nullptr;
|
||||
|
||||
return &it->second;
|
||||
}
|
||||
@ -53,7 +53,9 @@ public:
|
||||
|
||||
const std::string* GetParam(const std::string& key) const;
|
||||
bool GetParamFloat(const std::string& key, float& out) const;
|
||||
|
||||
|
||||
const Transform* GetLocation(const std::string& key) const;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
glm::vec3 col_offset_ = glm::vec3(0.0f);
|
||||
@ -68,6 +70,7 @@ private:
|
||||
AABB3 aabb_;
|
||||
|
||||
std::map<std::string, std::string> params_;
|
||||
std::map<std::string, Transform> locations_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -157,6 +157,18 @@ static const std::map<SDL_Scancode, game::PlayerInputType> s_inputmap = {
|
||||
{ SDL_SCANCODE_LSHIFT, game::IN_SPRINT },
|
||||
{ SDL_SCANCODE_LCTRL, game::IN_CROUCH },
|
||||
{ SDL_SCANCODE_E, game::IN_USE },
|
||||
{ SDL_SCANCODE_Q, game::IN_HOLSTER },
|
||||
{ SDL_SCANCODE_R, game::IN_RELOAD },
|
||||
{ SDL_SCANCODE_1, game::IN_WEAPON_1 },
|
||||
{ SDL_SCANCODE_2, game::IN_WEAPON_2 },
|
||||
{ SDL_SCANCODE_3, game::IN_WEAPON_3 },
|
||||
{ SDL_SCANCODE_4, game::IN_WEAPON_4 },
|
||||
{ SDL_SCANCODE_5, game::IN_WEAPON_5 },
|
||||
{ SDL_SCANCODE_6, game::IN_WEAPON_6 },
|
||||
{ SDL_SCANCODE_7, game::IN_WEAPON_7 },
|
||||
{ SDL_SCANCODE_8, game::IN_WEAPON_8 },
|
||||
{ SDL_SCANCODE_9, game::IN_WEAPON_9 },
|
||||
{ SDL_SCANCODE_0, game::IN_WEAPON_0 },
|
||||
{ SDL_SCANCODE_F3, game::IN_DEBUG1 },
|
||||
{ SDL_SCANCODE_F4, game::IN_DEBUG2 },
|
||||
{ SDL_SCANCODE_F5, game::IN_DEBUG3 },
|
||||
|
||||
@ -182,7 +182,7 @@ void game::Character::PlayActionAnim(assets::AnimIdx anim_idx, float speed)
|
||||
if (animstate_.action_anim_idx != anim_idx)
|
||||
{
|
||||
// continue from current time if same anim
|
||||
animstate_.action_phase = (speed > 0.0f) ? 0.0f : action_anim_end_;
|
||||
animstate_.action_time = (speed > 0.0f) ? 0.0f : action_anim_end_;
|
||||
}
|
||||
animstate_.action_anim_idx = anim_idx;
|
||||
action_anim_playback_speed_ = speed;
|
||||
@ -191,6 +191,12 @@ void game::Character::PlayActionAnim(assets::AnimIdx anim_idx, float speed)
|
||||
|
||||
void game::Character::PlayActionAnim(const std::string& anim_name, float speed)
|
||||
{
|
||||
if (anim_name.empty())
|
||||
{
|
||||
ClearActionAnim();
|
||||
return;
|
||||
}
|
||||
|
||||
PlayActionAnim(GetAnim(anim_name), speed);
|
||||
}
|
||||
|
||||
@ -206,12 +212,20 @@ void game::Character::SetAimTarget(const glm::vec3& target)
|
||||
|
||||
void game::Character::SetViewItem(const std::string& item_name)
|
||||
{
|
||||
if (item_ == item_name)
|
||||
return;
|
||||
|
||||
item_ = item_name;
|
||||
|
||||
auto msg = BeginEntMsg(net::EMSG_EQUIP);
|
||||
msg.Write(net::ModelName(item_name));
|
||||
}
|
||||
|
||||
void game::Character::SendFire()
|
||||
{
|
||||
auto msg = BeginEntMsg(net::EMSG_FIRE);
|
||||
}
|
||||
|
||||
void game::Character::SyncControllerTransform()
|
||||
{
|
||||
if (!controller_)
|
||||
@ -282,7 +296,7 @@ void game::Character::UpdateMovement()
|
||||
Turn(yaw_, turn_yaw, turn_speed_ * dt);
|
||||
float move_yaw = directional ? yaw_ + relative_yaw : yaw_;
|
||||
|
||||
move_dir = glm::vec3(-glm::sin(move_yaw), glm::cos(move_yaw), 0.0f) * walk_speed_ * dt;
|
||||
move_dir = glm::vec3(-glm::sin(move_yaw), glm::cos(move_yaw), 0.0f) * walk_speed_ * dt * weight_speed_mult_;
|
||||
|
||||
if (running)
|
||||
move_dir *= run_speed_mult_;
|
||||
@ -305,7 +319,7 @@ void game::Character::UpdateMovement()
|
||||
// update anim
|
||||
float run_blend_target = walking ? 0.5f : 0.0f;
|
||||
MoveToward(animstate_.loco_blend, run_blend_target, dt * 2.0f);
|
||||
float anim_speed = glm::mix(0.3f, 1.5f, UnMix(0.0f, 0.5f, animstate_.loco_blend));
|
||||
float anim_speed = glm::mix(0.3f, 1.5f, UnMix(0.0f, 0.5f, animstate_.loco_blend)) * weight_speed_mult_;
|
||||
if (running)
|
||||
anim_speed *= run_speed_mult_;
|
||||
animstate_.loco_phase = glm::mod(animstate_.loco_phase + anim_speed * dt, 1.0f);
|
||||
@ -396,7 +410,7 @@ void game::Character::UpdateSyncState()
|
||||
|
||||
// action
|
||||
state.action_anim = animstate_.action_anim_idx;
|
||||
state.action_phase.Encode(animstate_.action_phase);
|
||||
state.action_time.Encode(animstate_.action_time);
|
||||
|
||||
// aim
|
||||
state.aim_yaw.Encode(animstate_.yaw);
|
||||
@ -472,11 +486,11 @@ game::CharacterSyncFieldFlags game::Character::WriteState(net::OutMessage& msg,
|
||||
}
|
||||
|
||||
// action phase
|
||||
if (curr.action_phase.value != base.action_phase.value)
|
||||
if (curr.action_time.value != base.action_time.value)
|
||||
{
|
||||
fields |= CSF_ACTION_PHASE;
|
||||
fields |= CSF_ACTION_TIME;
|
||||
|
||||
net::WriteDelta(msg, curr.action_phase, base.action_phase);
|
||||
net::WriteDelta(msg, curr.action_time, base.action_time);
|
||||
}
|
||||
|
||||
// aim
|
||||
@ -628,21 +642,21 @@ void game::Character::UpdateActionAnim()
|
||||
if (action_anim_done_)
|
||||
return;
|
||||
|
||||
animstate_.action_phase += action_anim_playback_speed_ * (1.0f / 25.0f);
|
||||
animstate_.action_time += action_anim_playback_speed_ * (1.0f / 25.0f);
|
||||
|
||||
if (action_anim_playback_speed_ > 0.0f)
|
||||
{
|
||||
if (animstate_.action_phase >= action_anim_end_)
|
||||
if (animstate_.action_time >= action_anim_end_)
|
||||
{
|
||||
animstate_.action_phase = action_anim_end_;
|
||||
animstate_.action_time = action_anim_end_;
|
||||
action_anim_done_ = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (animstate_.action_phase <= 0.0f)
|
||||
if (animstate_.action_time <= 0.0f)
|
||||
{
|
||||
animstate_.action_phase = 0.0f;
|
||||
animstate_.action_time = 0.0f;
|
||||
action_anim_done_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,6 +94,8 @@ public:
|
||||
|
||||
void SetPosition(const glm::vec3& position);
|
||||
|
||||
void SetWeightSpeedMult(float mult) { weight_speed_mult_ = mult; }
|
||||
|
||||
virtual void ActivateHitBones() override;
|
||||
virtual void FinalizeFrame() override;
|
||||
|
||||
@ -112,6 +114,7 @@ protected:
|
||||
bool GetAiming() const { return aiming_; }
|
||||
void SetAimTarget(const glm::vec3& target);
|
||||
void SetViewItem(const std::string& item_name);
|
||||
void SendFire();
|
||||
|
||||
private:
|
||||
void SyncControllerTransform();
|
||||
@ -141,6 +144,7 @@ protected:
|
||||
float turn_speed_ = 8.0f;
|
||||
float walk_speed_ = 2.0f;
|
||||
float run_speed_mult_ = 3.0f;
|
||||
float weight_speed_mult_ = 1.0f;
|
||||
|
||||
private:
|
||||
CharacterTuning tuning_;
|
||||
|
||||
@ -44,7 +44,7 @@ void game::CharacterAnimState::ApplyToSkeleton(SkeletonInstance& sk) const
|
||||
auto action_anim = skeleton->GetAnimation(action_anim_idx);
|
||||
if (action_anim)
|
||||
{
|
||||
sk.ApplySkelAnim(*action_anim, action_phase, 1.0f);
|
||||
sk.ApplySkelAnim(*action_anim, action_time, 1.0f);
|
||||
}
|
||||
|
||||
if (glm::abs(yaw) > 0.01f || glm::abs(pitch) > 0.01f)
|
||||
|
||||
@ -15,7 +15,7 @@ struct CharacterAnimState
|
||||
float loco_phase = 0.0f;
|
||||
|
||||
assets::AnimIdx action_anim_idx = assets::NO_ANIM;
|
||||
float action_phase = 0.0f;
|
||||
float action_time = 0.0f;
|
||||
|
||||
float yaw = 0.0f;
|
||||
float pitch = 0.0f;
|
||||
|
||||
@ -21,11 +21,11 @@ struct CharacterSyncState
|
||||
assets::AnimIdx walk_anim = assets::NO_ANIM;
|
||||
assets::AnimIdx run_anim = assets::NO_ANIM;
|
||||
net::AnimBlendQ loco_blend;
|
||||
net::AnimTimeQ loco_phase;
|
||||
net::AnimPhaseQ loco_phase;
|
||||
|
||||
// action anim
|
||||
assets::AnimIdx action_anim = assets::NO_ANIM;
|
||||
net::AnimTimeQ action_phase;
|
||||
net::AnimTimeQ action_time;
|
||||
|
||||
// aim
|
||||
net::AnimAimAngleQ aim_yaw;
|
||||
@ -44,7 +44,7 @@ enum CharacterSyncFieldFlag
|
||||
CSF_LOCO_ANIMS = 4,
|
||||
CSF_LOCO_VALS = 8,
|
||||
CSF_ACTION_ANIM = 16,
|
||||
CSF_ACTION_PHASE = 32,
|
||||
CSF_ACTION_TIME = 32,
|
||||
CSF_AIM = 64,
|
||||
};
|
||||
|
||||
|
||||
@ -143,10 +143,12 @@ game::PlayerCharacter& game::Game::MovePlayerToWorld(PlayerGameInfo& player_info
|
||||
|
||||
auto old_character = old_world.GetPlayerCharacter(player);
|
||||
auto& tuning = old_character->GetHumanTuning();
|
||||
auto inventory = old_character->TakeInventory();
|
||||
old_world.RemovePlayer(player);
|
||||
|
||||
player.SetWorld(&new_world);
|
||||
auto& new_character = new_world.InsertPlayer(player, tuning, pos, yaw);
|
||||
new_character.SetInventory(std::move(inventory));
|
||||
|
||||
player_info.world = &new_world;
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "human_character.hpp"
|
||||
#include "drivable_vehicle.hpp"
|
||||
#include "utils/random.hpp"
|
||||
|
||||
static game::CharacterTuning GetCharacterTuning(const game::HumanCharacterTuning& tuning)
|
||||
{
|
||||
@ -21,6 +22,7 @@ void game::HumanCharacter::Update()
|
||||
{
|
||||
UpdateState();
|
||||
UpdateActionState();
|
||||
UpdateDispersion();
|
||||
Super::Update();
|
||||
}
|
||||
|
||||
@ -62,11 +64,36 @@ void game::HumanCharacter::Ride(Rideable* rideable, size_t seat_idx)
|
||||
}
|
||||
}
|
||||
|
||||
void game::HumanCharacter::Equip(std::shared_ptr<ItemInstance> item)
|
||||
{
|
||||
pending_item_ = std::move(item);
|
||||
}
|
||||
|
||||
game::HumanCharacter::~HumanCharacter()
|
||||
{
|
||||
Ride(nullptr, 0); // exit rideable
|
||||
}
|
||||
|
||||
bool game::HumanCharacter::HaveAmmo(const std::string& ammo_name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t game::HumanCharacter::GetAmmo(size_t required, const std::string& ammo_name)
|
||||
{
|
||||
return required; // unlimited by default
|
||||
}
|
||||
|
||||
int64_t game::HumanCharacter::GetTime() const
|
||||
{
|
||||
return GetWorld().GetTime();
|
||||
}
|
||||
|
||||
bool game::HumanCharacter::CanAim()
|
||||
{
|
||||
return !NeedReload() && !PendingItemSwitch() && !(GetVehicle() && IsDriver() && !item_);
|
||||
}
|
||||
|
||||
void game::HumanCharacter::SetAiming(bool aiming)
|
||||
{
|
||||
if (aiming == GetAiming())
|
||||
@ -76,16 +103,109 @@ void game::HumanCharacter::SetAiming(bool aiming)
|
||||
OnAimingChanged();
|
||||
}
|
||||
|
||||
bool game::HumanCharacter::CanFire()
|
||||
{
|
||||
return item_ && item_->ammo > 0 && ((GetTime() - last_fire_time_ + 40) >= item_->def->fire_delay);
|
||||
}
|
||||
|
||||
void game::HumanCharacter::Fire()
|
||||
{
|
||||
PlaySound("airrifle_fire");
|
||||
if (!item_)
|
||||
return; // fire wat?
|
||||
|
||||
// PlaySound("airrifle_fire");
|
||||
SendFire();
|
||||
|
||||
float range = 500.0f;
|
||||
// float dispersion = 5.0f; // m/100m
|
||||
|
||||
game::BulletInfo bullet{};
|
||||
bullet.start = GetEyePosition();
|
||||
bullet.end = bullet.start + GetAimDirection() * 1000.0f;
|
||||
bullet.end = bullet.start + ApplyRandomDispersion(GetAimDirection(), dispersion_) * range;
|
||||
bullet.damage = 1.0f;
|
||||
bullet.shooter = this;
|
||||
GetWorld().FireBullet(bullet);
|
||||
|
||||
last_fire_time_ = GetTime();
|
||||
dispersion_ = glm::min(dispersion_ + item_->def->dispersion_shot, item_->def->dispersion_max);
|
||||
|
||||
// it should always be >0 but if it has been faked this far
|
||||
// nothing else can be done than to pretend that
|
||||
// the bullet was there
|
||||
if (item_->ammo > 0)
|
||||
{
|
||||
--item_->ammo;
|
||||
}
|
||||
}
|
||||
|
||||
bool game::HumanCharacter::NeedReload()
|
||||
{
|
||||
return item_ && CanReload() && (item_->ammo == 0 || reloadheld_);
|
||||
}
|
||||
|
||||
bool game::HumanCharacter::CanReload()
|
||||
{
|
||||
return item_ && HaveAmmo(item_->def->ammo_type) && item_->ammo < item_->def->clip_size;
|
||||
}
|
||||
|
||||
void game::HumanCharacter::Reload()
|
||||
{
|
||||
if (!item_)
|
||||
return;
|
||||
|
||||
item_->ammo += GetAmmo(item_->def->clip_size - item_->ammo, item_->def->ammo_type);
|
||||
}
|
||||
|
||||
bool game::HumanCharacter::PendingItemSwitch()
|
||||
{
|
||||
return pending_item_ != item_;
|
||||
}
|
||||
|
||||
void game::HumanCharacter::SwitchItem()
|
||||
{
|
||||
if (item_ == pending_item_)
|
||||
return;
|
||||
|
||||
item_ = pending_item_;
|
||||
UpdateItemStuff();
|
||||
OnHeldItemChanged();
|
||||
}
|
||||
|
||||
void game::HumanCharacter::UpdateItemStuff()
|
||||
{
|
||||
// update legs anim
|
||||
if (state_ == HS_ON_FOOT)
|
||||
{
|
||||
if (item_ && !item_->def->legs_anim.empty())
|
||||
SetIdleAnim(item_->def->legs_anim);
|
||||
else
|
||||
SetIdleAnim("idle");
|
||||
|
||||
// SetIdleAnim("idle_relaxed");
|
||||
}
|
||||
|
||||
// update view item
|
||||
if (item_)
|
||||
{
|
||||
SetViewItem(item_->def->name);
|
||||
SetWeightSpeedMult(item_->def->walk_speed_mult);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetViewItem("");
|
||||
SetWeightSpeedMult(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void game::HumanCharacter::PlayItemActionAnim(const std::string assets::Item::*anim, float speed)
|
||||
{
|
||||
if (!item_)
|
||||
{
|
||||
ClearActionAnim();
|
||||
return;
|
||||
}
|
||||
|
||||
PlayActionAnim(item_->def.get()->*anim, speed);
|
||||
}
|
||||
|
||||
void game::HumanCharacter::UpdateState()
|
||||
@ -178,7 +298,7 @@ void game::HumanCharacter::StateOnFootEnter()
|
||||
SetMovementType(CMT_TURN);
|
||||
EnablePhysics(true);
|
||||
|
||||
EnterActionState(ACTION_IDLE);
|
||||
ResetActionState();
|
||||
}
|
||||
|
||||
game::HumanCharacterState game::HumanCharacter::StateOnFootUpdate()
|
||||
@ -205,7 +325,7 @@ void game::HumanCharacter::StateRidingEnter()
|
||||
SetYaw(0.0f);
|
||||
SetMovementType(CMT_DISABLED);
|
||||
|
||||
EnterActionState(ACTION_IDLE);
|
||||
ResetActionState();
|
||||
}
|
||||
|
||||
game::HumanCharacterState game::HumanCharacter::StateRidingUpdate()
|
||||
@ -231,6 +351,13 @@ game::HumanCharacterState game::HumanCharacter::StateKnockedDownUpdate()
|
||||
return HS_INIT;
|
||||
}
|
||||
|
||||
void game::HumanCharacter::ResetActionState()
|
||||
{
|
||||
item_.reset();
|
||||
EnterActionState(ACTION_IDLE);
|
||||
UpdateItemStuff();
|
||||
}
|
||||
|
||||
void game::HumanCharacter::UpdateActionState()
|
||||
{
|
||||
while (true)
|
||||
@ -247,72 +374,113 @@ void game::HumanCharacter::UpdateActionState()
|
||||
void game::HumanCharacter::EnterActionState(ActionState state)
|
||||
{
|
||||
actionstate_ = state;
|
||||
actionstate_start_ = GetWorld().GetTime();
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ACTION_IDLE:
|
||||
if (state_ == HS_ON_FOOT)
|
||||
SetIdleAnim("idle_relaxed");
|
||||
SetAiming(false);
|
||||
SetCanSprint(true);
|
||||
PlayActionAnim("rifle_idle");
|
||||
SetViewItem("airsniper");
|
||||
PlayItemActionAnim(&assets::Item::idle_anim);
|
||||
break;
|
||||
|
||||
case ACTION_RAISE:
|
||||
SwitchItem();
|
||||
SetAiming(false);
|
||||
SetCanSprint(true);
|
||||
PlayItemActionAnim(&assets::Item::raise_anim, 3.0f);
|
||||
break;
|
||||
|
||||
case ACTION_AIM:
|
||||
SetAiming(true);
|
||||
SetCanSprint(false);
|
||||
PlayActionAnim("rifle_aim", 3.0f);
|
||||
PlayItemActionAnim(&assets::Item::aim_anim, 3.0f);
|
||||
break;
|
||||
|
||||
case ACTION_AIMING:
|
||||
SetAiming(true);
|
||||
SetCanSprint(false);
|
||||
PlayActionAnim("rifle_aiming");
|
||||
PlayItemActionAnim(&assets::Item::aiming_anim);
|
||||
break;
|
||||
|
||||
case ACTION_FIRE:
|
||||
SetAiming(true);
|
||||
SetCanSprint(false);
|
||||
PlayActionAnim("rifle_fire");
|
||||
PlayItemActionAnim(&assets::Item::use_anim);
|
||||
Fire();
|
||||
break;
|
||||
|
||||
case ACTION_FIRE_REPEAT:
|
||||
PlayItemActionAnim(&assets::Item::use_anim, -5.0f);
|
||||
break;
|
||||
|
||||
case ACTION_RELOAD:
|
||||
// SetAiming(true);
|
||||
// SetCanSprint(true);
|
||||
PlayItemActionAnim(&assets::Item::reload_anim);
|
||||
break;
|
||||
|
||||
case ACTION_UNAIM:
|
||||
SetAiming(false);
|
||||
SetCanSprint(false);
|
||||
PlayActionAnim("rifle_aim", -3.0f);
|
||||
PlayItemActionAnim(&assets::Item::aim_anim, -3.0f);
|
||||
break;
|
||||
|
||||
case ACTION_PUTAWAY:
|
||||
SetAiming(false);
|
||||
SetCanSprint(true);
|
||||
PlayItemActionAnim(&assets::Item::raise_anim, -3.0f);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t game::HumanCharacter::GetActionStateTime() const
|
||||
{
|
||||
return GetWorld().GetTime() - actionstate_start_;
|
||||
}
|
||||
|
||||
game::ActionState game::HumanCharacter::CheckActionStateTransition()
|
||||
{
|
||||
switch (actionstate_)
|
||||
{
|
||||
case ACTION_IDLE:
|
||||
if (aimheld_) // want aim
|
||||
if (PendingItemSwitch())
|
||||
return ACTION_PUTAWAY;
|
||||
|
||||
if (NeedReload())
|
||||
return ACTION_RELOAD;
|
||||
|
||||
if (aimheld_ && CanAim()) // want aim
|
||||
return ACTION_AIM;
|
||||
|
||||
return ACTION_IDLE;
|
||||
|
||||
case ACTION_RAISE:
|
||||
if (PendingItemSwitch())
|
||||
return ACTION_PUTAWAY;
|
||||
|
||||
if (IsActionAnimDone())
|
||||
return ACTION_IDLE;
|
||||
|
||||
return ACTION_RAISE;
|
||||
|
||||
case ACTION_AIM:
|
||||
if (!aimheld_ || !CanAim())
|
||||
return ACTION_UNAIM;
|
||||
|
||||
if (IsActionAnimDone())
|
||||
return ACTION_AIMING;
|
||||
|
||||
if (!aimheld_) // stop aiming immediately
|
||||
return ACTION_UNAIM;
|
||||
|
||||
return ACTION_AIM;
|
||||
|
||||
case ACTION_AIMING:
|
||||
if (!aimheld_)
|
||||
return ACTION_UNAIM; // wants aim no more
|
||||
if (!aimheld_ || !CanAim())
|
||||
return ACTION_UNAIM;
|
||||
|
||||
if (fireheld_)
|
||||
if (fireheld_ && CanFire())
|
||||
return ACTION_FIRE;
|
||||
|
||||
return ACTION_AIMING;
|
||||
@ -321,18 +489,59 @@ game::ActionState game::HumanCharacter::CheckActionStateTransition()
|
||||
if (IsActionAnimDone())
|
||||
return ACTION_AIMING;
|
||||
|
||||
if (CanAim() && fireheld_ && CanFire())
|
||||
return ACTION_FIRE_REPEAT;
|
||||
|
||||
return ACTION_FIRE;
|
||||
|
||||
case ACTION_FIRE_REPEAT:
|
||||
// proxy to enter fire state again and reset anims and stuff
|
||||
if (GetActionStateTime() > 0)
|
||||
return ACTION_FIRE;
|
||||
|
||||
return ACTION_FIRE_REPEAT;
|
||||
|
||||
case ACTION_RELOAD:
|
||||
// SetAiming(aimheld_); // optional here
|
||||
if (IsActionAnimDone())
|
||||
{
|
||||
Reload();
|
||||
return ACTION_IDLE;
|
||||
}
|
||||
|
||||
return ACTION_RELOAD;
|
||||
|
||||
case ACTION_UNAIM:
|
||||
if (aimheld_ && CanAim()) // start aiming again
|
||||
return ACTION_AIM;
|
||||
|
||||
if (IsActionAnimDone())
|
||||
return ACTION_IDLE;
|
||||
|
||||
if (aimheld_) // start aiming again
|
||||
return ACTION_AIM;
|
||||
|
||||
return ACTION_UNAIM;
|
||||
|
||||
case ACTION_PUTAWAY:
|
||||
if (IsActionAnimDone())
|
||||
return ACTION_RAISE;
|
||||
|
||||
if (!PendingItemSwitch()) // possibly player wants that item again
|
||||
return ACTION_RAISE;
|
||||
|
||||
return ACTION_PUTAWAY;
|
||||
|
||||
default:
|
||||
return actionstate_;
|
||||
}
|
||||
}
|
||||
|
||||
void game::HumanCharacter::UpdateDispersion()
|
||||
{
|
||||
if (!item_)
|
||||
{
|
||||
dispersion_ = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
dispersion_ = glm::clamp(dispersion_ - (item_->def->dispersion_decay / 25.0f), item_->def->dispersion_min,
|
||||
item_->def->dispersion_max);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "character.hpp"
|
||||
#include "item_instance.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
@ -32,10 +33,14 @@ enum HumanCharacterState
|
||||
enum ActionState
|
||||
{
|
||||
ACTION_IDLE,
|
||||
ACTION_RAISE,
|
||||
ACTION_AIM,
|
||||
ACTION_AIMING,
|
||||
ACTION_FIRE,
|
||||
ACTION_FIRE_REPEAT,
|
||||
ACTION_RELOAD,
|
||||
ACTION_UNAIM,
|
||||
ACTION_PUTAWAY,
|
||||
};
|
||||
|
||||
class HumanCharacter : public Character
|
||||
@ -60,16 +65,33 @@ public:
|
||||
|
||||
void SetAimHeld(bool aimheld) { aimheld_ = aimheld; }
|
||||
void SetFireHeld(bool fireheld) { fireheld_ = fireheld; }
|
||||
void SetReloadHeld(bool reloadheld) { reloadheld_ = reloadheld; }
|
||||
|
||||
void Equip(std::shared_ptr<ItemInstance> item);
|
||||
const std::shared_ptr<ItemInstance>& GetHeldItem() const { return item_; }
|
||||
|
||||
virtual ~HumanCharacter() override;
|
||||
|
||||
protected:
|
||||
virtual void OnRideableChanged() {}
|
||||
virtual void OnAimingChanged() {}
|
||||
virtual void OnHeldItemChanged() {}
|
||||
virtual bool HaveAmmo(const std::string& ammo_name);
|
||||
virtual size_t GetAmmo(size_t required, const std::string& ammo_name);
|
||||
|
||||
private:
|
||||
int64_t GetTime() const;
|
||||
bool CanAim();
|
||||
void SetAiming(bool aiming);
|
||||
bool CanFire();
|
||||
void Fire();
|
||||
bool NeedReload();
|
||||
bool CanReload();
|
||||
void Reload();
|
||||
bool PendingItemSwitch();
|
||||
void SwitchItem();
|
||||
void UpdateItemStuff();
|
||||
void PlayItemActionAnim(const std::string assets::Item::*anim, float speed = 1.0f);
|
||||
|
||||
void UpdateState();
|
||||
void SetSignal(HumanCharacterStateSignal signal);
|
||||
@ -91,12 +113,14 @@ private:
|
||||
HumanCharacterState StateKnockedDownUpdate();
|
||||
// void StateKnockedDownExit();
|
||||
|
||||
|
||||
void ResetActionState();
|
||||
void UpdateActionState();
|
||||
|
||||
void EnterActionState(ActionState state);
|
||||
int64_t GetActionStateTime() const;
|
||||
ActionState CheckActionStateTransition();
|
||||
|
||||
void UpdateDispersion();
|
||||
|
||||
private:
|
||||
HumanCharacterTuning human_tuning_;
|
||||
|
||||
@ -112,9 +136,16 @@ private:
|
||||
|
||||
bool aimheld_ = false;
|
||||
bool fireheld_ = false;
|
||||
bool reloadheld_ = false;
|
||||
|
||||
ActionState actionstate_ = ACTION_IDLE;
|
||||
int64_t actionstate_start_ = 0;
|
||||
|
||||
std::shared_ptr<ItemInstance> item_;
|
||||
std::shared_ptr<ItemInstance> pending_item_;
|
||||
|
||||
int64_t last_fire_time_ = 0;
|
||||
float dispersion_ = 0.0f;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
16
src/game/inventory.hpp
Normal file
16
src/game/inventory.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "item_instance.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
|
||||
struct Inventory
|
||||
{
|
||||
std::shared_ptr<ItemInstance> slots[10];
|
||||
size_t active_slot = 0;
|
||||
|
||||
std::map<std::string, size_t> ammo;
|
||||
};
|
||||
|
||||
}
|
||||
12
src/game/item_instance.cpp
Normal file
12
src/game/item_instance.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "item_instance.hpp"
|
||||
#include "assets/cache.hpp"
|
||||
|
||||
game::ItemInstance::ItemInstance(std::shared_ptr<const assets::Item> def) : def(std::move(def))
|
||||
{
|
||||
ammo = this->def->clip_size; // full clip by default
|
||||
}
|
||||
|
||||
game::ItemInstance::ItemInstance(const std::string& name)
|
||||
: ItemInstance(assets::CacheManager::GetItem("data/" + name + ".item"))
|
||||
{
|
||||
}
|
||||
17
src/game/item_instance.hpp
Normal file
17
src/game/item_instance.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "assets/item.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
|
||||
struct ItemInstance
|
||||
{
|
||||
std::shared_ptr<const assets::Item> def;
|
||||
size_t ammo = 0;
|
||||
|
||||
ItemInstance(std::shared_ptr<const assets::Item> def);
|
||||
ItemInstance(const std::string& name);
|
||||
};
|
||||
|
||||
}
|
||||
@ -9,18 +9,18 @@ game::Marker::Marker(World& world, const MarkerInfo& info) : Super(world, net::E
|
||||
{
|
||||
root_.local.position = info_.position;
|
||||
root_.UpdateMatrix();
|
||||
|
||||
max_distance_ = 150.0f;
|
||||
}
|
||||
|
||||
void game::Marker::SendInitData(Player& player, net::OutMessage& msg) const
|
||||
{
|
||||
Super::SendInitData(player, msg);
|
||||
|
||||
net::PositionQ pos_q;
|
||||
net::EncodePosition(info_.position, pos_q);
|
||||
|
||||
msg.Write(info_.type);
|
||||
net::WritePositionQ(msg, pos_q);
|
||||
net::WritePosition(msg, info_.position);
|
||||
net::WriteRGB(msg, info_.color);
|
||||
msg.Write(net::ModelName(info_.model));
|
||||
}
|
||||
|
||||
void game::Marker::Update()
|
||||
@ -31,6 +31,9 @@ void game::Marker::Update()
|
||||
|
||||
bool game::Marker::QueryUseTarget(PlayerCharacter& character, uint32_t target_id, UseTargetQueryResult& res)
|
||||
{
|
||||
if (!useable_)
|
||||
return false;
|
||||
|
||||
if (!query_cb_)
|
||||
return false;
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ public:
|
||||
virtual void Use(PlayerCharacter& character, uint32_t target_id) override;
|
||||
|
||||
void SetUseTarget(const std::string& name, MarkerQueryCallback query, MarkerUseCallback use);
|
||||
void SetUseable(bool useable) { useable_ = useable; }
|
||||
|
||||
virtual ~Marker() override;
|
||||
|
||||
@ -35,6 +36,8 @@ private:
|
||||
|
||||
MarkerQueryCallback query_cb_;
|
||||
MarkerUseCallback use_cb_;
|
||||
|
||||
bool useable_ = true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ enum MarkerType : uint8_t
|
||||
{
|
||||
MARKER_FOOT,
|
||||
MARKER_VEHICLE,
|
||||
MARKER_PICKUP,
|
||||
};
|
||||
|
||||
struct MarkerInfo
|
||||
@ -18,7 +19,7 @@ struct MarkerInfo
|
||||
glm::vec3 position;
|
||||
MarkerType type;
|
||||
uint32_t color;
|
||||
std::string icon;
|
||||
std::string model;
|
||||
};
|
||||
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "assets/cache.hpp"
|
||||
#include "player.hpp"
|
||||
#include "vehicle.hpp"
|
||||
#include "player_character.hpp"
|
||||
@ -21,7 +22,7 @@ namespace game
|
||||
|
||||
static const char* GetRandomCarModel()
|
||||
{
|
||||
const char* vehicles[] = {"pickup_hd", "passat", "twingo", "polskifiat", "cow_static", "pig_static", "avia"};
|
||||
const char* vehicles[] = {"pickup_hd", "passat", "twingo", "polskifiat", "avia"};
|
||||
return vehicles[rand() % (sizeof(vehicles) / sizeof(vehicles[0]))];
|
||||
}
|
||||
|
||||
@ -88,6 +89,10 @@ game::OpenWorld::OpenWorld(Game& game) : EnterableWorld("openworld"), game_(game
|
||||
CreateTuningGarage(loc.transform.position, glm::eulerAngles(loc.transform.rotation).x);
|
||||
}
|
||||
|
||||
CreateItemPickups("pickup_uzi", "uzi");
|
||||
CreateItemPickups("pickup_ak47", "ak47");
|
||||
CreateItemPickups("pickup_airsniper", "airsniper");
|
||||
|
||||
// cow
|
||||
auto& cow = Spawn<Cow>(glm::vec3(0.0f, 0.0f, 2.0f), 0.0f);
|
||||
cow.SetNametag("no ty krávo");
|
||||
@ -200,7 +205,7 @@ void game::OpenWorld::CreateTuningGarage(const glm::vec3& position, float yaw)
|
||||
marker_info.position = position;
|
||||
marker_info.type = MARKER_VEHICLE;
|
||||
marker_info.color = 0x884400;
|
||||
marker_info.icon = "tuning";
|
||||
marker_info.model = "marker_tuning";
|
||||
|
||||
auto& marker = Spawn<Marker>(marker_info);
|
||||
marker.SetUseTarget("vject do tunírny",
|
||||
@ -245,6 +250,53 @@ void game::OpenWorld::CreateTuningGarage(const glm::vec3& position, float yaw)
|
||||
});
|
||||
}
|
||||
|
||||
void game::OpenWorld::CreateItemPickups(const std::string& loc_name, const std::string& item_name)
|
||||
{
|
||||
for (auto locs = GetMap().GetLocations(loc_name); const auto& loc : locs)
|
||||
{
|
||||
CreateItemPickup(loc.transform.position, item_name);
|
||||
}
|
||||
}
|
||||
|
||||
void game::OpenWorld::CreateItemPickup(const glm::vec3& position, const std::string& item_name)
|
||||
{
|
||||
auto item_def = assets::CacheManager::GetItem("data/" + item_name + ".item");
|
||||
|
||||
MarkerInfo marker_info{};
|
||||
marker_info.position = position;
|
||||
marker_info.type = MARKER_PICKUP;
|
||||
marker_info.color = 0xFFFFFF;
|
||||
marker_info.model = item_def->model_name;
|
||||
|
||||
auto& marker = Spawn<Marker>(marker_info);
|
||||
marker.SetUseTarget(
|
||||
"sebrat " + item_name,
|
||||
[](PlayerCharacter& character, UseTargetQueryResult& res) {
|
||||
res.enabled = true;
|
||||
res.delay = 0.1f;
|
||||
res.error_text = nullptr;
|
||||
return true;
|
||||
},
|
||||
[this, position, item_def,
|
||||
&marker](PlayerCharacter& character) {
|
||||
auto player = character.GetPlayer();
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
character.GiveItem(std::make_shared<ItemInstance>(item_def->name));
|
||||
character.GiveAmmo(item_def->ammo_type, item_def->clip_size * 15);
|
||||
character.PlaySound("pickup_ammo");
|
||||
|
||||
player->SendChat("sebrals " + item_def->name);
|
||||
marker.SetUseable(false);
|
||||
marker.Remove();
|
||||
|
||||
Schedule(5000, [this, position, item_def]() {
|
||||
CreateItemPickup(position, item_def->name);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void game::OpenWorld::RecoverPlayer(Player& player)
|
||||
{
|
||||
auto character = GetPlayerCharacter(player);
|
||||
|
||||
@ -24,6 +24,8 @@ private:
|
||||
void SpawnBot();
|
||||
|
||||
void CreateTuningGarage(const glm::vec3& position, float yaw);
|
||||
void CreateItemPickups(const std::string& loc_name, const std::string& item_name);
|
||||
void CreateItemPickup(const glm::vec3& position, const std::string& item);
|
||||
|
||||
void RecoverPlayer(Player& player);
|
||||
bool GetRecoveryPosition(const glm::vec3& current, glm::vec3& recovery);
|
||||
|
||||
@ -36,6 +36,9 @@ void game::Player::Update()
|
||||
SyncWorld();
|
||||
SendMenuMsgs();
|
||||
UpdateCamera();
|
||||
|
||||
// reset for next frame
|
||||
in_new_ = 0;
|
||||
}
|
||||
|
||||
void game::Player::SetWorld(World* world)
|
||||
@ -100,6 +103,62 @@ void game::Player::CloseMenu(const RemoteMenu& menu)
|
||||
remote_menu_.reset();
|
||||
}
|
||||
|
||||
void game::Player::SetHudData(const PlayerHudData& hud_data)
|
||||
{
|
||||
PlayerHudFields fields = 0;
|
||||
|
||||
auto msg = BeginMsg(net::MSG_HUD);
|
||||
auto fields_pos = msg.Reserve<PlayerHudFields>();
|
||||
|
||||
if (hud_data.health != hud_data_.health)
|
||||
{
|
||||
fields |= PHUD_HEALTH;
|
||||
hud_data_.health = hud_data.health;
|
||||
msg.Write(hud_data.health);
|
||||
}
|
||||
|
||||
if (hud_data.weapon_slots != hud_data_.weapon_slots)
|
||||
{
|
||||
fields |= PHUD_WEAPON_SLOTS;
|
||||
hud_data_.weapon_slots = hud_data.weapon_slots;
|
||||
msg.Write(hud_data.weapon_slots);
|
||||
}
|
||||
|
||||
if (hud_data.held_item != hud_data_.held_item)
|
||||
{
|
||||
fields |= PHUD_ITEM;
|
||||
hud_data_.held_item = hud_data.held_item;
|
||||
msg.Write(net::ModelName(hud_data.held_item));
|
||||
}
|
||||
|
||||
if (hud_data.ammo_loaded != hud_data_.ammo_loaded)
|
||||
{
|
||||
fields |= PHUD_AMMO_LOADED;
|
||||
hud_data_.ammo_loaded = hud_data.ammo_loaded;
|
||||
msg.Write(hud_data.ammo_loaded);
|
||||
}
|
||||
|
||||
if (hud_data.ammo_total != hud_data_.ammo_total)
|
||||
{
|
||||
fields |= PHUD_AMMO_TOTAL;
|
||||
hud_data_.ammo_total = hud_data.ammo_total;
|
||||
msg.Write(hud_data.ammo_total);
|
||||
}
|
||||
|
||||
if (fields == 0)
|
||||
{
|
||||
DiscardMsg();
|
||||
return;
|
||||
}
|
||||
|
||||
msg.WriteAt(fields_pos, fields);
|
||||
}
|
||||
|
||||
void game::Player::ResetHudData()
|
||||
{
|
||||
SetHudData(PlayerHudData{});
|
||||
}
|
||||
|
||||
bool game::Player::GetView(glm::vec3& eye, glm::vec3& forward)
|
||||
{
|
||||
if (!world_)
|
||||
@ -358,10 +417,17 @@ bool game::Player::ProcessMenuActionMsg(net::InMessage& msg)
|
||||
|
||||
void game::Player::Input(PlayerInputType type, bool enabled)
|
||||
{
|
||||
PlayerInputFlags flag = 1 << type;
|
||||
|
||||
if (enabled)
|
||||
in_ |= (1 << type);
|
||||
{
|
||||
in_ |= flag;
|
||||
in_new_ |= flag;
|
||||
}
|
||||
else
|
||||
in_ &= ~(1 << type);
|
||||
{
|
||||
in_ &= ~flag;
|
||||
}
|
||||
|
||||
game_.PlayerInput(*this, type, enabled);
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "remote_menu.hpp"
|
||||
#include "camera_info.hpp"
|
||||
#include "camera_controller.hpp"
|
||||
#include "player_hud_data.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
@ -40,9 +41,13 @@ public:
|
||||
void CloseMenu(const RemoteMenu& menu);
|
||||
bool HasOpenMenu() const { return (bool)remote_menu_; }
|
||||
|
||||
void SetHudData(const PlayerHudData& hud_data);
|
||||
void ResetHudData();
|
||||
|
||||
const std::string& GetName() const { return name_; }
|
||||
|
||||
PlayerInputFlags GetInput() const { return in_; }
|
||||
PlayerInputFlags GetNewInput() const { return in_new_; }
|
||||
float GetViewYaw() const { return camera_controller_.GetYaw(); }
|
||||
float GetViewPitch() const { return camera_controller_.GetPitch(); }
|
||||
bool GetView(glm::vec3& eye, glm::vec3& forward);
|
||||
@ -88,6 +93,7 @@ private:
|
||||
int64_t last_env_time_ = 0;
|
||||
|
||||
PlayerInputFlags in_ = 0;
|
||||
PlayerInputFlags in_new_ = 0;
|
||||
|
||||
CameraInfo camera_info_;
|
||||
CameraController camera_controller_;
|
||||
@ -97,6 +103,9 @@ private:
|
||||
// TODO: allow more menus
|
||||
net::MenuId menu_id_ = 0;
|
||||
std::unique_ptr<RemoteMenu> remote_menu_;
|
||||
|
||||
// hud
|
||||
PlayerHudData hud_data_;
|
||||
};
|
||||
|
||||
}
|
||||
@ -8,18 +8,27 @@ game::PlayerCharacter::PlayerCharacter(World& world, Player& player, const Human
|
||||
UpdatePlayerCamera();
|
||||
SetNametag(player.GetName());
|
||||
SendUseTargetInfo();
|
||||
|
||||
// give some shit
|
||||
GiveItem(std::make_shared<ItemInstance>("airsniper"), false);
|
||||
// GiveItem(std::make_shared<ItemInstance>("ak47"), false);
|
||||
// GiveItem(std::make_shared<ItemInstance>("uzi"), false);
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::Update()
|
||||
{
|
||||
UpdateUseTarget();
|
||||
UpdateAimTarget();
|
||||
CheckItemSwitch();
|
||||
UpdateInputs();
|
||||
Super::Update();
|
||||
|
||||
|
||||
if (GetRideable() && IsDriver())
|
||||
{
|
||||
GetRideable()->SetRideableViewAngles(GetViewYaw(), GetViewPitch());
|
||||
}
|
||||
|
||||
UpdateHudData();
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::ProcessInput(PlayerInputType type, bool enabled)
|
||||
@ -41,6 +50,64 @@ void game::PlayerCharacter::DetachFromPlayer()
|
||||
player_ = nullptr;
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::SetInventory(std::unique_ptr<Inventory> inventory)
|
||||
{
|
||||
if (inventory_)
|
||||
{
|
||||
TakeInventory();
|
||||
}
|
||||
|
||||
inventory_ = std::move(inventory);
|
||||
UpdateHudSlots();
|
||||
|
||||
// if (inventory_->active_slot > 0)
|
||||
// {
|
||||
// SetWeaponSlot(inventory_->active_slot);
|
||||
// }
|
||||
}
|
||||
|
||||
std::unique_ptr<game::Inventory> game::PlayerCharacter::TakeInventory()
|
||||
{
|
||||
Equip(nullptr);
|
||||
auto inv = std::move(inventory_);
|
||||
UpdateHudSlots();
|
||||
return inv;
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::GiveItem(std::shared_ptr<ItemInstance> item, bool can_equip)
|
||||
{
|
||||
EnsureInventory();
|
||||
|
||||
size_t slot_idx = ((item->def->slot + 9) % 10);
|
||||
auto& slot = inventory_->slots[slot_idx];
|
||||
|
||||
bool equip = can_equip && (!GetHeldItem() || GetHeldItem() == slot);
|
||||
|
||||
if (!slot || slot->def->name != item->def->name)
|
||||
{
|
||||
// current item in the slot is other item or none
|
||||
slot = std::move(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// already have this, extract ammo
|
||||
GiveAmmo(item->def->ammo_type, item->ammo);
|
||||
}
|
||||
|
||||
UpdateHudSlots();
|
||||
|
||||
if (equip)
|
||||
{
|
||||
SetWeaponSlot(slot_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::GiveAmmo(const std::string& ammo_name, size_t count)
|
||||
{
|
||||
EnsureInventory();
|
||||
inventory_->ammo[ammo_name] += count;
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::OnRideableChanged()
|
||||
{
|
||||
UpdatePlayerCamera();
|
||||
@ -52,6 +119,39 @@ void game::PlayerCharacter::OnAimingChanged()
|
||||
UpdatePlayerCamera();
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::OnHeldItemChanged()
|
||||
{
|
||||
const auto& item = GetHeldItem();
|
||||
hud_data_.held_item = item ? item->def->name : "";
|
||||
}
|
||||
|
||||
bool game::PlayerCharacter::HaveAmmo(const std::string& ammo_name)
|
||||
{
|
||||
if (!inventory_)
|
||||
return false;
|
||||
|
||||
auto it = inventory_->ammo.find(ammo_name);
|
||||
if (it == inventory_->ammo.end())
|
||||
return false;
|
||||
|
||||
return it->second > 0;
|
||||
}
|
||||
|
||||
size_t game::PlayerCharacter::GetAmmo(size_t required, const std::string& ammo_name)
|
||||
{
|
||||
if (!inventory_)
|
||||
return 0;
|
||||
|
||||
auto it = inventory_->ammo.find(ammo_name);
|
||||
if (it == inventory_->ammo.end())
|
||||
return 0;
|
||||
|
||||
size_t give = glm::min(it->second, required);
|
||||
it->second -= give;
|
||||
|
||||
return give;
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::UpdatePlayerCamera()
|
||||
{
|
||||
if (!player_)
|
||||
@ -75,9 +175,16 @@ void game::PlayerCharacter::UpdateInputs()
|
||||
{
|
||||
SetInputs(0);
|
||||
|
||||
auto rideable_in = in;
|
||||
|
||||
if (IsDriver())
|
||||
{
|
||||
rideable->SetRideableInput(in);
|
||||
if (GetVehicle() && GetHeldItem() && GetHeldItem()->def->twohanded)
|
||||
{
|
||||
rideable_in &= ~((1 << IN_RIGHT) | (1 << IN_LEFT));
|
||||
}
|
||||
|
||||
rideable->SetRideableInput(rideable_in);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -87,6 +194,7 @@ void game::PlayerCharacter::UpdateInputs()
|
||||
|
||||
SetAimHeld(in & (1 << IN_ATTACK_SECONDARY));
|
||||
SetFireHeld(in & (1 << IN_ATTACK_PRIMARY));
|
||||
SetReloadHeld(in & (1 << IN_RELOAD));
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::UpdateAimTarget()
|
||||
@ -111,6 +219,29 @@ void game::PlayerCharacter::UpdateAimTarget()
|
||||
// GetWorld().BeamBox(target - 0.05f, target + 0.05f, 0xFFFF00, 1.5f / 25.0f);
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::CheckItemSwitch()
|
||||
{
|
||||
if (!player_ || !inventory_)
|
||||
return;
|
||||
|
||||
auto in = player_->GetNewInput();
|
||||
|
||||
if (in & (1 << IN_HOLSTER))
|
||||
{
|
||||
Equip(GetHeldItem() ? nullptr : inventory_->slots[inventory_->active_slot]);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
{
|
||||
if ((in & (1 << (IN_WEAPON_1 + i))) == 0)
|
||||
continue;
|
||||
|
||||
SetWeaponSlot(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::UpdateUseTarget()
|
||||
{
|
||||
UseTargetQueryResult res{};
|
||||
@ -182,3 +313,59 @@ void game::PlayerCharacter::SendUseTargetInfo()
|
||||
|
||||
player_->SetUseTarget(use_target_->desc, error_text, using_ ? use_delay_ - use_progress_ : 0.0f);
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::EnsureInventory()
|
||||
{
|
||||
if (!inventory_)
|
||||
{
|
||||
inventory_ = std::make_unique<Inventory>();
|
||||
}
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::SetWeaponSlot(size_t slot)
|
||||
{
|
||||
if (!inventory_ || !inventory_->slots[slot])
|
||||
return;
|
||||
|
||||
inventory_->active_slot = slot;
|
||||
Equip(inventory_->slots[slot]);
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::UpdateHudData()
|
||||
{
|
||||
if (!player_)
|
||||
return;
|
||||
|
||||
hud_data_.health = 100;
|
||||
|
||||
const auto& item = GetHeldItem();
|
||||
hud_data_.ammo_loaded = item ? item->ammo : 0;
|
||||
|
||||
hud_data_.ammo_total = 0;
|
||||
if (item && inventory_)
|
||||
{
|
||||
auto it = inventory_->ammo.find(item->def->ammo_type);
|
||||
if (it != inventory_->ammo.end())
|
||||
{
|
||||
hud_data_.ammo_total = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
player_->SetHudData(hud_data_);
|
||||
}
|
||||
|
||||
void game::PlayerCharacter::UpdateHudSlots()
|
||||
{
|
||||
hud_data_.weapon_slots = 0;
|
||||
|
||||
if (!inventory_)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
{
|
||||
if (inventory_->slots[i])
|
||||
hud_data_.weapon_slots |= 1 << i;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "drivable_vehicle.hpp"
|
||||
#include "player.hpp"
|
||||
#include "world.hpp"
|
||||
#include "inventory.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
@ -22,19 +23,34 @@ public:
|
||||
|
||||
Player* GetPlayer() const { return player_; }
|
||||
|
||||
void SetInventory(std::unique_ptr<Inventory> inventory);
|
||||
std::unique_ptr<Inventory> TakeInventory();
|
||||
|
||||
void GiveItem(std::shared_ptr<ItemInstance> item, bool can_equip = true);
|
||||
void GiveAmmo(const std::string& ammo_name, size_t count);
|
||||
|
||||
protected:
|
||||
virtual void OnRideableChanged() override;
|
||||
virtual void OnAimingChanged() override;
|
||||
virtual void OnHeldItemChanged() override;
|
||||
virtual bool HaveAmmo(const std::string& ammo_name) override;
|
||||
virtual size_t GetAmmo(size_t required, const std::string& ammo_name) override;
|
||||
|
||||
private:
|
||||
void UpdatePlayerCamera();
|
||||
void UpdateInputs();
|
||||
void UpdateAimTarget();
|
||||
void CheckItemSwitch();
|
||||
|
||||
void UpdateUseTarget();
|
||||
void UseChanged(bool enabled);
|
||||
void SendUseTargetInfo();
|
||||
|
||||
void EnsureInventory();
|
||||
void SetWeaponSlot(size_t slot);
|
||||
|
||||
void UpdateHudData();
|
||||
void UpdateHudSlots();
|
||||
|
||||
private:
|
||||
Player* player_;
|
||||
@ -46,6 +62,10 @@ private:
|
||||
const char* use_error_ = nullptr;
|
||||
bool using_ = false; // not drugs lol
|
||||
float use_progress_ = 0.0f;
|
||||
|
||||
std::unique_ptr<Inventory> inventory_;
|
||||
|
||||
PlayerHudData hud_data_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
34
src/game/player_hud_data.hpp
Normal file
34
src/game/player_hud_data.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "net/defs.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
|
||||
using PlayerHudFields = uint8_t;
|
||||
enum PlayerHudField : PlayerHudFields
|
||||
{
|
||||
PHUD_HEALTH = 1,
|
||||
PHUD_WEAPON_SLOTS = 2,
|
||||
PHUD_ITEM = 4,
|
||||
PHUD_AMMO_LOADED = 8,
|
||||
PHUD_AMMO_TOTAL = 16,
|
||||
};
|
||||
|
||||
struct PlayerHudData
|
||||
{
|
||||
// general
|
||||
uint8_t health = 0;
|
||||
// TODO: stamina ?
|
||||
uint8_t weapon_slots = 0;
|
||||
|
||||
// held item
|
||||
std::string held_item;
|
||||
uint8_t ammo_loaded = 0;
|
||||
uint32_t ammo_total = 0;
|
||||
|
||||
// TODO: use target
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
namespace game
|
||||
{
|
||||
using PlayerInputFlags = uint16_t;
|
||||
using PlayerInputFlags = uint32_t;
|
||||
|
||||
enum PlayerInputType : uint8_t
|
||||
{
|
||||
@ -18,6 +18,18 @@ namespace game
|
||||
IN_USE,
|
||||
IN_ATTACK_PRIMARY,
|
||||
IN_ATTACK_SECONDARY,
|
||||
IN_HOLSTER,
|
||||
IN_RELOAD,
|
||||
IN_WEAPON_1,
|
||||
IN_WEAPON_2,
|
||||
IN_WEAPON_3,
|
||||
IN_WEAPON_4,
|
||||
IN_WEAPON_5,
|
||||
IN_WEAPON_6,
|
||||
IN_WEAPON_7,
|
||||
IN_WEAPON_8,
|
||||
IN_WEAPON_9,
|
||||
IN_WEAPON_0,
|
||||
IN_DEBUG1,
|
||||
IN_DEBUG2,
|
||||
IN_DEBUG3,
|
||||
|
||||
@ -206,22 +206,22 @@ static std::string GetMaterialImpactFx(collision::Material material)
|
||||
{
|
||||
switch (material)
|
||||
{
|
||||
// case collision::PM_STONE:
|
||||
// return "impact_stone";
|
||||
// case collision::PM_DIRT:
|
||||
// return "impact_dirt";
|
||||
case collision::PM_STONE:
|
||||
return "impact_stone";
|
||||
case collision::PM_DIRT:
|
||||
return "impact_dirt";
|
||||
case collision::PM_GRASS:
|
||||
return "impact_grass";
|
||||
// case collision::PM_WOOD:
|
||||
// // return "impact_wood";
|
||||
// case collision::PM_METAL:
|
||||
// return "impact_metal";
|
||||
// case collision::PM_GLASS:
|
||||
// return "impact_glass";
|
||||
// case collision::PM_FLESH:
|
||||
// return "impact_organic";
|
||||
default:
|
||||
case collision::PM_WOOD:
|
||||
return "impact_wood";
|
||||
case collision::PM_METAL:
|
||||
return "impact_metal";
|
||||
case collision::PM_GLASS:
|
||||
return "impact_glass";
|
||||
case collision::PM_FLESH:
|
||||
return "impact_flesh";
|
||||
default:
|
||||
return "impact_stone";
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ void game::World::FireBullet(const BulletInfo& bullet)
|
||||
obj_cb->OnBulletHit(bullet, hit_obj);
|
||||
|
||||
// TODO: remove
|
||||
// const float box_extent = 0.1f;
|
||||
const float box_extent = 0.1f;
|
||||
// BeamBox(hit_pos - box_extent, hit_pos + box_extent, GetMaterialColor(material), 1.0f);
|
||||
|
||||
// Beam(bullet.start, hit_pos, 0x0044DD, 0.04f);
|
||||
|
||||
@ -56,6 +56,8 @@ bool game::view::CharacterView::ProcessMsg(net::EntMsgType type, net::InMessage&
|
||||
{
|
||||
case net::EMSG_EQUIP:
|
||||
return ProcessEquipMsg(msg);
|
||||
case net::EMSG_FIRE:
|
||||
return ProcessFireMsg(msg);
|
||||
default:
|
||||
return Super::ProcessMsg(type, msg);
|
||||
}
|
||||
@ -88,7 +90,7 @@ void game::view::CharacterView::Update(const UpdateInfo& info)
|
||||
animstate_.loco_phase = glm::mod(glm::mix(loco_phase0, loco_phase1, t), 1.0f);
|
||||
|
||||
// action
|
||||
animstate_.action_phase = glm::mix(states_[0].action_phase, states_[1].action_phase, t_sane);
|
||||
animstate_.action_time = glm::mix(states_[0].action_time, states_[1].action_time, t_sane);
|
||||
// if (animstate_.action_anim_idx != assets::NO_ANIM)
|
||||
// {
|
||||
// std::cout <<"phase: " << animstate_.action_phase << std::endl;
|
||||
@ -195,7 +197,7 @@ bool game::view::CharacterView::ReadState(net::InMessage* msg)
|
||||
old_state.trans = root_.local;
|
||||
old_state.loco_blend = animstate_.loco_blend;
|
||||
old_state.loco_phase = animstate_.loco_phase;
|
||||
old_state.action_phase = animstate_.action_phase;
|
||||
old_state.action_time = animstate_.action_time;
|
||||
old_state.aim_yaw = animstate_.yaw;
|
||||
old_state.aim_pitch = animstate_.pitch;
|
||||
|
||||
@ -253,19 +255,19 @@ bool game::view::CharacterView::ReadState(net::InMessage* msg)
|
||||
animstate_.action_anim_idx = sync_.action_anim;
|
||||
}
|
||||
|
||||
// action phase
|
||||
if (fields & CSF_ACTION_PHASE)
|
||||
// action time
|
||||
if (fields & CSF_ACTION_TIME)
|
||||
{
|
||||
if (!net::ReadDelta(*msg, sync_.action_phase))
|
||||
if (!net::ReadDelta(*msg, sync_.action_time))
|
||||
return false;
|
||||
|
||||
new_state.action_phase = sync_.action_phase.Decode();
|
||||
new_state.action_time = sync_.action_time.Decode();
|
||||
|
||||
if (fields & CSF_ACTION_ANIM)
|
||||
{
|
||||
// anim just changed, dont blend phase
|
||||
old_state.action_phase = new_state.action_phase;
|
||||
animstate_.action_phase = new_state.action_phase;
|
||||
// anim just changed, dont blend time
|
||||
old_state.action_time = new_state.action_time;
|
||||
animstate_.action_time = new_state.action_time;
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,11 +342,22 @@ bool game::view::CharacterView::ProcessEquipMsg(net::InMessage& msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::view::CharacterView::ProcessFireMsg(net::InMessage& msg)
|
||||
{
|
||||
FireItem();
|
||||
return true;
|
||||
}
|
||||
|
||||
void game::view::CharacterView::SetItem(const std::string& item_name)
|
||||
{
|
||||
if (item_name == item_name_)
|
||||
return;
|
||||
|
||||
item_name_ = item_name;
|
||||
|
||||
fire_snd_.reset();
|
||||
fire_fx_.reset();
|
||||
|
||||
if (item_name.empty())
|
||||
{
|
||||
item_.reset();
|
||||
@ -357,6 +370,20 @@ void game::view::CharacterView::SetItem(const std::string& item_name)
|
||||
item_node_.parent = bone_node ? bone_node : &root_;
|
||||
item_node_.local = item_->bone_offset;
|
||||
|
||||
// snd
|
||||
if (!item_->fire_snd.empty())
|
||||
{
|
||||
fire_snd_ = assets::CacheManager::GetSound("data/" + item_->fire_snd + ".snd");
|
||||
}
|
||||
|
||||
// fx
|
||||
if (!item_->fire_fx.empty())
|
||||
{
|
||||
fire_fx_ = assets::CacheManager::GetEffect("data/" + item_->fire_fx + ".fx");
|
||||
auto loc = item_->model->GetLocation(item_->fire_fx_loc);
|
||||
fire_fx_offset_ = loc ? loc->position : glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void game::view::CharacterView::DrawItem(const DrawArgs& args)
|
||||
@ -373,3 +400,24 @@ void game::view::CharacterView::DrawItem(const DrawArgs& args)
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void game::view::CharacterView::FireItem()
|
||||
{
|
||||
if (!item_)
|
||||
return;
|
||||
|
||||
if (fire_snd_)
|
||||
{
|
||||
auto snd = audioplayer_.PlaySound(fire_snd_, &item_node_);
|
||||
// snd->SetPosition(item_node_.GetGlobalPosition());
|
||||
}
|
||||
|
||||
if (fire_fx_)
|
||||
{
|
||||
glm::vec3 pos = item_node_.matrix * glm::vec4(fire_fx_offset_, 1.0f);
|
||||
glm::vec3 dir = item_node_.matrix * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f);
|
||||
world_.GetEmitter().Emit(fire_fx_, pos, glm::normalize(dir));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "entityview.hpp"
|
||||
#include "assets/model.hpp"
|
||||
#include "assets/item.hpp"
|
||||
#include "assets/effect.hpp"
|
||||
#include "game/skeletoninstance.hpp"
|
||||
#include "skinning_ubo.hpp"
|
||||
#include "game/character_anim_state.hpp"
|
||||
@ -18,7 +19,7 @@ struct CharacterViewState
|
||||
float loco_blend = 0.0f;
|
||||
float loco_phase = 0.0f;
|
||||
|
||||
float action_phase = 0.0f;
|
||||
float action_time = 0.0f;
|
||||
|
||||
float aim_yaw = 0.0f;
|
||||
float aim_pitch = 0.0f;
|
||||
@ -57,9 +58,11 @@ private:
|
||||
void AddClothes(const std::string& name, const glm::vec3& color);
|
||||
|
||||
bool ProcessEquipMsg(net::InMessage& msg);
|
||||
bool ProcessFireMsg(net::InMessage& msg);
|
||||
|
||||
void SetItem(const std::string& item_name);
|
||||
void DrawItem(const DrawArgs& args);
|
||||
void FireItem();
|
||||
|
||||
private:
|
||||
float yaw_ = 0.0f;
|
||||
@ -80,8 +83,11 @@ private:
|
||||
float update_time_ = 0.0f;
|
||||
|
||||
std::string item_name_;
|
||||
std::shared_ptr<const assets::Item> item_;
|
||||
TransformNode item_node_;
|
||||
std::shared_ptr<const assets::Item> item_;
|
||||
std::shared_ptr<const audio::Sound> fire_snd_;
|
||||
std::shared_ptr<const assets::Effect> fire_fx_;
|
||||
glm::vec3 fire_fx_offset_{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -8,8 +8,9 @@
|
||||
#include "utils.hpp"
|
||||
#include "vehicleview.hpp"
|
||||
#include "assets/cache.hpp"
|
||||
#include "game/player_hud_data.hpp"
|
||||
|
||||
game::view::ClientSession::ClientSession(App& app) : app_(app), use_target_hud_(app.GetTime())
|
||||
game::view::ClientSession::ClientSession(App& app) : app_(app), hud_(app.GetTime())
|
||||
{
|
||||
crosshair_texture_ = assets::CacheManager::GetTexture("data/crosshair.png");
|
||||
|
||||
@ -50,6 +51,9 @@ bool game::view::ClientSession::ProcessSingleMessage(net::MessageType type, net:
|
||||
case net::MSG_CHAT:
|
||||
return ProcessChatMsg(msg);
|
||||
|
||||
case net::MSG_HUD:
|
||||
return ProcessHudMsg(msg);
|
||||
|
||||
case net::MSG_USETARGET:
|
||||
return ProcessUseTargetMsg(msg);
|
||||
|
||||
@ -103,10 +107,13 @@ void game::view::ClientSession::Draw(gfx::DrawList& dlist, gfx::DrawListParams&
|
||||
if (world_)
|
||||
{
|
||||
DrawWorld(dlist, params, gui);
|
||||
}
|
||||
|
||||
DrawCrosshair(gui);
|
||||
use_target_hud_.Draw(gui);
|
||||
if (world_->IsLoaded())
|
||||
{
|
||||
DrawCrosshair(gui);
|
||||
hud_.Draw(gui);
|
||||
}
|
||||
}
|
||||
|
||||
DrawMenus(gui);
|
||||
}
|
||||
@ -148,6 +155,70 @@ bool game::view::ClientSession::ProcessChatMsg(net::InMessage& msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::view::ClientSession::ProcessHudMsg(net::InMessage& msg)
|
||||
{
|
||||
PlayerHudFields fields{};
|
||||
if (!msg.Read(fields))
|
||||
return false;
|
||||
|
||||
PlayerHudData hud_data{};
|
||||
|
||||
if (fields & PHUD_HEALTH)
|
||||
{
|
||||
if (!msg.Read(hud_data.health))
|
||||
return false;
|
||||
|
||||
hud_.SetHealth(static_cast<float>(hud_data.health));
|
||||
}
|
||||
|
||||
if (fields & PHUD_WEAPON_SLOTS)
|
||||
{
|
||||
if (!msg.Read(hud_data.weapon_slots))
|
||||
return false;
|
||||
|
||||
hud_.SetWeaponSlots(hud_data.weapon_slots);
|
||||
}
|
||||
|
||||
if (fields & PHUD_ITEM)
|
||||
{
|
||||
net::ModelName item_name;
|
||||
if (!msg.Read(item_name))
|
||||
return false;
|
||||
|
||||
hud_data.held_item = item_name;
|
||||
|
||||
// determine clip size
|
||||
size_t clip_size = 0;
|
||||
size_t item_slot = 0;
|
||||
if (!hud_data.held_item.empty())
|
||||
{
|
||||
auto item = assets::CacheManager::GetItem("data/" + hud_data.held_item + ".item");
|
||||
clip_size = item->clip_size;
|
||||
item_slot = item->slot;
|
||||
}
|
||||
|
||||
hud_.SetItemInfo(hud_data.held_item, item_slot, clip_size);
|
||||
}
|
||||
|
||||
if (fields & PHUD_AMMO_LOADED)
|
||||
{
|
||||
if (!msg.Read(hud_data.ammo_loaded))
|
||||
return false;
|
||||
|
||||
hud_.SetLoadedAmmo(static_cast<size_t>(hud_data.ammo_loaded));
|
||||
}
|
||||
|
||||
if (fields & PHUD_AMMO_TOTAL)
|
||||
{
|
||||
if (!msg.Read(hud_data.ammo_total))
|
||||
return false;
|
||||
|
||||
hud_.SetTotalAmmo(static_cast<size_t>(hud_data.ammo_total));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::view::ClientSession::ProcessUseTargetMsg(net::InMessage& msg)
|
||||
{
|
||||
net::UseTargetName text, error_text;
|
||||
@ -156,7 +227,7 @@ bool game::view::ClientSession::ProcessUseTargetMsg(net::InMessage& msg)
|
||||
if (!msg.Read(text) || !msg.Read(error_text) || !msg.Read<net::UseDelayQ>(delay))
|
||||
return false;
|
||||
|
||||
use_target_hud_.SetData(text, error_text, delay);
|
||||
hud_.SetUseTargetData(text, error_text, delay);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
#include "net/inmessage.hpp"
|
||||
#include "net/msg_producer.hpp"
|
||||
#include "game/player_input.hpp"
|
||||
#include "gui/use_target_hud.hpp"
|
||||
#include "gui/player_hud.hpp"
|
||||
#include "remote_menu_view.hpp"
|
||||
#include "game/camera_info.hpp"
|
||||
#include "game/camera_controller.hpp"
|
||||
@ -41,6 +41,7 @@ private:
|
||||
bool ProcessWorldMsg(net::InMessage& msg);
|
||||
bool ProcessCameraMsg(net::InMessage& msg);
|
||||
bool ProcessChatMsg(net::InMessage& msg);
|
||||
bool ProcessHudMsg(net::InMessage& msg);
|
||||
bool ProcessUseTargetMsg(net::InMessage& msg);
|
||||
bool ProcessMenuMsg(net::InMessage& msg);
|
||||
|
||||
@ -68,7 +69,7 @@ private:
|
||||
net::ViewPitchQ view_pitch_q_;
|
||||
float last_send_time_ = 0.0f;
|
||||
|
||||
gui::UseTargetHud use_target_hud_;
|
||||
gui::PlayerHud hud_;
|
||||
|
||||
std::vector<std::unique_ptr<RemoteMenuView>> remote_menus_;
|
||||
|
||||
|
||||
@ -13,46 +13,75 @@ game::view::MarkerView::MarkerView(WorldView& world, net::InMessage& msg) : Supe
|
||||
|
||||
void game::view::MarkerView::Update(const UpdateInfo& info)
|
||||
{
|
||||
root_.local.rotation = glm::quat(glm::vec3(0.0f, 0.0f, info.time * 0.5f));
|
||||
float rotation_speed = marker_type_ == MARKER_PICKUP ? 2.0f : 0.5f;
|
||||
root_.local.rotation = glm::quat(glm::vec3(0.0f, 0.0f, info.time * rotation_speed));
|
||||
|
||||
icon_node_.parent = &root_;
|
||||
|
||||
if (marker_type_ == MARKER_PICKUP)
|
||||
{
|
||||
icon_node_.local.position.z = 0.6f + glm::sin(info.time * 2.0f) * 0.1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
icon_node_.local.position.z = 1.0f;
|
||||
}
|
||||
|
||||
root_.UpdateMatrix();
|
||||
icon_node_.UpdateMatrix();
|
||||
}
|
||||
|
||||
void game::view::MarkerView::Draw(const DrawArgs& args)
|
||||
{
|
||||
Super::Draw(args);
|
||||
|
||||
if (!model_)
|
||||
return;
|
||||
|
||||
const auto& mesh = *model_->GetMesh();
|
||||
for (const auto& surface : mesh.surfaces)
|
||||
if (base_model_)
|
||||
{
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.matrices = &root_.matrix;
|
||||
cmd.color = &color_;
|
||||
args.dlist.AddSurface(cmd);
|
||||
DrawModel(args, *base_model_, root_);
|
||||
}
|
||||
|
||||
if (model_)
|
||||
{
|
||||
DrawModel(args, *model_, icon_node_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool game::view::MarkerView::Init(net::InMessage& msg)
|
||||
{
|
||||
net::PositionQ pos_q;
|
||||
uint32_t color;
|
||||
|
||||
if (!msg.Read(marker_type_) || !net::ReadPositionQ(msg, pos_q) || !net::ReadRGB(msg, color))
|
||||
net::ModelName model_name;
|
||||
if (!msg.Read(marker_type_) || !net::ReadPosition(msg, root_.local.position) || !net::ReadRGB(msg, color) || !msg.Read(model_name))
|
||||
return false;
|
||||
|
||||
net::DecodePosition(pos_q, root_.local.position);
|
||||
root_.UpdateMatrix();
|
||||
|
||||
color_ = glm::unpackUnorm4x8(color | 0xFF000000);
|
||||
|
||||
std::string model_name_str = model_name;
|
||||
if (!model_name_str.empty())
|
||||
{
|
||||
model_ = assets::CacheManager::GetModel("data/" + model_name_str + ".mdl");
|
||||
}
|
||||
|
||||
if (marker_type_ == MARKER_FOOT || marker_type_ == MARKER_VEHICLE)
|
||||
{
|
||||
model_ = assets::CacheManager::GetModel("data/marker_tuning.mdl");
|
||||
base_model_ = assets::CacheManager::GetModel("data/marker_base.mdl");
|
||||
}
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void game::view::MarkerView::DrawModel(const DrawArgs& args, const assets::Model& model, const TransformNode& node)
|
||||
{
|
||||
const auto& mesh = *model.GetMesh();
|
||||
for (const auto& surface : mesh.surfaces)
|
||||
{
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.matrices = &node.matrix;
|
||||
cmd.color = &color_;
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,11 +22,15 @@ public:
|
||||
private:
|
||||
bool Init(net::InMessage& msg);
|
||||
|
||||
void DrawModel(const DrawArgs& args, const assets::Model& model, const TransformNode& node);
|
||||
|
||||
private:
|
||||
MarkerType marker_type_;
|
||||
glm::vec4 color_;
|
||||
|
||||
std::shared_ptr<const assets::Model> base_model_;
|
||||
std::shared_ptr<const assets::Model> model_;
|
||||
TransformNode icon_node_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ public:
|
||||
|
||||
float GetTime() const { return time_; }
|
||||
audio::Master& GetAudioMaster() const { return audiomaster_; }
|
||||
ParticleEmitter& GetEmitter() { return emitter_; }
|
||||
|
||||
bool IsLoaded() const;
|
||||
|
||||
|
||||
197
src/gui/player_hud.cpp
Normal file
197
src/gui/player_hud.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
#include "player_hud.hpp"
|
||||
|
||||
#include <format>
|
||||
|
||||
static uint32_t COLOR_ACTIVE = 0xFF00FFFF;
|
||||
static uint32_t COLOR_NORMAL = 0xFFFFFFFF;
|
||||
static uint32_t COLOR_DISABLED = 0xFFCCCCCC;
|
||||
static uint32_t COLOR_ERROR = 0xFF7777FF;
|
||||
|
||||
#define PREFIX_SEPARATOR "^aaa"
|
||||
#define PREFIX_CLIPSIZE "^ccc"
|
||||
#define PREFIX_AMMO_NORMAL "^fff"
|
||||
#define PREFIX_AMMO_FULL "^9f9"
|
||||
#define PREFIX_AMMO_EMPTY "^f77"
|
||||
|
||||
#define PREFIX_WEAPON_SLOT_INACTIVE "^888"
|
||||
#define PREFIX_WEAPON_SLOT_ACTIVE "^fff"
|
||||
#define PREFIX_WEAPON_SLOT_CURRENT "^ff0"
|
||||
|
||||
gui::PlayerHud::PlayerHud(const float& time) : time_(time)
|
||||
{
|
||||
UpdateWeaponSlotsText();
|
||||
}
|
||||
|
||||
void gui::PlayerHud::SetWeaponSlots(uint8_t slots)
|
||||
{
|
||||
weapon_slots_ = slots;
|
||||
UpdateWeaponSlotsText();
|
||||
}
|
||||
|
||||
void gui::PlayerHud::SetItemInfo(std::string item_name, size_t slot, size_t clip_size)
|
||||
{
|
||||
item_name_ = std::move(item_name);
|
||||
clip_size_ = clip_size;
|
||||
current_slot_ = slot;
|
||||
UpdateWeaponSlotsText();
|
||||
}
|
||||
|
||||
void gui::PlayerHud::SetUseTargetData(std::string text, std::string error_text, float delay)
|
||||
{
|
||||
ut_text_ = std::move(text);
|
||||
|
||||
if (error_text.empty())
|
||||
ut_error_text_.clear();
|
||||
else
|
||||
ut_error_text_ = "(" + error_text + ")";
|
||||
|
||||
ut_start_time_ = time_;
|
||||
ut_end_time_ = delay > 0.01f ? ut_start_time_ + delay : ut_start_time_;
|
||||
}
|
||||
|
||||
void gui::PlayerHud::Draw(Context& ctx) const
|
||||
{
|
||||
DrawHealthBar(ctx);
|
||||
DrawItemInfo(ctx);
|
||||
DrawUseTarget(ctx);
|
||||
}
|
||||
|
||||
void gui::PlayerHud::UpdateWeaponSlotsText()
|
||||
{
|
||||
weapon_slots_text_.clear();
|
||||
|
||||
for (size_t slot = 0; slot < 10; ++slot)
|
||||
{
|
||||
weapon_slots_text_.push_back(' ');
|
||||
|
||||
std::string_view prefix = PREFIX_WEAPON_SLOT_INACTIVE;
|
||||
|
||||
if (weapon_slots_ & (1 << slot))
|
||||
prefix = PREFIX_WEAPON_SLOT_ACTIVE;
|
||||
|
||||
if (current_slot_ == slot + 1)
|
||||
prefix = PREFIX_WEAPON_SLOT_CURRENT;
|
||||
|
||||
weapon_slots_text_ += prefix;
|
||||
weapon_slots_text_ += std::to_string((slot + 1) % 10);
|
||||
}
|
||||
}
|
||||
|
||||
void gui::PlayerHud::DrawHealthBar(Context& ctx) const
|
||||
{
|
||||
const float margin = 30.0f;
|
||||
const glm::vec2 size(100.0f, 20.0f);
|
||||
|
||||
glm::vec2 p0(margin, ctx.GetViewportSize().y - margin - size.y);
|
||||
glm::vec2 p1 = p0 + size;
|
||||
ctx.DrawRect(p0, p1, 0x99000000); // bg
|
||||
|
||||
glm::vec2 p1_bar = p0 + glm::vec2(size.x * health_ * 0.01f, size.y);
|
||||
ctx.DrawRect(p0, p1_bar, 0xDD00BB00); // bar
|
||||
}
|
||||
|
||||
static std::string_view GetAmmoColor(size_t loaded, size_t clip_size)
|
||||
{
|
||||
if (loaded == 0)
|
||||
return PREFIX_AMMO_EMPTY;
|
||||
|
||||
if (loaded == clip_size)
|
||||
return PREFIX_AMMO_FULL;
|
||||
|
||||
return PREFIX_AMMO_NORMAL;
|
||||
}
|
||||
|
||||
void gui::PlayerHud::DrawItemInfo(Context& ctx) const
|
||||
{
|
||||
const float margin = 30.0f;
|
||||
const float line_height = 30.0f;
|
||||
|
||||
glm::vec2 cursor(ctx.GetViewportSize() - margin);
|
||||
|
||||
ctx.DrawTextAligned(weapon_slots_text_, cursor, glm::vec2(-1.0f, -1.0f), COLOR_NORMAL);
|
||||
cursor.y -= line_height * 1.5f;
|
||||
|
||||
if (!item_name_.empty())
|
||||
{
|
||||
std::string ammo_text =
|
||||
std::format("{}{}" PREFIX_SEPARATOR "/" PREFIX_CLIPSIZE "{}" PREFIX_SEPARATOR " | {}{}",
|
||||
GetAmmoColor(loaded_ammo_, clip_size_), loaded_ammo_, clip_size_, GetAmmoColor(total_ammo_, 0), total_ammo_);
|
||||
|
||||
ctx.DrawTextAligned(ammo_text, cursor, glm::vec2(-1.0f, -1.0f), COLOR_NORMAL);
|
||||
cursor.y -= line_height;
|
||||
ctx.DrawTextAligned(item_name_, cursor, glm::vec2(-1.0f, -1.0f), COLOR_ACTIVE);
|
||||
cursor.y -= line_height;
|
||||
}
|
||||
}
|
||||
|
||||
void gui::PlayerHud::DrawUseTarget(Context& ctx) const
|
||||
{
|
||||
if (ut_text_.empty())
|
||||
return;
|
||||
|
||||
bool active = ut_start_time_ != ut_end_time_;
|
||||
uint32_t text_color = (!ut_error_text_.empty()) ? COLOR_DISABLED : (active ? COLOR_ACTIVE : COLOR_NORMAL);
|
||||
|
||||
const float spacing = 10.0f;
|
||||
glm::vec2 key_size(30.0f);
|
||||
glm::vec2 text_size = ctx.MeasureText(ut_text_);
|
||||
float total_width = key_size.x + spacing + text_size.x;
|
||||
|
||||
glm::vec2 error_size(0.0f);
|
||||
if (!ut_error_text_.empty())
|
||||
{
|
||||
error_size = ctx.MeasureText(ut_error_text_);
|
||||
total_width += spacing + error_size.x;
|
||||
}
|
||||
|
||||
glm::vec2 progress_size(60.0f, 10.0f);
|
||||
if (active)
|
||||
{
|
||||
total_width += spacing + progress_size.x;
|
||||
}
|
||||
|
||||
auto& viewport_size = ctx.GetViewportSize();
|
||||
float center_x = viewport_size.x * 0.5f;
|
||||
float center_y = viewport_size.y - 50.0f;
|
||||
float x = center_x - total_width * 0.5f;
|
||||
|
||||
// draw key bg
|
||||
glm::vec2 bg_p0(x, center_y - key_size.y * 0.5f);
|
||||
glm::vec2 bg_p1 = bg_p0 + key_size;
|
||||
ctx.DrawRect(bg_p0, bg_p1, 0x77000000);
|
||||
|
||||
// draw key text
|
||||
static constexpr std::string_view key_text = "E";
|
||||
ctx.DrawTextAligned(key_text, bg_p0 + key_size * 0.5f, glm::vec2(-0.5f, -0.5f), text_color);
|
||||
|
||||
x += key_size.x + spacing;
|
||||
|
||||
// draw text
|
||||
glm::vec2 text_p(x, center_y - text_size.y * 0.5f);
|
||||
ctx.DrawText(ut_text_, text_p, text_color);
|
||||
|
||||
x += text_size.x + spacing;
|
||||
|
||||
// draw error text
|
||||
if (!ut_error_text_.empty())
|
||||
{
|
||||
glm::vec2 error_text_p(x, center_y - error_size.y * 0.5f);
|
||||
ctx.DrawText(ut_error_text_, error_text_p, COLOR_ERROR);
|
||||
|
||||
x += error_size.x + spacing;
|
||||
}
|
||||
|
||||
// draw progress bar
|
||||
if (active)
|
||||
{
|
||||
float t = (time_ - ut_start_time_) / (ut_end_time_ - ut_start_time_);
|
||||
t = glm::clamp(t, 0.0f, 1.0f);
|
||||
|
||||
glm::vec2 progress_p0(x, center_y - progress_size.y * 0.5f);
|
||||
glm::vec2 progress_p1 = progress_p0 + progress_size;
|
||||
glm::vec2 progress_p1_bar = progress_p0 + glm::vec2(t * progress_size.x, progress_size.y);
|
||||
|
||||
ctx.DrawRect(progress_p0, progress_p1, 0x77000000);
|
||||
ctx.DrawRect(progress_p0, progress_p1_bar, COLOR_ACTIVE);
|
||||
}
|
||||
}
|
||||
63
src/gui/player_hud.hpp
Normal file
63
src/gui/player_hud.hpp
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class PlayerHud
|
||||
{
|
||||
public:
|
||||
PlayerHud(const float& time);
|
||||
|
||||
void SetHealth(float health) { health_ = health; }
|
||||
void SetWeaponSlots(uint8_t slots);
|
||||
void SetItemInfo(std::string item_name, size_t slot, size_t clip_size);
|
||||
void SetLoadedAmmo(size_t loaded_ammo) { loaded_ammo_ = loaded_ammo; }
|
||||
void SetTotalAmmo(size_t total_ammo) { total_ammo_ = total_ammo; }
|
||||
|
||||
void SetUseTargetData(std::string text, std::string error_text, float delay);
|
||||
|
||||
|
||||
void Draw(Context& ctx) const;
|
||||
|
||||
private:
|
||||
void UpdateWeaponSlotsText();
|
||||
|
||||
void DrawHealthBar(Context& ctx) const;
|
||||
void DrawItemInfo(Context& ctx) const;
|
||||
|
||||
void DrawUseTarget(Context& ctx) const;
|
||||
|
||||
private:
|
||||
const float& time_;
|
||||
|
||||
// general
|
||||
float health_ = 0.0f;
|
||||
|
||||
// weapon slots
|
||||
uint8_t weapon_slots_ = 0;
|
||||
size_t current_slot_ = 0;
|
||||
std::string weapon_slots_text_;
|
||||
|
||||
// held item
|
||||
std::string item_name_;
|
||||
size_t clip_size_ = 0;
|
||||
size_t loaded_ammo_ = 0;
|
||||
size_t total_ammo_ = 0;
|
||||
|
||||
// use target
|
||||
std::string ut_text_;
|
||||
std::string ut_error_text_;
|
||||
float ut_start_time_ = 0.0f;
|
||||
float ut_end_time_ = 0.0f;
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
#include "use_target_hud.hpp"
|
||||
|
||||
gui::UseTargetHud::UseTargetHud(const float& time) : time_(time) {}
|
||||
|
||||
void gui::UseTargetHud::SetData(std::string text, std::string error_text, float delay)
|
||||
{
|
||||
text_ = std::move(text);
|
||||
|
||||
if (error_text.empty())
|
||||
error_text_.clear();
|
||||
else
|
||||
error_text_ = "(" + error_text + ")";
|
||||
|
||||
start_time_ = time_;
|
||||
end_time_ = delay > 0.01f ? start_time_ + delay : start_time_;
|
||||
}
|
||||
|
||||
void gui::UseTargetHud::Draw(Context& ctx) const
|
||||
{
|
||||
if (text_.empty())
|
||||
return;
|
||||
|
||||
bool active = start_time_ != end_time_;
|
||||
uint32_t text_color = (!error_text_.empty()) ? 0xFFCCCCCC : (active ? 0xFF00FFFF : 0xFFFFFFFF);
|
||||
|
||||
const float spacing = 10.0f;
|
||||
glm::vec2 key_size(30.0f);
|
||||
glm::vec2 text_size = ctx.MeasureText(text_);
|
||||
float total_width = key_size.x + spacing + text_size.x;
|
||||
|
||||
glm::vec2 error_size(0.0f);
|
||||
if (!error_text_.empty())
|
||||
{
|
||||
error_size = ctx.MeasureText(error_text_);
|
||||
total_width += spacing + error_size.x;
|
||||
}
|
||||
|
||||
glm::vec2 progress_size(60.0f, 10.0f);
|
||||
if (active)
|
||||
{
|
||||
total_width += spacing + progress_size.x;
|
||||
}
|
||||
|
||||
auto& viewport_size = ctx.GetViewportSize();
|
||||
float center_x = viewport_size.x * 0.5f;
|
||||
float center_y = viewport_size.y - 50.0f;
|
||||
float x = center_x - total_width * 0.5f;
|
||||
|
||||
// draw key bg
|
||||
glm::vec2 bg_p0(x, center_y - key_size.y * 0.5f);
|
||||
glm::vec2 bg_p1 = bg_p0 + key_size;
|
||||
ctx.DrawRect(bg_p0, bg_p1, 0x77000000);
|
||||
|
||||
// draw key text
|
||||
static constexpr std::string_view key_text = "E";
|
||||
ctx.DrawTextAligned(key_text, bg_p0 + key_size * 0.5f, glm::vec2(-0.5f, -0.5f), text_color);
|
||||
|
||||
x += key_size.x + spacing;
|
||||
|
||||
// draw text
|
||||
glm::vec2 text_p(x, center_y - text_size.y * 0.5f);
|
||||
ctx.DrawText(text_, text_p, text_color);
|
||||
|
||||
x += text_size.x + spacing;
|
||||
|
||||
// draw error text
|
||||
if (!error_text_.empty())
|
||||
{
|
||||
glm::vec2 error_text_p(x, center_y - error_size.y * 0.5f);
|
||||
ctx.DrawText(error_text_, error_text_p, 0xFF7777FF);
|
||||
|
||||
x += error_size.x + spacing;
|
||||
}
|
||||
|
||||
// draw progress bar
|
||||
if (active)
|
||||
{
|
||||
float t = (time_ - start_time_) / (end_time_ - start_time_);
|
||||
t = glm::clamp(t, 0.0f, 1.0f);
|
||||
|
||||
glm::vec2 progress_p0(x, center_y - progress_size.y * 0.5f);
|
||||
glm::vec2 progress_p1 = progress_p0 + progress_size;
|
||||
glm::vec2 progress_p1_bar = progress_p0 + glm::vec2(t * progress_size.x, progress_size.y);
|
||||
|
||||
ctx.DrawRect(progress_p0, progress_p1, 0x77000000);
|
||||
ctx.DrawRect(progress_p0, progress_p1_bar, 0xFF00FFFF);
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class UseTargetHud
|
||||
{
|
||||
public:
|
||||
UseTargetHud(const float& time);
|
||||
|
||||
void SetData(std::string text, std::string error_text, float delay);
|
||||
|
||||
void Draw(Context& ctx) const;
|
||||
|
||||
private:
|
||||
const float& time_;
|
||||
std::string text_;
|
||||
std::string error_text_;
|
||||
float start_time_ = 0.0f;
|
||||
float end_time_ = 0.0f;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@ -46,6 +46,9 @@ enum MessageType : uint8_t
|
||||
// REMOTEMENU <MenuId> <MenuMessageType> ...
|
||||
MSG_REMOTEMENU,
|
||||
|
||||
// HUD ...
|
||||
MSG_HUD,
|
||||
|
||||
/*~~~~~~~~ Entity ~~~~~~~~*/
|
||||
// ENTSPAWN <EntNum> <EntType> data...
|
||||
MSG_ENTSPAWN,
|
||||
@ -115,6 +118,7 @@ enum EntMsgType : uint8_t
|
||||
EMSG_DEFORM,
|
||||
EMSG_TUNING,
|
||||
EMSG_EQUIP,
|
||||
EMSG_FIRE,
|
||||
};
|
||||
|
||||
using PositionElemQ = Quantized<uint32_t, -10000, 10000, 1>;
|
||||
@ -144,7 +148,8 @@ using SoundVolumeQ = Quantized<uint8_t, 0, 2>;
|
||||
using SoundPitchQ = Quantized<uint8_t, 0, 2>;
|
||||
|
||||
using AnimBlendQ = Quantized<uint8_t, 0, 1>;
|
||||
using AnimTimeQ = Quantized<uint8_t, 0, 1>;
|
||||
using AnimPhaseQ = Quantized<uint8_t, 0, 1>;
|
||||
using AnimTimeQ = Quantized<uint16_t, 0, 255>;
|
||||
|
||||
using AnimAimAngleQ = Quantized<uint16_t, -PI_N, PI_N, PI_D>;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user