04 Mar, 2009, boblinski wrote in the 21st comment:
Votes: 0
Okay, this is what I've got so far.

void do_history( CHAR_DATA *ch, char *argument )
{
char buf[MAX_STRING_LENGTH];

if (argument[0] == '\0')
{
sprintf(buf,"%s", ch->pcdata->history );
send_to_char(buf,ch);
}
else if (argument[0] == "edit")
{
sprintf(buf, "This?\n\r");
send_to_char(buf, ch);
string_append( ch, &ch->pcdata->history );
}
else
{
sprintf(buf, "Read \"Help History\" for help.\n\r");
send_to_char(buf, ch);
}

return;
}


however when I "make" I get this warning:
Quote
gcc -Wall -O -ggdb -DNOCRYPT -DQMFIXES -c -o obj/act_info.o act_info.c
act_info.c: In function `do_history':
act_info.c:140: warning: comparison between pointer and integer
rm -f rom

NOTE: line 140 refers to: else if (argument[0] == "edit")

How it should work:
"history" = shows the character their own history.
"history edit" = allows a character to edit their history.
"history x" (x being anything other then the word 'edit') = display ' Read "Help History" for help. '
04 Mar, 2009, Lobotomy wrote in the 22nd comment:
Votes: 0
Unlike some higher level languages that allow you to perform string comparisons in that way, you cannot actually (safely) perform that kind of comparison that you've attempted in C. The "edit" value you have in the code is what is called a string literal, and the compiler turns that value into a string and then replaces the location of "edit" in the code with a pointer to said string.

In order to check the contents of a string (safely) in C you need to run your string variable and your intended check ("edit", in this case) through a string checking function of some sort. In a number of codebases, this is done using a str_cmp() function. So, assuming a str_cmp function that returns false for strings that don't match, and true for strings that do match, your check would then look like:

else if( str_cmp( argument, "edit" ) )
04 Mar, 2009, Sharmair wrote in the 23rd comment:
Votes: 0
boblinski said:
however when I "make" I get this warning:
Quote
gcc -Wall -O -ggdb -DNOCRYPT -DQMFIXES -c -o obj/act_info.o act_info.c
act_info.c: In function `do_history':
act_info.c:140: warning: comparison between pointer and integer
rm -f rom

NOTE: line 140 refers to: else if (argument[0] == "edit")

How it should work:
"history" = shows the character their own history.
"history edit" = allows a character to edit their history.
"history x" (x being anything other then the word 'edit') = display ' Read "Help History" for help. '

What that comparison is using is the first char of the string stored in argument (of the type char, an
integral type) and the memory address of where the system stores the string literal "edit" (which
may or may not be the same as any other string literal of "edit" and for sure is not the same as
any user supplied buffer or string) which is a pointer.

What you probably want is to compare the contents of two strings. Most MUD codes have a function
called str_cmp(const char*, const char*) that will do that and return TRUE if the two strings are
different or FALSE if they are the same (case insensitive).

To rewrite your code to use str_cmp(), and also using sane indentation (you really seem to have
a problem with that, but that only affects readability), fixing improper line endings and removing
some unneeded code:
void do_history( CHAR_DATA *ch, char *argument ){
if (argument[0] == '\0'){
send_to_char(ch->pcdata->history, ch);
}else if (!str_cmp(argument, "edit")){
send_to_char("This?\r\n", ch);
string_append( ch, &ch->pcdata->history );
}else{
send_to_char("Read \"Help History\" for help.\r\n", ch);
}
return;
}
04 Mar, 2009, boblinski wrote in the 24th comment:
Votes: 0
Okay, thats great and working perfectly.

Next thing I'm wanting to add is to make it so an IMM can type:

history read <victim>

and read the said characters history.

this is what I've got so far:

else if (!str_cmp(argument, "read %s"
&& ch->level > HERO_LEVEL)
{
vch=%s;
if(vch->pcdata->concept != NULL)
{
printf_to_char(ch," {wConcept\n\r %s{x\n\r",wch->pcdata->concept);
}
}

I know it's wrong.. but is it close?
04 Mar, 2009, David Haley wrote in the 25th comment:
Votes: 0
What exactly are you expecting that to do? I ask because you really need to start learning the mechanics of this, and the best way to help us help you is to give us some insight into your thought process.
But to answer your question, no, unfortunately, it's not close at all. :thinking: It looks like you think the %s has some meaning, but (a) it means nothing more than literally "%s" in the string comparison, and (b) it means nothing at all outside of it.
04 Mar, 2009, boblinski wrote in the 26th comment:
Votes: 0
void do_concept( CHAR_DATA *ch, char *argument )
{
char vch;

if (argument[0] == '\0')
{
send_to_char("\t\t\t–==Your concept!==–\n\r", ch);
send_to_char(ch->pcdata->concept, ch);
}
else if (!str_cmp(argument, "edit"))
{
string_append( ch, &ch->pcdata->concept );
}
else if (!str_cmp(argument, "read %s"
&& ch->level > HERO_LEVEL)
{
vch=%s;
if(vch->pcdata->concept != NULL)
{
printf_to_char(ch," {wConcept\n\r %s{x\n\r",vch->pcdata->concept);
}
}
else
{
send_to_char("Read \"Help Concept\" for help.\n\r", ch);
}
return;
}


Thats the whole thing as it is right now. (Please note.. I've changed it from 'history' to 'concept')

So.. basically this piece I'm wanting to add needs to look at the following input:

"history read bob"

and if the person inputing it is an IMM ie. ch->level > HERO_LEVEL
then they are to be displayed the said characters history…
05 Mar, 2009, boblinski wrote in the 27th comment:
Votes: 0
I've had a little more of a play around…

This is what I've got to:
// NEW TEST concept
void do_concept( CHAR_DATA *ch, char *argument )
{
char vch;

if (argument[0] == '\0')
{
send_to_char("\t\t\t–==Your concept!==–\n\r", ch);
send_to_char(ch->pcdata->concept, ch);
}
else if (!str_cmp(argument, "edit"))
{
string_append( ch, &ch->pcdata->concept );
}
else if (!str_cmp(argument, "read")
&& IS_IMMORTAL (ch))
{
if(vch->pcdata->concept != NULL) /**LINE 147**/
{
vch=argument++;
printf_to_char(ch," {wConcept\n\r %s{x\n\r",vch->pcdata->concept);
}
else
{
send_to_char("You cannot find a concept for that one.\n\r", ch);
}
}
else
{
send_to_char("Read \"Help Concept\" for help.\n\r", ch);
}
return;
}


These are the errors:
Quote
gcc -Wall -O -ggdb -DNOCRYPT -DQMFIXES -c -o obj/act_info.o act_info.c
act_info.c: In function `do_concept':
act_info.c:147: error: invalid type argument of `->'
act_info.c:149: warning: assignment makes integer from pointer without a cast
act_info.c:150: error: invalid type argument of `->'
make: *** [obj/act_info.o] Error 1


Any help?
05 Mar, 2009, Igabod wrote in the 28th comment:
Votes: 0
did you define concept in struct pc_data within merc.h? that seems to be the problem with 147 and 150. I think fixing that will fix 149 but can't be sure.
05 Mar, 2009, Sharmair wrote in the 29th comment:
Votes: 0
The -> (pointer member selection) operator is used to select data members in a struct or union
pointer that you use on the left. In your case the variable vch. But you have vch defined as
the integral type char (on most systems a signed 8 bit integer), what you probably want is vch
defined as CHAR_DATA* (pointer to a char_data struct).

That will not fix the warning (though the warning will change slightly), and if you actually try
to run the code, it will most likely crash.
1) you will be using vch uninitialized, and therefor likely invalid.
2) when you assign the argument based pointer and try to dereference it, you will likely try
to access invalid memory.

It looks like you are having trouble figuring out how to parse a target character from the
input. Your code probably has a function called get_char_world(CHAR_DATA*, char*) that
can be used to look for a character, you pass it the character looking and the name of the
target, it will return either a pointer to the character if found, or NULL if not found.
As an example:
vch = get_char_world(ch, argument);

You could use that line if you changed your command syntax to not use the 'read' option,
but instead use just 'concept bob' to read someone's concept (you would look for the empty
argument for reading self, and the edit option, but then try to match a character if the
user is immortal). You should check to make sure vch is found before trying to use it, and
also that the target is not an NPC (you actually should also be checking for NPC in the self
option or you will crash if a mob ever uses this command, and there is a number of ways
this could happen).

If you really want to keep the 'read' part of the syntax, you will have to add in a char buffer
and learn how to use the one_argument function (well, there might be other ways of doing
it, but this would be the normal way with the code infrastructure you are working in).
05 Mar, 2009, Igabod wrote in the 30th comment:
Votes: 0
Again, Sharmair proves he's smarter than me… DAMN YOU!!
06 Mar, 2009, boblinski wrote in the 31st comment:
Votes: 0
So is their no way to have "history read <player>" ?
06 Mar, 2009, David Haley wrote in the 32nd comment:
Votes: 0
Yes, Sharmair described it in #29, here's the bit giving the high-level summary of what must be done:
Sharmair said:
If you really want to keep the 'read' part of the syntax, you will have to add in a char buffer
and learn how to use the one_argument function (well, there might be other ways of doing
it, but this would be the normal way with the code infrastructure you are working in).
06 Mar, 2009, boblinski wrote in the 33rd comment:
Votes: 0
Oh, sorry.

Can anyone provide me any explanation as to how the one_argument function works and how to use it?
06 Mar, 2009, Sharmair wrote in the 34th comment:
Votes: 0
boblinski said:
Oh, sorry.

Can anyone provide me any explanation as to how the one_argument function works and how to use it?

The one_argument(char*, char*) function is used to pull off arguments from a string. An argument
is a single unquoted word or if the first char is a quote, the string up to the next quote of the same
type (there are some versions that might not have the quote support and/or might handle spaces
before and after the argument differently, but this description should cover most merc derivatives).
The first parameter of the function call is the string you want to pull the first argument off of, and
the 2nd a buffer to hold the argument. The function will skip over leading spaces and then put the
first argument into the buffer, then it will skip any spaces after the argument and return a pointer
to the rest of the string after the argument. Note that the argument will not have leading or trailing
spaces unless it is a quoted argument and the argument will be converted to lower case (this behavior
might also not be in all versions). Common uses of the code are defining a buffer like:
char arg[MAX_INPUT_LENGTH];

And a use such as:
argument = one_argument(argument, arg);

In your code, you could do the one_argument call in a few places, you only NEED it for the 'read'
option, so you could do your self and edit checks like you do now, then do the one_argument and
use arg in the str_cmp check for 'read'. But you could also do the one_argument right at the start,
and use arg instead of argument in the self and edit checks. In either case, you would use the new
argument for the name of the target.

You could also use one_argument again in the 'read' option to pull off the name. The advantage of
this is that you would strip any errant spaces (or extra words) from the end of the string (like if
the user typed 'concept read bob ').
06 Mar, 2009, boblinski wrote in the 35th comment:
Votes: 0
That sounds perfect.. I'm not sure my skill level is high enough yet.. eh?
06 Mar, 2009, Igabod wrote in the 36th comment:
Votes: 0
It's really a lot simpler than it sounds. Just look up a function that uses one_argument in your code, look at it and see what it does. Try to pick a function that you are already able to at least mostly understand. Using this method is how I learned to code without taking any classes or reading any books. I just look at existing examples of the code that do something similar to what I need. Once you've figured out how to do this method, you'll find yourself asking much fewer questions of a simple nature.

Don't get discouraged yet, you've already put a bunch of effort into this and there's not much left till you've got it. Sometimes what sharmair says can be a little confusing, but just reread it a couple times and it'll make more sense to you. Especially if you're looking at a previously existing sample.

[edit to add]I should clarify that last bit. He's only confusing because he gives you multiple options but all of them should work. You just gotta figure out which option you wanna use.
10 Mar, 2009, boblinski wrote in the 37th comment:
Votes: 0
Okay, back to this thread now, I've decided to go for the simple option which is…

concept bob = read bob's concept (if you're an Immortal)

This is what I've got:
void do_concept( CHAR_DATA *ch, char *argument )
{
char vch;

if (IS_NPC (ch))
{
return;
}
else if (argument[0] == '\0')
{
send_to_char("Your concept is:\n\r", ch);
send_to_char(ch->pcdata->concept, ch);
}
else if (!str_cmp(argument, "edit"))
{
string_append( ch, &ch->pcdata->concept );
}
else if (IS_IMMORTAL (ch))
{
vch = get_char_world(ch, argument);

/*line 188*/ if(vch->pcdata->concept != NULL)
{
printf_to_char(ch,"%s's concept is:\n\r%s\n\r", vch->name, vch->pcdata->concept);

}
else
{
send_to_char("They aren't here, or you're typing wrong. \"Help Concept\".\n\r", ch);

}
}
else
{
send_to_char("Read \"Help Concept\" for help.\n\r", ch);
}
return;
}


And these are my errors:
Quote
gcc -Wall -O -ggdb -DNOCRYPT -DQMFIXES -c -o obj/act_info.o act_info.c
act_info.c: In function `do_concept':
act_info.c:186: warning: assignment makes integer from pointer without a cast
act_info.c:188: error: invalid type argument of `->'
act_info.c:190: error: invalid type argument of `->'
act_info.c:190: error: invalid type argument of `->'
make: *** [obj/act_info.o] Error 1
10 Mar, 2009, Hades_Kane wrote in the 38th comment:
Votes: 0
if((vch = get_char_world( ch, argument ) ) == NULL)
{
send_to_char("You can't find that person.\r\n",ch);
return;
}


Change line 186 to that, and that might fix the other errors coming after it as well. You'll find a lot of time in code, you fix the first error you come to, the rest will fall in line.

Also, you'll want to check to make sure that vch isn't an NPC, else you could be a PC using that on an NPC and crash the MUD since NPCs don't have the pcdata structure.
10 Mar, 2009, boblinski wrote in the 39th comment:
Votes: 0
Alright, this is what I've got now. I had to change the "char vch;" to "CHAR_DATA *vch".

I also changed the piece Hades_Kane gave me, and added the (IS_NPC (vch)) check in there too.

void do_concept( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *vch;

if (IS_NPC (ch))
{
return;
}
else if (argument[0] == '\0')
{
send_to_char("Your concept is:\n\r", ch);
send_to_char(ch->pcdata->concept, ch);
}
else if (!str_cmp(argument, "edit"))
{
string_append( ch, &ch->pcdata->concept );
}
else if (IS_IMMORTAL (ch))
{
if((vch = get_char_world( ch, argument ) ) == NULL
|| IS_NPC (vch))
{
send_to_char("You can't find that person.\r\n",ch);
return;
}
if(vch->pcdata->concept != NULL)
{
printf_to_char(ch,"%s's concept is:\n\r%s\n\r", vch->name, vch->pcdata->concept);

}
else
{
send_to_char("They have no concept.\n\r", ch);

}
}
else
{
send_to_char("Read \"Help Concept\" for help.\n\r", ch);
}
return;
}


My problem now is:

If I log two mortals onto my mud with similar names ie Sam and Samantha.. when I try to target Sam which my "concept sam" command, it reads Samantha's concept.. even if I put "sam" in quotes.

How do I fix this?

EDIT: I see this problem happening everywhere in the code, including 'whois' and 'stat'
10 Mar, 2009, Zeno wrote in the 40th comment:
Votes: 0
That's how get_char_world works. It makes it easier so you don't have to type out the full name and is intended that way.
20.0/46