ID:138802
 
Code:
mob
verb
show_text(msg as text)
TEXT(msg, 1, 3, 40, 0, "CHAT",32)



obj
letter
layer = 11
icon = 'HUD - Font.dmi'
New(letter)
..()
icon_state = "[letter]"



mob
var
list
TEXT = new
proc
TEXT(string, loc_x, loc_y, pix_x, pix_y, LABEL = "none")

var
slength = length(string)
txttrack = 1
index = 1


while(slength--)
var/letter = copytext("[string]",txttrack++, txttrack)
var/obj/letter/l = new(letter, pix_x, pix_y)
l.screen_loc = "[loc_x++],[loc_y]"
l.pixel_x = pix_x
l.pixel_y = pix_y


TEXT["[LABEL][index++]"] += l //keep track of text on screen




client.screen += l


Problem description:

I'm trying to make text on screen, however I was hoping not to just simply increase the screen_loc x variable for each letter, as this is very large spacing.

Instead I just want the increase the pixel offset of x by a certain amount. However all that I'm really struggling with is why does altering pixel_x or pixel_y in any manner not do anything?
Speedro wrote:
I'm trying to make text on screen, however I was hoping not to just simply increase the screen_loc x variable for each letter, as this is very large spacing.

Instead I just want the increase the pixel offset of x by a certain amount. However all that I'm really struggling with is why does altering pixel_x or pixel_y in any manner not do anything?

pixel_x/y alter the display of an atom on the map. If you want to change the pixel offsets in the screen, you need to use the special screen_loc syntax:

It is also possible to specify a pixel offset. Screen objects do not use pixel_x and pixel_y for this purpose, because it is intended that an object could exist on the map and in the screen object list simultaneously, so positioning must be independent. Pixel offsets are specified after a colon like this: "1:16,1:16". In this case the object is shifted to the northeast by 16 pixels.


So you would do:
l.screen_loc = "[loc_x]:[pix_x],[loc_y]:[pix_y]"


Also, instead of increasing loc_x with each character, you need to increase pix_x by the width of the character, and then when that reaches a certain threshold, convert that over to loc_x.
In response to DarkCampainger
Perfect thanks, that would explain it!

Also I'm wondering with this:
mob
proc
DEL_TEXT(LABEL)
if(!LABEL) return
var
index = 1
for()
var/text = TEXT["[LABEL][index++]"]
if(text)
client.screen -= text
TEXT -= text
del text
else
break //all text cleared!!


Notice how I remove it from the client's screen, remove it from the player's TEXT list, and then delete it. Is each step necessary, or if I simply deleted the text without removing it from the list index, would it matter?
In response to Speedro
Speedro wrote:
Notice how I remove it from the client's screen, remove it from the player's TEXT list, and then delete it. Is each step necessary, or if I simply deleted the text without removing it from the list index, would it matter?

There are two ways to delete something: you can explicitly call del() on it, or you can remove all references to it (or you can do both).

Basically, calling del() forces the garbage collector to delete the datum, and null-out all references to it.

Removing all references to a datum lets the garbage collector pick it up "at its leisure", for the lack of a better term. Typically this method will be more efficient, because it doesn't have to search for the references (because you handled them yourself) or immediately trigger the garbage collector. It has the downside that you need to maintain strict control over the references to the objects--one stray reference means the object will not be garbage collected, ie a memory leak.

When it comes to text-on-screen systems, where a single sentence can mean 200+ objects, I would suggest using the second method for performance.

I would also suggest storing each "group" of letter-objects in a normal list, so you can add/remove the entire group in one statement instead of using a loop. You can implement this as an associative list of lists, or use datums.
In response to DarkCampainger
Hm, a list within a list... I'll look into it, but until it becomes a problem I might not be digging into it too much.

In the mean-time, I'm now looking to create an options datum where I can work with a flexible list of options:

Options
New(mob/selector, list/l = new)
for(var/a in l)
world << "[a]"
//list("startgame:7,5" , "endgame:7,4")

var
index = 0
name
sloc
namecount = 0

for()
var/colon = copytext("[a]",index++,index)
namecount ++
if(colon == ":")
break

name = copytext("[a]",0,namecount)
sloc = copytext("[a]",namecount+1)


var/obj/option/b = new
b.title = name
b.screen_loc = sloc


world << "found [a]. TITLE: [name]. LOC: [sloc]."


As you can see, I am creating a list as an argument (which is something I haven't tried before), and I'm trying to loop through the contents of said list:
mob
verb
TEST_OPTIONS()
new/Options(src, list("testoptionone:5,5","testoption2:2,2","yayaya:1,1"))


As you can see from this, I'm trying to be able to not only define an option in a list, but also after the colon would be the screen_loc of the option. So in the first bit of code, I am trying to find all text before said colon for the option's title and reference point, and after the colon becomes the option's screen_loc.

However the issue is that I'm getting the error "list out of bounds, or bad text". I'm assuming its the way I'm trying to extract the text strings from the list, with <t>for(var/a in l)</t>, but for all I know it may be because I didn't initialize the list or something?

EDIT:

Woo got it, apparently it must have something to do with starting a copytext search on 0.

Options
New(mob/selector, list/l)
for(var/a in l)
//list("startgame:7,5" , "endgame:7,4")

var
indexc = 1
tname
sloc
namecount = 0


for()
namecount ++
if(copytext("[a]",indexc++,indexc) == ":")
break


tname = copytext("[a]",1,namecount)
sloc = copytext("[a]",namecount+1)


var/obj/option/b = new
b.title = tname
b.screen_loc = sloc


world << "found [a]. TITLE: [tname]. LOC: [sloc]."
In response to Speedro
Instead of parsing-out the screen_loc, why not just use an associative list?

list("testoptionone"="5,5", "testoption2"="2,2", "yayaya"="1,1")


Then you could just do:
Options
New(mob/selector, list/l)
for(var/tname in l)
var/obj/option/b = new()
b.title = tname
b.screen_loc = l[a]


Also, if you have to parse something, use findtext() to search text instead of manually looping through it. text2ascii() can also be helpful for checking individual characters.
In response to DarkCampainger
DarkCampainger wrote:
Instead of parsing-out the screen_loc, why not just use an associative list?

> list("testoptionone"="5,5", "testoption2"="2,2", "yayaya"="1,1")
>

Then you could just do:
> Options
> New(mob/selector, list/l)
> for(var/tname in l)
> var/obj/option/b = new()
> b.title = tname
> b.screen_loc = l[a]
>


Hmm, when you have the line b.screen_loc = l[a], how can I determine the index point of [a]?

Is there a way to have tname tell me what number in the list it is?



Also, if you have to parse something, use findtext() to search text instead of manually looping through it. text2ascii() can also be helpful for checking individual characters.

I actually started trying to use findtext, but I pretty much did the same thing, looping through each letter until I found the colon, and then doing the same thing. How can I treat it differently, since I thought it just returned 1 or 0?
In response to Speedro
Speedro wrote:
Hmm, when you have the line b.screen_loc = l[a], how can I determine the index point of [a]?

Is there a way to have tname tell me what number in the list it is?

I don't see you using the index anywhere, any reason you need it?

Also, if you have to parse something, use findtext() to search text instead of manually looping through it. text2ascii() can also be helpful for checking individual characters.

I actually started trying to use findtext, but I pretty much did the same thing, looping through each letter until I found the colon, and then doing the same thing. How can I treat it differently, since I thought it just returned 1 or 0?

Nope, it returns the index of the first occurrence it finds. So your loop:
for()
var/colon = copytext("[a]",index++,index)
namecount ++
if(colon == ":")
break


Could be replaced with this:
namecount = findtextEx("[a]", ":")


I used findtextEx instead of findtext because letter case isn't relevant here.

Whenever possible, use the native global processes instead of your own. Often times the native process will actually call some C++ code, which is much faster than DM code that has to be interpreted by the VM. Obviously, this isn't the case for non-global processes, such as atom/Move(), mob/Login(), turf/Enter(), ect.