forked from Mirrors/openclonk
462 lines
13 KiB
C
462 lines
13 KiB
C
/*
|
|
Ropeladder
|
|
Author: Randrian
|
|
|
|
A ladder consisting of ropesegments. The physics is done completly intern with point masses.
|
|
Verlet integration and simple stick constraints are used for the rope physics.
|
|
The segments are an extra object, which handels the graphics and the detection of the ladder by the clonk.
|
|
|
|
Interface:
|
|
Unroll(int dir, int unrolldir, int length); // dir == -1 expand to the left, dir == 1 expand right;
|
|
// unrolldir == COMD_Down, COMD_Right, COMD_Left or COMD_Up specifies where to adjust to a wall
|
|
// length, length of segments, default 15
|
|
*/
|
|
|
|
#include Library_Rope
|
|
|
|
static const Ladder_MaxParticles = 15;//30;//15*3;
|
|
static const Ladder_Iterations = 10;
|
|
static const Ladder_Precision = 100;
|
|
static const Ladder_SegmentLength = 5;//2;
|
|
|
|
local particles;
|
|
local segments;
|
|
local TestArray;
|
|
|
|
local MaxSegmentCount;
|
|
|
|
local SwitchRopes;
|
|
local MirrorSegments;
|
|
|
|
local UnrollDir;
|
|
|
|
local ParticleCount;
|
|
|
|
local grabber;
|
|
|
|
public func ControlUse(object clonk, int x, int y)
|
|
{
|
|
if(!clonk->GetContact(-1)) return true;
|
|
// Unroll dir
|
|
var dir = -1;
|
|
if(x > 0) dir = 1;
|
|
if(clonk->GetAction() == "Scale")
|
|
{
|
|
if(clonk->GetDir() == 0)
|
|
{
|
|
Exit(0, 8);
|
|
Unroll(1, COMD_Right);
|
|
}
|
|
else
|
|
{
|
|
Exit(0, 8);
|
|
Unroll(-1, COMD_Left);
|
|
}
|
|
}
|
|
else if(clonk->GetAction() == "Hangle")
|
|
{
|
|
Exit(0, 0);
|
|
Unroll(dir, COMD_Up);
|
|
}
|
|
else
|
|
{
|
|
Exit(0, 5);
|
|
Unroll(dir, COMD_Down);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public func UpdateSegmentOverlays()
|
|
{
|
|
for(var i = 1; i < GetLength(segments); i++)
|
|
{
|
|
segments[i]->SetGraphics("Line", GetID(), 2, 1);
|
|
segments[i]->SetGraphics("Line", GetID(), 3, 1);
|
|
segments[i]->SetGraphics(nil, nil, 4);
|
|
segments[i]->SetGraphics(nil, nil, 5);
|
|
if(i > 1)
|
|
segments[i]->SetGraphics("NoRope", segments[i]->GetID(), 4, 1);
|
|
if(i == GetLength(segments)-1)
|
|
segments[i]->SetGraphics("NoRope", segments[i]->GetID(), 5, 1);
|
|
if(i == 1)
|
|
{
|
|
segments[i]->SetGraphics("Anchor", GetID(), 1, 1);
|
|
segments[i]->SetGraphics("AnchorOverlay", GetID(), 5, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMoveOut(xdir, ydir)
|
|
{
|
|
if(!Stuck()) return;
|
|
for(var i = 0; i < 8; i++)
|
|
{
|
|
if(!GBackSolid(i*xdir, i*ydir))
|
|
{
|
|
SetPosition(GetX()+i*xdir, GetY()+i*ydir);
|
|
if(!Stuck())
|
|
break;
|
|
SetPosition(GetX()-i*xdir, GetY()-i*ydir);
|
|
}
|
|
}
|
|
}
|
|
|
|
public func Unroll(int dir, int unrolldir, int length)
|
|
{
|
|
if(!unrolldir) unrolldir = COMD_Down;
|
|
SwitchRopes = 0;
|
|
// Unroll dir
|
|
if(unrolldir == COMD_Left || unrolldir == COMD_Right)
|
|
{
|
|
var xdir = 1;
|
|
if(unrolldir == COMD_Right)
|
|
{
|
|
xdir = -1;
|
|
}
|
|
var x1 = 0;
|
|
var x2 = 0;
|
|
var x0 = 0;
|
|
var y1 =-5;
|
|
var y2 = 5;
|
|
while(!GBackSolid(x0, 0) && Abs(x0) < 10) x0 += xdir;
|
|
while(!GBackSolid(x1, y1) && Abs(x1) < 10) x1 += xdir;
|
|
while(!GBackSolid(x2, y2) && Abs(x2) < 10) x2 += xdir;
|
|
|
|
SetPosition(GetX()+x0-2*xdir, GetY());
|
|
SetR(90*xdir+Angle(x1, y1, x2, y2));
|
|
}
|
|
else if(unrolldir == COMD_Up)
|
|
{
|
|
var x1 =-2;
|
|
var x2 = 2;
|
|
var y1 = 0;
|
|
var y2 = 0;
|
|
var y0 = 0;
|
|
while(!GBackSolid( 0, y0) && Abs(y0) < 10) y0--;
|
|
while(!GBackSolid(x1, y1) && Abs(y1) < 10) y1--;
|
|
while(!GBackSolid(x2, y2) && Abs(y2) < 10) y2--;
|
|
|
|
SetPosition(GetX(), GetY()+y0+2);
|
|
SetR(-90+Angle(x2, y2, x1, y1));
|
|
SwitchRopes = 1;
|
|
}
|
|
else
|
|
{
|
|
var x1 =-2;
|
|
var x2 = 2;
|
|
var y1 = 0;
|
|
var y2 = 0;
|
|
var y0 = 0;
|
|
while(!GBackSolid( 0, y0) && Abs(y0) < 10) y0++;
|
|
while(!GBackSolid(x1, y1) && Abs(y1) < 10) y1++;
|
|
while(!GBackSolid(x2, y2) && Abs(y2) < 10) y2++;
|
|
|
|
SetPosition(GetX(), GetY()+y0-2);
|
|
SetR(90+Angle(x2, y2, x1, y1));
|
|
}
|
|
if(!length)
|
|
{
|
|
if(MaxSegmentCount == nil)
|
|
MaxSegmentCount = Ladder_MaxParticles;
|
|
}
|
|
else MaxSegmentCount = length;
|
|
DoUnroll(dir);
|
|
}
|
|
|
|
public func MakeBridge(obj1, obj2)
|
|
{
|
|
MirrorSegments = 1;
|
|
SetProperty("Collectible", 0);
|
|
StartRopeConnect(obj1, obj2);
|
|
AddEffect("IntHang", this, 1, 1, this);
|
|
}//MakeBridge(Object(221), Object(150))
|
|
|
|
protected func DoUnroll(dir)
|
|
{
|
|
MirrorSegments = dir;
|
|
UnrollDir = dir;
|
|
SetAction("Hanging");
|
|
SetProperty("Collectible", 0);
|
|
|
|
// TestArray = [[0, 1], [1, 0], [1, 1], [0, 2], [1, 2], [2, 0], [2, 1], [2, 2], [0, 3], [1, 3], [2, 3], [3, 0], [3, 1], [3, 2], [0, 4], [1, 4], [2, 4], [3, 3], [4, 0], [4, 1], [4, 2], [0, 5], [1, 5], [2, 5], [3, 4], [3, 5], [4, 3], [4, 4], [5, 0], [5, 1], [5, 2], [5, 3], [0, 6], [1, 6], [2, 6], [3, 6], [4, 5], [5, 4], [6, 0], [6, 1], [6, 2], [6, 3], [0, 7], [1, 7], [2, 7], [3, 7], [4, 6], [5, 5], [5, 6], [6, 4], [6, 5], [7, 0], [7, 1], [7, 2], [7, 3], [0, 8], [1, 8], [2, 8], [3, 8], [4, 7], [4, 8], [5, 7], [6, 6], [7, 4], [7, 5], [8, 0], [8, 1], [8, 2], [8, 3], [8, 4], [0, 9], [1, 9], [2, 9], [3, 9], [4, 9], [5, 8], [6, 7], [7, 6], [7, 7], [8, 5], [9, 0], [9, 1], [9, 2], [9, 3], [9, 4]];
|
|
|
|
grabber = CreateObject(Ropeladder_Grabber);
|
|
grabber->SetAction("Attach", this);
|
|
|
|
StartRope();
|
|
|
|
AddEffect("IntHang", this, 1, 1, this);
|
|
|
|
AddEffect("UnRoll", this, 1, 2, this);
|
|
}
|
|
|
|
func StartRollUp() { RemoveEffect("UnRoll", this); AddEffect("RollUp", this, 1, 1, this); }
|
|
|
|
func FxUnRollTimer()
|
|
{
|
|
if(ParticleCount == MaxSegmentCount)
|
|
{
|
|
if(GetActTime() < MaxSegmentCount*2*2) return;
|
|
// If it wasn't possible to acchieve at least half the full length we pull in again
|
|
if( -(particles[0][0][1]-particles[ParticleCount-1][0][1]) < (ParticleCount*Ladder_SegmentLength*Ladder_Precision)/2)
|
|
StartRollUp();
|
|
return -1;
|
|
}
|
|
AddSegment(UnrollDir*Ladder_Precision, 0);
|
|
}
|
|
|
|
func FxRollUpTimer() { if(ParticleCount == 0) return -1; RemoveSegment(); }
|
|
|
|
func FxIntHangTimer()
|
|
{
|
|
TimeStep();
|
|
if(!Stuck()) TestLength();
|
|
}
|
|
|
|
func TestLength()
|
|
{
|
|
if(GetActTime() < 36) return;
|
|
// If it wasn't possible to acchieve at least half the full length we pull in again
|
|
if( -(particles[0][0][1]-particles[ParticleCount-1][0][1]) < (ParticleCount*5*Ladder_Precision)/2)
|
|
StartRollUp();
|
|
}
|
|
|
|
/* --------------------- Callbacks form the rope ---------------------- */
|
|
|
|
/* To be overloaded for special segment behaviour */
|
|
private func CreateSegment(int index, object previous)
|
|
{
|
|
var segment;
|
|
if(index == 0)
|
|
{
|
|
segment = CreateObject(Ropeladder_Segment);
|
|
segment->SetGraphics("None");
|
|
segment->SetMaster(this, 0);
|
|
}
|
|
else
|
|
{
|
|
segment = CreateObject(Ropeladder_Segment);
|
|
|
|
segment->SetMaster(this, ParticleCount);
|
|
|
|
segment->SetNextLadder(previous);
|
|
previous->SetPreviousLadder(segment);
|
|
|
|
segment->SetGraphics("None");
|
|
}
|
|
return segment;
|
|
}
|
|
|
|
private func DeleteSegment(object segment, previous)
|
|
{
|
|
if(segment)
|
|
segment->RemoveObject();
|
|
if(previous)
|
|
previous->SetPreviousLadder(nil);
|
|
}
|
|
|
|
/* When the last segment is removed */
|
|
private func RopeRemoved()
|
|
{
|
|
RemoveEffect("IntHang", this);
|
|
SetCategory(C4D_Object);
|
|
SetAction("Idle");
|
|
SetProperty("Collectible", 1);
|
|
|
|
// Try to move the Ropeladder somewhere out if it is stuck
|
|
TestMoveOut( 0, -1); // Up
|
|
TestMoveOut( 0, +1); // Down
|
|
TestMoveOut(-1, 0); // Left
|
|
TestMoveOut(+1, 0); // Right
|
|
|
|
grabber->RemoveObject();
|
|
|
|
SetGraphics("", GetID());
|
|
}
|
|
|
|
/* --------------------- Graphics of segments ---------------------- */
|
|
func UpdateLines()
|
|
{
|
|
var oldangle;
|
|
for(var i=1; i < ParticleCount; i++)
|
|
{
|
|
// Update the Position of the Segment
|
|
segments[i]->SetPosition(GetPartX(i), GetPartY(i));
|
|
|
|
// Calculate the angle to the previous segment
|
|
var angle;
|
|
if(i >= 1)
|
|
{
|
|
angle = Angle(particles[i][0][0], particles[i][0][1], particles[i-1][0][0], particles[i-1][0][1]);
|
|
segments[i]->SetAngle(angle);
|
|
}
|
|
|
|
// Every segment has not its graphics, but the graphics of the previous segment (or achor for the first)
|
|
// Otherwise the drawing order would be wrong an we would get lines over segments
|
|
|
|
// Draw the segment as an overlay for the following segment (only the last segment has two graphics (its and the previous)
|
|
if(i > 1)
|
|
SetLineTransform(segments[i], -oldangle, particles[i-1][0][0]*10-GetPartX(i)*1000,particles[i-1][0][1]*10-GetPartY(i)*1000, 1000, 4, MirrorSegments );
|
|
if(i == ParticleCount-1)
|
|
SetLineTransform(segments[i], -angle, particles[i][0][0]*10-GetPartX(i)*1000,particles[i][0][1]*10-GetPartY(i)*1000, 1000, 5, MirrorSegments );
|
|
|
|
// The first segment has to draw the achor too
|
|
if(i == 1)
|
|
{
|
|
SetLineTransform(segments[i], -GetR(), GetX()*1000-GetPartX(i)*1000, GetY()*1000-GetPartY(i)*1000, 1000, 1);
|
|
SetLineTransform(segments[i], -GetR(), GetX()*1000-GetPartX(i)*1000, GetY()*1000-GetPartY(i)*1000, 1000, 5);
|
|
}
|
|
|
|
// Draw the left line
|
|
var start = GetRopeConnetPosition(i, 0, 0, angle, oldangle);
|
|
var end = GetRopeConnetPosition(i, 0, 1, angle, oldangle);
|
|
|
|
var diff = Vec_Sub(end,start);
|
|
var diffangle = Vec_Angle(diff, [0,0]);
|
|
var point = Vec_Add(start, Vec_Div(diff, 2));
|
|
var length = Vec_Length(diff)*1000/Ladder_Precision/8;
|
|
|
|
SetLineTransform(segments[i], -diffangle, point[0]*10-GetPartX(i)*1000,point[1]*10-GetPartY(i)*1000, length, 2 );
|
|
|
|
// Draw the right line
|
|
var start = GetRopeConnetPosition(i, 1, 0, angle, oldangle);
|
|
var end = GetRopeConnetPosition(i, 1, 1, angle, oldangle);
|
|
|
|
var diff = Vec_Sub(end,start);
|
|
var diffangle = Vec_Angle(diff, [0,0]);
|
|
var point = Vec_Add(start, Vec_Div(diff, 2));
|
|
var length = Vec_Length(diff)*1000/Ladder_Precision/8;
|
|
|
|
SetLineTransform(segments[i], -diffangle, point[0]*10-GetPartX(i)*1000,point[1]*10-GetPartY(i)*1000, length, 3 );
|
|
|
|
// Remember the angle
|
|
oldangle = angle;
|
|
}
|
|
}
|
|
|
|
static const Ropeladder_Segment_LeftXOffset = 200;
|
|
static const Ropeladder_Segment_RightXOffset = -100;
|
|
|
|
func GetRopeConnetPosition(int index, bool fRight, bool fEnd, int angle, int oldangle)
|
|
{
|
|
if(SwitchRopes && index == 1 && fEnd == 0) fRight = !fRight;
|
|
if(!(index == 1 && fEnd == 0) && MirrorSegments == -1) fRight = !fRight;
|
|
if(fEnd == 0)
|
|
{
|
|
var start = [0,0];
|
|
if(fRight == 0)
|
|
{
|
|
if(index >= 2)
|
|
{
|
|
start = particles[index-1][0][:];
|
|
start[0] += -Cos(oldangle, Ropeladder_Segment_LeftXOffset*MirrorSegments);
|
|
start[1] += -Sin(oldangle, Ropeladder_Segment_LeftXOffset*MirrorSegments);
|
|
}
|
|
else
|
|
{
|
|
start = [GetX()*Ladder_Precision, GetY()*Ladder_Precision];
|
|
start[0] += -Cos(GetR(), 188)+Sin(GetR(), 113);
|
|
start[1] += -Sin(GetR(), 188)-Cos(GetR(), 113);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(index >= 2)
|
|
{
|
|
start = particles[index-1][0][:];
|
|
start[0] += -Cos(oldangle, Ropeladder_Segment_RightXOffset*MirrorSegments);
|
|
start[1] += -Sin(oldangle, Ropeladder_Segment_RightXOffset*MirrorSegments);
|
|
}
|
|
else
|
|
{
|
|
start = [GetX()*Ladder_Precision, GetY()*Ladder_Precision];
|
|
start[0] += +Cos(GetR(), 188)+Sin(GetR(), 113);
|
|
start[1] += +Sin(GetR(), 188)-Cos(GetR(), 113);
|
|
}
|
|
}
|
|
return start;
|
|
}
|
|
else
|
|
{
|
|
var end = [0,0];
|
|
if(fRight == 0)
|
|
{
|
|
end = particles[index][0][:];
|
|
end[0] += -Cos(angle, Ropeladder_Segment_LeftXOffset*MirrorSegments);
|
|
end[1] += -Sin(angle, Ropeladder_Segment_LeftXOffset*MirrorSegments);
|
|
}
|
|
else
|
|
{
|
|
end = particles[index][0][:];
|
|
end[0] += -Cos(angle, Ropeladder_Segment_RightXOffset*MirrorSegments);
|
|
end[1] += -Sin(angle, Ropeladder_Segment_RightXOffset*MirrorSegments);
|
|
}
|
|
return end;
|
|
}
|
|
}
|
|
|
|
func SetLineTransform(obj, int r, int xoff, int yoff, int length, int layer, int MirrorSegments) {
|
|
if(!MirrorSegments) MirrorSegments = 1;
|
|
var fsin=Sin(r, 1000), fcos=Cos(r, 1000);
|
|
// set matrix values
|
|
obj->SetObjDrawTransform (
|
|
+fcos*MirrorSegments, +fsin*length/1000, xoff,
|
|
-fsin*MirrorSegments, +fcos*length/1000, yoff,layer
|
|
);
|
|
}
|
|
|
|
/* --------------------- Clonk on ladder ---------------------- */
|
|
public func OnLadderGrab(clonk, index)
|
|
{
|
|
// Do some speed when the clonk jumps on the ladder
|
|
if(index == 0) return;
|
|
particles[index][0][0] += BoundBy(clonk->GetXDir()/2, -25, 25)*Ladder_Precision;
|
|
}
|
|
|
|
public func OnLadderClimb(clonk, index)
|
|
{
|
|
// The clonk drags on the upper segments and pushes on the lower ones
|
|
if(index > 2 && index < ParticleCount-3)
|
|
{
|
|
particles[index-2][0][0] -= 1*Ladder_Precision/5*(-1+2*clonk->GetDir());
|
|
particles[index+2][0][0] += 1*Ladder_Precision/5*(-1+2*clonk->GetDir());
|
|
}
|
|
else if(index > 2 && index < ParticleCount-2)
|
|
{
|
|
particles[index-2][0][0] -= 1*Ladder_Precision/5*(-1+2*clonk->GetDir());
|
|
particles[index+1][0][0] += 1*Ladder_Precision/5*(-1+2*clonk->GetDir());
|
|
}
|
|
}
|
|
|
|
public func GetLadderData(index)
|
|
{
|
|
var startx = particles[index][0][0]*10;
|
|
var starty = particles[index][0][1]*10;
|
|
if(index == 0)
|
|
{
|
|
var angle = Angle(particles[2][0][0], particles[2][0][1], particles[0][0][0], particles[0][0][1]);
|
|
return [startx, starty, startx, starty-5000, angle];
|
|
}
|
|
if(index == ParticleCount-1 || segments[index+1]->CanNotBeClimbed())
|
|
{
|
|
angle = Angle(particles[index][0][0], particles[index][0][1], particles[index-2][0][0], particles[index-2][0][1]);
|
|
}
|
|
else
|
|
angle = Angle(particles[index+1][0][0], particles[index+1][0][1], particles[index-1][0][0], particles[index-1][0][1]);
|
|
var endx = particles[index-1][0][0]*10;
|
|
var endy = particles[index-1][0][1]*10;
|
|
return [startx, starty, endx, endy, angle];
|
|
}
|
|
|
|
local ActMap = {
|
|
Hanging = {
|
|
Prototype = Action,
|
|
Name = "Hanging"
|
|
},
|
|
};
|
|
local Name = "$Name$";
|
|
local Description = "$Description$";
|
|
local Collectible = 1;
|
|
local Rebuy = true;
|