23 Nov, 2011, Hades_Kane wrote in the 1st comment:
Votes: 0
I was not the one who installed the mobprogs on my game years ago, it was done before I ever even knew what an if statement was, and I've been staring at this code for a bit and attempted some changes, but I can't figure it out. I've always assumed this was a stock mobprog issue, but I finally got annoyed enough with it to try to fix it, but I can't figure it out and thought I'd see if anyone else has had this problem and / or fixed it.

Basically, when you trigger something with the act trigger on a mobprog, the program is executing before the messaging is sent.

For example, if I have 'act nod' as the program trigger with a mob echo of "Testing", it would look like:

Testing
You nod.

This is the same for oprogs and rprogs as well. Here is my act_new function, any suggestions would be greatly appreciated.

bool fNewline = TRUE;
void act_new( const char *format, CHAR_DATA *ch, const void *arg1,
const void *arg2, int type, int min_pos )
{
static char * const he_she [] =
{ "it", "he", "she"
};
static char * const him_her [] =
{ "it", "him", "her"
};
static char * const his_her [] =
{ "its", "his", "her"
};


CHAR_DATA *to;
CHAR_DATA *vch = (CHAR_DATA * ) arg2;
OBJ_DATA *obj1 = (OBJ_DATA * ) arg1;
OBJ_DATA *obj2 = (OBJ_DATA * ) arg2;
const char *str;
char *i = NULL;
char *point;
char *pbuff;
char *pgroupbuff;
char buffer[ MAX_STRING_LENGTH*2 ];
char group_buffer[ MAX_STRING_LENGTH*2 ];
char buf[ MAX_STRING_LENGTH*2 ];
char fname[ MAX_INPUT_LENGTH ];
bool fColour = FALSE;

/*
* Discard null and zero-length messages.
*/
if (! format || !*format )
return;

/* discard null rooms and chars */
if ( !ch || !ch->in_room )
return;

to = ch->in_room->people;

if ( type == TO_VICT )
{
if ( !vch )
{
bug( "Act: null vch with TO_VICT.", 0 );
return;
}

if ( !vch->in_room )
return;

to = vch->in_room->people;
}


for ( ; to ; to = to->next_in_room )
{
if ( (!IS_NPC(to) && to->desc == NULL )
|| to->position < min_pos )
continue;

if ( ( type == TO_CHAR ) && to != ch )
continue;
if ( type == TO_VICT && ( to != vch || to == ch ) )
continue;
if ( type == TO_ROOM && to == ch )
continue;
if ( type == TO_NOTVICT && (to == ch || to == vch) )
continue;

point = buf;
str = format;
while ( *str != '\0' )
{
if ( *str != '$' )
{
*point++ = *str++;
continue;
}

fColour = TRUE;
++str;
i = " <@@@> ";
if ( !arg2 && *str >= 'A' && *str <= 'Z' )
{
bug( "Act: missing arg2 for code %d.", *str );
i = " <@@@> ";
}
else
{
switch ( *str )
{
default:
bug( "Act: bad code %d.", *str );
i = " <@@@> ";
break;
/* Thx alex for 't' idea */
case 'L':
{
char areabuf[MSL];
sprintf (areabuf, "%s", ch->in_room->area->name);
i = areabuf;
break;
}
case 't':
i = (char *) arg1;
break;
case 'T':
i = (char *) arg2;
break;
case 'n':
i = PERS( ch, to );
break;
case 'N':
i = PERS( vch, to );
break;
/* case 'n':
if (capcolors)
i = colorcap(PERS (ch, to));
else
i = PERS (ch, to);
break;
case 'N':
if (capcolors)
i = colorcap(PERS (vch, to));
else
i = PERS (vch, to);
break; */
case 'e':
i = he_she [URANGE(0, ch ->sex, 2)];
break;
case 'E':
i = he_she [URANGE(0, vch ->sex, 2)];
break;
case 'm':
i = him_her [URANGE(0, ch ->sex, 2)];
break;
case 'M':
i = him_her [URANGE(0, vch ->sex, 2)];
break;
case 's':
i = his_her [URANGE(0, ch ->sex, 2)];
break;
case 'S':
i = his_her [URANGE(0, vch ->sex, 2)];
break;
case 'z':
{
char dambuf[MSL];
sprintf (dambuf, "%d", damcounter);
i = dambuf;
break;
}
case 'p':
i = can_see_obj( to, obj1 )
? obj1->short_descr
: "something";
break;

case 'P':
i = can_see_obj( to, obj2 )
? obj2->short_descr
: "something";
break;

case 'd':
if ( arg2 == NULL || ((char *) arg2)[0] == '\0' )
{
i = "door";
}
else
{
one_argument( (char *) arg2, fname );
i = fname;
}
break;
}
}

++str;
while ( ( *point = *i ) != '\0' )
++point, ++i;
}

if(fNewline)
{
*point++ = '\n';
*point++ = '\r';
}
*point = '\0';
buf[0] = UPPER(buf[0]);
pbuff = buffer;
if (type == TO_VICT || type == TO_ROOM)
{
pgroupbuff = group_buffer;
colourconv(pgroupbuff, buf, to);
}

colourconv( pbuff, buf, to );

if ( to->desc != NULL )
{
if ((type == TO_ROOM || type == TO_VICT
|| type == TO_NOTVICT)
&& ch && to
&& to != ch
&& makevis)
{
affect_strip (ch, gsn_hide);
REMOVE_BIT( ch->affected_by, AFF_HIDE );
affect_strip (ch, gsn_sneak);
REMOVE_BIT( ch->affected_by, AFF_SNEAK );
}
write_to_buffer( to->desc, buffer, 0 );
}
else
if ( MOBtrigger )
p_act_trigger( buf, to, NULL, NULL, ch, arg1, arg2, TRIG_ACT );
}
if ( type == TO_ROOM || type == TO_NOTVICT )
{
//If the player is in the arenaquest and not in startroom and echo is to room
if (IS_SET(ch->act, PLR_ARENAQUEST) && arenaquest.running && ch->in_room->vnum != arenaquest.startroom->vnum)
{
DESCRIPTOR_DATA * d;
CHAR_DATA * watcher;
for (d=descriptor_list; d != NULL; d = d->next)
{
if (d->connected != CON_PLAYING)
continue;
watcher = ( d->original != NULL ) ? d->original : d->character;
//If someone is watching from the startroom
if (watcher->watching == NULL)
continue;
if (IS_SET(watcher->act, PLR_ARENAQUEST) && watcher->in_room->vnum == arenaquest.startroom->vnum
&& (watcher->watching == ch || (watcher->watching != NULL
&& watcher->watching->in_room == ch->in_room && watcher->watching != ch)))
write_to_buffer(watcher->desc, group_buffer, 0);
}
}

OBJ_DATA *obj, *obj_next;
CHAR_DATA *tch, *tch_next;

point = buf;
str = format;
while ( *str != '\0' )
{
*point++ = *str++;
}
*point = '\0';

for ( obj = ch->in_room->contents; obj; obj = obj_next )
{
obj_next = obj->next_content;
if ( HAS_TRIGGER_OBJ( obj, TRIG_ACT ) )
p_act_trigger( buf, NULL, obj, NULL, ch, NULL, NULL, TRIG_ACT );
}

for ( tch = ch; tch; tch = tch_next )
{
tch_next = tch->next_in_room;

for ( obj = tch->carrying; obj; obj = obj_next )
{
obj_next = obj->next_content;
if ( HAS_TRIGGER_OBJ( obj, TRIG_ACT ) )
p_act_trigger( buf, NULL, obj, NULL, ch, NULL, NULL, TRIG_ACT );
}
}

if ( HAS_TRIGGER_ROOM( ch->in_room, TRIG_ACT ) )
p_act_trigger( buf, NULL, NULL, ch->in_room, ch, NULL, NULL, TRIG_ACT );
}


return;
}[/code=c]
23 Nov, 2011, Nich wrote in the 2nd comment:
Votes: 0
I'm not completely sure, since I don't know the codebase and my C is rusty, but I don't think the bug is in that code in particular. The bug would be in the function used to send players info.

From your example, a simplified version of what happens when a trigger is called is this: (I think)

The brackets represent scope, and there may be steps inbetween
nod -> (trigger -> trigger fires -> trigger prints) -> nod prints

p_act_trigger probably sends data to the user directly (speculating here), so you need to restructure your code to get more control over when that happens.
23 Nov, 2011, Runter wrote in the 3rd comment:
Votes: 0
That's one possibility perhaps. Another is the code can be executing too soon.

execute mprog if text.triggered?(mprog)
ch.print(text)


In this example, the mprog fires before the text is ever sent. Note, this may be preferable. Once the text is sent out the door you can't halt it from mprog. At least, not easily. Anyways. Here's what you probably want:

ch.print text
execute mprog if text.triggers?(mprog)
23 Nov, 2011, Rarva.Riendf wrote in the 4th comment:
Votes: 0
First thing I would check is where the act code is executed, because wat Nich is saying is probably right on tracks.
I know mine is in aggr_update, so this can not happen in my code.
I think I had these kind of problems in plenty places, ending up making mobs never ever call methods directly but use interp.c like any players as well.
Solved a lot of problems when your mobs actually act like players, as they should except for very few methods.
Using a debugger and following the code step by step should make it pretty obvious as well.
23 Nov, 2011, Hades_Kane wrote in the 5th comment:
Votes: 0
Ok, looking at it further based on what's been said and the best I can tell, it's looking like the program_flow thing intercepts the command and does all of the program stuff first, and if allowed (in the case of something like the exall trigger, it isn't), allows the normal command to pass after the fact.

The program_flow code is such a gigantic mess, though, I think I'm better off just dealing with the slightly off order trigger stuff.

Thanks anyhow!
23 Nov, 2011, Rarva.Riendf wrote in the 6th comment:
Votes: 0
How your mprog_act_trigger looks like ?
Mine is as simple as this: as you see if does not launch anyting yet, as the result would obvisouly be the same than yours
it just stores it there :mob->mpact = tmp_act;
void mprog_act_trigger(char *buf, CHAR_DATA *mob, CHAR_DATA *ch, OBJ_DATA *obj, void *vo) {

if ( !mob || IS_PC( mob ) || !(mob->pIndexData->progtypes & ACT_PROG))
return;

MPROG_ACT_LIST * tmp_act = malloc(sizeof(MPROG_ACT_LIST));
tmp_act->next = mob->mpact;
tmp_act->buf = str_dup(buf);
tmp_act->ch = ch;
tmp_act->obj = obj;
tmp_act->vo = vo;
mob->mpact = tmp_act;
}


To launch the mpact I have it done in aggr_update:

for (ch = char_list;ch ;ch = ch->next) {
/* MOBProgram ACT_PROG trigger */
if ( IS_PC( ch ) || !ch->mpact || !ch->valid)
continue;
MPROG_ACT_LIST * tmp_act, *tmp_act_next;
for (tmp_act = ch->mpact;tmp_act && ch->valid;tmp_act = tmp_act_next) {
tmp_act_next = tmp_act->next;
mprog_wordlist_check(tmp_act->buf, ch, tmp_act->ch, tmp_act->obj, tmp_act->vo, ACT_PROG );
free_string( &tmp_act->buf);
free(tmp_act);
}
/* Zap the pointer to the memory */
ch->mpact = NULL;
} /* mprog loop */
0.0/6