Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
color = rgb(255,0,0)

/datum/unit_test/test_change_area_appearance/RunTest()
var/area/subtype/S = new()
var/area/subtype/S = areas_by_type[/area/subtype]
var/list/block_turfs = block(locate(1,1,1), locate(2,2,2))
for(var/turf/T in block_turfs)
S.contents += T
Expand Down
162 changes: 125 additions & 37 deletions Content.IntegrationTests/DMProject/Tests/range.dm
Original file line number Diff line number Diff line change
@@ -1,40 +1,128 @@
//Tests that /proc/range() is iterating along the correct, wonky path
// tests all of range's possible cases

#define LOC(x, y) locate(x, y, 3)

/obj/contained/one
/obj/contained/two

/datum/unit_test/range/proc/run_case(atom/center, list/expected, identifier, isorange)
var/error_index = 0
var/list/result = isorange ? orange(center, 1) : range(center, 1)
try
if(result.len != expected.len)
error_index = expected.len
CRASH("result is [result.len > expected.len ? "longer" : "shorter"] than expected")
for(var/index in 1 to result.len)
if(result[index] != expected[index])
error_index = index
CRASH("result does not match expected")
catch(var/exception/exc)
var/list/error_output = list()
error_output += "[identifier]: [exc]"
error_output += "expected:"
for(var/i in 1 to expected.len)
var/atom/A = expected[i]
error_output += ("\t([A.x], [A.y]) [A.type] [i == error_index ? "<-- here" : null]")
error_output += "got:"
for(var/i in 1 to result.len)
var/atom/A = result[i]
error_output += ("\t([A.x], [A.y]) [A.type] [i == error_index ? "<-- here" : null]")


CRASH(error_output.Join("\n"))

// Tests the implementation of range() and orange()
/datum/unit_test/range/RunTest()
world.maxx = world.maxy = 5
//Test that it goes in the right order
var/list/correctCoordinates = list(
list(3,3),
list(2,2),
list(2,3),
list(2,4),
list(3,2),
list(3,4),
list(4,2),
list(4,3),
list(4,4)

var/turf/center = LOC(3, 3)
var/area/outer_area = areas_by_type[/area]
var/obj/container = new /obj(center)
var/obj/contained = new /obj/contained/one(container)
var/obj/contained_trash_1 = new /obj/contained/two(contained)
var/obj/contained_trash_2 = new /obj/contained/two(contained)
var/obj/contained_trash_3 = new /obj/contained/two(contained)

var/list/turf_range_case = list(
LOC(3, 3),
outer_area,
container,
LOC(2, 2),
LOC(2, 3),
LOC(2, 4),
LOC(3, 2),
LOC(3, 4),
LOC(4, 2),
LOC(4, 3),
LOC(4, 4),
)
var/i = 1
var/turf/centre = locate(3,3,1)
for(var/x in range(1,centre))
var/turf/T = x
ASSERT(!isnull(T))
var/list/coords = correctCoordinates[i]
ASSERT(coords[1] == T.x)
ASSERT(coords[2] == T.y)
i += 1
if(i != 10)
CRASH("range(1,centre) iterated over [i - 1] tiles, expected 9")
//Test that arguments are parsed correctly
var/std = range(1,centre)
if(std ~! range(centre,1))
CRASH("range(1,centre) and range(centre,1) do not return the same result.")
if(std ~! range("3x3",centre))
CRASH("ViewRange argument parsing for range() isn't working correctly.")
//Test that getting the range from a mob includes the mob's loc.
var/list/mob_seen_turfs = list()
var/mob/test/timmy = new(centre)
for(var/turf/x in range(1,timmy))
mob_seen_turfs += list(x)
if(std ~! mob_seen_turfs)
CRASH("Using a non-/turf Center for range() did not work correctly.")
del(timmy)

var/list/turf_orange_case = list(
LOC(2, 2),
outer_area,
LOC(2, 3),
LOC(2, 4),
LOC(3, 2),
LOC(3, 4),
LOC(4, 2),
LOC(4, 3),
LOC(4, 4),
)

var/list/container_range_case = list(
contained,
LOC(3, 3),
outer_area,
container,
LOC(2, 2),
LOC(2, 3),
LOC(2, 4),
LOC(3, 2),
LOC(3, 4),
LOC(4, 2),
LOC(4, 3),
LOC(4, 4),
)

var/list/container_orange_case = list(
LOC(3, 3),
outer_area,
LOC(2, 2),
LOC(2, 3),
LOC(2, 4),
LOC(3, 2),
LOC(3, 4),
LOC(4, 2),
LOC(4, 3),
LOC(4, 4),
)

var/list/contained_range_case = list(
contained_trash_1,
contained_trash_2,
contained_trash_3,
container,
contained,
)

var/list/contained_orange_case = list(
container,
)


run_case(center, turf_range_case, nameof(turf_range_case), FALSE)
run_case(center, turf_orange_case, nameof(turf_orange_case), TRUE)
run_case(container, container_range_case, nameof(container_range_case), FALSE)
run_case(container, container_orange_case, nameof(container_orange_case), TRUE)
run_case(contained, contained_range_case, nameof(contained_range_case), FALSE)
run_case(contained, contained_orange_case, nameof(contained_orange_case), TRUE)

// FIXME: these pass in BYOND, but the way we iterate over area turfs diverges
// var/list/area_range_case = list(outer_area) + outer_area.contents
// var/list/area_orange_case = area_range_case.Copy()
// run_case(outer_area, area_range_case, nameof(area_range_case), FALSE)
// run_case(outer_area, area_orange_case, nameof(area_orange_case), TRUE)

del(container)

#undef LOC
8 changes: 8 additions & 0 deletions Content.IntegrationTests/DMProject/code.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
/turf/border
/mob/test

var/global/list/areas_by_type = list()
/area/New()
areas_by_type[type] = src


//The actual tests
//NOTE: Tests placed in the IntegrationTests suite
// should actually require a normal server in order to work.
Expand All @@ -26,6 +31,9 @@
throw EXCEPTION("You must override RunTest()")

/world/New()
// prepare areas
for(var/area_subtype in typesof(/area) - /area)
new area_subtype()
for(var/subtype in typesof(/datum/unit_test))
if(subtype == /datum/unit_test) //skip the base class
continue
Expand Down
4 changes: 2 additions & 2 deletions OpenDreamRuntime/Objects/Types/DreamObjectClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ protected override bool TryGetVar(string varName, out DreamValue value) {
return true;
case "view":
// Number if square & centerable, string representation otherwise
if (View is { IsSquare: true, IsCenterable: true }) {
value = new DreamValue(View.Range);
if (View.CanSquareRange) {
value = new DreamValue(View.SquareRange.Value);
} else {
value = new DreamValue(View.ToString());
}
Expand Down
4 changes: 2 additions & 2 deletions OpenDreamRuntime/Objects/Types/DreamObjectWorld.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ protected override bool TryGetVar(string varName, out DreamValue value) {

case "view":
// Number if square & centerable, string representation otherwise
if (DefaultView.IsSquare && DefaultView.IsCenterable) {
value = new DreamValue(DefaultView.Range);
if (DefaultView.CanSquareRange) {
value = new DreamValue(DefaultView.SquareRange.Value);
} else {
value = new DreamValue(DefaultView.ToString());
}
Expand Down
84 changes: 82 additions & 2 deletions OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,86 @@
return tiles;
}

public static DreamValue HandleRange(NativeProc.Bundle bundle, DreamObject? usr, bool includeCenter) {
(DreamObjectAtom? center, ViewRange range) = DreamProcNativeHelpers.ResolveViewArguments(bundle.DreamManager, usr as DreamObjectAtom, bundle.Arguments);
if (center is null)
return new DreamValue(bundle.ObjectTree.CreateList());

HashSet<DreamObjectArea> seenAreas = [];
DreamList rangeList = bundle.ObjectTree.CreateList(range.Height * range.Width);

void addToList(DreamValue value) {

Check warning

Code scanning / InspectCode

Inconsistent Naming Warning

Name 'addToList' does not match rule 'Local functions'. Suggested name is 'AddToList'.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
rangeList.AddValue(value);
if(value.TryGetValueAsDreamObject<DreamObjectTurf>(out var turfValue) && !seenAreas.Contains(turfValue.Cell.Area)) {
var area = turfValue.Cell.Area;
rangeList.AddValue(new(area));
seenAreas.Add(area);
}
}

if(center is DreamObjectArea areaCenter) { // yeah you can do this
// setting rangeList directly cause we'll never hit the area case
rangeList.AddValue(new(center));
foreach(var turf in areaCenter.Turfs) {
rangeList.AddValue(new(turf));
foreach(var content in turf.Contents.EnumerateValues()) {
rangeList.AddValue(content);
}
}
return new(rangeList);

Check warning

Code scanning / InspectCode

Incorrect blank lines: Blank lines are missing elsewhere Warning

Blank lines are missing, expected minimum 1 instead of 0
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
}
else if(center is DreamObjectTurf turfCenter) {
if(includeCenter) { // if we're orange, we want to skip the else block too
addToList(new(center));
foreach(DreamValue content in turfCenter.Contents.EnumerateValues()) {
addToList(content);
}
}
}
else { // we're getting the range of a container
// add our contents first
if(includeCenter) {
if(center.TryGetVariable("contents", out var centerContents) && centerContents.TryGetValueAsDreamList(out var centerContentsList)) {
foreach(DreamValue content in centerContentsList.EnumerateValues()) {
addToList(content);
}
}
centerContents.Dispose();

Check warning

Code scanning / InspectCode

Incorrect blank lines: Blank lines are missing elsewhere Warning

Blank lines are missing, expected minimum 1 instead of 0
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
}

// then we include our loc and the loc's contents (which includes us)
if (center.TryGetVariable("loc", out DreamValue centerLoc)) {
if (centerLoc.TryGetValueAsDreamObject<DreamObjectAtom>(out var centerLocObject)) {
addToList(centerLoc);

using var contents = centerLocObject.GetVariable("contents");
if (contents.TryGetValueAsDreamList(out var locContentsList)) {
foreach (DreamValue content in locContentsList.EnumerateValues()) {
if(!includeCenter && content.TryGetValueAsDreamObject(out var dreamObject) && dreamObject == center)
continue;
addToList(content);
}
}
}
centerLoc.Dispose();

Check warning

Code scanning / InspectCode

Incorrect blank lines: Blank lines are missing elsewhere Warning

Blank lines are missing, expected minimum 1 instead of 0
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
// if center isn't a turf either, abort here
if(centerLocObject is not DreamObjectTurf) {
return new(rangeList);
}
}
}

// finally, add the surrounding turfs
foreach (var turf in DreamProcNativeHelpers.MakeViewSpiral(center, range)) {
addToList(new DreamValue(turf));
foreach (DreamValue content in turf.Contents.EnumerateValues()) {
addToList(content);
}
}

return new(rangeList);
}

public static DreamValue HandleViewersHearers(NativeProc.Bundle bundle, DreamObject? usr, bool ignoreLight) {
DreamValue? depthValue = null;
DreamObjectAtom? center = null;
Expand Down Expand Up @@ -228,7 +308,7 @@

var centerPos = bundle.AtomManager.GetAtomPosition(center);
if (depthValue is null || !depthValue.Value.TryGetValueAsInteger(out var depth))
depth = bundle.DreamManager.WorldInstance.DefaultView.Range;
depth = bundle.DreamManager.WorldInstance.DefaultView.BiggestAxis;

foreach (var mob in bundle.MapManager.GetMobsInRange(centerPos, depth)) {
var (_, range) = ResolveViewArguments(bundle.DreamManager, mob, bundle.Arguments);
Expand Down Expand Up @@ -283,7 +363,7 @@

var centerPos = bundle.AtomManager.GetAtomPosition(center);
if (depthValue is null || !depthValue.Value.TryGetValueAsInteger(out var depth))
depth = bundle.DreamManager.WorldInstance.DefaultView.Range;
depth = bundle.DreamManager.WorldInstance.DefaultView.BiggestAxis;

foreach (var atom in bundle.AtomManager.EnumerateAtoms(bundle.ObjectTree.Mob)) {
var mob = (DreamObjectMob)atom;
Expand Down
56 changes: 2 additions & 54 deletions OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1815,18 +1815,7 @@ public static DreamValue NativeProc_ohearers(NativeProc.Bundle bundle, DreamObje
[DreamProcParameter("Dist", Type = DreamValueTypeFlag.Float, DefaultValue = 5)]
[DreamProcParameter("Center", Type = DreamValueTypeFlag.DreamObject)]
public static DreamValue NativeProc_orange(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) {
(DreamObjectAtom? center, ViewRange range) = DreamProcNativeHelpers.ResolveViewArguments(bundle.DreamManager, usr as DreamObjectAtom, bundle.Arguments);
if (center is null)
return new DreamValue(bundle.ObjectTree.CreateList());
DreamList rangeList = bundle.ObjectTree.CreateList(range.Height * range.Width);
foreach (var turf in DreamProcNativeHelpers.MakeViewSpiral(center, range)) {
rangeList.AddValue(new DreamValue(turf));
foreach (DreamValue content in turf.Contents.EnumerateValues()) {
rangeList.AddValue(content);
}
}

return new DreamValue(rangeList);
return DreamProcNativeHelpers.HandleRange(bundle, usr, false);
}

[DreamProc("oview")]
Expand Down Expand Up @@ -1972,48 +1961,7 @@ public static DreamValue NativeProc_rand_seed(NativeProc.Bundle bundle, DreamObj
[DreamProcParameter("Dist", Type = DreamValueTypeFlag.Float, DefaultValue = 5)]
[DreamProcParameter("Center", Type = DreamValueTypeFlag.DreamObject)]
public static DreamValue NativeProc_range(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) {
(DreamObjectAtom? center, ViewRange range) = DreamProcNativeHelpers.ResolveViewArguments(bundle.DreamManager, usr as DreamObjectAtom, bundle.Arguments);
if (center is null)
return new DreamValue(bundle.ObjectTree.CreateList());

DreamList rangeList = bundle.ObjectTree.CreateList(range.Height * range.Width);

//Have to include centre
rangeList.AddValue(new DreamValue(center));

if(center.TryGetVariable("contents", out var centerContents) && centerContents.TryGetValueAsDreamList(out var centerContentsList)) {
foreach(DreamValue content in centerContentsList.EnumerateValues()) {
rangeList.AddValue(content);
}
}

centerContents.Dispose();

// If it's not a /turf, we have to include its loc and the loc's contents
if (center is not DreamObjectTurf && center.TryGetVariable("loc",out DreamValue centerLoc)) {
if (centerLoc.TryGetValueAsDreamObject<DreamObjectAtom>(out var centerLocObject)) {
rangeList.AddValue(centerLoc);

using var contents = centerLocObject.GetVariable("contents");
if (contents.TryGetValueAsDreamList(out var locContentsList)) {
foreach (DreamValue content in locContentsList.EnumerateValues()) {
rangeList.AddValue(content);
}
}
}

centerLoc.Dispose();
}

//And then everything else
foreach (var turf in DreamProcNativeHelpers.MakeViewSpiral(center, range)) {
rangeList.AddValue(new DreamValue(turf));
foreach (DreamValue content in turf.Contents.EnumerateValues()) {
rangeList.AddValue(content);
}
}

return new DreamValue(rangeList);
return DreamProcNativeHelpers.HandleRange(bundle, usr, true);
}

[DreamProc("ref")]
Expand Down
Loading
Loading