Created
April 1, 2024 17:57
Solving doors in RS by using Anchor and Door distance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
program new; | |
{$DEFINE SRL_USE_REMOTEINPUT} | |
{$I SRL/osr.simba} | |
(* | |
This solution assumes that you have an anchor that is either perpendicular or parallel to the door | |
(it's either in the angle of facing the closed door, or it's facing the open door) | |
// | |
//| | |
//|| | |
//_||______ | |
//| __ __ | | |
##| | || | | | |
##| | || | | _ | |
##| |__||__| | (_) | |
##| __ __()| Parallel example <--|--> | |
##| | || | | <-- Door --- Anchor --> _ | _ | |
##| | || | | `\__/ \__/` | |
##| | || | | `-. .-´ | |
##| |__||__| | ' | |
##|__________| | |
// | |
Concept: | |
Find the door and find some object which we call anchor, in this example I'm using a black ledger. | |
Now we can use the distance from the ledger to the door to determine if the door is open. | |
The door state can be defined as | |
If the distance between Door and Anchor is less than a set value, the state of the door is | |
> Open if: The anchor is parallel to the closed door (parallel meaning to the direction you enter) | |
> Closed if: The anchor is perpendicular to the closed door (reverse) | |
Written example, assuming the anchor's location is parallel to the direction you enter through the door. | |
An open door could have a distance between 18..19, | |
while a closed door could have a distance between 21..22. | |
We can therefor state the cutoff / middle between the two states as approximately 20. | |
How you choose to attack it is up to you. So long as you can generate varying distance-value | |
between states to something else (an anchor), you can use this concept. | |
*) | |
{$DEFINE DOOR_DEBUG_DISTANCE} | |
type | |
EDoorState = (dsUndefined, dsOpen, dsClosed); | |
var | |
RSW: TRSWalker; | |
(* | |
Input: | |
Door: The TPA of the door | |
Anchor: The TPA of the Anchor | |
Split: This is the cutoffpoint between a closed and an open door | |
Inverse: If the door is not parallel then this should be True (auto False) | |
UseMean: Mean may give less accurate distance value, so it may result in (more) false-positives | |
But it may be less prone to noisy pixels from color-finding. | |
Return `dsOpen` if the door is open based on given input data | |
if the method fails it will return `dsUndefined`, otherwise dsFalse; `ds` is short for doorstate | |
*) | |
function IsDoorOpen(Door, Anchor: TPointArray; Split: Double; Inverse: Boolean = False; UseMean: Boolean = False): EDoorState; | |
var | |
p,q: Vector3; | |
me,ptAnchor,ptDoor: TPoint; | |
ATPA: T2DPointArray; | |
begin | |
// ensure no failure | |
if (Length(Anchor) = 0) or (length(Door) = 0) then Exit(dsUndefined); | |
// find the point in the ledger that's closest to the door and vice versa | |
// alternatively we could just use the mean and skip this step in many cases. | |
if not UseMean then begin | |
anchor.Sort(door.Mean()); | |
ptAnchor := anchor[0]; | |
door.Sort(ptAnchor); | |
ptDoor := door[0]; | |
end else begin | |
ptAnchor := anchor.Mean(); | |
ptDoor := door.Mean(); | |
end; | |
// convert to minimap to work in 2d space, so distance is always the same no matter position, | |
// camera angle and zoom. | |
p := MainScreen.PointToMM(ptDoor); | |
q := MainScreen.PointToMM(ptAnchor); | |
{$IFDEF DOOR_DEBUG_DISTANCE} | |
// write the distance output for debugging | |
// this is to get the distance (number) between the anchor and door (in both states, open and closed) | |
WriteLn p.Distance(q); | |
{$ENDIF} | |
if (p.Distance(q) <= Split) xor inverse then | |
Exit(dsOpen) | |
else | |
Exit(dsClosed); | |
end; | |
(* | |
You can use your own methods to find doors and anchors. This is just as an example. | |
*) | |
procedure FindDoorAndAnchor(WorldPos: TPoint; out Door, Anchor: TPointArray); | |
var | |
ledger: TPointArray; | |
ATPA: T2DPointArray; | |
AnchorSearchBox, DoorSearchBox: TBox; | |
begin | |
// rough area of where the anchor and door is (world map coordinates) | |
AnchorSearchBox := RSW.GetTileMSEx(WorldPos, [2656, 2734]).Expand(10).Bounds(); | |
DoorSearchBox := RSW.GetTileMSEx(WorldPos, [2646, 2754]).Expand(30).Bounds(); | |
DoorSearchBox.LimitTo(MainScreen.Bounds); | |
AnchorSearchBox.LimitTo(MainScreen.Bounds); | |
// find the anchor, and the door tpas (regular color-data from ACA) | |
srl.FindColors(anchor, CTS2(1776416, 1, 0.01, 0.01), AnchorSearchBox); | |
srl.FindColors(door, CTS2(1993096, 13, 0.08, 1.02), DoorSearchBox); | |
// basic noise filtering | |
ATPA := door.Erode(1).Grow(1).Cluster(2); | |
door := ATPA.Biggest(); | |
// basic noise filtering, small object, so I dont erode noise. | |
ATPA := anchor.Cluster(2); | |
anchor := ATPA.Biggest(); | |
end; | |
(* | |
Test run: | |
*) | |
var | |
door,anchor: TPointArray; | |
begin | |
RSW.Setup([[2656-500, 2734-500, 2656+500, 2734+500]]); | |
RSClient.Image.SetFontSize(15); | |
RSClient.Image.SetFontAntialiasing(False); | |
WriteLn RSW.GetMyPos(); | |
while True do | |
begin | |
MM2MS.ZoomLevel := Options.GetZoomLevel(); | |
FindDoorAndAnchor(RSW.GetMyPos(), door, anchor); | |
RSClient.Image.Clear(); | |
case IsDoorOpen(Door, Anchor, 19.4, False, False) of | |
dsUndefined: ; | |
dsOpen: RSClient.Image.DrawText('Open', Door.Mean(), 1); | |
dsClosed: RSClient.Image.DrawText('Closed', Door.Mean(), 1); | |
end; | |
end; | |
end. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment