[Crossfire-wiki] [Crossfire DokuWiki] page added: user:mhoram:code:bwp

no-reply_wiki at metalforge.org no-reply_wiki at metalforge.org
Tue Jan 16 21:40:48 CST 2007


A page in your DokuWiki was added or changed. Here are the details:

Date        : 2007/01/16 21:40
User        : mhoram
Edit Summary: created, can anyone help debug?

====== Build Wiki Pages ======

This is a program I'm working on to automate the building of pages for the wiki from the monster archetypes (and eventually other categories, perhaps).  I'm still working on it, but I thought I'd put it here in case anyone wanted to help me fix a problem I'm having, or critique my code.  (I'm still very much an amateur at writing C.)

The main problem I'm having right now is that a couple variables are getting clobbered; I assume by bad memory allocation.  It happens at line 502: the variables *key[] and *val[] are fine before that call to join_with_comma(), but after it they are messed up.  (Search for BEFORE and AFTER.)  Since they aren't involved in that function at all, I assume that something in that function is causing an overflow.  There's probably a way with gdb or some other tool to see what variable is using a particular memory address, but I don't know what it is. 

Also, if anyone knows of a good C primer/tutorial online, please let me know.

===== Notes & Comments =====


===== Code =====
<code c>
/*
 * bwp - build wiki pages
 *
 * This program will sort out all monster archetypes and print wiki pages
 * for them, named 'a' through 'z'.  It uses some *_template subroutines taken
 * from Ryo's mapper.c.  It should compile if installed in server/trunk/utils.
 * Please direct all suggestions or corrections to aaron at baugher.biz (or
 * Mhoram on #crossfire).
 *
 * Compile command: gcc -g -pg -O0 bwp.c -I../include ../common/libcross.a ../socket/libsocket.a -o bwp -lz -lcrypt -lm
 */

/*
  CrossFire, A Multiplayer game for X-windows

  Copyright (C) 2002-2006 Mark Wedel & Crossfire Development Team
  Copyright (C) 1992 Frank Tore Johansen

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

  The authors can be reached via e-mail at crossfire-devel at real-time.com
*/

#define LO_NEWFILE 2
#define MAX_SIZE 64

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <global.h>

char* monster_page;            /* A single wiki page of monsters */
char* monster_entry;           /* A single monster entry         */
char* monster_canuse_row;      /* Can_use table row              */
char* monster_protected_row;   /* Protected table row            */
char* monster_vulnerable_row;  /* Vulnerable table row           */
char* monster_special_row;     /* Special table row              */
char* monster_attack_row;      /* Attack types table row         */
char* monster_lore_row;        /* Lore table row                 */

typedef struct string_array {
    sint16  count;
    sint16 longest;
    char** item;
} String_Array;

/**
 * This is a list of pointers that correspond to the FLAG_.. values.
 * This is a simple 1:1 mapping - if FLAG_FRIENDLY is 15, then
 * the 15'th element of this array should match that name.
 * If an entry is NULL, that is a flag not to loaded/saved.
 *
 * Copied from common/loader.c; perhaps should be defined elsewhere?
 * 
 */
const char *const flag_names[NUM_FLAGS+1] = {
    "alive", "wiz", NULL, NULL, "was_wiz", "applied", "unpaid",
    "can_use_shield", "no_pick", "client_anim_sync", "client_anim_random", /* 10 */
    "is_animated", NULL /* slow_move */, 
    NULL /* flying */, "monster", "friendly", "generator",
    "is_thrown", "auto_apply", "treasure", "player sold",   /* 20 */
    "see_invisible", "can_roll", "overlay_floor",
    "is_turnable", NULL /* walk_off */, NULL /* fly_on */,
    NULL /*fly_off*/, "is_used_up", "identified", "reflecting",	/* 30 */
    "changing", "splitting", "hitback", "startequip",
    "blocksview", "undead", "scared", "unaggressive",
    "reflect_missile", "reflect_spell",                             /* 40 */
    "no_magic", "no_fix_player", "is_lightable", "tear_down", 
    "run_away", NULL /*pass_thru */, NULL /*can_pass_thru*/, 
    "pick_up", "unique", "no_drop",					/* 50 */
    NULL /* wizcast*/, "can_cast_spell", "can_use_scroll", "can_use_range",
    "can_use_bow",  "can_use_armour", "can_use_weapon",
    "can_use_ring", "has_ready_range", "has_ready_bow",             /* 60 */
    "xrays", NULL, "is_floor", "lifesave", "no_strength", "sleep",
    "stand_still", "random_move", "only_attack", "confused",        /* 70 */
    "stealth", NULL, NULL, "cursed", "damned",
    "see_anywhere", "known_magical", "known_cursed",
    "can_use_skill", "been_applied",                                /* 80 */
    "has_ready_scroll", "can_use_rod", NULL,
    "can_use_horn", "make_invisible",  "inv_locked", "is_wooded",
    "is_hilly", "has_ready_skill", "has_ready_weapon",              /* 90 */
    "no_skill_ident", "is_blind", "can_see_in_dark", "is_cauldron",
    "is_dust", "no_steal", "one_hit", NULL, "berserk", "neutral",	/* 100 */
    "no_attack", "no_damage", NULL, NULL, "activate_on_push",
    "activate_on_release","is_water","use_content_on_gen",NULL,"is_buildable", /* 110 */
    NULL, "blessed", "known_blessed"
};

/**
 * Concatenates a string, and free concatenated string.
 *
 * @param source
 * string to append to. Can be NULL.
 * @param add
 * string that is appened. Will be free()d after. Must not be NULL.
 * @return
 * new string that should be free()d by caller.
 */
static char* cat_template(char* source, char* add) {
    if (!source)
        return add;
    source = realloc(source, strlen(source) + strlen(add) + 1);
    strcat(source, add);
    free(add);
    return source;
}

/**
 * Reads a file in memory.
 *
 * @param name
 * file path to read.
 * @param buffer
 * where to store. Can be left uninitialized in case of errors.
 * @return
 * 1 if error, 0 else.
 */
static int read_template(const char* name, char** buffer) {
    FILE* file;
    size_t size;
    struct stat info;

    if (stat(name, &info)) {
        printf("Couldn't stat template %s!\n", name);
        return 1;
    }

    (*buffer) = calloc(1, info.st_size + 1);
    if (!(*buffer)) {
        printf("Template %s calloc failed!\n", name);
        return 1;
    }

    if (info.st_size == 0) {
        (*buffer)[0] = '\0';
        return 0;
    }

    file = fopen(name, "rb");
    if (!file) {
        printf("Couldn't open template %s!\n", name);
        free(*buffer);
        return 1;
    }
    if (fread(*buffer, info.st_size, 1, file) != 1) {
        printf("Couldn't read template %s!\n", name);
        free(*buffer);
        fclose(file);
        return 1;
    }
    fclose(file);
    return 0;
}

/**
 * Processes a template.
 *
 * Variables in the form #VARIABLE# will be substituted for specified values.
 *
 * @param template
 * template to process.
 * @param vars
 * variables to replace. Array must be NULL-terminated.
 * @param values
 * variables to replace by. Must be the same size as vars, no NULL element allowed.
 * @return
 * filled-in template, that must be free()d be caller. NULL if memory allocation error.
 *
 * @note
 * returned string will be a memory block larger than required, for performance reasons.
 */
static char* do_template(const char* template, const char** vars, const char** values) {
    int count = 0;
    const char* sharp = template;
    int maxlen = 0;
    int var = 0;
    char* result;
    char* current_result;
    const char* end;

    while ((sharp = strchr(sharp, '#')) != NULL) {
        sharp++;
        count++;
    }
    if (!count)
        return strdup(template);
    if (count % 2) {
        printf("Malformed template, mismatched #!\n");
        return strdup(template);
    }

    while (vars[var] != NULL) {
        if (strlen(values[var]) > maxlen)
            maxlen = strlen(values[var]);
        var++;
    }
    result = calloc(1, strlen(template) + maxlen * (count / 2) + 1);
    if (!result)
        return NULL;
    current_result = result;

    sharp = template;
    while ((sharp = strchr(sharp, '#')) != NULL) {
        end = strchr(sharp + 1, '#');
        strncpy(current_result, template, sharp - template);
        if (end == sharp+1) {
            strcat(current_result, "#");
        }
        else {
            current_result = current_result + strlen(current_result);
            var = 0;
            while (vars[var] != 0 && strncmp(vars[var], sharp + 1, end - sharp - 1))
                var++;
            if (vars[var] == 0)
                printf("Wrong tag: %s\n", sharp);
            else
                strcpy(current_result, values[var]);
        }
        current_result = current_result + strlen(current_result);
        sharp = end + 1;
        template = sharp;
    }
    strcat(current_result, template);
    return result;
}


/****  Mhoram's code starts here *****/

/**
 * Sort values alphabetically
 *
 * Used by qsort to sort values alphabetically without regard to case
 *
 * @param a
 * First value
 *
 * @param b
 * Second value
 *
 */
static int sortbyname(const void *a, const void *b){
    char aa[MAX_SIZE];
    char bb[MAX_SIZE];
    int i;
    
    strcpy(aa,a);
    strcpy(bb,b);
    
    for(i=0; i<sizeof(aa) && i < sizeof(bb); i++){
        aa[i] = tolower(aa[i]);
        bb[i] = tolower(bb[i]);
        if(aa[i] != bb[i]){
            return(aa[i]-bb[i]);
        }
    }
    return 0;
}

/**
 * Appends a string to another separated by a comma and space.
 *
 * Appends the second argument to the first argument
 * 
 * @param old
 * Old string
 *
 * @param new
 * New string to append to old
 *
 */
static const char* append_with_comma(const char* old, const char* new){

    LOG(llevError, "%s + %s\n", old, new);
    
    if( old == NULL ){
        old = calloc(1, strlen(new)+1 );
    } else {
        old = realloc( old, strlen(old)+ strlen(new)+strlen(", ")+1 );
        old = strncat( old, ", ", 2 );
    }
    old = strncat( old, new, strlen(new));

    return(old);
}

void push(String_Array* array, const char* string){
    sint16 i = array->count;

    array->item[i] = calloc(1, strlen(string)+1);
    strncpy(array->item[i], string, strlen(string));
    if( strlen(array->item[i]) > array->longest ){
        array->longest = strlen(array->item[i]);
    }
    fprintf(stderr, "Pushed array item %d: %s\n", array->count, array->item[i]);
    array->count++;
}

/**
 * Joins strings with a comma and space.
 *
 * Takes an array of strings and joins them togther with a comma and a space
 * between each of them.
 * 
 * @param String_Array
 * Pointer to struct of type String_Array, containing strings to join
 *
 */
const char* join_with_comma(String_Array* array){
    char *newtext = "";
    int i;

    fprintf(stderr, "JOIN: %d %d %s\n", array->count, array->longest, array->item[0]);
    
    newtext = calloc(1,1);
    qsort(&array->item, array->count, array->longest, sortbyname);
    for(i=0;i<array->count;i++ ){
        if(i){
            newtext = realloc( newtext, strlen(newtext) + strlen(", ") +1 );
            newtext = strncat( newtext, ", ", 2 );
        }
        newtext = realloc( newtext, strlen(newtext) + strlen(array->item[i]) +1 );
        fprintf(stderr, "Reallocated %d bytes for newtext\n", strlen(newtext) + strlen(array->item[i]) +1 );
        newtext = strncat( newtext, array->item[i], strlen(array->item[i]));
    }
    fprintf(stderr, "JOINED: %s\n", newtext);
    
    return newtext;
}
        
    
int main(int argc, char *argv[]) {

    archetype *at;
    int archnum=0;
    int archmem=0;
    char archnames[4000][MAX_SIZE];
    int i;
    char letter;
    char last_letter;
    char *wiki_page=NULL;
    char *monster_entries=NULL;
    
    FILE *fp;
    const char *wikidir = "/tmp";  /* Should change this to come from command line? */
    char wikifile[40];

    init_globals();
    init_library();
    init_archetypes();
    init_artifacts();
    init_formulae();
    init_readable();

    init_gods();

        /* Initialize templates */
    if (read_template("templates/wiki/monster_page", &monster_page))
        return;
    if (read_template("templates/wiki/monster_page", &monster_page))
        return;
    if (read_template("templates/wiki/monster_entry", &monster_entry))
        return;
    if (read_template("templates/wiki/monster_canuse_row",
                      &monster_canuse_row))
        return;
    if (read_template("templates/wiki/monster_protected_row",
                      &monster_protected_row))
        return;
    if (read_template("templates/wiki/monster_vulnerable_row",
                      &monster_vulnerable_row))
        return;
    if (read_template("templates/wiki/monster_special_row",
                      &monster_special_row))
        return;
    if (read_template("templates/wiki/monster_attack_row",
                      &monster_attack_row))
        return;
    if (read_template("templates/wiki/monster_lore_row",
                      &monster_lore_row))
        return;
    
        /* Pick out the monster archetypes and sort them into an array */
    for(at=first_archetype; at!=NULL; at=at->next){
        if(QUERY_FLAG(&at->clone, FLAG_MONSTER) &&
           QUERY_FLAG(&at->clone,FLAG_ALIVE)){
            strcpy(archnames[archnum++], at->clone.name);
        }
    }
    printf("Sorting...");
    qsort(archnames, archnum, MAX_SIZE, sortbyname);
    printf("done.\n");
    
    for(i=0; i<archnum; i++) {
        at=find_archetype_by_object_name(archnames[i]);
        if(at){
            letter = tolower(archnames[i][0]);
            wikifile[0] = '\0';

            LOG(llevInfo, "Doing archetype %s\n", archnames[i]);
            
            
            snprintf(wikifile, sizeof(wikifile), "%s/%c", wikidir, letter);
            printf("%s\n",wikifile);
            
            if(letter != last_letter) {
                    /* add a monster entry */
                char *canuse_row     = "";
                char *protected_row  = "";
                char *vulnerable_row = "";
                char *special_row    = "";
                char *attack_row     = "";
                char *lore_row       = "";
                const char *key[] = {NULL, NULL, NULL, NULL, NULL, NULL};
                const char *val[] = {NULL, NULL, NULL, NULL, NULL, NULL};
                const int CANUSE_LENGTH = 16;
                String_Array canuse;
                String_Array resist;
                String_Array vulner;
                String_Array attack;
                String_Array special;
                    /* Some flags that seemed useful; may need to add to this list.
                     * *special_names[] is used because some of the names in
                     * define.h are a bit awkward.  Last one is negative to mark end.
                     */
                const sint8 special_flags[] = { 21, 93, 52, 38, 13, 32, 61, -1 };
                const char *special_names[] = { "see invisible", "see in dark",
                                                "spellcaster", "unaggressive",
                                                "flying", "splitting", "x-ray vision"
                };
                int keycount     = 0;
                int j;

                canuse.item = calloc(1,sizeof(const char*)*(CANUSE_LENGTH+1));
                resist.item = calloc(1,sizeof(const char*)*(NROFATTACKS+1));
                vulner.item = calloc(1,sizeof(const char*)*(NROFATTACKS+1));
                attack.item = calloc(1,sizeof(const char*)*(NROFATTACKS+1));
                special.item = calloc(1,sizeof(const char*)*(NROFATTACKS+1));
                
                    /* Do lore row */
                if( at->clone.lore ) {
                    key[keycount] = "LORE";
                    key[keycount+1] = NULL;
                    val[keycount] = at->clone.lore;
                    keycount++;
                    lore_row = do_template(monster_lore_row, key, val);
                }

                    /* Do canuse row */
                canuse.count = 0;
                keycount = 0;
                for( j=1; j<= NUM_FLAGS; j++ ) {
                    if( QUERY_FLAG(&at->clone, j) &&
                        flag_names[j] &&
                        ! strncmp(flag_names[j], "can_use_", 8) ) {
                        push(&canuse, flag_names[j]+8);
                    }
                }
                if( canuse.count ){
                    key[keycount] = "CANUSE";
                    key[keycount+1] = NULL;
                    val[keycount] = join_with_comma(&canuse);
                    canuse_row = do_template(monster_canuse_row, key, val);
                }
                
                    /* Do protected/vulnerable rows */
                resist.count = 0;
                vulner.count = 0;
                for( j=0; j<=NROFATTACKS; j++ ) {
                    if( at->clone.resist[j] && attacktype_desc[j] ){
                        char rowtext[32];
                        
                        if( at->clone.resist[j] < 0 ){
                            sprintf(rowtext, "%s %i",
                                    attacktype_desc[j], at->clone.resist[j]);
                            push(&vulner, rowtext);
                        } else {
                            sprintf(rowtext, "%s +%i",
                                    attacktype_desc[j], at->clone.resist[j]);
                            push(&resist, rowtext);
                        }
                    }
                }
                keycount = 0;
                if( resist.count ){
                    key[keycount] = "PROTECTED";
                    key[keycount+1] = NULL;
                    fprintf(stderr, "BEFORE: %s\n", key[keycount]);
                    
                    val[keycount] = join_with_comma(&resist);

                    fprintf(stderr, "AFTER: %s\n", key[keycount]);

                    protected_row = do_template(monster_protected_row, key, val);
                }

                keycount = 0;
                if( vulner.count ){
                    key[keycount] = "VULNERABLE";
                    key[keycount+1] = NULL;
                    val[keycount] = join_with_comma(&vulner);
                    vulnerable_row = do_template(monster_vulnerable_row, key, val);
                }
                
                    /* Do attacktype row */
                attack.count = 0;
                keycount = 0;
                val[keycount]=NULL;
                for( j=0; j<=NROFATTACKS; j++ ) {
                    if( at->clone.attacktype & (1U << j)) {
                        push(&attack, attacktype_desc[j]);
                    }
                }
                if( attack.count ){
                    key[keycount] = "ATTACKS";
                    key[keycount+1] = NULL;
                    val[keycount] = join_with_comma(&attack);
                    attack_row = do_template(monster_attack_row, key, val);
                }

                    /* Do special row */
                special.count = 0;
                keycount = 0;
                val[keycount]=NULL;
                for( j=0; special_flags[j] >= 0; j++ ) {
                    if( QUERY_FLAG(&at->clone, special_flags[j])){
                        push(&special, special_names[j]);
                    }
                }
                if( special.count ){
                    key[keycount] = "SPECIAL";
                    key[keycount+1] = NULL;
                    val[keycount] = join_with_comma(&special);
                    special_row = do_template(monster_special_row, key, val);
                }

/*                printf("%s%s%s%s%s\n",
                       canuse_row, protected_row, vulnerable_row, attack_row, special_row);
*/              

                if( i > 10 ){
                    exit(0);  /* stop after test with ten monsters */
                }
                
                free(canuse.item);
                free(resist.item);
                free(vulner.item);
                free(attack.item);
                free(special.item);
                
                

            } else {

                
            }
/*            fp = fopen(wikifile, "w");            
            if(! fp){
                fprintf(stderr, "Unable to write to wiki file!\n");
                exit(1); 
            }
            fprintf(fp, "\n====== %s ======\n", at->clone.name);
            fprintf(fp, "%d %d\n\n", at->tail_x, at->tail_y);
            
            fclose(fp);
*/          
        } else {
            printf("********* %d %s **********************\n",i, archnames[i]);
        }
    }



    exit(0);
}


void set_map_timeout(void) {}   /* doesn't need to do anything */

#include <global.h>


/* some plagarized code from apply.c--I needed just these two functions
   without all the rest of the junk, so.... */
int auto_apply (object *op) {
    object *tmp = NULL;
    int i;

    switch(op->type) {
        case SHOP_FLOOR:
            if (!HAS_RANDOM_ITEMS(op)) return 0;
            do {
                i=10; /* let's give it 10 tries */
                while((tmp=generate_treasure(op->randomitems,op->stats.exp?
                                             op->stats.exp:5))==NULL&&--i);
                if(tmp==NULL)
                    return 0;
                if(QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
                {
                    free_object(tmp);
                    tmp = NULL;
                }
            } while(!tmp);

            tmp->x=op->x,tmp->y=op->y;
            SET_FLAG(tmp,FLAG_UNPAID);
            insert_ob_in_map(tmp,op->map,NULL,0);
            CLEAR_FLAG(op,FLAG_AUTO_APPLY);
            identify(tmp);
            break;

        case TREASURE:
            if (HAS_RANDOM_ITEMS(op))
                while ((op->stats.hp--)>0)
                    create_treasure(op->randomitems, op, GT_ENVIRONMENT,
                                    op->stats.exp ? op->stats.exp :
                                    op->map == NULL ?  14: op->map->difficulty,0);
            remove_ob(op);
            free_object(op);
            break;
    }

    return tmp ? 1 : 0;
}

/* fix_auto_apply goes through the entire map (only the first time
 * when an original map is loaded) and performs special actions for
 * certain objects (most initialization of chests and creation of
 * treasures and stuff).  Calls auto_apply if appropriate.
 */

void fix_auto_apply(mapstruct *m) {
    object *tmp,*above=NULL;
    int x,y;

    for(x=0;x<MAP_WIDTH(m);x++)
        for(y=0;y<MAP_HEIGHT(m);y++)
            for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=above) {
                above=tmp->above;

                if(QUERY_FLAG(tmp,FLAG_AUTO_APPLY))
                    auto_apply(tmp);
                else if(tmp->type==TREASURE) {
                    if (HAS_RANDOM_ITEMS(tmp))
                        while ((tmp->stats.hp--)>0)
                            create_treasure(tmp->randomitems, tmp, 0,
                                            m->difficulty,0);
                }
                if(tmp && tmp->arch && tmp->type!=PLAYER && tmp->type!=TREASURE &&
                   tmp->randomitems){
                    if(tmp->type==CONTAINER) {
                        if (HAS_RANDOM_ITEMS(tmp))
                            while ((tmp->stats.hp--)>0)
                                create_treasure(tmp->randomitems, tmp, 0,
                                                m->difficulty,0);
                    }
                    else if (HAS_RANDOM_ITEMS(tmp))
                        create_treasure(tmp->randomitems, tmp, GT_APPLY,
                                        m->difficulty,0);
                }
            }
    for(x=0;x<MAP_WIDTH(m);x++)
        for(y=0;y<MAP_HEIGHT(m);y++)
            for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=tmp->above)
                if (tmp->above
                    && (tmp->type == TRIGGER_BUTTON || tmp->type == TRIGGER_PEDESTAL))
                    check_trigger(tmp,tmp->above);
}

/**
 * Those are dummy functions defined to resolve all symboles.
 * Added as part of glue cleaning.
 * Ryo 2005-07-15
 **/
 

void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *txt, const char *txt2){
    fprintf(logfile, "%s\n", txt);
}

void draw_ext_info_format(
    int flags, int pri, const object *pl, uint8 type, 
    uint8 subtype, 
    const char* new_format, 
    const char* old_format, 
    ...){

    va_list ap;
    va_start(ap, old_format);
    vfprintf(logfile, old_format, ap);
    va_end(ap);
}


void ext_info_map(int color, const mapstruct *map, uint8 type, uint8 subtype, const char *str1, const char *str2){
    fprintf(logfile, "ext_info_map: %s\n", str2);
}

void move_teleporter( object* ob){
}
 
void move_firewall( object* ob){
}
 
void move_duplicator( object* ob){
}
 
void move_marker( object* ob){
}
 
void move_creator( object* ob){
}
 
void emergency_save( int x ){
}
 
void clean_tmp_files( void ){
}
 
void esrv_send_item( object* ob, object* obx ){
}
 
void dragon_ability_gain( object* ob, int x, int y ){
}
 
void weather_effect( const char* c ){
}
 
void set_darkness_map( mapstruct* m){
}
 
void move_apply( object* ob, object* obt, object* obx ){
}
 
object* find_skill_by_number( object* ob, int x ){
    return NULL;
}
 
void esrv_del_item(player *pl, int tag){
}
 
void esrv_update_spells(player *pl){
}

void monster_check_apply( object* ob, object* obt ){
}
 
void trap_adjust( object* ob, int x ){
}

int execute_event(object* op, int eventcode, object* activator, object* third, const char* message, int fix){
    return 0;
}

int execute_global_event(int eventcode, ...){
    return 0;
}

</code>


IP-Address  : 206.71.197.56
Old Revision: none
New Revision: http://wiki.metalforge.net/doku.php/user:mhoram:code:bwp

-- 
This mail was generated by DokuWiki at
http://wiki.metalforge.net/




More information about the crossfire-wiki mailing list