Move some functions from unit.cpp to unit_find.cpp

Add unit_find.h
This commit is contained in:
Joris 2012-06-29 13:18:12 +02:00
parent 77ad8b6eb5
commit 55d9c4b267
21 changed files with 565 additions and 496 deletions

View file

@ -490,6 +490,7 @@ set(stratagus_generic_HDRS
src/include/ui.h
src/include/unit.h
src/include/unit_cache.h
src/include/unit_find.h
src/include/unit_manager.h
src/include/unitptr.h
src/include/unitsound.h

View file

@ -53,6 +53,7 @@
#include "spells.h"
#include "ui.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
/*----------------------------------------------------------------------------

View file

@ -42,6 +42,7 @@
#include "script.h"
#include "ui.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
enum {

View file

@ -47,6 +47,7 @@
#include "sound.h"
#include "ui.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
/*----------------------------------------------------------------------------

View file

@ -47,6 +47,7 @@
#include "spells.h"
#include "tileset.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
enum {

View file

@ -67,6 +67,7 @@
#include "player.h"
#include "script.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
/*----------------------------------------------------------------------------

View file

@ -41,6 +41,7 @@
#include "pathfinder.h"
#include "player.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
/*----------------------------------------------------------------------------

View file

@ -44,8 +44,9 @@
#include "depend.h"
#include "map.h"
#include "pathfinder.h"
#include "unittype.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
/*----------------------------------------------------------------------------
-- Types

View file

@ -43,6 +43,7 @@
#include "missile.h"
#include "pathfinder.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
/*----------------------------------------------------------------------------

View file

@ -46,6 +46,7 @@
#include "pathfinder.h"
#include "player.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
#include "upgrade.h"

View file

@ -36,29 +36,31 @@
#include <deque>
#include "stratagus.h"
#include "editor.h"
#include "commands.h"
#include "font.h"
#include "guichan.h"
#include "interface.h"
#include "iocompat.h"
#include "iolib.h"
#include "map.h"
#include "menus.h"
#include "minimap.h"
#include "network.h"
#include "replay.h"
#include "script.h"
#include "settings.h"
#include "sound.h"
#include "sound_server.h"
#include "tileset.h"
#include "ui.h"
#include "unit_find.h"
#include "unittype.h"
#include "video.h"
#include "map.h"
#include "tileset.h"
#include "minimap.h"
#include "settings.h"
#include "network.h"
#include "sound_server.h"
#include "ui.h"
#include "interface.h"
#include "font.h"
#include "widgets.h"
#include "editor.h"
#include "results.h"
#include "menus.h"
#include "sound.h"
#include "iolib.h"
#include "iocompat.h"
#include "commands.h"
#include "guichan.h"
#include "replay.h"
#include "script.h"
extern void DoScrollArea(int state, bool fast);
extern void DrawGuichanWidgets();

View file

@ -44,6 +44,7 @@
#include "results.h"
#include "script.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
/*----------------------------------------------------------------------------

View file

@ -466,37 +466,6 @@ public:
PixelPos GetMapPixelPosCenter() const;
};
//unit_find
struct CUnitTypeFinder {
const UnitTypeType type;
CUnitTypeFinder(const UnitTypeType t) : type(t) {}
bool operator()(const CUnit *const unit) const {
const CUnitType *const t = unit->Type;
if (t->Vanishes || (type != static_cast<UnitTypeType>(-1) && t->UnitType != type)) {
return false;
}
return true;
}
CUnit *Find(const CUnitCache &cache) const { return cache.find(*this); }
CUnit *Find(const CMapField *const mf) const { return mf->UnitCache.find(*this); }
};
struct CResourceFinder {
const int resource;
const int mine_on_top;
CResourceFinder(const int r, int on_top) : resource(r), mine_on_top(on_top) {}
inline bool operator()(const CUnit *const unit) const {
const CUnitType *const type = unit->Type;
return (type->GivesResource == resource
&& unit->ResourcesHeld != 0
&& (mine_on_top ? type->CanHarvest : !type->CanHarvest)
&& !unit->IsUnusable(true) //allow mines under construction
);
}
CUnit *Find(const CMapField *const mf) const { return mf->UnitCache.find(*this); }
};
#define NoUnitP (CUnit *)0 /// return value: for no unit found
#define MAX_UNIT_SLOTS 65535 /// Maximal number of used slots
@ -633,19 +602,6 @@ extern CUnit *CanBuildUnitType(const CUnit *unit, const CUnitType &type, const V
/// Get the suitable animation frame depends of unit's damaged type.
extern int ExtraDeathIndex(const char *death);
/// Find resource
extern CUnit *UnitFindResource(const CUnit &unit, const CUnit &startUnit, int range,
int resource, bool check_usage = false, const CUnit *deposit = NULL);
/// Find nearest deposit
extern CUnit *FindDeposit(const CUnit &unit, int range, int resource);
/// Find the next idle worker
extern CUnit *FindIdleWorker(const CPlayer &player, const CUnit *last);
/// Find the neareast piece of terrain with specific flags.
extern bool FindTerrainType(int movemask, int resmask, int range,
const CPlayer &player, const Vec2i &startPos, Vec2i *pos);
/// @todo more docu
extern CUnit *UnitOnScreen(CUnit *unit, int x, int y);
@ -713,29 +669,6 @@ extern int FindAndSortUnits(const CViewport *vp, std::vector<CUnit *> &table);
/// Show a unit's orders.
extern void ShowOrder(const CUnit &unit);
// in unit_find.cpp
extern void FindUnitsByType(const CUnitType &type, std::vector<CUnit *> &units);
/// Find all units of this type of the player
extern void FindPlayerUnitsByType(const CPlayer &player, const CUnitType &type, std::vector<CUnit *> &units);
/// Return any unit on that map tile
extern CUnit *UnitOnMapTile(const Vec2i &pos, unsigned int type);// = -1);
/// Return possible attack target on that map area
extern CUnit *TargetOnMap(const CUnit &unit, const Vec2i &pos1, const Vec2i &pos2);
/// Return resource, if on map tile
extern CUnit *ResourceOnMap(const Vec2i &pos, int resource, bool mine_on_top = true);
/// Return resource deposit, if on map tile
extern CUnit *ResourceDepositOnMap(const Vec2i &pos, int resource);
/// Find best enemy in numeric range to attack
extern CUnit *AttackUnitsInDistance(const CUnit &unit, int range, bool onlyBuildings = false);
/// Find best enemy in attack range to attack
extern CUnit *AttackUnitsInRange(const CUnit &unit);
/// Find best enemy in reaction range to attack
extern CUnit *AttackUnitsInReactRange(const CUnit &unit);
// in groups.c
/// Initialize data structures for groups

115
src/include/unit_find.h Normal file
View file

@ -0,0 +1,115 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name unit.h - The unit headerfile. */
//
// (c) Copyright 1998-2012 by Lutz Sammer, Jimmy Salmon and Joris Dauphin
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; only version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
#ifndef __UNIT_FIND_H__
#define __UNIT_FIND_H__
//@{
#include "unit.h"
#include "unittype.h"
class CUnitCache;
class CMapField;
/*----------------------------------------------------------------------------
-- Declarations
----------------------------------------------------------------------------*/
//unit_find
struct CUnitTypeFinder {
const UnitTypeType type;
CUnitTypeFinder(const UnitTypeType t) : type(t) {}
bool operator()(const CUnit *const unit) const {
const CUnitType *const t = unit->Type;
if (t->Vanishes || (type != static_cast<UnitTypeType>(-1) && t->UnitType != type)) {
return false;
}
return true;
}
CUnit *Find(const CUnitCache &cache) const { return cache.find(*this); }
CUnit *Find(const CMapField *const mf) const { return mf->UnitCache.find(*this); }
};
struct CResourceFinder {
const int resource;
const int mine_on_top;
CResourceFinder(const int r, int on_top) : resource(r), mine_on_top(on_top) {}
inline bool operator()(const CUnit *const unit) const {
const CUnitType *const type = unit->Type;
return (type->GivesResource == resource
&& unit->ResourcesHeld != 0
&& (mine_on_top ? type->CanHarvest : !type->CanHarvest)
&& !unit->IsUnusable(true) //allow mines under construction
);
}
CUnit *Find(const CMapField *const mf) const { return mf->UnitCache.find(*this); }
};
/// Find resource
extern CUnit *UnitFindResource(const CUnit &unit, const CUnit &startUnit, int range,
int resource, bool check_usage = false, const CUnit *deposit = NULL);
/// Find nearest deposit
extern CUnit *FindDeposit(const CUnit &unit, int range, int resource);
/// Find the next idle worker
extern CUnit *FindIdleWorker(const CPlayer &player, const CUnit *last);
/// Find the neareast piece of terrain with specific flags.
extern bool FindTerrainType(int movemask, int resmask, int range,
const CPlayer &player, const Vec2i &startPos, Vec2i *pos);
// in unit_find.cpp
extern void FindUnitsByType(const CUnitType &type, std::vector<CUnit *> &units);
/// Find all units of this type of the player
extern void FindPlayerUnitsByType(const CPlayer &player, const CUnitType &type, std::vector<CUnit *> &units);
/// Return any unit on that map tile
extern CUnit *UnitOnMapTile(const Vec2i &pos, unsigned int type);// = -1);
/// Return possible attack target on that map area
extern CUnit *TargetOnMap(const CUnit &unit, const Vec2i &pos1, const Vec2i &pos2);
/// Return resource, if on map tile
extern CUnit *ResourceOnMap(const Vec2i &pos, int resource, bool mine_on_top = true);
/// Return resource deposit, if on map tile
extern CUnit *ResourceDepositOnMap(const Vec2i &pos, int resource);
/// Find best enemy in numeric range to attack
extern CUnit *AttackUnitsInDistance(const CUnit &unit, int range, bool onlyBuildings = false);
/// Find best enemy in attack range to attack
extern CUnit *AttackUnitsInRange(const CUnit &unit);
/// Find best enemy in reaction range to attack
extern CUnit *AttackUnitsInReactRange(const CUnit &unit);
//@}
#endif // !__UNIT_FIND_H__

View file

@ -38,6 +38,7 @@
#include "map.h"
#include "unit.h"
#include "unit_find.h"
#include "pathfinder.h"

View file

@ -48,9 +48,10 @@
#include "sound.h"
#include "sound_server.h"
#include "tileset.h"
#include "unittype.h"
#include "unit.h"
#include "ui.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
#include "video.h"
#include "widgets.h"

View file

@ -58,6 +58,7 @@
#include "spells.h"
#include "tileset.h"
#include "unit.h"
#include "unit_find.h"
#include "unitsound.h"
#include "unittype.h"
#include "video.h"

View file

@ -46,6 +46,7 @@
#include "script.h"
#include "spells.h"
#include "trigger.h"
#include "unit_find.h"
#include "unit_manager.h"
#include "unittype.h"
#include "upgrade.h"

View file

@ -41,30 +41,32 @@
#include "stratagus.h"
#include "unit.h"
#include "actions.h"
#include "ai.h"
#include "animation.h"
#include "commands.h"
#include "construct.h"
#include "editor.h"
#include "interface.h"
#include "iolib.h"
#include "luacallback.h"
#include "map.h"
#include "missile.h"
#include "network.h"
#include "pathfinder.h"
#include "player.h"
#include "script.h"
#include "sound.h"
#include "sound_server.h"
#include "spells.h"
#include "tileset.h"
#include "ui.h"
#include "unit_find.h"
#include "unit_manager.h"
#include "video.h"
#include "unitsound.h"
#include "unittype.h"
#include "animation.h"
#include "player.h"
#include "tileset.h"
#include "map.h"
#include "actions.h"
#include "sound_server.h"
#include "missile.h"
#include "interface.h"
#include "sound.h"
#include "ai.h"
#include "pathfinder.h"
#include "network.h"
#include "ui.h"
#include "script.h"
#include "editor.h"
#include "spells.h"
#include "luacallback.h"
#include "construct.h"
#include "iolib.h"
#include "video.h"
/*----------------------------------------------------------------------------
-- Documentation
@ -2277,392 +2279,6 @@ void DropOutAll(const CUnit &source)
}
}
/*----------------------------------------------------------------------------
-- Finding units
----------------------------------------------------------------------------*/
class TerrainFinder
{
public:
TerrainFinder(const CPlayer &player, int maxDist, int movemask, int resmask, Vec2i *resPos) :
player(player), maxDist(maxDist), movemask(movemask), resmask(resmask), resPos(resPos) {}
VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from);
private:
const CPlayer &player;
int maxDist;
int movemask;
int resmask;
Vec2i *resPos;
};
VisitResult TerrainFinder::Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from)
{
if (!player.AiEnabled && !Map.IsFieldExplored(player, pos)) {
return VisitResult_DeadEnd;
}
// Look if found what was required.
if (Map.CheckMask(pos, resmask)) {
if (resPos) {
*resPos = pos;
}
return VisitResult_Finished;
}
if (CanMoveToMask(pos, movemask)) { // reachable
if (terrainTraversal.Get(pos) <= maxDist) {
return VisitResult_Ok;
} else {
return VisitResult_DeadEnd;
}
} else { // unreachable
return VisitResult_DeadEnd;
}
}
/**
** Find the closest piece of terrain with the given flags.
**
** @param movemask The movement mask to reach that location.
** @param resmask Result tile mask.
** @param range Maximum distance for the search.
** @param player Only search fields explored by player
** @param startPos Map start position for the search.
**
** @param terrainPos OUT: Map position of tile.
**
** @note Movement mask can be 0xFFFFFFFF to have no effect
** Range is not circular, but square.
** Player is ignored if nil(search the entire map)
**
** @return True if wood was found.
*/
bool FindTerrainType(int movemask, int resmask, int range,
const CPlayer &player, const Vec2i &startPos, Vec2i *terrainPos)
{
TerrainTraversal terrainTraversal;
terrainTraversal.SetSize(Map.Info.MapWidth, Map.Info.MapHeight);
terrainTraversal.Init();
terrainTraversal.PushPos(startPos);
TerrainFinder terrainFinder(player, range, movemask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit), resmask, terrainPos);
return terrainTraversal.Run(terrainFinder);
}
template <const bool NEARLOCATION>
class BestDepotFinder
{
inline void operator()(CUnit *const dest) {
/* Only resource depots */
if (dest->Type->CanStore[resource]
&& dest->IsAliveOnMap()
&& dest->CurrentAction() != UnitActionBuilt) {
// Unit in range?
if (NEARLOCATION) {
int d = dest->MapDistanceTo(u_near.loc);
//
// Take this depot?
//
if (d <= range && d < best_dist) {
best_depot = dest;
best_dist = d;
}
} else {
int d;
const CUnit *worker = u_near.worker;
if (!worker->Container) {
d = worker->MapDistanceTo(*dest);
} else {
d = worker->Container->MapDistanceTo(*dest);
}
// Use Circle, not square :)
if (d > range) {
return;
}
// calck real travel distance
if (!worker->Container) {
d = UnitReachable(*worker, *dest, 1);
}
//
// Take this depot?
//
if (d && d < best_dist) {
best_depot = dest;
best_dist = d;
}
}
}
}
public:
BestDepotFinder(const CUnit &w, int res, int ran) :
resource(res), range(ran),
best_dist(INT_MAX), best_depot(0) {
u_near.worker = &w;
}
BestDepotFinder(const Vec2i &pos, int res, int ran) :
resource(res), range(ran),
best_dist(INT_MAX), best_depot(0) {
u_near.loc = pos;
}
template <typename ITERATOR>
CUnit *Find(ITERATOR begin, ITERATOR end) {
for (ITERATOR it = begin; it != end; ++it) {
this->operator()(*it);
}
return best_depot;
}
CUnit *Find(CUnitCache &cache) {
cache.for_each(*this);
return best_depot;
}
private:
struct {
const CUnit *worker;
Vec2i loc;
} u_near;
const int resource;
const int range;
int best_dist;
public:
CUnit *best_depot;
};
CUnit *FindDepositNearLoc(CPlayer &p, const Vec2i &pos, int range, int resource)
{
BestDepotFinder<true> finder(pos, resource, range);
CUnit *depot = finder.Find(p.UnitBegin(), p.UnitEnd());
if (!depot) {
for (int i = 0; i < PlayerMax; ++i) {
if (i != p.Index &&
Players[i].IsAllied(p) &&
p.IsAllied(Players[i])) {
finder.Find(Players[i].UnitBegin(), Players[i].UnitEnd());
}
}
depot = finder.best_depot;
}
return depot;
}
class ResourceUnitFinder
{
public:
ResourceUnitFinder(const CUnit &worker, const CUnit *deposit, int resource, int maxRange, bool check_usage, CUnit **resultMine) :
worker(worker),
resinfo(*worker.Type->ResInfo[resource]),
deposit(deposit),
movemask(worker.Type->MovementMask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)),
resource(resource),
maxRange(maxRange),
check_usage(check_usage),
res_finder(resource, 1),
resultMine(resultMine) {
bestCost.SetToMax();
*resultMine = NULL;
}
VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from);
private:
bool MineIsUsable(const CUnit &mine) const;
struct ResourceUnitFinder_Cost {
public:
void SetFrom(const CUnit &mine, const CUnit *deposit, bool check_usage);
bool operator < (const ResourceUnitFinder_Cost &rhs) const {
if (assigned != rhs.assigned) {
return assigned < rhs.assigned;
} else if (waiting != rhs.waiting) {
return waiting < rhs.waiting;
} else {
return distance < rhs.distance;
}
}
void SetToMax() { assigned = waiting = distance = UINT_MAX; }
bool IsMin() const { return assigned == 0 && waiting == 0 && distance == 0; }
public:
unsigned int assigned;
unsigned int waiting;
unsigned int distance;
};
private:
const CUnit &worker;
const ResourceInfo &resinfo;
const CUnit *deposit;
unsigned int movemask;
int resource;
int maxRange;
bool check_usage;
CResourceFinder res_finder;
ResourceUnitFinder_Cost bestCost;
CUnit **resultMine;
};
bool ResourceUnitFinder::MineIsUsable(const CUnit &mine) const
{
return mine.Type->CanHarvest && mine.ResourcesHeld
&& (resinfo.HarvestFromOutside
|| mine.Player->Index == PlayerMax - 1
|| mine.Player == worker.Player
|| (worker.IsAllied(mine) && mine.IsAllied(worker)));
}
void ResourceUnitFinder::ResourceUnitFinder_Cost::SetFrom(const CUnit &mine, const CUnit *deposit, bool check_usage)
{
distance = deposit ? mine.MapDistanceTo(*deposit) : 0;
if (check_usage) {
assigned = mine.Resource.Assigned - mine.Type->MaxOnBoard;
waiting = GetNumWaitingWorkers(mine);
} else {
assigned = 0;
waiting = 0;
}
}
VisitResult ResourceUnitFinder::Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from)
{
if (!worker.Player->AiEnabled && !Map.IsFieldExplored(*worker.Player, pos)) {
return VisitResult_DeadEnd;
}
CUnit *mine = res_finder.Find(Map.Field(pos));
if (mine && mine != *resultMine && MineIsUsable(*mine)) {
ResourceUnitFinder::ResourceUnitFinder_Cost cost;
cost.SetFrom(*mine, deposit, check_usage);
if (cost < bestCost) {
*resultMine = mine;
if (cost.IsMin()) {
return VisitResult_Finished;
}
bestCost = cost;
}
}
if (CanMoveToMask(pos, movemask)) { // reachable
if (terrainTraversal.Get(pos) < maxRange) {
return VisitResult_Ok;
} else {
return VisitResult_DeadEnd;
}
} else { // unreachable
return VisitResult_DeadEnd;
}
}
/**
** Find Resource.
**
** @param unit The unit that wants to find a resource.
** @param startUnit Find closest unit from this location
** @param range Maximum distance to the resource.
** @param resource The resource id.
**
** @note This will return an usable resource building that doesn't
** belong to the player or one of his allies.
**
** @return NoUnitP or resource unit
*/
CUnit *UnitFindResource(const CUnit &unit, const CUnit &startUnit, int range, int resource,
bool check_usage, const CUnit *deposit)
{
if (!deposit) { // Find the nearest depot
deposit = FindDepositNearLoc(*unit.Player, startUnit.tilePos, range, resource);
}
TerrainTraversal terrainTraversal;
terrainTraversal.SetSize(Map.Info.MapWidth, Map.Info.MapHeight);
terrainTraversal.Init();
terrainTraversal.PushUnitPosAndNeighboor(startUnit);
CUnit *resultMine = NULL;
ResourceUnitFinder resourceUnitFinder(unit, deposit, resource, range, check_usage, &resultMine);
terrainTraversal.Run(resourceUnitFinder);
return resultMine;
}
/**
** Find deposit. This will find a deposit for a resource
**
** @param unit The unit that wants to find a resource.
** @param x Closest to x
** @param y Closest to y
** @param range Maximum distance to the deposit.
** @param resource Resource to find deposit from.
**
** @note This will return a reachable allied depot.
**
** @return NoUnitP or deposit unit
*/
CUnit *FindDeposit(const CUnit &unit, int range, int resource)
{
BestDepotFinder<false> finder(unit, resource, range);
CUnit *depot = finder.Find(unit.Player->UnitBegin(), unit.Player->UnitEnd());
if (!depot) {
for (int i = 0; i < PlayerMax; ++i) {
if (i != unit.Player->Index &&
Players[i].IsAllied(*unit.Player) &&
unit.Player->IsAllied(Players[i])) {
finder.Find(Players[i].UnitBegin(), Players[i].UnitEnd());
}
}
depot = finder.best_depot;
}
return depot;
}
/**
** Find the next idle worker
**
** @param player Player's units to search through
** @param last Previous idle worker selected
**
** @return NoUnitP or next idle worker
*/
CUnit *FindIdleWorker(const CPlayer &player, const CUnit *last)
{
CUnit *FirstUnitFound = NoUnitP;
int SelectNextUnit = (last == NoUnitP) ? 1 : 0;
const int nunits = player.GetUnitCount();
for (int i = 0; i < nunits; ++i) {
CUnit &unit = player.GetUnit(i);
if (unit.Type->Harvester && unit.Type->ResInfo && !unit.Removed) {
if (unit.CurrentAction() == UnitActionStill) {
if (SelectNextUnit && !IsOnlySelected(unit)) {
return &unit;
}
if (FirstUnitFound == NULL) {
FirstUnitFound = &unit;
}
}
}
if (&unit == last) {
SelectNextUnit = 1;
}
}
if (FirstUnitFound != NoUnitP && !IsOnlySelected(*FirstUnitFound)) {
return FirstUnitFound;
}
return NoUnitP;
}
/*----------------------------------------------------------------------------
-- Select units
----------------------------------------------------------------------------*/

View file

@ -37,7 +37,7 @@
#include "stratagus.h"
#include "unit.h"
#include "unit_find.h"
#include "actions.h"
#include "map.h"
@ -45,8 +45,395 @@
#include "pathfinder.h"
#include "player.h"
#include "spells.h"
#include "unit.h"
#include "unittype.h"
/*----------------------------------------------------------------------------
-- Finding units
----------------------------------------------------------------------------*/
class TerrainFinder
{
public:
TerrainFinder(const CPlayer &player, int maxDist, int movemask, int resmask, Vec2i *resPos) :
player(player), maxDist(maxDist), movemask(movemask), resmask(resmask), resPos(resPos) {}
VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from);
private:
const CPlayer &player;
int maxDist;
int movemask;
int resmask;
Vec2i *resPos;
};
VisitResult TerrainFinder::Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from)
{
if (!player.AiEnabled && !Map.IsFieldExplored(player, pos)) {
return VisitResult_DeadEnd;
}
// Look if found what was required.
if (Map.CheckMask(pos, resmask)) {
if (resPos) {
*resPos = pos;
}
return VisitResult_Finished;
}
if (CanMoveToMask(pos, movemask)) { // reachable
if (terrainTraversal.Get(pos) <= maxDist) {
return VisitResult_Ok;
} else {
return VisitResult_DeadEnd;
}
} else { // unreachable
return VisitResult_DeadEnd;
}
}
/**
** Find the closest piece of terrain with the given flags.
**
** @param movemask The movement mask to reach that location.
** @param resmask Result tile mask.
** @param range Maximum distance for the search.
** @param player Only search fields explored by player
** @param startPos Map start position for the search.
**
** @param terrainPos OUT: Map position of tile.
**
** @note Movement mask can be 0xFFFFFFFF to have no effect
** Range is not circular, but square.
** Player is ignored if nil(search the entire map)
**
** @return True if wood was found.
*/
bool FindTerrainType(int movemask, int resmask, int range,
const CPlayer &player, const Vec2i &startPos, Vec2i *terrainPos)
{
TerrainTraversal terrainTraversal;
terrainTraversal.SetSize(Map.Info.MapWidth, Map.Info.MapHeight);
terrainTraversal.Init();
terrainTraversal.PushPos(startPos);
TerrainFinder terrainFinder(player, range, movemask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit), resmask, terrainPos);
return terrainTraversal.Run(terrainFinder);
}
template <const bool NEARLOCATION>
class BestDepotFinder
{
inline void operator()(CUnit *const dest) {
/* Only resource depots */
if (dest->Type->CanStore[resource]
&& dest->IsAliveOnMap()
&& dest->CurrentAction() != UnitActionBuilt) {
// Unit in range?
if (NEARLOCATION) {
int d = dest->MapDistanceTo(u_near.loc);
//
// Take this depot?
//
if (d <= range && d < best_dist) {
best_depot = dest;
best_dist = d;
}
} else {
int d;
const CUnit *worker = u_near.worker;
if (!worker->Container) {
d = worker->MapDistanceTo(*dest);
} else {
d = worker->Container->MapDistanceTo(*dest);
}
// Use Circle, not square :)
if (d > range) {
return;
}
// calck real travel distance
if (!worker->Container) {
d = UnitReachable(*worker, *dest, 1);
}
//
// Take this depot?
//
if (d && d < best_dist) {
best_depot = dest;
best_dist = d;
}
}
}
}
public:
BestDepotFinder(const CUnit &w, int res, int ran) :
resource(res), range(ran),
best_dist(INT_MAX), best_depot(0) {
u_near.worker = &w;
}
BestDepotFinder(const Vec2i &pos, int res, int ran) :
resource(res), range(ran),
best_dist(INT_MAX), best_depot(0) {
u_near.loc = pos;
}
template <typename ITERATOR>
CUnit *Find(ITERATOR begin, ITERATOR end) {
for (ITERATOR it = begin; it != end; ++it) {
this->operator()(*it);
}
return best_depot;
}
CUnit *Find(CUnitCache &cache) {
cache.for_each(*this);
return best_depot;
}
private:
struct {
const CUnit *worker;
Vec2i loc;
} u_near;
const int resource;
const int range;
int best_dist;
public:
CUnit *best_depot;
};
CUnit *FindDepositNearLoc(CPlayer &p, const Vec2i &pos, int range, int resource)
{
BestDepotFinder<true> finder(pos, resource, range);
CUnit *depot = finder.Find(p.UnitBegin(), p.UnitEnd());
if (!depot) {
for (int i = 0; i < PlayerMax; ++i) {
if (i != p.Index &&
Players[i].IsAllied(p) &&
p.IsAllied(Players[i])) {
finder.Find(Players[i].UnitBegin(), Players[i].UnitEnd());
}
}
depot = finder.best_depot;
}
return depot;
}
class ResourceUnitFinder
{
public:
ResourceUnitFinder(const CUnit &worker, const CUnit *deposit, int resource, int maxRange, bool check_usage, CUnit **resultMine) :
worker(worker),
resinfo(*worker.Type->ResInfo[resource]),
deposit(deposit),
movemask(worker.Type->MovementMask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)),
resource(resource),
maxRange(maxRange),
check_usage(check_usage),
res_finder(resource, 1),
resultMine(resultMine) {
bestCost.SetToMax();
*resultMine = NULL;
}
VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from);
private:
bool MineIsUsable(const CUnit &mine) const;
struct ResourceUnitFinder_Cost {
public:
void SetFrom(const CUnit &mine, const CUnit *deposit, bool check_usage);
bool operator < (const ResourceUnitFinder_Cost &rhs) const {
if (assigned != rhs.assigned) {
return assigned < rhs.assigned;
} else if (waiting != rhs.waiting) {
return waiting < rhs.waiting;
} else {
return distance < rhs.distance;
}
}
void SetToMax() { assigned = waiting = distance = UINT_MAX; }
bool IsMin() const { return assigned == 0 && waiting == 0 && distance == 0; }
public:
unsigned int assigned;
unsigned int waiting;
unsigned int distance;
};
private:
const CUnit &worker;
const ResourceInfo &resinfo;
const CUnit *deposit;
unsigned int movemask;
int resource;
int maxRange;
bool check_usage;
CResourceFinder res_finder;
ResourceUnitFinder_Cost bestCost;
CUnit **resultMine;
};
bool ResourceUnitFinder::MineIsUsable(const CUnit &mine) const
{
return mine.Type->CanHarvest && mine.ResourcesHeld
&& (resinfo.HarvestFromOutside
|| mine.Player->Index == PlayerMax - 1
|| mine.Player == worker.Player
|| (worker.IsAllied(mine) && mine.IsAllied(worker)));
}
void ResourceUnitFinder::ResourceUnitFinder_Cost::SetFrom(const CUnit &mine, const CUnit *deposit, bool check_usage)
{
distance = deposit ? mine.MapDistanceTo(*deposit) : 0;
if (check_usage) {
assigned = mine.Resource.Assigned - mine.Type->MaxOnBoard;
waiting = GetNumWaitingWorkers(mine);
} else {
assigned = 0;
waiting = 0;
}
}
VisitResult ResourceUnitFinder::Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from)
{
if (!worker.Player->AiEnabled && !Map.IsFieldExplored(*worker.Player, pos)) {
return VisitResult_DeadEnd;
}
CUnit *mine = res_finder.Find(Map.Field(pos));
if (mine && mine != *resultMine && MineIsUsable(*mine)) {
ResourceUnitFinder::ResourceUnitFinder_Cost cost;
cost.SetFrom(*mine, deposit, check_usage);
if (cost < bestCost) {
*resultMine = mine;
if (cost.IsMin()) {
return VisitResult_Finished;
}
bestCost = cost;
}
}
if (CanMoveToMask(pos, movemask)) { // reachable
if (terrainTraversal.Get(pos) < maxRange) {
return VisitResult_Ok;
} else {
return VisitResult_DeadEnd;
}
} else { // unreachable
return VisitResult_DeadEnd;
}
}
/**
** Find Resource.
**
** @param unit The unit that wants to find a resource.
** @param startUnit Find closest unit from this location
** @param range Maximum distance to the resource.
** @param resource The resource id.
**
** @note This will return an usable resource building that doesn't
** belong to the player or one of his allies.
**
** @return NoUnitP or resource unit
*/
CUnit *UnitFindResource(const CUnit &unit, const CUnit &startUnit, int range, int resource,
bool check_usage, const CUnit *deposit)
{
if (!deposit) { // Find the nearest depot
deposit = FindDepositNearLoc(*unit.Player, startUnit.tilePos, range, resource);
}
TerrainTraversal terrainTraversal;
terrainTraversal.SetSize(Map.Info.MapWidth, Map.Info.MapHeight);
terrainTraversal.Init();
terrainTraversal.PushUnitPosAndNeighboor(startUnit);
CUnit *resultMine = NULL;
ResourceUnitFinder resourceUnitFinder(unit, deposit, resource, range, check_usage, &resultMine);
terrainTraversal.Run(resourceUnitFinder);
return resultMine;
}
/**
** Find deposit. This will find a deposit for a resource
**
** @param unit The unit that wants to find a resource.
** @param x Closest to x
** @param y Closest to y
** @param range Maximum distance to the deposit.
** @param resource Resource to find deposit from.
**
** @note This will return a reachable allied depot.
**
** @return NoUnitP or deposit unit
*/
CUnit *FindDeposit(const CUnit &unit, int range, int resource)
{
BestDepotFinder<false> finder(unit, resource, range);
CUnit *depot = finder.Find(unit.Player->UnitBegin(), unit.Player->UnitEnd());
if (!depot) {
for (int i = 0; i < PlayerMax; ++i) {
if (i != unit.Player->Index &&
Players[i].IsAllied(*unit.Player) &&
unit.Player->IsAllied(Players[i])) {
finder.Find(Players[i].UnitBegin(), Players[i].UnitEnd());
}
}
depot = finder.best_depot;
}
return depot;
}
/**
** Find the next idle worker
**
** @param player Player's units to search through
** @param last Previous idle worker selected
**
** @return NoUnitP or next idle worker
*/
CUnit *FindIdleWorker(const CPlayer &player, const CUnit *last)
{
CUnit *FirstUnitFound = NoUnitP;
int SelectNextUnit = (last == NoUnitP) ? 1 : 0;
const int nunits = player.GetUnitCount();
for (int i = 0; i < nunits; ++i) {
CUnit &unit = player.GetUnit(i);
if (unit.Type->Harvester && unit.Type->ResInfo && !unit.Removed) {
if (unit.CurrentAction() == UnitActionStill) {
if (SelectNextUnit && !IsOnlySelected(unit)) {
return &unit;
}
if (FirstUnitFound == NULL) {
FirstUnitFound = &unit;
}
}
}
if (&unit == last) {
SelectNextUnit = 1;
}
}
if (FirstUnitFound != NoUnitP && !IsOnlySelected(*FirstUnitFound)) {
return FirstUnitFound;
}
return NoUnitP;
}
/**
** Find all units of type.
**

View file

@ -50,6 +50,7 @@
#include "player.h"
#include "script.h"
#include "unit.h"
#include "unit_find.h"
#include "unittype.h"
#include "util.h"