ID:843116
 
The DM Reference is all I've ever needed to figure out what variables and functions BYOND has to offer (it's not much, really).
I've also followed all the new additions since I started.
I barely read any of the DM Guide. Of what I have read, it seems like Dan was a pretty witty guy.

(note: I didn't realize how long this would get. I'm not a writer, and I'm definitely not good at predicting if you'll even take the time and effort to go through this. So, there's a tl;dr.)

I don't remember how exactly I figured out language syntax, I think it just came to me naturally after a while.
A lot of programming is basic algebra; variables and functions, as I've come to know it.
You could say d(x)=a(b(c(x))), but d(x) ends up to be one value, of possibly many possible values.

a(x) = x
b(x) = 2*x
c(x) = 3*x
d(x) = a(b(c(x)))

d(4) = a(b(c(4)))
= a(b(3*4)) = a(b(12))
= a(2*12) = a(24)
= 1*24
d(4) = 24

Procedures in DM take input(s) (AKA arguments), and only return one value (AKA the return value).
Operators work exactly the same way, except their inputs are positioned differently.
There are operators that take 1, 2, or 3 inputs, but only return one output.
There's also the >> and << operators, but of those, only the bit-shift operators actually return something.

(Please note that none of this below is magical, undocumented observations. Everything is mentioned in the DM Reference, like short-circuiting and the fact that operators literally have a "return value" like procs do.)
// all of these expressions are true
(-a == -3) (when a == 3)
(!1 == 0)

(3 == 3) == 1
(3 != 3) == 0
(5 && 6) == 6
(4 || 5) == 4

(1 ? 2 : 3) == 2
(0 ? 4 : 5) == 5

What you choose to do with the output is up to you. The important thing is, they don't need an if() to be useful. You can treat the result as any other value, like by storing it in a variable.

A good thing to know about if() statements is, in the end, they're only checking one value (a number, string, object, or null). You can put a ton of operators and procedures (remember, essentially the same) in an if() statement, but in the end, it's just one value.
That's because operators and procedures only return one value.
Combinations of the two don't make a difference, they'll still only return one value.
var hair_r = 255, hair_g = 255, hair_b = 0
if(cake == "lie" && a_if_true("pie") && "Yes" && \
hair_r == 255 && hair_g == 255 && !hair_b)
-> if(1 && "a" && "Yes" && 1 && 1 && 1)
-> if("a" && "Yes" && 1 && 1 && 1)
-> if("Yes" && 1 && 1 && 1)
-> if(1 && 1 && 1)
-> if(1 && 1)
-> if(1)
src << "You poor, cakeless blonde. At least you have pie."

proc/a_if_true(x) return x && "a"
proc/True() return 1
proc/False() return 0
...
var cake = "lie" || a_if_true(!null) && "b" && \
"poo" == "poo" && 1 != 3 && !0 && True() && !False()

Nothing after || matters because cake == "lie".
None of the procs after the || are even called.
This is because of order of operations. (operations -> operator actions)

Playing with order of operations, you can do this:
mob
var locked
Move() return !locked && ..()

..() won't be called if mob.locked is true, the && operator stops at the first false value.

You could even say that only if(1) statements pass if they are read like if(!!(c)), where c is the entire condition.
if("blah" && 2)
-> if(!!("blah" && 2)) // precede condition with !!
-> if(!!(2)) // operate with &&
-> if(!(0)) // operate with !
-> if(1) // operate with !
-> do code

if(null)
-> if(!!null)
-> if(!1)
-> if(0)
-> do code under else

You should understand that if()s aren't as special as you might think. Conditional operators don't require them, similarly to how alert()s don't require a switch() statement to do anything. (alert() is just a proc, nothing more. It returns a value like everything else.)

Variables, however, are everywhere. Everything can be put inside a variable. If you always use call()() instead of calling procs by name, even procs (not built-in) can be put in variables and dynamically called.
var a = "a"
var a_is_a = a == "a"
var a_is_not_a = a != "a"
var pass = "Yay!"
var fail = "Huh?!"
...
if(a_is_a)
world << pass
if(a_is_not_a)
world << fail

var world_makes_sense = a_is_a && !a_is_not_a
if(world_makes_sense)
var double_pass = "Double [pass]"
world << double_pass

proc/a() return "alpha"
proc/b() return "beta"
...
var func_a = /proc/a
var func_b = /proc/b
var a = call(func_a)()
var b = call(func_b)()
world << a
world << b

The above could all be written like this:
if("a" == "a") world << "Yay!"
if("a" != "a") world << "Huh?!"
if("a" == "a" && !("a" != "a")) world << "Double Yay!"

world << "alpha"
world << "beta"

If you consider the last two lines, you might notice that I did a simple replacement.
Realistically, procedures have much more flexibility and can have many different return values.
proc/a_sometimes() return prob(50) ? "a" : "b"

Some procedures don't care about return values, so you'd never use them when considering other things.
proc/say_hello() world << "Hello, world!"
...
if(say_hello()) world << "Huh?"


This has been an over-complication of an extremely simple and fundamental part of most programming anyone will ever encounter:

tl; dr


Variables contain a data; procedures and operators return output data. Wherever you type an operator or procedure, it's replaced by its return value as soon as the code reaches it.

The reason I'm making this post is to make people realize how over-complicated they make some things. I've left out a lot, and if you read anything before the tl;dr, you probably have questions. I'll try to answer them, so long as they're not ridiculous.

Here's a poorly-made fish: </<>
A lot of BYOND users don't think about things this way because they associate procs with what they're commonly used to create. switch() and input() aren't things you can use to make a menu, to many people they *are* a menu. To many people oview() isn't a proc that returns a list of atoms, it's a proc for making enemies look for targets. People probably make these associations because:

1. They learned to program from looking at some game's code, so the only way they know about switch() and input() is from a menu they saw.
2. They don't have much programming experience and it's fairly logical to make these associations to simplify how you view programming.
3. DM provides useful procs so there's not much you need to add to create basic AI - oview() is the bulk of the work.

When you think about programming like this, it makes you think "what can DM do?" instead of thinking "what can I do?". It puts a low ceiling on what you can accomplish and this is why people make feature requests for things that can easily be accomplished with DM - people just don't realize they can make something if there's not a single DM proc that they associate with the feature.
That fish is bad and you should feel bad! :D

<º(((><
In response to A.T.H.K
A.T.H.K wrote:
That fish is bad and you should feel bad! :D

<º(((><

Of course it's bad. I didn't make it.
In response to Kaiochao
Kaiochao wrote:
A.T.H.K wrote:
That fish is bad and you should feel bad! :D
Of course it's bad. I didn't make it.

But I didn't make this fish, yet it looks good... <º(((><

For the eye it's ALT 1447 on the numpad ;)
In response to Truseeker
Truseeker wrote:
Kaiochao wrote:
A.T.H.K wrote:
That fish is bad and you should feel bad! :D
Of course it's bad. I didn't make it.

But I didn't make this fish, yet it looks good... <º(((><

I think he meant that the BYOND forum added the fish-like thing to the end of his post. It looks like it thinks that "<<" was a type of tag so it added a closing "<" tag, which ends up being </<>
My 2 cents if I remember correctly you can check for odd numbers by doing:

if(5 % 2 == 1) <- TRUE
if(6 % 2 == 1) <- FALSE
if(5 & 1) <- TRUE
if(6 & 1) <- FALSE

5 being any odd number and 6 being any even number.
In response to Zaltron
Actually, there is an alternative way to accurately check if a number is odd. In a case where the dividend(-5 in this case) is an odd negative, there will be a return value of 0 - the alternative way is dividend % divisor != 0

(5 % 2 == 1) Returns 1(TRUE) since 5 is an odd number.

(-5 % 2 == 1) Returns 0(FALSE) even though -5 is an odd number.

(-5 % 2 != 0) Returns 1(TRUE), as expected, since -5 is an odd number.
n & 1 is going to be faster though. Normally it doesn't matter but modulo is notoriously slow.
Well, it certainly depends on the hardware in question. From Wikipedia:

Optimizing compilers may recognize expressions of the form expression % constant where constant is a power of two and automatically implement them as expression & (constant-1). This can allow the programmer to write clearer code without compromising performance.

I'd rather sacrifice the petty performance gain for clarity.
Probably the most helpful thing I learned about this is as follows:

if ( ! Locked && moveable)


Anytime you use "and" which is the && if any of the arguements are not true then it auto returns false. && is good to use when the boolean must be true to continue.

Such as in the example. If we used || the or symbol than even if the player got locked by an ability but movable was true then the if statement would still return true which coukd lead to some odd things.