571 lines
14 KiB
C++
571 lines
14 KiB
C++
|
/*
|
||
|
** Command & Conquer Renegade(tm)
|
||
|
** Copyright 2025 Electronic Arts Inc.
|
||
|
**
|
||
|
** This program 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.
|
||
|
**
|
||
|
** This program 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.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** Confidential - Westwood Studios ***
|
||
|
***********************************************************************************************
|
||
|
* *
|
||
|
* Project Name : Commando *
|
||
|
* *
|
||
|
* $Archive:: /Commando/Code/Commando/priority.cpp $*
|
||
|
* *
|
||
|
* $Author:: Steve_t $*
|
||
|
* *
|
||
|
* $Modtime:: 2/23/02 12:35p $*
|
||
|
* *
|
||
|
* $Revision:: 15 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#include "priority.h"
|
||
|
|
||
|
#include <math.h>
|
||
|
|
||
|
#include "wwdebug.h"
|
||
|
#include "wwmath.h"
|
||
|
#include "vector3.h"
|
||
|
#include "gameobjmanager.h"
|
||
|
#include "soldier.h"
|
||
|
#include "humanphys.h"
|
||
|
#include "apppackettypes.h"
|
||
|
#include "vehicle.h"
|
||
|
#include "useroptions.h"
|
||
|
#include "playermanager.h"
|
||
|
#include "wwprofile.h"
|
||
|
|
||
|
//
|
||
|
// Class statics
|
||
|
//
|
||
|
float cPriority::MaxDistance = 300.0F;
|
||
|
const float cPriority::TURRET_FACTOR = 0.2f;
|
||
|
const float cPriority::VEHICLE_FACTOR = 0.85f;
|
||
|
const float cPriority::SOLDIER_FACTOR = 1.0f;
|
||
|
const float cPriority::SOLDIER_IN_VEHICLE_FACTOR = 0.1f;
|
||
|
const float cPriority::BUILDING_FACTOR = 0.2f;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Compute the priority of the given netobject to the given client
|
||
|
//
|
||
|
float
|
||
|
cPriority::Compute_Object_Priority
|
||
|
(
|
||
|
int client_id,
|
||
|
const Vector3 & client_pos,
|
||
|
NetworkObjectClass * p_netobject,
|
||
|
bool do_it_anyway,
|
||
|
SoldierGameObj * client_soldier
|
||
|
)
|
||
|
{
|
||
|
WWPROFILE("ObjPri");
|
||
|
WWASSERT(client_id > 0);
|
||
|
WWASSERT(p_netobject != NULL);
|
||
|
|
||
|
//
|
||
|
// Compute the priority of this object to the given client at his given position.
|
||
|
// Priority depends on physical distance. Objects with no physical location will
|
||
|
// have a priority of 1.
|
||
|
//
|
||
|
float priority = 0.0f;
|
||
|
|
||
|
if (p_netobject->Is_Client_Dirty(client_id) || do_it_anyway)
|
||
|
{
|
||
|
float distance = Get_Object_Distance(client_pos, p_netobject);
|
||
|
|
||
|
//
|
||
|
// Priority simply decreases linearly with distance and is zero at MaxDistance.
|
||
|
//
|
||
|
priority = 1 - distance / MaxDistance;
|
||
|
|
||
|
if (priority > 0.0f) {
|
||
|
priority *= Compute_Facing_Factor(client_id, client_pos, p_netobject, client_soldier);
|
||
|
}
|
||
|
|
||
|
if (priority > 0.0f) {
|
||
|
priority *= Compute_Type_Factor(p_netobject);
|
||
|
}
|
||
|
|
||
|
if (priority > 0.0f) {
|
||
|
priority *= Compute_Relevance_Factor(client_id, p_netobject, client_soldier);
|
||
|
}
|
||
|
|
||
|
// Add a bit of a curve to have low priority objects drop away faster.
|
||
|
if (priority > 0.0f && priority < 1.0f) {
|
||
|
//float bendy = WWMath::Fast_Sin(priority * DEG_TO_RAD(90.0f));
|
||
|
//priority = (priority + bendy) / 2.0f;
|
||
|
float bendy = WWMath::Fast_Asin(priority);
|
||
|
bendy = (RAD_TO_DEG(bendy)) / 90.0f;
|
||
|
priority = (priority + bendy) / 2.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Override all priority calculations if the client hints that he badly needs an
|
||
|
// update for this object. The client will hint if there is a vehicle or soldier nearby
|
||
|
// that hasn't been updated for a suspiciously long time.
|
||
|
//
|
||
|
if (p_netobject->Get_Client_Hint_Count(client_id) > 0)
|
||
|
{
|
||
|
priority = 1.0f;
|
||
|
p_netobject->Reset_Client_Hint_Count(client_id);
|
||
|
|
||
|
/*
|
||
|
WWDEBUG_SAY(("cPriority::Compute_Object_Priority %5.3f on object %d due to client %d hint.\n",
|
||
|
priority, p_netobject->Get_Network_ID(), client_id));
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
priority = WWMath::Clamp(priority, 0.0f, 1.0f);
|
||
|
|
||
|
return priority;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float
|
||
|
cPriority::Compute_Facing_Factor
|
||
|
(
|
||
|
int client_id,
|
||
|
const Vector3 & client_pos,
|
||
|
NetworkObjectClass * p_netobject,
|
||
|
SoldierGameObj *client_soldier
|
||
|
)
|
||
|
{
|
||
|
WWASSERT(client_id > 0);
|
||
|
WWASSERT(p_netobject != NULL);
|
||
|
|
||
|
float facing_factor = 1;
|
||
|
|
||
|
SoldierGameObj * p_soldier = client_soldier;
|
||
|
if (p_soldier == NULL) {
|
||
|
p_soldier = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
|
||
|
}
|
||
|
|
||
|
if (p_soldier != NULL)
|
||
|
{
|
||
|
WWASSERT(p_soldier->Peek_Human_Phys() != NULL);
|
||
|
float client_facing = p_soldier->Peek_Human_Phys()->Get_Heading();
|
||
|
|
||
|
Vector3 subject_position;
|
||
|
if (p_netobject->Get_World_Position(subject_position))
|
||
|
{
|
||
|
Vector3 position_delta = subject_position - client_pos;
|
||
|
float subject_facing = WWMath::Atan2(position_delta.Y, position_delta.X);
|
||
|
|
||
|
float facing_dif = ::fabs(subject_facing - client_facing);
|
||
|
|
||
|
if (facing_dif > WWMATH_PI)
|
||
|
{
|
||
|
facing_dif = 2 * WWMATH_PI - facing_dif;
|
||
|
}
|
||
|
|
||
|
facing_factor -= facing_dif / WWMATH_PI * cUserOptions::MaxFacingPenalty.Get();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return facing_factor;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float
|
||
|
cPriority::Get_Object_Distance
|
||
|
(
|
||
|
const Vector3 & client_pos,
|
||
|
NetworkObjectClass * p_netobject
|
||
|
)
|
||
|
{
|
||
|
WWASSERT(p_netobject != NULL);
|
||
|
|
||
|
//
|
||
|
// Objects without a physical location will return a distance of zero.
|
||
|
//
|
||
|
float distance = 0;
|
||
|
|
||
|
//
|
||
|
// Get the object's world position (if it has one)
|
||
|
//
|
||
|
Vector3 position;
|
||
|
if (p_netobject->Get_World_Position(position))
|
||
|
{
|
||
|
//
|
||
|
// Simple distance calculation based on the distance
|
||
|
// between points.
|
||
|
//
|
||
|
distance = (position - client_pos).Length();
|
||
|
}
|
||
|
|
||
|
return distance;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float
|
||
|
cPriority::Compute_Type_Factor
|
||
|
(
|
||
|
NetworkObjectClass *p_netobject
|
||
|
)
|
||
|
{
|
||
|
float type_factor = 1.0f;
|
||
|
|
||
|
char type = p_netobject->Get_App_Packet_Type();
|
||
|
|
||
|
switch (type) {
|
||
|
case APPPACKETTYPE_SOLDIER:
|
||
|
type_factor = SOLDIER_FACTOR;
|
||
|
break;
|
||
|
|
||
|
case APPPACKETTYPE_TURRET:
|
||
|
type_factor = TURRET_FACTOR;
|
||
|
break;
|
||
|
|
||
|
case APPPACKETTYPE_VEHICLE:
|
||
|
type_factor = VEHICLE_FACTOR;
|
||
|
break;
|
||
|
|
||
|
case APPPACKETTYPE_BUILDING:
|
||
|
type_factor = BUILDING_FACTOR;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
type_factor = 1.0f;
|
||
|
}
|
||
|
|
||
|
return(type_factor);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float
|
||
|
cPriority::Compute_Relevance_Factor
|
||
|
(
|
||
|
int client_id,
|
||
|
NetworkObjectClass * p_netobject,
|
||
|
SoldierGameObj *client_soldier
|
||
|
)
|
||
|
{
|
||
|
WWASSERT(p_netobject != NULL);
|
||
|
|
||
|
//
|
||
|
// This bumps the priority of objects that you are shooting or that are
|
||
|
// shooting at you.
|
||
|
//
|
||
|
|
||
|
bool is_relevant = false;
|
||
|
|
||
|
int id = p_netobject->Get_Network_ID();
|
||
|
|
||
|
SoldierGameObj * p_my_soldier = client_soldier;
|
||
|
if (p_my_soldier == NULL) {
|
||
|
p_my_soldier = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
|
||
|
}
|
||
|
VehicleGameObj * p_my_vehicle = NULL;
|
||
|
if (p_my_soldier != NULL) {
|
||
|
p_my_vehicle = GameObjManager::Find_Vehicle_Occupied_By(p_my_soldier);
|
||
|
}
|
||
|
|
||
|
if (p_my_soldier != NULL &&
|
||
|
(p_my_soldier->Get_Last_Object_Id_I_Damaged() == id ||
|
||
|
p_my_soldier->Get_Last_Object_Id_I_Got_Damaged_By() == id)) {
|
||
|
|
||
|
is_relevant = true;
|
||
|
}
|
||
|
|
||
|
if (p_my_vehicle != NULL &&
|
||
|
(p_my_vehicle->Get_Last_Object_Id_I_Damaged() == id ||
|
||
|
p_my_vehicle->Get_Last_Object_Id_I_Got_Damaged_By() == id)) {
|
||
|
|
||
|
is_relevant = true;
|
||
|
}
|
||
|
|
||
|
float factor = 1.0f;
|
||
|
if (!is_relevant) {
|
||
|
factor -= cUserOptions::IrrelevancePenalty.Get();
|
||
|
}
|
||
|
|
||
|
WWASSERT(factor > 0);
|
||
|
|
||
|
return factor;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
float
|
||
|
cPriority::Compute_Object_Priority_2
|
||
|
(
|
||
|
int client_id,
|
||
|
const Vector3 & client_pos,
|
||
|
NetworkObjectClass * p_netobject,
|
||
|
bool do_it_anyway,
|
||
|
SoldierGameObj * client_soldier
|
||
|
)
|
||
|
{
|
||
|
WWPROFILE("ObjPri");
|
||
|
WWASSERT(client_id > 0);
|
||
|
WWASSERT(p_netobject != NULL);
|
||
|
|
||
|
//
|
||
|
// Compute the priority of this object to the given client at his given position.
|
||
|
// Priority depends on physical distance. Objects with no physical location will
|
||
|
// have a priority of 1.
|
||
|
//
|
||
|
float priority = 0.0f;
|
||
|
|
||
|
if (p_netobject->Is_Client_Dirty(client_id) || do_it_anyway)
|
||
|
{
|
||
|
float distance = Get_Object_Distance_2(client_pos, p_netobject);
|
||
|
|
||
|
//
|
||
|
// Priority simply decreases linearly with distance and is zero at MaxDistance.
|
||
|
//
|
||
|
priority = 1 - distance / MaxDistance;
|
||
|
|
||
|
if (priority > 0.0f) {
|
||
|
priority *= Compute_Facing_Factor_2(client_id, client_pos, p_netobject, client_soldier);
|
||
|
}
|
||
|
|
||
|
if (priority > 0.0f) {
|
||
|
priority *= Compute_Type_Factor_2(p_netobject, distance);
|
||
|
}
|
||
|
|
||
|
if (priority > 0.0f) {
|
||
|
priority *= Compute_Relevance_Factor_2(client_id, p_netobject, client_soldier);
|
||
|
}
|
||
|
|
||
|
// Add a bit of a curve to have low priority objects drop away faster.
|
||
|
if (priority > 0.0f && priority < 1.0f) {
|
||
|
//float bendy = WWMath::Fast_Sin(priority * DEG_TO_RAD(90.0f));
|
||
|
//priority = (priority + bendy) / 2.0f;
|
||
|
float bendy = WWMath::Fast_Asin(priority);
|
||
|
bendy = (RAD_TO_DEG(bendy)) / 90.0f;
|
||
|
priority = (priority + bendy) / 2.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
priority = WWMath::Clamp(priority, 0.0f, 1.0f);
|
||
|
|
||
|
return priority;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float
|
||
|
cPriority::Compute_Facing_Factor_2
|
||
|
(
|
||
|
int client_id,
|
||
|
const Vector3 & client_pos,
|
||
|
NetworkObjectClass * p_netobject,
|
||
|
SoldierGameObj *client_soldier
|
||
|
)
|
||
|
{
|
||
|
WWASSERT(client_id > 0);
|
||
|
WWASSERT(p_netobject != NULL);
|
||
|
|
||
|
float facing_factor = 1;
|
||
|
|
||
|
SoldierGameObj * p_soldier = client_soldier;
|
||
|
if (p_soldier == NULL) {
|
||
|
p_soldier = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
|
||
|
}
|
||
|
|
||
|
if (p_soldier != NULL)
|
||
|
{
|
||
|
HumanPhysClass *hphys = p_soldier->Peek_Human_Phys();
|
||
|
WWASSERT(hphys != NULL);
|
||
|
float client_facing = hphys->Get_Heading();
|
||
|
|
||
|
Vector3 subject_position;
|
||
|
if (p_netobject->Get_World_Position(subject_position))
|
||
|
{
|
||
|
Vector3 position_delta = subject_position - client_pos;
|
||
|
float subject_facing = WWMath::Atan2(position_delta.Y, position_delta.X);
|
||
|
|
||
|
float facing_dif = ::fabs(subject_facing - client_facing);
|
||
|
|
||
|
if (facing_dif > WWMATH_PI)
|
||
|
{
|
||
|
facing_dif = 2 * WWMATH_PI - facing_dif;
|
||
|
}
|
||
|
|
||
|
facing_factor -= facing_dif / WWMATH_PI * cUserOptions::MaxFacingPenalty.Get();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return facing_factor;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float
|
||
|
cPriority::Get_Object_Distance_2
|
||
|
(
|
||
|
const Vector3 & client_pos,
|
||
|
NetworkObjectClass * p_netobject
|
||
|
)
|
||
|
{
|
||
|
WWASSERT(p_netobject != NULL);
|
||
|
|
||
|
//
|
||
|
// Objects without a physical location will return a distance of zero.
|
||
|
//
|
||
|
float distance = 0;
|
||
|
|
||
|
//
|
||
|
// Get the object's world position (if it has one)
|
||
|
//
|
||
|
Vector3 position;
|
||
|
if (p_netobject->Get_World_Position(position))
|
||
|
{
|
||
|
//
|
||
|
// Simple distance calculation based on the distance
|
||
|
// between points.
|
||
|
//
|
||
|
distance = (position - client_pos).Length();
|
||
|
}
|
||
|
|
||
|
return distance;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float
|
||
|
cPriority::Compute_Type_Factor_2
|
||
|
(
|
||
|
NetworkObjectClass *p_netobject,
|
||
|
float distance
|
||
|
)
|
||
|
{
|
||
|
float type_factor = 1.0f;
|
||
|
|
||
|
char type = p_netobject->Get_App_Packet_Type();
|
||
|
|
||
|
switch (type) {
|
||
|
case APPPACKETTYPE_SOLDIER:
|
||
|
{
|
||
|
type_factor = SOLDIER_FACTOR;
|
||
|
SoldierGameObj *soldier = (SoldierGameObj*) p_netobject;
|
||
|
if (soldier->Is_In_Vehicle()) {
|
||
|
type_factor = SOLDIER_IN_VEHICLE_FACTOR;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case APPPACKETTYPE_TURRET:
|
||
|
{
|
||
|
type_factor = TURRET_FACTOR;
|
||
|
float range_filter = p_netobject->Get_Filter_Distance();
|
||
|
if (distance > range_filter) {
|
||
|
type_factor = 0.0f;
|
||
|
} else {
|
||
|
if (distance > 100) {
|
||
|
type_factor = type_factor / 2.0f;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case APPPACKETTYPE_VEHICLE:
|
||
|
type_factor = VEHICLE_FACTOR;
|
||
|
break;
|
||
|
|
||
|
case APPPACKETTYPE_BUILDING:
|
||
|
type_factor = BUILDING_FACTOR;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
type_factor = 1.0f;
|
||
|
}
|
||
|
|
||
|
return(type_factor);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float
|
||
|
cPriority::Compute_Relevance_Factor_2
|
||
|
(
|
||
|
int client_id,
|
||
|
NetworkObjectClass * p_netobject,
|
||
|
SoldierGameObj *client_soldier
|
||
|
)
|
||
|
{
|
||
|
WWASSERT(p_netobject != NULL);
|
||
|
|
||
|
//
|
||
|
// This bumps the priority of objects that you are shooting or that are
|
||
|
// shooting at you.
|
||
|
//
|
||
|
|
||
|
bool is_relevant = false;
|
||
|
|
||
|
int id = p_netobject->Get_Network_ID();
|
||
|
|
||
|
SoldierGameObj * p_my_soldier = client_soldier;
|
||
|
VehicleGameObj * p_my_vehicle = NULL;
|
||
|
|
||
|
if (p_my_soldier == NULL) {
|
||
|
p_my_soldier = GameObjManager::Find_Soldier_Of_Client_ID(client_id);
|
||
|
}
|
||
|
if (p_my_soldier != NULL) {
|
||
|
if (p_my_soldier->Is_In_Vehicle()) {
|
||
|
p_my_vehicle = GameObjManager::Find_Vehicle_Occupied_By(p_my_soldier);
|
||
|
}
|
||
|
|
||
|
if ((p_my_soldier->Get_Last_Object_Id_I_Damaged() == id ||
|
||
|
p_my_soldier->Get_Last_Object_Id_I_Got_Damaged_By() == id)) {
|
||
|
is_relevant = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (p_my_vehicle != NULL &&
|
||
|
(p_my_vehicle->Get_Last_Object_Id_I_Damaged() == id ||
|
||
|
p_my_vehicle->Get_Last_Object_Id_I_Got_Damaged_By() == id)) {
|
||
|
|
||
|
is_relevant = true;
|
||
|
}
|
||
|
|
||
|
float factor = 1.0f;
|
||
|
if (!is_relevant) {
|
||
|
factor -= cUserOptions::IrrelevancePenalty.Get();
|
||
|
}
|
||
|
|
||
|
WWASSERT(factor > 0);
|
||
|
|
||
|
return factor;
|
||
|
}
|