openclonk/planet/Experimental.ocd/LiftTower.ocd/Rope.ocd/Script.c

256 lines
7.0 KiB
C

/*
Lift Tower Rope
Author: Randrian, Clonkonaut
The rope used for the lift tower.
Connect(obj1, obj2) connects two objects
BreakRope() breaks the rope
*/
#include Library_Rope
static const Library_Rope_MAXLENGTH = 1000;
// Call this to break the rope.
public func BreakRope(bool silent)
{
if(lib_rope_length == -1) return;
lib_rope_length = -1;
var act1 = lib_rope_objects[0][0];
var act2 = lib_rope_objects[1][0];
SetAction("Idle");
// notify action targets.
if (act1 != nil && !silent)
act1->~OnRopeBreak();
if (act2 != nil && !silent)
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 = CreateObjectAbove(LiftTower_Rope);
return segment;
}
/*-- Rope connecting --*/
// Connects two objects to the rope, but the length will vary on their positions.
public func Connect(object obj1, object obj2, int max_length)
{
StartRopeConnect(obj1, obj2);
if (!max_length) max_length = Library_Rope_MAXLENGTH;
SetMaxLength(max_length);
SetFixed(true, false);
SetAction("Hide");
AddEffect("IntHang", this, 1, 1, this);
return;
}
public func Reconnect(object reconnect)
{
lib_rope_objects[1][0] = reconnect;
}
public func GetConnectStatus() { return !lib_rope_length_auto; }
public func HookRemoved()
{
BreakRope();
}
func FxIntHangTimer() { TimeStep(); }
func UpdateLines()
{
var oldangle;
for(var i=1; i < lib_rope_particle_count; i++)
{
// Update the Position of the Segment
lib_rope_segments[i]->SetPosition(GetPartX(i), GetPartY(i));
// Calculate the angle to the previous segment
var angle = Angle(lib_rope_particles[i].x, lib_rope_particles[i].y, lib_rope_particles[i-1].x, lib_rope_particles[i-1].y);
// Draw the left line
var start = [lib_rope_particles[i-1].x, lib_rope_particles[i-1].y];
var end = [lib_rope_particles[i].x, lib_rope_particles[i].y];
if(i == 1 && lib_rope_particle_count > 2)
{
angle = Angle(lib_rope_particles[2].x, lib_rope_particles[2].y, lib_rope_particles[0].x, lib_rope_particles[0].y);
end = [lib_rope_particles[0].x, lib_rope_particles[0].y];
end[0] += -Sin(angle, 45*LIB_ROPE_Precision/10);
end[1] += +Cos(angle, 45*LIB_ROPE_Precision/10);
lib_rope_segments[i]->SetGraphics("Invis");
}
if(i == 2)
{
angle = Angle(lib_rope_particles[2].x, lib_rope_particles[2].y, lib_rope_particles[0].x, lib_rope_particles[0].y);
start = [lib_rope_particles[0].x, lib_rope_particles[0].y];
start[0] += -Sin(angle, 45*LIB_ROPE_Precision/10);
start[1] += +Cos(angle, 45*LIB_ROPE_Precision/10);
lib_rope_segments[i]->SetGraphics("Short");
}
var diff = Vec_Sub(end,start);
var point = Vec_Add(start, Vec_Div(diff, 2));
var diffangle = Vec_Angle(diff, [0,0]);
var length = Vec_Length(diff)*1000/LIB_ROPE_Precision/10;
if(i == lib_rope_particle_count-1)
{
var old = [lib_rope_particles[i-2].x, lib_rope_particles[i-2].y];
var old_diff = Vec_Sub(start,old);
var o_length = Vec_Length(old_diff)*1000/LIB_ROPE_Precision/10;
if(!o_length) diff = old_diff;
else diff = Vec_Div(Vec_Mul(old_diff, length),o_length);
diffangle = Vec_Angle(diff, [0,0]);
point = Vec_Add(start, Vec_Div(diff, 2));
}
lib_rope_segments[i]->SetGraphics(nil);
SetLineTransform(lib_rope_segments[i], -diffangle, point[0]*10-GetPartX(i)*1000,point[1]*10-GetPartY(i)*1000, length );
// Remember the angle
oldangle = angle;
}
}
public func GetHookAngle()
{
if(lib_rope_particle_count > 3)
return Angle(lib_rope_particles[-2].x, lib_rope_particles[-2].y, lib_rope_particles[-3].x, lib_rope_particles[-3].y)+180;
}
public 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
);
}
/* Overload */
local pull_position, pull_faults, pull_frame;
// Altered to not just pull the objects into the rope's direction but
// if the object doesn't not move it is tried to shake it free by applying
// impulses to every direction
func ForcesOnObjects()
{
if(!lib_rope_length) return;
var redo = LengthAutoTryCount();
while(lib_rope_length_auto && redo)
{
var speed = Vec_Length(Vec_Sub([lib_rope_particles[-1].x, lib_rope_particles[-1].y], [lib_rope_particles[-1].oldx, lib_rope_particles[-1].oldy]));
if(lib_rope_length == GetMaxLength())
{
if(ObjContact(lib_rope_objects[1][0]))
speed = 40;
else speed = 100;
}
if(speed > 150) DoLength(1);
else if(speed < 50) DoLength(-1);
else redo = 0;
if(redo) redo --;
}
var j = 0;
if (PullObjects())
for (var i = 0; i < 2; i++)
{
if (i == 1) j = lib_rope_particle_count-1;
var obj = lib_rope_objects[i][0];
if (obj == nil || !lib_rope_objects[i][1]) continue;
if (obj->Contained())
obj = obj->Contained();
if (obj->GetAction() == "Walk" || obj->GetAction() == "Scale" || obj->GetAction() == "Hangle" || obj->GetAction() == "Climb")
obj->SetAction("Jump");
var xdir = BoundBy(lib_rope_particles[j].x-lib_rope_particles[j].oldx, -100, 100);
var ydir = lib_rope_particles[j].y-lib_rope_particles[j].oldy;
if (!obj->GetContact(-1))
ydir = BoundBy(ydir, -120, 120);
if (pull_position && pull_frame != FrameCounter() && !Distance(pull_position[0], pull_position[1], obj->GetX(), obj->GetY()))
{
if (!pull_faults)
{
ydir *= -1;
pull_faults++;
}
else
{
xdir *= -1;
pull_faults = 0;
}
}
else
{
pull_position = [obj->GetX(), obj->GetY()];
pull_faults = 0;
}
pull_frame = FrameCounter();
obj->SetXDir( xdir, LIB_ROPE_Precision);
obj->SetYDir( obj->GetYDir(LIB_ROPE_Precision) + ydir, LIB_ROPE_Precision);
}
}
// Altered to function in 'ConnectPull' mode
public func ConstraintObjects()
{
if(lib_rope_length < GetMaxLength()) // in the rope library this is
{
for (var i = 0, i2 = 0; i < 2; i++ || i2--)
SetParticleToObject(i2, i);
}
}
// This is called constantly by the lift tower as long as something is reeled in
// Altered so the rope will not get shorter than the distance between the tower
// and the hooked up object
public func DoLength(int dolength)
{
var obj = lib_rope_objects[0][0]; // First connected object
var obj2 = lib_rope_objects[1][0]; // Second connected object
// I guess this is not possible but just to be sure
if (!obj || !obj2) return _inherited(dolength);
if (obj->Contained()) obj = obj->Contained();
if (obj2->Contained()) obj2 = obj2->Contained();
// Line would be shorter than the distance? Do nothing
if (dolength < 0 && ObjectDistance(obj, obj2) > GetLineLength()/100) return;
return _inherited(dolength);
}
func Definition(def)
{
def.LineColors = [RGB(66,33,00), RGB(66,33,00)];
}
local ActMap = {
Hide = {
Prototype = Action,
Name = "Hide",
},
};
local Name = "$Name$";