forked from Mirrors/openclonk
177 lines
6.7 KiB
C
177 lines
6.7 KiB
C
//Makes objects follow the solid mask of a vehicle if they are touching it (within a specified rectangle)
|
|
//Works for both livings and vehicles.
|
|
|
|
//To enable in a vehicle, you need two things:
|
|
//1. A parent object which will rotate 'children' to match it's rotation on the Z axis (screen-vertical)
|
|
// which either AlignObjectsToRotation or AlignToRotation will be called from, and
|
|
//2. A 'turnTarget' which will be queried for the Z-angle, in the form of 'turnTarget->GetTurnAngle()'.
|
|
// This function should return a value from 0-180, where 0 is turned completely left, and 180 right.
|
|
|
|
// The turnTarget can be the same object as the parent object. The reason they are separated is due
|
|
// to the separation of the 3D graphic and solidmask object of any solidmask enabled 3D vehicle.
|
|
// In this way, the 3D graphic module of a vehicle can query its own animation position.
|
|
|
|
///Function must be called from vehicle context
|
|
///param x,y,w,h = target rectangle to look for objects in to follow the vehicle's rotation
|
|
func AlignObjectsToRotation(object turnTarget, num x, num y, num w, num h)
|
|
{
|
|
if(!this) FatalError("Function AlignToRotation must be called from object (vehicle) context");
|
|
if(!turnTarget) FatalError("Function requires a valid turnTarget to get rotation value from");
|
|
|
|
if(x == nil) x = this->GetDefCoreVal("Offset", "DefCore", 0);
|
|
if(y == nil) y = this->GetDefCoreVal("Offset", "DefCore", 1);
|
|
if(w == nil) w = this->GetDefCoreVal("Width", "DefCore");
|
|
if(h == nil) h = this->GetDefCoreVal("Height", "DefCore");
|
|
|
|
//make objects follow ship rotation
|
|
for(var targetObj in FindObjects(Find_Not(Find_ID(this)), Find_NoContainer(), Find_Or(Find_Category(C4D_Living), Find_Category(C4D_Object), Find_Func("IsVehicle")), Find_InRect(x,y,w,h))){
|
|
AlignToRotation(targetObj, turnTarget);
|
|
}
|
|
}
|
|
|
|
//Used to parent a single object to the vehicle's rotation
|
|
/// param 'target' is the child object. Function is called from the vehicle context.
|
|
/// turnTarget is the object which must have a function returning the angle from
|
|
/// 0-180 (left to right), from the function GetTurnAngle()
|
|
func AlignToRotation(object target, object turnTarget)
|
|
{
|
|
if(target){
|
|
//only objects with lower indexes can ride
|
|
if(target.Plane <= this.Plane) return false;
|
|
|
|
//Must have a turnTarget to check the rotation
|
|
if(turnTarget == nil){
|
|
FatalError("No defined turnTarget object to get rotation from");
|
|
}
|
|
|
|
//turnTarget must have a function returning what angle the vehicle is at
|
|
else {
|
|
if(turnTarget->~GetTurnAngle() == nil){
|
|
FatalError(Format("turnTarget %s has no GetTurnAngleFunction", turnTarget->GetName()));
|
|
}
|
|
}
|
|
|
|
// if(target->GetContact(-1) > 0){
|
|
if(target->GetY() > -1 || target->GetY() < 1){
|
|
var oldNewX = nil;
|
|
var oldNewR = nil;
|
|
|
|
var oldEffect = GetEffect("AlignRotation", target);
|
|
//Get originalX from previous effect if it existed
|
|
if(oldEffect){
|
|
oldNewX = oldEffect.originalX * -1;
|
|
if(oldEffect.originalR != nil) oldNewR = oldEffect.originalR * -1;
|
|
RemoveEffect(nil,target,oldEffect);
|
|
}
|
|
|
|
//Create the turn effect
|
|
var effect = AddEffect("AlignRotation", target, 1, 1, this);
|
|
//set effect vars
|
|
effect.turnTarget = turnTarget;
|
|
|
|
//If the turn was already in progress, reuse the last one
|
|
if(oldNewX) effect.originalX = oldNewX;
|
|
if(oldNewR) effect.originalR = oldNewR;
|
|
}
|
|
}
|
|
}
|
|
|
|
private func FxAlignRotationStart(object target, proplist effect)
|
|
{
|
|
/// param 'target' is the object which follows 'this'
|
|
/// 'this' refers to the vehicle the effect is created in the object context of.
|
|
|
|
DebugLog(Format("Added AlignRotation effect to %s, child of %s", target->GetName(), GetName()));
|
|
|
|
effect.originalX = target->GetX(100) - this->GetX(100);
|
|
effect.originalY = target->GetY(100) - this->GetY(100);
|
|
effect.originalR = nil;
|
|
if(target->GetDefCoreVal("Rotate", "DefCore") >= 1){
|
|
effect.originalR = Normalize(target->GetR(),-180);
|
|
if(target->GetDefCoreVal("Rotate", "DefCore") == 1){
|
|
effect.noRSmooth = true;
|
|
}
|
|
}
|
|
|
|
//Temporary no collection effect
|
|
effect.oldCollect = target.Collectible;
|
|
if(effect.oldCollect == 1) target.Collectible = 0;
|
|
|
|
effect.floorYOff = (effect.originalY + ((target->GetDefCoreVal("Height", "DefCore") / 2 + 2) * 100));
|
|
|
|
//Create floor helper. Makes certain clonks don't fall while boat is turning
|
|
//(due to the solid mask flipping and such); so they keep a solid footing.
|
|
if(target->GetCategory() & C4D_Living){
|
|
// if(target->GetY() == 0){
|
|
effect.floorHelper = this->CreateObject(Vehicle_FloorHelper, effect.originalX / 100, (effect.originalY / 100) + (effect.floorYOff / 100) + 1);
|
|
|
|
//adjust floor helper size
|
|
var width = BoundBy(target->GetDefCoreVal("Width", "DefCore"),6,64);
|
|
effect.floorHelper->SetSolidMask(0,0,width,2, (width * -1) / 2 + 1,0);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//Basically what this does is move the target object in a sinal path to emulate
|
|
//rotating with the boat.
|
|
private func FxAlignRotationTimer(object target, proplist effect, int timer)
|
|
{
|
|
//Z-Rotation value of the vehicle
|
|
var ang = effect.turnTarget->GetTurnAngle() - 90;
|
|
|
|
//sinal movement and rotation
|
|
var newX = Sin(ang, Abs(effect.originalX));
|
|
var newR = Sin(ang, Abs(effect.originalR)) * -1;
|
|
|
|
//Is the target on the left side of the vehicle, and the vehicle is turning left?
|
|
//Is the target on the right side of the vehicle, and the vehicle is turning right?
|
|
if((effect.originalX < 0 && this->GetDir() == 0) || (effect.originalX > 0 && this->GetDir() == 1)){
|
|
newX = Sin(ang, Abs(effect.originalX)) * -1;
|
|
newR = Sin(ang, Abs(effect.originalR));
|
|
}
|
|
|
|
//Debug
|
|
//target->Message(Format("%d,%d; %d", effect.originalX, newX, effect.floorYOff));
|
|
|
|
if(newX != effect.originalX * -1){
|
|
target->SetPosition(newX + this->GetX(100), target->GetY(100), true, 100);
|
|
|
|
//change rotation
|
|
if(effect.originalR != nil){
|
|
//debug
|
|
// target->Message(Format("R=%d", newR));
|
|
|
|
// target->SetR(newR);
|
|
if(target->GetCategory() & C4D_Object) target->SetR();
|
|
}
|
|
|
|
//Make sure vehicles aren't affected by gravity when rotating
|
|
if((target->GetCategory() & C4D_Living) == 0){
|
|
if(target->GetProcedure() == "FLOAT") target->SetYDir(0);
|
|
else target->SetYDir(-2);
|
|
}
|
|
|
|
//update floor-helper position for clonks/livings
|
|
if(effect.floorHelper != nil){
|
|
effect.floorHelper->SetPosition(this->GetX(100) + newX, this->GetY(100) + effect.floorYOff, true, 100);
|
|
}
|
|
}
|
|
else{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
private func FxAlignRotationStop(object target, proplist effect)
|
|
{
|
|
//re-enable collection
|
|
target.Collectible = effect.oldCollect;
|
|
|
|
var newX = effect.originalX * -1;
|
|
//push vehicle up out of solidmask if it got stuck in it
|
|
while(target->Stuck()) target->SetPosition(newX + this->GetX(100), target->GetY(100) - 50, true, 100);
|
|
|
|
//Get rid of the floor helper object
|
|
if(effect.floorHelper) effect.floorHelper->RemoveObject();
|
|
}
|