//
    // Execute
    //
    U32 Turn::Execute(const U8 *data, Player &player)
    {
      const Data *d = (Data *) data;

      // Send the move order to all of the selected objects
      for (UnitObjList::Iterator i(&player.GetSelectedList()); *i; i++)
      {
        UnitObj *unit = **i;

        // Calculate desired front vector
        Vector v(d->x - unit->Position().x, 0, d->z - unit->Position().z);
        v.Normalize();

        // Can the unit move
        if (unit->CanEverMove())
        {
          if (!unit->GetDriver()->IsBoarded())
          {
            // Convert the given task Id into a move type
            Tasks::UnitMove *task = new Tasks::UnitMove(unit);
            task->SetDir(v);
            IssueTask(d->mod, unit, task, player);
          }
        }
      }

      return (sizeof (Data));
    }
  //
  // TagCondition::TagTest
  //
  Bool TagCondition::TagTest(Team *team)
  {
    ASSERT(tag)
    U32 count = 0;

    // Test the Objects in Tag to see if there is enough of them on this team
    if (tag->tag.Alive())
    {
      // Purge dead objects out of the list
      tag->tag->list.PurgeDead();

      for (MapObjList::Iterator o(&tag->tag->list); *o; o++)
      {
        MapObj *obj = **o;
        UnitObj *unitObj = Promote::Object<UnitObjType, UnitObj>(obj);
        UnitObj *parentObj = obj->GetParent() ? Promote::Object<UnitObjType, UnitObj>(obj->GetParent()) : NULL;

        if (
          unitObj && (unitObj->GetActiveTeam() == team) || 
          parentObj && (parentObj->GetActiveTeam() == team))
        {
          count++;
        }
      }
    }

    return (tag->oper(count, tag->GetAmount()));
  }
  //
  // TagInTransport::Test
  //
  Bool TagInTransport::Test(class Team *)
  {
    // Check to see if all of the tag members are inside transports
    if (tag->tag.Alive())
    {
      // Purge dead objects out of the list
      tag->tag->list.PurgeDead();

      // Count the number of tag members who are in transports
      U32 count = 0;

      if (tag->tag->list.GetCount())
      {
        for (MapObjList::Iterator o(&tag->tag->list); *o; o++)
        {
          MapObj *obj = **o;
          UnitObj *unitObj = Promote::Object<UnitObjType, UnitObj>(obj);

          if (unitObj && unitObj->GetFlag(UnitObj::FLAG_CARGO))
          {
            count++;
          }
        }
      }
      return (tag->oper(count, tag->GetAmount()));
    }
    return (FALSE);
  }
//
// Apply
//
void ExplosionObjType::Apply(const Vector &location, UnitObj *unit, Team *team)
{
  MapObjIter::All i(NULL, MapObjIter::FilterData(location, areaOuter));
  MapObj *obj;

  while ((obj = i.Next()) != NULL)
  {
    // Is the object within the full damage area
    F32 dist2 = i.GetProximity2() - areaInner2;
    S32 deltaHp;

    //Vector dir = obj->WorldMatrix().posit - location;
    //dir.Normalize();

    if (dist2 <= 0.0f)
    {
      // Apply the full damage to this object
      deltaHp = -damage.GetAmount(obj->MapType()->GetArmourClass());
      obj->ModifyHitPoints(deltaHp, unit, team/*, &dir*/);
    }
    else
    {
      F32 mod = 1.0f - (dist2 * areaDiff2Inv);
      ASSERT(mod >= 0 && mod <= 1.0f)

      // Apply a proportional damage to this object
      //Vector v = dir * mod;
      deltaHp = -(S32) (((F32) damage.GetAmount(obj->MapType()->GetArmourClass())) * mod);
      obj->ModifyHitPoints(deltaHp, unit, team/*, &v*/);
    }

    // Apply hit modifiers
    if (ArmourClass::Lookup(damage.GetDamageId(), obj->MapType()->GetArmourClass()))
    {
      damage.GetModifiers().Apply(obj);

      // Set blind target time
      if (blindTargetTime)
      {
        UnitObj *unitObj = Promote::Object<UnitObjType, UnitObj>(obj);

        if (unitObj)
        {
          unitObj->FlushTasks();
          unitObj->StartBlindTarget(blindTargetTime);
        }
      }

      // Apply the generic effect
      StartGenericFX(obj, 0x32FBA304); // "ExplosionObj::ApplyTarget"
    }

    // Is there an action to execute
    if (action && team)
    {
      Action::Execute(team, action);
    }
  }
}
  //
  // Assign constructors to this base
  //
  void Base::AssignConstructors(const char *tagName)
  {
    processTokens = TRUE;

    // Was a tag specified ?
    if (tagName)
    {
      // Resolve the tag
      TagObj *tag = TagObj::FindTag(tagName);

      if (tag)
      {
        // Iterate the units in the tag
        for (MapObjList::Iterator i(&tag->list); *i; i++)
        {
          // Is it alive
          if (!(*i)->Alive())
          {
            continue;
          }

          // Is this a unit ?
          UnitObj *unit = Promote::Object<UnitObjType, UnitObj>(**i);
          if (!unit)
          {
            continue;
          }

          // Is it on our team ?
          if (unit->GetTeam() != GetObject().GetTeam())
          {
            continue;
          }

          // Is it a constructor ?
          if (!unit->HasProperty(0xDCDE71CD)) // "Ability::Construction"
          {
            continue;
          }

//          LOG_AI(("Assigning constructor [%d] '%s' to base '%s'", unit->Id(), unit->UnitType()->GetName(), GetName()))

          // Add this constructor to the list of idle constructors
          constructorsIdle.Append(unit);
        }
      }
      else
      {
        ERR_CONFIG(("Could not find tag '%s' when assign constructors to base '%s'", tagName, GetName()))
      }
    }
    else
    {
      // Iterate all of the units on this team
      for (NList<UnitObj>::Iterator u(&GetObject().GetTeam()->GetUnitObjects()); *u; u++)
    //
    // Data constructor
    //
    Objects::Data::Data(MapObj *o) : object(o)
    {
      type = object->MapType();
      matrix = object->WorldMatrix();
      zipped = object->GetFootInstance() ? TRUE : FALSE;

      UnitObj *unit = Promote::Object<UnitObjType, UnitObj>(object);

      if (unit)
      {
        team = unit->GetTeam();
      }
    }
    //
    // Apply
    //
    void Tag::Apply()
    {
      MapObjList list;

      list.AppendList((const MapObjList &) script.GetSquad()->GetList());

      if (clear)
      {
        // Go through all of the tags and remove any units which are in the squad
        for (NBinTree<TagObj>::Iterator t(&TagObj::allTags); *t; t++)
        {
          // Iterate the objects in this tag
          MapObjList::Iterator m(&(*t)->list);
          
          while (MapObjListNode *node = m++)
          {
            UnitObj *unit;

            // Is this a unit
            if 
            (
              node->Alive() &&
              (unit = Promote::Object<UnitObjType, UnitObj>(*node)) != NULL
            )
            {
              // If this unit is in our squad, remove it
              if (unit->GetSquad() == script.GetSquad())
              {
                (*t)->list.Unlink(node);
              }
            }
          }
        }
      }

      if (append)
      {
        // Is there an existing tag with this name ?
        TagObj *tag = TagObj::FindTag(tagName.GetStr());

        if (tag)
        {
          // Append the units in the tag to the list
          list.AppendList(tag->list);
        }
      }

      TagObj::CreateTag(tagName.GetStr(), list);

      list.Clear();
    }
    //
    // Execute
    //
    U32 SelfDestruct::Execute(const U8 *, Player &player)
    {
      // Tell each object to SelfDestruct
      for (UnitObjList::Iterator i(&player.GetSelectedList()); *i; i++)
      {
        // Get the unit
        UnitObj *unit = **i;

        // Ensure it can self destruct
        if (unit->HasProperty(0x54D4152A) && !unit->UnderConstruction()) // "Ability::SelfDestruct"
        {
          unit->SelfDestruct(TRUE, unit->GetTeam());
        }
      }

      return (sizeof (Data));
    }
  //
  // Submit asset requests directly to the manager
  //
  Bool Asset::Request::TypeBase::DirectEvaluation(Manager &manager)
  {
    // Iterate the units in the base an evaluate them
    if (base.Alive())
    {
      for (UnitObjList::Iterator u(&base->GetUnits()); *u; u++)
      {
        if ((*u)->Alive())
        {
          UnitObj *unit = **u;
          manager.Evaluation(*this, *unit, config->GetAmounts().Find(unit->GameType()->Id()) ? 1.0f : 0.0f);
        }
      }
    }

    return (TRUE);
  }
  //
  // Offer
  //
  // The given asset is being offered, do we want it ?
  //
  Bool Asset::Request::TypeBase::Offer(Asset &asset)
  {
    // Get the unit out of the asset
    UnitObj *unit = asset.GetUnit();
    ASSERT(unit)

    // Do we have enough of this type ?
    U32 type = unit->GameType()->Id();

    // We should only be offered this type if we asked for it
    ASSERT(config->GetAmount(type))

    // How many of this type do we currently have ?
    U32 *amount = amounts.Find(type);

    // Is this less that what we need ?
    return ((!amount || *amount < config->GetAmount(type)) ? TRUE : FALSE);
  }
    //
    // Execute
    //
    U32 Store::Execute(const U8 *data, Player &player)
    {
      const Data *d = (Data *) data;

      // Create an iterator
      for (UnitObjList::Iterator i(&player.GetSelectedList()); *i; i++)
      {
        // Get the unit
        UnitObj *unit = **i;

        // Is this a collector ?
        if (Tasks::UnitCollect *task = TaskCtrl::PromoteIdle<Tasks::UnitCollect>(unit))
        {
          // Should we search for a place to store the resource
          if (d->search)
          {
            task->Store();
          }
          else
          {
            // Convert ID into a pointer
            if (UnitObj *storeObj = Resolver::Object<UnitObj, UnitObjType>(d->object))
            {
              // If not updating the position, flush tasks incase moving or something
              if (!d->update && unit->GetActiveTask())
              {
                unit->FlushTasks();
              }

              // Tell this task about the new storage point
              task->SetStorageObject(storeObj, d->update);
            }
          }
        }
      }

      return (sizeof (Data));
    }
  //
  // Initial state
  //
  void MapDeath::StateInit()
  {
    // Stop the objects driver
    UnitObj *unitObj = Promote::Object<UnitObjType, UnitObj>(subject);

    if (unitObj)
    {
      if (unitObj->GetWeapon())
      {
        unitObj->GetWeapon()->HaltFire();
      }

      if (unitObj->CanEverMove())
      {
        unitObj->GetDriver()->Stop(Movement::Driver::STOP_DYING);
      }
    }

    // Play the death animation
    subject->SetAnimation(0xF40D135F); // "Death"

    NextState(0x382D3C63); // "Dead"
  }
    //
    // RenderSpecialized
    //
    // Display specialized unit information
    //
    static void RenderSpecialized()
    {
      // Is there a unit under the cursor
      UnitObj *over = data.cInfo.gameWnd.Alive() ? data.cInfo.o.unit.GetPointer() : NULL;

      // Can we see this units special info
      if (over && Team::TestDisplayRelation(over, Relation::ALLY))
      {
        over->RenderSpecialized();
      }

      // Now display for all selected units
      for (UnitObjList::Iterator i(&data.sList); *i; ++i)
      {
        if (UnitObj *u = (*i)->GetPointer())
        {
          if (u != over)
          {
            u->RenderSpecialized();
          }
        }
      }
    }
//
// Execute
//
U32 PowerDown::Execute(const U8 *data, Player &player)
{
    const Data *d = (Data *) data;

    // Are we powering down or up
    if (d->down)
    {
        // Check each selected object
        for (UnitObjList::Iterator i(&player.GetSelectedList()); *i; i++)
        {
            UnitObj *subject = **i;

            // Can this unit power down ?
            if (subject->UnitType()->GetPower().GetRequired())
            {
                IssueTask(FLUSH, subject, new Tasks::UnitPowerDown(subject), player);
            }
        }
    }
    else
    {
        // Check each selected object
        for (UnitObjList::Iterator i(&player.GetSelectedList()); *i; i++)
        {
            UnitObj *subject = **i;

            // Is this unit powered down ?
            if (Tasks::UnitPowerDown *task = TaskCtrl::Promote<Tasks::UnitPowerDown>(subject))
            {
                task->ProcessEvent(Task::Event(0x57BE223A)); // "PowerUp"
            }
        }
    }

    return (sizeof (Data));
}
  //
  // StateInit
  //
  void SquadBoard::StateInit()
  {
    // Get each of the units in the squad to board transports
    SquadObj::UnitList::Iterator u(&subject->GetList());

    // We could enhance this to use units which are closest to transports etc.

    // Clear the squad's completed flags
    for (!u; *u; u++)
    {
      (*u)->completed = FALSE;
      (*u)->task = 0;
    }

    // Reset squad iterator
    !u;

    for (TransportObjList::Iterator t(&transports); *t; t++)
    {
      if ((*t)->Alive())
      {
        TransportObj *transport = **t;
        U32 slots = transport->TransportType()->GetSpaces();
        U32 added = 0;

        while (slots--)
        {
          UnitObjPtr *unitPtr = *u;

          if (unitPtr)
          {
            ASSERT(unitPtr->Alive())
            UnitObj *unit = *unitPtr;

            if (unit->CanEverMove())
            {
              // Tell the unit to board
              unit->FlushTasks(Tasks::UnitBoard::GetConfigBlockingPriority());

              Task *task;

              // Is this a telepad
              if (TaskCtrl::PromoteIdle<Tasks::TransportPad>(transport))
              {
                unit->PrependTask(task = new Tasks::UnitMove(unit, transport), GetFlags());
              }
              else
              {
                unit->PrependTask(task = new Tasks::UnitBoard(unit, transport), GetFlags());
              }

              (*u)->task = task->GetTaskId();

              added++;
            }
            else
            {
              // This unit can't move so complete it
              (*u)->completed = TRUE;
            }
          }
          else
          {
            break;
          }

          // Increment iterator
          u++;
        }

        if (!added)
        {
          // No units were added
          break;
        }
      }
    }

    // Complete those units which missed out
    for (; *u; u++)
    {
      (*u)->completed = TRUE;
    }

    NextState(0x09E5F977); // "Boarding"
  }
    //
    // RenderRallyPoint
    //
    // Display a rally point
    //
    static void RenderRallyPoint()
    {
      Point<S32> srcPoint(0, 0);
      Point<U32> dstPoint(0, 0);
      Matrix src, dst;

      // Get the single selected unit
      UnitObj *unit = data.cInfo.oneUnit.GetPointer();

      // Is there a rally point
      if (unit && unit->GetFootInstance() && unit->GetRallyPoint(dstPoint))
      {
        // Setup destination
        dst.ClearData();
        dst.posit.x = WorldCtrl::CellToMetresX(dstPoint.x);
        dst.posit.z = WorldCtrl::CellToMetresZ(dstPoint.z);
        dst.posit.y = TerrainData::FindFloorWithWater(dst.posit.x, dst.posit.z);

        // Find the source
        srcPoint.Set(S32(dstPoint.x), S32(dstPoint.z));
        unit->GetFootInstance()->ClampToFringe(srcPoint);
        src.ClearData();
        src.posit.x = WorldCtrl::CellToMetresX(srcPoint.x);
        src.posit.z = WorldCtrl::CellToMetresZ(srcPoint.z);
        src.posit.y = TerrainData::FindFloorWithWater(src.posit.x, src.posit.z);
      }
      else
      {
        // Act as if not selected
        unit = NULL;
      }

      if 
      (
        // Rally unit has been deselected
        (!unit && rallyUnitId) 
        
        || 
        
        (
          // Have a rally point unit
          unit 
          
          && 
          
          (
            // Different to last one
            (unit->Id() != rallyUnitId) 
            
            || 
            
            // Rally point location has changed
            (rallyUnitId && (dstPoint != rallyPoint))
          )
        )
      )
      {
        // Dispose of any current particle
        if (rallyParticle.Alive())
        {
          delete rallyParticle;
        }

        // Is there a unit selected
        if (unit)
        {
          // Never display a trail when same point
          if ((srcPoint.x != S32(dstPoint.x)) || (srcPoint.z != S32(dstPoint.z)))
          {
            // Create the runner particle
            if (ParticleClass *p = ParticleSystem::FindType(0x94E362BD)) // "Client::Rally"
            {
              Matrix m(src);
              m.posit.y += 3.0F;
			        Vector v(0.0f, 0.0f, 0.0f);
              rallyParticle = ParticleSystem::New(p, m, v, v, dst.posit - src.posit, 0.0F);
            }
          }

          // Remember this unit's info
          rallyUnitId = unit->Id();
          rallyPoint = dstPoint;
        }
        else
        {
          rallyUnitId = 0;
        }
      }

      // Render the start and end points
      if (unit)
      {     
        Common::Display::Mesh(0x693D5359, src, Color(0.0F, 0.8F, 0.0F, 0.7F)); // "Location"
        Common::Display::Mesh(0x693D5359, dst, Color(0.0F, 1.0F, 0.0F, 0.7F)); // "Location"
      }
    }
  //
  // Initial state
  //
  void SquadMoveTogether::StateInit()
  {
    LOG_DIAG(("SquadMoveTogether: Init"))

    // Find the slowest unit in the squad and use their traction type
    U8 traction = 0;
    F32 bottomSpeed = F32_MAX;

    // Work out the centre position of the squad
    Vector location(0.0f, 0.0f, 0.0f);
    U32 count = 0;
    SquadObj::UnitList::Iterator i(&subject->GetList());
    for (!i; *i; i++)
    {
      if ((*i)->Alive())
      {
        // Take this oportunity to reset the units node
        (*i)->completed = TRUE;
        (*i)->data = 0;

        if ((**i)->CanEverMove())
        {
          // Grab the unit so we don't need to continually pound the iterator
          UnitObj *unit = **i;

          // Flush its tasks
          unit->FlushTasks(Tasks::UnitMove::GetConfigBlockingPriority());

          // Add its position to the total
          location.x += unit->WorldMatrix().posit.x;
          location.z += unit->WorldMatrix().posit.z;
          count++;

          // Is this the slowest unit in the squad
          F32 speed = unit->GetMaxSpeed();
          if (speed < bottomSpeed)
          {
            bottomSpeed = speed;
            traction = unit->MapType()->GetTractionIndex(unit->MapType()->GetDefaultLayer());
          }
        }
      }
    }

    if (!count)
    {
      Quit();
      return;
    }

    // Work out the averate location
    location.x /= count;
    location.z /= count;
    location.y = TerrainData::FindFloor(location.x, location.z);

    // What is the direction from the source (location) to the dest (destination)
    VectorDir dir;
    Vector(destination - location).Convert(dir);
    direction = dir.u;

    // Build a formation from the units using the direction to the destination
    Formation::Create(location, direction, subject, 16.0f);

    // Get the source and destination in terms of cells
    Point<F32> avg(location.x, location.z);

    Point<U32> srcCell;
    WorldCtrl::MetresToCellPoint(avg, srcCell);

    Point<U32> destCell;
    WorldCtrl::MetresToCellPoint(Point<F32>(destination.x, destination.z), destCell);

    switch (subject->GetPathFinder().RequestPath(srcCell.x, srcCell.z, destCell.x, destCell.z, traction))
    {
      case PathSearch::Finder::RR_SUBMITTED:
        NextState(0xFDE9D5E3); // "Pathing"
        break;

      case PathSearch::Finder::RR_SAMECELL:
      case PathSearch::Finder::RR_OFFMAP:
        Quit();
        return;

      default:
        ERR_FATAL(("Unknown path request result"))
    }
  }
  //
  // Recycle
  //
  // Check timer and refund resources once recycled
  //
  void UnitRecycle::StateRecycle()
  {
    // Apply progress
    progressTotal -= progressMax;

    // Has recycling finished
    if (progressTotal <= 0.0F)
    {
      // Refund the resources
      if (subject->GetTeam() && refund > 0)
      {
        // Calculate the total refund
        U32 totalRefund = U32(subject->UnitType()->GetRecyclePercentage() * refund);

        // Add to the team
        subject->GetTeam()->AddResourceStore(totalRefund);

        // Report the resource type
        subject->GetTeam()->ReportResource(totalRefund, "resource.recycled");

        // Generate a message
        if (Team::GetDisplayTeam() == subject->GetTeam())
        {
          CON_MSG((TRANSLATE(("#game.messages.recyclerefund", 2, subject->GetUpgradedUnit().GetDesc(), totalRefund))));
        }
      }

      // Trigger finish FX
      subject->StartGenericFX(0x2062BAAD, NULL, TRUE); // "Recycle::Finish"

      // Did this building consume its constructor
      if 
      (
        subject->UnitType()->GetConstructorType() && 
        !subject->UnitType()->GetConstructorType()->GetIsFacility() &&
        subject->UnitType()->GetConstructorConsume()
      )
      {
        // Ensure the constructors resources are initialized
        subject->UnitType()->GetConstructorType()->InitializeResources();

        // Create a fresh new constructor
        UnitObj *unit = subject->UnitType()->GetConstructorType()->SpawnClosest
        (
          subject->Position(), subject->GetTeam()
        );

        // If this team is controlled by AI then assign the 
        // constructor to the primary base (if there is one)
        if (unit && unit->GetTeam() && unit->GetTeam()->IsAI())
        {
          Strategic::Object *object = unit->GetTeam()->GetStrategicObject();
          if (object)
          {
            Strategic::Base *base = object->GetBaseManager().GetPrimaryBase();

            if (base)
            {
              base->AddUnit(unit);
              base->AddConstructor(unit);
            }
          }
        }
      }

      // Remove the object
      subject->MarkForDeletion();

      // Remove boarded object
      if (subject->UnitType()->CanBoard() && subject->GetBoardManager()->InUse())
      {
        subject->GetBoardManager()->GetUnitObj()->SelfDestruct();
      }

      // Recycle completed
      Quit();
    }
  }
    //
    // Undo
    //
    // Undo operation
    //
    void Objects::Undo()
    {
      // Process each object
      for (NList<Data>::Iterator i(&dataList); *i; i++)
      {
        Data *d = *i;

        switch (op)
        {
          // Delete objects that were created
          case OP_CREATE:
            if (d->object.Alive())
            {
              GameObjCtrl::MarkForDeletion(d->object);
            }
            break;

          // Create objects that were deleted
          case OP_DELETE:
          {
            MapObj *m = MapObjCtrl::ObjectNewOnMap(d->type, d->matrix, 0, d->zipped);

            // Restore zipping
            if (d->zipped)
            {
              m->ToggleFootPrint(TRUE);
            }
  
            // Is this a unit
            UnitObj *u = Promote::Object<UnitObjType, UnitObj>(m);

            // Restore correct team
            if (u && d->team)
            {
              u->SetTeam(d->team);
            }

            // Save the old id
            U32 oldId = d->object.DirectId();

            // Now replace all references to the old object with the new object
            for (NList<Base>::Iterator items(&GetHistoryList()); *items; items++)
            {
              Objects *item = Promote<Objects>(*items);

              if (item)
              {
                for (NList<Data>::Iterator data(&item->dataList); *data; data++)
                {
                  if ((*data)->object.DirectId() == oldId)
                  {
                    (*data)->object = m;
                  }
                }
              }
            }

            break;
          }

          // Move objects back to original location
          case OP_MOVE:
            if (d->object.Alive())
            {
              d->object->SetSimCurrent(d->matrix);

              // Toggle claiming if necessary
              UnitObj *unitObj = Promote::Object<UnitObjType, UnitObj>(d->object);

              if (unitObj && unitObj->CanEverMove())
              {
                unitObj->GetDriver()->RemoveFromMapHook();
                unitObj->GetDriver()->AddToMapHook();
              }
            }
            break;

          // Restore zipped state
          case OP_ZIP:
            if (d->object.Alive())
            {
              d->object->ToggleFootPrint(d->zipped);
            }
            break;
        }
      }
    }
  //
  // GoToNextPoint
  //
  void SquadMoveTogether::GoToNextPoint(SquadObj::ListNode *node)
  {
    // We are attempting to go to the next point
    NPoint *p = points[node->data];
    node->data++;
    node->completed = TRUE;

    point = Max(point, node->data);

    ASSERT(node->Alive())
    UnitObj *unit = node->GetData();

    if (p)
    {
      // Mark the time a unit attempts to move to this point
      if (p->timestamp)
      {
        // The squad will need to wait for me to catch up
        if (GameTime::SimCycle() - p->timestamp > timeDiffMax)
        {
//          LOG_DIAG(("Detected that we're [%d] way behind at this point, changing to waiting state", node->Id()))
          NextState(0xCC45C48B); // "Waiting"
        }
      }
      else
      {
        p->timestamp = GameTime::SimCycle();

        // Since we're the first unit to this point we 
        // should check to make sure that no units are 
        // more than two points behind.  If there are,
        // then we'll have to wait here for them

        SquadObj::UnitList::Iterator i(&subject->GetList());
        for (!i; *i; i++)
        {
          if 
          (
            (*i)->Alive() && 
            ((*i)->data + pointDiffMax < point)
          )
          {
//            LOG_DIAG(("We're [%d] first to this point [%d] and detected that [%d] is way behind", node->Id(), node->data, (*i)->Id()))
            NextState(0xCC45C48B); // "Waiting"
            return;
          }
        }
      }

      // Find the closest point to that point that we can go to
      U32 x, z;

      // Use the formation slot offset for this unit
      Vector location(WorldCtrl::CellToMetresX(p->x), 0.0f, WorldCtrl::CellToMetresZ(p->z));

      F32 dir = p->direction + node->slot.direction;
      VectorDir::FixU(dir);

      F32 orient = p->direction + node->slot.orientation;
      VectorDir::FixU(orient);

      Vector offset;
      offset.x = (F32) cos(dir);
      offset.y = 0.0f;
      offset.z = (F32) sin(dir);
      offset *= node->slot.distance;
      offset += location;

      // Make sure the point is on the map
      WorldCtrl::ClampMetreMap(offset.x, offset.z);

      x = WorldCtrl::MetresToCellX(offset.x);
      z = WorldCtrl::MetresToCellZ(offset.z);

      if (PathSearch::FindClosestCell(unit->MapType()->GetTractionIndex(unit->MapType()->GetDefaultLayer()), x, z, x, z, meetingRange))
      {
        node->completed = FALSE;
        unit->PrependTask(new Tasks::UnitMove(unit, offset));
      }
      else
      {
        // Cound not find a cell, go to the next point
        GoToNextPoint(node);
      }
    }
  }
Exemple #21
0
//
// Poll
//
// Attempt to trigger this trap
//
Bool TrapObj::Poll()
{
  // Did we find a valid target
  Bool valid = FALSE;

  // Has recharging finished
  if (recharge.Test())
  {
    UnitObj *target;

    // Find an enemy within the configured range
    UnitObjIter::Tactical i
    (
      NULL, UnitObjIter::FilterData(GetTeam(), Relation::ENEMY, Position(), TrapType()->GetDistance())
    );
    
    // Step through each possible target
    while ((target = i.Next()) != NULL)
    {
      // Can this trap trigger on this target
      if (TrapType()->Test(target))
      {
        // Does this trap self destruct
        if (TrapType()->GetSelfDestruct())
        {
          SelfDestruct(TRUE, GetTeam());
          valid = TRUE;
        }

        // Does this trap attach a parasite
        if (TrapType()->GetParasite() && TrapType()->GetParasite()->Infect(target, GetTeam()))
        {
          valid = TRUE;
        }

        // Should we fire a weapon
        if (TrapType()->GetWeaponSpeed() > 0.0F)
        {
          if (GetWeapon())
          {
            Vector pos = Origin();
            pos.y += 20.0f;
            GetWeapon()->SetTarget(Target(pos));
            valid = TRUE;
          }
          else
          {
            LOG_WARN(("Trap %s configured to fire a weapon, but has none!", TypeName()));
          }
        }

        if (valid)
        {
          // Signal team radio that we were triggered
          if (GetTeam())
          {
            // "Trap::Triggered"
            GetTeam()->GetRadio().Trigger(0xA0FF29A5, Radio::Event(this));
          }

          // Signal the target that it has been trapped
          if (target->GetTeam())
          {
            // "Trap::Triggered::Target"
            target->GetTeam()->GetRadio().Trigger(0x3070E869, Radio::Event(target));
          }

          // Start charge time
          recharge.Start(TrapType()->GetChargeTime());
        }

        // Only affect one target for now
        break;
      }
    }
  }

  return (valid);
}