/*
** 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 : Combat *
* *
* $Archive:: /Commando/Code/Combat/harvester.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 1/18/02 11:16p $*
* *
* $Revision:: 11 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "harvester.h"
#include "vehicle.h"
#include "refinerygameobj.h"
#include "animcontrol.h"
#include "vehiclephys.h"
////////////////////////////////////////////////////////////////
// Local constants
////////////////////////////////////////////////////////////////
enum
{
ACTIONID_GOTO_TIBERIUM = 1000,
ACTIONID_DOCK,
};
////////////////////////////////////////////////////////////////
//
// HarvesterClass
//
////////////////////////////////////////////////////////////////
HarvesterClass::HarvesterClass (void) :
Refinery (NULL),
DockLocation (0, 0, 0),
DockEntrance (0, 0, 0),
StateTimer (0),
State (STATE_UNINITIALIZED),
Vehicle (NULL),
IsHarvesting (false)
{
Set_ID (GameObjObserverManager::Get_Next_Observer_ID ());
return ;
}
////////////////////////////////////////////////////////////////
//
// ~HarvesterClass
//
////////////////////////////////////////////////////////////////
HarvesterClass::~HarvesterClass (void)
{
return ;
}
////////////////////////////////////////////////////////////////
//
// Initialize
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Initialize (void)
{
Go_Harvest ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Go_Harvest
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Go_Harvest (void)
{
//
// Bail if the vehicle pointer is invalid, or if the vehicle
// isn't on the ground...
//
if (Vehicle == NULL || Vehicle->Peek_Vehicle_Phys ()->Is_In_Contact () == false) {
return ;
}
State = STATE_GOING_TO_HARVEST;
//
// Choose a random offset into the tiberium box
//
Vector3 rand_offset;
rand_offset.X = WWMath::Random_Float (0, TiberiumRegion.Extent.X - 5);
rand_offset.Y = WWMath::Random_Float (0, TiberiumRegion.Extent.Y - 5);
rand_offset.Z = 0;
//
// Tranform this random offset into a world space position
//
Vector3 rand_pos = TiberiumRegion.Center + (TiberiumRegion.Basis * rand_offset);
//
// Setup an action to instruct the vehicle to goto that location
//
ActionParamsStruct parameters;
parameters.Priority = 99;
parameters.ActionID = ACTIONID_GOTO_TIBERIUM;
parameters.ObserverID = Get_ID ();
parameters.Set_Movement (rand_pos, 1.0F, 1.0F);
Vehicle->Get_Action ()->Goto (parameters);
return ;
}
////////////////////////////////////////////////////////////////
//
// Go_Unload_Tiberium
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Go_Unload_Tiberium (void)
{
Play_Harvest_Animation (false);
//
// Switch states
//
IsHarvesting = false;
State = STATE_GOING_TO_UNLOAD;
//
// Setup an action to instruct the vehicle to dock in its refinery
//
ActionParamsStruct parameters;
parameters.Priority = 99;
parameters.ActionID = ACTIONID_DOCK;
parameters.ObserverID = Get_ID ();
parameters.Dock_Vehicle (DockLocation, DockEntrance);
parameters.MoveSpeed = 1.0F;
Vehicle->Get_Action ()->Dock_Vehicle (parameters);
return ;
}
////////////////////////////////////////////////////////////////
//
// Unload_Tiberium
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Unload_Tiberium (void)
{
Refinery->On_Harvester_Docked ();
State = STATE_UNLOADING;
return ;
}
////////////////////////////////////////////////////////////////
//
// Harvest_Tiberium
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Harvest_Tiberium (void)
{
//
// Reset the harvesting timer (if necessary)
//
if (IsHarvesting == false) {
HarvestTimer = 15.0F;
IsHarvesting = true;
}
//
// Harvest the tiberium for a few seconds
//
State = STATE_HARVESTING;
StateTimer = WWMath::Random_Float (3.0F, 7.0F);
//
// Start playing the harvest animation
//
Play_Harvest_Animation (true);
return ;
}
////////////////////////////////////////////////////////////////
//
// Action_Complete
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Action_Complete
(
GameObject * game_obj,
int action_id,
ActionCompleteReason complete_reason
)
{
//
// Check to ensure the harvester successfully completed
// its action...
//
if (complete_reason == ACTION_COMPLETE_NORMAL) {
//
// Which action was completed?
//
if (action_id == ACTIONID_GOTO_TIBERIUM) {
//
// Check to ensure we were going to harvest the
// tiberium before actually start harvesting
//
if (State == STATE_GOING_TO_HARVEST) {
Harvest_Tiberium ();
}
} else if (action_id == ACTIONID_DOCK) {
Unload_Tiberium ();
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Animation_Complete
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Animation_Complete
(
GameObject * game_obj,
const char * animation_name
)
{
return ;
}
////////////////////////////////////////////////////////////////
//
// Attach
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Attach (GameObject *game_obj)
{
WWASSERT (game_obj != NULL);
if (game_obj != NULL) {
//
// See if this game object is a vehicle (it better be)
//
PhysicalGameObj *physical_obj = game_obj->As_PhysicalGameObj ();
if (physical_obj != NULL) {
Vehicle = physical_obj->As_VehicleGameObj ();
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Detach
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Detach (GameObject * /*game_obj*/)
{
//
// Remove ourselves from the refinery
//
if (Refinery != NULL) {
Refinery->Set_Harvester (NULL);
}
Vehicle = NULL;
return ;
}
////////////////////////////////////////////////////////////////
//
// Think
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Think (void)
{
if (IsHarvesting) {
//
// If we are harvesting, check to see if its time to go unload
// the tiberium.
//
HarvestTimer -= TimeManager::Get_Frame_Seconds ();
if (HarvestTimer <= 0) {
Go_Unload_Tiberium ();
}
}
switch (State)
{
case STATE_HARVESTING:
//
// Check to see if we should harvest in a new location
//
StateTimer -= TimeManager::Get_Frame_Seconds ();
if (StateTimer <= 0) {
Go_Harvest ();
}
break;
case STATE_UNINITIALIZED:
if (Vehicle != NULL) {
Go_Harvest ();
}
break;
case STATE_GOING_TO_HARVEST:
case STATE_GOING_TO_UNLOAD:
case STATE_UNLOADING:
default:
break;
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Stop
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Stop (void)
{
//
// Stopt the harvester from moving
//
if (Vehicle != NULL) {
Vehicle->Get_Action ()->Reset (100);
Vehicle->Enable_Engine (false);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Play_Harvest_Animation
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Play_Harvest_Animation (bool onoff)
{
if (onoff) {
//
// Play the animation looped...
//
Vehicle->Set_Animation (HarvestAnimationName, true);
} else {
//
// Stop the animation
//
AnimControlClass *anim_control = Vehicle->Get_Anim_Control ();
if (anim_control != NULL) {
anim_control->Set_Animation( (const char *)NULL );
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Custom
//
////////////////////////////////////////////////////////////////
void
HarvesterClass::Custom
(
GameObject * game_obj,
int type,
int param,
GameObject * sender
)
{
//
// Are we backing into the docking bay?
//
if (type == CUSTOM_EVENT_DOCK_BACKING_IN) {
Refinery->Play_Unloading_Animation (true);
}
return ;
}
void HarvesterClass::Damaged(GameObject* damaged, GameObject* killer, float amount)
{
// Only report damage not heal
if (amount > 0.0f) {
VehicleGameObj* vehicle = static_cast(damaged);
assert(Vehicle == vehicle);
// If the unit has been damaged to the point of being destroyed
// then do not report the damaged event. A killed event will be sent
// later.
DefenseObjectClass* defense = vehicle->Get_Defense_Object();
if (defense && (defense->Get_Health() <= 0.0f)) {
return;
}
DamageableGameObj* damager = NULL;
if (killer) {
damager = killer->As_DamageableGameObj();
}
bool friendlyFire = (damager && (damager->Get_Player_Type() == vehicle->Get_Player_Type()));
// Notify the building
if (!friendlyFire) {
assert(Refinery != NULL);
Refinery->On_Harvester_Damaged(vehicle);
}
}
}
void HarvesterClass::Destroyed(GameObject* destroyed)
{
VehicleGameObj* vehicle = static_cast(destroyed);
assert(Vehicle == vehicle);
Refinery->On_Harvester_Destroyed(vehicle);
}