ID:1842892
 
(See the best response by Turboskill.)
Code:
mob
var/Squad/squad

var/list/Squads = list()

Squad
var
name = "Squad"
Number = -1
Rank = "Unseated"
Level = 1
Exp = 0
MaxExp = 5000
Funds = 0
list/Members = list()

New(var/Num)
if(Num)
src.Number = Num
else return src.Number
..()

mob/proc
JoinSquad(var/Num)
var/Squad/S = new(Num)
if(!(src in S.Members))
S.Members += src.key
Squads += S
SaveSquads()
src.squad = S

Then the usage on the DblClick(), it is like this usr.JoinSquad(usr, 1)


Problem description:

- Hello, not gonna lie, I am trying to learn how datums work properly, I spent 1 day trying to make a Squad datum. I have read guides and took a few looks at other's people code and so on. I am not sure where I am failing but I don't intend to quit as a I want to improve my programming.
It seems like squad datum is always null no matter what. Am I missing initialization or something somewhere ?

Thanks!

My first thought is to question if having something returned within the New() initialiser (apart from the standard return behaviour set by ..()) is a good idea, especially when what you're returning is not of the same type as src. Maybe that should be changed?

The main thing i noted, eitherway, is how you're calling the JoinSquad proc. It doesn't currently match the parameter length you defined it to be expected to take, so going from how you've programmed things you would instead have:
usr.JoinSquad(1)

Moving on, here are some further suggestions.

1. You should change the way you're adding players to a squad's member list so that you're handling that within the datum and not outside. You can pass in both a reference to the client and a number as arguments upon squad creation, in a fashion like below:
Squad
New(var/client/C, var/num)
members["[C.key]"] = C.mob
number = num ? num: 0;
..()


2. I also suggest you change the way you're currently approaching squad joining. A sensible way would be to have one proc/verb for creation of a Squad, and another verb for the joining of an existing squad. You'd then have things work either on an invitation basis (My suggestion for this being something along the lines of:
var/list/Squads = list() //might be better to initialise this on world startup

Squad
var
...
New(var/client/C, var/num)
members["[C.key]"] = C.mob
number = num ? num: 0; // Is number meant to act like a squad's ID?
..()
proc/AddMember(var/client/C)
members["[C.key]"] = C.mob
//could return something here if you liked to show whether adding failed or succeeded.
//could even act like a useful notification to all squad members that a new member has joined.

mob
var
tmp/Squad/squad

mob/verb
CreateSquad(var/Client/C,var/num)
if(squad) return
squad = new(C,num)
Squads+= squad
SaveSquads()

InviteToSquad(var/mob/M in oview()) //you can do this however, just for example
if (!(squad && squad.members[1] == "[key]"))
return //if not within a squad or not it's leader (i,e, not the first member... it's rough :P)
if(M.squad == null)
switch(input(M,"You have been invited to join Squad [squad.number]","Join?") in list("yes","no"))
if("yes")
squad.AddMember(M.Client)
if("no")
src << "[M] declined your squad invitation."
)
Ooor you could make it so that a mob has the choice to join an existing squad without invitation. This would not be very different to the InviteToSquad verb, at least for this example. What would change is the focus of the list chosen from so something like
mob/verb/JoinSquad(var/s in Squads)
    if(squad) return
    ....and so on...

As an extra it might be worth you taking a look at a Similar Topic made some time back, and reading through to get a better general idea on how to go about creating a system similar to yours.
A lot of what i would want to suggest (and more) is pretty well covered in that thread, i think you'd find it useful.
A related entry should also be helpful towards understanding how to go about the saving and loading aspects for each squad.

Edit: made some changes to code based on points made on the saving aspect and naming convention etc. Also made necessary changes in general to the post, attempted to remove fluff.
A few issues:

1) I'd fix the naming convention. Make your var names start with lowercase; it's what regular built-in vars use so it's a lot clearer. Has no impact on your code otherwise though.

2) Seems like your squad joining setup is a little weird; there really ought to be two procs or verbs here: one to create a new squad, one to join one that already exists. You're trying to combine the two and it's not working. To do the latter, you'd want the player to choose from a list of existing squads.

3) The join verb creates a new squad every time, instead of looking one up. Creating new squads should be the job of a different proc or verb.

4) The mob/var you made for holding the mob's squad should be a /tmp; you'll want a different var to hold the squad's name, or whatever you use to look it up. Here's why: You don't want the squad itself going into the mob's savefile, because it will also pull in all the other mobs in that squad. Instead, you want to avoid saving the squad itself, just a way to look it up, which you can do by overriding Write(). And then in Read(), you would simply look up the squad, if any, and attach this player to it.

5) Squads should probably not only list their members directly for convenience, but they should also keep track of members by key name--so that members who are offline can be included.
Just to add, concerning the number var, if it is meant to act as a squad's id -which is how i kind used it at one point in my example sinppet- a quick method to allow it to be unique would be to set it's value based on the amount of squad's that are currently in existence, this removes the need for the num parameter to be present in the New() proc completely, unless you were planning to do some randomisation or something in order to pass a unique number in each time.
Squad
New(var/Client/C)
members["[C.key]"] = C.mob
number = Squads.len + 1
..()

That would mean affected verbs like CreateSquad in my example would need to also be changed to reflect this, by not taking a num parameter nor passing one through to new.

Ideally though, it might just be better to fix a squad with a name that can be used to identify them instead, or use both side by side even.
Here is my new code, and I am still probably doing something wrong as it is still null for some reason. I tried to do this myself and with those tips aswell

mob
var/tmp/Squad/squad

var/list/Squads = list()

Squad
var
name = "Squad"
number = -1
rank = "Unseated"
level = 1
exp = 0
maxExp = 5000
funds = 0
list/members = list()

New(var/num)
// src.members["[M.key]"] = M
src.number = num ? num : -1
..()

proc
AddMember(var/mob/M, var/num)
src.members["[M.key]"] = M
M.squad.number = num
src.members << "<font color=green><b>Squad Info:</font></b> <font color=red><b> [M] is now part of the [src.name] [src.number]!</font></b>"
M << "<font color=green><b>Squad Info:</font></b> <font color=red><b> You are now part of the [src.name] [src.number]!</font></b>"

proc/LoadSquads()
if(fexists("Squads/Squads.sav"))
var/savefile/F = new("Squads/Squads.sav")
F["Squads"] >> Squads
world << "[Squads.len]"
else
for(var/i = 1 to 13)
var/Squad/S
S = new(i)
Squads += S
world << "[S.name] [S.number] [i]"

proc/SaveSquads()
var/savefile/F = new("Squads/Squads.sav")
F["Squads"] << Squads

mob/proc
JoinSquad(var/mob/M, var/num)
if(!M.squad)
squad.number = num // squad is still null.
if(squad.number in Squads)
squad.AddMember(M, num)
Hey again, been a bit out of action, so apologies for being somewhat delayed with my response. I don't plan to be online much longer though so i'll try and make this snappy.

Btw, just to clarify, what is ending up as null again? and in case the issue still persists after what i'm able to currently suggest, maybe post what the outputs to world (from the world << ...) line are?


1. Where is your CreateSquad proc (or something like it for squad creation)? You should have one. It seems that you're currently relying on LoadSquads being called before JoinSquad to provide the initialisation right now, but make sure you do remember to specially write a proc that handles squad creation. (Insert: Question: do you plan to have a fixed amount of squads in the game?)

2. JoinSquad should look more like
mob/verb/JoinSquad(var/Squad/sqd in Squads)
if(!squad)
sqd.AddMember(src)
//squad.number = num //should be unnecessary courtesy of the first line
//the assignment already occurs in New() within the Squad datum

//honestly don't understand what your other lines below were here for

You could alternatively use the AddMember proc directly if you're trying to... huh, you know what? i'm not actually sure of what you're trying to do.
Could you explain to me how joining a squad should work, as you've envisioned it? because that is driving your approach to the coding of it, and i've realised that i'm not certain of what your approach it.

3. Make your AddMember proc such that it handles the squad sharing within it (apologies, something i didn't consider properly till now) i.e.
proc/AddMember(var/mob/M) // i removed the whole num thing again, honestly i don't get it
// until you explain it i'm assuming you're doing something wrong
src.members["[M.key]"] = M
M.squad = src
// all the '<<' messages stuff here


As far as i can tell, with that should you run some small tests to retrieve information concerning a mob's squad, you should be able to actually access real values when they belong to one, and not null.

Still though, please make your to clarify within your next post what purpose your num variable serves, and also how the JoinSquad procedure is supposed to function: what entity/action in game is meant to trigger it, and in what way?. That way i could suggest code that apart from working, also fits the approach you're going for.
In response to Turboskill
Turboskill wrote:
Hey again, been a bit out of action, so apologies for being somewhat delayed with my response. I don't plan to be online much longer though so i'll try and make this snappy.

Btw, just to clarify, what is ending up as null again? and in case the issue still persists after what i'm able to currently suggest, maybe post what the outputs to world (from the world << ...) line are?



Squad is null, I tho by initializing them on the LoadSquads proc would do the trick but it seems like not unless I am doing it wrong. The world output says 13 when there are already squads in the Squads list otherwise it will output Squad 1, Squad 2... until Squad 13

1. Where is your CreateSquad proc (or something like it for squad creation)? You should have one. It seems that you're currently relying on LoadSquads being called before JoinSquad to provide the initialisation right now, but make sure you do remember to specially write a proc that handles squad creation. (Insert: Question: do you plan to have a fixed amount of squads in the game?)

Yes I do plan to have limited amount of Squads already created when you load the world, that is why I came up with the LoadSquads.

2. JoinSquad should look more like
mob/verb/JoinSquad(var/Squad/sqd in Squads)
> if(!squad)
> sqd.AddMember(src)
> //squad.number = num //should be unnecessary courtesy of the first line
> //the assignment already occurs in New() within the Squad datum
>
> //honestly don't understand what your other lines below were here for

You could alternatively use the AddMember proc directly if you're trying to... huh, you know what? i'm not actually sure of what you're trying to do.
Could you explain to me how joining a squad should work, as you've envisioned it? because that is driving your approach to the coding of it, and i've realised that i'm not certain of what your approach it.

So when you load the game, the squads are already created so when the players goes to talk with the NPC, NPC will ask which squad the player wishes to join.
There I add the player to the specific squad number, that is why I rely on passing the number around.

3. Make your AddMember proc such that it handles the squad sharing within it (apologies, something i didn't consider properly till now) i.e.
proc/AddMember(var/mob/M) // i removed the whole num thing again, honestly i don't get it
> // until you explain it i'm assuming you're doing something wrong
> src.members["[M.key]"] = M
> M.squad = src
> // all the '<<' messages stuff here
>

As far as i can tell, with that should you run some small tests to retrieve information concerning a mob's squad, you should be able to actually access real values when they belong to one, and not null.

Still though, please make your to clarify within your next post what purpose your num variable serves, and also how the JoinSquad procedure is supposed to function: what entity/action in game is meant to trigger it, and in what way?. That way i could suggest code that apart from working, also fits the approach you're going for.

My num variables are supposed to make the player join the specific already created squad number. JoinSquad is supposed to pass the player and the squad number that he choose from the NPC he talked with.

Runtime error from my logs telling the squad is null
runtime error: Cannot execute null.AddMember().
proc name: JoinSquad (/mob/proc/JoinSquad)
source file: Squads.dm,48
usr: YasuShy (/mob/Player)
src: YasuShy (/mob/Player)
call stack:
YasuShy (/mob/Player): JoinSquad(YasuShy (/mob/Player), 1)
Squad Recruiter (/obj/NPC/Shopish/Squad_Recruiter): DblClick(YasuShy (/mob/Player))
YasuShy (/mob/Player): HighAttack()
Best response
I can't stay on long right now, just checking in, so question: did you attempt any of the suggestions in my last post? .. >.>
Anyway, thanks for answering the question about the num variable, but as i was thinking already, interacting with the number variable so much isn't necessary. Try to define the relevant procs in a way similar to what i've suggested, and all should be fine.

To explain my version of the JoinSquad proc, the usr activating the verb chooses a squad out from a list of squads to which they're added via AddMember. It's pretty straight forward and should be fine for what you need. I have already suggested this some days ago already though but.. maybe you didn't get it?
You can just rely on the value already set via the numbers = ... assignment you defined in New(), and refer to that whenever you want to find out the number of the squad the mob belongs to. i.e.:
LoadSqauds (on first time make 13 new squads => S = new(i) etc.)
JoinSquad (using my approach) displays a list of squads to choose from, add mob to that squad
using the code within, easy.
Then to find squad number, retrieve usr.squad.number.

The only thing i'd add is that if, for whatever reason, you find that the list generated by my proposed JoinSquad proc doesn't represent the squads in a nice way (or perhaps not distinguish them at all) then you could maybe do something like:
proc/NicerSquadlist()
var/list/Squadlist = new()
for(var/i = 1 to Squads.len)
Squadlist["Squad [i]"] = Squads[i]
return Squadlist

mob/proc/JoinSquadV2()
var/list/store = new() // don't know if initialising before passing return val is necessary.
store = NicerSquadlist()

// Btw, i think this should work, but i've not tested at this time; It's just something
// i quickly thought up, so possibly there'll be something unnecessary/overly 'complex'
// about this, but that's not my concern atm. I actually think this is unnecessary, just doing
// it in case you think you still need to directly reference the number.

var/squadChoice = input(src,"Choose a Squad","Choose") in store
var/Squad/chosenSquad = store["[squadChoice]"] // ...should check out? I might be wrong.
chosenSquad.AddMember(src) // No need for num parameter in AddMember proc.
world << "[src] just joined [squad.name] [squad.number]" // Should show it worked, hopefully.

// And just to complete the process
obj/Squad_Recruiter
DblClick()
usr.JoinSquadV2()


So, i'll leave things here. I've gotta go now, but i don't think you should be having any more issues regarding this (at least not the same ones) if you're able to follow the advice and code examples i've given you till this point. Hope this helps ...finally xD. Peace.

Edit: Before i forget, define your Squads list as an actual list of Squad items, as in:
var/list/Squads=> var/Squad/list/Squads
In response to Turboskill
Turboskill wrote:
I can't stay on long right now, just checking in, so question: did you attempt any of the suggestions in my last post? .. >.>

Yes but squad was still null.

Anyway, thanks for answering the question about the num variable, but as i was thinking already, interacting with the number variable so much isn'tnecessary. Try to define the relevant procs in a way similar to what i've suggested, and all should be fine.

To explain my version of the JoinSquad proc, the usr activating the verb chooses a squad out from a list of squads to which they're added via AddMember. It's pretty straight forward and should be fine for what you need. I have already suggested this some days ago already though but.. maybe you didn't get it?
You can just rely on the value already set via the numbers = ... assignment you defined in New(), and refer to that whenever you want to find out the number of the squad the mob belongs to. i.e.:
LoadSqauds (on first time make 13 new squads => S = new(i) etc.)JoinSquad (using my approach) displays a list of squads to choose from, add mob to that squadusing the code within, easy.Then to find squad number, retrieve usr.squad.number.

The only thing i'd add is that if, for whatever reason, you find that the list generated by my proposed JoinSquad proc doesn't represent the squads in a nice way (or perhaps not distinguish them at all) then you could maybe do something like:
proc/NicerSquadlist()
> var/list/Squadlist = new()
> for(var/i = 1 to Squads.len)
> Squadlist["Squad [i]"] = Squads[i]
> return Squadlist
>
> mob/proc/JoinSquadV2()
> var/list/store = new() // don't know if initialising before passing return val is necessary.
> store = NicerSquadlist()
>
> // Btw, i think this should work, but i've not tested at this time; It's just something
> // i quickly thought up, so possibly there'll be something unnecessary/overly 'complex'
> // about this, but that's not my concern atm. I actually think this is unnecessary, just doing
> // it in case you think you still need to directly reference the number.
>
> var/squadChoice = input(src,"Choose a Squad","Choose") in store
> var/Squad/chosenSquad = store["[squadChoice]"] // ...should check out? I might be wrong.
> chosenSquad.AddMember(src) // No need for num parameter in AddMember proc.
> world << "[src] just joined [squad.name] [squad.number]" // Should show it worked, hopefully.
>
> // And just to complete the process
> obj/Squad_Recruiter
> DblClick()
> usr.JoinSquadV2()
>

So, i'll leave things here. I've gotta go now, but i don't think you should be having any more issues regarding this (at least not the same ones) if you're able to follow the advice and code examples i've given you till this point. Hope this helps ...finally xD. Peace.

Edit: Before i forget, define your Squads list as an actual list of Squad items, as in:
var/list/Squads=> var/Squad/list/Squads


This time it was much smoother, and everything going cool. Mind to explain why the change of the Squads list to be part of the squad ?

        Login(var/mob/M)
if(M.key in members)
M.squad = src
else
M.squad = null

proc/LoadSquads()
if(fexists("Squads/Squads.sav"))
var/savefile/F = new("Squads/Squads.sav")
F["Squads"] >> Squads
world << "[Squads.len]"
else
for(var/i = 1 to 13)
var/Squad/S
S = new(i)
Squads += S
world << "[S.name] [S.number] [i]"

proc/SaveSquads()
var/savefile/F = new("Squads/Squads.sav")
F["Squads"] << Squads

proc/GetSquad(var/mob/M)
var/list/tempList = new()
tempList = GetSquadList()
var/Squad/chosenSquad = tempList["Squad [M.squadNumber]"]
return chosenSquad

proc/GetSquadList()
var/list/squadList = new()
for(var/i = 1 to Squads.len)
squadList["Squad [i]"] = Squads[i]
return squadList

mob/proc
JoinSquad()
var/list/tempList = new()
tempList = GetSquadList()
var/squadChoice = input(src, "Squad asf") in tempList
var/Squad/chosenSquad = tempList["[squadChoice]"]
chosenSquad.AddMember(src)

On loading the player.

src.squad = GetSquad(src)
if(src.squad)
src.squad.Login(src)


Now everything is working smoothly. I need a more in depth understanding of DM and Datums. Thank you very much Turboskill.
Ah so it's all good now? Phew! (haha) thats good to know. Also no worries man, glad to help, apologies if you'd indeed been adopting code changes based off the suggestions and stuff just wasn't working for you. Perhaps i was wrongly assuming something about the way certain pieces of code i suggested worked, but hey at least we got through in the end :P.

As for the change for the Squads list var, it's just something that crossed my mind as i was writing the last snippet. I remember thinking it might be necessary to make sure you changed the definition so that the code knew to expect that it was a list of 'objects' of the type Squad (so meaning that if you wrote something like Squads[<index>].<Squad Proc or Variable here> it'd know it was a valid statement? something like that). I thought that it might be necessary somewhere along the line with all the list copying going on in the last snippet, but i'm not sure now if it really mattered afterall.
If it doesn't affect anything then you could leave that unchanged if you wanted, but i'd say it's good practice to strongly define the contents of the list "just in case", so if you've already changed it, might as well keep it *shrug*.
Oh yes, I understand now. Thank you very much for the help. Also for all the information and explanation you gave.
Cheers!