Fix some action with big units (TileSize > (1;1))

Fix regression whan AI should attack with transporter.
This commit is contained in:
Joris Dauphin 2011-07-18 12:54:15 +02:00
parent de0530e856
commit 2a313e217b
14 changed files with 275 additions and 305 deletions

View file

@ -500,7 +500,7 @@ void HandleActionBuilt(CUnit &unit)
unit.Data.Built.Worker = NoUnitP;
// HACK: make sure the sight is updated correctly
unit.CurrentSightRange = 1;
DropOutOnSide(*worker, LookingW, type->TileWidth, type->TileHeight);
DropOutOnSide(*worker, LookingW, &unit);
unit.CurrentSightRange = 0;
}
@ -544,7 +544,7 @@ void HandleActionBuilt(CUnit &unit)
worker->SubAction = 0;//may be 40
// HACK: make sure the sight is updated correctly
unit.CurrentSightRange = 1;
DropOutOnSide(*worker, LookingW, type->TileWidth, type->TileHeight);
DropOutOnSide(*worker, LookingW, &unit);
worker->CurrentOrder()->ClearGoal();

View file

@ -55,9 +55,7 @@
*/
void HandleActionDie(CUnit &unit)
{
//
// Show death animation
//
if (unit.Type->Animations && unit.Type->Animations->Death[unit.DamagedType]) {
UnitShowAnimation(unit, unit.Type->Animations->Death[unit.DamagedType]);
}
@ -68,43 +66,41 @@ void HandleActionDie(CUnit &unit)
unit.Anim.Unbreakable = 0;
}
//
if (unit.Anim.Unbreakable) {
return;
}
// Die sequence terminated, generate corpse.
//
if (!unit.Anim.Unbreakable) {
if (!unit.Type->CorpseType) {
// We may be in the cache if we just finished out death animation
// even though there is no corpse.
// (unit.Type->Animations && unit.Type->Animations->Death)
// Remove us from the map to be safe
unit.Remove(NULL);
unit.Release();
return;
}
if (!unit.Type->CorpseType) {
// We may be in the cache if we just finished out death animation
// even though there is no corpse.
// (unit.Type->Animations && unit.Type->Animations->Death)
// Remove us from the map to be safe
unit.Remove(NULL);
unit.Release();
return;
}
Assert(unit.Type->TileWidth == unit.Type->CorpseType->TileWidth &&
unit.Type->TileHeight == unit.Type->CorpseType->TileHeight);
Assert(unit.Type->TileWidth >= unit.Type->CorpseType->TileWidth &&
unit.Type->TileHeight >= unit.Type->CorpseType->TileHeight);
// Update sight for new corpse
// We have to unmark BEFORE changing the type.
// Always do that, since types can have different vision properties.
MapUnmarkUnitGuard(unit);
MapUnmarkUnitSight(unit);
unit.Type = unit.Type->CorpseType;
unit.CurrentSightRange =
unit.Type->Stats[unit.Player->Index].Variables[SIGHTRANGE_INDEX].Max;
MapMarkUnitSight(unit);
// Update sight for new corpse
// We have to unmark BEFORE changing the type.
// Always do that, since types can have different vision properties.
// We must be dead to get here, it we aren't we need to know why
// This assert replaces and old DEBUG message "Reset to die is really needed"
Assert(unit.CurrentAction() == UnitActionDie);
unit.Remove(NULL);
unit.Type = unit.Type->CorpseType;
unit.Stats = &unit.Type->Stats[unit.Player->Index];
unit.Place(unit.tilePos);
unit.SubAction = 0;
unit.Frame = 0;
UnitUpdateHeading(unit);
if (unit.Type->Animations && unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]) {
UnitShowAnimation(unit, unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]);
}
// We must be dead to get here, it we aren't we need to know why
// This assert replaces and old DEBUG message "Reset to die is really needed"
Assert(unit.CurrentAction() == UnitActionDie);
unit.SubAction = 0;
unit.Frame = 0;
UnitUpdateHeading(unit);
if (unit.Type->Animations && unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]) {
UnitShowAnimation(unit, unit.Type->Animations->Death[ANIMATIONS_DEATHTYPES]);
}
}

View file

@ -132,7 +132,7 @@ void HandleActionFollow(CUnit &unit)
// Teleport the unit
unit.Remove(NULL);
unit.tilePos = goal->Goal->tilePos;
DropOutOnSide(unit, unit.Direction, 1, 1);
DropOutOnSide(unit, unit.Direction, NULL);
#if 0
// FIXME: SoundForName() should be called once
PlayGameSound(SoundForName("invisibility"), MaxSampleVolume);

View file

@ -275,7 +275,7 @@ static void LoseResource(CUnit &unit, const CUnit &source)
CUnit *depot;
ResourceInfo *resinfo = unit.Type->ResInfo[unit.CurrentResource];
Assert((unit.Container && !resinfo->HarvestFromOutside) ||
Assert((unit.Container == &source && !resinfo->HarvestFromOutside) ||
(!unit.Container && resinfo->HarvestFromOutside));
if (resinfo->HarvestFromOutside) {
@ -287,8 +287,7 @@ static void LoseResource(CUnit &unit, const CUnit &source)
//
if (unit.ResourcesHeld && (depot = FindDeposit(unit, 1000, unit.CurrentResource))) {
if (unit.Container) {
DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(),
source.Type->TileWidth, source.Type->TileHeight);
DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), &source);
}
//
// Remember were it mined, so it can look around for another resource.
@ -306,8 +305,7 @@ static void LoseResource(CUnit &unit, const CUnit &source)
//
if (unit.Container) {
Assert(!resinfo->HarvestFromOutside);
DropOutOnSide(unit, LookingW, source.Type->TileWidth,
source.Type->TileHeight);
DropOutOnSide(unit, LookingW, &source);
}
unit.CurrentOrder()->goalPos.x = unit.CurrentOrder()->goalPos.y = -1;
//use depot as goal
@ -593,8 +591,7 @@ static int StopGathering(CUnit &unit)
!unit.ResourcesHeld) {
if (!(resinfo->HarvestFromOutside || resinfo->TerrainHarvester)) {
Assert(unit.Container);
DropOutOnSide(unit, LookingW, source->Type->TileWidth,
source->Type->TileHeight);
DropOutOnSide(unit, LookingW, source);
}
DebugPrint("%d: Worker %d report: Can't find a resource [%d] deposit.\n"
_C_ unit.Player->Index _C_ unit.Slot _C_ unit.CurrentResource);
@ -605,8 +602,7 @@ static int StopGathering(CUnit &unit)
} else {
if (!(resinfo->HarvestFromOutside || resinfo->TerrainHarvester)) {
Assert(unit.Container);
DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(),
source->Type->TileWidth, source->Type->TileHeight);
DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), source);
}
UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT);
}
@ -733,14 +729,14 @@ static int WaitInDepot(CUnit &unit)
pos = unit.CurrentOrder()->Arg1.Resource.Pos;
if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 0, 10, unit.Player, pos, &pos)) {
if(depot)
DropOutNearest(unit, pos, depot->Type->TileWidth,
depot->Type->TileHeight);
if (depot) {
DropOutNearest(unit, pos, depot);
}
unit.CurrentOrder()->goalPos = pos;
} else {
if(depot)
DropOutOnSide(unit, LookingW, depot->Type->TileWidth,
depot->Type->TileHeight);
if (depot) {
DropOutOnSide(unit, LookingW, depot);
}
unit.ClearAction();
}
} else {
@ -752,12 +748,12 @@ static int WaitInDepot(CUnit &unit)
goal = UnitFindResource(unit, pos.x, pos.y, range,
unit.CurrentResource, unit.Player->AiEnabled, depot);
if (goal) {
if(depot)
DropOutNearest(unit, goal->tilePos + goal->Type->GetHalfTileSize(),
depot->Type->TileWidth, depot->Type->TileHeight);
if (depot) {
DropOutNearest(unit, goal->tilePos + goal->Type->GetHalfTileSize(), depot);
}
if(goal != mine) {
if(mine) {
if (goal != mine) {
if (mine) {
unit.DeAssignWorkerFromMine(*mine);
mine->RefsDecrease();
}
@ -774,20 +770,17 @@ static int WaitInDepot(CUnit &unit)
_C_ unit.Player->Index _C_ unit.Slot
_C_ unit.tilePos.x _C_ unit.tilePos.y
_C_ pos.x _C_ pos.y _C_ range);
if(depot)
DropOutOnSide(unit,
LookingW, depot->Type->TileWidth, depot->Type->TileHeight);
if(mine) {
if (depot) {
DropOutOnSide(unit, LookingW, depot);
}
if (mine) {
unit.DeAssignWorkerFromMine(*mine);
mine->RefsDecrease();
unit.CurrentOrder()->Arg1.Resource.Mine = NULL;
}
unit.ClearAction();
}
}
return unit.CurrentAction() != UnitActionStill;
}

View file

@ -161,7 +161,7 @@ void HandleActionTrain(CUnit &unit)
AddToGroup(&nunit, 1, num);
}
DropOutOnSide(*nunit, LookingW, type->TileWidth, type->TileHeight);
DropOutOnSide(*nunit, LookingW, &unit);
// Set life span
if (type->DecayRate) {

View file

@ -49,84 +49,64 @@
-- Functions
----------------------------------------------------------------------------*/
// Flag for searching a valid tileset for unloading
#define LandUnitMask ( \
MapFieldLandUnit | \
MapFieldBuilding | \
MapFieldWall | \
MapFieldRocks | \
MapFieldForest | \
MapFieldCoastAllowed | \
MapFieldWaterAllowed | \
MapFieldUnpassable)
#define NavalUnitMask ( \
MapFieldLandUnit | \
MapFieldBuilding | \
MapFieldWall | \
MapFieldRocks | \
MapFieldForest | \
MapFieldCoastAllowed | \
MapFieldLandAllowed | \
MapFieldUnpassable)
/**
** Find a free position close to startPos
**
** @param transporter
** @param unit Unit to unload.
** @param startPos Original search position
** @param res Unload position.
** @param mask Movement mask for the unit to be droped.
** @param maxrange maximal range to unload.
** @param res Unload position.
**
** @return True if a position was found, False otherwise.
** @note resx and resy are undefined if a position is not found.
** @note res is undefined if a position is not found.
**
** @bug FIXME: Place unit only on fields reachable from the transporter
** @bug FIXME: This function fails for units larger than 1x1.
*/
static int FindUnloadPosition(const Vec2i startPos, Vec2i *res, int mask)
static bool FindUnloadPosition(const CUnit &transporter, const CUnit &unit, const Vec2i startPos, int maxRange, Vec2i *res)
{
int i;
int n;
int addx;
int addy;
Vec2i pos = startPos;
addx = addy = 1;
pos.x -= unit.Type->TileWidth - 1;
pos.y -= unit.Type->TileHeight - 1;
int addx = transporter.Type->TileWidth + unit.Type->TileWidth - 1;
int addy = transporter.Type->TileHeight + unit.Type->TileHeight - 1;
--pos.x;
for (n = 0; n < 2; ++n) {
// Nobody: There was some code here to check for unloading units that can
// only go on even tiles. It's useless, since we can only unload land units.
for (i = addy; i--; ++pos.y) {
if (CheckedCanMoveToMask(pos, mask)) {
for (int range = 0; range < maxRange; ++range) {
for (int i = addy; i--; ++pos.y) {
if (UnitCanBeAt(unit, pos)) {
*res = pos;
return 1;
return true;
}
}
++addx;
for (i = addx; i--; ++pos.x) {
if (CheckedCanMoveToMask(pos, mask)) {
for (int i = addx; i--; ++pos.x) {
if (UnitCanBeAt(unit, pos)) {
*res = pos;
return 1;
return true;
}
}
++addy;
for (i = addy; i--; --pos.y) {
if (CheckedCanMoveToMask(pos, mask)) {
for (int i = addy; i--; --pos.y) {
if (UnitCanBeAt(unit, pos)) {
*res = pos;
return 1;
return true;
}
}
++addx;
for (i = addx; i--; --pos.x) {
if (CheckedCanMoveToMask(pos, mask)) {
for (int i = addx; i--; --pos.x) {
if (UnitCanBeAt(unit, pos)) {
*res = pos;
return 1;
return true;
}
}
++addy;
}
return 0;
return false;
}
/**
@ -138,12 +118,13 @@ static int FindUnloadPosition(const Vec2i startPos, Vec2i *res, int mask)
**
** @bug FIXME: Place unit only on fields reachable from the transporter
*/
int UnloadUnit(CUnit &unit)
static int UnloadUnit(CUnit &transporter, CUnit &unit)
{
const int maxRange = 1;
Vec2i pos;
Assert(unit.Removed);
if (!FindUnloadPosition(unit.tilePos, &pos, unit.Type->MovementMask)) {
if (!FindUnloadPosition(transporter, unit, transporter.tilePos, maxRange, &pos)) {
return 0;
}
unit.Boarded = 0;
@ -152,72 +133,80 @@ int UnloadUnit(CUnit &unit)
}
/**
** Find the closest piece of coast you can unload units on
** Return true is possition is a correct place to drop out units.
**
** @param x start location for the search
** @param y start location for the search
** @param resPos coast position
**
** @return 1 if a location was found, 0 otherwise
** @param transporter Transporter unit.
** @param pos position to drop out units.
*/
static int ClosestFreeCoast(const Vec2i &startPos, Vec2i *resPos)
static bool IsDropZonePossible(const CUnit &transporter, const Vec2i &pos)
{
int i;
int addx;
int addy;
Vec2i nullpos;
Vec2i pos = startPos;
int n;
const int maxUnloadRange = 1;
addx = addy = 1;
if (Map.CoastOnMap(pos) &&
FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
*resPos = pos;
return 1;
if (!UnitCanBeAt(transporter, pos)) {
return false;
}
--pos.x;
// The maximum distance to the coast. We have to stop somewhere...
n = 20;
while (n--) {
for (i = addy; i--; ++pos.y) {
if (Map.Info.IsPointOnMap(pos) &&
Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) &&
FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
Vec2i dummyPos;
CUnit* unit = transporter.UnitInside;
for (int i = 0; i < transporter.InsideCount; ++i, unit = unit->NextContained) {
if (FindUnloadPosition(transporter, *unit, pos, maxUnloadRange, &dummyPos)) {
return true;
}
}
// Check unit can be droped from here.
return false;
}
/**
** Find the closest available drop zone for a transporter.
** Fail if transporter don't transport any unit..
**
** @param transporter the transporter
** @param startPos start location for the search
** @param maxRange The maximum distance from initial position to search...
** @param resPos drop zone position
**
** @return true if a location was found, false otherwise
** @note to be called only from ClosestFreeDropZone.
*/
static bool ClosestFreeDropZone_internal(const CUnit &transporter, const Vec2i &startPos, int maxRange, Vec2i *resPos)
{
int addx = 0;
int addy = 1;
Vec2i pos = startPos;
for (int range = 0; range < maxRange; ++range) {
for (int i = addy; i--; ++pos.y) {
if (IsDropZonePossible(transporter, pos)) {
*resPos = pos;
return 1;
return true;
}
}
++addx;
for (i = addx; i--; ++pos.x) {
if (Map.Info.IsPointOnMap(pos) &&
Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) &&
FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
for (int i = addx; i--; ++pos.x) {
if (IsDropZonePossible(transporter, pos)) {
*resPos = pos;
return 1;
return true;
}
}
++addy;
for (i = addy; i--; --pos.y) {
if (Map.Info.IsPointOnMap(pos) &&
Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) &&
FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
for (int i = addy; i--; --pos.y) {
if (IsDropZonePossible(transporter, pos)) {
*resPos = pos;
return 1;
return true;
}
}
++addx;
for (i = addx; i--; --pos.x) {
if (Map.Info.IsPointOnMap(pos) &&
Map.CoastOnMap(pos) && !UnitOnMapTile(pos, -1) &&
FindUnloadPosition(pos, &nullpos, LandUnitMask)) {
for (int i = addx; i--; --pos.x) {
if (IsDropZonePossible(transporter, pos)) {
*resPos = pos;
return 1;
return true;
}
}
++addy;
}
DebugPrint("Try clicking closer to an actual coast.\n");
return 0;
return false;
}
/**
@ -226,52 +215,31 @@ static int ClosestFreeCoast(const Vec2i &startPos, Vec2i *resPos)
**
** @param transporter the transporter
** @param startPos start location for the search
** @param resPos coast position
** @param maxRange The maximum distance from initial position to search...
** @param resPos drop zone position
**
** @return 1 if a location was found, 0 otherwise
**
*/
static int ClosestFreeDropZone(CUnit &transporter, const Vec2i& startPos, Vec2i *resPos)
static int ClosestFreeDropZone(CUnit &transporter, const Vec2i &startPos, int maxRange, Vec2i *resPos)
{
// Type (land/fly/naval) of the transporter
int transporterType;
// Type (land/fly/naval) of the units to unload
int loadedType;
// Check there are units onboard
if (!transporter.UnitInside) {
return 0;
}
const bool isTransporterRemoved = transporter.Removed;
transporterType = transporter.Type->UnitType;
// Take the type of the onboard unit
loadedType = transporter.UnitInside->Type->UnitType;
// Don't move in thoses cases
if ((transporterType == loadedType) || (loadedType == UnitTypeFly)) {
*resPos = startPos;
return 1;
if (!isTransporterRemoved) {
// Remove transporter to avoid "collision" with itself.
transporter.Remove(NULL);
}
switch (transporterType) {
case UnitTypeLand:
// in this case, loadedType == UnitTypeSea
return ClosestFreeCoast(startPos, resPos);
case UnitTypeNaval:
// Same ( but reversed... )
return ClosestFreeCoast(startPos, resPos);
case UnitTypeFly:
// Here we have loadedType in [ UnitTypeLand,UnitTypeNaval ]
if (loadedType == UnitTypeLand) {
return FindUnloadPosition(startPos, resPos, LandUnitMask);
} else {
return FindUnloadPosition(startPos, resPos, NavalUnitMask);
}
const bool res = ClosestFreeDropZone_internal(transporter, startPos, maxRange, resPos);
if (!isTransporterRemoved) {
transporter.Place(transporter.tilePos);
}
// Just to avoid a warning
return 0;
return res;
}
/**
** Move to dropzone.
**
@ -299,14 +267,10 @@ static int MoveToDropZone(CUnit &unit)
**
** @param unit Pointer to unit.
*/
static void LeaveTransporter(CUnit &unit)
static void LeaveTransporter(CUnit &transporter)
{
int i;
int stillonboard;
CUnit *goal;
stillonboard = 0;
goal = unit.CurrentOrder()->GetGoal();
int stillonboard = 0;
CUnit *goal = transporter.CurrentOrder()->GetGoal();
//
// Goal is the specific unit unit that you want to unload.
// This can be NULL, in case you want to unload everything.
@ -314,30 +278,30 @@ static void LeaveTransporter(CUnit &unit)
if (goal) {
if (goal->Destroyed) {
DebugPrint("destroyed unit unloading?\n");
unit.CurrentOrder()->ClearGoal();
transporter.CurrentOrder()->ClearGoal();
return;
}
unit.CurrentOrder()->ClearGoal();
goal->tilePos = unit.tilePos;
transporter.CurrentOrder()->ClearGoal();
goal->tilePos = transporter.tilePos;
// Try to unload the unit. If it doesn't work there is no problem.
if (UnloadUnit(*goal)) {
unit.BoardCount--;
if (UnloadUnit(transporter, *goal)) {
transporter.BoardCount--;
}
} else {
// Unload all units.
goal = unit.UnitInside;
for (i = unit.InsideCount; i; --i, goal = goal->NextContained) {
goal = transporter.UnitInside;
for (int i = transporter.InsideCount; i; --i, goal = goal->NextContained) {
if (goal->Boarded) {
goal->tilePos = unit.tilePos;
if (!UnloadUnit(*goal)) {
goal->tilePos = transporter.tilePos;
if (!UnloadUnit(transporter, *goal)) {
++stillonboard;
} else {
unit.BoardCount--;
transporter.BoardCount--;
}
}
}
}
if (IsOnlySelected(unit)) {
if (IsOnlySelected(transporter)) {
SelectedUnitChanged();
}
@ -345,12 +309,12 @@ static void LeaveTransporter(CUnit &unit)
if (stillonboard) {
// We tell it to unload at it's current position. This can't be done,
// so it will search for a piece of free coast nearby.
unit.CurrentOrder()->Action = UnitActionUnload;
unit.CurrentOrder()->ClearGoal();
unit.CurrentOrder()->goalPos = unit.tilePos;
unit.SubAction = 0;
transporter.CurrentOrder()->Action = UnitActionUnload;
transporter.CurrentOrder()->ClearGoal();
transporter.CurrentOrder()->goalPos = transporter.tilePos;
transporter.SubAction = 0;
} else {
unit.ClearAction();
transporter.ClearAction();
}
}
@ -361,19 +325,17 @@ static void LeaveTransporter(CUnit &unit)
*/
void HandleActionUnload(CUnit &unit)
{
int i;
Vec2i pos;
const int maxSearchRange = 20;
if (!unit.CanMove()) {
unit.SubAction = 2;
}
switch (unit.SubAction) {
//
// Move the transporter
//
case 0:
case 0: // Choose destination
if (!unit.CurrentOrder()->HasGoal()) {
if (!ClosestFreeDropZone(unit, unit.CurrentOrder()->goalPos, &pos)) {
Vec2i pos;
if (!ClosestFreeDropZone(unit, unit.CurrentOrder()->goalPos, maxSearchRange, &pos)) {
// Sorry... I give up.
unit.ClearAction();
return;
@ -383,12 +345,15 @@ void HandleActionUnload(CUnit &unit)
NewResetPath(unit);
unit.SubAction = 1;
case 1:
// follow on next case
case 1: // Move unit to destination
// The Goal is the unit that we have to unload.
if (!unit.CurrentOrder()->HasGoal()) {
const int moveResult = MoveToDropZone(unit);
// We have to unload everything
if ((i = MoveToDropZone(unit))) {
if (i == PF_REACHED) {
if (moveResult) {
if (moveResult == PF_REACHED) {
if (++unit.SubAction == 1) {
unit.ClearAction();
}
@ -398,10 +363,7 @@ void HandleActionUnload(CUnit &unit)
}
break;
}
//
// Leave the transporter
//
case 2:
case 2: // Leave the transporter
// FIXME: show still animations ?
LeaveTransporter(unit);
if (unit.CanMove() && unit.CurrentAction() != UnitActionStill) {

View file

@ -616,7 +616,7 @@ static void AiGroupAttackerForTransport(AiForce &aiForce)
}
}
if (transporterIndex == aiForce.Size()) {
aiForce.State = AiForceAttackingState_Attacking;
aiForce.State = AiForceAttackingState_AttackingWithTransporter;
return ;
}
for (unsigned int i = 0; i < aiForce.Size(); ++i) {
@ -628,7 +628,7 @@ static void AiGroupAttackerForTransport(AiForce &aiForce)
}
}
if (goNext == true) {
aiForce.State = AiForceAttackingState_Attacking;
aiForce.State = AiForceAttackingState_AttackingWithTransporter;
return ;
}
for (unsigned int i = 0; i < aiForce.Size(); ++i) {

View file

@ -98,6 +98,7 @@ enum AiForceAttackingState
AiForceAttackingState_Free = -1,
AiForceAttackingState_Waiting = 0,
AiForceAttackingState_Boarding,
AiForceAttackingState_AttackingWithTransporter,
AiForceAttackingState_Attacking,
};

View file

@ -208,8 +208,7 @@ extern int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scal
extern int UnitShowAnimation(CUnit &unit, const CAnimation *anim);
/// Handle the actions of all units each game cycle
extern void UnitActions();
/// Unload a unit.
extern int UnloadUnit(CUnit &unit);
//@}
#endif // !__ACTIONS_H__

View file

@ -1221,7 +1221,7 @@ struct CResourceDepositFinder {
class CPreference {
public:
CPreference() : ShowSightRange(false), ShowReactionRange(false),
ShowAttackRange(false), ShowMessages(false),
ShowAttackRange(false), ShowMessages(false),
BigScreen(false),ShowOrders(0) {};
bool ShowSightRange; /// Show sight range.
@ -1311,9 +1311,9 @@ extern void UnitHeadingFromDeltaXY(CUnit &unit, const Vec2i &delta);
/// @todo more docu
extern void DropOutOnSide(CUnit &unit, int heading, int addx, int addy);
extern void DropOutOnSide(CUnit &unit, int heading, const CUnit *container);
/// @todo more docu
extern void DropOutNearest(CUnit &unit, const Vec2i &goalPos, int addx, int addy);
extern void DropOutNearest(CUnit &unit, const Vec2i &goalPos, const CUnit *container);
/// Drop out all units in the unit
extern void DropOutAll(const CUnit &unit);

View file

@ -615,12 +615,13 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
bool goal_reachable = false;
const Vec2i goal = {gx, gy};
const Vec2i extratilesize = {tilesizex - 1, tilesizey - 1};
// top hemi cycle
const int miny = std::max(-maxrange, 0 - goal.y);
for (int offsety = miny; offsety < -minrange; ++offsety) {
const int miny = std::max(-maxrange - extratilesize.y, 0 - goal.y);
for (int offsety = miny; offsety < -minrange - extratilesize.y; ++offsety) {
const int offsetx = isqrt(square(maxrange + 1) - square(-offsety) - 1);
const int minx = std::max(0, goal.x - offsetx);
const int minx = std::max(0, goal.x - offsetx - extratilesize.x);
const int maxx = std::min(Map.Info.MapWidth, goal.x + gw + offsetx);
Vec2i mpos = {minx, goal.y + offsety};
const unsigned int offset = mpos.y * Map.Info.MapWidth;
@ -635,8 +636,8 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
}
if (minrange == 0) {
// center
for (int offsety = 0; offsety < gh; ++offsety) {
const int minx = std::max(0, goal.x - maxrange);
for (int offsety = -extratilesize.y; offsety < gh; ++offsety) {
const int minx = std::max(0, goal.x - maxrange - extratilesize.x);
const int maxx = std::min(Map.Info.MapWidth, goal.x + gw + maxrange);
Vec2i mpos = {minx, goal.y + offsety};
const unsigned int offset = mpos.y * Map.Info.MapWidth;
@ -651,12 +652,12 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
}
} else {
// top hemi cycle
const int miny = std::max(-minrange, 0 - goal.y);
const int miny = std::max(-minrange - extratilesize.y, 0 - goal.y);
for (int offsety = miny; offsety < 0; ++offsety) {
const int offsetx1 = isqrt(square(maxrange + 1) - square(-offsety) - 1);
const int offsetx2 = isqrt(square(minrange + 1) - square(-offsety) - 1);
const int minxs[2] = {std::max(0, goal.x - offsetx1), std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)};
const int maxxs[2] = {std::max(0, goal.x - offsetx2), std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)};
const int minxs[2] = {std::max(0, goal.x - offsetx1 - extratilesize.x), std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)};
const int maxxs[2] = {std::max(0, goal.x - offsetx2 - extratilesize.x), std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)};
for (int i = 0; i < 2; ++i)
{
@ -676,11 +677,11 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
}
// center
const int mincenters[] = {std::max(0, goal.x - maxrange), 0, std::min(Map.Info.MapWidth, goal.x + gw + minrange)};
const int maxcenters[] = {std::max(0, goal.x - minrange), gw, std::min(Map.Info.MapWidth, goal.x + gw + maxrange)};
const int mincenters[] = {std::max(0, goal.x - maxrange - extratilesize.x), -extratilesize.x, std::min(Map.Info.MapWidth, goal.x + gw + minrange)};
const int maxcenters[] = {std::max(0, goal.x - minrange - extratilesize.x), gw, std::min(Map.Info.MapWidth, goal.x + gw + maxrange)};
for (int i = 0; i < 3; ++i) {
for (int offsety = 0; offsety < gh; ++offsety) {
for (int offsety = -extratilesize.y; offsety < gh; ++offsety) {
const int minx = mincenters[i];
const int maxx = maxcenters[i];
Vec2i mpos = {minx, goal.y + offsety};
@ -701,8 +702,8 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
for (int offsety = 0; offsety < maxy; ++offsety) {
const int offsetx1 = isqrt(square(maxrange + 1) - square(offsety) - 1);
const int offsetx2 = isqrt(square(minrange + 1) - square(offsety) - 1);
const int minxs[2] = {std::max(0, goal.x - offsetx1), std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)};
const int maxxs[2] = {std::max(0, goal.x - offsetx2), std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)};
const int minxs[2] = {std::max(0, goal.x - offsetx1) - extratilesize.x, std::min(Map.Info.MapWidth, goal.x + gw + offsetx2)};
const int maxxs[2] = {std::max(0, goal.x - offsetx2) - extratilesize.x, std::min(Map.Info.MapWidth, goal.x + gw + offsetx1)};
for (int i = 0; i < 2; ++i)
{
@ -726,7 +727,7 @@ static int AStarMarkGoal(int gx, int gy, int gw, int gh,
const int maxy = std::min(maxrange, Map.Info.MapHeight - goal.y - gh);
for (int offsety = minrange; offsety < maxy; ++offsety) {
const int offsetx = isqrt(square(maxrange + 1) - square(offsety) - 1);
const int minx = std::max(0, goal.x - offsetx);
const int minx = std::max(0, goal.x - offsetx - extratilesize.x);
const int maxx = std::min(Map.Info.MapWidth, goal.x + gw + offsetx);
Vec2i mpos = {minx, goal.y + gh + offsety};
const unsigned int offset = mpos.y * Map.Info.MapWidth;

View file

@ -676,10 +676,9 @@ int Summon::Cast(CUnit &caster, const SpellType *spell,
//
target = MakeUnit(unittype, caster.Player);
if (target != NoUnitP) {
// This is a hack to walk around behaviour of DropOutOnSide
target->tilePos.x = x + 1;
target->tilePos.x = x;
target->tilePos.y = y;
DropOutOnSide(*target, LookingW, 0, 0); // FIXME : 0,0) : good parameter ?
DropOutOnSide(*target, LookingW, NULL);
//
// set life span. ttl=0 results in a permanent unit.
//

View file

@ -940,14 +940,12 @@ static int CclUnit(lua_State *l)
*/
static int CclMoveUnit(lua_State *l)
{
CUnit *unit;
int heading;
Vec2i ipos;
LuaCheckArgs(l, 2);
lua_pushvalue(l, 1);
unit = CclGetUnit(l);
CUnit *unit = CclGetUnit(l);
lua_pop(l, 1);
lua_rawgeti(l, 2, 1);
@ -957,14 +955,14 @@ static int CclMoveUnit(lua_State *l)
ipos.y = LuaToNumber(l, -1);
lua_pop(l, 1);
heading = SyncRand() % 256;
if (UnitCanBeAt(*unit, ipos)) {
unit->Place(ipos);
} else {
const int heading = SyncRand() % 256;
unit->tilePos = ipos;
DropOutOnSide(*unit, heading, 1, 1);
DropOutOnSide(*unit, heading, NULL);
}
// PlaceUnit(unit, ipos.x, ipos.y);
lua_pushvalue(l, 1);
return 1;
}
@ -980,7 +978,6 @@ static int CclCreateUnit(lua_State *l)
{
CUnitType *unittype;
CUnit *unit;
int heading;
int playerno;
Vec2i ipos;
@ -999,7 +996,6 @@ static int CclCreateUnit(lua_State *l)
ipos.y = LuaToNumber(l, -1);
lua_pop(l, 1);
heading = SyncRand() % 256;
lua_pushvalue(l, 2);
playerno = TriggerGetPlayer(l);
lua_pop(l, 1);
@ -1023,8 +1019,10 @@ static int CclCreateUnit(lua_State *l)
(unit->Type->Building && CanBuildUnitType(NULL, *unit->Type, ipos, 0))) {
unit->Place(ipos);
} else {
const int heading = SyncRand() % 256;
unit->tilePos = ipos;
DropOutOnSide(*unit, heading, 1, 1);
DropOutOnSide(*unit, heading, NULL);
}
UpdateForNewUnit(*unit, 0);
@ -1344,9 +1342,9 @@ static int CclSetUnitVariable(lua_State *l)
if (!strcmp(name, "RegenerationRate"))
{
value = LuaToNumber(l, 3);
if (value > unit->Variable[HP_INDEX].Max)
if (value > unit->Variable[HP_INDEX].Max)
unit->Stats->Variables[HP_INDEX].Increase = unit->Variable[HP_INDEX].Max;
else
else
unit->Stats->Variables[HP_INDEX].Increase = value;
}
else

View file

@ -634,6 +634,10 @@ void MarkUnitFieldFlags(const CUnit &unit)
int w, h = unit.Type->TileHeight; // Tile height of the unit.
const int width = unit.Type->TileWidth; // Tile width of the unit.
unsigned int index = unit.Offset;
if (unit.Type->Vanishes) {
return ;
}
do {
mf = Map.Field(index);
w = width;
@ -671,6 +675,10 @@ void UnmarkUnitFieldFlags(const CUnit &unit)
const int width = unit.Type->TileWidth; // Tile width of the unit.
unsigned int index = unit.Offset;
if (unit.Type->Vanishes) {
return ;
}
_UnmarkUnitFieldFlags funct(unit);
do {
@ -1814,62 +1822,74 @@ void UnitHeadingFromDeltaXY(CUnit &unit, const Vec2i &delta)
**
** @param unit Unit to drop out.
** @param heading Direction in which the unit should appear.
** @param addx Tile width of unit it's dropping out of.
** @param addy Tile height of unit it's dropping out of.
** @param container Unit "containing" unit to drop (may be different of unit.Container).
*/
void DropOutOnSide(CUnit &unit, int heading, int addx, int addy)
void DropOutOnSide(CUnit &unit, int heading, const CUnit *container)
{
Vec2i pos;
int i;
int addx = 0;
int addy = 0;
if (unit.Container) {
pos = unit.Container->tilePos;
if (container) {
pos = container->tilePos;
pos.x -= unit.Type->TileWidth - 1;
pos.y -= unit.Type->TileHeight - 1;
addx = container->Type->TileWidth + unit.Type->TileWidth - 1;
addy = container->Type->TileHeight + unit.Type->TileHeight - 1;
if (heading < LookingNE || heading > LookingNW) {
pos.x += addx - 1;
--pos.y;
goto startn;
} else if (heading < LookingSE) {
pos.x += addx;
pos.y += addy - 1;
goto starte;
} else if (heading < LookingSW) {
pos.y += addy;
goto starts;
} else {
--pos.x;
goto startw;
}
} else {
pos = unit.tilePos;
}
if (heading < LookingNE || heading > LookingNW) {
pos.x += addx - 1;
--pos.y;
goto startn;
if (heading < LookingNE || heading > LookingNW) {
goto starts;
} else if (heading < LookingSE) {
goto startw;
} else if (heading < LookingSW) {
goto startn;
} else {
goto starte;
}
}
if (heading < LookingSE) {
pos.x += addx;
pos.y += addy - 1;
goto starte;
}
if (heading < LookingSW) {
pos.y += addy;
goto starts;
}
--pos.x;
goto startw;
// FIXME: don't search outside of the map
for (;;) {
startw:
for (i = addy; i--; ++pos.y) {
for (int i = addy; i--; ++pos.y) {
if (UnitCanBeAt(unit, pos)) {
goto found;
}
}
++addx;
starts:
for (i = addx; i--; ++pos.x) {
for (int i = addx; i--; ++pos.x) {
if (UnitCanBeAt(unit, pos)) {
goto found;
}
}
++addy;
starte:
for (i = addy; i--; --pos.y) {
for (int i = addy; i--; --pos.y) {
if (UnitCanBeAt(unit, pos)) {
goto found;
}
}
++addx;
startn:
for (i = addx; i--; --pos.x) {
for (int i = addx; i--; --pos.x) {
if (UnitCanBeAt(unit, pos)) {
goto found;
}
@ -1882,29 +1902,34 @@ found:
}
/**
** Place a unit on the map nearest to x, y.
** Place a unit on the map nearest to goalPos.
**
** @param unit Unit to drop out.
** @param goalPos Goal map tile position.
** @param addx Tile width of unit it's dropping out of.
** @param addy Tile height of unit it's dropping out of.
*/
void DropOutNearest(CUnit &unit, const Vec2i &goalPos, int addx, int addy)
void DropOutNearest(CUnit &unit, const Vec2i &goalPos, const CUnit *container)
{
Vec2i pos;
Vec2i bestPos = {0, 0};
int bestd = 99999;
int addx = 0;
int addy = 0;
Assert(unit.Removed);
if (unit.Container) {
pos = unit.Container->tilePos;
if (container) {
pos = container->tilePos;
pos.x -= unit.Type->TileWidth - 1;
pos.y -= unit.Type->TileHeight - 1;
addx = container->Type->TileWidth + unit.Type->TileWidth - 1;
addy = container->Type->TileHeight + unit.Type->TileHeight - 1;
--pos.x;
} else {
pos = unit.tilePos;
}
// FIXME: if we reach the map borders we can go fast up, left, ...
--pos.x;
for (;;) {
for (int i = addy; i--; ++pos.y) { // go down
if (UnitCanBeAt(unit, pos)) {
@ -1964,18 +1989,14 @@ void DropOutNearest(CUnit &unit, const Vec2i &goalPos, int addx, int addy)
*/
void DropOutAll(const CUnit &source)
{
CUnit *unit;
int i;
CUnit *unit = source.UnitInside;
unit = source.UnitInside;
for (i = source.InsideCount; i; --i, unit = unit->NextContained) {
DropOutOnSide(*unit, LookingW,
source.Type->TileWidth, source.Type->TileHeight);
for (int i = source.InsideCount; i; --i, unit = unit->NextContained) {
DropOutOnSide(*unit, LookingW, &source);
Assert(!unit->CurrentOrder()->HasGoal());
unit->CurrentOrder()->Action = UnitActionStill;
unit->SubAction = 0;
}
DebugPrint("Drop out %d of %d\n" _C_ i _C_ source.Data.Resource.Active);
}