ID:265909
 
I'm sure this question has been asked sometime in the past, but I didn't find anything in the forum search.

How can you guys tell when you should use a datum and not conventional methods? For instance, a "guild system". Why is a datum so much better for this scenario?
Spunky_Girl wrote:
I'm sure this question has been asked sometime in the past, but I didn't find anything in the forum search.

How can you guys tell when you should use a datum and not conventional methods? For instance, a "guild system". Why is a datum so much better for this scenario?

Datums are handy because they allow you to group information in a way that is much friendlier to access and modify. For example:

var/list/guilds = list("Alathonia"="Alathon", "Spunky's Crib"="Spunky_Girl")

mob/var/guild=""


To set a persons guild, you'd probably refer to the entry in the guilds list. That entry's associative value is equal to the guild leader, in case you'd need to check that.

But what about when you add multiple ranks to a guild, multiple guild leaders? Guilds that have their own unique brackets around chat? Guild-wide loot settings? A guild bank vault?

You're going to be amassing a rather large set of lists and variables for that, at some point. Or you could group all of it under a /guild datum and have nice and centralized access to everything. It also means that when you want guild-specific procs such as talking in the guild channel, you can find it under the /guild datum instead of through a global procedure:

guild
var
name=""
leader_key=""
chat_bracket_l="\["
chat_bracket_r="\]"
list/members_online=list()

proc/MemberOffline(mob/M)
if(M in members_online) members_online -= M
if(M.guild==src) M.guild=name

proc/MemberOnline(mob/M)
if(M.guild != name) return
M.guild=src
members_online += M

proc/Talk(mob/M, msg)
if(!(M in members_online)) return
for(var/mob/C in members_online)
C << "[chat_bracket_l][M][chat_bracket_r]: [msg]"
A datum is best thought of as an intangible object, such as a bond between tangible objects. For example, an event is a thing, but it is inanimate, therefore a datum would be well suited for it. In a traditional event system, you would have to use many procedures and variables, all contained within a mob (EDIT: or globally), to handle things. With datums, you could have one variable (or a list) for storing the current event datum(s), and one mob variable. This keeps things simple, as you can easily check whether they are engaged in an event, what that event is, and when the event ends or when the user logs out, its easy to clean up.

The description towards the beginning of the paragraph is a little vague, and not always applicable. However, for all but the smallest of things, datums keep your systems flexible; there are very few things in my more recent projects not handled with datums.
Datums are good in all situations, and in all places. Datums are the solution to every programming problem you're having. If you're not using Datums, then you're not programming. Datums are programming. [Note to trolls about to respond, this is from the Object Oriented point of view, which DM obviously subscribes to.]

Unfortunately, Datums were given the name "datum" instead of "object". This is unfortunate, because most people don't know what a "datum" is (many even confusing it for "DanTom"). The Datum of DM is the same thing as the objects of other languages; it's the ancestor of all other types, it's the generic object that you use when you want to make a new type of object. If you ever go to "hire" a programmer on BYOND and they say that they kinda know what datums are, and they've used one before, run away, this is someone who isn't a programmer.

That being said, never use datums. The /datum type does nothing. It isn't anything. A lot of people get confused trying to find out why everyone thinks /datums are so hot. They're not, they do nothing. Let me give you an example of datums that are useful:
battle
var
list/combatants
proc
add_combatant(var/unit/who)
if(who in combatants)
return
combatants.Add(who)
show_battle_screen(who)
pass_turn()
var/unit/next_unit = magic()
// do magic to calculate who's turn is next
next_unit.take_turn()
show_battle_screen(var/unit/who)
magic()
// transport the client eye, and all that nifty stuff.
unit
var
some_path/player
// some_path could be a client, a /player object or
// something else that let's the player control the unit
behavior = "seek"
turn = FALSE
proc
take_turn()
turn = TRUE
if(player)
return
// wait for the player to initiate the action
call(src, behavior)()
seek()
magic() // seek out and attack hostile units
turn = FALSE
hide()
magic() // hide from view, attack only from shadow
turn = FALSE


Here we see three object that have started to be defined. The battle is an object that represents an instance of combat. Combatant units are add to it, and it handles which unit gets to take the next turn (this example uses turn based combat). The unit object represents one combatant, either a player or a "monster", or anything that can move around and fight. The unit has a take_turn() proc which is called to let the unit know that it can act now. The third object we're dealing with is the player, an object defined elsewhere. This is an object through which the person playing your game interfaces with units (they may be commanding an entire army of units, instead of being confined to one "mob"). The interactions between these objects are unique procedures (procs) that they call to send messages to each other.

"Programming" is the act of defining these objects and the procedures they'll use to interact. Once that has been thoroughly defined, you can then pass off your document so the code writer can write all the code which will run your program. The program is separate from the code; the program is where all the design takes place. The code is how the program is implemented.
Spunky_Girl wrote:
How can you guys tell when you should use a datum and not conventional methods? For instance, a "guild system". Why is a datum so much better for this scenario?

A datum is ideal for a guild system because the guild is, essentially, an independent entity. It isn't merely a property of a mob, such that a simple list would suffice; it is its own thing, and mobs and such will interact with it as if it's separate from them.

Compare this to a list of equipment in use by a give mob, and you'll see the difference. A mob's equipment list is meaningless without the mob itself.

Datums are good for anything that might need to be treated as a separate entity, or any time you want to collect several vars in one place. For example:

  • A party/guild
  • The host's preferences for a game
  • A game or round
  • A complex HUD or HUD element
  • A quest
  • An NPC's AI manager
  • A piece of data that may be slightly more complex/exotic than a number or text

    In SotS II, I'm currently using datums for all of the following:
  • Game rounds
  • Maps (an extension of SwapMaps)
  • Cached icon operations
  • The DmiFontsPlus library
  • Certain HUD objects
  • Objects that make it easier to send output to the skin as needed
  • AI managers
  • AI "memory objects" (things an AI remembers seeing)
  • AI goals
  • Pathfinding managers
  • Host preferences
  • Tracking medal-related goals and accomplishments for each player

    In each case the item represents either an entity like a game round, map, HUD element, or goal, or it's a way of keeping a bunch of info in one place. An AI memory object for instance records what was seen (by type path), details the NPC remembers about it, and where it was when they saw it; this allows the object to be deleted or picked up when they're out of sight and lets them be genuinely surprised not to see it if they go looking for it again. The medal-related goals are just a bunch of vars in one place so I can have one list to keep track of them instead of 15 lists, which makes the code a lot easier to work with.

    Lummox JR
In response to Alathon
Right now I'm using your example.

I created these two little verbs; one for the datum and the other to create a guild.
mob/verb/Create_Guild()
var/a = input("","") as null|text
if(!a) return
var/Guild/g = new
guild = a
g.name = a
g.leader_key = src.key
g.MemberOnline(src)
verbs += typesof(/Guild/verb) -/Guild

Guild
verb/Online_Guildies()
for(var/mob/M in members_online)
usr<<M


It gives me this runtime error :\

runtime error: undefined variable /mob/Player/var/members_online

Which leads me to believe that it's assuming members_online var is apart of the usr vars, in which case I did...
Guild
verb/Online_Guildies()
var/Guild/g
for(var/mob/M in g.members_online)
usr<<M


...and it gave me the below runtime error.

runtime error: Cannot read null.members_online

How would I fix that? I'm kind of new to datums :\ I realize this post should belong in Developer How-To, but it's just easier to post it here. If you really want me to, I can make a post in Developer How-To.
In response to Spunky_Girl
You never gave g a value.
In response to Jeff8500
Sorry I didn't specify in the post, but I knew that it needed a value, I was trying to ask what value to give it. :\
In response to Spunky_Girl
src
In response to Jeff8500
Guild
verb
Online_Guildmates()
var/Guild/g = src
for(var/mob/M in g.members_online)
usr<<M

runtime error: undefined variable /mob/Player/var/members_online
In response to Spunky_Girl
Either this is a bug, or, more likely, verbs were never meant to be contained within datums. Create a subtype of /mob for storing guild verbs, etc.
In response to Jeff8500
Jeff8500 wrote:
Either this is a bug, or, more likely, verbs were never meant to be contained within datums. Create a subtype of /mob for storing guild verbs, etc.

Verbs work fine if the datum isn't intended for purposes like this. Take an administration system for example, you're not going to go var/admin/A = new. You're just going to go src.verbs += typesof(/admin/verb).

As for guilds. Using Alathon's example as a forefront:
guild
var
name = null
bracket_l = null
bracket_r = null
owner = null
list
members = new/list

New(n, bl, br, mob/M)
. = ..()
src.name = n
src.bracket_l = bl
src.bracket_r = br
src.owner = M.key
AddMember(M)


proc
AddMember(mob/M)
if(!M.myGuild)
src.members += M.key
M.verbs += typesof(/guild/verb)

RemoveMember(mob/M)
if(M.key in src.members)
src.members -= M.key
M.verbs -= typesof(/guild/verb)

verb
Check()
//usr is safe in verbs kiddies.
for(var/A in usr.myGuild.members)
world << A

var
list
guilds = null


mob
var/guild/myGuild = null
Login()
..()

verb
checkGuilds()
if(guilds)
for(var/guild/G in guilds)
src << G.name
else src << "No guilds have been created"

addGuilds()
var/name = input("Please name your guild", "Name") as null|text
if(name) guild_foo(new /guild(name, "{", "}", src), src)

proc
guild_foo(guild, mob/M)
M.myGuild = guild
if(!guilds)
guilds = new/list
guilds += guild


That'll report your guild participants and what guilds exist. I'll leave it to the rest of you to make the necessary adjustments to add users, remove users, make the leader actually count and do everything else you need.
In response to Jeff8500
Jeff8500 wrote:
Either this is a bug, or, more likely, verbs were never meant to be contained within datums.

Actually, it's because she's going about this the wrong way. Her code would work were it written properly, but it's not.

Create a subtype of /mob for storing guild verbs, etc.

This is still the easier and more preferable option.
In response to Spunky_Girl
I want you to think through this process with me, it'll help you understand what's going wrong.

You have a /Guild datum with variables like "members_online" and such. /Guild itself, as a type, has no values for any of these variables (for the same reason that /mob doesn't have a "name," but every player has a mob object with a "name"); the values are held in individual Guild objects (the stuff you create with new). So you create a verb for the /Guild type, and then add this verb to your player's verbs. Notice, you never created any Guild objects, you just added its verbs to the players verbs. This means that you still don't have any variables to access. Verbs should not be used in this way.

Instead, you should give your new types some procs to do the things that operate on its variables, and in your verbs call the procs that belong to an object of that type.

Here's a quick sample I made; it strays from your specific /Guild datum, but it is entirely functional as a standalone project, and it should be somewhat useful as an example of using procs belonging to a datum. It looks like a lot, but there's little actual code.

// I don't know the specifics of your implementation and don't care to check.
// This example should work on its own, and you can apply the same concepts to your own project.

guild
var/list/m_list

proc
Add(x)
if(!m_list)
m_list = list(x)
else
m_list += x

Remove(x)
if(m_list)
m_list -= x
if(!m_list.len) // clean up the list if we don't need it
m_list = null

Report(target) // "target" is who you're outputting to, can be a list for this example
if(istype(target, /list))
for(var/i in target)
Report(i)
else
if(m_list)
target << "List contains:"
for(var/i in m_list)
target << "- [i]"
else
target << "List is empty."

mob
var/guild/m_guild = new

verb
Add()
var/i = input(src) as null|text
if(i)
m_guild.Add(i)

Remove()
var/i = input(src) as null|anything in m_guild.m_list
if(i)
m_guild.Remove(i)

Show()
m_guild.Report(src)
In response to Popisfizzy
Popisfizzy wrote:
Actually, it's because she's going about this the wrong way. Her code would work were it written properly, but it's not.

It's not nice to say things like this without offering any help. Shame on you. :(
In response to Kuraudo
She was in Chatters earlier and her code, and all the problems with it, were discussed in full, as well as alternatives to base it off of. I didn't feel the need to reiterate it here.