DM allows you to overload most of the operators it uses when working with datums and other objects. This means that A + B can call a proc defined under A instead, with B as an argument, and the return value of that proc would be the result.

The proc name for an overloaded operator is “operator” followed immediately by the operator itself, such as operator* to override the multiplication operator. A * B will call A.operator*(B) if the proc is available.

complex     // complex number a+bi
    var/a as num
    var/b as num
 
    New(_a,_b)
        a = _a
        b = _b
 
    proc/operator+(complex/C)
        if(istype(C)) return new/complex(a+C.a, b+C.b)
        if(isnum(C)) return new/complex(a+C, b)
        return src
 
    proc/operator+=(complex/C)
        if(istype(C))
            a += C.a
            b += C.b
        else if(isnum(C)) a += C

The following operators may be overloaded:

OperatorsProcNotes
Arithmetic and binary (return new value)
A + BA.operator+(B)
A - BA.operator-(B)
-AA.operator-()Same proc as subtraction, but has no arguments
A * BA.operator*(B)
A / BA.operator/(B)
A % BA.operator%(B)
A (B)
A ** BA.operator**(B)
ABA.operator
A & BA.operator&(B)
A ^ BA.operator^(B)
~AA.operator~()
A << B (shift)A.operator<<(B)
A >> B (shift)A.operator>>(B)
A << B (output)A.operator<<(B,A,window)Ignores return value
..() falls back on default behavior which breaks target into a list of clients and tries again (see notes below)
A >> B (input)A.operator>>(null,A)Return value is assigned to B
..() falls back on default behavior
Comparisons (return true or false)
A ~= BA.operator~=(B)
A ~! BA.operator~!(B)
A < BA.operator<(B)
A >= BA.operator>=(B)
A > BA.operator>(B)
A BA.operator(B)
A > BA.operator>(B)
Assignments with side effects (return value defaults to src)
A += BA.operator+=(B)
A -= BA.operator—(B)
A *= BA.operator*=(B)
A /= BA.operator/=(B)
A %= BA.operator%=(B)
A =(B)
A= BA.operator
A &= BA.operator&=(B)
A ^= BA.operator^=(B)
A < BA.operator<(B)
A >>= BA.operator>>=(B)
A := BA.operator:=(B)
++AA.operator++()
—AA.operator—()
A++A.operator++(1)
A—A.operator—(1)
List access
A[idx]A.operatorUsed for reading a list value
A[idx] = BA.operator[]=(idx, B)Used for writing a list value; ignores return value
Other
turn(A, B)A.operator_turn(B)
“[A]“A.operator""()Specifies a custom way for converting A to text (see notes below)

If an overloaded proc is not available for an operator you try to use on a datum, a runtime error may result.

Operators without overloads

Fallback overloads

Comparison operators come in opposing pairs: ~= vs. ~!, < vs. >=, > vs. <=. You only need to override one operator from each pair; DM is smart enough to know that !(A ~= B) is the same as A ~! B.

By the same logic, you don’t have to define the assign-with-side-effect operators like += if you don’t want to. For instance if you override + but not +=, then A += B will be handled internally as A = A + B, which means the value of A after the statement may be a different datum than A was before. The value of A can also change if you do overload += but the proc returns a value other than null; its return value will be the new A.

List access

The list access operators have two different versions, because reading to a list and writing to it are different things. The [] overload is for reading, and []= is for writing.

Output

Output operations are given special treatment. If no overload is defined for the current left-hand-side value (the recipient), and the recipient isn’t a client, then the recipient is broken down into a list of clients who would ordinarily receive the output as a broadcast. E.g., outputting to world will output to each individual client instead. Each client is then checked for an overload before they receive output. Calling ..() will of course fall back on default output behavior.

// Send an effect to a player or list of players
proc/DoEffect(target, effect/E)
    if(istype(target, /client)) 
        ... // do something here to show the effect
    else
        target << E   // send as output overload
 
client/proc/operator<<(out, target)
    if(target)
        if(istype(out, /effect)) DoEffect(src, out)
        else ..()

Text operator

There is an overload for converting a datum to text. By having operator"" return a text string, that text will automatically appear anywhere that you embed the datum in a string, use json_encode() on the datum, or many other situations.

It won’t work for atoms being sent directly to output (e.g., world << obj) or other skin controls because the client has special handling for these situations, and the client isn’t given any info about the overloaded text. Likewise, the overloaded text won’t appear for objects in an input() prompt list, which is also handled mainly on the client and therefore can’t call any procs on the server. Despite these limitations, the text overload offers greater flexibility.

See also

operators