reworked grappler rope physics and grappler handling, it now uses with the ropeladder a common rope library

floating-point
Richard Gerum 2010-08-15 15:24:24 +02:00
parent 43a84d1fcb
commit cad5661c00
22 changed files with 1058 additions and 662 deletions

View File

@ -1,20 +1,21 @@
[DefCore]
id=GrappleBow
Category=C4D_Object
Width=6
Height=4
Offset=-3,-2
Vertices=3
VertexX=0,-3,3
VertexY=0,0,0
VertexFriction=40,90,40
Width=4
Height=20
Offset=-2,-10
Vertices=4
VertexX=-2,2,-2,2
VertexY=-10,-10,10,10
VertexFriction=100,100,100,100,100
Picture=4,0,21,21
Value=20
Value=8
Mass=10
Collectible=1
Components=Wood=1;Metal=1;Rope=1;
Components=WOOD=3;
Rebuy=1
Rotate=1
ContactIncinerate=5
BlastIncinerate=30
Float=1
BurnTo=NONE
Float=2

View File

@ -1,12 +0,0 @@
[DefCore]
id=GrappleHelp
Version=4,10,0,0
Category=C4D_Vehicle
Width=8
Height=20
Offset=-4,-10
Vertices=7
VertexX=0,0,0,-2,2,-4,4
VertexY=2,-7,9,-3,-3,3,3
VertexFriction=300,300,100,300,300,300,300
Mass=50

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -1,203 +0,0 @@
/*
Grapple Help
Author: Maikel
Helper object to let the clonk control the rope with movement keys.
The clonk is attached to the helper object, which is pulled by the rope.
With the movement keys(up/down) the player is able to shorten and lengthen the rope.
Swinging the rope can be done with left/right.
*/
/*-- Locals + Get/Setters --*/
local bow; // Grapple bow associated with this helper object.
local clonk; // Clonk pulled by the helper.
local rope; // Rope connected to the helper.
public func SetBow(object to_bow)
{
bow = to_bow;
// Helper object is inside the bow until the hooks hits solid material.
Enter(to_bow);
return;
}
public func SetClonk(object to_clonk)
{
clonk = to_clonk;
return;
}
public func GetClonk()
{
return clonk;
}
public func SetRope(object to_rope)
{
rope = to_rope;
return;
}
public func GetRope()
{
return rope;
}
/*-- Misc --*/
// Called from hook to hang the clonk on the helper object.
public func HangClonkOntoMe()
{
Exit(0, 10);
SetXDir(clonk->GetXDir());
SetYDir(clonk->GetYDir());
clonk->SetAction("Idle");
clonk->SetAction("HangOnto", this);
return;
}
// Called from the rope to notify the helper that the rope snapped.
public func OnRopeBreak()
{
bow->OnRopeBreak();
RemoveObject();
return;
}
// Called from the clonk to notify the helper that the clonk has let go of the helper.
public func HangOntoLost(object clonk)
{
if (rope)
rope->BreakRope();
RemoveObject();
return;
}
protected func Destruction()
{
if (clonk)
{
if (this == clonk->GetActionTarget())
{
clonk->SetAction("Jump");
// Pass speed onto clonk.
clonk->SetXDir(GetXDir());
clonk->SetYDir(GetYDir());
}
}
return;
}
// Overload of GetMass() to return the clonks mass.
public func GetMass()
{
if (!clonk)
return GetMass();
return clonk->GetMass();
}
/*-- Grapple rope controls --*/
public func ControlUp()
{
// Shorten rope.
if (rope)
{
var fxnum = GetEffect("IntGrappleControl", this);
if (!fxnum)
fxnum = AddEffect("IntGrappleControl", this, 100, 1, this);
EffectVar(0, this, fxnum) = true;
}
return true;
}
public func ControlDown()
{
// Lengthen rope.
if (rope)
{
var fxnum = GetEffect("IntGrappleControl", this);
if (!fxnum)
fxnum = AddEffect("IntGrappleControl", this, 100, 1, this);
EffectVar(1, this, fxnum) = true;
}
return true;
}
public func ControlLeft()
{
clonk->SetDir(DIR_Left);
var fxnum = GetEffect("IntGrappleControl", this);
if (!fxnum)
fxnum = AddEffect("IntGrappleControl", this, 100, 1, this);
EffectVar(2, this, fxnum) = true;
return true;
}
public func ControlRight()
{
clonk->SetDir(DIR_Right);
var fxnum = GetEffect("IntGrappleControl", this);
if (!fxnum)
fxnum = AddEffect("IntGrappleControl", this, 100, 1, this);
EffectVar(3, this, fxnum) = true;
return true;
}
public func ControlStop(object clonk, int control)
{
var fxnum = GetEffect("IntGrappleControl", this);
if (!fxnum)
return true;
if (control == CON_Up) EffectVar(0, this, fxnum) = false;
if (control == CON_Down) EffectVar(1, this, fxnum) = false;
if (control == CON_Left) EffectVar(2, this, fxnum) = false;
if (control == CON_Right) EffectVar(3, this, fxnum) = false;
return true;
}
// Effect for smooth movement.
public func FxIntGrappleControlTimer(object target, int fxnum)
{
if (!EffectVar(0, this, fxnum) && !EffectVar(1, this, fxnum)
&& !EffectVar(2, this, fxnum) && !EffectVar(3, this, fxnum))
return -1;
// Movement.
if (EffectVar(0, this, fxnum))
if (rope)
rope->DoLength(-1);
if (EffectVar(1, this, fxnum))
if (rope)
rope->DoLength(+1);
if (EffectVar(2, this, fxnum))
SetXDir(GetXDir(100) - 5, 100);
if (EffectVar(3, this, fxnum))
SetXDir(GetXDir(100) + 5, 100);
return FX_OK;
}
// Turn when control using objects.
public func ControlUseHolding(object clonk, int x, int y)
{
return ControlUseTurn(clonk, x, y);
}
public func ControlUseAltHolding(object clonk, int x, int y)
{
return ControlUseTurn(clonk, x, y);
}
private func ControlUseTurn(object clonk, int x, int y)
{
if (x > 0)
if (clonk->GetDir() == DIR_Left)
clonk->SetDir(DIR_Right);
if (x < 0)
if (clonk->GetDir() == DIR_Right)
clonk->SetDir(DIR_Left);
return false;
}

View File

@ -2,11 +2,12 @@
id=GrappleHook
Version=4,10,0,0
Category=C4D_Vehicle
Width=6
Height=7
Offset=-3,-4
Width=5
Height=6
Offset=-3,0
Vertices=3
VertexY=-3,2,0
VertexY=+3,2,0
VertexX=0,0,0
VertexFriction=120,120,120
Value=15
Mass=15

View File

@ -1,35 +1,40 @@
/*
Grapple Hook
Author: Maikel
Author: Randrian
The hook can be shot with the grappling bow.
On impact the hook will
On impact the hook will stick to the ground
The hook also controls the swinging controls for the clonk
*/
local rope; // The rope is the connection between the hook and the helper object.
local help; // The clonk is attached to the helper object.
local rope; // The rope is the connection between the hook
local clonk;
local pull;
public func ArrowStrength() { return 10; }
protected func Construction()
public func GetRope() { return rope; }
public func New(object new_clonk, object new_rope)
{
SetR(90);
return _inherited(...);
SetObjDrawTransform(0, 1, 0, 0, 0, 0, 0); // Hide
clonk = new_clonk;
rope = new_rope;
}
public func Launch(int angle, int str, object shooter, object bow)
{
SetObjDrawTransform(0, 1, 0, 0, 0, 0, 0); // Hide
Exit();
// Create rope and helper object.
rope = CreateObject(GrappleRope, 0, 0, NO_OWNER);
help = CreateObject(GrappleHelp, 0, 0, NO_OWNER);
rope->ConnectFree(this, help);
help->SetBow(bow);
help->SetClonk(shooter);
help->SetRope(rope);
bow->SetHelp(help);
pull = 0;
// Create rope
rope = CreateObject(GrappleRope, 0, 0, NO_OWNER);
rope->Connect(this, bow);
rope->ConnectLoose();
clonk = shooter;
var xdir = Sin(angle,str);
var ydir = Cos(angle,-str);
@ -41,6 +46,12 @@ public func Launch(int angle, int str, object shooter, object bow)
AddEffect("InFlight", this, 1, 1, this);
}
public func Destruction()
{
if(rope)
rope->HookRemoved();
}
private func Stick()
{
if (GetEffect("InFlight",this))
@ -57,7 +68,6 @@ private func Stick()
var y = Cos(GetR(), -9);
if(GBackSolid(x,y) && 1)
{
//Log("Attach hook");
// stick in landscape
SetVertex(2,VTX_Y,-12,2);
SetVertex(3,VTX_X,-3,2);
@ -71,12 +81,25 @@ private func Stick()
}
if (rope)
rope->ConnectPull();
if (help)
help->HangClonkOntoMe();
ScheduleCall(this, "StartPull", 5);
}
}
public func StartPull()
{
pull = 1;
AddEffect("IntGrappleControl", clonk, 1, 1, this);
if(clonk->GetAction() == "Jump")
{
rope->HockAnchored(1);
EffectVar(5, clonk, GetEffect("IntGrappleControl", clonk)) = 1;
EffectVar(6, clonk, GetEffect("IntGrappleControl", clonk)) = 10;
}
else
rope->HockAnchored();
}
public func Hit()
{
Stick();
@ -127,10 +150,135 @@ public func Entrance(object container)
public func OnRopeBreak()
{
RemoveEffect("IntGrappleControl", clonk);
RemoveObject();
return;
}
protected func Definition(def) {
SetProperty("Name", "$Name$", def);
}
/*-- Grapple rope controls --*/
public func FxIntGrappleControlControl(object target, int fxnum, ctrl, x,y,strength, repeat, release)
{
if(ctrl != CON_Up && ctrl != CON_Down && ctrl != CON_Right && ctrl != CON_Left) return;
if(ctrl == CON_Right)
{
EffectVar(3, target, fxnum) = !release;
}
if(ctrl == CON_Left)
{
EffectVar(2, target, fxnum) = !release;
}
if(ctrl == CON_Up)
{
EffectVar(0, target, fxnum) = !release;
if(target->GetAction() == "Jump" && !release && pull)
rope->ConnectPull();
}
if(ctrl == CON_Down)
{
EffectVar(1, target, fxnum) = !release;
}
}
local iSwingAnimation;
// Effect for smooth movement.
public func FxIntGrappleControlTimer(object target, int fxnum, int time)
{
// Movement.
if (EffectVar(0, target, fxnum))
if (rope && time%2 == 0)
rope->DoLength(-1);
if (EffectVar(1, target, fxnum))
if (rope)
rope->DoLength(+1);
if (EffectVar(2, target, fxnum))
{
rope->DoSpeed(-50);
SetXDir(GetXDir(100) - 1000, 100);
}
if (EffectVar(3, target, fxnum))
{
rope->DoSpeed(+50);
SetXDir(GetXDir(100) + 1000, 100);
}
if(target->GetAction() != "Jump")
{
if(rope->GetConnectStatus())
rope->ConnectLoose();
}
if(target->GetAction() == "Jump" && rope->GetConnectStatus() && !EffectVar(6, target, fxnum))
{
if(!EffectVar(4, target, fxnum))
target->SetTurnType(1);
target->SetObjDrawTransform(1000, 0, 3000*(1-2*target->GetDir()), 0, 1000);
if(EffectVar(0, target, fxnum))
{
if(EffectVar(4, target, fxnum) != 2)
{
EffectVar(4, target, fxnum) = 2;
target->PlayAnimation("RopeClimb", 10, Anim_Linear(target->GetAnimationLength("RopeClimb")/2, 0, target->GetAnimationLength("RopeClimb"), 35), Anim_Linear(0, 0, 1000, 5, ANIM_Remove));
}
}
else if(EffectVar(1, target, fxnum))
{
if(EffectVar(4, target, fxnum) != 3)
{
EffectVar(4, target, fxnum) = 3;
target->PlayAnimation("RopeDown", 10, Anim_Const(0), Anim_Linear(0, 0, 1000, 5, ANIM_Remove));
}
}
else if(EffectVar(2, target, fxnum) || EffectVar(3, target, fxnum))
{
var start = target->GetAnimationLength("RopeSwing")/2;
var length = target->GetAnimationLength("RopeSwing");
var dir = 0;
if( (EffectVar(2, target, fxnum) && !target->GetDir())
|| (!EffectVar(2, target, fxnum) && target->GetDir())
) dir = 1;
if(EffectVar(4, target, fxnum) != 4+dir)
{
iSwingAnimation = target->PlayAnimation("RopeSwing", 10, Anim_Linear(start, length*dir, length*(!dir), 35, ANIM_Hold), Anim_Linear(0, 0, 1000, 5, ANIM_Remove));
EffectVar(4, target, fxnum) = 4+dir;
}
}
else if(EffectVar(4, target, fxnum) != 1)
{
EffectVar(4, target, fxnum) = 1;
target->PlayAnimation("OnRope", 10, Anim_Linear(0, 0, target->GetAnimationLength("OnRope"), 35*2, ANIM_Loop), Anim_Linear(0, 0, 1000, 5, ANIM_Remove));
}
var angle = rope->GetClonkAngle();
var off = rope->GetClonkOff();
target->SetProperty("MeshTransformation", Trans_Mul(Trans_Translate(0, -10000), Trans_Rotate(angle,0,0,1), Trans_Translate(-off[0],-off[1]+10000)));
}
else if(EffectVar(4, target, fxnum))
{
target->SetProperty("MeshTransformation");
target->SetObjDrawTransform(1000, 0, 0, 0, 1000);
target->StopAnimation(target->GetRootAnimation(10));
EffectVar(4, target, fxnum) = 0;
}
if(EffectVar(6, target, fxnum)) EffectVar(6, target, fxnum)--;
return FX_OK;
}
public func FxIntGrappleControlStop(object target, int fxnum, int reason, int tmp)
{
if(tmp) return;
target->SetTurnType(0);
target->SetProperty("MeshTransformation");
target->StopAnimation(target->GetRootAnimation(10));
target->SetObjDrawTransform();
}

View File

@ -3,5 +3,9 @@ id=GrappleRope
Version=4,10,0,0
Category=C4D_StaticBack
Vertices=2
Line=C4D_LineRope
LineIntersect=0
#Line=C4D_LineRope
#LineIntersect=0
Width=5
Height=12
Offset=-1,-6
Picture=0,0,36,32

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -1,204 +1,258 @@
/*
Grapple Rope
Author: Maikel
Author: Randrian
The rope used for grappling devices.
Call BreakRope() to snap the rope.
Calls "OnRopeBreak" in both action targets when the rope snaps.
Rope snaps on overtension and bending (>5 vertices).
TODO: mix both, breaking should depend on a combination of both tension and bending.
Connect(obj1, obj2) connects two objects
BreakRope() breaks the rope
DrawIn() draws the hook in
*/
/*-- Rope length --*/
#include Library_Rope
local length; // The physical length of the rope.
static const Rope_Iterations = 10;
static const Rope_Precision = 100;
static const Rope_PointDistance = 10;
// The maximal physical length of the rope.
private func RopeMaxLength() { return 400; }
static const Weight = 1;
// Functions to modify the physical rope length.
public func GetLength() { return length; }
public func SetLength(int newlength) { length = BoundBy(newlength, 0, RopeMaxLength()); return; }
public func DoLength(int dolength) { length += dolength; length = BoundBy(length, 0, RopeMaxLength()); return; }
// Call this to set physical rope length to vertex length.
public func SetToVertexLength() { length = VertexLength(); return; }
// Calculates the the vertex length of the rope.
private func VertexLength()
// Call this to break the rope.
public func BreakRope()
{
var len = 0;
for (var i = 0; i < GetVertexNum() - 1; i++)
len += Distance (
GetVertex (i, VTX_X),
GetVertex (i, VTX_Y),
GetVertex (i + 1, VTX_X),
GetVertex (i + 1, VTX_Y));
return len;
if(length == -1) return;
length = -1;
var act1 = objects[0][0];
var act2 = objects[1][0];
SetAction("Idle");
// notify action targets.
if (act1 != nil)
act1->~OnRopeBreak();
if (act2 != nil)
act2->~OnRopeBreak();
RemoveRope();
RemoveObject();
return;
}
/* --------------------- Callbacks form the rope ---------------------- */
/* To be overloaded for special segment behaviour */
private func CreateSegment(int index, object previous)
{
if(index == 0) return;
var segment;
segment = CreateObject(GrappleRope);
return segment;
}
/*-- Rope connecting --*/
// Connects two objects to the rope, but the length will vary on their positions.
public func ConnectFree(object obj1, object obj2)
public func Connect(object obj1, object obj2)
{
SetAction("ConnectFree", obj1, obj2);
return;
}
StartRopeConnect(obj1, obj2);
Max_Length = 100;
HoockAnchored = 0;
// Connects two objects to the rope, which will try to keep constant length.
public func ConnectPull(object obj1, object obj2)
{
length = VertexLength();
SetAction("ConnectPull", obj1, obj2);
return;
}
/*-- Rope pulling --*/
private func RopeStrength() { return 40; } // How much a rope can maximally be stretched in % of the physical length.
private func RopeElasticity() { return 100; } // Defines stretching of the rope.
protected func PullObjects()
{
// Break rope on bending -> more than 5 vertices.
if (GetVertexNum() > 5)
{
Log("Rope break: overbending");
return BreakRope();
}
// Check rope strength.
if (100 * dif > RopeStrength() * (length + 10))
{
Log("Rope break: overtension");
return BreakRope();
}
// Only if vertex length larger than physical length.
var vlen = VertexLength();
var dif = vlen - length;
if (dif < 0)
{
length = vlen;
return;
}
// Rope to short.
if (vlen < 12)
return BreakRope();
// Get objects.
var obj1 = GetActionTarget(0);
var obj2 = GetActionTarget(1);
// Object positions.
var o1x = obj1->GetX();
var o1y = obj1->GetY();
var o2x = obj2->GetX();
var o2y = obj2->GetY();
// Get containers.
while (obj1->Contained())
obj1 = obj1->Contained();
while (obj2->Contained())
obj2 = obj2->Contained();
// Check movability.
var mov1 = 50;
var mov2 = 50;
if (obj1->Stuck() || obj1->GetCategory() & (C4D_StaticBack | C4D_Structure))
{
mov1 = 0;
mov2 *= 2;
}
if (obj2->Stuck() || obj2->GetCategory() & (C4D_StaticBack | C4D_Structure))
{
mov1 *= 2;
mov2 = 0;
}
if (!mov1 && !mov2)
return;
// Get vertex coordinates.
var v1x = GetVertex(1, VTX_X);
var v1y = GetVertex(1, VTX_Y);
var v2x = GetVertex(GetVertexNum() - 2, VTX_X);
var v2y = GetVertex(GetVertexNum() - 2, VTX_Y);
// Angles.
var ang1 = Angle(v1x, v1y, o1x, o1y);
var ang2 = Angle(v2x, v2y, o2x, o2y);
// Mass.
var mass1 = Max(5, obj1->GetMass());
var mass2 = Max(5, obj2->GetMass());
// Calculate rope acceleration.
var acc1 = 200 * dif * mov1 * RopeElasticity();
var acc2 = 200 * dif * mov2 * RopeElasticity();
acc1 /= 10 * mass1 * (length + 10);
acc2 /= 10 * mass2 * (length + 10);
SetAction("Hide");
// Artificial energy loss.
var xdir1 = 39 * obj1->GetXDir(1000) / 40;
var ydir1 = 39 * obj1->GetYDir(1000) / 40;
var xdir2 = 39 * obj2->GetXDir(1000) / 40;
var ydir2 = 39 * obj2->GetYDir(1000) / 40;
// Accelerate objects.
obj1->SetXDir(xdir1 - Sin(ang1, acc1), 1000);
obj1->SetYDir(ydir1 + Cos(ang1, acc1), 1000);
obj2->SetXDir(xdir2 - Sin(ang2, acc2), 1000);
obj2->SetYDir(ydir2 + Cos(ang2, acc2), 1000);
AddEffect("IntHang", this, 1, 1, this);
return;
}
/*-- Other --*/
public func GetConnectStatus() { return !length_auto; }
protected func Initialize()
local HoockAnchored;
/* Callback form the hook, when it hits ground */
public func HockAnchored(bool pull)
{
SetVertex(0, VTX_X, GetX()); SetVertex(0, VTX_Y, GetY());
SetVertex(1, VTX_X, GetX()); SetVertex(1, VTX_Y, GetY());
return;
if(pull)
ConnectPull();
HoockAnchored = 1;
}
// Call this to break the rope.
public func BreakRope()
public func HookRemoved()
{
var act1 = GetActionTarget(0);
var act2 = GetActionTarget(1);
SetAction("Idle");
// notify action targets.
if (act1)
act1->~OnRopeBreak();
if (act2)
act2->~OnRopeBreak();
RemoveObject();
return;
var new_hook = CreateObject(GrappleHook);
new_hook->New(objects[1][0]->Contained(), this);
objects[1][0]->SetHook(new_hook);
objects[0][0] = new_hook;
DrawIn();
}
/* Callback form the rope library */
public func MaxLengthReached()
{
var clonk = objects[1][0];
if(clonk->Contained()) clonk = clonk->Contained();
if(!HoockAnchored)
DrawIn();
else if(!clonk->GetContact(-1))
{
ConnectPull();
}
}
/* for swinging */
func DoSpeed(int value)
{
particles[-1][1][0] -= value;
}
func FxIntHangTimer() { TimeStep(); }
func FxDrawInTimer()
{
if(length < 15)
{
BreakRope();
return -1;
}
DoLength(-5);
}
func DrawIn()
{
if(!GetEffect("DrawIn", this)) AddEffect("DrawIn", this, 1, 1, this);
SetFixed(0, 1);
ConnectPull();
var clonk = objects[1][0];
if(clonk->Contained()) clonk = clonk->Contained();
RemoveEffect("IntGrappleControl", clonk);
}
func ConnectPull()
{
if(length_auto == 1 && objects[1][1])
{
var obj = objects[1][0];
if(obj->Contained()) obj = obj->Contained();
// Vector r of the last segment
var r = Vec_Sub(particles[-1][0],particles[-3][0]);
r = [-r[1], r[0]]; // Get the orthogonal vector
r = Vec_Normalize(r, 100); // Make it lenght 0
var v = [obj->GetXDir(Rope_Precision), obj->GetYDir(Rope_Precision)]; // Get the speed vector
var projection = Vec_Dot(r, Vec_Mul(v, 100))/10000; // Projekt the speed on the orthogonal vector
obj->SetXDir(r[0]*projection/100, Rope_Precision);
obj->SetYDir(r[1]*projection/100, Rope_Precision);
}
if(length_auto && objects[1][1])
{
var obj = objects[1][0];
if(obj->Contained()) obj = obj->Contained();
particles[-1][1][0] = particles[-1][0][0]-obj->SetXDir(Rope_Precision);
particles[-1][1][1] = particles[-1][0][1]-obj->SetYDir(Rope_Precision);
}
_inherited(...);
AccumulateForces();
TimeStep();
}
local hook_angle;
func UpdateLines()
{
var fTimeStep = 1;
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 = Angle(particles[i][0][0], particles[i][0][1], particles[i-1][0][0], particles[i-1][0][1]);
// Draw the left line
var start = particles[i-1][0];
var end = particles[i][0];
if(i == 1 && ParticleCount > 2)
{
angle = Angle(particles[2][0][0], particles[2][0][1], particles[0][0][0], particles[0][0][1]);
end = particles[0][0];
end[0] += -Sin(angle, 45*Rope_Precision/10);
end[1] += +Cos(angle, 45*Rope_Precision/10);
}
if(i == 2)
{
angle = Angle(particles[2][0][0], particles[2][0][1], particles[0][0][0], particles[0][0][1]);
start = particles[0][0];
start[0] += -Sin(angle, 45*Rope_Precision/10);
start[1] += +Cos(angle, 45*Rope_Precision/10);
}
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/Rope_Precision/10;
if(i == 1)
{
segments[i]->SetGraphics(nil, GrappleHook);
point[0] += -Cos(diffangle, 15*Rope_Precision/10)+Sin(diffangle, 4*Rope_Precision);
point[1] += -Cos(diffangle, 4*Rope_Precision)-Sin(diffangle, 15*Rope_Precision/10);
length = 1000;
}
SetLineTransform(segments[i], -diffangle, point[0]*10-GetPartX(i)*1000,point[1]*10-GetPartY(i)*1000, length );
// Remember the angle
oldangle = angle;
}
}
func GetClonkAngle()
{
if(ParticleCount > 3)
return Angle(particles[-1][0][0], particles[-1][0][1], particles[-3][0][0], particles[-3][0][1]);
}
local ClonkOldSpeed;
func GetClonkOff()
{
var clonk = objects[1][0];
var speed = [clonk->GetXDir(Rope_Precision), clonk->GetYDir(Rope_Precision)];
var offset = speed;
offset[0] = offset[0]*1000/Rope_Precision;
offset[1] = offset[1]*1000/Rope_Precision;
if(!ClonkOldSpeed)
{
ClonkOldSpeed = offset;
}
var ret = ClonkOldSpeed;
ClonkOldSpeed = offset;
return ret;
}
func SetSegmentTransform(obj, int r, int xoff, int yoff, int length) {
var fsin=Sin(r, 1000), fcos=Cos(r, 1000); //length = 1200;
// set matrix values
obj->SetObjDrawTransform (
+fcos, +fsin*length/1200, xoff,
-fsin, +fcos*length/1200, yoff,
);
}
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
);
}
protected func Definition(def) {
SetProperty("Name", "$Name$", def);
SetProperty("LineColors", [RGB(66,33,00) , RGB(66,33,00)], def);
SetProperty("ActMap", {
ConnectFree = {
Hide = {
Prototype = Action,
Name = "ConnectFree",
Procedure = DFA_CONNECT,
FacetBase = 1,
NextAction = "ConnectFree",
},
ConnectPull = {
Prototype = Action,
Name = "ConnectPull",
Procedure = DFA_CONNECT,
Length = 1,
Delay = 1,
FacetBase = 1,
NextAction = "ConnectPull",
StartCall = "PullObjects",
Name = "Hide",
},
}, def);
}

View File

@ -1,6 +1,6 @@
/*
Grapple Bow
Author: Maikel
Author: Randrian
A crossbow which is enabled to fire grappling hooks, also has a winching system.
*/
@ -18,11 +18,11 @@ public func SetHelp(object tohelp)
return;
}
public func GetCarryMode() { return CARRY_HandBack; }
public func GetCarryMode() { if(hook->Contained() == nil) return CARRY_Back; return CARRY_HandBack; }
public func GetCarrySpecial(clonk) { if(fAiming) return "pos_hand2"; }
public func GetCarryBone2(clonk) { return "main2"; }
public func GetCarryMode(clonk) { if(fAiming >= 0) return CARRY_Grappler; }
public func GetCarryMode(clonk) { if(hook->Contained() == nil) return CARRY_Back; if(fAiming >= 0) return CARRY_Grappler; }
/* +++++++++++ Controls ++++++++++++++ */
@ -36,9 +36,6 @@ func Initialize()
animation_set = {
AimMode = AIM_Position, // The aiming animation is done by adjusting the animation position to fit the angle
AnimationAim = "CrossbowAimArms",
// AnimationLoad = "BowLoadArms",
// LoadTime = 10,
// LoadTime2 = 5*10/20,
AnimationShoot = nil,
ShootTime = 20,
TurnType = 1,
@ -49,6 +46,11 @@ func Initialize()
OnRopeBreak();
}
public func SetHook(object new_hook)
{
hook = new_hook;
}
public func OnRopeBreak()
{
if(hook_attach)
@ -60,18 +62,33 @@ public func OnRopeBreak()
PlayAnimation("Load", 5, Anim_Const(GetAnimationLength("Load")), Anim_Const(1000));
}
protected func Destruction()
{
var rope = hook->GetRope();
if (rope)
rope->BreakRope();
}
protected func Departure()
{
var rope = hook->GetRope();
if (rope)
rope->DrawIn();
}
public func GetAnimationSet() { return animation_set; }
public func ControlUseStart(object clonk, int x, int y)
{
// Cut rope, or otherwise remove helper object.
if (help)
if (hook->Contained() != this)
{
var rope = help->GetRope();
var rope = hook->GetRope();
if (rope)
rope->BreakRope();
else
help->RemoveObject();
{
rope->DrawIn();
// rope->BreakRope();
}
return true;
}
@ -84,10 +101,9 @@ public func ControlUseStart(object clonk, int x, int y)
// Start aiming
fAiming = 1;
FinishedLoading(clonk);
// PlayAnimation("Draw", 6, Anim_Linear(0, 0, GetAnimationLength("Draw"), animation_set["LoadTime"], ANIM_Hold), Anim_Const(1000));
ControlUseHolding(clonk, x, y);
// clonk->StartLoad(this);
FinishedLoading(clonk);
return true;
}
@ -127,8 +143,6 @@ public func FinishedAiming(object clonk, int angle)
DetachMesh(hook_attach);
hook_attach = nil;
// shoot
// var hook = CreateObject(GrappleHook, 0, 0, NO_OWNER);
hook->Exit();
hook->Launch(angle, 100, clonk, this);
DetachMesh(hook_attach);
@ -136,7 +150,6 @@ public func FinishedAiming(object clonk, int angle)
// Open the hand to let the string go and play the fire animation
PlayAnimation("Fire", 6, Anim_Linear(0, 0, GetAnimationLength("Fire"), animation_set["ShootTime"], ANIM_Hold), Anim_Const(1000));
clonk->PlayAnimation("Close1Hand", 11, Anim_Const(0), Anim_Const(1000));
clonk->StartShoot(this);
return true;
}

View File

@ -5,12 +5,7 @@
local master, index;
local angle;
public func SetAngle(int new_angle)
{
angle = new_angle;
}
public func SetAngle(int new_angle) { angle = new_angle; }
public func SetMaster(new_master, new_index) { master = new_master; index = new_index; }

View File

@ -12,6 +12,8 @@
// 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;
@ -65,25 +67,6 @@ public func ControlUse(object clonk, int x, int y)
return true;
}
protected func AddSegment()
{
segments[ParticleCount] = CreateObject(Ropeladder_Segment);
segments[ParticleCount]->SetMaster(this, ParticleCount);
segments[ParticleCount]->SetNextLadder(segments[ParticleCount-1]);
segments[ParticleCount-1]->SetPreviousLadder(segments[ParticleCount]);
segments[ParticleCount]->SetGraphics("None");
var oldx = particles[ParticleCount][0][0];
var oldy = particles[ParticleCount][0][1];
particles[ParticleCount] = [[ oldx+UnrollDir*Ladder_Precision, oldy], [ oldx, oldy], [0,1*Ladder_Precision], 1]; // Pos, Oldpos, acceleration (gravity), mass
ParticleCount++;
UpdateSegmentOverlays();
}
public func UpdateSegmentOverlays()
{
for(var i = 1; i < GetLength(segments); i++)
@ -104,38 +87,6 @@ public func UpdateSegmentOverlays()
}
}
protected func RemoveSegment()
{
ParticleCount--;
segments[ParticleCount]->RemoveObject();
if(ParticleCount == 0)
{
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());
return;
}
SetLength(segments, ParticleCount);
SetLength(particles, ParticleCount);
segments[ParticleCount-1]->SetPreviousLadder(nil);
UpdateSegmentOverlays();
}
func TestMoveOut(xdir, ydir)
{
if(!Stuck()) return;
@ -213,32 +164,27 @@ public func Unroll(int dir, int unrolldir, int 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;
SetGraphics("Anchor");
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]];
// 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);
ParticleCount = 1;
segments = CreateArray(ParticleCount);
segments[0] = CreateObject(Ropeladder_Segment);
segments[0]->SetGraphics("None");
segments[0]->SetMaster(this, 0);
particles = CreateArray(ParticleCount);
for(var i = 0; i < MaxSegmentCount; i++)
particles[i] = [[ (GetX()+i*dir)*Ladder_Precision, GetY()*Ladder_Precision], [ (GetX()+i*1)*Ladder_Precision, GetY()*Ladder_Precision], [0,1*Ladder_Precision], 1]; // Pos, Oldpos, acceleration (gravity), mass
particles[0] = [[ GetX()*Ladder_Precision, GetY()*Ladder_Precision], [(GetX()+1)*Ladder_Precision, GetY()*Ladder_Precision], [0,1*Ladder_Precision], 0];
StartRope();
AddEffect("IntHang", this, 1, 1, this);
@ -257,7 +203,15 @@ func FxUnRollTimer()
StartRollUp();
return -1;
}
AddSegment();
AddSegment(UnrollDir*Ladder_Precision, 0);
}
func FxRollUpTimer() { if(ParticleCount == 0) return -1; RemoveSegment(); }
func FxIntHangTimer()
{
TimeStep();
if(!Stuck()) TestLength();
}
func TestLength()
@ -268,32 +222,62 @@ func TestLength()
StartRollUp();
}
func FxRollUpTimer() { if(ParticleCount == 0) return -1; RemoveSegment(); }
/* --------------------- Callbacks form the rope ---------------------- */
func FxIntHangTimer() { TimeStep(); }
// Verlet integration step
func Verlet(fFirst)
/* To be overloaded for special segment behaviour */
private func CreateSegment(int index, object previous)
{
var fTimeStep = 1;
for(var i = 1; i < ParticleCount; i++)
var segment;
if(index == 0)
{
var x = particles[i][0];
var temp = x;
var oldx = particles[i][1];
var a = particles[i][2];
// Verlet step, get speed out of distance moved relativ to the last position
particles[i][0][0] += x[0]-oldx[0]+a[0]*fTimeStep*fTimeStep;
particles[i][0][1] += x[1]-oldx[1]+a[1]*fTimeStep*fTimeStep;
particles[i][1] = temp;
segment = CreateObject(Ropeladder_Segment);
segment->SetGraphics("None");
segment->SetMaster(this, 0);
}
particles[0][0] = [GetX()*Ladder_Precision, GetY()*Ladder_Precision];
if(!Stuck()) TestLength();
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()
{UpdateSegmentOverlays();
{
var fTimeStep = 1;
var oldangle;
for(var i=1; i < ParticleCount; i++)
@ -423,22 +407,7 @@ func SetLineTransform(obj, int r, int xoff, int yoff, int length, int layer, int
);
}
func LogSpeed()
{
// Helperfunction for Debugpurpose
var array = [];
for(var i=0; i < ParticleCount; i++)
{
var x = particles[i][0];
var oldx = particles[i][1];
array[GetLength(array)] = Distance(x[0]-oldx[0], x[1]-oldx[1]);
}
Log("%v", array);
}
func GetPartX(index) { return (particles[index][0][0]+Ladder_Precision/2)/Ladder_Precision; }
func GetPartY(index) { return (particles[index][0][1]+Ladder_Precision/2)/Ladder_Precision; }
/* --------------------- Clonk on ladder ---------------------- */
public func OnLadderGrab(clonk, index)
{
// Do some speed when the clonk jumps on the ladder
@ -483,112 +452,6 @@ public func GetLadderData(index, &startx, &starty, &endx, &endy, &angle)
return true;
}
func SatisfyConstraints()
{
for(var j=0; j < Ladder_Iterations; j++)
{
// Satisfy all stick constraints (move the particles to fit the length)
for(var i=0; i < ParticleCount-1; i++)
{
// Keep length
var restlength = Ladder_SegmentLength*Ladder_Precision; // normal length between laddersegments
// Get coordinates and inverse masses
var x1 = particles[i][0];
var x2 = particles[i+1][0];
var invmass1 = particles[i][3];
var invmass2 = particles[i+1][3];
// calculate difference
var delta = Vec_Sub(x2,x1);
var deltalength = Sqrt(Vec_Dot(delta,delta));
var diff = 0;
if(deltalength != 0) // savety against division throught zero
diff = (deltalength-restlength)*1000/(deltalength*(invmass1+invmass2));
// Set new positions
particles[i][0] = Vec_Add(x1, Vec_Div(Vec_Mul(delta, invmass1*diff), 1000));
particles[i+1][0] = Vec_Sub(x2, Vec_Div(Vec_Mul(delta, invmass2*diff), 1000));
}
for(var i=0; i < ParticleCount; i++)
{
// Don't touch ground
if(GBackSolid(GetPartX(i)-GetX(), GetPartY(i)-GetY()) )
{
// Moving left?
var xdir = -1;
if(particles[i][0][0] < particles[i][1][0])
xdir = 1;
var ydir = -1;
// Moving up?
if(particles[i][0][1] < particles[i][1][1])
ydir = 1;
// Look for all possible places where the particle could move (from nearest to farest)
for(var pos in TestArray)
{
if(!GBackSolid(GetPartX(i)-GetX()+xdir*pos[0], GetPartY(i)-GetY()+ydir*pos[1]))
{
// Calculate the new position (if we don't move in a direction don't overwrite the old value)
var new = [0,0];
if(pos[0])
new[0] = (GetPartX(i)+xdir*pos[0])*Ladder_Precision-xdir*Ladder_Precision/2;
else
new[0] = particles[i][0][0];
if(pos[1])
new[1] = (GetPartY(i)+ydir*pos[1])*Ladder_Precision-ydir*Ladder_Precision/2;
else
new[1] = particles[i][0][1];
// Calculate the normalvector to apply the normal force accordingly
var dif = Vec_Sub(new, particles[i][0]);
var vel = Vec_Sub(particles[i][0], particles[i][1]);
var tang = [dif[1], -dif[0]];
var speed = Vec_Length(vel);
var normalforce = Vec_Length(dif);
// if the force is smaller then the speed decelerate
if(normalforce < speed)
{
vel = Vec_Mul(vel, 1000-normalforce*1000/speed);
vel = Vec_Div(vel, 1000);
particles[i][1] = Vec_Sub(new, vel);
}
// If not stop instantly
else
particles[i][1] = new;
particles[i][0] = new;
break;
}
}
}
}
}
}
func LogArray()
{
// Helperfunction which has created "TestArray"
var array = [];
for(var dist = 1; dist < 10; dist++)
for(var x = 0; x < 10; x++)
for(var y = 0; y < 10; y++)
{
if(Distance(0,0,x,y) != dist) continue;
array[GetLength(array)] = [x,y];
}
Log("%v", array);
}
func TimeStep() {
Verlet();
SatisfyConstraints();
UpdateLines();
}
// Some vector math
func Vec_Sub(array x, array y) { return [x[0]-y[0], x[1]-y[1]]; }
func Vec_Add(array x, array y) { return [x[0]+y[0], x[1]+y[1]]; }
func Vec_Mul(array x, int i) { return [x[0]*i, x[1]*i]; }
func Vec_Div(array x, int i) { return [x[0]/i, x[1]/i]; }
func Vec_Dot(array x, array y) { return x[0]*y[0]+x[1]*y[1]; }
func Vec_Length(array x) { return Sqrt(x[0]*x[0]+x[1]*x[1]); }
func Vec_Angle(array x, array y) { return Angle(x[0], x[1], y[0], y[1]); }
func Definition(def) {
SetProperty("ActMap", {

View File

@ -0,0 +1,4 @@
[DefCore]
id=Library_Rope
Version=4,10,0,0
Category=C4D_StaticBack

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

View File

@ -0,0 +1,513 @@
/*--
Rope control
Authors: Randrian
Containes the basic functionality for ladders.
--*/
static const Rope_MaxParticles = 15;//30;//15*3;
static const Rope_Iterations = 10;
static const Rope_Precision = 100;
static const Rope_SegmentLength = 5;//2;
local particles;
local segments;
local TestArray;
local ParticleCount;
local objects;
local length;
local length_auto;
local Max_Length;
protected func StartRope()
{
objects = [[this, 0], [nil, nil]];
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]];
length = Rope_SegmentLength;
ParticleCount = 1;
segments = CreateArray(ParticleCount);
segments[0] = CreateSegment(0, nil);
particles = CreateArray(ParticleCount);
particles[0] = [[ GetX()*Rope_Precision, GetY()*Rope_Precision], [(GetX()+1)*Rope_Precision, GetY()*Rope_Precision], [0,1*Rope_Precision], 0];
}
public func StartRopeConnect(object obj1, object obj2)
{
length = ObjectDistance(obj1, obj2);
objects = [[obj1, 0], [obj2, 1]];
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]];
ParticleCount = length/Rope_SegmentLength;
var yoff = 0;
if(ParticleCount < 2)
{
ParticleCount = 2;
yoff = 1;
length = 10;
}
segments = CreateArray(ParticleCount);
for(var i = 0; i < ParticleCount; i++)
{
var prev = nil;
if(i > 0) prev = segments[i-1];
segments[i] = CreateSegment(i, prev);
}
particles = CreateArray(ParticleCount);
var x, y;
for(var i = 0; i < ParticleCount; i++)
{
x = obj1->GetX(Rope_Precision)*(ParticleCount-i)/ParticleCount+obj2->GetX(Rope_Precision)*i/ParticleCount;
y = obj1->GetY(Rope_Precision)*(ParticleCount-i)/ParticleCount+obj2->GetY(Rope_Precision)*i/ParticleCount;
y += yoff*i;
particles[i] = [[ x, y], [ x, y], [0,1*Rope_Precision], 1]; // Pos, Oldpos, acceleration (gravity), mass
}
particles[0][2] = [0,0];
particles[0][3] = 0;
particles[-1][2] = [0,0];
particles[-1][3] = 1;
ConnectLoose();
UpdateSegmentOverlays();
TimeStep();
return;
}
protected func Destruction()
{
RemoveRope();
}
public func RemoveRope()
{
if(segments)
for(var segment in segments)
DeleteSegment(segment);
}
public func SetFixed(bool fixed_1, bool fixed_2)
{
objects[0][1] = !fixed_1;
objects[1][1] = !fixed_2;
particles[ 0][3] = objects[0][1];
particles[-1][3] = objects[1][1];
}
func ConnectLoose()
{
length_auto = 1;
}
func ConnectPull()
{
length_auto = 0;
}
/* To be overloaded for special segment behaviour */
private func CreateSegment(int index, object previous) { }
private func DeleteSegment(object segment, previous)
{
if(segment)
segment->RemoveObject();
}
/* When the last segment is removed */
private func RopeRemoved() { }
/* Adding and removing segments */
public func AddSegment(int xoffset, int yoffset)
{
segments[ParticleCount] = CreateSegment(ParticleCount, segments[ParticleCount-1]);
var oldx = particles[ParticleCount-1][0][0];
var oldy = particles[ParticleCount-1][0][1];
particles[ParticleCount] = [[ oldx+xoffset, oldy+yoffset], [ oldx, oldy], [0,1*Rope_Precision], 1]; // Pos, Oldpos, acceleration (gravity), mass
ParticleCount++;
length += Rope_SegmentLength;
UpdateSegmentOverlays();
}
public func PickSegment(int index) // Removes a segment form the middle
{
if(index >= ParticleCount-1) return RemoveSegment();
var previous = nil;
if(index > 0) previous = segments[index-1];
DeleteSegment(segments[index], previous);
for(var i = index; i < ParticleCount-1; i++)
{
segments[i] = segments[i+1];
particles[i] = particles[i+1];
}
ParticleCount--;
SetLength(segments, ParticleCount);
SetLength(particles, ParticleCount);
UpdateSegmentOverlays();
}
public func RemoveSegment(fNoLengthAdjust)
{
ParticleCount--;
var previous = nil;
if(ParticleCount-1 >= 0) previous = segments[ParticleCount-1];
DeleteSegment(segments[ParticleCount], previous);
if(ParticleCount == 0)
{
RopeRemoved();
return;
}
if(!fNoLengthAdjust)
length -= Rope_SegmentLength;
SetLength(segments, ParticleCount);
SetLength(particles, ParticleCount);
UpdateSegmentOverlays();
}
public func MaxLengthReached() { }
public func DoLength(int dolength)
{
length += dolength;
if(Max_Length)
if(length > Max_Length)
{
MaxLengthReached();
length = Max_Length;
}
if(length < Rope_SegmentLength*2) length = Rope_SegmentLength*2;
var last_length = GetLastLength();
// Remove Points
while( last_length < Rope_SegmentLength*Rope_Precision/2 && ParticleCount > 2)
{
particles[ParticleCount-2] = particles[ParticleCount-1];
RemoveSegment(1);
last_length = GetLastLength();
}
var i = 0;
while( last_length > Rope_SegmentLength*Rope_Precision*3/2)
{
ParticleCount++;
SetLength(particles, ParticleCount);
var x2 = particles[ParticleCount-2][0][0];
var y2 = particles[ParticleCount-2][0][1];
var x4 = particles[ParticleCount-2][1][0];
var y4 = particles[ParticleCount-2][1][1];
particles[-1] = [[0,0], [0,0], [0,0], 0];
particles[-1] = [[x2, y2], [x4, y4], [0,2*Rope_Precision], 1];
for(var i = ParticleCount-2; i > 0; i--)
{
var x = particles[i-1][0][0];
var y = particles[i-1][0][1];
var x2 = particles[i][0][0];
var y2 = particles[i][0][1];
var x3 = particles[i-1][1][0];
var y3 = particles[i-1][1][1];
var x4 = particles[i][1][0];
var y4 = particles[i][1][1];
particles[i] = [[ x/ParticleCount +x2*(ParticleCount-1)/ParticleCount, y/ParticleCount +y2*(ParticleCount-1)/ParticleCount],
[ x3/ParticleCount+x4*(ParticleCount-1)/ParticleCount, y3/ParticleCount+y4*(ParticleCount-1)/ParticleCount], [0,1*Rope_Precision], 1];
}
SetLength(segments, ParticleCount);
segments[ParticleCount-1] = CreateSegment(ParticleCount, segments[ParticleCount-2]);
last_length = GetLastLength();
}
UpdateLines();
particles[ 0][3] = objects[0][1];
particles[-1][3] = objects[1][1];
return;
}
func GetLastLength()
{
return length*Rope_Precision-Rope_SegmentLength*Rope_Precision*(ParticleCount-1);
}
/* for the graphics appeareance should be overloaded */
private func UpdateSegmentOverlays() { }
private func UpdateLines() {}
/* The procedure of a time step, this should be called with a timercall or an effect! */
public func TimeStep()
{
if(length_auto) AccumulateForces();
Verlet();
SatisfyConstraints();
ForcesOnObjects();
UpdateLines();
}
func AccumulateForces()
{
for(var i = 1; i < ParticleCount; i++)
{
var fx = 0, fy = 0, angle;
if(i < ParticleCount-2 && length_auto)
{
angle = Angle(particles[i][0][0], particles[i][0][1], particles[i+1][0][0], particles[i+1][0][1]);
fx = Sin(angle, 5*Rope_Precision);
fy =-Cos(angle, 5*Rope_Precision);
/* angle = Angle(particles[i-1][0][0], particles[i-1][0][1], particles[i+1][0][0], particles[i+1][0][1]);
var middle = Vec_Div(Vec_Add(particles[i-1][0], particles[i+1][0]), 1);
var diff = Vec_Sub(middle, particles[i][0]);
var length = Vec_Length(diff);
fx = Sin(angle, length/2);
fy =-Cos(angle, length/2);*/
}
particles[i][2] = [fx,fy+1*Rope_Precision];
}
}
// Verlet integration step
private func Verlet(fFirst)
{
var fTimeStep = 1;
// Copy Position of the objects
var j = 0;
for(var i = 0; i < 2; i++ || j--)
{
if(objects[i][1] == 0)
SetParticleToObject(j, i);
}
// Verlet
for(var i = 1; i < ParticleCount; i++)
{
var x = particles[i][0];
var temp = x;
var oldx = particles[i][1];
var a = particles[i][2];
// Verlet step, get speed out of distance moved relativ to the last position
particles[i][0][0] += x[0]-oldx[0]+a[0]*fTimeStep*fTimeStep;
particles[i][0][1] += x[1]-oldx[1]+a[1]*fTimeStep*fTimeStep;
particles[i][1] = temp;
}
}
public func SetParticleToObject(int index, int obj_index)
{
var obj = objects[obj_index][0];
if(obj == nil) return;
if(obj->Contained()) obj = obj->Contained();
particles[index][0][0] = obj->GetX(Rope_Precision);
particles[index][0][1] = obj->GetY(Rope_Precision);
particles[index][1][0] = particles[index][0][0];
particles[index][1][1] = particles[index][0][1];
particles[index][1][0] = obj->GetX(Rope_Precision);
particles[index][1][1] = obj->GetY(Rope_Precision);
}
private func SatisfyConstraints()
{
var segment_pick = nil;
for(var j=0; j < Rope_Iterations; j++)
{
if(length_auto)
{
// Copy Position of the objects
for(var i = 0, i2 = 0; i < 2; i++ || i2--)
SetParticleToObject(i2, i);
}
// Satisfy all stick constraints (move the particles to fit the length)
for(var i=0; i < ParticleCount-1; i++)
{
// Keep length
var restlength = Rope_SegmentLength*Rope_Precision; // normal length between two points
//var restlength = Rope_PointDistance*Rope_Precision; // normal length between laddersegments
if(i == ParticleCount-2)
{
restlength = GetLastLength();
}
// Get coordinates and inverse masses
var x1 = particles[i][0];
var x2 = particles[i+1][0];
var invmass1 = particles[i][3];
var invmass2 = particles[i+1][3];
// calculate difference
var delta = Vec_Sub(x2,x1);
var deltalength = Sqrt(Vec_Dot(delta,delta));
if(deltalength < restlength)
{
if(deltalength < restlength*3/4 && length_auto && i < ParticleCount-3)
segment_pick = i;
continue;
}
var diff = 0;
if(deltalength != 0) // savety against division throught zero
diff = (deltalength-restlength)*1000/(deltalength*(invmass1+invmass2));
// Set new positions
particles[i][0] = Vec_Add(x1, Vec_Div(Vec_Mul(delta, invmass1*diff), 1000));
particles[i+1][0] = Vec_Sub(x2, Vec_Div(Vec_Mul(delta, invmass2*diff), 1000));
}
if(segment_pick != nil)
;//PickSegment(segment_pick);
for(var i=0; i < ParticleCount; i++)
{
// Don't touch ground
if(GBackSolid(GetPartX(i)-GetX(), GetPartY(i)-GetY()) )
{
// Moving left?
var xdir = -1;
if(particles[i][0][0] < particles[i][1][0])
xdir = 1;
var ydir = -1;
// Moving up?
if(particles[i][0][1] < particles[i][1][1])
ydir = 1;
// Look for all possible places where the particle could move (from nearest to farest)
for(var pos in TestArray)
{
if(!GBackSolid(GetPartX(i)-GetX()+xdir*pos[0], GetPartY(i)-GetY()+ydir*pos[1]))
{
// Calculate the new position (if we don't move in a direction don't overwrite the old value)
var new = [0,0];
if(pos[0])
new[0] = (GetPartX(i)+xdir*pos[0])*Rope_Precision-xdir*Rope_Precision/2;
else
new[0] = particles[i][0][0];
if(pos[1])
new[1] = (GetPartY(i)+ydir*pos[1])*Rope_Precision-ydir*Rope_Precision/2;
else
new[1] = particles[i][0][1];
// Calculate the normalvector to apply the normal force accordingly
var dif = Vec_Sub(new, particles[i][0]);
var vel = Vec_Sub(particles[i][0], particles[i][1]);
var tang = [dif[1], -dif[0]];
var speed = Vec_Length(vel);
var normalforce = Vec_Length(dif);
// if the force is smaller then the speed decelerate
if(normalforce < speed)
{
vel = Vec_Mul(vel, 1000-normalforce*1000/speed);
vel = Vec_Div(vel, 1000);
particles[i][1] = Vec_Sub(new, vel);
}
// If not stop instantly
else
particles[i][1] = new;
particles[i][0] = new;
break;
}
}
}
}
}
}
func GetLineLength()
{
var length_vertex = 0;
for(var i=1; i < ParticleCount; i++)
length_vertex += Distance(particles[i][0][0], particles[i][0][1], particles[i-1][0][0], particles[i-1][0][1]);
return length_vertex;
}
func ForcesOnObjects()
{
if(!length) return;
var length_vertex = 0;
var redo = 5;
for(var i=1; i < ParticleCount; i++)
length_vertex += Distance(particles[i][0][0], particles[i][0][1], particles[i-1][0][0], particles[i-1][0][1]);
while( length_auto && redo)
{
var speed = Vec_Length(Vec_Sub(particles[-1][0], particles[-1][1]));
if(speed > 150) DoLength(1);
else if(speed < 50) DoLength(-1);
else redo = 0;
if(redo) redo --;
particles[-1][3] = 1;
SatisfyConstraints();
}
var j = 0;
if(!length_auto || length == Max_Length)
for(var i = 0; i < 2; i++)
{
if(i == 1) j = ParticleCount-1;
var obj = objects[i][0];
if(obj == nil || objects[i][1] == 0) continue;
if(obj->Contained()) obj = obj->Contained();
var speed = obj->GetXDir(Rope_Precision);
var x = obj->GetX(Rope_Precision), y = obj->GetY(Rope_Precision);
obj->SetPosition(particles[j][0][0], particles[j][0][1], 1, Rope_Precision);
if(obj->Stuck())
obj->SetPosition(x, y, 1, Rope_Precision);
obj->SetXDir( particles[j][0][0]-particles[j][1][0], Rope_Precision);
obj->SetYDir( particles[j][0][1]-particles[j][1][1], Rope_Precision);
}
}
/* Helperstuff */
private func LogArray()
{
// Helperfunction which has created "TestArray"
var array = [];
for(var dist = 1; dist < 10; dist++)
for(var x = 0; x < 10; x++)
for(var y = 0; y < 10; y++)
{
if(Distance(0,0,x,y) != dist) continue;
array[GetLength(array)] = [x,y];
}
Log("%v", array);
}
func LogSpeed()
{
// Helperfunction for Debugpurpose
var array = [];
for(var i=0; i < ParticleCount; i++)
{
var x = particles[i][0];
var oldx = particles[i][1];
array[GetLength(array)] = Distance(x[0]-oldx[0], x[1]-oldx[1]);
}
Log("%v", array);
}
// Some vector math
func Vec_Sub(array x, array y) { return [x[0]-y[0], x[1]-y[1]]; }
func Vec_Add(array x, array y) { return [x[0]+y[0], x[1]+y[1]]; }
func Vec_Mul(array x, int i) { return [x[0]*i, x[1]*i]; }
func Vec_Div(array x, int i) { return [x[0]/i, x[1]/i]; }
func Vec_Dot(array x, array y) { return x[0]*y[0]+x[1]*y[1]; }
func Vec_Length(array x) { return Sqrt(x[0]*x[0]+x[1]*x[1]); }
func Vec_Angle(array x, array y) { return Angle(x[0], x[1], y[0], y[1]); }
func Vec_Normalize(array x, int precision) { return Vec_Div(Vec_Mul(x, precision), Vec_Length(x)); }
func GetPartX(index) { return (particles[index][0][0]+Rope_Precision/2)/Rope_Precision; }
func GetPartY(index) { return (particles[index][0][1]+Rope_Precision/2)/Rope_Precision; }

View File

@ -2,9 +2,13 @@
func Initialize()
{
CreateObject(Ropeladder, 174, 445)->Unroll(-1);
// CreateObject(Ropeladder, 174, 445)->Unroll(-1, 0, 25);
CreateObject(Ropeladder, 328, 564);
CreateObject(Ropeladder, 226, 330);
var Rock1 = CreateObject(Rock, 159, 363);
var Rock2 = CreateObject(Rock, 232, 388);
// CreateObject(Ropeladder, 197, 432)->MakeBridge(Rock1, Rock2);
}
func InitializePlayer(int iPlr, int iX, int iY, object pBase, int iTeam)
@ -27,11 +31,11 @@ func Initialize()
var clonk = GetCrew(iPlr);
clonk->DoEnergy(100000);
clonk->SetPosition(50, 490);
clonk->CreateContents(Musket);
clonk->CreateContents(LeadShot);
// clonk->CreateContents(Musket);
// clonk->CreateContents(LeadShot);
// clonk->CreateContents(Javelin);
// clonk->CreateContents(DynamiteBox);
// clonk->CreateContents(GrappleBow);
clonk->CreateContents(GrappleBow);
// clonk->CreateContents(Bow);
// clonk->Collect(CreateObject(Arrow));
return;

View File

@ -511,6 +511,13 @@ void C4Object::ForcePosition(int32_t tx, int32_t ty)
UpdateSolidMask(false);
}
void C4Object::ForcePosition(int32_t tx, int32_t ty, long iPrec)
{
fix_x=itofix(tx, iPrec); fix_y=itofix(ty, iPrec);
UpdatePos();
UpdateSolidMask(false);
}
void C4Object::MovePosition(int32_t dx, int32_t dy)
{
// move object position; repositions SolidMask

View File

@ -306,6 +306,7 @@ public:
bool Exit(int32_t iX=0, int32_t iY=0, int32_t iR=0, C4Real iXDir=Fix0, C4Real iYDir=Fix0, C4Real iRDir=Fix0, bool fCalls=true);
void CopyMotion(C4Object *from);
void ForcePosition(int32_t tx, int32_t ty);
void ForcePosition(int32_t tx, int32_t ty, long iPrec);
void MovePosition(int32_t dx, int32_t dy);
void DoMotion(int32_t mx, int32_t my);
bool ActivateEntrance(int32_t by_plr, C4Object *by_obj);

View File

@ -470,16 +470,17 @@ static C4Void FnRemoveObject(C4AulObjectContext *cthr, bool fEjectContents)
return C4VNull;
}
static C4Void FnSetPosition(C4AulObjectContext *cthr, long iX, long iY, bool fCheckBounds)
static C4Void FnSetPosition(C4AulObjectContext *cthr, long iX, long iY, bool fCheckBounds, long iPrec)
{
if (!iPrec) iPrec = 1;
if (fCheckBounds)
{
// BoundsCheck takes ref to C4Real and not to long
C4Real i_x = itofix(iX), i_y = itofix(iY);
C4Real i_x = itofix(iX, iPrec), i_y = itofix(iY, iPrec);
cthr->Obj->BoundsCheck(i_x, i_y);
iX = fixtoi(i_x); iY = fixtoi(i_y);
iX = fixtoi(i_x, iPrec); iY = fixtoi(i_y, iPrec);
}
cthr->Obj->ForcePosition(iX,iY);
cthr->Obj->ForcePosition(iX,iY, iPrec);
// update liquid
cthr->Obj->UpdateInLiquid();
return C4VNull;
@ -1205,10 +1206,11 @@ static long FnGetComDir(C4AulObjectContext *cthr)
return cthr->Obj->Action.ComDir;
}
static Nillable<long> FnGetX(C4AulContext *cthr)
static Nillable<long> FnGetX(C4AulContext *cthr, long iPrec)
{
if (!cthr->Obj) return C4VNull;
return cthr->Obj->GetX();
if (!iPrec) iPrec = 1;
return fixtoi(cthr->Obj->fix_x, iPrec);
}
static long FnGetVertexNum(C4AulObjectContext *cthr)
@ -1299,10 +1301,11 @@ static C4Void FnSetContactDensity(C4AulObjectContext *cthr, long iDensity)
return C4VNull;
}
static Nillable<long> FnGetY(C4AulContext *cthr)
static Nillable<long> FnGetY(C4AulContext *cthr, long iPrec)
{
if (!cthr->Obj) return C4VNull;
return cthr->Obj->GetY();
if (!iPrec) iPrec = 1;
return fixtoi(cthr->Obj->fix_y, iPrec);
}
static bool FnGetAlive(C4AulObjectContext *cthr)