diff options
Diffstat (limited to 'src/map')
-rw-r--r-- | src/map/battle.c | 12 | ||||
-rw-r--r-- | src/map/chrif.c | 2 | ||||
-rw-r--r-- | src/map/clif.c | 19 | ||||
-rw-r--r-- | src/map/guild.c | 4 | ||||
-rw-r--r-- | src/map/map.c | 9 | ||||
-rw-r--r-- | src/map/map.h | 8 | ||||
-rw-r--r-- | src/map/mapreg.h | 67 | ||||
-rw-r--r-- | src/map/mapreg_sql.c | 816 | ||||
-rw-r--r-- | src/map/mob.c | 4 | ||||
-rw-r--r-- | src/map/mob.h | 1 | ||||
-rw-r--r-- | src/map/npc.c | 13 | ||||
-rw-r--r-- | src/map/npc.h | 6 | ||||
-rw-r--r-- | src/map/pc.h | 4 | ||||
-rw-r--r-- | src/map/script.c | 554 | ||||
-rw-r--r-- | src/map/script.h | 21 | ||||
-rw-r--r-- | src/map/skill.c | 21 | ||||
-rw-r--r-- | src/map/skill.h | 2 | ||||
-rw-r--r-- | src/map/status.c | 37 | ||||
-rw-r--r-- | src/map/status.h | 4 |
19 files changed, 1095 insertions, 509 deletions
diff --git a/src/map/battle.c b/src/map/battle.c index c8cd71b94..a8193d24a 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -2832,18 +2832,18 @@ static int64 battle_calc_damage(struct block_list *src, struct block_list *bl, s d->dmg_lv = ATK_BLOCK; if(src_skill_id == MH_STEINWAND){ if (--group->val2<=0) - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); if( (group->val3 - damage) > 0 ) group->val3 -= (int)cap_value(damage, INT_MIN, INT_MAX); else - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); return 0; } if( skill_id == SO_ELEMENTAL_SHIELD ) { if ( ( group->val2 - damage) > 0 ) { group->val2 -= (int)cap_value(damage,INT_MIN,INT_MAX); } else - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); return 0; } /** @@ -2853,12 +2853,12 @@ static int64 battle_calc_damage(struct block_list *src, struct block_list *bl, s if ( ( group->val2 - damage) > 0 ) { group->val2 -= (int)cap_value(damage,INT_MIN,INT_MAX); } else - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); if (--group->val3<=0) - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); #else if (--group->val2<=0) - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); #endif return 0; } diff --git a/src/map/chrif.c b/src/map/chrif.c index b131907e0..ac5fb4dd0 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -863,6 +863,8 @@ static bool chrif_changesex(struct map_session_data *sd, bool change_account) nullpo_retr(false, sd); chrif_check(false); + chrif->save(sd, 0); + WFIFOHEAD(chrif->fd,44); WFIFOW(chrif->fd,0) = 0x2b0e; WFIFOL(chrif->fd,2) = sd->status.account_id; diff --git a/src/map/clif.c b/src/map/clif.c index ab13ffe1f..a0ec1fdf6 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -10389,7 +10389,8 @@ static const char *clif_process_chat_message(struct map_session_data *sd, const * @param[in] sd The source character. * @param[in] packet The packet data. * @param[out] out_name The parsed target name buffer (must be a valid - * buffer of size NAME_LENGTH). + * buffer of size NAME_LENGTH + 1 because the client + * can send 24 characters without NULL terminator). * @param[out] out_message The output message buffer (must be a valid buffer). * @param[in] out_messagelen The size of out_message. * @retval true if the validation succeeded and the message is a chat message. @@ -10399,7 +10400,7 @@ static const char *clif_process_chat_message(struct map_session_data *sd, const */ static bool clif_process_whisper_message(struct map_session_data *sd, const struct packet_whisper_message *packet, char *out_name, char *out_message, int out_messagelen) { - int namelen = 0, messagelen = 0; + int messagelen = 0; nullpo_retr(false, sd); nullpo_retr(false, packet); @@ -10412,15 +10413,6 @@ static bool clif_process_whisper_message(struct map_session_data *sd, const stru return false; } - // validate name - namelen = (int)strnlen(packet->name, NAME_LENGTH-1); // name length (w/o zero byte) - - if (packet->name[namelen] != '\0') { - // only restriction is that the name must be zero-terminated - ShowWarning("clif_process_whisper_message: Player '%s' sent an unterminated name!\n", sd->status.name); - return false; - } - #if PACKETVER >= 20151001 // Packet doesn't include a NUL terminator messagelen = packet->packet_len - NAME_LENGTH - 4; @@ -10439,7 +10431,7 @@ static bool clif_process_whisper_message(struct map_session_data *sd, const stru return false; } - safestrncpy(out_name, packet->name, namelen+1); // [!] packet->name is not NUL terminated + safestrncpy(out_name, packet->name, NAME_LENGTH + 1); // [!] packet->name is not NUL terminated safestrncpy(out_message, packet->message, messagelen+1); // [!] packet->message is not necessarily NUL terminated if (!pc->process_chat_message(sd, out_message)) @@ -11778,7 +11770,8 @@ static void clif_parse_WisMessage(int fd, struct map_session_data *sd) struct map_session_data* dstsd; int i; - char target[NAME_LENGTH], message[CHAT_SIZE_MAX + 1]; + char target[NAME_LENGTH + 1]; // Client can send 24 characters without NULL terminator. + char message[CHAT_SIZE_MAX + 1]; const struct packet_whisper_message *packet = RP2PTR(fd); if (!clif->process_whisper_message(sd, packet, target, message, sizeof message)) diff --git a/src/map/guild.c b/src/map/guild.c index f344878e1..90f870f1c 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -1519,7 +1519,7 @@ static void guild_guildaura_refresh(struct map_session_data *sd, uint16 skill_id if( !skill_lv ) return; if (sd->sc.data[type] && (group = skill->id2group(sd->sc.data[type]->val4)) != NULL) { - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); status_change_end(&sd->bl,type,INVALID_TIMER); } group = skill->unitsetting(&sd->bl,skill_id,skill_lv,sd->bl.x,sd->bl.y,0); @@ -2069,7 +2069,7 @@ static int guild_break(struct map_session_data *sd, const char *name) } } for(i = 0; i < count; i++) { // FIXME: Why is this not done in the above loop? - skill->del_unitgroup(groups[i],ALC_MARK); + skill->del_unitgroup(groups[i]); } } diff --git a/src/map/map.c b/src/map/map.c index f66f40dfc..63d05c1c0 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -4459,7 +4459,6 @@ static bool inter_config_read_connection(const char *filename, const struct conf static bool inter_config_read_database_names(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; - bool retval = true; nullpo_retr(false, filename); nullpo_retr(false, config); @@ -4477,16 +4476,14 @@ static bool inter_config_read_database_names(const char *filename, const struct libconfig->setting_lookup_mutable_string(setting, "npc_barter_data_db", map->npc_barter_data_db, sizeof(map->npc_barter_data_db)); libconfig->setting_lookup_mutable_string(setting, "npc_expanded_barter_data_db", map->npc_expanded_barter_data_db, sizeof(map->npc_expanded_barter_data_db)); - if (!mapreg->config_read(filename, setting, imported)) - retval = false; - if ((setting = libconfig->lookup(config, "inter_configuration/database_names/registry")) == NULL) { if (imported) - return retval; + return true; ShowError("inter_config_read: inter_configuration/database_names/registry was not found in %s!\n", filename); return false; } - return retval; + + return mapreg->config_read_registry(filename, setting, imported); } /*======================================= diff --git a/src/map/map.h b/src/map/map.h index 64736a6f5..17f210bc3 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -329,6 +329,14 @@ enum bl_type { enum npc_subtype { WARP, SHOP, SCRIPT, CASHSHOP, TOMB }; +/** optional flags for script labels, used by the label db */ +enum script_label_flags { + /** the label can be called from outside the local scope of the NPC */ + LABEL_IS_EXTERN = 0x1, + /** the label is a public or private local NPC function */ + LABEL_IS_USERFUNC = 0x2, +}; + /** * Race type IDs. * diff --git a/src/map/mapreg.h b/src/map/mapreg.h index b3b89e1b2..4318eaea8 100644 --- a/src/map/mapreg.h +++ b/src/map/mapreg.h @@ -21,55 +21,66 @@ #ifndef MAP_MAPREG_H #define MAP_MAPREG_H -#include "map/script.h" // struct reg_db -#include "common/hercules.h" +#include "map/script.h" #include "common/db.h" +#include "common/hercules.h" -/* Forward Declarations */ -struct config_setting_t; // common/conf.h +/** Forward Declarations **/ +struct config_setting_t; struct eri; -/** Container for a mapreg value */ +#ifndef MAPREG_AUTOSAVE_INTERVAL +#define MAPREG_AUTOSAVE_INTERVAL (300 * 1000) //!< Interval for auto-saving permanent global variables to the database in milliseconds. +#endif /** MAPREG_AUTOSAVE_INTERVAL **/ + +/** Global variable structure. **/ struct mapreg_save { - int64 uid; ///< Unique ID - union { - int i; ///< Numeric value - char *str; ///< String value + int64 uid; //!< The variable's unique ID. + union value { //!< The variable's value container. + int i; //!< The variable's integer value. + char *str; //!< The variable's string value. } u; - bool is_string; ///< true if it's a string, false if it's a number - bool save; ///< Whether a save operation is pending + bool is_string; //!< Whether the variable's value is a string. + bool save; //!< Whether the variable's save operation is pending. }; +/** The mapreg interface structure. **/ struct mapreg_interface { - struct reg_db regs; - /* */ - bool skip_insert; - /* */ - struct eri *ers; //[Ind/Hercules] - /* */ - char table[32]; - /* */ - bool dirty; ///< Whether there are modified regs to be saved - /* */ - void (*init) (void); - void (*final) (void); - /* */ + /** Interface variables. **/ + struct eri *ers; //!< Entry manager for global variables. + struct reg_db regs; //!< Generic database for global variables. + bool dirty; //!< Whether there are modified global variables to be saved. + bool skip_insert; //!< Whether to skip inserting the variable into the SQL database in mapreg_set_*_db(). + char num_db[32]; //!< Name of SQL table which holds permanent global integer variables. + char str_db[32]; //!< Name of SQL table which holds permanent global string variables. + + /** Interface functions. **/ int (*readreg) (int64 uid); - char* (*readregstr) (int64 uid); + char *(*readregstr) (int64 uid); + bool (*set_num_db) (int64 uid, const char *name, unsigned int index, int value); + bool (*delete_num_db) (int64 uid, const char *name, unsigned int index); bool (*setreg) (int64 uid, int val); + bool (*set_str_db) (int64 uid, const char *name, unsigned int index, const char *value); + bool (*delete_str_db) (int64 uid, const char *name, unsigned int index); bool (*setregstr) (int64 uid, const char *str); + void (*load_num_db) (void); + void (*load_str_db) (void); void (*load) (void); + void (*save_num_db) (const char *name, unsigned int index, int value); + void (*save_str_db) (const char *name, unsigned int index, const char *value); void (*save) (void); int (*save_timer) (int tid, int64 tick, int id, intptr_t data); int (*destroyreg) (union DBKey key, struct DBData *data, va_list ap); void (*reload) (void); - bool (*config_read) (const char *filename, const struct config_setting_t *config, bool imported); + bool (*config_read_registry) (const char *filename, const struct config_setting_t *config, bool imported); + void (*final) (void); + void (*init) (void); }; #ifdef HERCULES_CORE void mapreg_defaults(void); -#endif // HERCULES_CORE +#endif /** HERCULES_CORE **/ HPShared struct mapreg_interface *mapreg; -#endif /* MAP_MAPREG_H */ +#endif /** MAP_MAPREG_H **/ diff --git a/src/map/mapreg_sql.c b/src/map/mapreg_sql.c index 2963637da..16d8b0ff6 100644 --- a/src/map/mapreg_sql.c +++ b/src/map/mapreg_sql.c @@ -22,7 +22,7 @@ #include "mapreg.h" -#include "map/map.h" // map-"mysql_handle +#include "map/map.h" #include "map/script.h" #include "common/cbasetypes.h" #include "common/conf.h" @@ -38,289 +38,596 @@ #include <stdlib.h> #include <string.h> -static struct mapreg_interface mapreg_s; -struct mapreg_interface *mapreg; - -#define MAPREG_AUTOSAVE_INTERVAL (300*1000) +static struct mapreg_interface mapreg_s; //!< Private interface structure. +struct mapreg_interface *mapreg; //!< Public interface structure. /** - * Looks up the value of an integer variable using its uid. + * Looks up the value of a global integer variable using its unique ID. * - * @param uid variable's unique identifier. - * @return variable's integer value - */ -static int mapreg_readreg(int64 uid) + * @param uid The variable's unique ID. + * @return The variable's value or 0 if the variable does not exist. + * + **/ +static int mapreg_get_num_reg(int64 uid) { - struct mapreg_save *m = i64db_get(mapreg->regs.vars, uid); - return m?m->u.i:0; + struct mapreg_save *var = i64db_get(mapreg->regs.vars, uid); + return (var != NULL) ? var->u.i : 0; } /** - * Looks up the value of a string variable using its uid. + * Looks up the value of a global string variable using its unique ID. * - * @param uid variable's unique identifier - * @return variable's string value - */ -static char *mapreg_readregstr(int64 uid) + * @param uid The variable's unique ID. + * @return The variable's value or NULL if the variable does not exist. + * + **/ +static char *mapreg_get_str_reg(int64 uid) { - struct mapreg_save *m = i64db_get(mapreg->regs.vars, uid); - return m?m->u.str:NULL; + struct mapreg_save *var = i64db_get(mapreg->regs.vars, uid); + return (var != NULL) ? var->u.str : NULL; } /** - * Modifies the value of an integer variable. + * Sets the value of a global integer variable. * - * @param uid variable's unique identifier - * @param val new value - * @retval true value was successfully set - */ -static bool mapreg_setreg(int64 uid, int val) + * @param uid The variable's unique ID. + * @param name The variable's name. + * @param index The variable's array index. + * @param value The variable's new value. + * @return True on success, otherwise false. + * + **/ +static bool mapreg_set_num_db(int64 uid, const char *name, unsigned int index, int value) { - struct mapreg_save *m; - int num = script_getvarid(uid); - unsigned int i = script_getvaridx(uid); - const char* name = script->get_str(num); - - nullpo_retr(true, name); - if( val != 0 ) { - if( (m = i64db_get(mapreg->regs.vars, uid)) ) { - m->u.i = val; - if(name[1] != '@') { - m->save = true; - mapreg->dirty = true; - } - } else { - if( i ) - script->array_update(&mapreg->regs, uid, false); - - m = ers_alloc(mapreg->ers, struct mapreg_save); - - m->u.i = val; - m->uid = uid; - m->save = false; - m->is_string = false; - - if (name[1] != '@' && !mapreg->skip_insert) {// write new variable to database - char tmp_str[(SCRIPT_VARNAME_LENGTH+1)*2+1]; - SQL->EscapeStringLen(map->mysql_handle, tmp_str, name, strnlen(name, SCRIPT_VARNAME_LENGTH+1)); - if( SQL_ERROR == SQL->Query(map->mysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%u','%d')", mapreg->table, tmp_str, i, val) ) - Sql_ShowDebug(map->mysql_handle); - } - i64db_put(mapreg->regs.vars, uid, m); + nullpo_retr(false, name); + Assert_retr(false, *name != '\0'); + Assert_retr(false, strlen(name) <= SCRIPT_VARNAME_LENGTH); + + if (value == 0) + return mapreg->delete_num_db(uid, name, index); + + struct mapreg_save *var = i64db_get(mapreg->regs.vars, uid); + + // Update variable. + if (var != NULL) { + var->u.i = value; + + if (script->is_permanent_variable(name)) { + var->save = true; + mapreg->dirty = true; } - } else { // val == 0 - if( i ) - script->array_update(&mapreg->regs, uid, true); - if( (m = i64db_get(mapreg->regs.vars, uid)) ) { - ers_free(mapreg->ers, m); + + return true; + } + + // Add new variable. + if (index != 0) + script->array_update(&mapreg->regs, uid, false); + + var = ers_alloc(mapreg->ers, struct mapreg_save); + var->u.i = value; + var->uid = uid; + var->save = false; + var->is_string = false; + i64db_put(mapreg->regs.vars, uid, var); + + if (script->is_permanent_variable(name) && !mapreg->skip_insert) { + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); + + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return false; } - i64db_remove(mapreg->regs.vars, uid); - if( name[1] != '@' ) {// Remove from database because it is unused. - if( SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%u'", mapreg->table, name, i) ) - Sql_ShowDebug(map->mysql_handle); + const char *query = "INSERT INTO `%s` (`key`, `index`, `value`) VALUES (?, ?, ?)"; + + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, mapreg->num_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_STRING, name, strlen(name)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 1, SQLDT_UINT32, &index, sizeof(index)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 2, SQLDT_INT32, &value, sizeof(value)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return false; } + + SQL->StmtFree(stmt); } return true; } /** - * Modifies the value of a string variable. + * Deletes a global integer variable. * - * @param uid variable's unique identifier - * @param str new value - * @retval true value was successfully set - */ -static bool mapreg_setregstr(int64 uid, const char *str) + * @param uid The variable's unique ID. + * @param name The variable's name. + * @param index The variable's array index. + * @return True on success, otherwise false. + * + **/ +static bool mapreg_delete_num_db(int64 uid, const char *name, unsigned int index) { - struct mapreg_save *m; - int num = script_getvarid(uid); - unsigned int i = script_getvaridx(uid); - const char* name = script->get_str(num); - - nullpo_retr(true, name); - - if( str == NULL || *str == 0 ) { - if( i ) - script->array_update(&mapreg->regs, uid, true); - if(name[1] != '@') { - if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%u'", mapreg->table, name, i)) - Sql_ShowDebug(map->mysql_handle); + nullpo_retr(false, name); + Assert_retr(false, *name != '\0'); + Assert_retr(false, strlen(name) <= SCRIPT_VARNAME_LENGTH); + + struct mapreg_save *var = i64db_get(mapreg->regs.vars, uid); + + if (var != NULL) + ers_free(mapreg->ers, var); + + if (index != 0) + script->array_update(&mapreg->regs, uid, true); + + i64db_remove(mapreg->regs.vars, uid); + + if (script->is_permanent_variable(name)) { + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); + + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return false; } - if( (m = i64db_get(mapreg->regs.vars, uid)) ) { - if( m->u.str != NULL ) - aFree(m->u.str); - ers_free(mapreg->ers, m); + + const char *query = "DELETE FROM `%s` WHERE `key`=? AND `index`=?"; + + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, mapreg->num_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_STRING, name, strlen(name)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 1, SQLDT_UINT32, &index, sizeof(index)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return false; } - i64db_remove(mapreg->regs.vars, uid); - } else { - if( (m = i64db_get(mapreg->regs.vars, uid)) ) { - if( m->u.str != NULL ) - aFree(m->u.str); - m->u.str = aStrdup(str); - if(name[1] != '@') { - mapreg->dirty = true; - m->save = true; - } - } else { - if( i ) - script->array_update(&mapreg->regs, uid, false); - - m = ers_alloc(mapreg->ers, struct mapreg_save); - - m->uid = uid; - m->u.str = aStrdup(str); - m->save = false; - m->is_string = true; - - if(name[1] != '@' && !mapreg->skip_insert) { //put returned null, so we must insert. - char tmp_str[(SCRIPT_VARNAME_LENGTH+1)*2+1]; - char tmp_str2[SCRIPT_STRING_VAR_LENGTH * 2 + 1]; - SQL->EscapeStringLen(map->mysql_handle, tmp_str, name, strnlen(name, SCRIPT_VARNAME_LENGTH+1)); - SQL->EscapeStringLen(map->mysql_handle, tmp_str2, str, strnlen(str, SCRIPT_STRING_VAR_LENGTH)); - if( SQL_ERROR == SQL->Query(map->mysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%u','%s')", mapreg->table, tmp_str, i, tmp_str2) ) - Sql_ShowDebug(map->mysql_handle); - } - i64db_put(mapreg->regs.vars, uid, m); + + SQL->StmtFree(stmt); + } + + return true; +} + +/** + * Sets the value of a global integer variable or deletes it if passed value is 0. + * + * @param uid The variable's unique ID. + * @param val The variable's new value. + * @return True on success, otherwise false. + * + **/ +static bool mapreg_set_num(int64 uid, int val) +{ + unsigned int index = script_getvaridx(uid); + const char *name = script->get_str(script_getvarid(uid)); + + if (val != 0) + return mapreg->set_num_db(uid, name, index, val); + else + return mapreg->delete_num_db(uid, name, index); +} + +/** + * Sets the value of a global string variable. + * + * @param uid The variable's unique ID. + * @param name The variable's name. + * @param index The variable's array index. + * @param value The variable's new value. + * @return True on success, otherwise false. + * + **/ +static bool mapreg_set_str_db(int64 uid, const char *name, unsigned int index, const char *value) +{ + nullpo_retr(false, name); + Assert_retr(false, *name != '\0'); + Assert_retr(false, strlen(name) <= SCRIPT_VARNAME_LENGTH); + + if (value == NULL || *value == '\0') + return mapreg->delete_str_db(uid, name, index); + + if (script->is_permanent_variable(name)) + Assert_retr(false, strlen(value) <= SCRIPT_STRING_VAR_LENGTH); + + struct mapreg_save *var = i64db_get(mapreg->regs.vars, uid); + + // Update variable. + if (var != NULL) { + if (var->u.str != NULL) + aFree(var->u.str); + + var->u.str = aStrdup(value); + + if (script->is_permanent_variable(name)) { + var->save = true; + mapreg->dirty = true; } + + return true; + } + + // Add new variable. + if (index != 0) + script->array_update(&mapreg->regs, uid, false); + + var = ers_alloc(mapreg->ers, struct mapreg_save); + var->u.str = aStrdup(value); + var->uid = uid; + var->save = false; + var->is_string = true; + i64db_put(mapreg->regs.vars, uid, var); + + if (script->is_permanent_variable(name) && !mapreg->skip_insert) { + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); + + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return false; + } + + const char *query = "INSERT INTO `%s` (`key`, `index`, `value`) VALUES (?, ?, ?)"; + + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, mapreg->str_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_STRING, name, strlen(name)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 1, SQLDT_UINT32, &index, sizeof(index)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 2, SQLDT_STRING, value, strlen(value)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return false; + } + + SQL->StmtFree(stmt); } return true; } /** - * Loads permanent variables from database. - */ -static void script_load_mapreg(void) + * Deletes a global string variable. + * + * @param uid The variable's unique ID. + * @param name The variable's name. + * @param index The variable's array index. + * @return True on success, otherwise false. + * + **/ +static bool mapreg_delete_str_db(int64 uid, const char *name, unsigned int index) +{ + nullpo_retr(false, name); + Assert_retr(false, *name != '\0'); + Assert_retr(false, strlen(name) <= SCRIPT_VARNAME_LENGTH); + + struct mapreg_save *var = i64db_get(mapreg->regs.vars, uid); + + if (var != NULL) { + if (var->u.str != NULL) + aFree(var->u.str); + + ers_free(mapreg->ers, var); + } + + if (index != 0) + script->array_update(&mapreg->regs, uid, true); + + i64db_remove(mapreg->regs.vars, uid); + + if (script->is_permanent_variable(name)) { + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); + + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return false; + } + + const char *query = "DELETE FROM `%s` WHERE `key`=? AND `index`=?"; + + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, mapreg->str_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_STRING, name, strlen(name)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 1, SQLDT_UINT32, &index, sizeof(index)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return false; + } + + SQL->StmtFree(stmt); + } + + return true; +} + +/** + * Sets the value of a global string variable or deletes it if passed value is NULL or an empty string. + * + * @param uid The variable's unique ID. + * @param str The variable's new value. + * @return True on success, otherwise false. + * + **/ +static bool mapreg_set_str(int64 uid, const char *str) +{ + unsigned int index = script_getvaridx(uid); + const char *name = script->get_str(script_getvarid(uid)); + + if (str != NULL && *str != '\0') + return mapreg->set_str_db(uid, name, index, str); + else + return mapreg->delete_str_db(uid, name, index); +} + +/** + * Loads permanent global interger variables from the database. + * + **/ +static void mapreg_load_num_db(void) { - /* - 0 1 2 - +-------------------------+ - | varname | index | value | - +-------------------------+ - */ struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); - char varname[SCRIPT_VARNAME_LENGTH+1]; - int index; - char value[SCRIPT_STRING_VAR_LENGTH + 1]; - uint32 length; - if ( SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `varname`, `index`, `value` FROM `%s`", mapreg->table) - || SQL_ERROR == SQL->StmtExecute(stmt) - ) { + if (stmt == NULL) { SqlStmt_ShowDebug(stmt); - SQL->StmtFree(stmt); return; } - mapreg->skip_insert = true; + const char *query = "SELECT `key`, `index`, `value` FROM `%s`"; + char name[SCRIPT_VARNAME_LENGTH + 1]; + unsigned int index; + int value; - SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &varname, sizeof varname, &length, NULL); - SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &index, sizeof index, NULL, NULL); - SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &value, sizeof value, NULL, NULL); + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, mapreg->num_db) + || SQL_ERROR == SQL->StmtExecute(stmt) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &name, sizeof(name), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UINT32, &index, sizeof(index), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_INT32, &value, sizeof(value), NULL, NULL)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return; + } - while ( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) { - int s = script->add_variable(varname); - int i = index; + if (SQL->StmtNumRows(stmt) < 1) { + SQL->StmtFree(stmt); + return; + } + mapreg->skip_insert = true; + + while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { + int var_key = script->add_variable(name); + int64 uid = reference_uid(var_key, index); - if( i64db_exists(mapreg->regs.vars, reference_uid(s, i)) ) { - ShowWarning("load_mapreg: duplicate! '%s' => '%s' skipping...\n",varname,value); + if (i64db_exists(mapreg->regs.vars, uid)) { + ShowWarning("mapreg_load_num_db: Duplicate! '%s' => '%d' Skipping...\n", name, value); continue; } - if( varname[length-1] == '$' ) { - mapreg->setregstr(reference_uid(s, i),value); - } else { - mapreg->setreg(reference_uid(s, i),atoi(value)); - } + + mapreg->setreg(uid, value); } + mapreg->skip_insert = false; SQL->StmtFree(stmt); +} + +/** + * Loads permanent global string variables from the database. + * + **/ +static void mapreg_load_str_db(void) +{ + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); + + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return; + } + + const char *query = "SELECT `key`, `index`, `value` FROM `%s`"; + char name[SCRIPT_VARNAME_LENGTH + 1]; + unsigned int index; + char value[SCRIPT_STRING_VAR_LENGTH + 1]; + + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, mapreg->str_db) + || SQL_ERROR == SQL->StmtExecute(stmt) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &name, sizeof(name), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UINT32, &index, sizeof(index), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &value, sizeof(value), NULL, NULL)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return; + } + + if (SQL->StmtNumRows(stmt) < 1) { + SQL->StmtFree(stmt); + return; + } + + mapreg->skip_insert = true; + + while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { + int var_key = script->add_variable(name); + int64 uid = reference_uid(var_key, index); + + if (i64db_exists(mapreg->regs.vars, uid)) { + ShowWarning("mapreg_load_str_db: Duplicate! '%s' => '%s' Skipping...\n", name, value); + continue; + } + + mapreg->setregstr(uid, value); + } mapreg->skip_insert = false; + SQL->StmtFree(stmt); +} +/** + * Loads permanent global variables from the database. + * + **/ +static void mapreg_load(void) +{ + mapreg->load_num_db(); + mapreg->load_str_db(); mapreg->dirty = false; } /** - * Saves permanent variables to database. - */ -static void script_save_mapreg(void) + * Saves a permanent global integer variable to the database. + * + * @param name The variable's name. + * @param index The variable's array index. + * @param value The variable's value. + * + **/ +static void mapreg_save_num_db(const char *name, unsigned int index, int value) +{ + nullpo_retv(name); + Assert_retv(*name != '\0'); + Assert_retv(strlen(name) <= SCRIPT_VARNAME_LENGTH); + + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); + + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return; + } + + const char *query = "UPDATE `%s` SET `value`=? WHERE `key`=? AND `index`=? LIMIT 1"; + + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, mapreg->num_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT32, &value, sizeof(value)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 1, SQLDT_STRING, name, strlen(name)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 2, SQLDT_UINT32, &index, sizeof(index)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { + SqlStmt_ShowDebug(stmt); + } + + SQL->StmtFree(stmt); +} + +/** + * Saves a permanent global string variable to the database. + * + * @param name The variable's name. + * @param index The variable's array index. + * @param value The variable's value. + * + **/ +static void mapreg_save_str_db(const char *name, unsigned int index, const char *value) +{ + nullpo_retv(name); + nullpo_retv(value); + Assert_retv(*name != '\0'); + Assert_retv(strlen(name) <= SCRIPT_VARNAME_LENGTH); + Assert_retv(*value != '\0'); + Assert_retv(strlen(value) <= SCRIPT_STRING_VAR_LENGTH); + + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); + + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return; + } + + const char *query = "UPDATE `%s` SET `value`=? WHERE `key`=? AND `index`=? LIMIT 1"; + + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, mapreg->str_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_STRING, value, strlen(value)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 1, SQLDT_STRING, name, strlen(name)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 2, SQLDT_UINT32, &index, sizeof(index)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { + SqlStmt_ShowDebug(stmt); + } + + SQL->StmtFree(stmt); +} + +/** + * Saves permanent global variables to the database. + * + **/ +static void mapreg_save(void) { if (mapreg->dirty) { struct DBIterator *iter = db_iterator(mapreg->regs.vars); - struct mapreg_save *m = NULL; - for (m = dbi_first(iter); dbi_exists(iter); m = dbi_next(iter)) { - if (m->save) { - int num = script_getvarid(m->uid); - int i = script_getvaridx(m->uid); - const char* name = script->get_str(num); - nullpo_retv(name); - if (!m->is_string) { - if( SQL_ERROR == SQL->Query(map->mysql_handle, "UPDATE `%s` SET `value`='%d' WHERE `varname`='%s' AND `index`='%d' LIMIT 1", mapreg->table, m->u.i, name, i) ) - Sql_ShowDebug(map->mysql_handle); - } else { - char tmp_str2[SCRIPT_STRING_VAR_LENGTH * 2 + 1]; - SQL->EscapeStringLen(map->mysql_handle, tmp_str2, m->u.str, safestrnlen(m->u.str, SCRIPT_STRING_VAR_LENGTH)); - if( SQL_ERROR == SQL->Query(map->mysql_handle, "UPDATE `%s` SET `value`='%s' WHERE `varname`='%s' AND `index`='%d' LIMIT 1", mapreg->table, tmp_str2, name, i) ) - Sql_ShowDebug(map->mysql_handle); - } - m->save = false; + struct mapreg_save *var = NULL; + + for (var = dbi_first(iter); dbi_exists(iter); var = dbi_next(iter)) { + if (var->save) { + int index = script_getvaridx(var->uid); + const char *name = script->get_str(script_getvarid(var->uid)); + + if (!var->is_string) + mapreg->save_num_db(name, index, var->u.i); + else + mapreg->save_str_db(name, index, var->u.str); + + var->save = false; } } + dbi_destroy(iter); mapreg->dirty = false; } } /** - * Timer event to auto-save permanent variables. + * Timer event to auto-save permanent global variables. * - * @see timer->do_timer - */ -static int script_autosave_mapreg(int tid, int64 tick, int id, intptr_t data) + * @see timer->do_timer() + * + * @param tid Unused. + * @param tick Unused. + * @param id Unused. + * @param data Unused. + * @return Always 0. + * + **/ +static int mapreg_save_timer(int tid, int64 tick, int id, intptr_t data) { mapreg->save(); return 0; } /** - * Destroys a mapreg_save structure, freeing the contained string, if any. + * Destroys a mapreg_save structure and frees the contained string, if any. * * @see DBApply - */ -static int mapreg_destroyreg(union DBKey key, struct DBData *data, va_list ap) + * + * @param key Unused. + * @param data The DB data holding the mapreg_save data. + * @param ap Unused. + * @return 0 on success, otherwise 1. + * + **/ +static int mapreg_destroy_reg(union DBKey key, struct DBData *data, va_list ap) { - struct mapreg_save *m = NULL; + nullpo_retr(1, data); if (data->type != DB_DATA_PTR) // Sanity check - return 0; + return 1; - m = DB->data2ptr(data); + struct mapreg_save *var = DB->data2ptr(data); - if (m->is_string) { - if (m->u.str) - aFree(m->u.str); - } - ers_free(mapreg->ers, m); + if (var == NULL) + return 1; + + if (var->is_string && var->u.str != NULL) + aFree(var->u.str); + ers_free(mapreg->ers, var); return 0; } /** - * Reloads mapregs, saving to database beforehand. + * Reloads permanent global variables, saving them to the database beforehand. * - * This has the effect of clearing the temporary variables, and - * reloading the permanent ones. - */ + * This has the effect of clearing the temporary global variables and reloading the permanent ones. + * + **/ static void mapreg_reload(void) { mapreg->save(); - mapreg->regs.vars->clear(mapreg->regs.vars, mapreg->destroyreg); - if( mapreg->regs.arrays ) { + if (mapreg->regs.arrays != NULL) { mapreg->regs.arrays->destroy(mapreg->regs.arrays, script->array_free_db); mapreg->regs.arrays = NULL; } @@ -329,86 +636,105 @@ static void mapreg_reload(void) } /** - * Finalizer. - */ + * Loads the mapreg database table names from configuration file. + * + * @param filename Path to configuration file. (Used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * @return True on success, otherwise false. + * + **/ +static bool mapreg_config_read_registry(const char *filename, const struct config_setting_t *config, bool imported) +{ + nullpo_retr(false, filename); + nullpo_retr(false, config); + + bool ret_val = true; + size_t sz = sizeof(mapreg->num_db); + int result = libconfig->setting_lookup_mutable_string(config, "map_reg_num_db", mapreg->num_db, sz); + + if (result != CONFIG_TRUE && !imported) { + ShowError("%s: inter_configuration/database_names/registry/map_reg_num_db was not found in %s!\n", + __func__, filename); + ret_val = false; + } + + sz = sizeof(mapreg->str_db); + result = libconfig->setting_lookup_mutable_string(config, "map_reg_str_db", mapreg->str_db, sz); + + if (result != CONFIG_TRUE && !imported) { + ShowError("%s: inter_configuration/database_names/registry/map_reg_str_db was not found in %s!\n", + __func__, filename); + ret_val = false; + } + + return ret_val; +} + +/** + * Saves permanent global variables to the database and frees all the memory they use afterwards. + * + **/ static void mapreg_final(void) { mapreg->save(); - mapreg->regs.vars->destroy(mapreg->regs.vars, mapreg->destroyreg); - ers_destroy(mapreg->ers); - if( mapreg->regs.arrays ) + if (mapreg->regs.arrays != NULL) mapreg->regs.arrays->destroy(mapreg->regs.arrays, script->array_free_db); } /** - * Initializer. - */ + * Allocates memory for permanent global variables, loads them from the database and initializes the auto-save timer. + * + **/ static void mapreg_init(void) { mapreg->regs.vars = i64db_alloc(DB_OPT_BASE); mapreg->ers = ers_new(sizeof(struct mapreg_save), "mapreg_sql.c::mapreg_ers", ERS_OPT_CLEAN); - mapreg->load(); - - timer->add_func_list(mapreg->save_timer, "mapreg_script_autosave_mapreg"); + timer->add_func_list(mapreg->save_timer, "mapreg_save_timer"); timer->add_interval(timer->gettick() + MAPREG_AUTOSAVE_INTERVAL, mapreg->save_timer, 0, 0, MAPREG_AUTOSAVE_INTERVAL); } /** - * Loads the mapreg configuration file. - * - * @param filename Path to configuration file (used in error and warning messages). - * @param config The current config being parsed. - * @param imported Whether the current config is imported from another file. + * Initializes the mapreg interface defaults. * - * @retval false in case of error. - */ -static bool mapreg_config_read(const char *filename, const struct config_setting_t *config, bool imported) -{ - nullpo_retr(false, filename); - nullpo_retr(false, config); - - if (libconfig->setting_lookup_mutable_string(config, "mapreg_db", mapreg->table, sizeof(mapreg->table)) != CONFIG_TRUE) - return false; - - return true; -} - -/** - * Interface defaults initializer. - */ + **/ void mapreg_defaults(void) { + /** Interface structure. **/ mapreg = &mapreg_s; - /* */ - mapreg->regs.vars = NULL; + /** Interface variables. **/ mapreg->ers = NULL; - mapreg->skip_insert = false; - - safestrncpy(mapreg->table, "mapreg", sizeof(mapreg->table)); - mapreg->dirty = false; - - /* */ + mapreg->regs.vars = NULL; mapreg->regs.arrays = NULL; - - /* */ - mapreg->init = mapreg_init; - mapreg->final = mapreg_final; - - /* */ - mapreg->readreg = mapreg_readreg; - mapreg->readregstr = mapreg_readregstr; - mapreg->setreg = mapreg_setreg; - mapreg->setregstr = mapreg_setregstr; - mapreg->load = script_load_mapreg; - mapreg->save = script_save_mapreg; - mapreg->save_timer = script_autosave_mapreg; - mapreg->destroyreg = mapreg_destroyreg; + mapreg->dirty = false; + mapreg->skip_insert = false; + safestrncpy(mapreg->num_db, "map_reg_num_db", sizeof(mapreg->num_db)); + safestrncpy(mapreg->str_db, "map_reg_str_db", sizeof(mapreg->str_db)); + + /** Interface functions. **/ + mapreg->readreg = mapreg_get_num_reg; + mapreg->readregstr = mapreg_get_str_reg; + mapreg->set_num_db = mapreg_set_num_db; + mapreg->delete_num_db = mapreg_delete_num_db; + mapreg->setreg = mapreg_set_num; + mapreg->set_str_db = mapreg_set_str_db; + mapreg->delete_str_db = mapreg_delete_str_db; + mapreg->setregstr = mapreg_set_str; + mapreg->load_num_db = mapreg_load_num_db; + mapreg->load_str_db = mapreg_load_str_db; + mapreg->load = mapreg_load; + mapreg->save_num_db = mapreg_save_num_db; + mapreg->save_str_db = mapreg_save_str_db; + mapreg->save = mapreg_save; + mapreg->save_timer = mapreg_save_timer; + mapreg->destroyreg = mapreg_destroy_reg; mapreg->reload = mapreg_reload; - mapreg->config_read = mapreg_config_read; - + mapreg->config_read_registry = mapreg_config_read_registry; + mapreg->final = mapreg_final; + mapreg->init = mapreg_init; } diff --git a/src/map/mob.c b/src/map/mob.c index dcbdccedd..b6ba5e5a9 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -3758,6 +3758,8 @@ static int mobskill_event(struct mob_data *md, struct block_list *src, int64 tic res = mob->skill_use(md, tick, MSC_CLOSEDATTACKED); else if (flag&BF_LONG && !(flag&BF_MAGIC)) //Long-attacked should not include magic. res = mob->skill_use(md, tick, MSC_LONGRANGEATTACKED); + else if ((flag & BF_MAGIC) != 0) + res = mob->skill_use(md, tick, MSC_MAGICATTACKED); if (res != 0) //Restore previous target only if skill condition failed to trigger. [Skotlex] @@ -5679,7 +5681,7 @@ static bool mob_skill_db_libconfig_sub_skill(struct config_setting_t *it, int n, } i32 = MSC_ALWAYS; - if (mob->lookup_const(it, "CastCondition", &i32) && (i32 < MSC_ALWAYS || i32 > MSC_SPAWN)) { + if (mob->lookup_const(it, "CastCondition", &i32) && (i32 < MSC_ALWAYS || i32 > MSC_MAGICATTACKED)) { ShowWarning("%s: Invalid skill condition %d for skill id %d (%s) in %s %d, defaulting to MSC_ALWAYS.\n", __func__, i32, skill_id, skill_name, mob_str, mob_id); i32 = MSC_ALWAYS; diff --git a/src/map/mob.h b/src/map/mob.h index 6ad1ce705..59b288fcc 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -321,6 +321,7 @@ enum { MSC_MASTERATTACKED, MSC_ALCHEMIST, MSC_SPAWN, + MSC_MAGICATTACKED, }; /** Special monster(-name) constants used to assign skills to a group of monsters. **/ diff --git a/src/map/npc.c b/src/map/npc.c index 40ec380ee..2f03623e4 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -389,7 +389,10 @@ static int npc_event_export(struct npc_data *nd, int i) Assert_ret(i >= 0 && i < nd->u.scr.label_list_num); lname = nd->u.scr.label_list[i].name; pos = nd->u.scr.label_list[i].pos; - if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) { + + if ((nd->u.scr.label_list[i].flags & LABEL_IS_EXTERN) != 0 + && ((nd->u.scr.label_list[i].flags & LABEL_IS_USERFUNC) == 0 + || script->config.functions_as_events)) { struct event_data *ev; struct linkdb_node **label_linkdb = NULL; char buf[EVENT_NAME_LENGTH]; @@ -3054,11 +3057,11 @@ static int npc_unload(struct npc_data *nd, bool single, bool unload_mobs) aFree(nd->u.shop.shop_item); /// src check for duplicate shops. [Orcao] } else if (nd->subtype == SCRIPT) { char evname[EVENT_NAME_LENGTH]; - + snprintf(evname, ARRAYLENGTH(evname), "%s::OnNPCUnload", nd->exname); struct event_data *ev = strdb_get(npc->ev_db, evname); - + if (ev != NULL) script->run_npc(nd->u.scr.script, ev->pos, 0, nd->bl.id); /// Run OnNPCUnload. @@ -3665,6 +3668,7 @@ static void npc_convertlabel_db(struct npc_label_list *label_list, const char *f for( i = 0; i < script->label_count; i++ ) { const char* lname = script->get_str(script->labels[i].key); int lpos = script->labels[i].pos; + enum script_label_flags flags = script->labels[i].flags; struct npc_label_list* label; const char *p; size_t len; @@ -3686,6 +3690,7 @@ static void npc_convertlabel_db(struct npc_label_list *label_list, const char *f safestrncpy(label->name, lname, sizeof(label->name)); label->pos = lpos; + label->flags = flags; } } @@ -5606,7 +5611,7 @@ static int npc_reload(void) npc->npc_last_npd = NULL; npc->npc_last_path = NULL; npc->npc_last_ref = NULL; - + const int npc_new_min = npc->npc_id; struct s_mapiterator *iter = mapit_geteachiddb(); diff --git a/src/map/npc.h b/src/map/npc.h index 1585a2bc8..16d7a984b 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -56,9 +56,15 @@ enum npc_shop_types { struct npc_timerevent_list { int timer,pos; }; + +/** list of labels within a NPC (used internally by the label db) */ struct npc_label_list { + /** label name */ char name[NAME_LENGTH]; + /** start point within the script */ int pos; + /** optional label flags */ + enum script_label_flags flags; }; struct npc_barter_currency { diff --git a/src/map/pc.h b/src/map/pc.h index e560df549..8d1cd0ca4 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -629,10 +629,6 @@ END_ZEROED_BLOCK; uint8 lang_id; - // temporary debugging of bug #3504 - const char* delunit_prevfile; - int delunit_prevline; - // HatEffect VECTOR_DECL(int) hatEffectId; diff --git a/src/map/script.c b/src/map/script.c index 347ef3d2a..e4a57194d 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -848,79 +848,134 @@ static const char *parse_callfunc(const char *p, int require_paren, int is_custo nullpo_retr(NULL, p); // is need add check for arg null pointer below? - func = script->add_word(p); - if (script->str_data[func].type == C_FUNC) { - script->syntax.nested_call++; - if (script->syntax.last_func != -1) { - if (script->str_data[func].val == script->buildin_lang_macro_offset) { - script->syntax.lang_macro_active = true; - macro = true; - } else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) { - script->syntax.lang_macro_fmtstring_active = true; - macro = true; - } + + if (*p == '"') { + p2 = ++p; // jump to the start of the word + + // find the closing quote + while (*p2 != '"') { + ++p2; } - if( !macro ) { - // buildin function + if (p2[1] == ':' && p2[2] == ':') { + func = script->add_str("callfunctionofnpc"); + arg = "*"; // we already take care of the "vs" part of "vs*" + + script->syntax.nested_call++; script->syntax.last_func = script->str_data[func].val; script->addl(func); script->addc(C_ARG); - } - arg = script->buildin[script->str_data[func].val]; - if (script->str_data[func].deprecated) - DeprecationWarning(p); - if( !arg ) arg = &null_arg; // Use a dummy, null string - } else if( script->str_data[func].type == C_USERFUNC || script->str_data[func].type == C_USERFUNC_POS ) { - // script defined function - script->addl(script->buildin_callsub_ref); - script->addc(C_ARG); - script->addl(func); - arg = script->buildin[script->str_data[script->buildin_callsub_ref].val]; - if( *arg == 0 ) - disp_error_message("parse_callfunc: callsub has no arguments, please review its definition",p); - if( *arg != '*' ) - ++arg; // count func as argument + script->addc(C_STR); + do { + script->addb(*p++); // npc name + } while (p < p2); + script->addb(0); + + p = p2 + 3; // skip to start of func name + p2 = script->skip_word(p); + + script->addc(C_STR); + do { + script->addb(*p++); // func name + } while (p < p2); + script->addb(0); + + p = p2; // skip to just before the () + } else { + disp_error_message("script:parse_callfunc: invalid public function call syntax!", p2 + 1); + } } else { + func = script->add_word(p); + if (script->str_data[func].type == C_FUNC) { + script->syntax.nested_call++; + + if (script->syntax.last_func != -1) { + if (script->str_data[func].val == script->buildin_lang_macro_offset) { + script->syntax.lang_macro_active = true; + macro = true; + } else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) { + script->syntax.lang_macro_fmtstring_active = true; + macro = true; + } + } + + if (!macro) { + // buildin function + script->syntax.last_func = script->str_data[func].val; + script->addl(func); + script->addc(C_ARG); + } + + arg = script->buildin[script->str_data[func].val]; + + if (script->str_data[func].deprecated == 1) { + DeprecationWarning(p); + } + + if (arg == NULL) { + arg = &null_arg; // Use a dummy, null string + } + } else if (script->str_data[func].type == C_USERFUNC || script->str_data[func].type == C_USERFUNC_POS) { + // script defined function + script->addl(script->buildin_callsub_ref); + script->addc(C_ARG); + script->addl(func); + arg = script->buildin[script->str_data[script->buildin_callsub_ref].val]; + + if (*arg == 0) { + disp_error_message("script:parse_callfunc: callsub has no arguments, please review its definition", p); + } + + if (*arg != '*') { + ++arg; // count func as argument + } + } else { #ifdef SCRIPT_CALLFUNC_CHECK - const char* name = script->get_str(func); - if( !is_custom && strdb_get(script->userfunc_db, name) == NULL ) { + const char *name = script->get_str(func); + if (is_custom == 0 && strdb_get(script->userfunc_db, name) == NULL) { #endif - disp_error_message("parse_line: expect command, missing function name or calling undeclared function",p); + disp_error_message("script:parse_callfunc: expect command, missing function name or calling undeclared function", p); #ifdef SCRIPT_CALLFUNC_CHECK - } else {; - script->addl(script->buildin_callfunc_ref); - script->addc(C_ARG); - script->addc(C_STR); - while( *name ) script->addb(*name ++); - script->addb(0); - arg = script->buildin[script->str_data[script->buildin_callfunc_ref].val]; - if( *arg != '*' ) ++ arg; - } + } else { + script->addl(script->buildin_callfunc_ref); + script->addc(C_ARG); + script->addc(C_STR); + + while (*name != '\0') { + script->addb(*name++); + } + + script->addb(0); + arg = script->buildin[script->str_data[script->buildin_callfunc_ref].val]; + + if (*arg != '*') { + ++ arg; + } + } #endif + } } p = script->skip_word(p); p = script->skip_space(p); script->syntax.curly[script->syntax.curly_count].type = TYPE_ARGLIST; script->syntax.curly[script->syntax.curly_count].count = 0; - if( *p == ';' ) - {// <func name> ';' + + if (*p == ';') { + // <func name> ';' script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_NO_PAREN; - } else if( *p == '(' && *(p2=script->skip_space(p+1)) == ')' ) - {// <func name> '(' ')' + } else if (*p == '(' && *(p2 = script->skip_space(p + 1)) == ')') { + // <func name> '(' ')' script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_PAREN; p = p2; - /* - } else if( 0 && require_paren && *p != '(' ) - {// <func name> - script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_NO_PAREN; - */ - } else {// <func name> <arg list> - if( require_paren ) { - if( *p != '(' ) - disp_error_message("need '('",p); + } else { + // <func name> <arg list> + if (require_paren == 1) { + if (*p != '(') { + disp_error_message("script:parse_callfunc: need '('", p); + } + ++p; // skip '(' script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_PAREN; } else if( *p == '(' ) { @@ -928,41 +983,65 @@ static const char *parse_callfunc(const char *p, int require_paren, int is_custo } else { script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_NO_PAREN; } + ++script->syntax.curly_count; - while( *arg ) { - p2=script->parse_subexpr(p,-1); - if( p == p2 ) - break; // not an argument - if( *arg != '*' ) - ++arg; // next argument - p=script->skip_space(p2); - if( *arg == 0 || *p != ',' ) - break; // no more arguments + while (*arg != '\0') { + p2 = script->parse_subexpr(p, -1); + + if (p == p2) { + // not an argument + break; + } + + if (*arg != '*') { + // next argument + ++arg; + } + + p = script->skip_space(p2); + + if (*arg == 0 || *p != ',') { + // no more arguments + break; + } + ++p; // skip comma } + --script->syntax.curly_count; } - if( arg && *arg && *arg != '?' && *arg != '*' ) - disp_error_message2("parse_callfunc: not enough arguments, expected ','", p, script->config.warn_func_mismatch_paramnum); - if( script->syntax.curly[script->syntax.curly_count].type != TYPE_ARGLIST ) - disp_error_message("parse_callfunc: DEBUG last curly is not an argument list",p); - if( script->syntax.curly[script->syntax.curly_count].flag == ARGLIST_PAREN ) { - if( *p != ')' ) - disp_error_message("parse_callfunc: expected ')' to close argument list",p); + + if (arg != NULL && *arg != '\0' && *arg != '?' && *arg != '*') { + disp_error_message2("script:parse_callfunc: not enough arguments, expected ','", p, script->config.warn_func_mismatch_paramnum); + } + + if (script->syntax.curly[script->syntax.curly_count].type != TYPE_ARGLIST) { + disp_error_message("parse_callfunc: DEBUG last curly is not an argument list", p); + } + + if (script->syntax.curly[script->syntax.curly_count].flag == ARGLIST_PAREN) { + if (*p != ')') { + disp_error_message("script:parse_callfunc: expected ')' to close argument list", p); + } + ++p; - if (script->str_data[func].val == script->buildin_lang_macro_offset) + if (script->str_data[func].val == script->buildin_lang_macro_offset) { script->syntax.lang_macro_active = false; - else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) + } else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) { script->syntax.lang_macro_fmtstring_active = false; + } } if (!macro) { - if (0 == --script->syntax.nested_call) + if (0 == --script->syntax.nested_call) { script->syntax.last_func = -1; + } + script->addc(C_FUNC); } + return p; } @@ -1230,16 +1309,29 @@ static int script_string_dup(char *str) *------------------------------------------*/ static const char *parse_simpleexpr(const char *p) { - p=script->skip_space(p); + p = script->skip_space(p); nullpo_retr(NULL, p); - if (*p == ';' || *p == ',') - disp_error_message("parse_simpleexpr: unexpected end of expression",p); + + if (*p == ';' || *p == ',') { + disp_error_message("script:parse_simpleexpr: unexpected end of expression", p); + } + if (*p == '(') { return script->parse_simpleexpr_paren(p); } else if (is_number(p)) { return script->parse_simpleexpr_number(p); } else if(*p == '"') { + const char *p2 = p + 1; + + while (*p2 != '"') { + ++p2; + } + + if (p2[1] == ':' && p2[2] == ':') { + return script->parse_callfunc(p, 1, 0); // XXX: why does callfunc use int for booleans? + } + return script->parse_simpleexpr_string(p); } else { return script->parse_simpleexpr_name(p); @@ -1577,6 +1669,85 @@ static const char *parse_line(const char *p) return p; } +/** + * parses a local function expression + * + * expects these formats: + * function <name>; + * function <name> { <script> } + * + * this is invoked by script->parse_syntax() after checking whether the function + * is public or not + * + * @param p - a pointer to the start of the function expression + * @param is_public - whether this function should be accessible from outside the NPC scope + */ +static const char *parse_syntax_function (const char *p, bool is_public) +{ + const char *func_name = script->skip_space(p); // the name of the local function + p = script->skip_word(func_name); + + if (p == func_name) { + disp_error_message("script:parse_syntax_function: function name is missing or invalid", p); + } + + const char *p2 = script->skip_space(p); + + if (*p2 == ';') { + // function <name> ; + // function declaration - just register the name + int l = script->add_word(func_name); + + if (script->str_data[l].type == C_NOP) { + // register only, if the name was not used by something else + script->str_data[l].type = C_USERFUNC; + } else if (script->str_data[l].type != C_USERFUNC) { + disp_error_message("script:parse_syntax_function: function name is already in use", func_name); + } + + // Close condition of if, for, while + p = script->parse_syntax_close(p2 + 1); + return p; + } else if (*p2 == '{') { + // function <name> <line/block of code> + script->syntax.curly[script->syntax.curly_count].type = TYPE_USERFUNC; + script->syntax.curly[script->syntax.curly_count].count = 1; + script->syntax.curly[script->syntax.curly_count].index = script->syntax.index++; + script->syntax.curly[script->syntax.curly_count].flag = 0; + ++script->syntax.curly_count; + + // Jump over the function code + char label[256]; + sprintf(label, "goto __FN%x_FIN;", (unsigned int)script->syntax.curly[script->syntax.curly_count - 1].index); + script->syntax.curly[script->syntax.curly_count].type = TYPE_NULL; + ++script->syntax.curly_count; + script->parse_line(label); + --script->syntax.curly_count; + + // Set the position of the function (label) + int l = script->add_word(func_name); + + if (script->str_data[l].type == C_NOP || script->str_data[l].type == C_USERFUNC) { + // register only, if the name was not used by something else + script->str_data[l].type = C_USERFUNC; + script->set_label(l, VECTOR_LENGTH(script->buf), p); + + if ((script->parse_options & SCRIPT_USE_LABEL_DB) != 0) { + script->label_add(l, VECTOR_LENGTH(script->buf), + LABEL_IS_USERFUNC | (is_public ? LABEL_IS_EXTERN : 0)); + } + } else { + disp_error_message("script:parse_syntax_function: function name is already in use", func_name); + } + + return script->skip_space(p); + } else { + disp_error_message("script:parse_syntax_function: expected ';' or '{' at function syntax", p); + } + + return p; +} + // { ... } Closing process static const char *parse_curly_close(const char *p) { @@ -1920,65 +2091,11 @@ static const char *parse_syntax(const char *p) script->set_label(l, VECTOR_LENGTH(script->buf), p); return p; } else if( p2 - p == 8 && strncmp(p, "function", 8) == 0 ) { - // internal script function - const char *func_name; - - func_name = script->skip_space(p2); - p = script->skip_word(func_name); - if( p == func_name ) - disp_error_message("parse_syntax:function: function name is missing or invalid", p); - p2 = script->skip_space(p); - if( *p2 == ';' ) - {// function <name> ; - // function declaration - just register the name - int l; - l = script->add_word(func_name); - if( script->str_data[l].type == C_NOP )// register only, if the name was not used by something else - script->str_data[l].type = C_USERFUNC; - else if( script->str_data[l].type == C_USERFUNC ) - ; // already registered - else - disp_error_message("parse_syntax:function: function name is invalid", func_name); - - // Close condition of if, for, while - p = script->parse_syntax_close(p2 + 1); - return p; - } - else if(*p2 == '{') - {// function <name> <line/block of code> - char label[256]; - int l; - - script->syntax.curly[script->syntax.curly_count].type = TYPE_USERFUNC; - script->syntax.curly[script->syntax.curly_count].count = 1; - script->syntax.curly[script->syntax.curly_count].index = script->syntax.index++; - script->syntax.curly[script->syntax.curly_count].flag = 0; - ++script->syntax.curly_count; - - // Jump over the function code - sprintf(label, "goto __FN%x_FIN;", (unsigned int)script->syntax.curly[script->syntax.curly_count-1].index); - script->syntax.curly[script->syntax.curly_count].type = TYPE_NULL; - ++script->syntax.curly_count; - script->parse_line(label); - --script->syntax.curly_count; - - // Set the position of the function (label) - l=script->add_word(func_name); - if( script->str_data[l].type == C_NOP || script->str_data[l].type == C_USERFUNC )// register only, if the name was not used by something else - { - script->str_data[l].type = C_USERFUNC; - script->set_label(l, VECTOR_LENGTH(script->buf), p); - if( script->parse_options&SCRIPT_USE_LABEL_DB ) - script->label_add(l, VECTOR_LENGTH(script->buf)); - } - else - disp_error_message("parse_syntax:function: function name is invalid", func_name); - - return script->skip_space(p); - } - else - { - disp_error_message("expect ';' or '{' at function syntax",p); + // local function not marked as public or private + if (script->config.functions_private_by_default) { + return script->parse_syntax_function(p2, false); + } else { + return script->parse_syntax_function(p2, true); } } break; @@ -2006,6 +2123,26 @@ static const char *parse_syntax(const char *p) return p; } break; + case 'p': + case 'P': + if (p2 - p == 6 && strncmp(p, "public", 6) == 0) { + p2 = script->skip_space(p2); + const char *p3 = script->skip_word(p2); + + if (p3 - p2 == 8 && strncmp(p2, "function", 8) == 0) { + // local function explicitly marked as public + return script->parse_syntax_function(p3, true); + } + } else if (p2 - p == 7 && strncmp(p, "private", 7) == 0) { + p2 = script->skip_space(p2); + const char *p3 = script->skip_word(p2); + + if (p3 - p2 == 8 && strncmp(p2, "function", 8) == 0) { + // local function explicitly marked as private + return script->parse_syntax_function(p3, false); + } + } + break; case 's': case 'S': if( p2 - p == 6 && strncmp(p, "switch", 6) == 0 ) { @@ -2668,25 +2805,32 @@ static struct script_code *parse_script(const char *src, const char *file, int l } } - while( script->syntax.curly_count != 0 || *p != end ) - { - if( *p == '\0' ) - disp_error_message("unexpected end of script",p); + while (script->syntax.curly_count != 0 || *p != end) { + if (*p == '\0') { + disp_error_message("script:parse_script: unexpected end of script", p); + } + // Special handling only label - tmpp=script->skip_space(script->skip_word(p)); - if(*tmpp==':' && !(strncmp(p,"default:",8) == 0 && p + 7 == tmpp)) { - i=script->add_word(p); + tmpp = script->skip_space(script->skip_word(p)); + + if (*tmpp == ':' && !(strncmp(p, "default:", 8) == 0 && p + 7 == tmpp) + && !(strncmp(p, "function", 8) == 0 && script->skip_space(p + 8) == tmpp)) { + i = script->add_word(p); script->set_label(i, VECTOR_LENGTH(script->buf), p); - if( script->parse_options&SCRIPT_USE_LABEL_DB ) - script->label_add(i, VECTOR_LENGTH(script->buf)); - p=tmpp+1; - p=script->skip_space(p); + + if ((script->parse_options & SCRIPT_USE_LABEL_DB) != 0) { + bool is_extern = ((p[0] == 'O' || p[0] == 'o') && (p[1] == 'N' || p[1] == 'n')); + script->label_add(i, VECTOR_LENGTH(script->buf), is_extern ? LABEL_IS_EXTERN : 0); + } + + p = tmpp + 1; + p = script->skip_space(p); continue; } // All other lumped - p=script->parse_line(p); - p=script->skip_space(p); + p = script->parse_line(p); + p = script->skip_space(p); script->parse_nextline(false, p); } @@ -4866,6 +5010,8 @@ static bool script_config_read(const char *filename, bool imported) libconfig->setting_lookup_bool_real(setting, "warn_func_mismatch_paramnum", &script->config.warn_func_mismatch_paramnum); libconfig->setting_lookup_bool_real(setting, "warn_func_mismatch_argtypes", &script->config.warn_func_mismatch_argtypes); + libconfig->setting_lookup_bool_real(setting, "functions_private_by_default", &script->config.functions_private_by_default); + libconfig->setting_lookup_bool_real(setting, "functions_as_events", &script->config.functions_as_events); libconfig->setting_lookup_int(setting, "check_cmdcount", &script->config.check_cmdcount); libconfig->setting_lookup_int(setting, "check_gotocount", &script->config.check_gotocount); libconfig->setting_lookup_int(setting, "input_min_value", &script->config.input_min_value); @@ -6435,6 +6581,111 @@ static BUILDIN(callfunc) return true; } + +/** + * Calls a local function within a NPC as if it was part of the current scope. + * Resumes execution in the previous scope once the NPC function returns. This + * is essentially a clone of buildin_callsub that can run in arbitrary NPCs. + * + * Usage: + * callfunctionofnpc("<npc name>", "<function name>"{, <arg>...}) + * callfunctionofnpc(<npc id>, "<function name>"{, <arg>...}) + * + * This buildin is also used internally by this syntax: + * "<npc name>"::<function name>({<arg>...}) + */ +static BUILDIN(callfunctionofnpc) { + struct npc_data *nd = NULL; + + if (script_isstring(st, 2)) { + nd = npc->name2id(script_getstr(st, 2)); + } else { + nd = map->id2nd(script_getnum(st, 2)); + } + + if (nd == NULL) { + ShowError("script:callfunctionofnpc: NPC not found.\n"); + st->state = END; + return false; + } + + const char *function_name = script_getstr(st, 3); + int pos = -1; + + // find the function label within the label list of the NPC + for (int i = 0; i < nd->u.scr.label_list_num; ++i) { + if (strcmp(nd->u.scr.label_list[i].name, function_name) == 0) { + if ((nd->u.scr.label_list[i].flags & LABEL_IS_EXTERN) != 0 + && (nd->u.scr.label_list[i].flags & LABEL_IS_USERFUNC) != 0) { + // function label found: set the start location + pos = nd->u.scr.label_list[i].pos; + } else if ((nd->u.scr.label_list[i].flags & LABEL_IS_USERFUNC) != 0) { + ShowError("script:callfunctionofnpc: function '%s' is not marked as public in NPC '%s'.\n", function_name, nd->name); + st->state = END; + return false; + } + break; + } + } + + if (pos < 0) { + ShowError("script:callfunctionofnpc: function '%s' not found in NPC '%s'!\n", function_name, nd->name); + st->state = END; + return false; + } + + // alloc a reg_db reference of the current scope for the new scope + struct reg_db *ref = (struct reg_db *)aCalloc(sizeof(struct reg_db), 2); + // scope variables (.@var) + ref[0].vars = st->stack->scope.vars; + ref[0].arrays = st->stack->scope.arrays; + // npc variables (.var) + ref[1].vars = st->script->local.vars; + ref[1].arrays = st->script->local.arrays; + + int i = 0; + + // make sure the arguments we push retain their current reg_db references: + // this allows to do things like set(getarg(0), ...) + for (i = st->start + 4; i < st->end; i++) { + struct script_data *data = script->push_copy(st->stack, i); + + if (data_isreference(data) && data->ref == NULL) { + const char *name = reference_getname(data); + + if (name[0] == '.') { + data->ref = (name[1] == '@' ? &ref[0] : &ref[1]); + } + } + } + + // save the previous scope + struct script_retinfo *ri = NULL; + CREATE(ri, struct script_retinfo, 1); + ri->script = st->script; // script code + ri->scope.vars = st->stack->scope.vars; // scope variables + ri->scope.arrays = st->stack->scope.arrays; // scope arrays + ri->pos = st->pos; // script location + ri->nargs = i - st->start - 4; // argument count + ri->defsp = st->stack->defsp; // default stack pointer + script->push_retinfo(st->stack, ri, ref); + + // change the current scope to the scope of the function + st->pos = pos; + st->script = nd->u.scr.script; + st->stack->defsp = st->stack->sp; + st->state = GOTO; + st->stack->scope.vars = i64db_alloc(DB_OPT_RELEASE_DATA); + st->stack->scope.arrays = idb_alloc(DB_OPT_BASE); + + // make sure local reg_db of the other NPC is initialized + if (st->script->local.vars == NULL) { + st->script->local.vars = i64db_alloc(DB_OPT_RELEASE_DATA); + } + + return true; +} + /*========================================== * subroutine call *------------------------------------------*/ @@ -27428,6 +27679,8 @@ static void script_parse_builtin(void) BUILDIN_DEF(identify, "i"), BUILDIN_DEF(identifyidx, "i"), BUILDIN_DEF(openlapineddukddakboxui, "i"), + + BUILDIN_DEF(callfunctionofnpc, "vs*"), }; int i, len = ARRAYLENGTH(BUILDIN); RECREATE(script->buildin, char *, script->buildin_count + len); // Pre-alloc to speed up @@ -27439,7 +27692,7 @@ static void script_parse_builtin(void) #undef BUILDIN_DEF #undef BUILDIN_DEF2 -static void script_label_add(int key, int pos) +static void script_label_add(int key, int pos, enum script_label_flags flags) { int idx = script->label_count; @@ -27450,6 +27703,7 @@ static void script_label_add(int key, int pos) script->labels[idx].key = key; script->labels[idx].pos = pos; + script->labels[idx].flags = flags; script->label_count++; } @@ -27868,6 +28122,7 @@ static void script_hardcoded_constants(void) script->set_constant("MSC_MASTERATTACKED", MSC_MASTERATTACKED, false, false); script->set_constant("MSC_ALCHEMIST", MSC_ALCHEMIST, false, false); script->set_constant("MSC_SPAWN", MSC_SPAWN, false, false); + script->set_constant("MSC_MAGICATTACKED", MSC_MAGICATTACKED, false, false); script->constdb_comment("monster skill targets"); script->set_constant("MST_TARGET", MST_TARGET, false, false); @@ -28261,6 +28516,7 @@ void script_defaults(void) script->parse_syntax_close = parse_syntax_close; script->parse_syntax_close_sub = parse_syntax_close_sub; script->parse_syntax = parse_syntax; + script->parse_syntax_function = parse_syntax_function; script->get_com = get_com; script->get_num = get_num; script->op2name = script_op2name; @@ -28365,6 +28621,8 @@ void script_defaults(void) script->config.ontouch_name = "OnTouch_"; //ontouch_name (runs on first visible char to enter area, picks another char if the first char leaves) script->config.ontouch2_name = "OnTouch"; //ontouch2_name (run whenever a char walks into the OnTouch area) script->config.onuntouch_name = "OnUnTouch"; //onuntouch_name (run whenever a char walks from the OnTouch area) + script->config.functions_private_by_default = true; + script->config.functions_as_events = false; // for ENABLE_CASE_CHECK script->calc_hash_ci = calc_hash_ci; diff --git a/src/map/script.h b/src/map/script.h index 5fa81dc0e..df5297ac0 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -584,6 +584,8 @@ enum itemskill_flag { struct Script_Config { bool warn_func_mismatch_argtypes; bool warn_func_mismatch_paramnum; + bool functions_private_by_default; + bool functions_as_events; int check_cmdcount; int check_gotocount; int input_min_value; @@ -725,8 +727,14 @@ struct str_data_struct { uint8 deprecated : 1; }; +/** a label within a script (does not use the label db) */ struct script_label_entry { - int key,pos; + /** label name (held within str_data) */ + int key; + /** position within the script */ + int pos; + /** optional flags for the label */ + enum script_label_flags flags; }; struct script_syntax_data { @@ -917,7 +925,7 @@ struct script_interface { void (*set_constant) (const char *name, int value, bool is_parameter, bool is_deprecated); void (*set_constant2) (const char *name, int value, bool is_parameter, bool is_deprecated); bool (*get_constant) (const char* name, int* value); - void (*label_add)(int key, int pos); + void (*label_add)(int key, int pos, enum script_label_flags flags); void (*run) (struct script_code *rootscript, int pos, int rid, int oid); void (*run_npc) (struct script_code *rootscript, int pos, int rid, int oid); void (*run_pet) (struct script_code *rootscript, int pos, int rid, int oid); @@ -948,10 +956,11 @@ struct script_interface { int (*queue_create) (void); bool (*queue_clear) (int idx); /* */ - const char * (*parse_curly_close) (const char *p); - const char * (*parse_syntax_close) (const char *p); - const char * (*parse_syntax_close_sub) (const char *p, int *flag); - const char * (*parse_syntax) (const char *p); + const char *(*parse_curly_close) (const char *p); + const char *(*parse_syntax_close) (const char *p); + const char *(*parse_syntax_close_sub) (const char *p, int *flag); + const char *(*parse_syntax) (const char *p); + const char *(*parse_syntax_function) (const char *p, bool is_public); c_op (*get_com) (const struct script_buf *scriptbuf, int *pos); int (*get_num) (const struct script_buf *scriptbuf, int *pos); const char* (*op2name) (int op); diff --git a/src/map/skill.c b/src/map/skill.c index caa1a0f29..e3fa6b0a1 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -12535,7 +12535,7 @@ static struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 if (!group->alive_count) { //No cells? Something that was blocked completely by Land Protector? - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); return NULL; } @@ -12651,7 +12651,7 @@ static int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int unsigned short m = sg->val3; if( --count <= 0 ) - skill->del_unitgroup(sg,ALC_MARK); + skill->del_unitgroup(sg); if ( map->mapindex2mapid(sg->val3) == sd->bl.m && x == sd->bl.x && y == sd->bl.y ) working = 1;/* we break it because officials break it, lovely stuff. */ @@ -12957,7 +12957,7 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b sg->val1--; } if (sg->val1 <= 0) - skill->del_unitgroup(sg, ALC_MARK); + skill->del_unitgroup(sg); break; case UNT_EVILLAND: @@ -16644,7 +16644,7 @@ static int skill_clear_group(struct block_list *bl, int flag) } for (i=0;i<count;i++) - skill->del_unitgroup(group[i],ALC_MARK); + skill->del_unitgroup(group[i]); return count; } @@ -17292,7 +17292,7 @@ static int skill_delunit(struct skill_unit *su) map->deliddb(&su->bl); idb_remove(skill->unit_db, su->bl.id); if(--group->alive_count==0) - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); return 0; } @@ -17351,7 +17351,7 @@ static struct skill_unit_group *skill_initunitgroup(struct block_list *src, int j = i; } } - skill->del_unitgroup(ud->skillunit[j],ALC_MARK); + skill->del_unitgroup(ud->skillunit[j]); //Since elements must have shifted, we use the last slot. i = MAX_SKILLUNITGROUP-1; } @@ -17390,18 +17390,13 @@ static struct skill_unit_group *skill_initunitgroup(struct block_list *src, int /*========================================== * *------------------------------------------*/ -static int skill_delunitgroup(struct skill_unit_group *group, const char *file, int line, const char *func) +static int skill_delunitgroup(struct skill_unit_group *group) { struct block_list* src; struct unit_data *ud; int i,j; struct map_session_data *sd = NULL; - if( group == NULL ) { - ShowDebug("skill_delunitgroup: group is NULL (source=%s:%d, %s)! Please report this! (#3504)\n", file, line, func); - return 0; - } - src = map->id2bl(group->src_id); ud = unit->bl2ud(src); sd = BL_CAST(BL_PC, src); @@ -17533,7 +17528,7 @@ static int skill_clear_unitgroup(struct block_list *src) nullpo_ret(ud); while (ud->skillunit[0]) - skill->del_unitgroup(ud->skillunit[0],ALC_MARK); + skill->del_unitgroup(ud->skillunit[0]); return 1; } diff --git a/src/map/skill.h b/src/map/skill.h index 4dbbaf147..651a355cf 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -2020,7 +2020,7 @@ struct skill_interface { struct skill_unit *(*initunit) (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2); int (*delunit) (struct skill_unit *su); struct skill_unit_group *(*init_unitgroup) (struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, int limit, int interval); - int (*del_unitgroup) (struct skill_unit_group *group, const char* file, int line, const char* func); + int (*del_unitgroup) (struct skill_unit_group *group); int (*clear_unitgroup) (struct block_list *src); int (*clear_group) (struct block_list *bl, int flag); int (*unit_onplace) (struct skill_unit *src, struct block_list *bl, int64 tick); diff --git a/src/map/status.c b/src/map/status.c index dc49f7e4e..b0cb07d11 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -1201,7 +1201,7 @@ static int status_damage(struct block_list *src, struct block_list *target, int6 if ((sce=sc->data[SC_GRAVITATION]) && sce->val3 == BCT_SELF) { struct skill_unit_group* sg = skill->id2group(sce->val4); if (sg) { - skill->del_unitgroup(sg, ALC_MARK); + skill->del_unitgroup(sg); sce->val4 = 0; status_change_end(target, SC_GRAVITATION, INVALID_TIMER); } @@ -10788,7 +10788,7 @@ static int status_change_clear(struct block_list *bl, int type) /*========================================== * Special condition we want to effectuate, check before ending a status. *------------------------------------------*/ -static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, const char *file, int line) +static int status_change_end_(struct block_list *bl, enum sc_type type, int tid) { struct map_session_data *sd; struct status_change *sc; @@ -10958,23 +10958,9 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, break; case SC_DANCING: { - const char* prevfile = "<unknown>"; - int prevline = 0; struct map_session_data *dsd; struct status_change_entry *dsc; - if (sd) { - if (sd->delunit_prevfile) { - // initially this is NULL, when a character logs in - prevfile = sd->delunit_prevfile; - prevline = sd->delunit_prevline; - } else { - prevfile = "<none>"; - } - sd->delunit_prevfile = file; - sd->delunit_prevline = line; - } - if (sce->val4 && sce->val4 != BCT_SELF && (dsd=map->id2sd(sce->val4)) != NULL) { // end status on partner as well dsc = dsd->sc.data[SC_DANCING]; @@ -10990,19 +10976,10 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, // erase associated land skill struct skill_unit_group *group = skill->id2group(sce->val2); - if (group == NULL) { - ShowDebug("status_change_end: SC_DANCING is missing skill unit group (val1=%d, val2=%d, val3=%d, val4=%d, timer=%d, tid=%d, char_id=%d, map=%s, x=%d, y=%d, prev=%s:%d, from=%s:%d). Please report this! (#3504)\n", - sce->val1, sce->val2, sce->val3, sce->val4, sce->timer, tid, - sd ? sd->status.char_id : 0, - mapindex_id2name(map_id2index(bl->m)), bl->x, bl->y, - prevfile, prevline, - file, line); - } - sce->val2 = 0; if( group ) - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); } if ((sce->val1&0xFFFF) == CG_MOONLIT) @@ -11106,7 +11083,7 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, struct skill_unit_group* group = skill->id2group(sce->val3); sce->val3 = 0; if( group ) - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); } break; case SC_HERMODE: @@ -11125,7 +11102,7 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, struct skill_unit_group* group = skill->id2group(sce->val4); sce->val4 = 0; if( group ) /* might have been cleared before status ended, e.g. land protector */ - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); } break; case SC_KAAHI: @@ -11223,7 +11200,7 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, struct skill_unit_group* group = skill->id2group(sce->val2); sce->val2 = 0; if (group) /* might have been cleared before status ended, e.g. land protector */ - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); } break; case SC_BANDING: @@ -11231,7 +11208,7 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, struct skill_unit_group *group = skill->id2group(sce->val4); sce->val4 = 0; if( group ) /* might have been cleared before status ended, e.g. land protector */ - skill->del_unitgroup(group,ALC_MARK); + skill->del_unitgroup(group); } break; case SC_CURSEDCIRCLE_ATKER: diff --git a/src/map/status.h b/src/map/status.h index d5cb3da75..43cfd6931 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -1214,7 +1214,7 @@ struct status_change { #define sc_start2(src, bl, type, rate, val1, val2, tick) (status->change_start((src),(bl),(type),100*(rate),(val1),(val2),0,0,(tick),SCFLAG_NONE)) #define sc_start4(src, bl, type, rate, val1, val2, val3, val4, tick) (status->change_start((src),(bl),(type),100*(rate),(val1),(val2),(val3),(val4),(tick),SCFLAG_NONE)) -#define status_change_end(bl,type,tid) (status->change_end_((bl),(type),(tid),__FILE__,__LINE__)) +#define status_change_end(bl,type,tid) (status->change_end_((bl),(type),(tid))) #define status_calc_bl(bl, flag) (status->calc_bl_((bl), (enum scb_flag)(flag), SCO_NONE)) #define status_calc_mob(md, opt) (status->calc_bl_(&(md)->bl, SCB_ALL, (opt))) @@ -1308,7 +1308,7 @@ struct status_interface { int (*get_sc_def) (struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int tick, int flag); int (*change_start) (struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, int tick, int flag); int (*change_start_sub) (struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, int tick, int total_tick, int flag); - int (*change_end_) (struct block_list* bl, enum sc_type type, int tid, const char* file, int line); + int (*change_end_) (struct block_list* bl, enum sc_type type, int tid); bool (*is_immune_to_status) (struct status_change* sc, enum sc_type type); bool (*is_boss_resist_sc) (enum sc_type type); bool (*end_sc_before_start) (struct block_list *bl, struct status_data *st, struct status_change* sc, enum sc_type type, int undead_flag, int val1, int val2, int val3, int val4); |