Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements naval factory and unit support. #795

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
147 changes: 147 additions & 0 deletions src/extensions/building/buildingext_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "rules.h"
#include "voc.h"
#include "iomap.h"
#include "target.h"
#include "spritecollection.h"
#include "fatal.h"
#include "asserthandler.h"
Expand All @@ -55,6 +56,149 @@
#include "hooker_macros.h"


/**
* #issue-531
*
* Buildings with IsNavalYard will be assigned an initial random rally point at
* least two cells aways from the building occupation footprint.
*
* @author: CCHyper
*/
static Cell NearbyCell;
static void Building_Get_Nearby_Location_MZONE_AMPHIBIOUS_DESTROYER(BuildingClass *this_ptr) { NearbyCell = Map.Nearby_Location(this_ptr->Get_Cell(), SPEED_FLOAT, -1, MZONE_AMPHIBIOUS_DESTROYER, false, 5, 5); }
DECLARE_PATCH(_BuildingClass_Grand_Opening_NavalYard_Initial_Rally_Point_Patch)
{
GET_REGISTER_STATIC(BuildingClass *, this_ptr, esi);
static BuildingTypeClassExtension *buildingtypeext;
static BuildingTypeClass *buildingtype;

buildingtype = this_ptr->Class;

if (buildingtype->IsWeaponsFactory) {

/**
* Find the type class extension instance.
*/
buildingtypeext = BuildingTypeClassExtensions.find(buildingtype);
if (buildingtypeext && buildingtypeext->IsNavalYard) {
if (Target_Legal(this_ptr->ArchiveTarget)) {
Building_Get_Nearby_Location_MZONE_AMPHIBIOUS_DESTROYER(this_ptr);
NearbyCell.X += 2;
NearbyCell.Y += 2;
if (NearbyCell) {
this_ptr->Assign_Rally_Point(NearbyCell);
}
}
}
}

/**
* Stolen bytes/code.
*/
_asm { mov eax, [esi+0x0EC] } // this->House
_asm { mov byte ptr [eax+0x0D2], 1 } // House->IsRecalcNeeded = true

JMP(0x0042E4D4);
}


/**
* #issue-531
*
* This patch allows buildings with the IsNavalYard property to be assigned
* as "Primary" seperate from other UnitType factories.
*
* @author: CCHyper
*/
DECLARE_PATCH(_BuildingClass_Toggle_Primary_NavalYard_Check_Patch)
{
GET_REGISTER_STATIC(BuildingClass *, this_ptr, esi);
GET_REGISTER_STATIC(BuildingClass *, building, eax);
static BuildingTypeClassExtension *this_buildingtypeext;
static BuildingTypeClassExtension *i_buildingtypeext;

/**
* Find the type class extension instance.
*/
this_buildingtypeext = BuildingTypeClassExtensions.find(this_ptr->Class);
i_buildingtypeext = BuildingTypeClassExtensions.find(building->Class);

/**
* If we found another naval yard, remove its leadership status so we can
* assign it to ourself.
*/
if (this_buildingtypeext && i_buildingtypeext
&& this_buildingtypeext->IsNavalYard && i_buildingtypeext->IsNavalYard) {

building->IsLeader = false;

} else {
building->IsLeader = false;
}

continue_loop:
JMP(0x0042F61A);
}


/**
* #issue-531
*
* This patch allows buildings with the IsNavalYard property to place rally
* points on water rather than on land.
*
* @author: CCHyper
*/
DECLARE_PATCH(_BuildingClass_Set_Rally_To_Point_NavalYard_Check_Patch)
{
GET_REGISTER_STATIC(BuildingClass *, this_ptr, esi);
GET_REGISTER_STATIC(SpeedType, speed, ebx);
GET_REGISTER_STATIC(MZoneType, mzone, edi);
static BuildingTypeClassExtension *buildingtypeext;
static BuildingTypeClass *buildingtype;

buildingtype = this_ptr->Class;

/**
* Stolen bytes/code.
*/
if (buildingtype->ToBuild == RTTI_AIRCRAFTTYPE) {
speed = SPEED_WINGED;
mzone = MZONE_FLYER;

} else {

/**
* Find the type class extension instance.
*/
buildingtypeext = BuildingTypeClassExtensions.find(buildingtype);
if (buildingtypeext) {

/**
* If this is a factory that produces units, but is flagged as a shipyard, then
* change the zone flags to scan for water regions only.
*/
if (buildingtype->ToBuild == RTTI_UNITTYPE && buildingtypeext->IsNavalYard) {
speed = SPEED_AMPHIBIOUS;
mzone = MZONE_AMPHIBIOUS_DESTROYER;
}

}

}

/**
* Assign the zone flags to the expected registers.
*/
_asm { mov eax, speed }
_asm { mov dword ptr [esp+0x14], eax }

_asm { mov edi, mzone }

JMP(0x0042C38E);
}


/**
* #issue-26
*
Expand Down Expand Up @@ -493,4 +637,7 @@ void BuildingClassExtension_Hooks()
Patch_Jump(0x00429A96, &_BuildingClass_AI_ProduceCash_Patch);
Patch_Jump(0x0042F67D, &_BuildingClass_Captured_ProduceCash_Patch);
Patch_Jump(0x0042E179, &_BuildingClass_Grand_Opening_ProduceCash_Patch);
Patch_Jump(0x0042C37F, &_BuildingClass_Set_Rally_To_Point_NavalYard_Check_Patch);
Patch_Jump(0x0042F614, &_BuildingClass_Toggle_Primary_NavalYard_Check_Patch);
Patch_Jump(0x0042E4C7, &_BuildingClass_Grand_Opening_NavalYard_Initial_Rally_Point_Patch);
}
6 changes: 5 additions & 1 deletion src/extensions/buildingtype/buildingtypeext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ BuildingTypeClassExtension::BuildingTypeClassExtension(BuildingTypeClass *this_p
ProduceCashBudget(0),
IsStartupCashOneTime(false),
IsResetBudgetOnCapture(false),
IsEligibleForAllyBuilding(false)
IsEligibleForAllyBuilding(false),
IsNavalYard(false)
{
ASSERT(ThisPtr != nullptr);
//EXT_DEBUG_TRACE("BuildingTypeClassExtension constructor - Name: %s (0x%08X)\n", ThisPtr->Name(), (uintptr_t)(ThisPtr));
Expand Down Expand Up @@ -167,6 +168,7 @@ void BuildingTypeClassExtension::Compute_CRC(WWCRCEngine &crc) const
//EXT_DEBUG_TRACE("BuildingTypeClassExtension::Compute_CRC - Name: %s (0x%08X)\n", ThisPtr->Name(), (uintptr_t)(ThisPtr));

crc(IsEligibleForAllyBuilding);
crc(IsNavalYard);
}


Expand Down Expand Up @@ -200,5 +202,7 @@ bool BuildingTypeClassExtension::Read_INI(CCINIClass &ini)
IsEligibleForAllyBuilding = ini.Get_Bool(ini_name, "EligibleForAllyBuilding",
ThisPtr->IsConstructionYard ? true : IsEligibleForAllyBuilding);

IsNavalYard = ini.Get_Bool(ini_name, "Shipyard", IsNavalYard);

return true;
}
11 changes: 10 additions & 1 deletion src/extensions/buildingtype/buildingtypeext.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class BuildingTypeClassExtension final : public Extension<BuildingTypeClass>
unsigned ProduceCashBudget;

/**
* Is the capture bonus a "one one" special (further captures will not get the bonus)?
* Is the capture bonus a "one off" special (further captures will not get the bonus)?
*/
bool IsStartupCashOneTime;

Expand All @@ -96,6 +96,15 @@ class BuildingTypeClassExtension final : public Extension<BuildingTypeClass>
* Is this building eligible for proximity checks by players who are its owner's allies?
*/
bool IsEligibleForAllyBuilding;

/**
* Is this building a factory that is placed on water and produces vessels (i.e. ships)?
*
* #NOTE: This is a temporary property that is used to enforce special rules to
* allow better naval and shipyard support until a full Vessel class is hooked
* up and implemented.
*/
bool IsNavalYard;
};


Expand Down
106 changes: 106 additions & 0 deletions src/extensions/house/houseext_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
#include "housetype.h"
#include "technotype.h"
#include "super.h"
#include "building.h"
#include "buildingtype.h"
#include "buildingtypeext.h"
#include "rules.h"
#include "rulesext.h"
#include "iomap.h"
#include "fatal.h"
#include "debughandler.h"
#include "asserthandler.h"
Expand All @@ -41,6 +47,102 @@
#include "hooker_macros.h"


/**
* A fake class for implementing new member functions which allow
* access to the "this" pointer of the intended class.
*
* @note: This must not contain a constructor or destructor!
* @note: All functions must be prefixed with "_" to prevent accidental virtualization.
*/
class HouseClassExt final : public HouseClass
{
public:
Cell _Find_Build_Location(BuildingTypeClass *buildingtype, int (__fastcall *callback)(int, Cell &, int, int), int a3 = -1);
};


/**
* #issue-531
*
* Interception of Find_Build_Location. This allows us to find a suitable building
* location for the specific buildings, such as the Naval Yard.
*
* @author: CCHyper
*/
Cell HouseClassExt::_Find_Build_Location(BuildingTypeClass *buildingtype, int (__fastcall *callback)(int, Cell &, int, int), int a3)
{
/**
* Find the type class extension instance.
*/
BuildingTypeClassExtension *buildingtypeext = BuildingTypeClassExtensions.find(buildingtype);
if (buildingtypeext && buildingtypeext->IsNavalYard) {

if (RulesExtension) {

DEV_DEBUG_INFO("Find_Build_Location(%s): Searching for Naval Yard \"%s\" build location...\n", Name(), buildingtype->Name());

Cell cell(0,0);

//BuildingTypeClass *desired_navalyard = Get_First_Ownable(RulesExtension->BuildNavalYard);
//ASSERT_PRINT(desired_navalyard != nullptr, "Failed to find a ownable building in BuildNavalYard list!");

/**
* Get the cell footprint for the Naval Yard, then add a safety margin of 2.
*/
//int area_w = desired_navalyard->Width() + 2;
//int area_h = desired_navalyard->Height() + 2;
int area_w = buildingtype->Width() + 2;
int area_h = buildingtype->Height() + 2;

/**
* find a nearby location from the center of the base that fits our naval yard.
*/
Cell found_cell = Map.Nearby_Location(Coord_Cell(Center), SPEED_FLOAT, -1, MZONE_NORMAL, 0, area_w, area_h);
if (found_cell) {

DEV_DEBUG_INFO("Find_Build_Location(%s): Found possible Naval Yard location at %d,%d...\n", Name(), found_cell.X, found_cell.Y);

/**
* Iterate over all owned construction yards and find the first that is closest to our cell.
*/
for (int i = 0; i < ConstructionYards.Count(); ++i) {
BuildingClass *conyard = ConstructionYards[i];
if (conyard) {

Coordinate conyard_coord = conyard->Center_Coord();
Coordinate found_coord = Map[found_cell].Center_Coord();

/**
* Is this location close enough to the construction yard for us to use?
*/
if (Distance(conyard_coord, found_coord) <= Cell_To_Lepton(RulesExtension->AINavalYardAdjacency)) {
DEV_DEBUG_INFO("Find_Build_Location(%s): Using location %d,%d for Naval Yard.\n", Name(), found_cell.X, found_cell.Y);
cell = found_cell;
break;
}

}

}

}

if (!cell) {
DEV_DEBUG_WARNING("Find_Build_Location(%s): Failed to find suitable location for \"%s\"!\n", Name(), buildingtype->Name());
}

return cell;
}

}

/**
* Call the original function to find a location for land buildings.
*/
return HouseClass::Find_Build_Location(buildingtype, callback, a3);
}


/**
* Patch for InstantSuperRechargeCommandClass
*
Expand Down Expand Up @@ -167,4 +269,8 @@ void HouseClassExtension_Hooks()

Patch_Jump(0x004BBD26, &_HouseClass_Can_Build_BuildCheat_Patch);
Patch_Jump(0x004BD30B, &_HouseClass_Super_Weapon_Handler_InstantRecharge_Patch);

Patch_Call(0x0042D460, &HouseClassExt::_Find_Build_Location);
Patch_Call(0x0042D53C, &HouseClassExt::_Find_Build_Location);
Patch_Call(0x004C8104, &HouseClassExt::_Find_Build_Location);
}
Loading