You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

5525 lines
220 KiB

//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code 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.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/* $Header: F:\projects\c&c\vcs\code\building.cpv 2.13 02 Aug 1995 17:00:14 JOE_BOSTIC $ */
/***********************************************************************************************
*** 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 : Command & Conquer *
* *
* File Name : BUILDING.CPP *
* *
* Programmer : Joe L. Bostic *
* *
* Start Date : September 10, 1993 *
* *
* Last Update : August 20, 1995 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* BuildingClass::AI -- Handles non-graphic AI processing for buildings. *
* BuildingClass::Active_Click_With -- Handles cell selection for buildings. *
* BuildingClass::As_Target -- Convert the building into a target value. *
* BuildingClass::Assign_Target -- Assigns a target to the building. *
* BuildingClass::Begin_Mode -- Begins an animation mode for the building. *
* BuildingClass::BuildingClass -- Constructor for buildings. *
* BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? *
* BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. *
* BuildingClass::Can_Fire -- Determines if this building can fire. *
* BuildingClass::Captured -- Captures the building. *
* BuildingClass::Center_Coord -- Fetches the center coordinate for the building. *
* BuildingClass::Click_With -- Handles clicking on the map while the building is selected. *
* BuildingClass::Crew_Type -- This determines the crew that this object generates. *
* BuildingClass::Death_Announcement -- Announce the death of this building. *
* BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. *
* BuildingClass::Detach -- Handles target removal from the game system. *
* BuildingClass::Detach_All -- Possibly abandons production according to factory type. *
* BuildingClass::Draw_It -- Displays the building at the location specified. *
* BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. *
* BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. *
* BuildingClass::Exit_Object -- Initiates an object to leave the building. *
* BuildingClass::Fire_At -- Fires weapon at specified target. *
* BuildingClass::Fire_Coord -- Calculates the coordinate that projectile would appear. *
* BuildingClass::Fire_Direction -- Fetches the direction of firing. *
* BuildingClass::Fire_Out -- Handles when attached animation expires. *
* BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. *
* BuildingClass::Grand_Opening -- Handles construction completed special operations. *
* BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. *
* BuildingClass::Init -- Initialize the building system to an empty null state. *
* BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. *
* BuildingClass::Look -- Reveal map around building. *
* BuildingClass::Mark -- Building interface to map rendering system. *
* BuildingClass::Mission_Attack -- Handles attack mission for building. *
* BuildingClass::Mission_Construction -- Handles mission construction. *
* BuildingClass::Mission_Deconstruction -- Handles building deconstruction. *
* BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. *
* BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. *
* BuildingClass::Mission_Missile -- State machine for nuclear missile launch. *
* BuildingClass::Mission_Repair -- Handles the repair (active) state for building. *
* BuildingClass::Mission_Unload -- Handles the unload mission for a building. *
* BuildingClass::Pip_Count -- Determines "full" pips to display for building. *
* BuildingClass::Power_Output -- Fetches the current power output from this building. *
* BuildingClass::Read_INI -- Reads buildings from INI file. *
* BuildingClass::Receive_Message -- Handle an incoming message to the building. *
* BuildingClass::Refund_Amount -- Fetches the refund amount if building is sold. *
* BuildingClass::Remap_Table -- Fetches the remap table to use for this building. *
* BuildingClass::Repair -- Initiates or terminates the repair process. *
* BuildingClass::Revealed -- Reveals the building to the specified house. *
* BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. *
* BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. *
* BuildingClass::Take_Damage -- Inflicts damage points upon a building. *
* BuildingClass::Toggle_Primary -- Toggles the primary factory state. *
* BuildingClass::Unlimbo -- Removes a building from limbo state. *
* BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. *
* BuildingClass::Update_Specials -- removes computer specials for lost bld *
* BuildingClass::What_Action -- Determines action to perform if click on specified object. *
* BuildingClass::What_Action -- Determines what action will occur. *
* BuildingClass::Write_INI -- Writes all building data to an INI file. *
* BuildingClass::delete -- Deallocates building object. *
* BuildingClass::new -- Allocates a building object from building pool. *
* BuildingClass::~BuildingClass -- Destructor for building type objects. *
* BuildingClass::Validate -- validates building pointer *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
/*
** New sidebar for GlyphX multiplayer. ST - 3/26/2019 12:24PM
*/
#include "SidebarGlyphx.h"
enum SAMState {
SAM_NONE=-1, // Used for non SAM site buildings.
SAM_UNDERGROUND, // Launcher is underground and awaiting orders.
SAM_RISING, // Doors open and launcher rises to normal locked down position.
SAM_READY, // Launcher can be facing any direction tracking targets.
SAM_FIRING, // Stationary while missile is being fired.
SAM_READY2, // Launcher can be facing any direction tracking targets.
SAM_FIRING2, // Stationary while missile is being fired.
SAM_LOCKING, // Rotating to locked position in preparation for lowering.
SAM_LOWERING, // Launcher is lowering into the ground.
};
/***************************************************************************
** Center of building offset table.
*/
COORDINATE const BuildingClass::CenterOffset[BSIZE_COUNT] = {
0x00800080L,
0x008000FFL,
0x00FF0080L,
0x00FF00FFL,
0x018000FFL,
0x00FF0180L,
0x01800180L,
0x00FF0200L,
0x02800280L,
};
/*
** This contains the value of the Virtual Function Table Pointer
*/
void * BuildingClass::VTable;
/***********************************************************************************************
* BuildingClass::Validate -- validates building pointer *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* 1 = ok, 0 = error *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 08/09/1995 BRR : Created. *
*=============================================================================================*/
#ifdef CHEAT_KEYS
int BuildingClass::Validate(void) const
{
int num;
num = Buildings.ID(this);
if (num < 0 || num >= BUILDING_MAX) {
Validate_Error("BUILDING");
return (0);
}
else
return (1);
}
#else
#define Validate()
#endif
/***********************************************************************************************
* BuildingClass::Receive_Message -- Handle an incoming message to the building. *
* *
* This routine handles an incoming message to the building. Messages regulate the *
* various cooperative ventures between buildings and units. This might include such *
* actions as coordinating the construction yard animation with the actual building's *
* construction animation. *
* *
* INPUT: from -- The originator of the message received. *
* *
* message -- The radio message received. *
* *
* param -- Reference to an optional parameter that might be used to return *
* extra information to the message originator. *
* *
* OUTPUT: Returns with the response to the message (typically, this is just RADIO_OK). *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 06/09/1994 JLB : Created. *
* 06/26/1995 JLB : Forces refinery load anim to start immediately. *
* 08/13/1995 JLB : Uses ScenarioInit for special loose "CAN_LOAD" check. *
*=============================================================================================*/
RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
{
Validate();
switch (message) {
/*
** This message is received as a request to attach/load/dock with this building.
** Verify that this is allowed and return the appropriate response.
*/
case RADIO_HELLO:
//Refineries can't be interupted while they're processing a harvester - LLL April 22, 2020
if (Mission == MISSION_HARVEST) {
return(RADIO_NEGATIVE);
}
break;
case RADIO_CAN_LOAD:
TechnoClass::Receive_Message(from, message, param);
if (BState == BSTATE_CONSTRUCTION || (!ScenarioInit && Class->Type != STRUCT_REFINERY && In_Radio_Contact())) return(RADIO_NEGATIVE);
switch (Class->Type) {
case STRUCT_AIRSTRIP:
if (from->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass const *)from) == AIRCRAFT_CARGO) {
return(RADIO_ROGER);
}
break;
case STRUCT_HELIPAD:
if (from->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass const *)from)->Class->IsFixedWing) {
return(RADIO_ROGER);
}
break;
case STRUCT_REPAIR:
if (/*from->Health_Ratio() < 0x0100 &&*/ from->What_Am_I() == RTTI_UNIT || from->What_Am_I() == RTTI_AIRCRAFT) {
return(RADIO_ROGER);
}
return(RADIO_NEGATIVE);
case STRUCT_REFINERY:
if (from->What_Am_I() == RTTI_UNIT &&
*((UnitClass *)from) == UNIT_HARVESTER &&
(ScenarioInit || !Is_Something_Attached())) {
return((Contact_With_Whom() != from) ? RADIO_ROGER : RADIO_NEGATIVE);
}
break;
default:
break;
}
return(RADIO_NEGATIVE);
/*
** This message is received when the object has attached itself to this
** building.
*/
case RADIO_IM_IN:
if (Mission == MISSION_DECONSTRUCTION) {
return(RADIO_NEGATIVE);
}
switch (Class->Type) {
case STRUCT_REPAIR:
IsReadyToCommence = true;
Assign_Mission(MISSION_REPAIR);
from->Assign_Mission(MISSION_SLEEP);
return(RADIO_ROGER);
case STRUCT_HELIPAD:
Assign_Mission(MISSION_REPAIR);
from->Assign_Mission(MISSION_SLEEP);
return(RADIO_ROGER);
case STRUCT_REFINERY:
ScenarioInit++;
Begin_Mode(BSTATE_ACTIVE);
ScenarioInit--;
Mark(MARK_CHANGE);
Assign_Mission(MISSION_HARVEST);
return(RADIO_ATTACH);
}
break;
/*
** Docking maneuver maintenance message. See if new order should be given to the
** unit trying to dock.
*/
case RADIO_DOCKING:
TechnoClass::Receive_Message(from, message, param);
/*
** When in radio contact for loading, the refinery starts
** flashing the lights.
*/
//Fix for refinery animation bug when mission is harvest - LLL April 22, 2020
if (*this == STRUCT_REFINERY && BState != BSTATE_FULL && Mission != MISSION_HARVEST) {
Begin_Mode(BSTATE_FULL);
}
/*
** If this building is already in radio contact, then it might
** be able to satisfy the request to load by bumping off any
** preoccupying task.
*/
if (*this == STRUCT_REPAIR) {
if (Contact_With_Whom() != from) {
if (Transmit_Message(RADIO_ON_DEPOT) == RADIO_ROGER) {
if (Transmit_Message(RADIO_NEED_REPAIR) == RADIO_NEGATIVE) {
Transmit_Message(RADIO_RUN_AWAY);
Transmit_Message(RADIO_OVER_OUT);
return(RADIO_ROGER);
}
}
}
}
/*
** Establish contact with the object if this building isn't already in contact
** with another.
*/
if (!In_Radio_Contact()) {
Transmit_Message(RADIO_HELLO, from);
}
if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
if (*this == STRUCT_HELIPAD) {
param = As_Target();
} else {
if (*this == STRUCT_REPAIR) {
Transmit_Message(RADIO_TETHER);
param = ::As_Target(Coord_Cell(Center_Coord()));
} else {
param = ::As_Target(Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_SW)));
}
}
/*
** Tell the harvester to move to the docking pad of the refinery.
*/
if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) {
/*
** Since the harvester is already there, tell it to begin the backup
** procedure now. If it can't, then tell it to get outta here.
*/
Transmit_Message(RADIO_TETHER);
if (*this == STRUCT_REFINERY && Transmit_Message(RADIO_BACKUP_NOW, from) != RADIO_ROGER) {
from->Scatter(NULL, true);
}
}
}
return(RADIO_ROGER);
/*
** If a transport or harvester is requesting permission to head toward, dock
** and load/unload, check to make sure that this is allowed given the current
** state of the building.
*/
case RADIO_ARE_REFINERY:
if (Is_Something_Attached() || In_Radio_Contact() || IsInLimbo || House->Class->House != from->Owner() || (*this != STRUCT_REFINERY/* && *this != STRUCT_REPAIR*/)) {
return(RADIO_NEGATIVE);
}
return(RADIO_ROGER);
/*
** Someone is telling us that it is starting construction. This should only
** occur if this is a construction yard and a building was just placed on
** the map.
*/
case RADIO_BUILDING:
Assign_Mission(MISSION_REPAIR);
TechnoClass::Receive_Message(from, message, param);
return(RADIO_ROGER);
/*
** Someone is telling us that they have finished construction. This should
** only occur if this is a construction yard and the building that was being
** constructed has finished. In this case, stop the construction yard
** animation.
*/
case RADIO_COMPLETE:
if (Mission != MISSION_DECONSTRUCTION) {
Assign_Mission(MISSION_GUARD);
}
TechnoClass::Receive_Message(from, message, param);
return(RADIO_ROGER);
/*
** This message may occur unexpectedly if the unit in contact with this
** building is suddenly destroyed. Handle any cleanup necessary. For example,
** a construction yard should stop its construction animation in this case.
*/
case RADIO_OVER_OUT:
Begin_Mode(BSTATE_IDLE);
TechnoClass::Receive_Message(from, message, param);
return(RADIO_ROGER);
/*
** This message is received when an object has completely left
** building. Sometimes special cleanup action is required when
** this event occurs.
*/
case RADIO_UNLOADED:
if (*this == STRUCT_REPAIR) {
if (Distance(from) < 0x0180) {
return(RADIO_ROGER);
}
}
//Turn off the refinery lights - LLL April 22, 2020
if (*this == STRUCT_REFINERY) {
Begin_Mode(BSTATE_IDLE);
}
TechnoClass::Receive_Message(from, message, param);
if (*this == STRUCT_WEAP || *this == STRUCT_AIRSTRIP || *this == STRUCT_REPAIR) return(RADIO_RUN_AWAY);
return(RADIO_ROGER);
}
/*
** Pass along the message to the default message handler in the radio itself.
*/
return(TechnoClass::Receive_Message(from, message, param));
}
#ifdef CHEAT_KEYS
/***********************************************************************************************
* BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. *
* *
* This utility function will output the current status of the building class to the *
* monochrome screen. It is through this data that bugs may be fixed or detected. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/31/1994 JLB : Created. *
*=============================================================================================*/
void BuildingClass::Debug_Dump(MonoClass *mono) const
{
Validate();
mono->Set_Cursor(0, 0);
mono->Print(
"�Name:���������������Mission:����TarCom:���������Radio:�Coord:������������St:Ŀ\n"
"� � � � � � � � �\n"
"����������������N�Y�Health:��������Turret:���������Building:���Cargo:��������Ĵ\n"
"�Active........� � � � � � � � �\n"
"�Limbo.........� � ����������������������������������������������������������Ĵ\n"
"�Owned.........� � �Last Message: �\n"
"�Discovered....� � �Timer:�Arm:��������Tiberium:�Flash:�Stage:�����������������\n"
"�Selected......� � � � � � � � � \n"
"�Teathered.....� � �������������������������������������������� \n"
"�Locked on Map.� � � \n"
"�Is A Loaner...� � � \n"
"� � � � \n"
"� � � � \n"
"� � � � \n"
"�Repairing.....� � � \n"
"� � � � \n"
"� � � � \n"
"�Recoiling.....� � � \n"
"�To Display....� � � \n"
"�������������������� \n");
mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName);
mono->Set_Cursor(35, 3);mono->Printf("%02X:%02X", PrimaryFacing.Current(), PrimaryFacing.Desired());
mono->Set_Cursor(50, 3);
if (Factory) {
mono->Printf(Factory->Get_Object()->Class_Of().IniName);
mono->Printf(" ");
mono->Printf("%d%%", Factory->Completion());
} else {
mono->Printf("(empty)");
}
mono->Text_Print("X", 16 + (IsRepairing?2:0), 14);
// mono->Set_Cursor(44, 3);mono->Printf("%d", SAM);
mono->Set_Cursor(34, 1);mono->Printf("%04X", TarCom);
mono->Set_Cursor(28, 7);mono->Printf("%2d", Arm);
TechnoClass::Debug_Dump(mono);
}
#endif
/***********************************************************************************************
* BuildingClass::Draw_It -- Displays the building at the location specified. *
* *
* This is the low level graphic routine that displays the building at the location *
* specified. *
* *
* INPUT: x,y -- The coordinate to draw the building at. *
* *
* window -- The clipping window to use. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 06/20/1994 JLB : Created. *
* 06/27/1994 JLB : Takes a clipping window parameter. *
* 07/06/1995 JLB : Handles damaged silos correctly. *
*=============================================================================================*/
void BuildingClass::Draw_It(int x, int y, WindowNumberType window)
{
Validate();
void const * shapefile; // Pointer to loaded shape file.
int shapenum;
shapenum = Fetch_Stage();
/*
** The shape file to use for rendering depends on whether the building
** is undergoing construction or not.
*/
if (BState == BSTATE_CONSTRUCTION) {
shapefile = Class->Get_Buildup_Data();
/*
** If the building is deconstructing, then the display frame progresses
** from the end to the beginning. Reverse the shape number accordingly.
*/
if (Mission == MISSION_DECONSTRUCTION) {
shapenum = (Class->Anims[BState].Start+Class->Anims[BState].Count-1)-shapenum;
}
} else {
shapefile = Class->Get_Image_Data();
/*
** The obelisk has a stage value than can be overridden by
** its current state.
*/
if (*this == STRUCT_OBELISK) {
if (IsCharged) {
shapenum = 3;
} else {
if (IsCharging) {
shapenum = Fetch_Stage();
} else {
shapenum = 0;
}
}
}
/*
** Buildings that contain a turret handle their shape determination
** differently than normal buildings. They need to take into consideration
** the direction the turret is facing.
*/
if (Class->IsTurretEquipped) {
shapenum = UnitClass::BodyShape[Facing_To_32(PrimaryFacing.Current())];
if (*this == STRUCT_SAM) {
/*
** SAM sites that are free to rotate fetch their animation frame
** from the building's turret facing. All other animation stages
** fetch their frame from the embedded animation sequencer.
*/
if (Status == SAM_READY || Status == SAM_FIRING || Status == SAM_READY2 || Status == SAM_FIRING2 || Status == SAM_LOCKING) {
shapenum += 16;
} else {
shapenum = Fetch_Stage();
}
} else {
if (IsInRecoilState) {
shapenum += 32;
}
}
if (Health_Ratio() < 0x0080) {
shapenum += 64;
}
} else {
/*
** If it has only one point of strength left, it is shown in the
** worst state possible.
*/
if (Strength <= 1) {
shapenum = Get_Build_Frame_Count(shapefile)-1;
} else {
if (*this == STRUCT_WEAP) {
shapenum = 0;
if (Health_Ratio() < 0x0080) {
shapenum = 1;
}
} else {
/*
** Special render stage for silos. The stage is dependant on the current
** Tiberium collected as it relates to Tiberium capacity.
*/
if (*this == STRUCT_STORAGE) {
int level = 0;
if (House->Capacity) {
level = (House->Tiberium * 5) / House->Capacity;
}
// int level = Fixed_To_Cardinal(4, Cardinal_To_Fixed(House->Capacity, House->Tiberium));
shapenum += Bound(level, 0, 4);
if (Health_Ratio() < 0x0080) {
shapenum += 5;
}
} else {
if (Health_Ratio() < 0x0080) {
/*
** Special damage stage for pump.
*/
if (!Class->IsSimpleDamage) {
int last1 = Class->Anims[BSTATE_IDLE].Start + Class->Anims[BSTATE_IDLE].Count;
int last2 = Class->Anims[BSTATE_ACTIVE].Start + Class->Anims[BSTATE_ACTIVE].Count;
int largest = MAX(last1, last2);
last2 = Class->Anims[BSTATE_AUX1].Start + Class->Anims[BSTATE_AUX1].Count;
largest = MAX(largest, last2);
last2 = Class->Anims[BSTATE_AUX2].Start + Class->Anims[BSTATE_AUX2].Count;
largest = MAX(largest, last2);
shapenum += largest;
} else {
/*
** Presume that the damage stage is the end frame.
*/
shapenum = Get_Build_Frame_Count(shapefile) - 2;
}
}
}
}
}
}
}
/*
** Actually draw the building shape.
*/
IsTheaterShape = Class->IsTheater;
Techno_Draw_Object(shapefile, shapenum, x, y, window);
IsTheaterShape = false;
/*
** Patch for adding overlay onto weapon factory. Only add the overlay if
** the building has more than 1 hp. Also, if the building's in radio
** contact, he must be unloading a constructed vehicle, so draw that
** vehicle before drawing the overlay.
*/
if (BState != BSTATE_CONSTRUCTION) {
/*
** A Tethered object is always rendered AFTER the building.
*/
if (*this == STRUCT_WEAP && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) {
TechnoClass * contact = Contact_With_Whom();
assert(contact->IsActive);
int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord())));
int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord())));
contact->Draw_It(xxx, yyy, window);
contact->IsToDisplay = false;
}
/*
** Draw the weapon factory custom overlay graphic.
*/
if (*this == STRUCT_WEAP && Strength > 1) {
shapenum = Door_Stage();
if (Health_Ratio() < 0x0080) shapenum += 10;
// Added override shape file name. ST - 6/20/2019 1:35PM
//Techno_Draw_Object(WarFactoryOverlay, shapenum, x, y, window);
Techno_Draw_Object_Virtual(WarFactoryOverlay, shapenum, x, y, window, "WEAP2");
}
/*
** Draw any repair feedback graphic required.
*/
if (IsRepairing && IsWrenchVisible) {
CC_Draw_Shape(ObjectTypeClass::SelectShapes, SELECT_WRENCH, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL);
}
}
TechnoClass::Draw_It(x, y, window);
}
/***********************************************************************************************
* BuildingClass::Mark -- Building interface to map rendering system. *
* *
* This routine is used to mark the map cells so that when it renders *
* the underlying icons will also be updated as necessary. *
* *
* INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) *
* MARK_UP -- Building is removed. *
* MARK_CHANGE -- Building changes shape. *
* MARK_DOWN -- Building is added. *
* *
* OUTPUT: bool; Did the mark operation succeed? Failure could be the result of marking down *
* when the building is already marked down, or visa versa. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/31/1994 JLB : Created. *
* 04/15/1994 JLB : Converted to member function. *
* 04/16/1994 JLB : Added health bar tracking. *
* 12/23/1994 JLB : Calls low level check before proceeding. *
* 01/27/1995 JLB : Special road spacer template added. *
*=============================================================================================*/
bool BuildingClass::Mark(MarkType mark)
{
Validate();
if (TechnoClass::Mark(mark)) {
short const *offset = Overlap_List();
short const *occupy = Occupy_List();
CELL cell = Coord_Cell(Coord);
SmudgeType bib;
switch (mark) {
case MARK_UP:
Map.Pick_Up(cell, this);
if (Class->Bib_And_Offset(bib, cell)) {
SmudgeClass * smudge = new SmudgeClass(bib);
if (smudge) {
smudge->Disown(cell);
delete smudge;
}
}
break;
case MARK_DOWN:
/*
** Special wall logic is handled here. A building that is really a wall
** gets converted into an overlay wall type when it is placed down. The
** actual building object itself is destroyed.
*/
if (Class->IsWall) {
switch (Class->Type) {
case STRUCT_BRICK_WALL:
new OverlayClass(OVERLAY_BRICK_WALL, cell, House->Class->House);
break;
case STRUCT_BARBWIRE_WALL:
new OverlayClass(OVERLAY_BARBWIRE_WALL, cell, House->Class->House);
break;
case STRUCT_SANDBAG_WALL:
new OverlayClass(OVERLAY_SANDBAG_WALL, cell, House->Class->House);
break;
case STRUCT_WOOD_WALL:
new OverlayClass(OVERLAY_WOOD_WALL, cell, House->Class->House);
break;
case STRUCT_CYCLONE_WALL:
new OverlayClass(OVERLAY_CYCLONE_WALL, cell, House->Class->House);
break;
}
Transmit_Message(RADIO_OVER_OUT);
Delete_This();
} else {
if (Can_Enter_Cell(cell) == MOVE_OK) {
/*
** Determine if a bib is required for this building. If one is, then
** create and place it.
*/
CELL newcell = cell;
if (Class->Bib_And_Offset(bib, newcell)) {
new SmudgeClass(bib, Cell_Coord(newcell), House->Class->House);
}
Map.Place_Down(cell, this);
} else {
return(false);
}
}
break;
default:
Map.Refresh_Cells(cell, offset);
Map.Refresh_Cells(cell, occupy);
break;
}
return(true);
}
return(false);
}
/***********************************************************************************************
* BuildingClass::Fire_At -- Fires weapon at specified target. *
* *
* This routine does the actual firing of a projectile from the *
* building toward the specified target. Prior to calling this *
* routine, the building must have rotated into position and acquired *
* a suitable target. *
* *
* INPUT: target -- The target to fire upon. *
* *
* which -- Which weapon to use for firing. 0=primary, 1=secondary. *
* *
* OUTPUT: Returns with a pointer to the projectile just launched. This *
* may come in handy if additional adjustments to the projectile *
* are required. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 04/16/1994 JLB : Created. *
*=============================================================================================*/
BulletClass * BuildingClass::Fire_At(TARGET target, int which)
{
Validate();
BulletClass * bullet; // Projectile.
WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary];
bullet = TechnoClass::Fire_At(target, which);
if (bullet) {
if (*this == STRUCT_SAM) {
AnimClass *anim = new AnimClass((AnimType)(ANIM_SAM_N + Dir_Facing(PrimaryFacing.Current())), Center_Coord());
if (anim) {
anim->Attach_To(this);
}
// MBL 04.17.2020
Sound_Effect(weapon->Sound, Coord);
} else {
/*
** Flash the muzzle, play sound, and perform any firing animation.
*/
Sound_Effect(weapon->Sound, Coord);
AnimClass* anim = NULL;
if (weapon->Fires == BULLET_BULLET) {
anim = new AnimClass((AnimType)(ANIM_GUN_N + Dir_Facing(PrimaryFacing.Current())), Fire_Coord(which));
} else {
switch (weapon->Fires) {
case BULLET_SPREADFIRE:
break;
case BULLET_LASER:
IsCharging = false;
IsCharged = false;
Set_Stage(0);
Set_Rate(0);
break;
default:
anim = new AnimClass(ANIM_MUZZLE_FLASH, Fire_Coord(which));
break;
}
}
if (anim != NULL) {
anim->Attach_To(this);
}
Mark(MARK_CHANGE);
}
}
return(bullet);
}
/***********************************************************************************************
* BuildingClass::AI -- Handles non-graphic AI processing for buildings. *
* *
* This function is to handle the AI logic for the building. The graphic logic (facing, *
* firing, and animation) is handled elsewhere. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/31/1994 JLB : Created. *
* 12/26/1994 JLB : Handles production. *
* 06/11/1995 JLB : Revamped. *
*=============================================================================================*/
void BuildingClass::AI(void)
{
Validate();
/*
** Process building animation state changes. Transition to a following state
** if there is one specified and the current animation sequence has expired.
** This process must occur before mission AI since the mission AI relies on
** the bstate change to occur immediately before the MissionClass::AI.
*/
bool stagechange = Graphic_Logic();
bool toloop = false;
/*
** Always refresh the SAM site if it has an animation change.
*/
if (*this == STRUCT_SAM && stagechange) Mark(MARK_CHANGE);
if ((!Class->IsTurretEquipped && *this != STRUCT_OBELISK) || Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION) {
if (stagechange) {
/*
** Check for animation end or if special case of MCV deconstructing when it is allowed
** to convert back into an MCV.
*/
BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control();
/*
** When the last frame of the current animation sequence is reached, flag that
** a new mission may be started. This must occur before the animation actually
** loops so that if a mission change does occur, it will have a chance to change
** the building graphic before the last frame is replaced by the first frame of
** the loop.
*/
if (Fetch_Stage() == ctrl->Start+ctrl->Count-1 || (Special.IsMCVDeploy && *this == STRUCT_CONST && Mission == MISSION_DECONSTRUCTION && Fetch_Stage() == (42-19))) {
IsReadyToCommence = true;
}
/*
** If the animation advances beyond the last frame, then start the animation
** sequence over from the beginning.
*/
if (Fetch_Stage() >= ctrl->Start+ctrl->Count) {
toloop = true;
}
Mark(MARK_CHANGE);
} else {
if (BState == BSTATE_NONE || Fetch_Rate() == 0) {
IsReadyToCommence = true;
}
}
}
/*
** If there is a door that is animating, then it might cause this building
** to be redrawn. Check for and flag to redraw as necessary.
*/
if (Time_To_Redraw()) {
Clear_Redraw_Flag();
Mark(MARK_CHANGE);
}
/*
** The animation sequence has looped. Restart it and flag this loop condition.
** This is used to tell the mission system that the animation has completed. It
** also signals that now is a good time to act on any pending mission.
*/
if (toloop) {
BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control();
if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) {
Set_Rate(Options.Normalize_Delay(ctrl->Rate));
} else {
Set_Rate(ctrl->Rate);
}
Set_Stage(ctrl->Start);
Mark(MARK_CHANGE);
}
/*
** If now is a good time to act on a new mission, then do so. This process occurs
** here because some outside event may have requested a mission change for the building.
** Such outside requests (player input) must be initiated BEFORE the normal AI process.
*/
if (IsReadyToCommence && BState != BSTATE_CONSTRUCTION) {
/*
** Clear the commencement flag ONLY if something actually occured. By acting
** this way, a building can set the IsReadyToCommence flag before it goes
** to "sleep" knowing that it will wake up as soon as a new mission comes
** along.
*/
if (Commence()) {
IsReadyToCommence = false;
}
}
/*
** Proceed with normal logic processing. This is where the mission processing
** occurs. This call must be located after the animation sequence makes the
** transition to the next frame (see above) in order for the mission logic to
** act at the exact moment of graphic transition BEFORE it has a chance to
** be displayed.
*/
TechnoClass::AI();
/*
** If now is a good time to act on a new mission, then do so. This occurs here because
** some AI event may have requested a mission change (usually from another mission
** state machine). This must occur here before it has a chance to render.
*/
if (IsReadyToCommence) {
/*
** Clear the commencement flag ONLY if something actually occured. By acting
** this way, a building can set the IsReadyToCommence flag before it goes
** to "sleep" knowing that it will wake up as soon as a new mission comes
** along.
*/
if (Commence()) {
IsReadyToCommence = false;
}
}
/*
** If a change of animation was requested, then make the change
** now. The building animation system acts independantly but subordinate
** to the mission state machine system. By performing the animation change-up
** here, the mission AI system is ensured of immediate visual affect when it
** decides to change the animation state of the building.
*/
if (QueueBState != BSTATE_NONE) {
if (BState != QueueBState) {
BState = QueueBState;
BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control();
if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) {
Set_Rate(Options.Normalize_Delay(ctrl->Rate));
} else {
Set_Rate(ctrl->Rate);
}
Set_Stage(ctrl->Start);
}
QueueBState = BSTATE_NONE;
}
/*
** If the building's strength has changed, then update the power
** accordingly.
*/
if (Strength != LastStrength) {
int oldpower = Power_Output();
LastStrength = Strength;
int newpower = Power_Output();
House->Adjust_Power(newpower - oldpower);
}
/*
** Check to see if the destruction countdown timer is active. If so, then decrement it.
** When this timer reaches zero, the building is removed from the map. All the explosions
** are presumed to be in progress at this time.
*/
if (!Strength) {
if (CountDown.Expired()) {
Limbo();
Drop_Debris(WhomToRepay);
Delete_This();
}
return;
}
/*
** Obelisk charging logic.
*/
if (*this == STRUCT_OBELISK && BState != BSTATE_CONSTRUCTION) {
if (Target_Legal(TarCom) && House->Power_Fraction() >= 0x0100) {
if (!IsCharged) {
if (IsCharging) {
if (stagechange) {
Mark(MARK_CHANGE);
if (Fetch_Stage() >= 4) {
IsCharged = true;
IsCharging = false;
Set_Rate(0);
}
}
} else {
IsCharged = false;
IsCharging = true;
Set_Stage(0);
Set_Rate(OBELISK_ANIMATION_RATE);
Sound_Effect(VOC_LASER_POWER, Coord);
}
}
} else {
if (IsCharging || IsCharged) {
Mark(MARK_CHANGE);
IsCharging = false;
IsCharged = false;
Set_Stage(0);
Set_Rate(0);
}
}
}
/*
** Handle any repair process that may be going on.
*/
if (IsRepairing) {
if ((Frame % 15) == 0) {
IsWrenchVisible = (IsWrenchVisible == false);
Mark(MARK_CHANGE);
int cost = Class->Repair_Cost();
int step = Class->Repair_Step();
/*
** Check for and expend any necessary monies to continue the repair.
*/
if (House->Available_Money() >= cost) {
House->Spend_Money(cost);
Strength += step;
if (Strength >= Class->MaxStrength) {
Strength = Class->MaxStrength;
IsRepairing = false;
}
} else {
IsRepairing = false;
}
}
}
/*
** Handle any production tied to this building. Only computer controlled buildings have
** production attached to the building itself. The player uses the sidebar interface for
** all production control.
*/
if (Factory && Factory->Has_Completed() && PlacementDelay.Expired()) {
switch (Exit_Object(Factory->Get_Object())) {
/*
** If the object could not leave the factory, then either request
** a transport, place the (what must be a) building using another method, or
** abort the production and refund money.
*/
case 0:
Factory->Abandon();
delete Factory;
Factory = 0;
break;
case 1:
PlacementDelay = TICKS_PER_SECOND*3;
break;
case 2:
Factory->Completed();
delete Factory;
Factory = 0;
break;
}
}
/*
** For computer controlled buildings, determine what should be produced and start
** production accordingly.
*/
if (!House->IsHuman && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) {
/*
** Possibly start repair process if the building is below half strength.
*/
int ratio = 0x0040;
if (Scenario > 6) ratio = 0x0080;
if (Scenario > 10) ratio = 0x00C0;
if (Class->IsRepairable && Health_Ratio() <= (unsigned) ratio) {
if (House->Available_Money() >= REPAIR_THRESHHOLD) {
Repair(1);
} else {
if (IsTickedOff && (int)Scenario > 2 && Random_Pick(0, 50) < (int)Scenario && !Trigger) {
if (GameToPlay != GAME_NORMAL || Scenario != 15 || PlayerPtr->ActLike != HOUSE_GOOD || *this != STRUCT_TEMPLE) {
Sell_Back(1);
}
}
}
}
/*
** Buildings that produce other objects have special factory logic handled here.
*/
if (Class->ToBuild != RTTI_NONE) {
if (Factory) {
/*
** If production has halted, then just abort production and make the
** funds available for something else.
*/
if (PlacementDelay.Expired() && !Factory->Is_Building()) {
Factory->Abandon();
delete Factory;
Factory = 0;
}
} else {
/*
** Only look to start production if there is at least a small amount of
** money available. In cases where there is no practical money left, then
** production can never complete -- don't bother starting it.
*/
if (House->IsStarted && House->Available_Money() > 10) {
TechnoTypeClass const * techno = House->Suggest_New_Object(Class->ToBuild);
/*
** If a suitable object type was selected for production, then start
** producing it now.
*/
if (techno) {
Factory = new FactoryClass;
if (Factory) {
if (!Factory->Set(*techno, *House)) {
delete Factory;
Factory = 0;
} else {
#ifdef USE_RA_AI
House->Production_Begun(Factory->Get_Object()); // Added for RA AI in TD. ST - 7/26/2019 9:46AM
#endif
Factory->Start();
}
}
}
}
}
}
}
/*
** Check for demolition timeout. When timeout has expired, the building explodes.
*/
if (IsGoingToBlow && CountDown.Expired()) {
/*
** Maybe trigger an achivement. ST - 11/14/2019 1:53PM
*/
TechnoTypeClass const *object_type = Techno_Type_Class();
if (object_type) {
TechnoClass *saboteur = As_Techno(WhomToRepay);
if (saboteur && saboteur->IsActive && saboteur->House && saboteur->House->IsHuman) {
On_Achievement_Event(saboteur->House, "BUILDING_DESTROYED_C4", object_type->IniName);
}
}
SabotagedType = Class->Type;
int damage = 5000;
Take_Damage(damage, 0, WARHEAD_FIRE, As_Techno(WhomToRepay));
Mark(MARK_CHANGE);
}
/*
** If the building was in a recoil state (as it would be just as it fires), then
** restore the building.
*/
if (IsInRecoilState) {
IsInRecoilState = false;
Mark(MARK_CHANGE);
}
/*
** Turret equiped buildings must handle turret rotation logic here. This entails
** rotating the turret to the desired facing as well as figuring out what that
** desired facing should be.
*/
if (Class->IsTurretEquipped && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) {
/*
** Rotate turret to match desired facing.
*/
if (PrimaryFacing.Is_Rotating()) {
if (*this == STRUCT_SAM) {
if (PrimaryFacing.Rotation_Adjust(15)) {
Mark(MARK_CHANGE);
}
} else {
if (PrimaryFacing.Rotation_Adjust(12)) {
Mark(MARK_CHANGE);
}
}
}
}
}
/***********************************************************************************************
* BuildingClass::Unlimbo -- Removes a building from limbo state. *
* *
* Use this routine to transform a building that has been held in limbo *
* state, into one that really exists on the map. Once a building as *
* been unlimboed, then it becomes a normal object in the game world. *
* *
* INPUT: pos -- The position to place the building on the map. *
* *
* dir (optional) -- not used for this class *
* *
* OUTPUT: bool; Was the unlimbo successful? *
* *
* WARNINGS: The unlimbo operation might not be successful if the *
* building could not be placed at the location specified. *
* *
* HISTORY: *
* 04/16/1994 JLB : Created. *
* 06/07/1994 JLB : Matches virtual function format for base class. *
* 05/09/1995 JLB : Handles wall placement. *
* 06/18/1995 JLB : Checks for wall legality before placing down. *
*=============================================================================================*/
bool BuildingClass::Unlimbo(COORDINATE coord, DirType dir)
{
Validate();
#ifdef OBSOLETE
if (*this == STRUCT_ROAD) {
if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) {
ObjectClass * o = OverlayTypeClass::As_Reference(OVERLAY_ROAD).Create_One_Of(House);
if (o && o->Unlimbo(coord)) {
Transmit_Message(RADIO_OVER_OUT);
Delete_This();
return(true);
}
}
return(false);
}
#endif
/*
** If this is a wall type building, then it never gets unlimboed. Instead, it gets
** converted to an overlay type.
*/
if (Class->IsWall) {
if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) {
OverlayType otype = OVERLAY_NONE;
switch (Class->Type) {
case STRUCT_SANDBAG_WALL:
otype = OVERLAY_SANDBAG_WALL;
break;
case STRUCT_CYCLONE_WALL:
otype = OVERLAY_CYCLONE_WALL;
break;
case STRUCT_BRICK_WALL:
otype = OVERLAY_BRICK_WALL;
break;
case STRUCT_BARBWIRE_WALL:
otype = OVERLAY_BARBWIRE_WALL;
break;
case STRUCT_WOOD_WALL:
otype = OVERLAY_WOOD_WALL;
break;
}
if (otype != OVERLAY_NONE) {
ObjectClass * o = OverlayTypeClass::As_Reference(otype).Create_One_Of(House);
if (o && o->Unlimbo(coord)) {
Map[Coord_Cell(coord)].Owner = House->Class->House;
Transmit_Message(RADIO_OVER_OUT);
Delete_This();
return(true);
}
}
}
return(false);
}
/*
** Normal building unlimbo process.
*/
if (TechnoClass::Unlimbo(coord, dir)) {
/*
** Ensure that the owning house knows about the
** new object.
*/
House->BScan |= (1L << Class->Type);
House->ActiveBScan |= (1L << Class->Type);
#ifdef USE_RA_AI
//
// Added for RA AI in TD. ST - 7/26/2019 9:25AM
//
House->Recalc_Center();
#endif
/*
** Update the total factory type, assuming this building has a factory.
*/
switch (Class->ToBuild) {
case RTTI_AIRCRAFTTYPE:
House->AircraftFactories++;
break;
case RTTI_INFANTRYTYPE:
House->InfantryFactories++;
break;
case RTTI_UNITTYPE:
House->UnitFactories++;
break;
case RTTI_BUILDINGTYPE:
House->BuildingFactories++;
break;
default:
break;
}
/*
** Possibly the sidebar will be affected by this addition.
*/
House->IsRecalcNeeded = true;
LastStrength = 0;
// Changed for new multiplayer. ST - 4/3/2019 11:20AM
//if ((!IsDiscoveredByPlayer && Map[Coord_Cell(coord)].IsVisible) || GameToPlay != GAME_NORMAL) {
// Revealed(PlayerPtr);
//}
if (!Is_Discovered_By_Player(House) && Map[Coord_Cell(coord)].Is_Visible(House) || GameToPlay != GAME_NORMAL) {
if (House->IsHuman) {
Revealed(House);
}
}
if (!House->IsHuman) {
Revealed(House);
}
//Changed for multiplayer ST - 3/13/2019 5:31PM
if (Is_Owned_By_Player()) {
//if (IsOwnedByPlayer) {
Map.PowerClass::IsToRedraw = true;
Map.Flag_To_Redraw(false);
}
return(true);
}
return(false);
}
/***********************************************************************************************
* BuildingClass::Take_Damage -- Inflicts damage points upon a building. *
* *
* This routine will inflict damage points upon the specified building. *
* It will handle the damage animation and building destruction. Use *
* this routine whenever a building is attacked. *
* *
* INPUT: damage -- Amount of damage to inflict. *
* *
* distance -- The distance from the damage center point to the object's center point.*
* *
* warhead -- The kind of damage to inflict. *
* *
* source -- The source of the damage. This is used to change targeting. *
* *
* OUTPUT: true/false; Was the building destroyed? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/21/1991 : Created. *
* 04/15/1994 JLB : Converted to member function. *
* 04/16/1994 JLB : Added warhead modifier to damage. *
* 06/03/1994 JLB : Added source of damage as target value. *
* 06/20/1994 JLB : Source is a base class pointer. *
* 11/22/1994 JLB : Shares base damage handler for techno objects. *
* 07/15/1995 JLB : Power ratio gets adjusted. *
*=============================================================================================*/
ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source)
{
Validate();
ResultType res = RESULT_NONE;
int shakes;
if (this != source) {
if (source) Base_Is_Attacked(source);
short const *offset = Occupy_List();
/*
** SPECIAL CASE:
** SAM sites that are closed will take half damage, but never less than one point.
*/
if (*this == STRUCT_SAM && Status == SAM_UNDERGROUND) {
damage /= 2;
damage++;
}
/*
** Damage from an ion cannon against the Temple of Nod does more damage than
** usual.
*/
if (GameToPlay == GAME_NORMAL && *this == STRUCT_TEMPLE && warhead == WARHEAD_PB) {
damage += damage/2;
}
/*
** Perform the low level damage assessment.
*/
res = TechnoClass::Take_Damage(damage, distance, warhead, source);
switch (res) {
case RESULT_DESTROYED:
/*
** Destroy all attached objects.
*/
while (Attached_Object()) {
FootClass * obj = Detach_Object();
Detach_All(true);
delete obj;
}
Sound_Effect(VOC_XPLOBIG4, Coord);
while (*offset != REFRESH_EOL) {
CELL cell = Coord_Cell(Coord) + *offset++;
/*
** If the building is destroyed, then lots of
** explosions occur.
*/
new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Cell_Coord(cell));
if (Random_Pick(0, 1) == 0) {
new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0080), Random_Pick(0, 7), Random_Pick(1, 3));
if (Random_Pick(0, 1) == 0) {
new AnimClass(ANIM_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 7), Random_Pick(1, 3));
}
}
//Start_Profiler();
new AnimClass(ANIM_FBALL1, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0,3));
}
shakes = Class->Cost_Of() / 400;
if (shakes) {
Shake_The_Screen(shakes, Owner());
if (source && Owner() != source->Owner()) {
Shake_The_Screen(shakes, source->Owner());
}
}
Sound_Effect(VOC_CRUMBLE, Coord);
if (Mission == MISSION_DECONSTRUCTION) {
CountDown = 0;
Set_Rate(0);
} else {
CountDown = 8;
}
/*
** A destuction of the Temple by an ion cannon requires a global
** remembering of this fact. The finale uses this information to
** play the correct movie.
*/
if (*this == STRUCT_TEMPLE && warhead == WARHEAD_PB) {
TempleIoned = true;
/*
** Maybe trigger an achivement if the structure is owned by an AI house in campaign mode. ST - 11/14/2019 1:53PM
*/
if (GameToPlay == GAME_NORMAL && !House->IsHuman && source && source->House && source->House->IsHuman) {
TechnoTypeClass const *object_type = Techno_Type_Class();
if (object_type) {
On_Achievement_Event(source->House, "ION_DESTROYS_TEMPLE", object_type->IniName);
}
}
} else {
TempleIoned = false;
}
if (House) {
House->Check_Pertinent_Structures();
}
break;
case RESULT_HALF:
if (*this == STRUCT_PUMP) {
AnimClass *anim = new AnimClass(ANIM_OILFIELD_BURN, Coord_Add(Coord, 0x00400130L), 1);
if (anim) {
anim->Attach_To(this);
}
}
// Fall into next case.
case RESULT_MAJOR:
Sound_Effect(VOC_XPLOBIG4, Coord);
while (*offset != REFRESH_EOL) {
CELL cell = Coord_Cell(Coord) + *offset++;
AnimClass * anim = NULL;
/*
** Show pieces of fire to indicate that a significant change in
** damage level has occurred.
*/
if (warhead == WARHEAD_FIRE) {
switch (Random_Pick(0, 13)) {
case 0:
case 1:
case 2:
case 3:
case 4:
anim = new AnimClass(ANIM_ON_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3));
break;
case 5:
case 6:
case 7:
anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3));
break;
case 8:
anim = new AnimClass(ANIM_ON_FIRE_BIG, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, 1);
break;
case 9:
case 10:
case 11:
case 12:
case 13:
break;
}
} else {
if (Random_Pick(0, 1) == 0) {
anim = new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), Random_Pick(0, 7), Random_Pick(1, 3));
}
}
/*
** If the animation was created, then attach it to the building.
*/
if (anim) {
anim->Attach_To(this);
}
}
break;
case RESULT_NONE:
break;
}
if (source && res != RESULT_NONE) {
/*
** If any damage occurred, then inform the house of this fact. If it is the player's
** house, it might announce this fact.
*/
House->Attacked(this);
/*
** Save the type of the house that's doing the damage, so if the building burns
** to death credit can still be given for the kill
*/
WhoLastHurtMe = source->Owner();
/*
** When certain buildings are hit, they "snap out of it" and
** return fire if they are able and allowed.
*/
if (*this != STRUCT_SAM &&
!House->Is_Ally(source) &&
Class->Primary != WEAPON_NONE &&
(!Target_Legal(TarCom) || !In_Range(TarCom))) {
if (source->What_Am_I() != RTTI_AIRCRAFT && (!House->IsHuman || Special.IsSmartDefense)) {
Assign_Target(source->As_Target());
} else {
/*
** Generate a random rotation effect since there is nothing else that this
** building can do.
*/
if (!PrimaryFacing.Is_Rotating()) {
PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX));
}
}
}
}
}
return(res);
}
/***********************************************************************************************
* BuildingClass::Look -- Reveal map around building. *
* *
* Given a building, reveal the cells around the building in accordance *
* with the building's sighting range. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: This is a very slow routine. Call only when necessary. *
* *
* HISTORY: *
* 08/05/1992 JLB : Created. *
* 04/15/1994 JLB : Converted to member function. *
*=============================================================================================*/
void BuildingClass::Look(bool)
{
/*
** Changed this function to reveal for the appropriate players in GlyphX multiplayer. ST - 4/17/2019 11:50AM
*/
Validate();
if (Class->SightRange) {
Map.Sight_From(House, Coord_Cell(Center_Coord()), Class->SightRange, false);
}
#if (0)
if (GameToPlay != GAME_GLYPHX_MULTIPLAYER) {
if (Is_Owned_By_Player(PlayerPtr) || Is_Discovered_By_Player(PlayerPtr)) {
Map.Sight_From(PlayerPtr, Coord_Cell(Center_Coord()), Class->SightRange, false);
}
} else {
for (int i = 0; i < MPlayerCount; i++) {
HousesType house_type = MPlayerHouses[i];
HouseClass *house = HouseClass::As_Pointer(house_type);
if (Is_Owned_By_Player(house) || Is_Discovered_By_Player(house)) {
Map.Sight_From(house, Coord_Cell(Center_Coord()), Class->SightRange, false);
}
}
}
#endif
}
#if (0) // For reference. ST - 4/17/2019 11:38AM
void BuildingClass::Look(bool)
{
Validate();
if (IsOwnedByPlayer || IsDiscoveredByPlayer) {
Map.Sight_From(PlayerPtr, Coord_Cell(Center_Coord()), Class->SightRange, false);
}
}
#endif
/***********************************************************************************************
* BuildingClass::new -- Allocates a building object from building pool. *
* *
* This routine will allocate a building slot from the building alloc *
* system. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with a pointer to the allocated building. If NULL is *
* returned, then this indicates a failure to allocate. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 04/11/1994 JLB : Created. *
* 04/21/1994 JLB : Converted to operator new. *
* 05/17/1994 JLB : Revamped allocation scheme *
* 07/29/1994 JLB : Simplified. *
*=============================================================================================*/
void * BuildingClass::operator new(size_t )
{
void * ptr = Buildings.Allocate();
if (ptr) {
((BuildingClass *)ptr)->Set_Active();
}
return(ptr);
}
/***********************************************************************************************
* BuildingClass::delete -- Deallocates building object. *
* *
* This is the memory deallocation operation for a building object. *
* Since buildings are allocated out of a fixed memory block, all that *
* is needed is to flag the unit as inactive. *
* *
* INPUT: ptr -- Pointer to building to deallocate. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 04/21/1994 JLB : Created. *
*=============================================================================================*/
void BuildingClass::operator delete(void *ptr)
{
if (ptr) {
((BuildingClass *)ptr)->IsActive = false;
}
Buildings.Free((BuildingClass *)ptr);
//Map.Validate();
}
/***********************************************************************************************
* BuildingClass::BuildingClass -- Constructor for buildings. *
* *
* This routine inserts a building into the object tracking system. *
* It is placed into a limbo state unless a location is provided for *
* it to unlimbo at. *
* *
* INPUT: type -- The structure type to make this object. *
* *
* house -- The owner of this building. *
* *
* pos -- The position to unlimbo the building. If -1 is *
* specified, then the building remains in a limbo *
* state. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 04/21/1994 JLB : Created. *
* 08/07/1995 JLB : Fixed act like value to match expected value. *
*=============================================================================================*/
BuildingClass::BuildingClass(StructType type, HousesType house) :
Class(&BuildingTypeClass::As_Reference(type)),
TechnoClass(house)
{
PlacementDelay = 0;
LastStrength = 0;
ActLike = House->ActLike;
BState = BSTATE_NONE;
CountDown.Set(0);
Factory = 0;
#ifndef USE_RA_AI
House->CurBuildings++; // Removed for RA AI in TD
#endif
WhomToRepay = TARGET_NONE;
IsCaptured = false;
IsCharged = false;
IsCharging = false;
IsSurvivorless = false;
IsGoingToBlow = false;
IsReadyToCommence = false;
IsRepairing = false;
IsSecondShot = !Class->IsTwoShooter;
IsWrenchVisible = false;
QueueBState = BSTATE_NONE;
Strength = Class->MaxStrength;
WhoLastHurtMe = house;
Ammo = Class->MaxAmmo;
/*
** Make sure that newly built house specific building types will act like
** the house they are supposed to act like, regardless of who the current
** owner may happen to be.
*/
if ((type == STRUCT_AIRSTRIP || type == STRUCT_HAND) && house != HOUSE_BAD) {
ActLike = HOUSE_BAD;
IsCaptured = true;
}
if ((type == STRUCT_WEAP || type == STRUCT_BARRACKS) && house != HOUSE_GOOD) {
ActLike = HOUSE_GOOD;
IsCaptured = true;
}
if (GameToPlay == GAME_INTERNET){
House->BuildingTotals->Increment_Unit_Total( (int) type);
}
#ifdef USE_RA_AI
//
// Added for RA AI in TD. ST - 7/26/2019 9:12AM
//
House->Tracking_Add(this);
#endif // USE_RA_AI
}
/***********************************************************************************************
* BuildingClass::~BuildingClass -- Destructor for building type objects. *
* *
* This destructor for building objects will put the building in limbo if possible. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 01/18/1995 JLB : Created. *
*=============================================================================================*/
BuildingClass::~BuildingClass(void)
{
if (GameActive && Class) {
if (House) {
#ifndef USE_RA_AI
House->CurBuildings--;
#else
//
// Added for RA AI in TD. ST - 7/26/2019 9:12AM
//
House->Tracking_Remove(this);
#endif
}
Limbo();
}
}
/***********************************************************************************************
* BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. *
* *
* This routine is called when a building is destroyed. It handles *
* placing the rubble down. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/14/1994 JLB : Created. *
* 06/13/1995 JLB : Added smoke and normal infantry survivor possibility. *
* 07/16/1995 JLB : Survival rate depends on if captured or sabotaged. *
*=============================================================================================*/
void BuildingClass::Drop_Debris(TARGET source)
{
Validate();
CELL const *offset;
CELL cell;
/*
** Special case for Chan to run from destroyed technology
** building.
*/
if (GameToPlay == GAME_NORMAL && *this == STRUCT_MISSION && PlayerPtr->ActLike == HOUSE_BAD && Scenario == 10) {
InfantryClass * i = new InfantryClass(INFANTRY_CHAN, House->Class->House);
ScenarioInit++;
if (i->Unlimbo(Center_Coord(), DIR_N)) {
i->Trigger = TriggerClass::As_Pointer("win");
i->Strength = Random_Pick(5, (int)i->Class->MaxStrength);
ScenarioInit--;
i->Scatter(0, true);
ScenarioInit++;
i->Assign_Mission(MISSION_GUARD_AREA);
} else {
delete i;
PlayerPtr->Flag_To_Win();
}
ScenarioInit--;
}
/*
** Generate random survivors from the destroyed building.
*/
cell = Coord_Cell(Coord);
offset = Occupy_List();
int odds = 2;
if (Target_Legal(WhomToRepay)) odds -= 1;
if (IsCaptured) odds += 6;
while (*offset != REFRESH_EOL) {
CELL newcell;
newcell = cell + *offset++;
/*
** Infantry could run out of a destroyed building.
*/
if (!House->IsToDie && !IsSurvivorless) {
InfantryClass * i = NULL;
if (Random_Pick(0, odds) == 1) {
i = new InfantryClass(Crew_Type(), House->Class->House);
if (i) {
if (Class->Get_Buildup_Data() != NULL && i->Class->IsNominal) i->IsTechnician = true;
ScenarioInit++;
if (i->Unlimbo(Cell_Coord(newcell), DIR_N)) {
i->Strength = Random_Pick(5, (int)i->Class->MaxStrength);
i->Scatter(0, true);
if (source != TARGET_NONE && !House->Is_Ally(As_Object(source))) {
i->Assign_Mission(MISSION_ATTACK);
i->Assign_Target(source);
} else {
if (House->IsHuman) {
i->Assign_Mission(MISSION_GUARD);
} else {
i->Assign_Mission(MISSION_HUNT);
}
}
} else {
delete i;
}
ScenarioInit--;
}
}
}
/*
** Possibly add some smoke rising from the ashes of the building.
*/
switch (Random_Pick(0, 5)) {
case 0:
case 1:
case 2:
new AnimClass(ANIM_SMOKE_M, Coord_Scatter(Cell_Coord(newcell), 0x0050, false), Random_Pick(0, 5), Random_Pick(1, 2));
break;
default:
break;
}
/*
** The building always scars the ground in some fashion.
*/
if (Random_Pick(0, 3) == 0) {
new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(newcell));
} else {
new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Coord_Scatter(Cell_Coord(newcell), 0x0080, false));
}
}
}
/***********************************************************************************************
* BuildingClass::Active_Click_With -- Handles clicking on the map while the building is selected.*
* *
* This interface routine handles when the player clicks on the map while this building *
* is currently selected. This is used to assign an override target to a turret or *
* guard tower. *
* *
* INPUT: target -- The target that was clicked upon. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/28/1994 JLB : Created. *
*=============================================================================================*/
void BuildingClass::Active_Click_With(ActionType action, ObjectClass * object)
{
Validate();
if (action == ACTION_ATTACK) {
Player_Assign_Mission(MISSION_ATTACK, object->As_Target());
}
if (action == ACTION_TOGGLE_PRIMARY && Class->IsFactory) {
OutList.Add(EventClass(EventClass::PRIMARY, As_Target()));
}
}
/***********************************************************************************************
* BuildingClass::Active_Click_With -- Handles cell selection for buildings. *
* *
* This routine really only serves one purpose -- to allow targeting of the ground for *
* buildings that are euipped with weapons. *
* *
* INPUT: action -- The requested action to perform. *
* *
* cell -- The cell location to perform the action upon. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/04/1995 JLB : Created. *
*=============================================================================================*/
void BuildingClass::Active_Click_With(ActionType action, CELL cell)
{
Validate();
if (action == ACTION_ATTACK) {
Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell));
}
if (action == ACTION_MOVE && *this == STRUCT_CONST) {
OutList.Add(EventClass(EventClass::ARCHIVE, As_Target(), ::As_Target(cell)));
OutList.Add(EventClass(EventClass::SELL, As_Target()));
COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y());
OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord, 1 << PlayerPtr->Class->House));
}
}
/***********************************************************************************************
* BuildingClass::Assign_Target -- Assigns a target to the building. *
* *
* Assigning of a target to a building makes sense if the building is one that can attack. *
* This routine would be used to assign the attack target to a turret or guard tower. *
* *
* INPUT: target -- The target that was clicked on while this building was selected. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/28/1994 JLB : Created. *
* 11/02/1994 JLB : Checks for range before assigning target. *
*=============================================================================================*/
void BuildingClass::Assign_Target(TARGET target)
{
Validate();
if (*this != STRUCT_SAM && !In_Range(target, 0)) {
target = TARGET_NONE;
}
TechnoClass::Assign_Target(target);
}
/***********************************************************************************************
* BuildingClass::Init -- Initialize the building system to an empty null state. *
* *
* This routine initializes the building system in preparation for a scenario load. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
*=============================================================================================*/
void BuildingClass::Init(void)
{
BuildingClass *ptr;
Buildings.Free_All();
ptr = new BuildingClass();
VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0];
delete ptr;
}
/***********************************************************************************************
* BuildingClass::Exit_Object -- Initiates an object to leave the building. *
* *
* This function is used to cause an object to exit the building. It is called when a *
* factory produces a vehicle or other mobile object and that object needs to exit the *
* building to join the ranks of a regular unit. Typically, the object is placed down on *
* the map such that it overlaps the building and then it is given a movement order so that *
* it will move to an adjacent free cell. *
* *
* INPUT: base -- Pointer to the object that is to exit the building. *
* *
* OUTPUT: Returns the success rating for the exit attempt; *
* 0 = complete failure (refund money please) *
* 1 = temporarily prevented (try again later please) *
* 2 = successful *
* *
* WARNINGS: The building is placed in radio contact with the object. The object is in a *
* teathered condition. This condition will be automatically broken when the *
* object reaches the adjacent square. *
* *
* HISTORY: *
* 11/28/1994 JLB : Created. *
* 04/10/1995 JLB : Handles building production by computer. *
* 06/17/1995 JLB : Handles refinery exit. *
*=============================================================================================*/
int BuildingClass::Exit_Object(TechnoClass * base)
{
Validate();
if (!base) return(0);
TechnoTypeClass const * ttype = (TechnoTypeClass const *)&base->Class_Of();
/*
** A unit exiting a building is always considered to be "locked". That means, it
** will be considered as to have legally entered the visible map domain.
*/
base->IsLocked = true;
/*
** Find a good cell to unload the object to. The object, probably a vehicle
** will drive/walk to the adjacent free cell.
*/
switch (base->What_Am_I()) {
CELL cell;
case RTTI_AIRCRAFT:
if (!In_Radio_Contact()) {
AircraftClass *air = (AircraftClass *)base;
air->Altitude = 0;
ScenarioInit++;
if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) {
Transmit_Message(RADIO_HELLO, air);
Transmit_Message(RADIO_TETHER);
ScenarioInit--;
return(2);
}
ScenarioInit--;
} else {
AircraftClass *air = (AircraftClass *)base;
if (Cell_X(Coord_Cell(Center_Coord())) - Map.MapCellX < Map.MapCellWidth/2) {
cell = XY_Cell(Map.MapCellX-1, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY);
} else {
cell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY);
}
ScenarioInit++;
if (air->Unlimbo(Cell_Coord(cell), DIR_N)) {
air->Assign_Destination(::As_Target(Nearby_Location(air)));
air->Assign_Mission(MISSION_MOVE);
ScenarioInit--;
return(2);
}
ScenarioInit--;
}
break;
case RTTI_INFANTRY:
case RTTI_UNIT:
switch (Class->Type) {
case STRUCT_REFINERY:
if (base->What_Am_I() == RTTI_UNIT) {
CELL cell = Coord_Cell(Center_Coord());
UnitClass * unit = (UnitClass *)base;
cell = Adjacent_Cell(cell, FACING_SW);
ScenarioInit++;
if (unit->Unlimbo(Coord_Add(unit->Coord, 0x00550060L), DIR_SW_X2)) {
unit->PrimaryFacing = DIR_SW_X2;
Transmit_Message(RADIO_HELLO, unit);
Transmit_Message(RADIO_TETHER);
unit->Assign_Mission(MISSION_HARVEST);
unit->Force_Track(DriveClass::OUT_OF_REFINERY, Cell_Coord(cell));
unit->Set_Speed(128);
}
ScenarioInit--;
} else {
base->Scatter(true);
}
break;
case STRUCT_AIRSTRIP:
if (Create_Special_Reinforcement(House, &AircraftTypeClass::As_Reference(AIRCRAFT_CARGO), ttype, TMISSION_UNLOAD, As_Target())) {
delete base;
return(2);
}
return(0);
case STRUCT_WEAP:
ScenarioInit++;
if (base->Unlimbo(Coord_Add(Coord, Class->ExitPoint), DIR_SW)) {
// base->Assign_Mission(MISSION_MOVE);
// base->Assign_Destination(::As_Target(As_Cell(Coord)+MAP_CELL_W*2));
base->Mark(MARK_UP);
base->Coord = Coord_Add(Coord, Class->ExitPoint);
base->Mark(MARK_DOWN);
Transmit_Message(RADIO_HELLO, base);
Transmit_Message(RADIO_TETHER);
Assign_Mission(MISSION_UNLOAD);
ScenarioInit--;
return(2);
}
ScenarioInit--;
break;
case STRUCT_BARRACKS:
case STRUCT_HAND:
CELL cell;
bool found = false;
cell = Find_Exit_Cell(base);
if (cell) found = true;
#ifdef OBSOLETE
CELL const *ptr;
bool found = false;
ptr = Class->ExitList;
while (*ptr != REFRESH_EOL) {
cell = Coord_Cell(Coord) + *ptr++;
if (base->Can_Enter_Cell(cell) == MOVE_OK) {
found = true;
break;
}
}
#endif
if (found) {
DirType dir = Direction(cell);
COORDINATE start = Coord_Add(Coord, Class->ExitPoint);
ScenarioInit++;
if (base->Unlimbo(start, dir)) {
base->Assign_Mission(MISSION_MOVE);
base->Assign_Destination(::As_Target(cell));
/*
** Establish radio contact so unload coordination can occur. This
** radio contact should always succeed.
*/
if (Transmit_Message(RADIO_HELLO, base) == RADIO_ROGER) {
Transmit_Message(RADIO_UNLOAD);
}
ScenarioInit--;
return(2);
}
ScenarioInit--;
}
break;
}
break;
case RTTI_BUILDING:
if (!House->IsHuman) {
/*
** Find the next available spot to place this newly created building. If the
** building could be placed at the desired location, fine. If not, then this
** routine will return failure. The calling routine will probably abandon this
** building in preference to building another.
*/
BaseNodeClass * node = Base.Next_Buildable(((BuildingClass *)base)->Class->Type);
// Replaced with RA AI functioality. ST - 7/25/2019 4:14PM
#ifndef USE_RA_AI
if (node) {
if (Flush_For_Placement(base, Coord_Cell(node->Coord))) {
return(1);
}
if (base->Unlimbo(node->Coord)) {
return(2);
}
}
#else
if (GameToPlay == GAME_NORMAL) {
if (node) {
if (Flush_For_Placement(base, Coord_Cell(node->Coord))) {
return(1);
}
if (base->Unlimbo(node->Coord)) {
return(2);
}
}
} else {
COORDINATE coord = 0;
if (node) {
coord = node->Coord;
} else {
/*
** Find a suitable new spot to place.
*/
coord = House->Find_Build_Location((BuildingClass *)base);
}
if (coord) {
if (Flush_For_Placement(base, Coord_Cell(coord))) {
return(1);
}
if (base->Unlimbo(coord)) {
if (node && ((BuildingClass *)base)->Class->Type == House->BuildStructure) {
House->BuildStructure = STRUCT_NONE;
}
return(2);
}
}
}
#endif
}
break;
}
/*
** Failure to exit the object results in a false return value.
*/
return(0);
}
/***********************************************************************************************
* BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. *
* *
* This routine will tell the sidebar of objects that can be built. The function is called *
* whenever a building matures. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/11/1994 JLB : Created. *
* 12/23/1994 JLB : Only updates for PLAYER buildings. *
*=============================================================================================*/
void BuildingClass::Update_Buildables(void)
{
Validate();
/*
** Only do this for real human players. ST - 3/22/2019 1:38PM
*/
if (House != PlayerPtr) {
if (GameToPlay != GAME_GLYPHX_MULTIPLAYER || House->IsHuman == false) {
return;
}
}
bool buildable_via_capture = (IsCaptured && ActLike != House->ActLike) ? true : false;
if (!IsInLimbo && Is_Discovered_By_Player()) {
switch (Class->ToBuild) {
StructType i;
UnitType u;
InfantryType f;
AircraftType a;
case RTTI_BUILDINGTYPE:
for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) {
if (PlayerPtr->Can_Build(i, ActLike)) {
// if (BuildingTypeClass::As_Reference(i).Who_Can_Build_Me(true, true, ActLike)) {
if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) {
Sidebar_Glyphx_Add(RTTI_BUILDINGTYPE, i, House, buildable_via_capture);
} else {
Map.Add(RTTI_BUILDINGTYPE, i, buildable_via_capture);
}
// }
}
}
break;
case RTTI_UNITTYPE:
for (u = UNIT_FIRST; u < UNIT_COUNT; u++) {
if (PlayerPtr->Can_Build(u, ActLike)) {
// if (UnitTypeClass::As_Reference(u).Who_Can_Build_Me(true, true, ActLike)) {
if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) {
Sidebar_Glyphx_Add(RTTI_UNITTYPE, u, House, buildable_via_capture);
} else {
Map.Add(RTTI_UNITTYPE, u, buildable_via_capture);
}
// }
}
}
break;
case RTTI_INFANTRYTYPE:
for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) {
if (PlayerPtr->Can_Build(f, ActLike)) {
// if (InfantryTypeClass::As_Reference(f).Who_Can_Build_Me(true, true, ActLike)) {
if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) {
Sidebar_Glyphx_Add(RTTI_INFANTRYTYPE, f, House, buildable_via_capture);
} else {
Map.Add(RTTI_INFANTRYTYPE, f, buildable_via_capture);
}
// }
}
}
break;
case RTTI_AIRCRAFTTYPE:
for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) {
if (PlayerPtr->Can_Build(a, ActLike)) {
// if (AircraftTypeClass::As_Reference(a).Who_Can_Build_Me(true, true, ActLike)) {
if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) {
Sidebar_Glyphx_Add(RTTI_AIRCRAFTTYPE, a, House, buildable_via_capture);
} else {
Map.Add(RTTI_AIRCRAFTTYPE, a, buildable_via_capture);
}
// }
}
}
break;
}
}
}
#if (0) //ST - 3/22/2019 1:33PM
/***********************************************************************************************
* BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. *
* *
* This routine will tell the sidebar of objects that can be built. The function is called *
* whenever a building matures. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/11/1994 JLB : Created. *
* 12/23/1994 JLB : Only updates for PLAYER buildings. *
*=============================================================================================*/
void BuildingClass::Update_Buildables(void)
{
Validate();
if (House == PlayerPtr && !IsInLimbo && IsDiscoveredByPlayer) {
switch (Class->ToBuild) {
StructType i;
UnitType u;
InfantryType f;
AircraftType a;
case RTTI_BUILDINGTYPE:
for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) {
if (PlayerPtr->Can_Build(i, ActLike)) {
// if (BuildingTypeClass::As_Reference(i).Who_Can_Build_Me(true, true, ActLike)) {
Map.Add(RTTI_BUILDINGTYPE, i);
// }
}
}
break;
case RTTI_UNITTYPE:
for (u = UNIT_FIRST; u < UNIT_COUNT; u++) {
if (PlayerPtr->Can_Build(u, ActLike)) {
// if (UnitTypeClass::As_Reference(u).Who_Can_Build_Me(true, true, ActLike)) {
Map.Add(RTTI_UNITTYPE, u);
// }
}
}
break;
case RTTI_INFANTRYTYPE:
for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) {
if (PlayerPtr->Can_Build(f, ActLike)) {
// if (InfantryTypeClass::As_Reference(f).Who_Can_Build_Me(true, true, ActLike)) {
Map.Add(RTTI_INFANTRYTYPE, f);
// }
}
}
break;
case RTTI_AIRCRAFTTYPE:
for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) {
if (PlayerPtr->Can_Build(a, ActLike)) {
// if (AircraftTypeClass::As_Reference(a).Who_Can_Build_Me(true, true, ActLike)) {
Map.Add(RTTI_AIRCRAFTTYPE, a);
// }
}
}
break;
}
}
}
#endif
/***********************************************************************************************
* BuildingClass::Fire_Out -- Handles when attached animation expires. *
* *
* This routine is used to perform any fixups necessary when the attached animation has *
* terminated. This occurs when the fire & smoke animation that a SAM site produces stops. *
* At that point, normal reload procedures can commence. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/30/1994 JLB : Created. *
*=============================================================================================*/
void BuildingClass::Fire_Out(void)
{
Validate();
// SAM = SAM_READY;
// IsFiring = false;
}
/***********************************************************************************************
* BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. *
* *
* This routine will handle the power adjustments for the associated house when the *
* building goes into limbo. This means that its power drain or production is subtracted *
* from the house accumulated totals. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Was the building limboed? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 12/24/1994 JLB : Created. *
*=============================================================================================*/
bool BuildingClass::Limbo(void)
{
Validate();
// HouseClass *housep;
// RTTIType bld_type;
if (!IsInLimbo) {
/*
** Update the total factory type, assuming this building has a factory.
*/
switch (Class->ToBuild) {
case RTTI_AIRCRAFTTYPE:
House->AircraftFactories--;
break;
case RTTI_INFANTRYTYPE:
House->InfantryFactories--;
break;
case RTTI_UNITTYPE:
House->UnitFactories--;
break;
case RTTI_BUILDINGTYPE:
House->BuildingFactories--;
break;
default:
break;
}
House->IsRecalcNeeded = true;
#ifdef USE_RA_AI
//
// Added for RA AI in TD. ST - 7/26/2019 9:25AM
//
House->Recalc_Center();
#endif
/*
** Update the power status of the owner's house.
*/
House->Adjust_Power(-Power_Output());
House->Adjust_Drain(-Class->Drain);
House->Adjust_Capacity(-(int)Class->Capacity, true);
if (House == PlayerPtr) {
Map.PowerClass::IsToRedraw = true;
Map.Flag_To_Redraw(false);
}
#ifdef OBSOLETE
/*
** If this building is building something, the FactoryClass doing the
** building must be deleted, as well as the object being built.
** - If this building is controlled by Computer AI, this building's Factory
** pointer will point to the factory doing the building.
** - Otherwise, the owner House's Factory indices will indicate what's
** being built. (The player's sidebar also contains indices to indicate
** what's being built, but those just echo the house's indices.)
*/
if (Factory) {
Factory->Abandon();
delete Factory;
Factory = 0;
}
/*
** If the owner HouseClass is building something, and this building can
** build that thing, we may be the last building for that house that can
** build that thing; if so, abandon production of it.
*/
housep = HouseClass::As_Pointer(Owner());
if (housep) {
FactoryClass * factory = 0;
bld_type = RTTI_NONE;
if (housep->AircraftFactory != -1 && Class->ToBuild == RTTI_AIRCRAFTTYPE) {
bld_type = RTTI_AIRCRAFTTYPE;
factory = Factories.Raw_Ptr(housep->AircraftFactory);
}
if (housep->InfantryFactory != -1 && Class->ToBuild==RTTI_INFANTRYTYPE) {
bld_type = RTTI_INFANTRYTYPE;
factory = Factories.Raw_Ptr(housep->InfantryFactory);
}
if (housep->UnitFactory != -1 && Class->ToBuild==RTTI_UNITTYPE) {
bld_type = RTTI_UNITTYPE;
factory = Factories.Raw_Ptr(housep->UnitFactory);
}
if (housep->BuildingFactory != -1 && Class->ToBuild==RTTI_BUILDINGTYPE) {
bld_type = RTTI_BUILDINGTYPE;
factory = Factories.Raw_Ptr(housep->BuildingFactory);
}
if (housep->SpecialFactory != -1 && Class->ToBuild==RTTI_SPECIAL) {
bld_type = RTTI_SPECIAL;
}
if (bld_type != RTTI_NONE) {
if (factory) {
TechnoClass * object = factory->Get_Object();
IsInLimbo = true;
if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, housep->Class->House)) {
housep->Abandon_Production(bld_type);
}
IsInLimbo = false;
}
}
}
#endif
/*
** This could be a building that builds. If so, then the sidebar may need adjustment.
** Set IsInLimbo to true to "fool" the sidebar into knowing that this building
** isn't available. Set it back to false so the rest of the Limbo code works.
** Otherwise, the sidebar won't properly remove non-available buildables.
*/
if (IsOwnedByPlayer && !ScenarioInit) {
IsInLimbo = true;
Map.Recalc();
IsInLimbo = false;
}
#ifdef NEVER
if (!House->IsHuman) {
Update_Specials();
}
#endif
}
return(TechnoClass::Limbo());
}
/***********************************************************************************************
* BuildingClass::Fire_Coord -- Calculates the coordinate that projectile would appear. *
* *
* This routine is used to determine where a projectile would appear if this building *
* were to fire. The location usually depends on the current rotation setting of the *
* turret or else on the direction of the target that will be fired upon. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with coordinate that the projectile should appear at if the building *
* were to fire. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 12/24/1994 JLB : Created. *
*=============================================================================================*/
FireDataType BuildingClass::Fire_Data(int ) const
{
Validate();
COORDINATE coord = Center_Coord();
int dist = 0;
/*
** Make adjustments to the firing coordinate to account for turret
** position. This depends on building type and turret facing.
*/
switch (Class->Type) {
default:
case STRUCT_GTOWER:
case STRUCT_ATOWER:
coord = Coord_Move(coord, DIR_N, 0x0030);
dist = 0x0040;
break;
case STRUCT_OBELISK:
coord = Coord_Move(coord, DIR_N, 0x00A8);
coord = Coord_Move(coord, DIR_W, 0x0018);
break;
case STRUCT_SAM:
case STRUCT_TURRET: