ID:151410
 
Well I thought about making a whole bunch of datums for everything I could possibly need and then putting them in one library I can include in my projects. So far I have a String Datum, points, and a color datum, I have another assortment of procs but focusing on the datums right now. Any suggestions on how to improve the current ones and anyone have ideas for future datums?


Definitions:
#define ILLEGAL 1
#define ALLOWED 2

#define ROUND 1
#define NOROUND 0

#define BACK 1
#define FRONT 2

#define NO_MAX null

#define NUMBERS list("0","1","2","3","4","5","6","7","8","9")
#define LALPHABET list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")
#define UALPHABET list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z")
#define ALPHABET list("A","a","B","b","C","c","D","d","E","e","F","f","G","g","H","h","I","i","J","j","K","k","L","l",\
"M","m","N","n","O","o","P","p","Q","q","R","r","S","s","T","t","U","u","V","v","W","w","X","x","Y","y","Z","z")

String Datum:
String
var
text = ""
max_length = null
list/filter//only initialized when needed.
filter_mode = ILLEGAL
proc
append(Append as text, Direction=BACK)
if(max_length != null && length(Append)+text_length() <= max_length) {if(Direction==BACK){text+=Append;};else{text=Append+text;};filter();return 1}
else if(Append) {if(Direction==BACK){text+=Append;};else{text=Append+text;};filter();return 1}
return 0
set_max_length(new_max_length)
if(new_max_length && new_max_length > 0)
max_length = new_max_length
if(text_length() > max_length)
text = copytext( text, 1, max_length+1 )
return 0
insert(Insert as text, Place=text_length()+1)
return copytext(text, 1, Place)+Insert+copytext(text, Place+1)
set_filter(var/list/X)
if(X && length(X)){filter=list();filter=X;filter();return 1;}
return 0
set_filter_mode(NewMode = ILLEGAL)
if(NewMode != ILLEGAL && NewMode != ALLOWED) return 0
filter_mode = NewMode
return 1
filter()
if(filter && filter.len)
if(src.filter_mode == ILLEGAL)
for(var/v in filter){replace(v, filter[v]?filter[v]:"")}
return 1
if(src.filter_mode == ALLOWED)
var/t
var/new_text = text
for(var/i=1 to text_length())
t = copytext(text, i, i+1)
if(!filter.Find(t)) new_text = textreplaceEx(new_text, t, "")//replace all
text = new_text
return 1
else return 0
replace(Replace as text, Replacement as text)
text = textreplace(text, Replace, Replacement)
replaceEx(Replace as text, Replacement as text)
text = textreplaceEx(text, Replace, Replacement)
text_length()
return length(text)
get()//just in case
return text
set_text(Newtext as text)
if(max_length != null && max_length >= length(Newtext)){text=Newtext;filter();return 1;}
return 0
append_string(String/Append, Direction=BACK)
if(Append){append(Append.text, Direction);return 1;}
return 0
random_append(Characters=1, Direction=BACK, ascii_min=97, ascii_max=122)//stupid function...
while(Characters)
append( ascii2text(rand(ascii_min, ascii_max)), Direction )
Characters--
New( new_text as text, new_max_length as num, list/new_filter, new_filter_mode as num )
if(new_max_length) max_length = new_max_length
if(new_text) set_text(new_text)
if(new_filter_mode) set_filter_mode(new_filter_mode)
if(new_filter) set_filter(new_filter)

proc
String( new_text as text, new_max_length as num, list/new_filter, new_filter_mode as num )
return new/String (new_text, new_max_length, new_filter, new_filter_mode)


Points:
point//point, makes it easier for me...
var
x = 0
y = 0
New(_x, _y)
if(_x)
x = _x
if(_y)
y = _y
proc/SetPoints(_x, _y)
if(_x)
x = _x
if(_y)
y = _y
proc
point(_x_, _y_, should_round)//Creates a point
if(!should_round)
return new/point(_x_, _y_)
else
return new/point(round(_x_), round(_y_))
text_point(text, should_round)//Creates a point from text such as "1,1"
var/comma = findtext(text, ",")
var/_x_ = copytext(text, 1, comma)
var/_y_ = copytext(text, comma+1, length(text)+1)
return point(should_round?round(text2num(_x_)):text2num(_x_), should_round?round(text2num(_y_)):text2num(_y_))


point3
var
x = 0
y = 0
z = 0
New(_x, _y, _z)
if(_x) x = _x
if(_y) y = _y
if(_z) z = _z
proc
point3(_x_=0, _y_=_x_,_z_=1,should_round = NOROUND)//same as above
if(!should_round) new/point3(_x_, _y_, _z_)
else return new/point3(round(_x_),round(_y_),round(_z_))


Colors:
proc
getAlpha(hex)//Made by Kuraudo
if(length(hex) < 9)
return 255
else
hex = uppertext(hex)
var
hi = text2ascii(hex,8)
lo = text2ascii(hex,8)
return ( ((hi >= 65 ? hi-55 : hi-48)<<4) | (lo >= 65 ? lo-55 : lo-48) )
getRed(hex)//Made by Kuraudo
hex = uppertext(hex)
var
hi = text2ascii(hex, 2)
lo = text2ascii(hex, 3)
return ( ((hi >= 65 ? hi-55 : hi-48)<<4) | (lo >= 65 ? lo-55 : lo-48) )

getGreen(hex)//Made by Kuraudo
hex = uppertext(hex)
var
hi = text2ascii(hex, 4)
lo = text2ascii(hex, 5)
return ( ((hi >= 65 ? hi-55 : hi-48)<<4) | (lo >= 65 ? lo-55 : lo-48) )

getBlue(hex)//Made by Kuraudo
hex = uppertext(hex)
var
hi = text2ascii(hex, 6)
lo = text2ascii(hex, 7)
return ( ((hi >= 65 ? hi-55 : hi-48)<<4) | (lo >= 65 ? lo-55 : lo-48) )

GetColors(hex)//Made by Kuraudo
return list( getRed(hex), getGreen(hex), getBlue(hex), getAlpha(hex) )


Color//Easy shizzle
var
r = 0
g = 0
b = 0
a = 255
value = "#000000"

New(_R, _G, _B, _A)
..()
if(!_R&&!_G&&!_B&&!_A)
value = null
r = null
g = null
b = null
a = 0
else
if(_R&&istext(_R))
value = _R
make_rgb()
else
if(_R) r=_R
if(_G) g=_G
if(_B) b=_B
if(_A) a=_A
value = rgb(r, g, b, a)

proc
make_rgb()
r = getRed(value)
g = getGreen(value)
b = getBlue(value)
a = getAlpha(value)
make_color()//form_color() must be used or the value will not automatically change.
value = rgb(min(max(r, 0), 255), min(max(g, 0), 255), min(max(b, 0), 255), min(max(a, 0), 255))
set_rgba(new_r, new_g, new_b, new_a)
r = min(max(new_r, 0), 255)
g = min(max(new_g, 0), 255)
b = min(max(new_b, 0), 255)
a = min(max(new_a, 0), 255)
make_color()
set_r(val)
val = min(max(val, 0), 255)
r = val
make_color()
set_g(val)
val = min(max(val, 0), 255)
g = val
make_color()
set_b(val)
val = min(max(val, 0), 255)
b = val
make_color()
set_a(val)
val = min(max(val, 0), 255)
a = val
make_color()
Cset_value(val)//Does checks to make sure stuff is right.
if(length(val) > 8) val = copytext(val, 1, 9)
if(copytext(val, 1,2) != "#") val = "#[val]"
value = val
make_rgb()
set_value(val)
value = val
make_rgb()

proc
Color(_r, _g, _b, _a)
return new/Color(_r, _g, _b, _a)



necessary procs:
proc
textreplace(text as text, to_replace as text, replacement as text)
var/pos = findtext(text, to_replace)
var/replacement_length = length(replacement)
var/to_replace_length = length(to_replace)
while(pos)
text = copytext(text, 1, pos)+replacement+copytext(text, pos+to_replace_length)
pos = findtext(text, to_replace, pos+replacement_length)
return text
textreplaceEx(text as text, to_replace as text, replacement as text)
var/pos = findtextEx(text, to_replace)
var/replacement_length = length(replacement)
var/to_replace_length = length(to_replace)
while(pos)
text = copytext(text, 1, pos)+replacement+copytext(text, pos+to_replace_length)
pos = findtextEx(text, to_replace, pos+replacement_length)
return text
I'd say games usually use Vectors not points. Point it-self is quite useless. There's vector library made already, but if you wish to make own one, I'd suggest to add these features:
  • dot product
  • cross product
  • vector rotation
  • vector multiplication

    However on other hand, it's quite useless on BYOND. Compared to how slow new() proc is, it'd impossible to make anything decent.
In response to Ripiz (#1)
Ripiz wrote:
However on other hand, it's quite useless on BYOND. Compared to how slow new() proc is, it'd impossible to make anything decent.

Then recycle them. That also solves the problem of how slow del() can be. The only issue would be tracking old references, but since vectors are usually only referenced by a single object, that wouldn't be too bad.
In response to Ripiz (#1)
I was going to make vectors but for the sake of simplicity I decided to go with points, could always change that though.
A DateTime proc would be useful:

DateTime
var
day
month
year
hours
minutes
seconds

New(timestamp)
// parse the timestamp to set the day/month/year/etc.

proc
format(f)
// return a formatted timestamp


You could also include a procs to add or subtract time. You tell it to subtract 57 hours and it figures out how to adjust the hours, day, month, and year vars.
In response to Ripiz (#1)
However on other hand, it's quite useless on BYOND. Compared to how slow new() proc is, it'd impossible to make anything decent.

This is the second time I've heard this opinion, and I'm not sure why. I quickly wrote some code to test this. The code handled basic "pixel movement", collision detection, and some simple collision physics. It was written as quickly as possible, and was purposefully as unoptimized as it gets. The result: I had no problem creating thousands of vectors a tick, and it ran smoothly with hundreds of objects warping around the screen in the speed of light because someone forgot to bound acceleration vectors (tick_lag wasn't changed, either). I'll admit you should lower your expectations for an online game, but you really have no reason to "fear" vectors. With some basic optimizations (such as recycling vectors, like DC suggested), as well as a decent server, you should have no problem at all running a game with pixel movement online. Anyone who doubts this should simply test it himself.
In response to Toadfish (#5)
May I see what you have made? Whenever I try to make something it ends up extremely slow.

Could even compare Forum_Account's Pixel Movement library, it runs at 50% CPU on average, pretty much any other language could do it near to 0% CPU.
In response to Ripiz (#6)
I thought the comparison was BYOND with datums vs BYOND without datums?
In response to Ripiz (#6)
Hopefully this works: download. I don't have the source code handy. Use the populate verb to spawn blobs at random (you might have to restart the game a few times if a blob spawns on you, especially if you create 500+ of the buggers). I think you can click objects to have them start moving at random, but I'm not sure. The physics, as I mentioned, is bugged (the mass needs tweaking and bounding doesn't work), so be /very/ careful lest things start moving FAST. #DEBUG is on, I think, so profiling works.
In response to Forum_account (#4)
Tried to come up with something like that :P. It works for the most part but I couldn't figure out a good way to increment the text days as well and it can only take timestamps in a single format(DDD MMM DD hh:mm:ss YYYY).

DateTime
var
day = "Sun"//at a lost with how to increment this right, due to inconsistencies
month = "Jan"
dom = 1
year = 1
hours = 1
minutes = 1
seconds = 1
leap_year = FALSE

New(timestamp)
var/list/Info = text2list( timestamp," " )
var/list/Time = text2list(Info[4],":")
day = Info[1]
month = Info[2]
dom = text2num(Info[3])
year = text2num(Info[5])
hours = text2num(Time[1])
minutes = text2num(Time[2])
seconds = text2num(Time[3])
if( year%4 == 0 ) leap_year = TRUE
else leap_year = FALSE

proc
get_time()
return "[day] [month] [dom] [hours]:[minutes]:[seconds] [year]"

format( f )
f = textreplace(f, "DDD", day)
f = textreplace(f, "MMM", month)
f = textreplace(f, "DD", dom)
f = textreplace(f, "hh", hours)
f = textreplace(f, "mm", minutes)
f = textreplace(f, "ss", seconds)
f = textreplace(f, "YYYY", year)


Seconds(var/n)
if( n/60 > 1 )
Minutes( round(n/60) )
n %= 60
seconds+=n
if(seconds < 0)
seconds = 60-abs(seconds)
Minutes(-1)
if(seconds >= 60)
seconds -= 60
Minutes(1)
Minutes(var/n)
if( n/60 > 1 )
Hours( round(n/60) )
n %= 24
minutes+=n
if(minutes < 0)
minutes = 60 - abs(minutes)
Hours(-1)
if(minutes >= 60)
minutes -= 60
Hours(1)
Hours(var/n)
if( n/24 > 1 )
Days( round(n/24) )
n %= 24
hours+=n
if(hours < 0)
hours = 60-abs(hours)
Days(-1)
if(hours >= 24)
hours -= 24
Days(1)
Days(var/n)
var/max_months = days_of_months[lowertext(month)]
if( n/max_months > 1 )
Months( round(n/max_months) )
n %= 24
dom += n
if(dom < 0)
dom = max_months-(abs(dom)-1)
Months(-1)
if(dom >= max_months)
dom -= max_months
Months(1)
Months(var/n)
if( n/12 > 1 )
Years( round(n/12) )
n%=12
var/month_number = month_change[month]
month_number += n
if(month_number<0)
month_number = 12-abs(month_number)
if(month_number == 0) month_number = 12//takes care of that
Years(-1)
if(month_number>12)
month_number -= 12
if(month_number == 0) month_number = 12//takes care of that
Years(1)
month = reverse_moth_change["[month_number]"]
Years(var/n)
year++//:D


var/list/day_change = list( "sun"=1,"mon"=2,"tue"=3,"thu"=4,"fri"=5,"sat"=6 )
var/list/month_change = list( "jan"=1,"feb"=2,"mar"=3,"apr"=4,"may"=5,"jun"=6,"jul"=7,"aug"=8,"sep"=9,"oct"=10,"nov"=11,"dec"=13 )

var/list/reverse_day_change = list( "1"="sun","2"="mon","3"="tue","4"="thu","5"="fri","6"="sat" )
var/list/reverse_moth_change = list( "1"="jan","2"="feb","3"="mar","4"="apr","5"="may","6"="jun","7"="jul","8"="aug","9"="sep","10"="oct","11"="nov","13"="dec" )

var/list/days_of_months = list( "jan"=31,"feb"=28,"mar"=31,"apr"=30,"may"=31,"jun"=30,"jul"=31,"aug"=31,"sep"=30,"oct"=31,"nov"=30,"dec"=31 )


//Needed this poem
/*
30 days hath September,
April, June and November,
All the rest have 31,
Excepting February alone
(And that has 28 days clear,
With 29 in each leap year).
*/
Packaged the string datum into a library.
In response to Ripiz (#6)
I'm curious just to see the chaos.
In response to Hiro the Dragon King (#11)
To see it, move to the centre of the map, and 'populate 500'. If you're lucky and no blob spawned in your freaking face you should see it after bumping a few. It might take a few tries to get since you're sometimes surrounded by 'blobs sitting on blobs' (the populate verb doesn't bother to check that) which would make them stuck in place for a while. I think I used to have the maximum speed about twice as high, which made this a lot more crazy when you started warping around the screen yourself.