/** Ladder Climbing Gives the ability to clonks climb on ladders, to be included by the clonk. @author Randrian */ public func Definition(proplist def) { // Only add action if included by clonk. if (!def.ActMap) return _inherited(def); // Add actions for climbing and overload jumping actions to search for ladders. def.ActMap = { Prototype = def.ActMap, Climb = { Prototype = Action, Name = "Climb", Directions = 2, Length = 0, Delay = 0, Wdt = 8, Hgt = 20, Procedure = DFA_FLOAT, }, Jump = { Prototype = def.ActMap.Jump, StartCall = "StartSearchLadder", // Save the old phasecall of the jump. StartCallLadderOverloaded = def.ActMap.Jump.StartCall }, WallJump = { Prototype = def.ActMap.WallJump, StartCall = "StartSearchLadder", // Save the old phasecall of the wall jump. StartCallLadderOverloaded = def.ActMap.WallJump.StartCall } }; return _inherited(def); } public func GetTurnPhase() { return _inherited(...); } public func SetTurnType() { return _inherited(...); } public func SetHandAction() { return _inherited(...); } // Should be overloaded, and add a climb animation here public func StartScale() { return _inherited(...); } /*-- Ladder Searching --*/ public func StartSearchLadder() { // Call the overwriten old phase call. if (GetAction() == "Jump" && this.ActMap.Jump.StartCallLadderOverloaded) Call(this.ActMap.Jump.StartCallLadderOverloaded); if (GetAction() == "WallJump" && this.ActMap.WallJump.StartCallLadderOverloaded) Call(this.ActMap.WallJump.StartCallLadderOverloaded); // Add an effect to search for ladders. if (!GetEffect("InSearchLadder", this)) AddEffect("IntSearchLadder", this, 1, 2, this); FxIntSearchLadderTimer(); return; } public func GetLadderScaleAnimation() { var animation = _inherited(...); if (animation) return animation; return "Scale"; } public func FxIntSearchLadderTimer(object target, proplist effect, int time) { // Only search for a ladder if jumping. if (GetAction() != "Jump" && GetAction() != "WallJump") return FX_Execute_Kill; // Find a ladder which can be climbed. var ladder; for (ladder in FindObjects(Find_AtRect(-5, -10, 10, 8), Find_Func("IsLadder"), Find_NoContainer(), Find_Layer(GetObjectLayer()))) { // Don't climb ladders that are blocked. if (ladder->~CanNotBeClimbed(false, this) || IsBlockedLadder(ladder)) continue; SetAction("Climb"); ladder->~OnLadderGrab(this); PlayAnimation(GetLadderScaleAnimation(), CLONK_ANIM_SLOT_Movement, Anim_Y(0, GetAnimationLength(GetLadderScaleAnimation()), 0, 15), Anim_Linear(0, 0, 1000, 5, ANIM_Remove)); AddEffect("IntClimbControl", this, 1, 1, this, nil, ladder); return FX_Execute_Kill; } return FX_OK; } public func FxIntSearchLadderStop(object target, proplist effect, reason, tmp) { if (tmp) return FX_OK; return FX_OK; } /*-- Ladder Block --*/ private func AddLadderBlock(object ladder, int duration) { AddEffect("IntBlockLadder", this, 100, duration, this, nil, ladder); return; } private func IsBlockedLadder(object ladder) { var index = 0; var fx; while (fx = GetEffect("IntBlockLadder", this, index++)) if (fx.ladder->IsSameLadder(ladder)) return true; return false; } public func FxIntBlockLadderStart(object target, effect fx, int tmp, object to_ladder) { if (tmp) return FX_OK; fx.ladder = to_ladder; return FX_OK; } public func FxIntBlockLadderTimer(object target, effect fx, int time) { return FX_Execute_Kill; } /*-- Ladder Control --*/ public func FxIntClimbControlStart(object target, effect fx, int tmp, object ladder) { if (tmp) return FX_OK; fx.ladder = ladder; SetXDir(0); SetYDir(0); SetComDir(COMD_Stop); // Start on an even segment. fx.odd = 0; // Correctly initalize the relative y-position of the clonk to the segment. var data = fx.ladder->GetLadderData(); var sy = data[1], ey = data[3]; var cy = GetY(1000); var posy = 0; if (ey - sy != 0) posy = 100 * (cy - sy) / (ey - sy); fx.pos = BoundBy(posy, 0, 100); // Set some stuff for the clonk. SetHandAction(1); SetTurnType(1); return FX_OK; } public func LadderStep(object target, effect fx, int climb_direction) { if (climb_direction != 1 && climb_direction != -1) return fx.ladder != nil; // Store old segment to forward to blocking. var old_ladder_segment = fx.ladder; // Increase position depending on direction and move to new segment if needed. fx.pos += 10 * climb_direction; if (fx.pos > 100) { fx.pos = 0; fx.ladder = fx.ladder->GetNextLadder(); fx.odd = !fx.odd; } if (fx.pos < 0) { fx.pos = 100; fx.ladder = fx.ladder->GetPreviousLadder(); fx.odd = !fx.odd; } // If no ladder has been found scale or jump off. if (fx.ladder == nil) { var contact = GetContact(-1); if (contact & CNAT_Left || contact & CNAT_Right) { SetAction("Scale"); old_ladder_segment->~OnLadderReleased(this); return false; } AddLadderBlock(old_ladder_segment, 10); SetAction("Jump"); // Increase speed if moving up. if (climb_direction == 1) { SetXDir(-5 + 10 * GetDir()); SetYDir(-5); } old_ladder_segment->~OnLadderReleased(this); return false; } return true; } public func FxIntClimbControlTimer(object target, effect fx, int time) { if (GetAction() != "Climb" || Contained()) return FX_Execute_Kill; if (!fx.ladder || fx.ladder->~CanNotBeClimbed(true, this)) { AddLadderBlock(fx.ladder, 5); SetAction("Jump"); SetXDir(-5 + 10 * GetDir()); SetYDir(-5); return FX_Execute_Kill; } // Progress movement in the controlled direction. var climb_direction = 0; if (GetComDir() == COMD_Down) climb_direction = -1; if (GetComDir() == COMD_Up) climb_direction = 1; // LadderStep advances the ladder segment or the ladder position. if (climb_direction && !LadderStep(target, fx, climb_direction)) return FX_Execute_Kill; // Move the clonk along the ladder according to the new pos/segment. var data = fx.ladder->GetLadderData(); var startx = data[0], starty = data[1], endx = data[2], endy = data[3], angle = data[4]; var x = startx + (endx - startx) * fx.pos / 100 + 5000 - 100 * GetTurnPhase(); var y = starty + (endy - starty) * fx.pos / 100; var lx = LadderToLandscapeCoordinates(x); var ly = LadderToLandscapeCoordinates(y); var old_x = GetX(), old_y = GetY(); if (Abs(old_x - lx) + Abs(old_y - ly) > 10 && time > 1) return FX_Execute_Kill; SetPosition(lx, ly); SetXDir(0); SetYDir(0); SetLadderRotation(-angle, x - GetX() * 1000, y - GetY() * 1000); // Handle if the clonk gets stuck. if (Stuck()) { var dir = -1; if (GetDir() == DIR_Left) dir = 1; for (var i = 1; i <= 5; i++) { SetPosition(LadderToLandscapeCoordinates(x) + i * dir, LadderToLandscapeCoordinates(y)); if (!Stuck()) break; } if (Stuck()) SetPosition(LadderToLandscapeCoordinates(x) + 5 * dir, LadderToLandscapeCoordinates(y)); } if (Stuck()) { // Revert Position and step. SetPosition(old_x, old_y); if (climb_direction) LadderStep(target, fx, -climb_direction); // If we are too far left or right try to turn. if (GetDir() == DIR_Left && LadderToLandscapeCoordinates(x) - 2 > GetX()) { SetComDir(COMD_Right); SetDir(DIR_Right); } else if (GetDir() == DIR_Right && LadderToLandscapeCoordinates(x) + 2 < GetX()) { SetComDir(COMD_Left); SetDir(DIR_Left); } } else { fx.ladder->~OnLadderClimb(this); } // Make the animation synchron with movement. // TODO: this only makes the feet synchronous for the arms the animation has to be adapted. var animation = GetRootAnimation(5); if (animation != nil) { if (GetAnimationName(animation) != nil) { var length = GetAnimationLength(GetAnimationName(animation)); var pos = fx.pos * length / 200 + length / 2 * fx.odd; SetAnimationPosition(animation, Anim_Const(pos)); } } // Start walking or hangling if the clonk makes contact with the floor or ceiling. var contact = GetContact(-1); if (contact) { if (contact & CNAT_Top && GetComDir() == COMD_Up) { SetAction("Hangle"); if (GetDir() == 0) { SetComDir(COMD_Right); SetDir(1); } else { SetComDir(COMD_Left); SetDir(0); } return FX_Execute_Kill; } if (contact & CNAT_Bottom && GetComDir() == COMD_Down) { SetAction("Walk"); return FX_Execute_Kill; } } return FX_OK; } private func LadderToLandscapeCoordinates(int x) { // Round to the next thousand. return (x + 500) / 1000; } public func FxIntClimbControlStop(object target, effect fx, int reason, bool tmp) { if (tmp) return FX_OK; if (GetAction() == "Climb") SetAction("Walk"); if (fx.ladder) fx.ladder->~OnLadderReleased(this); SetLadderRotation(0); SetHandAction(0); return FX_OK; } public func FxIntClimbControlControl(object target, effect fx, int ctrl, int x, int y, int strength, bool repeat, int status) { // Only handle movement controls. if (ctrl != CON_Up && ctrl != CON_Down && ctrl != CON_Right && ctrl != CON_Left) return false; // Perform actions on key down and not on release. if (status != CONS_Down) return false; // Move up and down by setting com dir. if (ctrl == CON_Up) { SetComDir(COMD_Up); return true; } if (ctrl == CON_Down) { SetComDir(COMD_Down); return true; } // Handle left and right controls. if (ctrl == CON_Left) { if (GetDir() == DIR_Left) { // Switch to the other side of the ladder. if (GetComDir() == COMD_Stop) { SetPosition(GetX() - 10, GetY()); if (!Stuck()) { SetComDir(COMD_Right); SetDir(DIR_Right); } SetPosition(GetX() + 10, GetY()); } SetComDir(COMD_Stop); } else { // Let go of the ladder and remove the effect. AddLadderBlock(fx.ladder, 5); if (GetComDir() == COMD_Up) this->ObjectCommand("Jump"); else this->ObjectComLetGo(-10); RemoveEffect(nil, target, fx); } return true; } if (ctrl == CON_Right) { if (GetDir() == DIR_Right) { // Switch to the other side of the ladder. if (GetComDir() == COMD_Stop) { SetPosition(GetX() + 10, GetY()); if (!Stuck()) { SetComDir(COMD_Left); SetDir(DIR_Left); } SetPosition(GetX() - 10, GetY()); } SetComDir(COMD_Stop); } else { // Let go of the ladder and remove the effect. AddLadderBlock(fx.ladder, 5); if (GetComDir() == COMD_Up) this->ObjectCommand("Jump"); else this->ObjectComLetGo(10); RemoveEffect(nil, target, fx); } return true; } return true; } public func SetLadderRotation(int r, int xoff, int yoff) { SetMeshTransformation(Trans_Mul(Trans_Translate(0, -10000), Trans_Rotate(-r, 0, 0, 1), Trans_Translate(xoff, 10000 + yoff)), 5); return; } // Defined to prevent an error, because SetMeshTransformation is overloaded by the clonk. func SetMeshTransformation() { return _inherited(...); }