/* Hatch @authors: Clonkonaut, Ringwaul (Graphics) */ #include Library_Structure local opened = false; /*-- Engine Callbacks --*/ func Construction() { SetProperty("MeshTransformation", Trans_Mul(Trans_Translate(0,-10000), Trans_Rotate(-10,0,1,0), Trans_Rotate(-8,1,0,0))); SetCategory(C4D_StaticBack); return _inherited(...); } func Initialize() { // If the hatch has not been properly combined with a basement, we might want to correct that if (!GetBasement()) { var basements = FindObjects(Find_AtRect(-1,0, 2,4), Find_Func("IsBasement"), Sort_Distance()); for (var basement in basements) { if (!basement->GetParent() && basement->GetWidth() == 40) { SetPosition(basement->GetX(), basement->GetY()-2); basement->SetParent(this); break; } } } return _inherited(...); } /*-- Callbacks --*/ public func IsHammerBuildable() { return true; } public func DoConstructionEffects(object construction_site) { // The hatch sets itself a bit lower after it has been placed with the construction previewer SetPosition(GetX(), GetY()+4); return false; } public func NoConstructionFlip() { return true; } public func ConstructionCombineWith() { return "IsBasement"; } public func ConstructionCombineDirection(object other) { return CONSTRUCTION_STICK_Top; } public func CombineWith(object stick_to) { if (stick_to && stick_to->~IsBasement()) stick_to->SetParent(this); SetPosition(GetX(), GetY()+1); } /*-- Library overloads --*/ func MoveOutOfSolidMask() { // Find all objects inside the solid mask which are stuck. var lying_around = FindObjects(Find_Or(Find_Category(C4D_Vehicle), Find_Category(C4D_Object), Find_Category(C4D_Living)), Find_InRect(-13, -2, 26, 4), Find_NoContainer()); // Move up these objects. for (var obj in lying_around) { var x = obj->GetX(); var y = obj->GetY(); var delta_y = 0; var max_delta = obj->GetObjHeight(); // Move up object until it is not stuck any more. while (obj->Stuck() || obj->GetContact(-1, CNAT_Bottom)) { // Only move up the object by at most its height plus the basements height. if (delta_y > max_delta) { obj->SetPosition(x, y); break; } delta_y++; obj->SetPosition(x, y - delta_y); } } } /*-- Basement --*/ public func GetBasementWidth() { return 40; } public func SetBasement(object to_basement) { _inherited(to_basement); if (!to_basement) return; to_basement->ChangeDef(HatchBasement); } public func GetBasementOffset() { return [0, -15]; } /*-- Interaction --*/ public func IsInteractable(object clonk) { if (GetCon() < 100) return false; if (opened && FindClonkInHatchArea()) return false; if (Hostile(clonk->GetOwner(), GetOwner())) return false; return true; } public func GetInteractionMetaInfo(object clonk) { if (!ActIdle()) return { Description = "$Close$", IconName = nil, IconID = Icon_Exit, Selected = false }; return { Description = "$Open$", IconName = nil, IconID = Icon_Enter, Selected = false }; } public func Interact(object clonk) { if (ActIdle()) return DoOpen(); if (GetAction() == "Open") return DoClose(); return false; } /*-- Open/Close --*/ public func DoOpen() { SetAction("Open"); Sound("Structures::DoorOpen?"); return true; } public func DoClose() { if (opened) { if (FindClonkInHatchArea()) return false; SetAction("Close"); Sound("Structures::DoorClose?"); opened = false; return true; } if (GetAction() == "Open") { var phase = 2 - GetPhase(); SetAction("Close"); Sound("Structures::DoorClose?"); SetPhase(phase); return true; } SetAction("Close"); Sound("Structures::DoorClose?"); return true; } func FindClonkInHatchArea() { return FindObject(Find_InRect(-16, -12, 32, 24), Find_OCF(OCF_CrewMember), Find_OCF(OCF_Alive), Find_AnyLayer(), Find_NoContainer()); } func ChangeSolidMask() { if (opened) return; if (GetAction() == "Close") // Upon closing, the solid mask must reappear instantaneously // to prevent clonks from getting stuck return SetSolidMask(0,0, 26,4, 0,11); var phase = GetPhase(); var width = 26 - 8*phase; if (phase == 3) { ClosingTimer(); width -= 2; } SetSolidMask(0,0, width,4, 0,11); } func ClosingTimer() { opened = true; ScheduleCall(this, "CheckForClose", 15); } func CheckForClose() { if (GetAction() == "Close" || ActIdle()) return; if (!FindClonkInHatchArea()) return ScheduleCall(this, "CloseForReal", 25); ScheduleCall(this, "CheckForClose", 5); } func CloseForReal() { // One final check that the hatch is clear if (FindClonkInHatchArea()) return ScheduleCall(this, "CheckForClose", 5); DoClose(); } /*-- Display --*/ func Definition(def) { SetProperty("PictureTransformation", Trans_Mul(Trans_Translate(0,-3000,-5000), Trans_Rotate(-30,1,0,0), Trans_Rotate(30,0,1,0), Trans_Translate(1000,1,0)), def); return _inherited(def); } /*-- Properties --*/ local ActMap = { Open = { Prototype = Action, Name = "Open", Length = 3, Delay = 5, Animation = "Open", NextAction = "Hold", PhaseCall = "ChangeSolidMask" }, Close = { Prototype = Action, Name = "Close", Length = 3, Delay = 5, Animation = "Close", NextAction = "Idle", PhaseCall = "ChangeSolidMask" }, }; local Name = "$Name$"; local Description = "$Description$"; local BlastIncinerate = 100; local HitPoints = 70; local Components = {Wood = 2};