/*
** 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 .
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWPhys *
* *
* $Archive:: /Commando/Code/wwphys/motorcycle.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 8/17/01 8:45p $*
* *
* $Revision:: 28 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "motorcycle.h"
#include "physcontrol.h"
#include "persistfactory.h"
#include "simpledefinitionfactory.h"
#include "wwphysids.h"
#include "wwhack.h"
#include "wwprofile.h"
DECLARE_FORCE_LINK(motorcycle);
/*
** Declare a PersistFactory for MotorcycleClasses
*/
SimplePersistFactoryClass _MotorcycleFactory;
/*
** Chunk-ID's used by MotorcycleClass
*/
enum
{
MOTORCYCLE_CHUNK_WHEELEDVEHICLE = 0x06511110,
MOTORCYCLE_CHUNK_VARIABLES,
MOTORCYCLE_VARIABLE_LEANK0 = 0x00,
MOTORCYCLE_VARIABLE_LEANK1 = 0x01,
};
MotorcycleClass::MotorcycleClass(void) :
LeanK0(18.0f),
LeanK1(5.0f)
{
}
void MotorcycleClass::Init(const MotorcycleDefClass & def)
{
WheeledVehicleClass::Init(def);
LeanK0 = def.LeanK0;
LeanK1 = def.LeanK1;
}
MotorcycleClass::~MotorcycleClass(void)
{
}
void MotorcycleClass::Compute_Force_And_Torque(Vector3 * force,Vector3 * torque)
{
static float MAX_ROLL = DEG_TO_RADF(20.0f);
static float MAX_VDOT = 15.0f; // at this speed, cycle will lean over MAX_ROLL
static float MAX_TURN = 1.0f; // at this turn, cycle will lean over MAX_ROLL
static float MAX_LVEL = 10.0f;
{
WWPROFILE("MotorcycleClass::Compute_Force_And_Torque");
// Read the controller inputs
// Accept either strafe or turn as a turn command
float turn_left = 0.0f;
if (Get_Controller() != NULL) {
if (WWMath::Fabs(Controller->Get_Turn_Left()) > WWMath::Fabs(Controller->Get_Move_Left())) {
turn_left = Controller->Get_Turn_Left();
} else {
turn_left = Controller->Get_Move_Left();
}
}
// What is our rotation about our x axis, we want it to be zero always
Vector3 yvec;
Get_Transform().Get_Y_Vector(&yvec);
float roll = atan2(yvec.Z,sqrt(yvec.X * yvec.X + yvec.Y * yvec.Y));
float target_roll;
float vdot = Vector3::Dot_Product(Get_Transform().Get_X_Vector(),Velocity);
// compute our lateral velocity, first we need a vector in the lateral direction
Vector3 lateral_vel = Velocity - vdot * Get_Transform().Get_X_Vector();
float lvel_sign = WWMath::Sign(Vector3::Dot_Product(lateral_vel,Get_Transform().Get_Y_Vector()));
lateral_vel.Z = 0;
float lvel = lateral_vel.Length();
if (lvel > 0.0f) {
float clamp_lvel = lvel/MAX_LVEL;
if (clamp_lvel > 1.0f) clamp_lvel = 1.0f;
float clamp_turn = turn_left/MAX_TURN;
if (clamp_turn > 1.0f) clamp_turn = 1.0f;
if (clamp_turn < -1.0f) clamp_turn = -1.0f;
target_roll = MAX_ROLL * lvel_sign * clamp_lvel;
} else {
target_roll = 0.0f;
}
// What is our angular velocity about the x axis we want it to be zero as well
float droll = Vector3::Dot_Product(AngularVelocity,Get_Transform().Get_X_Vector());
WWASSERT(WWMath::Is_Valid_Float(roll));
WWASSERT(WWMath::Is_Valid_Float(target_roll));
WWASSERT(WWMath::Is_Valid_Float(droll));
WWASSERT(WWMath::Is_Valid_Float(torque->X));
WWASSERT(WWMath::Is_Valid_Float(torque->Y));
WWASSERT(WWMath::Is_Valid_Float(torque->Z));
// Apply a balancing torque
*torque += (LeanK0 * (target_roll - roll) + LeanK1 * (0.0f - droll)) * Get_Transform().Get_X_Vector();
// Record the amount that the character on the bike should lean
LeanValue = (target_roll - roll) / MAX_ROLL;
}
// Parent classes compute their forces and torques
WheeledVehicleClass::Compute_Force_And_Torque(force,torque);
}
void MotorcycleClass::Compute_Inertia(void)
{
WheeledVehicleClass::Compute_Inertia();
}
const PersistFactoryClass & MotorcycleClass::Get_Factory (void) const
{
return _MotorcycleFactory;
}
bool MotorcycleClass::Save (ChunkSaveClass &csave)
{
csave.Begin_Chunk(MOTORCYCLE_CHUNK_WHEELEDVEHICLE);
WheeledVehicleClass::Save(csave);
csave.End_Chunk();
csave.Begin_Chunk(MOTORCYCLE_CHUNK_VARIABLES);
WRITE_MICRO_CHUNK(csave,MOTORCYCLE_VARIABLE_LEANK0,LeanK0);
WRITE_MICRO_CHUNK(csave,MOTORCYCLE_VARIABLE_LEANK1,LeanK1);
csave.End_Chunk();
return true;
}
bool MotorcycleClass::Load (ChunkLoadClass &cload)
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case MOTORCYCLE_CHUNK_WHEELEDVEHICLE:
WheeledVehicleClass::Load(cload);
break;
case MOTORCYCLE_CHUNK_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK(cload,MOTORCYCLE_VARIABLE_LEANK0,LeanK0);
READ_MICRO_CHUNK(cload,MOTORCYCLE_VARIABLE_LEANK1,LeanK1);
}
cload.Close_Micro_Chunk();
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
/***********************************************************************************************
**
** MotorcycleDefClass Implementation
**
***********************************************************************************************/
/*
** Persist factory for MotorcycleDefClass
*/
SimplePersistFactoryClass _MotorcycleDefFactory;
/*
** Definition factory for MotorcycleDefClass. This makes it show up in the editor
*/
DECLARE_DEFINITION_FACTORY(MotorcycleDefClass, CLASSID_MOTORCYCLEDEF, "Motorcycle") _MotorcycleDefDefFactory;
/*
** Chunk ID's used by MotorcycleDefClass
*/
enum
{
MOTORCYCLEDEF_CHUNK_WHEELEDVEHICLEDEF = 0x00516000, // (parent class)
MOTORCYCLEDEF_CHUNK_VARIABLES,
MOTORCYCLEDEF_VARIABLE_LEANK0 = 0x00,
MOTORCYCLEDEF_VARIABLE_LEANK1,
};
MotorcycleDefClass::MotorcycleDefClass(void) :
LeanK0(18.0f),
LeanK1(5.0f)
{
// make our parameters editable!
FLOAT_EDITABLE_PARAM(MotorcycleDefClass, LeanK0, 0.01f, 100000.0f);
FLOAT_EDITABLE_PARAM(MotorcycleDefClass, LeanK1, 0.01f, 100000.0f);
}
uint32 MotorcycleDefClass::Get_Class_ID (void) const
{
return CLASSID_MOTORCYCLEDEF;
}
PersistClass * MotorcycleDefClass::Create(void) const
{
MotorcycleClass * obj = NEW_REF(MotorcycleClass,());
obj->Init(*this);
return obj;
}
const PersistFactoryClass & MotorcycleDefClass::Get_Factory (void) const
{
return _MotorcycleDefFactory;
}
bool MotorcycleDefClass::Save(ChunkSaveClass &csave)
{
csave.Begin_Chunk(MOTORCYCLEDEF_CHUNK_WHEELEDVEHICLEDEF);
WheeledVehicleDefClass::Save(csave);
csave.End_Chunk();
csave.Begin_Chunk(MOTORCYCLEDEF_CHUNK_VARIABLES);
WRITE_MICRO_CHUNK(csave,MOTORCYCLEDEF_VARIABLE_LEANK0,LeanK0);
WRITE_MICRO_CHUNK(csave,MOTORCYCLEDEF_VARIABLE_LEANK1,LeanK1);
csave.End_Chunk();
return true;
}
bool MotorcycleDefClass::Load(ChunkLoadClass &cload)
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case MOTORCYCLEDEF_CHUNK_WHEELEDVEHICLEDEF:
WheeledVehicleDefClass::Load(cload);
break;
case MOTORCYCLEDEF_CHUNK_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK(cload,MOTORCYCLEDEF_VARIABLE_LEANK0,LeanK0);
READ_MICRO_CHUNK(cload,MOTORCYCLEDEF_VARIABLE_LEANK1,LeanK1);
}
cload.Close_Micro_Chunk();
}
break;
}
cload.Close_Chunk();
}
return true;
}
bool MotorcycleDefClass::Is_Type(const char * type_name)
{
if (stricmp(type_name,MotorcycleDefClass::Get_Type_Name()) == 0) {
return true;
} else {
return WheeledVehicleDefClass::Is_Type(type_name);
}
}