Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ class DeliverPayloadData
Coord3D m_dropVariance;
UnsignedInt m_dropDelay;
Bool m_fireWeapon;

UnsignedInt m_fireWeaponSlots;

Bool m_selfDestructObject;
Int m_visibleNumBones; ///< The number of visible bones to process.
Real m_diveStartDistance;
Expand All @@ -278,6 +281,7 @@ class DeliverPayloadData
m_dropVariance.zero();
m_dropDelay = 0;
m_fireWeapon = false;
m_fireWeaponSlots = 1u << PRIMARY_WEAPON;
m_visibleNumBones = 0;
m_diveStartDistance = 0.0f;
m_diveEndDistance = 0.0f;
Expand Down Expand Up @@ -326,14 +330,15 @@ class DeliverPayloadAIUpdate : public AIUpdateInterface
const Coord3D& getDropOffset() const { return m_data.m_dropOffset; }
const Coord3D& getDropVariance() const { return m_data.m_dropVariance; }
Bool isFireWeapon() const { return m_data.m_fireWeapon; }
Bool shouldFireWeaponSlot(WeaponSlotType wslot) const { return (m_data.m_fireWeaponSlots & (1 << wslot)) != 0; }
Int getVisibleItemsDelivered() const { return m_visibleItemsDelivered; }
void setVisibleItemsDelivered( Int num ) { m_visibleItemsDelivered = num; }

Bool isCloseEnoughToTarget();
Bool isOffMap() const;
Real calcMinTurnRadius(Real* timeToTravelThatDist) const;

void deliverPayload( const Coord3D *moveToPos, const Coord3D *targetPos, const DeliverPayloadData *data );
void deliverPayload( const Coord3D *moveToPos, const Coord3D *targetPos, const DeliverPayloadData *data, const Coord3D *decalOffset = nullptr );
void deliverPayloadViaModuleData( const Coord3D *moveToPos );

const DeliverPayloadData* getData() { return &m_data; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class OCLUpdateModuleData : public UpdateModuleData
Bool m_isCreateAtEdge; ///< Otherwise, it is created on top of myself
Bool m_isFactionTriggered; ///< Faction has to be present before update will happen

Bool m_isDirectionalDelivery; ///< Deliver payload aligned to source object
Bool m_isDirectionalDeliveryFurthestEdge; ///< always get the furthest edge matching angle

OCLUpdateModuleData();

static void buildFieldParse(MultiIniFieldParse& p);
Expand Down
4 changes: 4 additions & 0 deletions GeneralsMD/Code/GameEngine/Include/GameLogic/TerrainLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ class TerrainLogic : public Snapshot,
virtual void getMaximumPathfindExtent( Region3D *extent ) const { DEBUG_CRASH(("not implemented")); } ///< @todo This should not be a stub - this should own this functionality
virtual Coord3D findClosestEdgePoint( const Coord3D *closestTo ) const ;
virtual Coord3D findFarthestEdgePoint( const Coord3D *farthestFrom ) const ;

virtual Coord3D findEdgePointForAngle(const Coord3D* pos, Real angle, bool farthest = FALSE, bool closest = FALSE) const;


virtual Bool isClearLineOfSight(const Coord3D& pos, const Coord3D& posOther) const;

virtual AsciiString getSourceFilename( void ) { return m_filenameString; }
Expand Down
110 changes: 110 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2169,7 +2169,117 @@ Coord3D TerrainLogic::findFarthestEdgePoint( const Coord3D *farthestFrom ) const
}


//-------------------------------------------------------------------------------------------------
/** Returns the ground aligned point on the bounding box closest to the given point*/
//-------------------------------------------------------------------------------------------------
Coord3D TerrainLogic::findEdgePointForAngle(const Coord3D* pos, Real angle, bool farthest/* = FALSE*/, bool closest/* = FALSE*/) const
{
Region3D mapExtent;
getExtent(&mapExtent);

// Map bounds (XY only)
const Real minX = mapExtent.lo.x;
const Real minY = mapExtent.lo.y;
const Real maxX = mapExtent.hi.x;
const Real maxY = mapExtent.hi.y;

const Real x0 = pos->x;
const Real y0 = pos->y;

// Direction from angle
const Real dx = std::cos(angle);
const Real dy = std::sin(angle);

// Small epsilon for float comparisons
const Real eps = static_cast<Real>(1e-6);

struct Hit
{
Real t;
Real x;
Real y;
};

Hit hits[4];
int hitCount = 0;

auto addHit = [&](Real t)
{
if (t < 0) return; // only forward along the ray

const Real x = x0 + t * dx;
const Real y = y0 + t * dy;

// Keep only points that lie on the rectangle boundary
if (x >= minX - eps && x <= maxX + eps &&
y >= minY - eps && y <= maxY + eps)
{
// Avoid duplicates (corner hits can be found twice)
for (int i = 0; i < hitCount; ++i)
{
if (std::abs(hits[i].x - x) < eps &&
std::abs(hits[i].y - y) < eps)
{
return;
}
}

hits[hitCount++] = { t, x, y };
}
};

// Intersect ray P(t) = (x0,y0) + t(dx,dy), t >= 0
// with the 4 rectangle edges

// x = minX
if (std::abs(dx) > eps)
addHit((minX - x0) / dx);

// x = maxX
if (std::abs(dx) > eps)
addHit((maxX - x0) / dx);

// y = minY
if (std::abs(dy) > eps)
addHit((minY - y0) / dy);

// y = maxY
if (std::abs(dy) > eps)
addHit((maxY - y0) / dy);

// No hit should only happen if pos is invalid or outside map
if (hitCount == 0)
return *pos;

// Default behavior:
// - if closest == true -> nearest hit
// - if farthest == true -> farthest hit
// - otherwise -> nearest forward hit
int best = 0;

if (farthest)
{
for (int i = 1; i < hitCount; ++i)
{
if (hits[i].t > hits[best].t)
best = i;
}
}
else // closest or default
{
for (int i = 1; i < hitCount; ++i)
{
if (hits[i].t < hits[best].t)
best = i;
}
}

Coord3D result = *pos;
result.x = hits[best].x;
result.y = hits[best].y;
// preserve original Z (XY only matters)
return result;
}


//-------------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ class DeliverPayloadNugget : public ObjectCreationNugget
m_payload.clear();
m_putInContainerName.clear();
m_transportName.clear();
m_targetOffset = { 0.0, 0.0 };
}

virtual Object* create(const Object *primaryObj, const Coord3D *primary, const Coord3D *secondary, Real angle, UnsignedInt lifetimeFrames = 0 ) const
Expand All @@ -283,7 +284,9 @@ class DeliverPayloadNugget : public ObjectCreationNugget
//resultant vectors to the initial vectors, we can calculate the delta positions for each plane.
Real CCWx = 0.0f, CCWy = 0.0f, CWx = 0.0f, CWy = 0.0f;

if( m_formationSize > 1 )
Real targetOffsetX = 0.0f, targetOffsetY = 0.0f;

if (m_formationSize > 1 || m_targetOffset.x != 0.0f || m_targetOffset.y != 0.0f)
{
//Get the delta x and y values from the target to the origin.
Real dx = primary->x - secondary->x;
Expand All @@ -296,16 +299,19 @@ class DeliverPayloadNugget : public ObjectCreationNugget
dx /= length;
dy /= length;

targetOffsetX = dx * m_targetOffset.x - dy * m_targetOffset.y;
targetOffsetY = dy * m_targetOffset.x + dx * m_targetOffset.y;

//Rotate 90 degrees CCW.
Real radians = 90.0f * PI / 180.0f;
Real s = Sin( radians );
Real c = Cos( radians );
Real s = Sin(radians);
Real c = Cos(radians);
CCWx = dx * c + dy * -s + dx;
CCWy = dx * s + dy * c + dy;

//Rotate 90 degrees CW
s = Sin( -radians );
c = Cos( -radians );
s = Sin(-radians);
c = Cos(-radians);
CWx = dx * c + dy * -s + dx;
CWy = dx * s + dy * c + dy;
}
Expand All @@ -331,6 +337,9 @@ class DeliverPayloadNugget : public ObjectCreationNugget
offset.y = CWy * offsetMultiplier;
}

offset.x += targetOffsetX;
offset.y += targetOffsetY;

Coord3D startPos = *primary;
Coord3D moveToPos = *secondary;
startPos.add( &offset );
Expand Down Expand Up @@ -407,9 +416,9 @@ class DeliverPayloadNugget : public ObjectCreationNugget

static NameKeyType key_DeliverPayloadAIUpdate = NAMEKEY("DeliverPayloadAIUpdate");
DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)transport->findUpdateModule(key_DeliverPayloadAIUpdate);
if( ai )
if (ai)
{
if( m_startAtMaxSpeed && createOwner )
if (m_startAtMaxSpeed && createOwner)
{
PhysicsBehavior* physics = transport->getPhysics();
if (physics)
Expand All @@ -420,15 +429,24 @@ class DeliverPayloadNugget : public ObjectCreationNugget
startingForce.x *= factor;
startingForce.y *= factor;
startingForce.z *= factor;
physics->applyMotiveForce( &startingForce );
physics->applyMotiveForce(&startingForce);
}
}

// only the first guy in each formation gets a delivery decal
DeliverPayloadData data = m_data;
if (formationIndex != 0)
if (formationIndex != 0) {
data.m_deliveryDecalRadius = 0;
ai->deliverPayload( &moveToPos, &targetPos, &data );
}

if (targetOffsetX != 0.0 || targetOffsetY != 0.0) {
Coord3D decalOffset = { -targetOffsetX, -targetOffsetY };
ai->deliverPayload(&moveToPos, &targetPos, &data, &decalOffset);
}
else {
ai->deliverPayload(&moveToPos, &targetPos, &data);
}

if( m_startAtPreferredHeight && createOwner )
{
startPos.z = TheTerrainLogic->getGroundHeight(startPos.x, startPos.y) + ai->getCurLocomotor()->getPreferredHeight();
Expand Down Expand Up @@ -538,6 +556,8 @@ class DeliverPayloadNugget : public ObjectCreationNugget
{ "WeaponErrorRadius", INI::parseReal, nullptr, offsetof( DeliverPayloadNugget, m_errorRadius ) },
{ "DelayDeliveryMax", INI::parseDurationUnsignedInt, nullptr, offsetof( DeliverPayloadNugget, m_delayDeliveryFramesMax ) },

{ "TargetOffset", INI::parseCoord2D, nullptr, offsetof( DeliverPayloadNugget, m_targetOffset ) },

//Payload information (it's all created now and stored inside)
{ "Payload", parsePayload, nullptr, 0 },
{ "PutInContainer", INI::parseAsciiString, nullptr, offsetof( DeliverPayloadNugget, m_putInContainerName) },
Expand Down Expand Up @@ -579,6 +599,8 @@ class DeliverPayloadNugget : public ObjectCreationNugget
Bool m_startAtPreferredHeight;
Bool m_startAtMaxSpeed;

Coord2D m_targetOffset;

//AI specific data passed over to DeliverPayloadAIUpdate::deliver()
DeliverPayloadData m_data;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@


//-------------------------------------------------------------------------------------------------
// duplicate from TurretAI
static void parseTWS(INI* ini, void* /*instance*/, void* store, const void* /*userData*/)
{
UnsignedInt* tws = (UnsignedInt*)store;
const char* token = ini->getNextToken();
while (token != nullptr)
{
WeaponSlotType wslot = (WeaponSlotType)INI::scanIndexList(token, TheWeaponSlotTypeNames);
*tws |= (1 << wslot);
token = ini->getNextTokenOrNull();
}
}
// ---

const FieldParse* DeliverPayloadData::getFieldParse()
{
static const FieldParse dataFieldParse[] =
Expand Down Expand Up @@ -80,6 +94,7 @@ const FieldParse* DeliverPayloadData::getFieldParse()

//Weapon based payload
{ "FireWeapon", INI::parseBool, nullptr, offsetof( DeliverPayloadData, m_fireWeapon ) },
{ "FireWeaponSlots", parseTWS, nullptr, offsetof(DeliverPayloadData, m_fireWeaponSlots) },

//Specify an additional weaponslot to be fired while strafing
{ "DiveStartDistance", INI::parseReal, nullptr, offsetof( DeliverPayloadData, m_diveStartDistance ) },
Expand Down Expand Up @@ -239,7 +254,8 @@ UpdateSleepTime DeliverPayloadAIUpdate::update( void )
void DeliverPayloadAIUpdate::deliverPayload(
const Coord3D *moveToPos,
const Coord3D *targetPos,
const DeliverPayloadData *data
const DeliverPayloadData *data,
const Coord3D *decalOffset /* = NULL*/
)
{

Expand All @@ -255,8 +271,18 @@ void DeliverPayloadAIUpdate::deliverPayload(
m_data = *data;

m_deliveryDecal.clear();
m_data.m_deliveryDecalTemplate.createRadiusDecal(*targetPos,
m_data.m_deliveryDecalRadius, getObject()->getControllingPlayer(), m_deliveryDecal);

if (decalOffset != nullptr) {
Coord3D decalPos = *targetPos;
decalPos.add(decalOffset);
m_data.m_deliveryDecalTemplate.createRadiusDecal(decalPos,
m_data.m_deliveryDecalRadius, getObject()->getControllingPlayer(), m_deliveryDecal);
}
else {
m_data.m_deliveryDecalTemplate.createRadiusDecal(*targetPos,
m_data.m_deliveryDecalRadius, getObject()->getControllingPlayer(), m_deliveryDecal);
}


if( m_data.m_diveStartDistance <= 0.0f )
{
Expand Down Expand Up @@ -434,6 +460,7 @@ void DeliverPayloadAIUpdate::xfer( Xfer *xfer )
xfer->xferCoord3D(&data.m_dropVariance);
xfer->xferUnsignedInt(&data.m_dropDelay);
xfer->xferBool(&data.m_fireWeapon);
xfer->xferUnsignedInt(&data.m_fireWeaponSlots);
xfer->xferBool(&data.m_selfDestructObject);
xfer->xferInt(&data.m_visibleNumBones);
xfer->xferReal(&data.m_diveStartDistance);
Expand Down Expand Up @@ -715,7 +742,17 @@ StateReturnType DeliveringState::update() // Kick a dude out every so often
pos.x += ai->getDropOffset().x;
pos.y += ai->getDropOffset().y;
pos.z += ai->getDropOffset().z;
owner->fireCurrentWeapon( &pos );

for (int i = 0; i < WEAPONSLOT_COUNT; i++) {
WeaponSlotType wslot = (WeaponSlotType)i;
if (ai->shouldFireWeaponSlot(wslot)) {
owner->setWeaponLock(wslot, LOCKED_TEMPORARILY);
owner->fireCurrentWeapon(&pos);
}
}
//owner->fireCurrentWeapon( &pos );


TheGameLogic->destroyObject( item );
}
else
Expand Down
Loading
Loading