ID:826712
 
(See the best response by DarkCampainger.)
Code:
mob
Login()
var/NL = input("New or Load?") in list("New", "Load")
if(NL == "New")
Race = input("Select a race") in list("Human", "Beast")
Size = input("Select a size") in list("Large", "Medium", "Small")
if(Race == "Human")
if(Size == "Small")
src.BPMod = Human/Small/BPMod

Name = input("Give yourself a name") as text
src.loc = locate(5,5,1)
world << "[src.key] has created a character."

else if(src.LoadProc())
world << "[src.key] has returned."


Problem description:

Hey~
I'm attempting to uhm.. pull a variable from another object, but I'm having issues. Just looking at this, and it just seems wrong (the Human/Small/BPMod bit)

I'm wondering, what's the easiest way to assign a an array of variable values to a mob's same variables without reiterating a ton of if statements? Also, what's the proper way of assigning said values.

I just woke up, so bear with me.
But why not use the && operator?

Also, example with your code:
if(Race == "Human" && Size == "Small")
src.BPMod = Human/Small/BPMod
As Raimo said the && operator here's and example of how you would us it in this case:
if(Race=="Human"&&Size=="Small")
src.BPMod = Human/Small/BPMod
In response to Raimo
I assume they're adding more if() statements inside of that, so the && operation is useless.

They're likely trying to figure out how to do the first one before they do all of them wrong.
Well, there are three body sizes for each race. Small, Medium, and Large. I figured to save time by only checking the race first, and then worrying about the sizes afterwards.

obj
Human
Small
var
BPMod = 1
PhysMod = 1
ForMod = 1.75
OffMod = 3
DefMod = 3


That's how I'm declaring the small size for humans, and assigning it the way I tried is giving me errors even.

Is there no simpler way to do it than by stacking conditionals so many times?

I was thinking maybe a switch statement could work.
You can use a text to path on something like "/bpmod/[race]/[build]" to create a new instance of this path.
Best response
You can create paths dynamically using text2path(). However, you cannot access a type's initial variable values without an actual instance of it.

First off, you'll want a base-class that defines the variables centrally. We can use a datum because we don't need any of the extra variables from obj:
ClassData
var
BPMod = 1
PhysMod = 1
ForMod = 1
OffMod = 1
DefMod = 1

Human
Small
BPMod = 1
PhysMod = 1
ForMod = 1.75
OffMod = 3
DefMod = 3
Medium
BPMod = 1
PhysMod = 1
ForMod = 1.75
OffMod = 3
DefMod = 3
Large
BPMod = 1
PhysMod = 1
ForMod = 1.75
OffMod = 3
DefMod = 3
Beast
Small
// ect


So, now you have to get the path:
var/classPath = text2path("/ClassData/[Race]/[Size]") // Create type path based on race and size


Then you have to create the object:
var/ClassData/data = new classPath() // Create an instance of the type path


Next we just need to copy over the settings. We can automate this using the vars list:
for(var/varName in data.vars) // For each variable defined in ClassData
src.vars[varName] = data.vars[varName] // Copy over the value to src
I can sort of see where you're going with that, but my being new to DM is causing me to fail a little bit here. Here's what I did with it;

ClassData
var
BPMod = 1
PhysMod = 1
ForMod = 1
OffMod = 1
DefMod = 1
Human
Small
BPMod = 1
PhysMod = 1
ForMod = 1.75
OffMod = 3
DefMod = 3

Medium
BPMod = 1
PhysMod = 1.5
ForMod = 1.5
OffMod = 2
DefMod = 2

Large
BPMod = 1
PhysMod = 2.4
ForMod = 1.25
OffMod = 1.5
DefMod = 1

Beast //cut some out to save space in the post


var/classPath = text2path("/ClassData/[Race]/[Size]") // Create type path based on race and size
var/ClassData/data = new classPath() // Create an instance of the type path


Assuming that's where those two variables belonged, I placed them in the Races.dmi file. Now, I'm getting two errors telling me Race and Size aren't defined variables, I assume I was supposed to change them maybe.

Here's what I did to the CharCreate.dmi file;

mob
Login()
var/NL = input("New or Load?") in list("New", "Load")
if(NL == "New")
Race = input("Select a race") in list("Human", "Beast")
Size = input("Select a size") in list("Large", "Medium", "Small")
for(var/varName in data.vars) // For each variable defined in class data
src.vars[varName] = data.vars[varName] // Copy over the value to src

Name = input("Give yourself a name") as text
src.loc = locate(5,5,1)
world << "[src.key] has created a character."

else if(src.LoadProc())
world << "[src.key] has returned."


Now, I see that the goal is to loop through and assign each variable the property it needs. I've never seen data.vars, though, so I'm unsure how this is working out.

Looking at src.vars brings me to think that you can access all the sources variables like it's an array/list. The uhm.. src.vars[varsName] is a bit confusing though, I'll assume it works through that data.vars bit I don't understand in the loop.

Or maybe I was supposed to edit it. Either way, thanks for the help, every bit helps in the learning process.
So this was supposed to be in your CreateChar:

var/classPath = text2path("/ClassData/[Race]/[Size]") // Create type path based on race and size
var/ClassData/data = new classPath() // Create an instance of the type path


Data refers to this new one we created, and vars are the vars.

Before the loop, but after the Race/Size settings.
Your assumption is correct, its just a list of vars, and its saying for each of the vars defined, set the one with the same name to the current mob.
Ah, sorry, I guess I didn't really give you much context for where to place those. You want the classPath and data definitions within your Login() process. The 'Race' and 'Size' that it's complaining about being undefined, are the ones you're asking for in your Login() process.

So after using those input()s to get the desired race/size from the user, you're using them to dynamically create a matching type path to the object that contains the data. Then you create an instance of that type path so you can access its variables. Finally, you copy over the variables from the dynamically created data object to the player's mob.

The 'vars' variable is defined for every datum (the most basic type that all other types inherit from). It contains an associative list of the instance's variable names and their respective values. You can read up on associative lists in the reference, but basically each item in the list has an "associated" value of its own. To look at it another way, instead of accessing the list by a numeric index, you can use any [non-numeric] data type as indexes, such as text strings or even object instances. In the 'vars' variable's case, you use the variable's name as the index to get that variable's value. In the example I gave you, I use a for() loop to loop through all of the variable names in the 'vars' list, and then set the mob's value for that variable to the data-object's value for that variable.

Also, you mean "dm" files, named after the language "DM" (which I believe is named after the IDE software, Dream Maker). "dmi" files are "Dream Maker Icon" files.
Aha! I understand now, I believe. This is nice.

mob
Login()
var/NL = input("New or Load?") in list("New", "Load")
if(NL == "New")
Race = input("Select a race") in list("Human", "Beast")
Size = input("Select a size") in list("Large", "Medium", "Small")
var/classPath = text2path("/ClassData/[Race]/[Size]") // Create type path based on race and size
var/ClassData/data = new classPath() // Create an instance of the type path
for(var/varName in data.vars) // For each variable defined in class data
src.vars[varName] = data.vars[varName] // Copy over the value to src

Name = input("Give yourself a name") as text
src.loc = locate(5,5,1)
world << "[src.key] has created a character."

else if(src.LoadProc())
world << "[src.key] has returned."


Theres the full code.

var/classPath = text2path("/ClassData/[Race]/[Size]") // Create type path based on race and size


This creates the classPath var, which holds a stored path, using the Race and Size variables as placeholders for the changing data.

var/ClassData/data = new classPath() // Create an instance of the type path


this data variable is held in the ClassData object, which stores an instance of the dynamic path we're using. (Although, I do need to read up on using 'new', and the () at the end of classPath is a bit confusing to me, as it looks like a function/verb/proc.. and classPath is a variable.)

for(var/varName in data.vars) // For each variable defined in class data


by creating the varName variable, we can use it as an index for our data.vars list. Judging by the comment, it assigns each var in data.vars an index as varName?

src.vars[varName] = data.vars[varName] // Copy over the value to src


using the same index, we.. match variables with the same variable name in src.vars, as those in data.vars. This assigns the matching variables the same data. Well, it assigns data.vars' value to src.vars'.


I totally forgot it's DM, not DMI. I'm even an iconner, I should know better haha.
Just gonna jump in here.
new /type (loc) is a shortcut for datum.New(). That is all it calls. It has it uses. any arguments you put into () will be passed to New(). Since that only has one predefined argument, loc, it is pointless to you right now. But you could overwrite datum.New() and give it extra arguments to accomplish other things at object creation and initialization.

with the new shortcut there also comes the rarely used modified types format. which you can read about in the newlist reference entry.
Ah, I think I understand. So for example...

mob/verb
Grass()
set category = "Build"
var/bgrass = text2path("/turf/Grass")
new bgrass(src.loc)


Is a proper use of new? (it works in game)


Also, I'm getting a major fail with this code atm, here's what I'm getting when I try to make a new character.

runtime error: Cannot write to atom.type.
proc name: Login (/mob/Login)
source file: CharCreate.dm,11
usr: Kitsueki (/mob/Player)
src: Kitsueki (/mob/Player)
call stack:
Kitsueki (/mob/Player): Login()


The code is

mob
Login()
var/NL = input("New or Load?") in list("New", "Load")
if(NL == "New")
Race = input("Select a race") in list("Human", "Beast")
Size = input("Select a size") in list("Large", "Medium", "Small")

var/classPath = text2path("/ClassData/[Race]/[Size]") // Create type path based on race and size
var/ClassData/data = new classPath() // Create an instance of the type path
for(var/varName in data.vars) // For each variable defined in class data
src.vars[varName] = data.vars[varName] // Copy over the value to src


Name = input("Give yourself a name") as text
src.loc = locate(5,5,1)
world << "[src.key] has created a character."

else if(src.LoadProc())
world << "[src.key] has returned."


Line 11 is...

src.vars[varName] = data.vars[varName] // Copy over the value to src


Any help here?
You cannot overwrite some specific variables, so you should check like..
I think the issaved() proc would fix that.
    for(var/varName in data.vars) // For each variable defined in class data
if(issaved(data.vars[varName]))
src.vars[varName] = data.vars[varName] // Copy over the value to src


All it does is check if a variable global, constant, or temporary. A few of a mob's variables are like that, and cannot be changed during run-time. (or compile time)
Well, I don't get any errors after using that, I'll assume it worked properly. (Although, I'm trying to figure out how to look at my own variables in game, which is turning out to be a little challenging.) I was thinking of using something like...

mob/verb/ViewVars(set src in world)
for(var/X in src.vars)
src << "[src.vars[X]]"


That doesn't seem quite right though, and I'm working on something else atm. Can someone break this down for me quickly? Wanting to use it to see the src's vars, to tell if this is working correctly.
Since you are the src, the person as to whom the verb is focused on, you don't need to have any arguments defined in the verb.
So, remove that, and make the output a bit clearer. X would be the name of the variable; while vars[X] is the value. And, you'd get:
mob/verb/ViewVars()
for(var/X in src.vars)
src << "[X] = [src.vars[X]]"


But, if you wanted to use the verb on someone other /mob in the world, the argument would have to be something like mob/M in world, and you'd have to replace all instances of "src" with "M" or whatever you chose to put there.
Haha! It works! Thanks man, lol. I see what you mean now that I look at it, since the verb is attached to the mob, the src is the verb's parent (the mob)

Although, I'm a little unclear as to how X is the variable itself. I'm trying to understand the logic of that..

for(var/X in src.vars)


X is a variable.. that's being placed in the list src.vars..
So it's like.. -scratches head-
I keep wanting to think X is just a counter used for an index. Yet when you put it in the list it's acting as a representative for the variable itself.
X isn't the variable. It's the name of the variable.
Think about it like this;
atoms have multiple pre-defined variables, and ones you can define yourself.
So, internally, it might look something like
atom
var
x = 1
y = 1
z = 1
density = 0
opacity = 0
and so on.

Then, the atom also has a variable list called vars.

The list might look like this:
 vars = list("x" = 1, "y" = 1, "z" = 1, "density" = 0, "opacity" = 0)

That's an associative list.
So, when you loop through the list as seen in for(var/X in src.vars)
You're picking up the first portion. If you do that loop, X at different iterations will be equal to "x", "y", "z", "density", and "opacity". Just those text values, nothing else, which are the names of the variables.

Then, you use the bracket operators to find the associated value.
Since "x" is associated with 1, in the vars list;
vars["x"] will output the numerical value 1.

So, in that loop, when X is at the "density" iteration; X will equal "density", and vars[X] is the same as doing vars["density"], but the actual value when the list is accessed, the associated value will be 0, because that's what the variable density equals.
Ahhh, Ok, that put things into perspective for me. Thanks for the help!