07 Jan, 2009, Kayle wrote in the 1st comment:
Votes: 0
Ok, so I'm writing up some channel histories for MW. And I've come across one helluva problem. I have a function, updateHistory, that takes their input and caches it so that people can then view the history later to see what was said while they were doing other things. Now. That's all fine a good. it works. Sortof. Here's the data:
struct channel_data
{
int channel; //Actual channel
const char *prefix; // These
const char *act_text; //Three
const char *buf_text; //entries are how the channel looks.
int color; //this is the color of the channel
int type; //this is the type of channel (global, area, etc.)
const char *histname[MAX_CHANHISTORY]; //This is the name of the person speaking for eat entry
const char *argument[MAX_CHANHISTORY]; //This is what was said
time_t timestamp[MAX_CHANHISTORY]; //This is the current_time from when they said it.
int visibility[MAX_CHANHISTORY]; //This is whether or not they were invis/hidden/wizinvis
int invislevel[MAX_CHANHISTORY]; //This is the level of them being wizinvis
int language[MAX_CHANHISTORY]; //This is the language they were speaking
bool emote[MAX_CHANHISTORY]; //Whether it was an emote or not
};


Well, now you know what's in them. This is where it's showing it to them.

void showChanHistory( CharData * ch, const char *verb, int channel )
{
char buf[MSL], buf2[MSL], buf3[MSL], buf4[MSL];
bool speaks = false;
int x;

if( channel < 0 )
{
bug( "%s: unknown bitvector value '%d' passed", __FUNCTION__, channel );
return;
}

x = chan_data[channel].channel;

if( x == CHANNEL_GUILD || x == CHANNEL_CLAN || x == CHANNEL_ORDER )
{
showClanHist( ch, verb, channel );
return;
}
else if( x == CHANNEL_RACETALK )
{
showRaceHist( ch, verb, channel );
return;
}

if( !chan_data[channel].argument[0] || chan_data[channel].argument[0][0] == '\0' )
{
sprintf( buf, "&RNobody's used the '%s' channel!\r\n", verb );
ch->print( buf );
return;
}

else
{
sprintf( buf, "&WChannel history for '%s' channel:\r\n", verb );
ch->print( buf );
ch->setColor( chan_data[channel].color );
for( x = 0; x < MAX_CHANHISTORY; x++ )
{
if( !chan_data[channel].argument[x] || chan_data[channel].argument[x][0] == '\0' )
{
if( x == 0 )
bug( "%s: channel history initialized, but has no data!", __FUNCTION__ );
return;
}

if( !str_cmp( ch->getName( ), chan_data[channel].histname[x] ) )
mudstrlcpy( buf, ch->getName( ), MSL );
else if( !ch->isNPC( )
&& !IS_AFFECTED( ch, AFF_TRUESIGHT )
&& !xIS_SET( ch->m_Act, PLR_HOLYLIGHT )
&& ( ( IS_SET( chan_data[channel].visibility[x], VIS_HIDE )
&& !IS_AFFECTED( ch, AFF_DETECT_HIDDEN ) )
|| ( IS_SET( chan_data[channel].visibility[x], VIS_INVIS )
&& !IS_AFFECTED( ch, AFF_DETECT_INVIS ) ) ) )
mudstrlcpy( buf, "Someone", MSL );
else if( IS_SET( chan_data[channel].visibility[x], VIS_WIZINVIS )
&& ch->checkTrust( ) < chan_data[channel].invislevel[x] )
mudstrlcpy( buf, "Someone", MSL );
else
mudstrlcpy( buf, chan_data[channel].histname[x], MSL );

if( chan_data[channel].channel != CHANNEL_IMMTALK )
speaks = IS_SET( chan_data[channel].language[x], ch->speaks );
mudstrlcpy( buf2, chan_data[channel].argument[x], MSL );
ch->printf( "&W[&D%s&W] ", mini_c_time( chan_data[channel].timestamp[x],
ch->getPcData( )->timezone ) );
if( !chan_data[channel].emote[x] && ( chan_data[channel].channel == -1
|| chan_data[channel].channel == CHANNEL_NEWBIE
|| chan_data[channel].channel == CHANNEL_MUSIC ) )
{
sprintf( buf4, "%ss", verb );
ch->printf( chan_data[channel].buf_text, buf, buf4, speaks
? buf2 : scramble( buf2, chan_data[channel].language[x] ) );
}
else if( !chan_data[channel].emote[x] && chan_data[channel].channel != -1
&& chan_data[channel].channel != CHANNEL_IMMTALK )
ch->printf( chan_data[channel].buf_text, capitalize( verb ), buf, speaks
? buf2 : scramble( buf2, chan_data[channel].language[x] ) );
else if( !chan_data[channel].emote[x]
&& chan_data[channel].channel == CHANNEL_IMMTALK )
ch->printf( chan_data[channel].buf_text, capitalize( verb ), buf, buf2 );
else
{
sprintf( buf3, "%s %s\r\n", buf, buf2 );
ch->printf( chan_data[channel].prefix, capitalize( verb ), buf3 );
}
}
return;
}
}


The problem appears when the code reaches line 64 above. Here's the problem.
My Client said:
<32700hp 30000m 30000mv>
[Chat] You: Test

<32700hp 30000m 30000mv>
Channel history for 'chat' channel:
[01/06/09 07:27P] [Chat] Kayle: Test


Scoyn's Client said:
<454hp 297m 292mv>
[Chat] Kayle: Test

<454hp 297m 292mv>
Channel history for 'chat' channel:
[01/06/09 12:27P] [Chat] Kayle: Grtq


Now, if I change lines 68-79 to:
if( !chan_data[channel].emote[x] && ( chan_data[channel].channel == -1 
|| chan_data[channel].channel == CHANNEL_NEWBIE
|| chan_data[channel].channel == CHANNEL_MUSIC ) )
{
sprintf( buf4, "%ss", verb );
ch->printf( chan_data[channel].buf_text, buf, buf4, !speaks
? buf2 : scramble( buf2, chan_data[channel].language[x] ) );
}
else if( !chan_data[channel].emote[x] && chan_data[channel].channel != -1
&& chan_data[channel].channel != CHANNEL_IMMTALK )
ch->printf( chan_data[channel].buf_text, capitalize( verb ), buf, !speaks
? buf2 : scramble( buf2, chan_data[channel].language[x] ) );


It then looks like this:
My Client said:
<32700hp 30000m 30000mv>
[Chat] You: Test

<32700hp 30000m 30000mv>
Channel history for 'chat' channel:
[01/06/09 08:35P] [Chat] Kayle: Kgai
[01/06/09 08:35P] [Chat] Scoyn: test


Scoyn's Client said:
<454hp 297m 292mv>
[Chat] Kayle: Test

<454hp 297m 292mv>
Channel history for 'chat' channel:
[01/06/09 12:35P] [Chat] Kayle: Test
[01/06/09 12:35P] [Chat] Scoyn: ngtu


Here's the kicker. Immortals know all languages, and speak all languages. Nothing should be scrambling for either of us since we're both immortals. Now, I don't want to just rip out the language support because languages are going to play a large role in MW's gameplay. So I need to figure out where this is going wrong and try and fix it, and I'm running into dead end after dead end. And maybe it's because I've been staring at this for so long.

After previewing this I realized some people my be confused by some of the things in the code itself, and by the difference in timestamps between Scoyn and I. Well, Scoyn's a 'tard and never set his timezone properly so his times are off. Now the syntax in the code is a bit less funny. I converted char_data to a class and encapsulated things and moved things and made them member functions. So.. Yeah. Clears up all that. Now let's talk about the oddness of it all.
07 Jan, 2009, Kayle wrote in the 2nd comment:
Votes: 0
I should also point out that all parties involved are speaking common.
07 Jan, 2009, Baiou wrote in the 3rd comment:
Votes: 0
Did you change the IS_SET macro?

speaks = IS_SET( chan_data[channel].language[x], ch->speaks );


if not, shouldn't that be IS_SET( ch->speaks, chan_data[channel].language[x] ) ?
07 Jan, 2009, Kayle wrote in the 4th comment:
Votes: 0
Didn't change the macro, and swapped them around and it's still scrambling them.
07 Jan, 2009, Baiou wrote in the 5th comment:
Votes: 0
try using an ifcheck on speaks instead of a value assignment.

if( IS_SET( ch->speaks, chan_data[channel].language[x] ) )
speaks = true;


instead of speaks = etc… There's also the option of using the knows_language function.
07 Jan, 2009, kiasyn wrote in the 6th comment:
Votes: 0
mostly irrelevant, but wouldn't it make sense to make a ChanHistory struct, containing everything you have set as an array, and just array a list of that? >_>
07 Jan, 2009, Kayle wrote in the 7th comment:
Votes: 0
Baiou said:
There's also the option of using the knows_language function.


Knows language needs a second CharData, which I don't have.

kiasyn said:
mostly irrelevant, but wouldn't it make sense to make a ChanHistory struct, containing everything you have set as an array, and just array a list of that? >_>

Probably. But I already have this one written and aside from this language issue debugged. I don't want to have to start over. :P
07 Jan, 2009, Kayle wrote in the 8th comment:
Votes: 0
Just tested switching to an ifcheck and then assigning true and still get the garbling. It's not just affecting Immortals either, it's mortals too.
07 Jan, 2009, Baiou wrote in the 9th comment:
Votes: 0
Curious. What happens if you keep the first call to speaks as 'speaks' and make the second one '!speaks'?
07 Jan, 2009, Sharmair wrote in the 10th comment:
Votes: 0
Kayle said:
Baiou said:
There's also the option of using the knows_language function.


Knows language needs a second CharData, which I don't have.

The second CharData in knows_language is only used for making all the clan languages
different. You will be ok just using the one CharData twice in this use.
07 Jan, 2009, Kayle wrote in the 11th comment:
Votes: 0
How would I go about using knows_language in this instance? it returns an int from 0 to 100, so would I do:
if( knows_language( ch, chan_data[channel].language, ch )  == 100 )
speaks = true;

or
if( knows_language( ch, chan_data[channel].language, ch )  != 0 )
speaks = true;

or
if( knows_language( ch, chan_data[channel].language, ch ) >= someothernumber )
speaks = true;
07 Jan, 2009, Kayle wrote in the 12th comment:
Votes: 0
knows_language isn't going to work so well after all:
act_comm.cpp:546: error: invalid conversion from `int*' to `int'
act_comm.cpp:546: error: initializing argument 2 of `int knows_language(CharData*, int, CharData*)'


If I try to cast it to int the compiler then complains about a loss of precision.
07 Jan, 2009, Kayle wrote in the 13th comment:
Votes: 0
Ok, so after realizing I'm a total retard. Using knows_language works. but it helps if you pass a single member of the array to the function instead of trying to pass the whole array.

if( knows_language( ch, chan_data[channel].language[x], ch )  != 0 )
speaks = true;


The above works, but I wonder if it should be checking for >= 50% or so. Since technically if you're only half proficient in speaking a language you could make out some but not all of what was said. I might have to fidget with this a little bit.
07 Jan, 2009, elanthis wrote in the 14th comment:
Votes: 0
Isn't the whole problem about immortals knowing all languages? You should in this case just have knows_language always return 100 for immortals, but for things like the chan history command, it just shouldn't even _check_ whether the language is known or not for immortals. It should always just spit out the unscrambled text for immortals without even bothering to check the known languages or call the scramble function or anything. I only see one call to scramble in the code you posted, so that line likely just needs to be wrapped with an additional "if immortal" check that either spits out the plan channel history data (for immortals) or calls the existing code (for non-immortals).
07 Jan, 2009, Kayle wrote in the 15th comment:
Votes: 0
Well, I got it all updated, and the clan and race histories don't check language at the moment, but here's the updated showChanHistory that works perfectly. Even partially unscrambles as people know a language better and better.

void showChanHistory( CharData * ch, const char *verb, int channel )
{
char buf[MSL], buf2[MSL], buf3[MSL], buf4[MSL];
int x, speakswell;

if( channel < 0 )
{
bug( "%s: unknown bitvector value '%d' passed", __FUNCTION__, channel );
return;
}

x = chan_data[channel].channel;

if( x == CHANNEL_GUILD || x == CHANNEL_CLAN || x == CHANNEL_ORDER )
{
showClanHist( ch, verb, channel );
return;
}
else if( x == CHANNEL_RACETALK )
{
showRaceHist( ch, verb, channel );
return;
}

if( !chan_data[channel].argument[0] || chan_data[channel].argument[0][0] == '\0' )
{
sprintf( buf, "&RNobody's used the '%s' channel!\r\n", verb );
ch->print( buf );
return;
}

else
{
sprintf( buf, "&WChannel history for '%s' channel:\r\n", verb );
ch->print( buf );
ch->setColor( chan_data[channel].color );
for( x = 0; x < MAX_CHANHISTORY; x++ )
{
int speaking = -1, lang;
const char *sbuf = chan_data[channel].argument[x];

for( lang = 0; lang_array[lang] != LANG_UNKNOWN; lang++ )
if( chan_data[channel].language[x] & lang_array[lang] )
{
speaking = lang;
break;
}

if( !chan_data[channel].argument[x] || chan_data[channel].argument[x][0] == '\0' )
{
if( x == 0 )
bug( "%s: channel history initialized, but has no data!", __FUNCTION__ );
return;
}

if( !str_cmp( ch->getName( ), chan_data[channel].histname[x] ) )
mudstrlcpy( buf, ch->getName( ), MSL );
else if( !ch->isNPC( )
&& !IS_AFFECTED( ch, AFF_TRUESIGHT )
&& !xIS_SET( ch->m_Act, PLR_HOLYLIGHT )
&& ( ( IS_SET( chan_data[channel].visibility[x], VIS_HIDE )
&& !IS_AFFECTED( ch, AFF_DETECT_HIDDEN ) )
|| ( IS_SET( chan_data[channel].visibility[x], VIS_INVIS )
&& !IS_AFFECTED( ch, AFF_DETECT_INVIS ) ) ) )
mudstrlcpy( buf, "Someone", MSL );
else if( IS_SET( chan_data[channel].visibility[x], VIS_WIZINVIS )
&& ch->checkTrust( ) < chan_data[channel].invislevel[x] )
mudstrlcpy( buf, "Someone", MSL );
else
mudstrlcpy( buf, chan_data[channel].histname[x], MSL );

if( speaking != -1 && ( !ch->isNPC( ) || chan_data[channel].language[x] ) )
{
speakswell = knows_language( ch, chan_data[channel].language[x], ch );
if( speakswell < 85 )
sbuf = translate( speakswell, chan_data[channel].argument[x], lang_names[speaking] );
}
mudstrlcpy( buf2, chan_data[channel].argument[x], MSL );
ch->printf( "&W[&D%s&W] ", mini_c_time( chan_data[channel].timestamp[x],
ch->getPcData( )->timezone ) );
if( !chan_data[channel].emote[x] && ( chan_data[channel].channel == -1
|| chan_data[channel].channel == CHANNEL_NEWBIE
|| chan_data[channel].channel == CHANNEL_MUSIC ) )
{
sprintf( buf4, "%ss", verb );
ch->printf( chan_data[channel].buf_text, buf, buf4, sbuf );
}
else if( !chan_data[channel].emote[x] && chan_data[channel].channel != -1
&& chan_data[channel].channel != CHANNEL_IMMTALK )
ch->printf( chan_data[channel].buf_text, capitalize( verb ), buf, sbuf );
else if( !chan_data[channel].emote[x] && chan_data[channel].channel == CHANNEL_IMMTALK )
ch->printf( chan_data[channel].buf_text, capitalize( verb ), buf, buf2 );
else
{
sprintf( buf3, "%s %s\r\n", buf, buf2 );
ch->printf( chan_data[channel].prefix, capitalize( verb ), buf3 );
}
}
return;
}
}
Random Picks
0.0/15