Advanced Strategic Command
moveunitcommand.cpp
Go to the documentation of this file.
1 /*
2  This file is part of Advanced Strategic Command; http://www.asc-hq.de
3  Copyright (C) 1994-2010 Martin Bickel and Marc Schellenberger
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; see the file COPYING. If not, write to the
17  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  Boston, MA 02111-1307 USA
19 */
20 
21 #include <iostream>
22 
23 #include "moveunitcommand.h"
24 
25 #include "../vehicle.h"
26 #include "../mapfield.h"
27 #include "../gamemap.h"
28 #include "../viewcalculation.h"
29 #include "../spfst.h"
30 #include "../mapdisplayinterface.h"
31 #include "vehicleattack.h"
32 #include "action-registry.h"
33 #include "moveunit.h"
34 
35 
37 {
38 
39  if ( eht )
40  return eht->canMove();
41 
42  return false;
43 }
44 
45 
47 {
48  if ( veh )
49  if ( veh->getHeightChange( +1 ))
50  return true;
51  return false;
52 }
53 
55 {
56  if ( veh )
57  if ( veh->getHeightChange( -1 ))
58  return true;
59 
60  return false;
61 }
62 
63 
65 {
66  MapField* fld = getMap()->getField(pos);
67  if ( !fld )
68  return false;
69 
70  if ( !getUnit() )
71  return false;
72 
73  if ( pos == getUnit()->getPosition() )
74  return false;
75 
76  for ( int h = 0; h < 8; ++h )
77  if ( getUnit()->typ->height & (1<<h))
78  if ( fieldAccessible( fld, getUnit(), 1 << h ) == 2 )
79  return true;
80 
81  return false;
82 
83 }
84 
85 void MoveUnitCommand::changeCoordinates( const MapCoodinateVector& delta )
86 {
87  destination += delta;
88 }
89 
90 MoveUnitCommand::MoveUnitCommand( GameMap* map ) : UnitCommand( map ) , limiter(NULL)
91 {
92  map->sigCoordinateShift.connect( sigc::mem_fun( *this, &MoveUnitCommand::changeCoordinates ));
93 }
94 
96 {
97  delete limiter;
98 }
99 
100 MoveUnitCommand :: MoveUnitCommand ( Vehicle* unit )
101  : UnitCommand ( unit ), flags(0), verticalDirection(0), multiTurnMovement(false), limiter(NULL)
102 {
103  if ( unit )
104  unit->getMap()->sigCoordinateShift.connect( sigc::mem_fun( *this, &MoveUnitCommand::changeCoordinates ));
105 }
106 
107 
109  bool allow_Height_Change;
110  public:
111  HeightChangeLimitation ( bool allow_Height_Change_ ) : allow_Height_Change ( allow_Height_Change_ ) {};
112  virtual bool allowHeightChange() { return allow_Height_Change; };
113  virtual bool allowMovement() { return true; };
114  virtual bool allowEnteringContainer() { return true; };
115  virtual bool allowLeavingContainer() { return true; };
116  virtual bool allowDocking() { return true; };
117 };
118 
120  bool simpleMode;
121  int hcNum;
122  public:
123  MovementLimitation ( bool simpleMode_ ) : simpleMode ( simpleMode_ ), hcNum(0) {};
124  virtual bool allowHeightChange() { ++hcNum; if ( simpleMode) return hcNum <= 1 ; else return true; };
125  virtual bool allowMovement() { return !simpleMode; };
126  virtual bool allowEnteringContainer() { return true; };
127  virtual bool allowLeavingContainer() { return true; };
128  virtual bool allowDocking() { return true; };
129 };
130 
131 
132 class PathFinder : public AStar3D {
133  public:
134  PathFinder ( Vehicle* veh, int maxDistance ) : AStar3D(veh->getMap(), veh, false, maxDistance ) {};
135 
140  void getMovementFields ( set<MapCoordinate3D>& reachableFields, set<MapCoordinate3D>& reachableFieldsIndirect, int height );
141 };
142 
143 
144 void PathFinder :: getMovementFields ( set<MapCoordinate3D>& reachableFields, set<MapCoordinate3D>& reachableFieldsIndirect, int height )
145 {
146  Path dummy;
147  findPath ( dummy, MapCoordinate3D(-1, -1, veh->height) ); //this field does not exist...
148 
149  int unitHeight = veh->getPosition().getNumericalHeight();
150  if ( !this->actmap->getField ( veh->getPosition())->unitHere ( veh ))
151  unitHeight = -1;
152 
153  // there are different entries for the same x/y coordinate but different height.
154  // Since the UI is only in xy, we need to find the height which is the easiest to reach
155  typedef multimap<MapCoordinate, Node* > Fields;
156  Fields fields;
157  int orgHeight=-1;
158  int minMovement = maxint;
159  for ( AStar3D::VisitedContainer::iterator i = visited.begin(); i != visited.end(); ++i ) {
160  AStar3D::Node node = *i;
161  if ( node.h.x != veh->getPosition().x || node.h.y != veh->getPosition().y || node.h.getNumericalHeight() != unitHeight ) {
162  int h = node.h.getNumericalHeight();
163  // if ( h == -1 )
164  // h = node.enterHeight;
165  if ( h == -1 || height == -1 || h == height ) {
166  if ( node.canStop )
167  fields.insert(make_pair(MapCoordinate(node.h), &(*i)));
168  else
169  reachableFieldsIndirect.insert( node.h );
170  }
171  }
172  if ( node.h.getNumericalHeight() >= 0 )
173  if ( node.gval < minMovement ) {
174  orgHeight = node.h.getNumericalHeight();
175  minMovement = int (node.gval);
176  }
177  }
178  for ( Fields::iterator i = fields.begin(); i != fields.end(); ) {
179  int height = i->second->h.getNumericalHeight();
180  int move = int(i->second->gval);
181  Fields::key_type key = i->first;
182  ++i;
183  while ( i != fields.end() && i->first == key ) {
184  if ( i->second->gval < move || ( i->second->gval == move && abs(i->second->h.getNumericalHeight()-orgHeight) < abs(height-orgHeight) ))
185  height = i->second->h.getNumericalHeight();
186  ++i;
187  }
188  MapCoordinate3D f;
189  f.setnum( key.x, key.y, height );
190  reachableFields.insert ( f );
191  }
192 }
193 
194 
195 ActionResult MoveUnitCommand::searchFields(int height, int capabilities)
196 {
197  Vehicle* veh = getUnit();
198  if ( !veh )
199  return ActionResult(101);
200 
201  int h;
202  if ( getMap()->getField(veh->getPosition())->unitHere(veh) )
203  h = getFirstBit(veh->height); // != height-change: true
204  else
205  h = -1; // height-change = false
206 
207  if ( height == -2 )
208  h = -1;
209 
210  if ( (capabilities | flags) & LimitVerticalDirection ) {
211  if ( getVerticalDirection() == 0 ) {
212  HeightChangeLimitation* hcl = new HeightChangeLimitation( !(capabilities & DisableHeightChange) );
213  PathFinder pf ( veh, veh->getMovement() );
214  pf.registerOperationLimiter( hcl );
215  pf.getMovementFields ( reachableFields, reachableFieldsIndirect, h );
216  limiter = hcl;
217  } else {
219  if ( !hcm )
220  fatalError ( "Inconsistent call to changeheight ");
221 
222  h = veh->getPosition().getNumericalHeight() + hcm->heightDelta;
223 
224  MovementLimitation* ml = new MovementLimitation( capabilities & ShortestHeightChange );
225  PathFinder pf ( veh, veh->getMovement() );
226  pf.registerOperationLimiter( ml );
227  pf.getMovementFields ( reachableFields, reachableFieldsIndirect, h );
228  limiter = NULL; // the unit shouldn't take any detours anyway to reach the destination,
229  // and MovementLimitation can only be used once due to its internal counter.
230  }
231  } else {
232  PathFinder pf ( veh, veh->getMovement() );
233  pf.getMovementFields ( reachableFields, reachableFieldsIndirect, -1 );
234  limiter = NULL;
235  }
236 
237  if ( reachableFields.size() == 0 )
238  return ActionResult(107);
239 
240  setState( Evaluated );
241 
242  return ActionResult(0);
243 }
244 
245 
246 
248 {
249  this->destination = destination;
250  setState( SetUp );
251 }
252 
254 {
255  if ( getState() != Evaluated )
256  searchFields();
257 
258  bool found = false;
259  for ( set<MapCoordinate3D>::iterator i = reachableFields.begin(); i != reachableFields.end(); ++i )
260  if ( destination.x == i->x && destination.y == i->y ) {
261  this->destination = *i;
262  found = true;
263  multiTurnMovement = false;
264  break;
265  }
266 
267  if ( !found ) {
268  // assuming long dist movement
269  MapField* fld = getMap()->getField( destination );
270  if ( !fld )
271  return;
272 
273  if ( fld->getContainer() )
274  this->destination.setnum( destination.x, destination.y, -1 );
275  else
276  this->destination = MapCoordinate3D( destination, getUnit()->getHeight() );
277 
278 
279  multiTurnMovement = true;
280  }
281 
282  setState( SetUp );
283 }
284 
286 {
287  if ( direct ) {
288  for ( set<MapCoordinate3D>::iterator i = reachableFields.begin(); i != reachableFields.end(); ++i )
289  if ( i->x == pos.x && i->y == pos.y )
290  return true;
291  } else {
292  for ( set<MapCoordinate3D>::iterator i = reachableFieldsIndirect.begin(); i != reachableFieldsIndirect.end(); ++i )
293  if ( i->x == pos.x && i->y == pos.y )
294  return true;
295  }
296  return false;
297 }
298 
300 {
301  AStar3D a( getMap(), getUnit(), false);
302  a.registerOperationLimiter(limiter);
303  calcPath( &a );
304 }
305 
306 
308 {
309 
310  const int maxMovement = getUnit()->getMovement();
311  path.clear();
312 
313  AStar3D::Path totalPath;
314 
315  astar->findPath ( totalPath, destination );
316  if ( totalPath.empty() ) // found no path at all
317  return;
318 
319  // trace the found path back to the furthest point we can reach this round
320  for ( const AStar3D::Node* n = astar->visited.find(destination); n != NULL; n = n->previous ) {
321  if ( ( n->gval <= maxMovement ) && n->canStop) {
322  // found!
323  astar->constructPath ( path, n );
324  break;
325  }
326  }
327 }
328 
330 {
331  return path;
332 }
333 
334 
336 {
337  if ( getState() != SetUp )
338  return ActionResult(22000);
339 
340  /*
341  searchFields();
342 
343  if ( reachableFields.find( destination ) == reachableFields.end() )
344  return ActionResult(105);
345 */
346  if ( !getUnit() )
347  return ActionResult(21001);
348 
349  if (( getUnit()->getPosition() == destination) || (getUnit()->getPosition().x == destination.x && getUnit()->getPosition().y == destination.y && destination.getNumericalHeight() == -1 ))
350  return ActionResult(22003);
351 
352  if ( !avail(getUnit()) && !descendAvail(getUnit()) && !ascendAvail(getUnit()))
353  return ActionResult(22004);
354 
355  calcPath();
356 
357  if ( path.empty() )
358  return ActionResult( 105 );
359 
360  if ( !multiTurnMovement && ( path.rbegin()->x != destination.x || path.rbegin()->y != destination.y ))
361  return ActionResult( 105 );
362 
363  int nwid = getUnit()->networkid;
364 
365  if ( context.display )
366  context.display->startAction();
367 
368  ActionResult res = (new MoveUnit(getUnit(), path, flags&NoInterrupt))->execute(context);
369 
370  if ( context.display )
371  context.display->stopAction();
372 
373 
374  int destDamage;
375  if ( getMap()->getUnit( nwid ))
376  destDamage = getUnit()->damage;
377  else
378  destDamage = 100;
379 
380  if ( res.successful() ) {
381  if ( destDamage < 100 ) {
382  if ( multiTurnMovement && getUnit()->getPosition3D() != destination )
383  setState( Run );
384  else
385  setState( Finished );
386  } else
387  setState( Finished );
388  } else
389  setState( Failed );
390 
391  return res;
392 
393 }
394 
396 {
397  verticalDirection = dir;
398  flags |= LimitVerticalDirection;
399 }
400 
401 
402 static const int moveCommandVersion = 2;
403 
405 {
406  UnitCommand::readData( stream );
407  int version = stream.readInt();
408  if ( version > moveCommandVersion )
409  throw tinvalidversion ( "MoveCommand", moveCommandVersion, version );
410  destination.read( stream );
411 
412  flags = stream.readInt( );
413  verticalDirection = stream.readInt();
414  if ( version >= 2 )
415  multiTurnMovement = stream.readInt();
416  else
417  multiTurnMovement = false;
418 }
419 
421 {
422  UnitCommand::writeData( stream );
423  stream.writeInt( moveCommandVersion );
424  destination.write( stream );
425  stream.writeInt( flags );
426  stream.writeInt( verticalDirection );
427  stream.writeInt( multiTurnMovement );
428 }
429 
431  ASCString c;
432  c.format("unitMovement ( map, %d, asc.MapCoordinate( %d, %d), %d )", getUnitID(), destination.x, destination.y, destination.getNumericalHeight() );
433  return c;
434 }
435 
437 {
439 }
440 
442 {
443  ASCString s = "Move unit";
444  if ( getUnit() ) {
445  s += " " + getUnit()->getName();
446  }
447  s += " to " + destination.toString();
448  return s;
449 }
450 
451 
453 {
454  return 50;
455 }
456 
458 {
459  return getUnit();
460 }
461 
463 {
464  if ( getState() == Finished || getState() == Run )
465  setState( SetUp );
466 
467  deleteChildren();
468 }
469 
470 vector<MapCoordinate> MoveUnitCommand::getCoordinates() const
471 {
472  vector<MapCoordinate> pos;
473  pos.push_back( destination );
474  return pos;
475 }
476 
477 
478 namespace {
479  const bool r1 = registerAction<MoveUnitCommand> ( ActionRegistry::MoveUnitCommand );
480 }
int fieldAccessible(const MapField *field, const Vehicle *vehicle, int uheight, const bool *attacked, bool ignoreVisibility)
Definition: spfst.cpp:124
void writeData(tnstream &stream) const
Definition: unitcommand.cpp:67
virtual void writeInt(int i)
Writes a 32 bit signed Integer. In the stream little-endian byte order is used and a translation is p...
Definition: basestrm.cpp:363
Definition: ndir.h:39
const AStar3D::Path & getPath()
int GameActionID
Definition: action.h:35
the command is totally done
Definition: command.h:120
int damage
Damage. 0 is no damage, when damage reaches 100 the container is destroyed.
int getUnitID() const
Definition: unitcommand.h:37
void readData(tnstream &stream)
the unit will not interrupt the movement if it runs onto a mine of into reaction fire ...
virtual vector< MapCoordinate > getCoordinates() const
this is for informational purposes, so the player can see where a Command has taken place when review...
void move(Vehicle *veh, const MapCoordinate &dest)
ASCString toString(bool coordinates=false) const
Definition: typen.cpp:314
virtual int readInt(void)
Reads a 32 bit signed Integer. In the stream little-endian byte order is used and a translation is pe...
Definition: basestrm.cpp:284
VisitedContainer visited
Definition: astar2.h:122
int getFirstBit(int zahl)
Count the number of zero bits on the LSB side of "zahl".
Definition: misc.cpp:45
virtual bool allowHeightChange()
we want only results on a certain level of height
virtual bool allowMovement()
void getMovementFields(set< MapCoordinate3D > &reachableFields, set< MapCoordinate3D > &reachableFieldsIndirect, int height)
searches for all fields that are within the range of maxDist and marks them.
State getState() const
Definition: command.h:125
int getNumericalHeight() const
Definition: typen.h:242
static const int moveCommandVersion
MapCoordinate3D getPosition() const
returns the units position
Definition: vehicle.cpp:1552
virtual bool allowLeavingContainer()
GameMap * getMap()
Definition: action.h:92
storage_t::iterator iterator
Definition: astar2.h:104
ContainerBase * getContainer()
returns a pointer to the ContainerBase of the field or NULL if there is none
Definition: mapfield.cpp:331
The interface for all kinds of IO stream.
a single field of the map
Definition: mapfield.h:26
ASCString & format(const charT *pFormat,...)
Definition: ascstring.cpp:78
void write(tnstream &stream) const
Definition: typen.h:253
The ASCString class provides an abstract way to manipulate strings.
Definition: ascstring.h:14
void read(tnstream &stream)
Definition: typen.h:254
const VehicleType::HeightChangeMethod * getHeightChange(int dir, int height=0) const
returns the method for changing the height in the specified direction, or none if there is none...
Definition: vehicle.cpp:777
int height
the current level of height ( BITMAPPED ! )
Definition: vehicle.h:118
bool findPath(const MapCoordinate3D &dest)
searches for a path from the unit's current position to dest
Definition: astar2.cpp:335
PathFinder(Vehicle *veh, int maxDistance)
ASCString getDescription() const
MapCoordinate3D getPosition3D() const
returns the units position; if inside building then Height is -1
Definition: vehicle.cpp:1557
const Node * previous
Definition: astar2.h:63
bool longDistAvailable(const MapCoordinate &pos)
ActionResult execute(const Context &context)
Definition: action.cpp:41
static bool descendAvail(Vehicle *eht)
Coordinate on the twodimensional map.
Definition: typen.h:202
virtual bool allowMovement()
virtual bool allowEnteringContainer()
HeightChangeLimitation(bool allow_Height_Change_)
bool canStop
Definition: astar2.h:68
ASCString getName() const
returns the units name or, if it does not exist, the unit type's name or description ...
Definition: vehicle.cpp:1569
void setnum(int _x, int _y, int numericalz)
Definition: typen.h:250
represents a change of a MapCoordinate
Definition: typen.h:189
virtual bool allowHeightChange()
ASCString getCommandString() const
void setState(State state)
Definition: command.cpp:44
static bool avail(Vehicle *eht)
MovementLimitation(bool simpleMode_)
void writeData(tnstream &stream) const
virtual bool allowEnteringContainer()
const Vehicle * getUnit() const
GameActionID getID() const
virtual bool allowDocking()
static bool ascendAvail(Vehicle *eht)
GameMap * actmap
Definition: astar2.h:80
bool successful() const
MapDisplayInterface * display
Definition: context.h:39
void setVerticalDirection(int dir)
defines whether we want to end up either at the same level of height (0), lower(<0), or heigher(>0)
void setDestination(const MapCoordinate &destination)
bool canMove(void) const
can the unit move from its current position (does not check neighbouring fields)
Definition: vehicle.cpp:599
the command is completed this turn, but there is still something to do next turn
Definition: command.h:119
void deleteChildren()
Definition: action.cpp:159
void registerOperationLimiter(OperationLimiter *ol)
the search can be restricted to certain operations
Definition: astar2.h:138
virtual void startAction(void)=0
ActionResult searchFields(int height=-1, int capabilities=0)
deque< PathPoint > Path
Definition: astar2.h:48
virtual void stopAction(void)=0
Coordinate on the map including height.
Definition: typen.h:238
bool constructPath(Path &path, const Node *n)
construct a path from a pointer to a visited node, return false if pointer is NULL, else true
Definition: astar2.cpp:546
A 3D path finding algorithm, based on the 2D algorithm by Amit J. Patel.
Definition: astar2.h:19
void readData(tnstream &stream)
Definition: unitcommand.cpp:52
#define maxint
Definition: typen.h:462
the unit will not change its height even if that would provide a shortcut
virtual bool allowLeavingContainer()
GameMap * getMap() const
sigc::signal< void, const MapCoodinateVector & > sigCoordinateShift
called when the map is resized and all coordinates have to be adjusted
Definition: gamemap.h:505
int getVerticalDirection() const
const Node * find(const MapCoordinate3D &pos)
Definition: astar2.h:110
Vehicle * veh
Definition: astar2.h:79
void fatalError(const ASCString &string)
bool isFieldReachable(const MapCoordinate &pos, bool direct)
checks if the field can be reached by the unit this turn Precondition: searchFields(int,int) was called
AStar3D::DistanceType gval
Definition: astar2.h:65
int networkid
a unique identification of the unit that is used everywhere in ASC (and not only the network protocol...
Definition: vehicle.h:140
int getMovement(bool checkFuel=true, bool checkRF=true) const
returns the movement points the unit has left for this turn. CheckFuel should almost always be true...
Definition: vehicle.cpp:558
ActionResult go(const Context &context)
The map. THE central structure of ASC, which holds everything not globally available together...
Definition: gamemap.h:182
MapField * getField(int x, int y)
Definition: gamemap.h:465
MapCoordinate3D h
Definition: astar2.h:64
bool operatable()
checks if the task can still be operated.