forked from Mirrors/openclonk
WIP First step in introducing rope links
parent
f10f7a4d48
commit
db68467d91
|
@ -736,8 +736,8 @@ bool C4Game::Execute() // Returns true if the game is over
|
|||
|
||||
// Game
|
||||
|
||||
EXEC_S( Ropes.Execute(); , ExecRopesStat )
|
||||
EXEC_S( ExecObjects(); , ExecObjectsStat )
|
||||
EXEC_S( Ropes.Execute(); , ExecRopesStat )
|
||||
if (pGlobalEffects)
|
||||
EXEC_S_DR( pGlobalEffects->Execute(NULL); , GEStats , "GEEx\0");
|
||||
EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx")
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <C4Landscape.h>
|
||||
#include <C4Rope.h>
|
||||
|
||||
//#define C4ROPE_DRAW_DEBUG
|
||||
#define C4ROPE_DRAW_DEBUG
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -147,18 +147,30 @@ namespace
|
|||
}
|
||||
|
||||
C4RopeElement::C4RopeElement(C4Object* obj, bool fixed):
|
||||
Fixed(fixed), fx(Fix0), fy(Fix0), rx(Fix0), ry(Fix0), rdt(Fix0), fcx(Fix0), fcy(Fix0),
|
||||
Next(NULL), Prev(NULL), Object(obj), LastContactVertex(-1)
|
||||
Fixed(fixed), oldx(obj->fix_x), oldy(obj->fix_y), fx(Fix0), fy(Fix0),
|
||||
rx(Fix0), ry(Fix0), rdt(Fix0), fcx(Fix0), fcy(Fix0),
|
||||
Next(NULL), Prev(NULL), FirstLink(NULL), LastLink(NULL),
|
||||
Object(obj), LastContactVertex(-1)
|
||||
{
|
||||
}
|
||||
|
||||
C4RopeElement::C4RopeElement(C4Real x, C4Real y, C4Real m, bool fixed):
|
||||
Fixed(fixed), x(x), y(y), vx(Fix0), vy(Fix0), m(m),
|
||||
Fixed(fixed), x(x), y(y), oldx(x), oldy(y), vx(Fix0), vy(Fix0), m(m),
|
||||
fx(Fix0), fy(Fix0), rx(Fix0), ry(Fix0), rdt(Fix0), fcx(Fix0), fcy(Fix0),
|
||||
Next(NULL), Prev(NULL), Object(NULL), LastContactVertex(-1)
|
||||
Next(NULL), Prev(NULL), FirstLink(NULL), LastLink(NULL),
|
||||
Object(NULL), LastContactVertex(-1)
|
||||
{
|
||||
}
|
||||
|
||||
C4RopeElement::~C4RopeElement()
|
||||
{
|
||||
for(C4RopeLink* link = FirstLink, *next; link != NULL; link = next)
|
||||
{
|
||||
next = link->Next;
|
||||
delete link;
|
||||
}
|
||||
}
|
||||
|
||||
void C4RopeElement::AddForce(C4Real x, C4Real y)
|
||||
{
|
||||
fx += x;
|
||||
|
@ -194,6 +206,116 @@ C4Real C4RopeElement::GetTargetY() const
|
|||
return obj->fix_y + itofix(obj->Shape.VtxY[LastContactVertex]);
|
||||
}
|
||||
|
||||
bool C4RopeElement::InsertLinkPosition(int from_x, int from_y, int to_x, int to_y, int link_x, int link_y, int& insert_x, int& insert_y)
|
||||
{
|
||||
if(PathFree(to_x, to_y, link_x, link_y)) return false;
|
||||
|
||||
// Sanity check, these might be violated if the landscape changed.
|
||||
// In that case the rope might end up buried in earth, in which case we
|
||||
// do not want to do any maneuvering around, but the rope is just stuck.
|
||||
if(!PathFree(from_x, from_y, link_x, link_y)) return false;
|
||||
|
||||
// If the element has been moved through solid material, only take the part
|
||||
// of the way into account that is free.
|
||||
int stop_x, stop_y;
|
||||
if(!PathFree(from_x, from_y, to_x, to_y, &stop_x, &stop_y))
|
||||
{
|
||||
to_x = stop_x;
|
||||
to_y = stop_y;
|
||||
}
|
||||
|
||||
// Find the position between from_x,from_y, and to_x,to_y
|
||||
// where there is no path free to link_x,link_y anymore.
|
||||
int prev_x = from_x;
|
||||
int prev_y = from_y;
|
||||
int coll_x = -1;
|
||||
int coll_y = -1;
|
||||
int max_p = Max(abs(to_x - from_x), abs(to_y - from_y));
|
||||
for(int i = 1; i <= max_p; ++i)
|
||||
{
|
||||
int inter_x = from_x + i * (to_x - from_x) / max_p;
|
||||
int inter_y = from_y + i * (to_y - from_y) / max_p;
|
||||
if(!PathFree(inter_x, inter_y, link_x, link_y))
|
||||
{
|
||||
coll_x = inter_x;
|
||||
coll_y = inter_y;
|
||||
break;
|
||||
}
|
||||
|
||||
prev_x = inter_x;
|
||||
prev_y = inter_y;
|
||||
}
|
||||
|
||||
// Such a position must have been found, because of the initial
|
||||
// PathFree(to_x, to_y, link_x, link_y) check.
|
||||
assert(coll_x != -1 && coll_y != -1);
|
||||
|
||||
// Now we have:
|
||||
// prev_x,prev_y: Last position between from_x,from_y and to_x,to_y
|
||||
// where the path to link_x,link_y is still free.
|
||||
// coll_x,coll_y: First position between from_x,from_y and to_x,to_y
|
||||
// where the path to link_x,link_y is no longer free.
|
||||
// prev_x,prev_y and coll_x,coll_y are 1 pixel apart from each other.
|
||||
|
||||
// Now the idea is to insert a new link somewhere on the line from
|
||||
// prev_x,prev_y to link_x,link_y such that
|
||||
// PathFree(coll_x, coll_y, insert_x, insert_y) holds and the point is
|
||||
// as close to link_x, link_y as possible.
|
||||
max_p = Max(abs(link_x - prev_x), abs(link_y - prev_y));
|
||||
for(int i = 1; i <= max_p; ++i)
|
||||
{
|
||||
int inter_x = link_x + i * (prev_x - link_x) / max_p;
|
||||
int inter_y = link_y + i * (prev_y - link_y) / max_p;
|
||||
|
||||
// Again a sanity check, to account for pixel rounding mismatches
|
||||
if(!PathFree(inter_x, inter_y, link_x, link_y)) continue;
|
||||
|
||||
if(PathFree(coll_x, coll_y, inter_x, inter_y))
|
||||
{
|
||||
insert_x = inter_x;
|
||||
insert_y = inter_y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The final point in the loop is prev_x, prev_y, and there should be a
|
||||
// free path from it, as per the sanity check precondition.
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
void C4RopeElement::InsertLink(C4RopeElement* from, C4RopeElement* to, int insert_x, int insert_y)
|
||||
{
|
||||
assert(this == from || this == to);
|
||||
|
||||
C4RopeLink* Link = new C4RopeLink;
|
||||
Link->x = itofix(insert_x);
|
||||
Link->y = itofix(insert_y);
|
||||
|
||||
if(this == from)
|
||||
{
|
||||
assert(from->Next == to);
|
||||
|
||||
Link->Next = from->FirstLink;
|
||||
from->FirstLink = Link;
|
||||
Link->Prev = NULL;
|
||||
|
||||
if(to->LastLink == NULL)
|
||||
to->LastLink = Link;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(to->Prev == from);
|
||||
|
||||
Link->Prev = to->LastLink;
|
||||
to->LastLink = Link;
|
||||
Link->Next = NULL;
|
||||
|
||||
if(from->FirstLink == NULL)
|
||||
from->FirstLink = Link;
|
||||
}
|
||||
}
|
||||
|
||||
void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
|
||||
{
|
||||
ResetForceRedirection(dt);
|
||||
|
@ -214,9 +336,9 @@ void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
|
|||
int iy = fixtoi(y);
|
||||
if(GBackSolid(ix+1, iy) || GBackSolid(ix-1, iy) || GBackSolid(ix, iy-1) || GBackSolid(ix, iy+1))
|
||||
{
|
||||
if(vx*vx + vy*vy < Fix1/4)
|
||||
if(vx*vx + vy*vy < Fix1/4) // TODO: Threshold should be made a property
|
||||
{
|
||||
if(fx*fx + fy*fy < Fix1/4)
|
||||
if(fx*fx + fy*fy < Fix1/4) // TODO: Threshold should be made a property
|
||||
{
|
||||
fx = fy = Fix0;
|
||||
vx = vy = Fix0;
|
||||
|
@ -248,19 +370,25 @@ void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
|
|||
// Execute movement
|
||||
if(!Target)
|
||||
{
|
||||
// Compute old and new coordinates
|
||||
int old_x = fixtoi(x);
|
||||
int old_y = fixtoi(y);
|
||||
int new_x = fixtoi(x + dt * vx);
|
||||
int new_y = fixtoi(y + dt * vy);
|
||||
// Maximum distance in pixels in either X or Y
|
||||
int max_p = Max(abs(new_x - old_x), abs(new_y - old_y));
|
||||
|
||||
// Check all pixels between old and new position
|
||||
int prev_x = old_x;
|
||||
int prev_y = old_y;
|
||||
bool hit = false;
|
||||
for(int i = 1; i <= max_p; ++i)
|
||||
{
|
||||
// Intermediate pixel position
|
||||
int inter_x = old_x + i * (new_x - old_x) / max_p;
|
||||
int inter_y = old_y + i * (new_y - old_y) / max_p;
|
||||
|
||||
// Collision check
|
||||
if(GBackSolid(inter_x, inter_y))
|
||||
{
|
||||
x = itofix(prev_x);
|
||||
|
@ -683,6 +811,28 @@ void C4Rope::Execute()
|
|||
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
|
||||
cur->Execute(this, dt);
|
||||
}
|
||||
|
||||
// Insert/remove links between rope elements. We rely that object movement
|
||||
// is executed before rope movement at this point, so this needs to stay in
|
||||
// sync with C4Game::Execute().
|
||||
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
|
||||
{
|
||||
int insert_x, insert_y;
|
||||
if(cur->Prev && cur->InsertLinkPosition(cur->oldx, cur->oldy, cur->GetX(), cur->GetY(), cur->GetPrevLinkX(), cur->GetPrevLinkY(), insert_x, insert_y))
|
||||
cur->InsertLink(cur->Prev, cur, insert_x, insert_y);
|
||||
if(cur->Next && cur->InsertLinkPosition(cur->oldx, cur->oldy, cur->GetX(), cur->GetY(), cur->GetNextLinkX(), cur->GetNextLinkY(), insert_x, insert_y))
|
||||
cur->InsertLink(cur, cur->Next, insert_x, insert_y);
|
||||
|
||||
// TODO: removal
|
||||
}
|
||||
|
||||
// Update old coordinates for next iteration. Don't do this in the loop above,
|
||||
// so that GetPrevLinkX/GetPrevLinkY still return the old values.
|
||||
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
|
||||
{
|
||||
cur->oldx = cur->GetX();
|
||||
cur->oldy = cur->GetY();
|
||||
}
|
||||
}
|
||||
|
||||
void C4Rope::ClearPointers(C4Object* obj)
|
||||
|
@ -820,6 +970,18 @@ void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
|
|||
glEnd();
|
||||
}
|
||||
|
||||
// Draw links
|
||||
for(C4RopeLink* link = cur->FirstLink; link != NULL; link = link->Next)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
glVertex2f(fixtof(cur->x)-1.0f, fixtof(cur->y)-1.0f);
|
||||
glVertex2f(fixtof(cur->x)-1.0f, fixtof(cur->y)+1.0f);
|
||||
glVertex2f(fixtof(cur->x)+1.0f, fixtof(cur->y)+1.0f);
|
||||
glVertex2f(fixtof(cur->x)+1.0f, fixtof(cur->y)-1.0f);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
const float vx = fixtof(cur->GetVx());
|
||||
const float vy = fixtof(cur->GetVy());
|
||||
const float v = sqrt(vx*vx + vy*vy);
|
||||
|
|
|
@ -29,6 +29,16 @@ public:
|
|||
C4RopeError(const std::string& message): std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
// C4RopeLinks are intermediate rope elements that are inserted and removed
|
||||
// such that
|
||||
class C4RopeLink
|
||||
{
|
||||
public:
|
||||
C4Real x, y; // pos
|
||||
C4RopeLink* Next;
|
||||
C4RopeLink* Prev;
|
||||
};
|
||||
|
||||
class C4Rope;
|
||||
class C4RopeElement
|
||||
{
|
||||
|
@ -36,6 +46,7 @@ class C4RopeElement
|
|||
public:
|
||||
C4RopeElement(C4Object* obj, bool fixed);
|
||||
C4RopeElement(C4Real x, C4Real y, C4Real m, bool fixed);
|
||||
~C4RopeElement();
|
||||
|
||||
C4Real GetX() const { return Object ? GetTargetX() : x; }
|
||||
C4Real GetY() const { return Object ? GetTargetY() : y; }
|
||||
|
@ -47,23 +58,34 @@ public:
|
|||
C4Real GetTargetX() const;
|
||||
C4Real GetTargetY() const;
|
||||
|
||||
C4Real GetPrevLinkX() const { assert(Prev); return LastLink ? LastLink->x : Prev->oldx; }
|
||||
C4Real GetPrevLinkY() const { assert(Prev); return LastLink ? LastLink->y : Prev->oldy; }
|
||||
C4Real GetNextLinkX() const { assert(Next); return FirstLink ? FirstLink->x : Next->oldx; }
|
||||
C4Real GetNextLinkY() const { assert(Next); return FirstLink ? FirstLink->y : Next->oldy; }
|
||||
|
||||
void AddForce(C4Real x, C4Real y);
|
||||
void Execute(const C4Rope* rope, C4Real dt);
|
||||
private:
|
||||
bool InsertLinkPosition(int from_x, int from_y, int to_x, int to_y, int link_x, int link_y, int& insert_x, int& insert_y);
|
||||
void InsertLink(C4RopeElement* from, C4RopeElement* to, int insert_x, int insert_y);
|
||||
|
||||
void ResetForceRedirection(C4Real dt);
|
||||
void SetForceRedirection(const C4Rope* rope, int ox, int oy);
|
||||
bool SetForceRedirectionByLookAround(const C4Rope* rope, int ox, int oy, C4Real dx, C4Real dy, C4Real l, C4Real angle);
|
||||
|
||||
bool Fixed; // Apply rope forces to this element?
|
||||
C4Real x, y; // pos
|
||||
C4Real vx, vy; // velocity
|
||||
C4Real m; // mass
|
||||
C4Real x, y; // pos; ignored if Object != NULL
|
||||
C4Real oldx, oldy; // position in the previous frame. Used for linking
|
||||
C4Real vx, vy; // velocity; ignored if Object != NULL
|
||||
C4Real m; // mass; ignored if Object != NULL
|
||||
C4Real fx, fy; // force
|
||||
C4Real rx, ry; // force redirection
|
||||
C4Real rdt; // force redirection timeout
|
||||
C4Real fcx, fcy; // force after solve -- for debug output only
|
||||
C4RopeElement* Next; // next rope element, or NULL
|
||||
C4RopeElement* Prev; // prev rope element, or NULL
|
||||
C4RopeLink* FirstLink; // first rope link between this and next, or NULL
|
||||
C4RopeLink* LastLink; // last rope link between this and prev, or NULL
|
||||
C4Object* Object; // Connected object. If set, x/y/vx/vy/m are ignored.
|
||||
int LastContactVertex; // Vertex which most recently had collision with landscape
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue