Ticket #3280: formation_pathfinding.2.diff

File formation_pathfinding.2.diff, 27.1 KB (added by sanderd17, 9 years ago)
  • binaries/data/mods/public/simulation/components/Formation.js

     
    548548        xMin = Math.min(xMin, offset.x);
    549549        yMin = Math.min(yMin, offset.y);
    550550    }
    551     this.width = xMax - xMin;
    552     this.depth = yMax - yMin;
     551    this.width = xMax - xMin + 4;// TODO take formation width + passClass clearance
     552    this.depth = yMax - yMin + 4;
     553
     554    // set an obstruction size to calculate collisions
     555    var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
     556    if (cmpObstruction)
     557        cmpObstruction.SetSize(this.depth, this.width);
     558    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
     559    if (cmpUnitMotion)
     560        cmpUnitMotion.SetAllowedDeviation(this.width);
    553561};
    554562
    555563Formation.prototype.MoveToMembersCenter = function()
     
    878886{
    879887    var maxRadius = 0;
    880888    var minSpeed = Infinity;
     889    var runSpeed = 0;
    881890
    882891    for each (var ent in this.members)
    883892    {
     
    887896
    888897        var cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
    889898        if (cmpUnitMotion)
     899        {
    890900            minSpeed = Math.min(minSpeed, cmpUnitMotion.GetWalkSpeed());
     901            runSpeed += cmpUnitMotion.GetRunSpeed();
     902        }
    891903    }
     904    runSpeed /= this.members.length;
    892905    minSpeed *= this.GetSpeedMultiplier();
    893906
    894907    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    895908    cmpUnitMotion.SetUnitRadius(maxRadius);
    896909    cmpUnitMotion.SetSpeed(minSpeed);
     910    cmpUnitMotion.SetRunSpeed(runSpeed);
    897911
    898912    // TODO: we also need to do something about PassabilityClass, CostClass
    899913};
  • binaries/data/mods/public/simulation/components/UnitMotionFormation.js

     
     1function UnitMotionFormation() {}
     2
     3UnitMotionFormation.prototype.Schema =
     4    "<empty/>";
     5
     6UnitMotionFormation.prototype.Init = function()
     7{
     8    this.goal = null;
     9    this.pathTicket = 0;
     10    this.passClass = 0;
     11    this.path = [];
     12    this.speed = 1;
     13    this.runSpeed = 1;
     14    this.pathIdx = -1;
     15    this.state = "IDLE";
     16    this.allowedDeviation = 8;
     17
     18    var cmpPathfinder = Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder);
     19    this.passClass = cmpPathfinder.GetPassabilityClass("default");
     20};
     21
     22UnitMotionFormation.prototype.SetPassabilityClassName = function(name)
     23{
     24    var cmpPathfinder = Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder);
     25    this.passClass = cmpPathfinder.GetPassabilityClass(name);
     26};
     27
     28// Ignore, unit radius is not important anymore, only the passability class is
     29UnitMotionFormation.prototype.SetUnitRadius = function() {};
     30
     31UnitMotionFormation.prototype.SetAllowedDeviation = function(allowedDeviation)
     32{
     33    this.allowedDeviation = allowedDeviation;
     34};
     35
     36UnitMotionFormation.prototype.IsMoving = function()
     37{
     38    return this.pathIdx >= 0;
     39};
     40UnitMotionFormation.prototype.SetSpeed = function(speed)
     41{
     42    this.speed = speed;
     43};
     44UnitMotionFormation.prototype.SetRunSpeed = function(speed)
     45{
     46    this.runSpeed = speed;
     47};
     48UnitMotionFormation.prototype.MoveToPointRange = function(x, z, minRange, maxRange)
     49{
     50    this.goal = {
     51        "type": 0,
     52        "x": x, // target x
     53        "z": z, // target z
     54        "u": {"x": 1, "y": 0}, // first unit axis
     55        "v": {"x": 0, "y": 1}, // second unit axis
     56        "hw": maxRange, // for a circle, hw is range
     57        "hh": maxRange,
     58        "maxdist": 0 // maxdist between waypoints, 0 -> unlimited
     59    }
     60    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     61    if (!cmpPosition || !cmpPosition.IsInWorld())
     62        return false;
     63
     64    var pos = cmpPosition.GetPosition2D();
     65
     66    var cmpPathfinder = Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder);
     67    this.pathTicket = cmpPathfinder.ComputePathAsync(pos.x, pos.y, this.goal, this.passClass, this.entity);
     68
     69    this.state = "PATH_REQUESTED";
     70    return true;
     71};
     72
     73UnitMotionFormation.prototype.OnPathResult = function(msg)
     74{
     75    if (msg.ticket != this.pathTicket)
     76        return;
     77    if (!msg.path.length)
     78        return;
     79
     80    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     81    if (!cmpPosition || !cmpPosition.IsInWorld())
     82        return;
     83
     84    var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
     85    if (!cmpFormation)
     86        return;
     87    // path generalisation
     88    var size = Math.min(cmpFormation.GetSize().width, cmpFormation.GetSize().depth);
     89    var pos = msg.path[0];
     90    this.path = [pos];
     91    for (var i = 1; i < msg.path.length; ++i)
     92    {
     93        var dx = pos.x - msg.path[i].x;
     94        var dy = pos.y - msg.path[i].y;
     95        if (dx * dx + dy * dy < size * size)
     96            continue;
     97        pos = msg.path[i];
     98        this.path.push(pos);
     99    }
     100    this.pathIdx = this.path.length - 1;
     101    // add the current position for reference
     102    pos = cmpPosition.GetPosition2D();
     103    this.path.push({"x" : pos.x, "y": pos.y});
     104
     105    var direction = Vector2D.clone(this.path[this.pathIdx]).sub(pos);
     106    cmpPosition.TurnTo(Math.atan2(direction.x, direction.y));
     107
     108    this.state = "FOLLOW_LINE";
     109    Engine.PostMessage(this.entity, MT_MotionChanged, { "starting": true, "error": false });
     110};
     111
     112UnitMotionFormation.prototype.OnUpdate = function(msg)
     113{
     114    if (!this.IsMoving())
     115        return;
     116    if (this.state == "FOLLOW_LINE")
     117        this.FollowLine(msg.turnLength);
     118    else if (this.state == "TAKE_TURN")
     119        this.TakeTurn(msg.turnLength);
     120};
     121
     122UnitMotionFormation.prototype.FollowLine = function(turnLength)
     123{
     124    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     125    if (!cmpPosition || !cmpPosition.IsInWorld())
     126        return;
     127
     128    var pos = cmpPosition.GetPosition2D();
     129
     130    // project current position to original path
     131    var target = this.path[this.pathIdx];
     132    var source = this.path[this.pathIdx + 1];
     133    var direction = Vector2D.clone(target).sub(source);
     134    var posVec = Vector2D.clone(pos).sub(source);
     135    var pathPosition = Vector2D.clone(direction).mult(posVec.dot(direction) / direction.lengthSquared());
     136
     137    // Get the distance on the original path
     138    var pathToWalk = Vector2D.clone(direction).sub(pathPosition);
     139    var distanceToTarget = pathToWalk.length();
     140    var distanceToWalk = this.speed * turnLength;
     141
     142    if (distanceToTarget < distanceToWalk) // just walk straight on, and start turning
     143    {
     144        pos.add(pathToWalk);
     145        cmpPosition.MoveTo(pos.x, pos.y);
     146        // if this is the last target, stop moving
     147        if (this.pathIdx)
     148        {
     149            // else, take a turn
     150            var usedTime = distanceToTarget / this.speed;
     151            this.state = "TAKE_TURN";
     152            this.TakeTurn(turnLength - usedTime);
     153        }
     154        else
     155        {
     156            this.pathIdx--;
     157            Engine.PostMessage(this.entity, MT_MotionChanged, { "starting": false, "error": false });
     158            this.state = "IDLE";
     159        }
     160        return;
     161    }
     162
     163    var currentDeviation = posVec.distanceTo(pathPosition);
     164    if (this.pathIdx == 0 && distanceToTarget <= currentDeviation)
     165    {
     166        var pathToTarget = Vector2D.clone(target).sub(pos).normalize().mult(distanceToWalk);
     167        pos.add(pathToTarget);
     168        cmpPosition.MoveTo(pos.x, pos.y);
     169        return;
     170    }
     171
     172    // Find the next position
     173    // scale the path to walk to include the walkable distance
     174    var centerPathToWalk = Vector2D.clone(pathToWalk).mult(distanceToWalk / distanceToTarget);
     175    // expand the rotation matrix for the 45deg rotations
     176    var leftPathToWalk = new Vector2D(centerPathToWalk.x - centerPathToWalk.y, centerPathToWalk.x + centerPathToWalk.y);
     177    var rightPathToWalk = new Vector2D(centerPathToWalk.x + centerPathToWalk.y, -centerPathToWalk.x + centerPathToWalk.y);
     178   
     179    var newCenterPosVec = Vector2D.clone(posVec).add(centerPathToWalk);
     180    var newLeftPosVec = Vector2D.clone(posVec).add(leftPathToWalk);
     181    var newRightPosVec = Vector2D.clone(posVec).add(rightPathToWalk);
     182
     183    // project the center to the original path
     184    var centerPathPosition = Vector2D.clone(direction).mult(newCenterPosVec.dot(direction) / direction.lengthSquared());
     185
     186    var allowLeft = newLeftPosVec.distanceToSquared(centerPathPosition) < this.allowedDeviation * this.allowedDeviation;
     187    var allowRight = newRightPosVec.distanceToSquared(centerPathPosition) < this.allowedDeviation * this.allowedDeviation;
     188
     189    // compare the obstructions
     190    var cmpPathfinder = Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder);
     191    var centerObstructedTiles = cmpPathfinder.GetObstructedTiles(centerPathToWalk, this.entity, this.passClass);
     192    var leftObstructedTiles = Infinity;
     193    var rightObstructedTiles = Infinity;
     194    if (allowLeft)
     195        leftObstructedTiles = cmpPathfinder.GetObstructedTiles(leftPathToWalk, this.entity, this.passClass);
     196    if (allowRight)
     197        rightObstructedTiles = cmpPathfinder.GetObstructedTiles(rightPathToWalk, this.entity, this.passClass);
     198
     199    // choose the best of the three positions
     200    var min = Math.min(centerObstructedTiles, leftObstructedTiles, rightObstructedTiles);
     201    if (min == centerObstructedTiles)
     202        pos.add(centerPathToWalk);
     203    else if (min == leftObstructedTiles)
     204        pos.add(leftPathToWalk);
     205    else if (min == rightObstructedTiles)
     206        pos.add(rightPathToWalk);
     207
     208    cmpPosition.MoveTo(pos.x, pos.y);
     209};
     210
     211UnitMotionFormation.prototype.TakeTurn = function(turnLength)
     212{
     213    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     214    var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
     215    if (!cmpPosition || !cmpPosition.IsInWorld() || !cmpFormation)
     216        return;
     217    var formationSize = Math.max(cmpFormation.GetSize().width, cmpFormation.GetSize().depth) / 2;
     218    var maxTurnAngle = turnLength * this.runSpeed / formationSize;
     219    var currentAngle = cmpPosition.GetRotation().y;
     220    var newSource = Vector2D.clone(this.path[this.pathIdx]);
     221    var newTarget = Vector2D.clone(this.path[this.pathIdx - 1]);
     222    var newDirection = Vector2D.clone(newTarget).sub(newSource);
     223    var targetAngle = Math.atan2(newDirection.x, newDirection.y);
     224    warn("new");
     225    warn(currentAngle);
     226    warn(targetAngle);
     227
     228    // check where we need to turn to (between -Pi and Pi)
     229    var posMod = function(a, n){ return a - Math.floor(a/n) * n; };
     230    var relativeTurn = posMod(targetAngle - currentAngle + Math.PI, 2 * Math.PI) - Math.PI;
     231    // if we can't turn the full length, turn the maximum we can
     232    warn(relativeTurn);
     233    if (Math.abs(relativeTurn) > maxTurnAngle)
     234    {
     235        if (relativeTurn < 0)
     236            cmpPosition.TurnTo(currentAngle - maxTurnAngle);
     237        else
     238            cmpPosition.TurnTo(currentAngle + maxTurnAngle);
     239        return;
     240    }
     241    // we can take the full turn: finish the turn and continue in a straight line
     242    cmpPosition.TurnTo(targetAngle);
     243    this.state = "FOLLOW_LINE";
     244    var usedTime = Math.abs(relativeTurn) / maxTurnAngle * turnLength;
     245    this.pathIdx--;
     246    this.FollowLine(turnLength - usedTime);
     247};
     248
     249Engine.RegisterComponentType(IID_UnitMotion, "UnitMotionFormation", UnitMotionFormation);
  • binaries/data/mods/public/simulation/templates/template_formation.xml

     
    11<?xml version="1.0" encoding="utf-8"?>
    22<Entity>
    3   <!--
     3  <!---->
    44  <VisualActor>
    55    <Actor>props/special/common/waypoint_flag.xml</Actor>
     6    <SilhouetteDisplay>false</SilhouetteDisplay>
     7    <SilhouetteOccluder>true</SilhouetteOccluder>
     8    <VisibleInAtlasOnly>false</VisibleInAtlasOnly>
    69  </VisualActor>
    7   -->
     10  <!---->
    811  <Formation>
    912    <RequiredMemberCount>1</RequiredMemberCount>
    1013    <DisabledTooltip></DisabledTooltip>
     
    2124  <FormationAttack>
    2225    <CanAttackAsFormation>false</CanAttackAsFormation>
    2326  </FormationAttack>
     27  <Obstruction>
     28    <Active>false</Active>
     29    <BlockMovement>false</BlockMovement>
     30    <BlockPathfinding>false</BlockPathfinding>
     31    <BlockFoundation>false</BlockFoundation>
     32    <BlockConstruction>false</BlockConstruction>
     33    <DisableBlockMovement>true</DisableBlockMovement>
     34    <DisableBlockPathfinding>true</DisableBlockPathfinding>
     35    <Static width="30.0" depth="30.0"/>
     36  </Obstruction>
    2437  <Ownership/>
    2538  <Position>
    2639    <Altitude>0</Altitude>
     
    3548    <FormationController>true</FormationController>
    3649    <CanGuard>true</CanGuard>
    3750  </UnitAI>
    38   <UnitMotion>
    39     <FormationController>true</FormationController>
    40     <WalkSpeed>1.0</WalkSpeed>
    41     <PassabilityClass>siege-large</PassabilityClass>
    42   </UnitMotion>
     51  <UnitMotionFormation/>
    4352  <Trader>
    4453    <GainMultiplier>1.0</GainMultiplier>
    4554  </Trader>
  • binaries/data/mods/public/simulation/templates/template_unit.xml

     
    3737  <Identity>
    3838    <GenericName>Unit</GenericName>
    3939    <Classes datatype="tokens">Unit ConquestCritical</Classes>
    40         <Formations datatype="tokens">
    41             formations/null
    42         </Formations>
     40    <Formations datatype="tokens">
     41      formations/null
     42      formations/box
     43      formations/column_closed
     44      formations/line_closed
     45      formations/column_open
     46      formations/line_open
     47      formations/flank
     48      formations/battle_line
     49    </Formations>
    4350  </Identity>
    4451  <Looter/>
    4552  <Minimap>
  • source/scriptinterface/ScriptConversions.cpp

     
    2020#include "ScriptInterface.h"
    2121
    2222#include "graphics/Entity.h"
     23#include "simulation2/helpers/PathGoal.h" // to convert path goals
    2324#include "ps/utf16string.h"
    2425#include "ps/CLogger.h"
    2526#include "ps/CStr.h"
     
    205206    JS::RootedValue position(cx);
    206207    JS::RootedValue rotation(cx);
    207208
    208     // TODO: Report type errors
     209    // TODO: Report type errors when loading attributes
    209210    if (!JS_GetProperty(cx, obj, "player", &player) || !FromJSVal(cx, player, out.playerID))
    210211        FAIL("Failed to read Entity.player property");
    211212    if (!JS_GetProperty(cx, obj, "templateName", &templateName) || !FromJSVal(cx, templateName, out.templateName))
     
    220221    return true;
    221222}
    222223
     224// TODO this doesn't work (probably always uses POINT)
     225template<> bool ScriptInterface::FromJSVal<PathGoal::Type>(JSContext* cx, JS::HandleValue v, PathGoal::Type& out)
     226{
     227    std::string strVal;
     228    ScriptInterface::FromJSVal(cx, v, static_cast<std::string&>(strVal));
     229    if (strVal == "CIRCLE")
     230        out = PathGoal::CIRCLE;
     231    if (strVal == "INVERTED_CIRCLE")
     232        out = PathGoal::INVERTED_CIRCLE;
     233    else if (strVal == "SQUARE")
     234        out = PathGoal::SQUARE;
     235    else if (strVal == "INVERTED_SQUARE")
     236        out = PathGoal::INVERTED_SQUARE;
     237    else
     238        out = PathGoal::POINT;
     239    return true;
     240}
     241
     242template<> bool ScriptInterface::FromJSVal<PathGoal>(JSContext* cx, JS::HandleValue val, PathGoal& out)
     243{
     244    JSAutoRequest rq(cx);
     245    if (!val.isObject())
     246        FAIL("Argument must be an object");
     247
     248    JS::RootedObject obj(cx, &val.toObject());
     249    JS::RootedValue type(cx);
     250    JS::RootedValue x(cx);
     251    JS::RootedValue z(cx);
     252    JS::RootedValue u(cx);
     253    JS::RootedValue v(cx);
     254    JS::RootedValue hw(cx);
     255    JS::RootedValue hh(cx);
     256
     257   
     258    // TODO: Report type errors
     259    if (!JS_GetProperty(cx, obj, "type", &type) || !FromJSVal(cx, type, out.type))
     260        FAIL("Failed to read goal.type property");
     261    if (!JS_GetProperty(cx, obj, "x", &x) || !FromJSVal(cx, x, out.x))
     262        FAIL("Failed to read goal.x property");
     263    if (!JS_GetProperty(cx, obj, "z", &z) || !FromJSVal(cx, z, out.z))
     264        FAIL("Failed to read goal.z property");
     265    if (!JS_GetProperty(cx, obj, "u", &u) || !FromJSVal(cx, u, out.u))
     266        FAIL("Failed to read goal.u property");
     267    if (!JS_GetProperty(cx, obj, "v", &v) || !FromJSVal(cx, v, out.v))
     268        FAIL("Failed to read goal.v property");
     269    if (!JS_GetProperty(cx, obj, "hw", &hw) || !FromJSVal(cx, hw, out.hw))
     270        FAIL("Failed to read goal.hw property");
     271    if (!JS_GetProperty(cx, obj, "hh", &hh) || !FromJSVal(cx, hh, out.hh))
     272        FAIL("Failed to read goal.hh property");
     273    return true;
     274}
     275
    223276////////////////////////////////////////////////////////////////
    224277// Primitive types:
    225278
  • source/simulation2/components/CCmpObstruction.cpp

     
    515515            m_Clearance = clearance;
    516516    }
    517517
     518    virtual void SetSize(entity_pos_t width, entity_pos_t height)
     519    {
     520        // require this to be inactive, so we don't have to force grid updates
     521        ENSURE(m_Active == false);
     522        m_Size0 = width;
     523        m_Size0 = height;
     524    }
     525
    518526    virtual bool IsControlPersistent()
    519527    {
    520528        return m_ControlPersist;
  • source/simulation2/components/CCmpPathfinder.cpp

     
    184184}
    185185
    186186
    187 pass_class_t CCmpPathfinder::GetPassabilityClass(const std::string& name)
     187pass_class_t CCmpPathfinder::GetPassabilityClass(std::string name)
    188188{
    189189    if (m_PassClassMasks.find(name) == m_PassClassMasks.end())
    190190    {
     
    610610
    611611// Async path requests:
    612612
    613 u32 CCmpPathfinder::ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, entity_id_t notify)
     613u32 CCmpPathfinder::ComputePathAsync(entity_pos_t x0, entity_pos_t z0, PathGoal goal, pass_class_t passClass, entity_id_t notify)
    614614{
    615615    AsyncLongPathRequest req = { m_NextAsyncTicket++, x0, z0, goal, passClass, notify };
    616616    m_AsyncLongPathRequests.push_back(req);
     
    804804    return ICmpObstruction::FOUNDATION_CHECK_SUCCESS;
    805805}
    806806
     807u32 CCmpPathfinder::GetObstructedTiles(CFixedVector2D offset, entity_id_t id, pass_class_t passClass)
     808{
     809    // Test against terrain:
     810    ICmpObstructionManager::ObstructionSquare square;
     811    CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), id);
     812    if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(square))
     813        return 0;
     814
     815    square.x += offset.X;
     816    square.z += offset.Y;
     817
     818    UpdateGrid();
     819
     820    entity_pos_t expand;
     821    const PathfinderPassability* passability = GetPassabilityFromMask(passClass);
     822    if (passability && passability->m_HasClearance)
     823        expand = passability->m_Clearance;
     824
     825    SimRasterize::Spans spans;
     826    SimRasterize::RasterizeRectWithClearance(spans, square, expand, Pathfinding::NAVCELL_SIZE);
     827    u32 obstructedTiles = 0;
     828    for (SimRasterize::Span& span : spans)
     829    {
     830        i16 i0 = span.i0;
     831        i16 i1 = span.i1;
     832        i16 j = span.j;
     833
     834        // Fail if any span includes an impassable tile
     835        for (i16 i = i0; i < i1; ++i)
     836            if (i < 0 || i > m_Grid->m_W || j < 0 || j > m_Grid->m_H || !IS_PASSABLE(m_Grid->get(i, j), passClass))
     837                ++obstructedTiles;
     838    }
     839
     840    return obstructedTiles;
     841}
  • source/simulation2/components/CCmpPathfinder_Common.h

     
    137137
    138138    virtual void HandleMessage(const CMessage& msg, bool global);
    139139
    140     virtual pass_class_t GetPassabilityClass(const std::string& name);
     140    virtual pass_class_t GetPassabilityClass(std::string name);
    141141
    142142    virtual std::map<std::string, pass_class_t> GetPassabilityClasses();
    143143
     
    177177        m_LongPathfinder.ComputePath(x0, z0, goal, passClass, ret);
    178178    }
    179179
    180     virtual u32 ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, entity_id_t notify);
     180    virtual u32 ComputePathAsync(entity_pos_t x0, entity_pos_t z0, PathGoal goal, pass_class_t passClass, entity_id_t notify);
    181181
    182182    virtual void ComputeShortPath(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t r, entity_pos_t range, const PathGoal& goal, pass_class_t passClass, WaypointPath& ret);
    183183
     
    212212
    213213    virtual ICmpObstruction::EFoundationCheck CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass, bool onlyCenterPoint);
    214214
     215    virtual u32 GetObstructedTiles(CFixedVector2D offset, entity_id_t id, pass_class_t passClass);
     216
    215217    virtual void FinishAsyncRequests();
    216218
    217219    void ProcessLongRequests(const std::vector<AsyncLongPathRequest>& longRequests);
  • source/simulation2/components/ICmpObstruction.cpp

     
    4747
    4848BEGIN_INTERFACE_WRAPPER(Obstruction)
    4949DEFINE_INTERFACE_METHOD_0("GetUnitRadius", entity_pos_t, ICmpObstruction, GetUnitRadius)
     50DEFINE_INTERFACE_METHOD_2("SetSize", void, ICmpObstruction, SetSize, entity_pos_t, entity_pos_t)
    5051DEFINE_INTERFACE_METHOD_2("CheckFoundation", std::string, ICmpObstruction, CheckFoundation_wrapper, std::string, bool)
    5152DEFINE_INTERFACE_METHOD_0("CheckDuplicateFoundation", bool, ICmpObstruction, CheckDuplicateFoundation)
    5253DEFINE_INTERFACE_METHOD_2("GetEntityCollisions", std::vector<entity_id_t>, ICmpObstruction, GetEntityCollisions, bool, bool)
  • source/simulation2/components/ICmpObstruction.h

     
    5454
    5555    virtual entity_pos_t GetSize() = 0;
    5656
     57    /**
     58     * Passively set the size of this obstruction
     59     * This must only be used on inactive obstructions, as it won't update any grids
     60     */
     61    virtual void SetSize(entity_pos_t width, entity_pos_t height) = 0;
     62
    5763    virtual entity_pos_t GetUnitRadius() = 0;
    5864
    5965    virtual void SetUnitClearance(const entity_pos_t& clearance) = 0;
  • source/simulation2/components/ICmpPathfinder.cpp

     
    2424BEGIN_INTERFACE_WRAPPER(Pathfinder)
    2525DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpPathfinder, SetDebugOverlay, bool)
    2626DEFINE_INTERFACE_METHOD_1("SetHierDebugOverlay", void, ICmpPathfinder, SetHierDebugOverlay, bool)
     27DEFINE_INTERFACE_METHOD_1("GetPassabilityClass", pass_class_t, ICmpPathfinder, GetPassabilityClass, std::string)
     28DEFINE_INTERFACE_METHOD_3("GetObstructedTiles", u32, ICmpPathfinder, GetObstructedTiles, CFixedVector2D, entity_id_t, pass_class_t)
     29DEFINE_INTERFACE_METHOD_5("ComputePathAsync", u32, ICmpPathfinder, ComputePathAsync, entity_pos_t, entity_pos_t, PathGoal, pass_class_t, entity_id_t)
    2730END_INTERFACE_WRAPPER(Pathfinder)
  • source/simulation2/components/ICmpPathfinder.h

     
    5959     * Get the tag for a given passability class name.
    6060     * Logs an error and returns something acceptable if the name is unrecognised.
    6161     */
    62     virtual pass_class_t GetPassabilityClass(const std::string& name) = 0;
     62    virtual pass_class_t GetPassabilityClass(std::string name) = 0;
    6363
    6464    virtual entity_pos_t GetClearance(pass_class_t passClass) const = 0;
    6565
     
    9494     * Returns a unique non-zero number, which will match the 'ticket' in the result,
    9595     * so callers can recognise each individual request they make.
    9696     */
    97     virtual u32 ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, entity_id_t notify) = 0;
     97    virtual u32 ComputePathAsync(entity_pos_t x0, entity_pos_t z0, PathGoal goal, pass_class_t passClass, entity_id_t notify) = 0;
    9898
    9999    /**
    100100     * If the debug overlay is enabled, render the path that will computed by ComputePath.
     
    150150     */
    151151    virtual ICmpObstruction::EFoundationCheck CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass, bool onlyCenterPoint) = 0;
    152152
     153    /**
     154     * Get the number of obstructed tiles that are under an obstruction of a
     155     * certain entity. Allow an offset to easily test alternative positions.
     156     */
     157    virtual u32 GetObstructedTiles(CFixedVector2D offset, entity_id_t id, pass_class_t passClass) = 0;
    153158
    154159    /**
    155160     * Toggle the storage and rendering of debug info.
  • source/simulation2/scripting/EngineScriptConversions.cpp

     
    2727#include "ps/Shapes.h"
    2828#include "ps/utf16string.h"
    2929#include "simulation2/helpers/Grid.h"
     30#include "simulation2/helpers/Pathfinding.h"
    3031#include "simulation2/system/IComponent.h"
    3132#include "simulation2/system/ParamNode.h"
    3233
     
    247248    ret.setObject(*obj);
    248249}
    249250
     251template<> void ScriptInterface::ToJSVal<Waypoint>(JSContext* cx, JS::MutableHandleValue ret, const Waypoint& val)
     252{
     253    JSAutoRequest rq(cx);
     254
     255    // apply the Vector2D prototype to the return value
     256    ScriptInterface::CxPrivate* pCxPrivate = ScriptInterface::GetScriptInterfaceAndCBData(cx);
     257    JS::RootedObject proto(cx, &pCxPrivate->pScriptInterface->GetCachedValue(ScriptInterface::CACHE_VECTOR2DPROTO).toObject());
     258    JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, proto, JS::NullPtr()));
     259    if (!obj)
     260    {
     261        ret.setUndefined();
     262        return;
     263    }
     264
     265    JS::RootedValue x(cx);
     266    JS::RootedValue y(cx);
     267    ToJSVal(cx, &x, val.x);
     268    ToJSVal(cx, &y, val.z);
     269
     270    JS_SetProperty(cx, obj, "x", x);
     271    JS_SetProperty(cx, obj, "y", y);
     272
     273    ret.setObject(*obj);
     274}
     275
    250276template<> void ScriptInterface::ToJSVal<Grid<u8> >(JSContext* cx, JS::MutableHandleValue ret, const Grid<u8>& val)
    251277{
    252278    JSAutoRequest rq(cx);
     
    309335    }
    310336    ret.setObject(*obj);
    311337}
     338
     339template<> void ScriptInterface::ToJSVal<WaypointPath>(JSContext* cx, JS::MutableHandleValue ret, const WaypointPath& val)
     340{
     341    JSAutoRequest rq(cx);
     342    JS::RootedObject obj(cx, JS_NewArrayObject(cx, 0));
     343    if (!obj)
     344    {
     345        ret.setUndefined();
     346        return;
     347    }
     348    const std::vector<Waypoint>& waypointList = val.m_Waypoints;
     349    for (size_t i = 0; i < waypointList.size(); ++i)
     350    {
     351        JS::RootedValue el(cx);
     352        ScriptInterface::ToJSVal<Waypoint>(cx, &el, waypointList[i]);
     353        JS_SetElement(cx, obj, i, el);
     354    }
     355    ret.setObject(*obj);
     356}
  • source/simulation2/scripting/MessageTypeConversions.cpp

     
    385385
    386386////////////////////////////////
    387387
    388 JS::Value CMessagePathResult::ToJSVal(ScriptInterface& UNUSED(scriptInterface)) const
     388JS::Value CMessagePathResult::ToJSVal(ScriptInterface& scriptInterface) const
    389389{
    390     LOGWARNING("CMessagePathResult::ToJSVal not implemented");
    391     return JS::UndefinedValue();
     390    TOJSVAL_SETUP();
     391    SET_MSG_PROPERTY(ticket);
     392    SET_MSG_PROPERTY(path);
     393    return JS::ObjectValue(*obj);
    392394}
    393395
    394396CMessage* CMessagePathResult::FromJSVal(ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))