00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "constructunitcommand.h"
00023
00024 #include "../vehicle.h"
00025 #include "../mapfield.h"
00026 #include "../gamemap.h"
00027 #include "../viewcalculation.h"
00028 #include "../spfst.h"
00029 #include "../mapdisplayinterface.h"
00030 #include "action-registry.h"
00031 #include "../itemrepository.h"
00032 #include "../containercontrols.h"
00033 #include "consumeresource.h"
00034 #include "spawnunit.h"
00035 #include "changeunitmovement.h"
00036 #include "consumeammo.h"
00037 #include "changeunitproperty.h"
00038 #include "viewregistration.h"
00039
00040
00041 bool ConstructUnitCommand :: externalConstructionAvail ( const ContainerBase* eht )
00042 {
00043 const Vehicle* vehicle = dynamic_cast<const Vehicle*>(eht);
00044 if ( !eht )
00045 return false;
00046
00047 MapField* fld = vehicle->getMap()->getField ( vehicle->getPosition() );
00048 if ( fld && fld->vehicle == vehicle )
00049 if (vehicle->getOwner() == vehicle->getMap()->actplayer )
00050 if ( vehicle->typ->vehiclesBuildable.size() )
00051 if ( !vehicle->attacked )
00052 return true;
00053 return false;
00054 }
00055
00056 bool ConstructUnitCommand :: internalConstructionAvail( const ContainerBase* eht )
00057 {
00058 if ( eht->getOwner() == eht->getMap()->actplayer )
00059 if ( eht->vehiclesLoaded() < eht->baseType->maxLoadableUnits )
00060 return eht->baseType->hasFunction( ContainerBaseType::InternalVehicleProduction );
00061
00062 return false;
00063 }
00064
00065 bool ConstructUnitCommand :: avail ( const ContainerBase* eht )
00066 {
00067 return internalConstructionAvail(eht) || externalConstructionAvail(eht);
00068 }
00069
00070
00071 ConstructUnitCommand :: ConstructUnitCommand ( ContainerBase* container )
00072 : ContainerCommand ( container ), mode( undefined ), vehicleTypeID(-1), newUnitID(-1)
00073 {
00074
00075 }
00076
00077
00078
00079 ConstructUnitCommand::Lack ConstructUnitCommand::unitProductionPrerequisites( const VehicleType* type ) const
00080 {
00081 int l = 0;
00082
00083 if ( mode == internal ) {
00084 Resources cost = getContainer()->getProductionCost( type );
00085 for ( int r = 0; r < resourceTypeNum; r++ )
00086 if ( getContainer()->getAvailableResource( cost.resource(r), r ) < cost.resource(r) )
00087 l |= 1 << r;
00088
00089 if ( !type->techDependency.available( getMap()->getPlayer(getContainer()).research ) && getMap()->getgameparameter( cgp_produceOnlyResearchedStuffInternally ) )
00090 l |= Lack::Research;
00091
00092 if ( !getContainer()->vehicleUnloadable( type ) && !getContainer()->baseType->hasFunction( ContainerBaseType::ProduceNonLeavableUnits ))
00093 l |= Lack::Unloadability;
00094 } else
00095 if ( mode == external ) {
00096
00097 if ( !type->techDependency.available( getMap()->getPlayer(getContainer()).research ) && getMap()->getgameparameter( cgp_produceOnlyResearchedStuffExternally ) )
00098 l |= Lack::Research;
00099
00100 const Vehicle* veh = dynamic_cast<const Vehicle*>(getContainer());
00101 if ( veh->getMovement() < veh->maxMovement() * veh->typ->unitConstructionMoveCostPercentage / 100 )
00102 l |= Lack::Movement;
00103
00104 Resources cost = veh->getExternalVehicleConstructionCost( type );
00105 for ( int r = 0; r < resourceTypeNum; r++ )
00106 if ( getContainer()->getAvailableResource( cost.resource(r), r ) < cost.resource(r) )
00107 l |= 1 << r;
00108
00109 }
00110
00111 return Lack(l);
00112 }
00113
00114 ConstructUnitCommand::Producables ConstructUnitCommand :: getProduceableVehicles( )
00115 {
00116 Producables entries;
00117 if ( mode == internal ) {
00118 const ContainerBase::Production& prod = getContainer()->getProduction();
00119 for ( ContainerBase::Production::const_iterator i = prod.begin(); i != prod.end(); ++i )
00120 entries.push_back ( ProductionEntry ( *i, getContainer()->getProductionCost( *i ), unitProductionPrerequisites( *i ) ));
00121 } else
00122 if ( mode == external ) {
00123 Vehicle* veh = dynamic_cast<Vehicle*>(getContainer());
00124 if ( veh ) {
00125 ContainerConstControls cc ( getContainer() );
00126 for ( int i = 0; i < veh->typ->vehiclesBuildable.size(); i++ )
00127 for ( int j = veh->typ->vehiclesBuildable[i].from; j <= veh->typ->vehiclesBuildable[i].to; j++ )
00128 if ( veh->getMap()->getgameparameter(cgp_forbid_unitunit_construction) == 0 || veh->getMap()->unitProduction.check(j) ) {
00129 VehicleType* v = veh->getMap()->getvehicletype_byid ( j );
00130 if ( v )
00131 entries.push_back ( ProductionEntry ( v, veh->getExternalVehicleConstructionCost( v ), unitProductionPrerequisites( v ) ));
00132 }
00133 }
00134 }
00135 return entries;
00136 }
00137
00138 void ConstructUnitCommand :: fieldChecker( const MapCoordinate& pos )
00139 {
00140 MapField* fld = getMap()->getField(pos);
00141 if ( !fld )
00142 return;
00143
00144 if ( fld->vehicle || fld->building )
00145 return;
00146
00147 VehicleType* vt = getContainer()->getMap()->getvehicletype_byid( vehicleTypeID );
00148
00149 if ( fieldvisiblenow( getMap()->getField(pos)) && vt ) {
00150 Vehicle* veh = dynamic_cast<Vehicle*>(getContainer() );
00151 if ( veh && veh->vehicleconstructable( vt, pos.x, pos.y ))
00152 unitsConstructable[pos].push_back(vehicleTypeID);
00153 }
00154 }
00155
00156
00157 vector<MapCoordinate> ConstructUnitCommand::getFields()
00158 {
00159 vector<MapCoordinate> fields;
00160 Vehicle* veh = dynamic_cast<Vehicle*>(getContainer() );
00161 if ( vehicleTypeID > 0 && veh ) {
00162 circularFieldIterator( veh->getMap(), veh->getPosition(), veh->typ->unitConstructionMaxDistance, veh->typ->unitConstructionMinDistance, FieldIterationFunctor( this, &ConstructUnitCommand::fieldChecker ) );
00163 for ( map<MapCoordinate,vector<int> >::const_iterator i = unitsConstructable.begin(); i != unitsConstructable.end(); ++i )
00164 fields.push_back ( i->first );
00165 }
00166
00167 return fields;
00168 }
00169
00170 bool ConstructUnitCommand :: isFieldUsable( const MapCoordinate& pos )
00171 {
00172 vector<MapCoordinate> fields = getFields();
00173 return find( fields.begin(), fields.end(), pos ) != fields.end() ;
00174 }
00175
00176
00177 void ConstructUnitCommand :: setTargetPosition( const MapCoordinate& pos )
00178 {
00179 this->target = pos;
00180 MapField* fld = getMap()->getField(target);
00181
00182 if( !fld )
00183 throw ActionResult(21002);
00184
00185 if ( mode != undefined && vehicleTypeID > 0 )
00186 setState( SetUp );
00187
00188 }
00189
00190
00191
00192 ActionResult ConstructUnitCommand::go ( const Context& context )
00193 {
00194 if ( getState() != SetUp )
00195 return ActionResult(22000);
00196
00197
00198 Producables prods = getProduceableVehicles();
00199
00200 const VehicleType* vehicleType = NULL;
00201 Resources cost;
00202 for ( Producables::const_iterator i = prods.begin(); i != prods.end(); ++i ) {
00203 if ( i->type->id == vehicleTypeID ) {
00204 if ( i->prerequisites.ok() ) {
00205 vehicleType = i->type;
00206 cost = i->cost;
00207 } else
00208 return ActionResult( 21702 );
00209 }
00210 }
00211 if ( !vehicleType )
00212 return ActionResult( 21701 );
00213
00214
00215 if ( mode == external && !isFieldUsable( target ))
00216 return ActionResult( 21703 );
00217
00218
00219 int height;
00220 for ( int j = 0; j < 8; j++ ) {
00221 int a = int( getContainer()->getHeight() ) << j;
00222 int b = int( getContainer()->getHeight() ) >> j;
00223 if ( vehicleType->height & a ) {
00224 height = a;
00225 break;
00226 }
00227 if ( vehicleType->height & b ) {
00228 height = b;
00229 break;
00230 }
00231 }
00232 MapCoordinate3D position ( target, height );
00233
00234 if ( mode == external ) {
00235
00236 SpawnUnit* spawnUnit = new SpawnUnit(getMap(), position, vehicleTypeID, getContainer()->getOwner() );
00237 ActionResult res = spawnUnit->execute( context );
00238 if ( !res.successful() )
00239 return res;
00240
00241 spawnUnit->getUnit()->attacked = true;
00242
00243 newUnitID = spawnUnit->getUnit()->networkid;
00244
00245
00246 if ( !res.successful() )
00247 return res;
00248
00249 Vehicle* veh = dynamic_cast<Vehicle*>(getContainer() );
00250 if ( veh ) {
00251 res = (new ChangeUnitMovement( veh, veh->maxMovement() * veh->typ->unitConstructionMoveCostPercentage/100, true ))->execute( context );
00252 if ( !res.successful() )
00253 return res;
00254 }
00255 } else {
00256 SpawnUnit* spawnUnit = new SpawnUnit(getMap(), getContainer(), vehicleTypeID );
00257 ActionResult res = spawnUnit->execute( context );
00258 if ( !res.successful() )
00259 return res;
00260
00261 spawnUnit->getUnit()->attacked = true;
00262
00263 newUnitID = spawnUnit->getUnit()->networkid;
00264
00265 if ( getMap()->getgameparameter(cgp_bi3_training) >= 1 ) {
00266 int cnt = 0;
00267
00268 for ( Player::BuildingList::iterator bi = getMap()->player[getMap()->actplayer].buildingList.begin(); bi != getMap()->player[getMap()->actplayer].buildingList.end(); bi++ )
00269 if ( (*bi)->typ->hasFunction( ContainerBaseType::TrainingCenter ) )
00270 cnt++;
00271
00272 Vehicle* vehicle = spawnUnit->getUnit();
00273 vehicle->experience += cnt * getMap()->getgameparameter(cgp_bi3_training);
00274 if ( vehicle->experience > maxunitexperience )
00275 vehicle->experience = maxunitexperience;
00276 }
00277 }
00278
00279 ActionResult res = (new ConsumeResource(getContainer(), cost ))->execute( context );
00280
00281 if ( !res.successful() )
00282 return res;
00283
00284 Vehicle* veh = dynamic_cast<Vehicle*>(getContainer());
00285 if ( veh )
00286 res = (new ChangeUnitProperty( veh, ChangeUnitProperty::AttackedFlag, 1 ))->execute(context);
00287
00288 if ( context.display )
00289 context.display->repaintDisplay();
00290
00291 if ( res.successful() )
00292 setState( Finished );
00293 else
00294 setState( Failed );
00295
00296 return res;
00297 }
00298
00299 Vehicle* ConstructUnitCommand :: getProducedUnit()
00300 {
00301 if ( newUnitID > 0 )
00302 return getMap()->getUnit( newUnitID );
00303 else
00304 return NULL;
00305 }
00306
00307 void ConstructUnitCommand :: setMode( Mode mode )
00308 {
00309 this->mode = mode;
00310 if ( mode == internal )
00311 target = getContainer()->getPosition();
00312 };
00313
00314
00315 static const int ConstructUnitCommandVersion = 1;
00316
00317 void ConstructUnitCommand :: readData ( tnstream& stream )
00318 {
00319 ContainerCommand::readData( stream );
00320 int version = stream.readInt();
00321 if ( version > ConstructUnitCommandVersion )
00322 throw tinvalidversion ( "ConstructUnitCommand", ConstructUnitCommandVersion, version );
00323 target.read( stream );
00324 vehicleTypeID = stream.readInt();
00325 mode = (Mode) stream.readInt();
00326 newUnitID = stream.readInt();
00327 }
00328
00329 void ConstructUnitCommand :: writeData ( tnstream& stream ) const
00330 {
00331 ContainerCommand::writeData( stream );
00332 stream.writeInt( ConstructUnitCommandVersion );
00333 target.write( stream );
00334 stream.writeInt( vehicleTypeID );
00335 stream.writeInt( mode );
00336 stream.writeInt( newUnitID );
00337 }
00338
00339 void ConstructUnitCommand :: setVehicleType( const VehicleType* type )
00340 {
00341 vehicleTypeID = type->id;
00342
00343 VehicleType* vt = getMap()->getvehicletype_byid( vehicleTypeID );
00344 if( !vt )
00345 throw ActionResult(21701);
00346
00347 if ( mode != undefined && target.valid())
00348 setState( SetUp );
00349
00350 }
00351
00352
00353 ASCString ConstructUnitCommand :: getCommandString() const
00354 {
00355 ASCString c;
00356 c.format("constructUnit ( map, %d, asc.MapCoordinate( %d, %d), %d )", getContainerID(), target.x, target.y, vehicleTypeID );
00357 return c;
00358 }
00359
00360 GameActionID ConstructUnitCommand::getID() const
00361 {
00362 return ActionRegistry::ConstructUnitCommand;
00363 }
00364
00365 ASCString ConstructUnitCommand::getDescription() const
00366 {
00367 ASCString s = "Construct ";
00368
00369 const VehicleType* vt = getMap()->getvehicletype_byid( vehicleTypeID );
00370 if ( vt )
00371 s += vt->name;
00372 else
00373 s += "unit";
00374
00375 if ( getContainer() ) {
00376 s += " by " + getContainer()->getName();
00377 }
00378 s += " at " + target.toString();
00379 return s;
00380 }
00381
00382 namespace
00383 {
00384 const bool r1 = registerAction<ConstructUnitCommand> ( ActionRegistry::ConstructUnitCommand );
00385 }
00386