ID:1671812
 
(See the best response by Ter13.)
Hello. I have written the code below to create a click and drag selection box for turfs, which seems to work quite well. However, I am uncertain whether there's a better way I should be doing this. I'm looking for any suggestions for improvement.

Also, what would be a good way to limit the size of the selection box? As it is now, it is possible for players to attempt to select large areas of tiles, which slows down the creation of the box.

Thanks! :)

client
var/list/selectionImages = list()
var/turf/selectionStart

MouseDown(object, location, control, params)
src.clearSelectionImages()
src.selectionStart = object:getTurf()

MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params)
if(src.selectionStart)
src.clearSelectionImages()

for(var/turf/t in block(src.selectionStart, over_location))
var/image/i = new('icons/other/effects.dmi', t, "selection", EFFECT_LAYER + 1)
src.selectionImages += i
src.mob << i

MouseUp(object, location, control, params)
src.clearSelectionImages()
src.selectionStart = null

proc/clearSelectionImages()
for(var/image/i in src.selectionImages)
del(i)
Best response
Yours was pretty good, but I decided to pretty it up a bit by constraining it all to a temporary datum. I also opted to make it so that it doesn't clear all images every time you drag to a new square, and instead implemented an approach that would only update the squares needed.

selection_action
var
client/owner
list/turfs = list()
list/images = list()
New(location,client/owner)
src.owner = owner
selection_turfs = list(location)
AddTurf(location)
proc
MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params)
if(isturf(over_location))
var/list/l = block(selection_turfs[1],over_location)
var/list/added = l - src.turfs
var/list/removed = src.turfs - l
for(var/turf/t in added)
AddTurf(t)
for(var/turf/t in removed)
RemoveTurf(t)
AddTurf(turf/t)
turfs += t
var/image/i = new('icons/other/effects.dmi',t,"selection",EFFECT_LAYER+1)
images[t] = i
owner << i

RemoveTurf(turf/t)
var/image/i = images[t]
owner.images -= i
i.loc = null
images -= t
turfs -= t

Clear()
for(var/turf/t in turfs)
RemoveTurf(t)

client
var/selection_action/selection_action

MouseDown(object, location, control, params)
if(isturf(location)&&control=="map1") //make sure to change this.
selection_action = new(location,src)

MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params)
if(selection_action)
selection_action.MouseDrag(src_object,over_object,src_location,over_location,src_control,over_control,params)

MouseUp(object, location, control, params)
src.selection_action.Clear()
src.selection_action = null
Thank you, Ter. I'll definitely use something similar to your approach. :)

As an aside, what would it take to create a version that only allows a selection to be a single tile wide, non-diagonal line? (I'm really not sure how else to describe that, which is why I don't know where to begin with that.)
clamp the coordinates to the max offset:

var/turf/startloc = turfs[1]
var/list/l = block(startloc,locate(min(over_object.x,startloc.x+maxwidth-1),min(over_object.y,startloc.y+maxheight-1),startloc.z))


Just embed those maxwidth/maxheight variables in your datum, and give that a shot.
I see, thank you. I'll work on this more this evening and see what I get. :)
Actually, I need to better describe what I meant. I thought about it, and your clamping suggestion would solve my need for a size limit as described in my original post. However, I'm also looking to implement something else.

I want to create an additional selection method that only allows "drawing" lines. Perhaps that can be described as being like using the line creation tool in MS Paint; however I want to only allow horizontal and vertical lines, not diagonals. I apologize that I cannot think of a visual example.
var/list/p = params2list(params)
var/dx = over_object.x - startloc.x + (text2num(p["icon-x"])-1) / TILE_WIDTH
var/dy = over_object.y - startloc.y + (text2num(p["icon-y"])-1) / TILE_HEIGHT
if(dx>dy)
dy = 0
else
dx = 0

var/list/l = block(startloc,locate(round(startloc.x+dx),round(startloc.y+dy),startloc.z))


Something more like that, then? You'd have to define a special subtype of selection_action, and override the area where I get the block of turfs:

selection_action
line_select
MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params)
if(isturf(over_location))
var/list/p = params2list(params)
var/dx = over_object.x - startloc.x + (text2num(p["icon-x"])-1) / TILE_WIDTH
var/dy = over_object.y - startloc.y + (text2num(p["icon-y"])-1) / TILE_HEIGHT
if(dx>dy)
dy = 0
else
dx = 0
var/list/l = block(startloc,locate(round(startloc.x+dx),round(startloc.y+dy),startloc.z))
var/list/added = l - src.turfs
var/list/removed = src.turfs - l
for(var/turf/t in added)
AddTurf(t)
for(var/turf/t in removed)
RemoveTurf(t)


Then you'd initialize a new selection_action/line_select where appropriate, rather than initializing a regular selection_action type.
Wow, it's already been almost a year since I made this.

atom.transform and bounds().