From f4ca4466f16b9a29074db06d22165760ee1ffedc Mon Sep 17 00:00:00 2001 From: Clonkonaut Date: Fri, 5 Aug 2016 18:21:34 +0200 Subject: [PATCH] Basements combine in all four directions when constructing! --- .../ConstructionPreviewer.ocd/Script.c | 190 ++++++++++++++---- .../Structures.ocd/Basement.ocd/Script.c | 85 +++++--- .../Structures.ocd/Elevator.ocd/Script.c | 14 +- 3 files changed, 217 insertions(+), 72 deletions(-) diff --git a/planet/Objects.ocd/Libraries.ocd/Constructor.ocd/ConstructionPreviewer.ocd/Script.c b/planet/Objects.ocd/Libraries.ocd/Constructor.ocd/ConstructionPreviewer.ocd/Script.c index b0f865ebe..756e19460 100644 --- a/planet/Objects.ocd/Libraries.ocd/Constructor.ocd/ConstructionPreviewer.ocd/Script.c +++ b/planet/Objects.ocd/Libraries.ocd/Constructor.ocd/ConstructionPreviewer.ocd/Script.c @@ -8,6 +8,7 @@ static const CONSTRUCTION_STICK_Left = 1; static const CONSTRUCTION_STICK_Right = 2; static const CONSTRUCTION_STICK_Bottom = 4; +static const CONSTRUCTION_STICK_Top = 8; local extra_overlay, dimension_x, dimension_y, clonk, structure, direction, stick_to, blocked; local GFX_StructureOverlay = 1; @@ -110,47 +111,118 @@ public func AdjustPreview(bool below_surface, bool look_up, bool no_call) // x and y are refined mouse coordinates so always centered at the clonk func Reposition(int x, int y) { - x = BoundBy(x, -dimension_x/2, dimension_x/2); - y = BoundBy(y, -dimension_y/2, dimension_y/2); + var clonk_width = clonk->GetObjWidth(); + var clonk_height = clonk->GetObjHeight(); + x = BoundBy(x, -dimension_x - clonk_width/2, dimension_x + clonk_width/2); + y = BoundBy(y, -dimension_y - clonk_height/2, dimension_y + clonk_height/2); // Try to combine the structure with other structures. var found = false; if (structure->~ConstructionCombineWith()) { - var stick_dir = structure->~ConstructionCombineDirection(); - var find_rect = Find_InRect(AbsX(clonk->GetX() + x - dimension_x/2 - 10), AbsY(clonk->GetY() + y - dimension_y/2 - 10), dimension_x + 20, dimension_y + 20); - if ((stick_dir & CONSTRUCTION_STICK_Bottom)) - find_rect = Find_AtPoint(AbsX(clonk->GetX() + x), AbsY(clonk->GetY() + y)); - var other = FindObject(Find_Func(structure->ConstructionCombineWith(), this), - find_rect, - Find_OCF(OCF_Fullcon), - Find_Layer(clonk->GetObjectLayer()), - Find_Allied(clonk->GetOwner()), - Find_NoContainer()); - if (other) + // There is no use in doing all the other checks if no sticking direction is defined at all + // That's just wrong use of ConstructionCombineWith + if (structure->~ConstructionCombineDirection()) { - x = other->GetX(); - y = other->GetY(); - // Combine to different directions. - if ((stick_dir & CONSTRUCTION_STICK_Left) && other->GetX() >= GetX()) - x = other->GetX() - other->GetObjWidth()/2 - dimension_x / 2; - if ((stick_dir & CONSTRUCTION_STICK_Right) && other->GetX() < GetX()) - x = other->GetX() + other->GetObjWidth()/2 + dimension_x / 2; - if ((stick_dir & CONSTRUCTION_STICK_Bottom)) - y = other->GetY() + other->GetObjHeight()/2 + dimension_y / 2; - // Add an additional offset if needed, for example a basement can be place - // only under a part of the structure. - var stick_offset = structure->~ConstructionCombineOffset(other); - if (stick_offset) + //var find_rect = Find_InRect(AbsX(clonk->GetX() + x - dimension_x/2 - 10), AbsY(clonk->GetY() + y - dimension_y/2 - 10), dimension_x + 20, dimension_y + 20); + //if ((stick_dir & CONSTRUCTION_STICK_Bottom)) + + var find_rect = Find_AtPoint(clonk->GetX() - GetX() + x, clonk->GetY() - GetY() + y); + + var other = FindObject(Find_Func(structure->ConstructionCombineWith(), this), + find_rect, + Find_OCF(OCF_Fullcon), + Find_Layer(clonk->GetObjectLayer()), + Find_Allied(clonk->GetOwner()), + Find_NoContainer()); + + if (other) { - x += stick_offset[0]; - y += stick_offset[1]; + var stick_dir = structure->ConstructionCombineDirection(other); + var other_width = other->GetObjWidth(); + var other_height = other->GetObjHeight(); + // Determine the position from the other object's center currently hovered + var other_offset_x = clonk->GetX() + x - other->GetX(); + var other_offset_y = clonk->GetY() + y - other->GetY(); + + // The tricky part is now to determine which of the four possible directions should be used + // The shape of the 'other' is divided like this (* is center): + // __________________________ + // | | Top | | + // | Left |----*----| Right | + // |_______|_Bottom__|_______| + // + // Whichever part is howered on is checked first for stick direction + + // Hopefully, in the end this contains a single sticking direction. + var single_stick_to = 0; + + // Left + if (other_offset_x < other_width / -6) + { + single_stick_to = GetStickingDirection(stick_dir, CONSTRUCTION_STICK_Left, CONSTRUCTION_STICK_Bottom, CONSTRUCTION_STICK_Top, CONSTRUCTION_STICK_Right, other_offset_y, 0); + } + // Right + else if (other_offset_x > other_width / 6) + { + single_stick_to = GetStickingDirection(stick_dir, CONSTRUCTION_STICK_Right, CONSTRUCTION_STICK_Bottom, CONSTRUCTION_STICK_Top, CONSTRUCTION_STICK_Left, other_offset_y, 0); + } + // Bottom + else if (other_offset_y >= 0) + { + single_stick_to = GetStickingDirection(stick_dir, CONSTRUCTION_STICK_Bottom, CONSTRUCTION_STICK_Right, CONSTRUCTION_STICK_Left, CONSTRUCTION_STICK_Top, other_offset_x, 0); + } + // Top + else if (other_offset_y < 0) + { + single_stick_to = GetStickingDirection(stick_dir, CONSTRUCTION_STICK_Top, CONSTRUCTION_STICK_Right, CONSTRUCTION_STICK_Left, CONSTRUCTION_STICK_Bottom, other_offset_x, 0); + } + + // If no direction is found, something went wrong. + // Probably ConstructionCombineDirection() returned garbage. + if (single_stick_to) + { + if (single_stick_to == CONSTRUCTION_STICK_Left) + { + x = other->GetX() - other_width/2 - dimension_x/2; + y = other->GetY(); + } + if (single_stick_to == CONSTRUCTION_STICK_Right) + { + x = other->GetX() + other_width/2 + dimension_x/2; + y = other->GetY(); + } + if (single_stick_to == CONSTRUCTION_STICK_Bottom) + { + x = other->GetX(); + y = other->GetY() + other_height/2 + dimension_y/2; + } + if (single_stick_to == CONSTRUCTION_STICK_Top) + { + x = other->GetX(); + y = other->GetY() - other_height/2 - dimension_y/2; + } + // Add an additional offset if needed, for example a basement can be place + // only under a part of the structure. + var stick_offset = structure->~ConstructionCombineOffset(other, single_stick_to); + if (stick_offset) + { + x += stick_offset[0]; + y += stick_offset[1]; + } + // Save the other building for use in AdjustPreview and for color changing + stick_to = other; + // Found another building and a way to stick to it! + found = true; + } } - stick_to = other; - found = true; } } + if (!found) { + // Narrow the distance a construction site can be built around the clonk + x = BoundBy(x, -dimension_x/2 - clonk_width/2, dimension_x/2 + clonk_width/2); + y = BoundBy(y, -dimension_y/2 - clonk_height/2, dimension_y/2 + clonk_height/2); x = clonk->GetX() + x; y = clonk->GetY() + y; } @@ -163,11 +235,14 @@ func Reposition(int x, int y) else if (stick_to) { SetGraphics(nil, ConstructionPreviewer_IconCombine, GFX_CombineIconOverlay, GFXOV_MODE_Base); - var dir = 1; - if (stick_to->GetX() < GetX()) dir = -1; - if (structure->~CombineToBottom()) - dir = 0; - SetObjDrawTransform(1000, 0, dimension_x/2 * 1000 * dir, 0, 1000, 0, GFX_CombineIconOverlay); + var x_dir = 1, y_dir = 1; + if (stick_to->GetX() < GetX()) x_dir = -1; + if (stick_to->GetY() < GetY()) y_dir = -1; + if (single_stick_to == CONSTRUCTION_STICK_Bottom || single_stick_to == CONSTRUCTION_STICK_Top) + x_dir = 0; + if (single_stick_to == CONSTRUCTION_STICK_Left || single_stick_to == CONSTRUCTION_STICK_Right) + y_dir = 0; + SetObjDrawTransform(1000, 0, dimension_x/2 * 1000 * x_dir, 0, 1000, dimension_y/2 * 1000 * y_dir, GFX_CombineIconOverlay); } // Update the extra overlay possibly added to the preview. extra_overlay = structure->~ConstructionPreview(this, GFX_PreviewerPictureOverlay, direction); @@ -177,6 +252,47 @@ func Reposition(int x, int y) AdjustPreview(structure->~IsBelowSurfaceConstruction()); } +// Helper function to return a definite sticking direction. +// Used whenever the cursor hovers the 'left' or 'right' part of the other building. +// See Reposition() to see an example of the four parts. +func GetStickingDirection(int stick_dir, int primary_dir, int secondary_dir, int tertiary_dir, int fourth_dir, int cursor_coord, int other_coord) +{ + // If the primary direction is in stick_dir, we're done + if (stick_dir & primary_dir) + { + return primary_dir; + } + // Afterwards check second / third directions + else if (stick_dir & secondary_dir || stick_dir & tertiary_dir) + { + // If one of those isn't in stick_dir, no coordinate checking is necessary + if (!(stick_dir & tertiary_dir)) + { + return secondary_dir; + } + else if (!(stick_dir & secondary_dir)) + { + return tertiary_dir; + } + else + { + // Coordinates have to be checked + // secondary always is one pixel better than tertiary + if (cursor_coord >= other_coord) + return secondary_dir; + else + return tertiary_dir; + } + } + // Fourth direction is returned last but no other directions takes the point + else if (stick_dir & fourth_dir) + { + return fourth_dir; + } + // If this happens, something is wrong + return 0; +} + // Flips the preview horizontally func Flip() { @@ -196,4 +312,6 @@ func Flip() } // UI not saved. -func SaveScenarioObject() { return false; } \ No newline at end of file +func SaveScenarioObject() { return false; } + +local Plane = 210; \ No newline at end of file diff --git a/planet/Objects.ocd/Structures.ocd/Basement.ocd/Script.c b/planet/Objects.ocd/Structures.ocd/Basement.ocd/Script.c index 1291d26c7..0fb137f50 100644 --- a/planet/Objects.ocd/Structures.ocd/Basement.ocd/Script.c +++ b/planet/Objects.ocd/Structures.ocd/Basement.ocd/Script.c @@ -2,7 +2,7 @@ Basement Provides basements to structures, but can also be built as a single object. - @author Maikel + @author: Maikel */ #include Library_Structure @@ -10,16 +10,14 @@ local parent; local width; -protected func Construction() +func Construction() { // Make sure the basement does not move while constructing. SetCategory(C4D_StaticBack); return _inherited(...); } -public func IsHammerBuildable() { return true; } - -protected func Initialize() +func Initialize() { var wdt = GetObjWidth(); if (parent) @@ -34,7 +32,7 @@ protected func Initialize() return _inherited(...); } -protected func Destruction() +func Destruction() { // Cast a single rock. CastObjects(Rock, 1, 15, 0, -5); @@ -53,13 +51,6 @@ public func SetWidth(int wdt) public func GetWidth() { return width; } -// Set the parent if the basement is attached to a structure. -public func CombineWith(object stick_to) -{ - SetParent(stick_to); - return; -} - public func SetParent(object to_parent) { parent = to_parent; @@ -74,12 +65,48 @@ public func SetParent(object to_parent) public func GetParent() { return parent; } +/*-- Saving --*/ + +public func SaveScenarioObject(proplist props) +{ + if (!inherited(props, ...)) + return false; + if (parent) + props->AddCall("BasementParent", this, "SetParent", parent); + else if (width != GetObjWidth()) + props->AddCall("BasementWidth", this, "SetWidth", width); + props->Remove("Category"); + return true; +} + +/*-- Construction --*/ + +public func IsHammerBuildable() { return true; } +// It should not be a structure. +public func IsStructure() { return false; } +// But a basement! +public func IsBasement() { return true; } + // Is a construction that is built just below the surface. public func IsBelowSurfaceConstruction() { return true; } -// Sticking to other structures, at the bottom of that structure. +// This makes it possible to combine basements with each other. +public func IsStructureWithoutBasement() { return true; } + +// Sticking to other structures. + public func ConstructionCombineWith() { return "IsStructureWithoutBasement"; } -public func ConstructionCombineDirection() { return CONSTRUCTION_STICK_Bottom; } + +public func ConstructionCombineDirection(object other) +{ + // All directions are possible for other basements + if (other && other->~IsBasement()) + return CONSTRUCTION_STICK_Left | CONSTRUCTION_STICK_Right | CONSTRUCTION_STICK_Bottom | CONSTRUCTION_STICK_Top; + + // For everything else, the basement is below. + return CONSTRUCTION_STICK_Bottom; +} + public func ConstructionCombineOffset(object other) { // Some structures like the elevator require the basement to have an offset. @@ -90,9 +117,10 @@ public func NoConstructionFlip() { return true; } public func AlternativeConstructionPreview(object previewer, int direction, object combine_with) { + if (combine_with && combine_with->~IsBasement()) return; + var wdt = GetSiteWidth(direction, combine_with); previewer->SetObjDrawTransform(1000 * wdt / 40, 0, 0, 0, 1000, 0, previewer.GFX_StructureOverlay); - return; } public func GetSiteWidth(int direction, object combine_with) @@ -109,6 +137,8 @@ public func GetSiteWidth(int direction, object combine_with) public func SetConstructionSiteOverlay(object site, int direction, object combine_with) { + if (combine_with && combine_with->~IsBasement()) return; + var wdt = GetSiteWidth(direction, combine_with); site->SetGraphics(nil, Basement, 1, GFXOV_MODE_Base); site->SetClrModulation(RGBa(255, 255, 255, 128), 1); @@ -116,26 +146,15 @@ public func SetConstructionSiteOverlay(object site, int direction, object combin return true; } -// Don't stick to itself, so it should not be a structure. -public func IsStructure() { return false; } - - -/*-- Saving --*/ - -public func SaveScenarioObject(proplist props) +// Set the parent if the basement is attached to a structure. +public func CombineWith(object stick_to) { - if (!inherited(props, ...)) - return false; - if (parent) - props->AddCall("BasementParent", this, "SetParent", parent); - else if (width != GetObjWidth()) - props->AddCall("BasementWidth", this, "SetWidth", width); - props->Remove("Category"); - return true; + if (stick_to && stick_to->~IsBasement()) return; + + SetParent(stick_to); } - -/*-- Proplist --*/ +/*-- Properties --*/ local Name = "$Name$"; local Description ="$Description$"; diff --git a/planet/Objects.ocd/Structures.ocd/Elevator.ocd/Script.c b/planet/Objects.ocd/Structures.ocd/Elevator.ocd/Script.c index 875afb29f..e5287e712 100644 --- a/planet/Objects.ocd/Structures.ocd/Elevator.ocd/Script.c +++ b/planet/Objects.ocd/Structures.ocd/Elevator.ocd/Script.c @@ -227,11 +227,19 @@ public func ConstructionPreview(object previewer, int overlay, int dir) } // Sticking to other elevators -public func ConstructionCombineWith() { return "IsElevator"; } -public func ConstructionCombineDirection() { return CONSTRUCTION_STICK_Left | CONSTRUCTION_STICK_Right; } +public func ConstructionCombineWith() { return "CanCombineElevator"; } +public func ConstructionCombineDirection(object other) +{ + if (!other) return CONSTRUCTION_STICK_Left | CONSTRUCTION_STICK_Right; + + // Only combine when facing correctly + if (other->GetDir() == DIR_Left) + return CONSTRUCTION_STICK_Right; + return CONSTRUCTION_STICK_Left; +} // Called to determine if sticking is possible -public func IsElevator(object previewer) +public func CanCombineElevator(object previewer) { if (!previewer) return true;