Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- .gitignore | 1 +
- conf/languages.conf | 39 +++
- conf/map_athena.conf | 3 +
- conf/msg_conf/map_msg.conf | 22 +-
- lang/README.md | 107 +++++++
- src/common/cli.c | 20 ++
- src/common/cli.h | 4 +
- src/map/atcommand.c | 170 ++++++++--
- src/map/atcommand.h | 17 +
- src/map/map.c | 116 ++-----
- src/map/map.h | 25 +-
- src/map/npc.c | 4 +
- src/map/pc.c | 31 +-
- src/map/pc.h | 3 +-
- src/map/script.c | 754 ++++++++++++++++++++++++++++++++++++++++++---
- src/map/script.h | 40 +++
- 16 files changed, 1164 insertions(+), 192 deletions(-)
- diff --git a/.gitignore b/.gitignore
- index 1f3e0bf..eedaaad 100644
- --- a/.gitignore
- +++ b/.gitignore
- @@ -31,6 +31,7 @@ Thumbs.db
- /config.status
- /core
- /ipch
- +/lang/*
- /login-server
- /Makefile
- /Makefile.cache
- diff --git a/conf/languages.conf b/conf/languages.conf
- new file mode 100644
- index 0000000..2e6d12c
- --- /dev/null
- +++ b/conf/languages.conf
- @@ -0,0 +1,39 @@
- +//===== rAthena Configuration File ===========================
- +//
- +// Translations generated with '—-generate-translations' should be pointed to here
- +//============================================================
- +
- +// When employing more than one language, this setting is used as a fallback.
- +default_language: "English"
- +
- +// List of languages.
- +languages: {
- + //===== Structure ============================================
- + //
- + //LanguageName: { // Name of language, used for looking up user's language on @langtype.
- + // lang: ( // List of translations file(s) for NPC Script dialogs,
- + // "path/to/translation.po", // use comma (,) to separated next file, don't forget the quotes, and
- + // "path/to/translation2a.po // DO NOT use comma (,) for last file.
- + // )
- + // motd: "path/to/motd.file" // For MOTD file, alternative conf/motd.txt for this language. TODO: Use PO.
- + // help: "path/to/help.file" // For @Help file, alternative conf/help.txt for this language. TODO: Use PO.
- + //}
- + //============================================================
- + // Examples:
- + //
- + //Spanish: {
- + // lang: (
- + // "lang/Spanish.po"
- + // )
- + //}
- + //Indonesian: {
- + // motd: "lang/Indonesian/motd.txt"
- + // help: "lang/Indonesian/help.txt"
- + // lang: (
- + // "lang/Indonesian/msg_conf.po",
- + // "lang/Indonesian/npc/airports/airship.po",
- + // "lang/Indonesian/npc/re/cities/malangdo.po",
- + // "lang/Indonesian/npc/re/jobs/novice/novice.po"
- + // )
- + //}
- +}
- diff --git a/conf/map_athena.conf b/conf/map_athena.conf
- index 5df09c3..2f084d5 100644
- --- a/conf/map_athena.conf
- +++ b/conf/map_athena.conf
- @@ -119,6 +119,9 @@ help_txt: conf/help.txt
- help2_txt: conf/help2.txt
- charhelp_txt: conf/charhelp.txt
- +// Translation file
- +language_conf: conf/languages.conf
- +
- // Maps:
- import: conf/maps_athena.conf
- diff --git a/conf/msg_conf/map_msg.conf b/conf/msg_conf/map_msg.conf
- index 2e50a20..1cac0e7 100644
- --- a/conf/msg_conf/map_msg.conf
- +++ b/conf/msg_conf/map_msg.conf
- @@ -805,7 +805,7 @@
- 900: Usage:
- 901: @send len <packet hex number>
- 902: @send <packet hex number> {<value>}*
- -903: Value: <type=B(default),W,L><number> or S<length>"<string>"
- +903: Value: <type=B(default),W,L><number> or S<length>\"<string>\"
- 904: Packet 0x%x length: %d
- 905: Unknown packet: 0x%x
- 906: Not a string:
- @@ -1162,8 +1162,8 @@
- 1193: You're currently not autolooting this item.
- 1194: Removed item: '%s'/'%s' {%hu} from your autolootitem list.
- 1195: You can have %d items on your autolootitem list.
- -1196: To add an item to the list, use "@alootid +<item name or ID>". To remove an item, use "@alootid -<item name or ID>".
- -1197: "@alootid reset" will clear your autolootitem list.
- +1196: To add an item to the list, use \"@alootid +<item name or ID>\". To remove an item, use \"@alootid -<item name or ID>\".
- +1197: \"@alootid reset\" will clear your autolootitem list.
- 1198: Your autolootitem list is empty.
- 1199: Items on your autolootitem list:
- 1200: Your autolootitem list has been reset.
- @@ -1368,8 +1368,8 @@
- // @mapflag
- 1311: Enabled Mapflags in this map:
- -1312: Usage: "@mapflag monster_noteleport 1" (0=Off | 1=On)
- -1313: Type "@mapflag available" to list the available mapflags.
- +1312: Usage: \"@mapflag monster_noteleport 1\" (0=Off | 1=On)
- +1313: Type \"@mapflag available\" to list the available mapflags.
- 1314: Invalid flag name or flag.
- 1315: Available Flags:
- @@ -1448,13 +1448,13 @@
- // @accinfo
- 1365: Usage: @accinfo/@accountinfo <account_id/char name>
- -1366: You may search partial name by making use of '%' in the search, ex. "@accinfo %Mario%" lists all characters whose name contains "Mario".
- +1366: You may search partial name by making use of '%' in the search, ex. \"@accinfo %Mario%\" lists all characters whose name contains \"Mario\".
- // @set
- 1367: Usage: @set <variable name> <value>
- -1368: Usage: ex. "@set PoringCharVar 50"
- -1369: Usage: ex. "@set PoringCharVarSTR$ Super Duper String"
- -1370: Usage: ex. "@set PoringCharVarSTR$" outputs its value, Super Duper String.
- +1368: Usage: ex. \"@set PoringCharVar 50\"
- +1369: Usage: ex. \"@set PoringCharVarSTR$ Super Duper String\"
- +1370: Usage: ex. \"@set PoringCharVarSTR$\" outputs its value, Super Duper String.
- 1371: NPC variables may not be used with @set.
- 1372: Instance variables may not be used with @set.
- 1373: %s value is now: %d
- @@ -1596,9 +1596,9 @@
- 1483: Autolooting item type: '%s' {%d}
- 1484: You're currently not autolooting this item type.
- 1485: Removed item type: '%s' {%d} from your autoloottype list.
- -1486: To add an item type to the list, use "@aloottype +<type name or ID>". To remove an item type, use "@aloottype -<type name or ID>".
- +1486: To add an item type to the list, use \"@aloottype +<type name or ID>\". To remove an item type, use \"@aloottype -<type name or ID>\".
- 1487: Type List: healing = 0, usable = 2, etc = 3, armor = 4, weapon = 5, card = 6, petegg = 7, petarmor = 8, ammo = 10
- -1488: "@aloottype reset" will clear your autoloottype list.
- +1488: \"@aloottype reset\" will clear your autoloottype list.
- 1489: Your autoloottype list is empty.
- 1490: Item types on your autoloottype list:
- 1491: Your autoloottype list has been reset.
- diff --git a/lang/README.md b/lang/README.md
- new file mode 100644
- index 0000000..62637bc
- --- /dev/null
- +++ b/lang/README.md
- @@ -0,0 +1,107 @@
- +# rAthena Language Library
- +=========
- +
- +### Table of Contents
- +---------
- +1. Generating Translation (.pot) File
- +2. Translating Language (.po) File
- +3. Adding New Language
- +4. Set Default Language
- +5. Change Language
- +6. Other Translation
- +7. Contributing
- +8. Known Translation Project
- +
- +## 1. Generating Translation (.pot) File
- +---------
- +Please makes sure map-server was compiled.
- +### Windows:
- +````
- +map-server.exe --generate-translations [path/to/generated.file]
- +````
- +### Linux:
- +````
- +map-server --generate-translations [path/to/generated.file]
- +````
- +
- +## 2. Translating Language (.po) File
- +---------
- +* After generating the translation/template .pot file, rename it to "My_Translation.po"
- +* Open the file, then try find this dialog.
- +````
- +#: npc/re/jobs/novice/novice.txt
- +# mes "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips.";
- +msgctxt "Sprakki#newbe01::NvSprakkiA"
- +msgid "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips."
- +msgstr ""
- +````
- +* Add/edit the dialog inside `msgstr ""` (Example in Indonesian from idRO) ````
- +#: npc/re/jobs/novice/novice.txt
- +# mes "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips.";
- +msgctxt "Sprakki#newbe01::NvSprakkiA"
- +msgid "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips."
- +msgstr "Halo! Selamat datang di Ragnarok Online Indonesia. Namaku adalah Sprakki dan aku disini untuk membantumu mengenai pengetahuan dasar bermain."
- +````
- +* Save the file.
- +**PS:** Steps above are translating manual using text editor.
- +
- +## 3. Adding New Language
- +---------
- +1. Open conf/translation.conf
- +2. Add your language name and the file for translations.
- +````
- + My_Language: { // Langauge Name
- + lang: (
- + "lang/My_Translation.po" // Translation file
- + )
- + }
- +````
- +
- +Notes:
- +* More than 1 language can be added
- +* More than 1 file can be listed for a language, use comma to separated the file pathname.
- +
- +
- +## 4. Set Default Language
- +---------
- +1. Before changing default language, amke sure the language is added.
- +2. Change the default language value in
- +````
- +default_language: "My_Language"
- +````
- +
- +
- +## 5. Change Language
- +---------
- +Use **@langtype** in-game. Example: `@langtype My_Language`.
- +The language changed will be stored stored as player's account variable.
- +
- +
- +## 6. Other Translation
- +---------
- +Besides NPC dialogs, translation is also available for:
- +* Atcommand messages (since multilanguage of msg_conf was depreciated).
- +* Atcommand Help message (@help)
- +* Message of The Day (MoTD)
- +````
- +translations: {
- + My_Language: { // Langauge Name
- + motd: "lang/Indonesian/motd.txt" // MOTD Translation
- + help: "lang/Indonesian/help.txt" // Help Translation
- + lang: (
- + "lang/My_Translation/msg_conf.po" // map_athena.conf Translation
- + )
- + }
- +}
- +````
- +
- +## 7. Contributing
- +---------
- +* rAthena **DOES NOT** officially manage the translation files. Please contact respective community that provide translation project.
- +* rAthena **ONLY** will lists the translation project that fit our regulations.
- +* Try visit [rAthena International Forums](https://rathena.org/board/forum/6-international-forums/) to dig informations.
- +* Originally by @Hercules https://github.com/HerculesWS/Hercules/commit/330e31cc71ece055908acb1eb967b4009ebc9c46, first ported to rAthena by @aleos89, restructured by @cydh.
- +
- +## 8. Known Translation Project
- +---------
- +* *No informations yet*
- diff --git a/src/common/cli.c b/src/common/cli.c
- index 2c9318a..ba5528f 100644
- --- a/src/common/cli.c
- +++ b/src/common/cli.c
- @@ -20,6 +20,7 @@
- #include "showmsg.h"
- #include "core.h"
- #include "cli.h"
- +#include "malloc.h"
- //map confs
- char* MAP_CONF_NAME;
- @@ -37,6 +38,9 @@
- //common conf (used by multiple serv)
- char* LAN_CONF_NAME; //char-login
- char* MSG_CONF_NAME_EN; //all
- +char* TRANSLATION_CONF_FILE;
- +FILE *lang_export_fp;
- +char *lang_export_file;
- /**
- * Function to check if the specified option has an argument following it.
- @@ -56,6 +60,12 @@ bool opt_has_next_value(const char* option, int i, int argc){
- return true;
- }
- +bool opt_has_next_value_(const char* option, int i, int argc){
- + if (i >= argc - 1)
- + return false;
- + return true;
- +}
- +
- /**
- * Display some information about the emulator, such as:
- * svn version
- @@ -171,6 +181,16 @@ int cli_get_options(int argc, char ** argv) {
- else if (strcmp(arg, "log-config") == 0) {
- if (opt_has_next_value(arg, i, argc))
- LOG_CONF_NAME = argv[++i];
- + } else if (strcmp(arg, "generate-translations") == 0) {
- + if (opt_has_next_value_(arg, i, argc))
- + lang_export_file = aStrdup(argv[++i]);
- + else
- + lang_export_file = aStrdup("./lang/generated_translations.pot");
- +
- + if (!(lang_export_fp = fopen(lang_export_file,"wb")))
- + ShowError("export-dialog: failed to open '%s' for writing!\n", lang_export_file);
- +
- + runflag = CORE_ST_STOP;
- }
- else {
- ShowError("Unknown option '%s'.\n", argv[i]);
- diff --git a/src/common/cli.h b/src/common/cli.h
- index 4819dfa..7f28cdc 100644
- --- a/src/common/cli.h
- +++ b/src/common/cli.h
- @@ -24,6 +24,9 @@ extern "C" {
- extern char* ATCOMMAND_CONF_FILENAME;
- extern char* SCRIPT_CONF_NAME;
- extern char* GRF_PATH_FILENAME;
- +// Set during startup when attempting to export the lang, unset after server initialization is over
- + extern FILE *lang_export_fp;
- + extern char *lang_export_file; // for lang_export_fp
- //char
- extern char* CHAR_CONF_NAME;
- extern char* SQL_CONF_NAME;
- @@ -37,6 +40,7 @@ extern void display_helpscreen(bool exit);
- bool cli_hasevent();
- void display_versionscreen(bool do_exit);
- bool opt_has_next_value(const char* option, int i, int argc);
- +bool opt_has_next_value_(const char* option, int i, int argc);
- int cli_get_options(int argc, char ** argv);
- int parse_console_timer(int tid, unsigned int tick, int id, intptr_t data);
- extern int parse_console(const char* buf); //particular for each serv
- diff --git a/src/map/atcommand.c b/src/map/atcommand.c
- index deb6a73..4545dd6 100644
- --- a/src/map/atcommand.c
- +++ b/src/map/atcommand.c
- @@ -67,6 +67,8 @@ struct AliasInfo {
- char alias[ATCOMMAND_LENGTH];
- };
- +char*** msg_table;
- +uint8 max_message_table;
- char atcommand_symbol = '@'; // first char of the commands
- char charcommand_symbol = '#';
- @@ -96,6 +98,123 @@ struct atcmd_binding_data* get_atcommandbind_byname(const char* name) {
- return ( i < atcmd_binding_count ) ? atcmd_binding[i] : NULL;
- }
- +const char* atcommand_msgsd(struct map_session_data *sd, int msg_number)
- +{
- + if (!(msg_number >= 0 && msg_number < MAX_MSG))
- + return "??";
- + if (!sd || sd->lang_id >= max_message_table || !msg_table[sd->lang_id][msg_number] )
- + return msg_table[0][msg_number];
- + return msg_table[sd->lang_id][msg_number];
- +}
- +
- +/**
- + * Return the message string of the specified number by [Yor]
- + */
- +const char* atcommand_msg(int msg_number)
- +{
- + if (msg_number >= 0 && msg_number < MAX_MSG) {
- + if(msg_table[default_lang_id][msg_number] != NULL && msg_table[default_lang_id][msg_number][0] != '\0')
- + return msg_table[default_lang_id][msg_number];
- +
- + if(msg_table[0][msg_number] != NULL && msg_table[0][msg_number][0] != '\0')
- + return msg_table[0][msg_number];
- + }
- +
- + return "??";
- +}
- +
- +/**
- + * Reads Message Data
- + *
- + * @param[in] cfg_name configuration filename to read.
- + * @param[in] allow_override whether to allow duplicate message IDs to override the original value.
- + * @return success state.
- + */
- +bool msg_config_read(const char *cfg_name, bool allow_override)
- +{
- + int msg_number, msg_count = 0;
- + char line[1024], w1[16], w2[1024];
- + FILE *fp;
- + static int called = 0;
- +
- + if ((fp = fopen(cfg_name, "r")) == NULL) {
- + ShowError("Messages file not found: %s\n", cfg_name);
- + return false;
- + }
- +
- + if (!max_message_table)
- + atcommand_expand_message_table();
- +
- + while(fgets(line, sizeof(line), fp)) {
- + if (line[0] == '/' && line[1] == '/')
- + continue;
- + if (sscanf(line, "%15[^:]: %1023[^\r\n]", w1, w2) != 2)
- + continue;
- +
- + if (strcmpi(w1, "import") == 0) {
- + msg_config_read(w2, true);
- + } else {
- + msg_number = atoi(w1);
- + if (msg_number >= 0 && msg_number < MAX_MSG) {
- + if (msg_table[0][msg_number] != NULL) {
- + if (!allow_override) {
- + ShowError("Duplicate message: ID '%d' was already used for '%s'. Message '%s' will be ignored.\n",
- + msg_number, w2, msg_table[0][msg_number]);
- + continue;
- + }
- + aFree(msg_table[0][msg_number]);
- + }
- + // This could easily become consecutive memory like get_str() and save the malloc overhead for over 1k calls [Ind]
- + msg_table[0][msg_number] = (char *)aMalloc((strlen(w2) + 1)*sizeof (char));
- + strcpy(msg_table[0][msg_number],w2);
- + msg_count++;
- + }
- + }
- + }
- +
- + ShowInfo("Done reading "CL_WHITE"'%d'"CL_RESET" messages in "CL_WHITE"'%s'"CL_RESET".\n",msg_count,cfg_name);
- + fclose(fp);
- +
- + if (++called == 1) { // Original
- + if (lang_export_fp) {
- + int i;
- +
- + for(i = 0; i < MAX_MSG;i++) {
- + if (msg_table[0][i] != NULL ) {
- + fprintf(lang_export_fp, "#: map_msg.conf::[%d]\n"
- + "msgctxt \"map_msg.conf\"\n"
- + "msgid \"%s\"\n"
- + "msgstr \"\"\n",
- + i,
- + msg_table[0][i]
- + );
- + }
- + }
- + }
- + }
- +
- + return true;
- +}
- +
- +/**
- + * Cleanup Message Data
- + */
- +void do_final_msg(void)
- +{
- + int i, j;
- +
- + for(i = 0; i < max_message_table; i++) {
- + for (j = 0; j < MAX_MSG; j++) {
- + if (msg_table[i][j])
- + aFree(msg_table[i][j]);
- + }
- + aFree(msg_table[i]);
- + }
- +
- + if (msg_table)
- + aFree(msg_table);
- +}
- +
- /**
- * retrieves the help string associated with a given command.
- *
- @@ -3869,7 +3988,8 @@ static void atcommand_raise_sub(struct map_session_data* sd) {
- clif_displaymessage(fd, msg_txt(sd,100)); // Scripts have been reloaded.
- } else if (strstr(command, "msgconf") || strncmp(message, "msgconf", 3) == 0) {
- - map_msg_reload();
- + do_final_msg();
- + msg_config_read(MSG_CONF_NAME_EN, true);
- clif_displaymessage(fd, msg_txt(sd,463)); // Message configuration has been reloaded.
- } else if (strstr(command, "questdb") || strncmp(message, "questdb", 3) == 0) {
- do_reload_quest();
- @@ -9424,38 +9544,31 @@ static inline void atcmd_channel_help(struct map_session_data *sd, const char *c
- return 0;
- }
- -ACMD_FUNC(langtype)
- -{
- - char langstr[8];
- - int i=0, fail=0;
- +ACMD_FUNC(langtype) {
- + char langstr[16];
- + int i = 0;
- memset(langstr, '\0', sizeof(langstr));
- - if(sscanf(message, "%3s", langstr) >= 1){
- - int lang=0;
- - lang = msg_langstr2langtype(langstr); //Switch langstr to associated langtype
- - if( msg_checklangtype(lang,false) == 1 ){ //Verify it's enabled and set it
- - char output[100];
- - pc_setaccountreg(sd, add_str("#langtype"), lang); //For login/char
- - sd->langtype = lang;
- - sprintf(output,msg_txt(sd,461),msg_langtype2langstr(lang)); // Language is now set to %s.
- - clif_displaymessage(fd,output);
- - return 0;
- - } else if (lang != -1) { //defined langage but failed check
- - clif_displaymessage(fd,msg_txt(sd,462)); // This langage is currently disabled.
- - return -1;
- + if(sscanf(message, "%15s", langstr) >= 1){
- + for (i = 0; i < max_lang_id; i++) {
- + if (strcmpi(languages[i], langstr) == 0)
- + break;
- }
- + return pc_set_language(sd, i) ? 0 : -1;
- }
- //wrong or no entry
- clif_displaymessage(fd,msg_txt(sd,460)); // Please enter a valid language (usage: @langtype <language>).
- clif_displaymessage(fd,msg_txt(sd,464)); // ---- Available languages:
- - while(fail != -1){ //out of range
- - fail = msg_checklangtype(i,false);
- - if(fail == 1)
- - clif_displaymessage(fd,msg_langtype2langstr(i));
- - i++;
- + for (i = 0; i < max_lang_id; i++) {
- + char output[CHAT_SIZE_MAX];
- + sprintf(output, "- %s", languages[i]);
- + if (sd->lang_id == i)
- + strcat(output, "*");
- + clif_displaymessage(fd, output);
- }
- +
- return -1;
- }
- @@ -10550,6 +10663,17 @@ void atcommand_doload(void) {
- atcommand_config_read(ATCOMMAND_CONF_FILENAME);
- }
- +void atcommand_expand_message_table(void) {
- + RECREATE(msg_table, char **, ++max_message_table);
- + CREATE(msg_table[max_message_table - 1], char *, MAX_MSG);
- +}
- +
- +void atcommand_msg_set(uint8 lang_id, uint16 num, char *ptr) {
- + if (msg_table[lang_id][num] != NULL)
- + aFree(msg_table[lang_id][num]);
- + msg_table[lang_id][num] = aStrdup(ptr);
- +}
- +
- void do_init_atcommand(void) {
- atcommand_doload();
- }
- diff --git a/src/map/atcommand.h b/src/map/atcommand.h
- index e1f02e8..210bcb9 100644
- --- a/src/map/atcommand.h
- +++ b/src/map/atcommand.h
- @@ -12,11 +12,20 @@ struct map_session_data;
- //Note: The range is unlimited unless this define is set.
- //#define AUTOLOOT_DISTANCE AREA_SIZE
- +#define MAX_MSG 1500
- +#define msg_txt(sd, msg_number) atcommand_msgsd((sd), (msg_number))
- +
- //global var
- extern char atcommand_symbol;
- extern char charcommand_symbol;
- extern int atcmd_binding_count;
- +/**
- + * msg_table[lang_id][msg_id]
- + **/
- +extern char*** msg_table;
- +extern uint8 max_message_table;
- +
- typedef enum {
- COMMAND_ATCOMMAND = 1,
- COMMAND_CHARCOMMAND = 2,
- @@ -42,4 +51,12 @@ struct atcmd_binding_data {
- struct atcmd_binding_data** atcmd_binding;
- struct atcmd_binding_data* get_atcommandbind_byname(const char* name);
- +const char* atcommand_msg(int msg_number);
- +const char* atcommand_msgsd(struct map_session_data *sd, int msg_number);
- +void atcommand_expand_message_table(void);
- +void atcommand_msg_set(uint8 lang_id, uint16 num, char *ptr);
- +
- +bool msg_config_read(const char *cfg_name, bool allow_override);
- +void do_final_msg(void);
- +
- #endif /* _ATCOMMAND_H_ */
- diff --git a/src/map/map.c b/src/map/map.c
- index 13ebb5e..b4b2282 100644
- --- a/src/map/map.c
- +++ b/src/map/map.c
- @@ -15,6 +15,7 @@
- #include "../common/cli.h"
- #include "../common/ers.h"
- +#include "atcommand.h"
- #include "map.h"
- #include "path.h"
- #include "chrif.h"
- @@ -53,6 +54,9 @@
- Sql* mmysql_handle;
- Sql* qsmysql_handle; /// For query_sql
- +const char *default_lang_str = "English";
- +uint8 default_lang_id = 0;
- +
- int db_use_sqldbs = 0;
- char buyingstores_db[32] = "buyingstores";
- char buyingstore_items_db[32] = "buyingstore_items";
- @@ -102,8 +106,6 @@
- static struct block_list *bl_list[BL_LIST_MAX];
- static int bl_list_count = 0;
- -#define MAP_MAX_MSG 1500
- -
- struct map_data map[MAX_MAP_PER_SERVER];
- int map_num = 0;
- int map_port=0;
- @@ -146,6 +148,7 @@ struct map_cache_map_info {
- char help_txt[256] = "conf/help.txt";
- char help2_txt[256] = "conf/help2.txt";
- char charhelp_txt[256] = "conf/charhelp.txt";
- +char language_conf[256] = "conf/languages.conf";
- char wisp_server_name[NAME_LENGTH] = "Server"; // can be modified in char-server configuration file
- @@ -3882,6 +3885,8 @@ int map_config_read(char *cfgName)
- console_msg_log = atoi(w2);//[Ind]
- else if (strcmpi(w1, "console_log_filepath") == 0)
- safestrncpy(console_log_filepath, w2, sizeof(console_log_filepath));
- + else if (strcmpi(w1, "language_conf") == 0)
- + safestrncpy(language_conf, w2, sizeof(language_conf));
- else if (strcmpi(w1, "import") == 0)
- map_config_read(w2);
- else
- @@ -4503,6 +4508,7 @@ void display_helpscreen(bool do_exit)
- ShowInfo(" --grf-path <file>\t\tAlternative GRF path configuration.\n");
- ShowInfo(" --inter-config <file>\t\tAlternative inter-server configuration.\n");
- ShowInfo(" --log-config <file>\t\tAlternative logging configuration.\n");
- + ShowInfo(" --generate-translations <outfile>\tCreates 'outfile' (or default is './lang/generated_translations.pot') file with all translateable strings from scripts, server terminates afterwards.");
- if( do_exit )
- exit(EXIT_SUCCESS);
- }
- @@ -4515,86 +4521,6 @@ void set_server_type(void)
- SERVER_TYPE = ATHENA_SERVER_MAP;
- }
- -/*======================================================
- - * Message System
- - *------------------------------------------------------*/
- -struct msg_data {
- - char* msg[MAP_MAX_MSG];
- -};
- -struct msg_data *map_lang2msgdb(uint8 lang){
- - return (struct msg_data*)idb_get(map_msg_db, lang);
- -}
- -
- -void map_do_init_msg(void){
- - int test=0, i=0, size;
- - char * listelang[] = {
- - MSG_CONF_NAME_EN, //default
- - MSG_CONF_NAME_RUS,
- - MSG_CONF_NAME_SPN,
- - MSG_CONF_NAME_GRM,
- - MSG_CONF_NAME_CHN,
- - MSG_CONF_NAME_MAL,
- - MSG_CONF_NAME_IDN,
- - MSG_CONF_NAME_FRN,
- - MSG_CONF_NAME_POR,
- - MSG_CONF_NAME_THA
- - };
- -
- - map_msg_db = idb_alloc(DB_OPT_BASE);
- - size = ARRAYLENGTH(listelang); //avoid recalc
- - while(test!=-1 && size>i){ //for all enable lang +(English default)
- - test = msg_checklangtype(i,false);
- - if(test == 1) msg_config_read(listelang[i],i); //if enabled read it and assign i to langtype
- - i++;
- - }
- -}
- -void map_do_final_msg(void){
- - DBIterator *iter = db_iterator(map_msg_db);
- - struct msg_data *mdb;
- -
- - for (mdb = (struct msg_data *)dbi_first(iter); dbi_exists(iter); mdb = (struct msg_data *)dbi_next(iter)) {
- - _do_final_msg(MAP_MAX_MSG,mdb->msg);
- - aFree(mdb);
- - }
- - dbi_destroy(iter);
- - map_msg_db->destroy(map_msg_db, NULL);
- -}
- -void map_msg_reload(void){
- - map_do_final_msg(); //clear data
- - map_do_init_msg();
- -}
- -int map_msg_config_read(char *cfgName, int lang){
- - struct msg_data *mdb;
- -
- - if( (mdb = map_lang2msgdb(lang)) == NULL )
- - CREATE(mdb, struct msg_data, 1);
- - else
- - idb_remove(map_msg_db, lang);
- - idb_put(map_msg_db, lang, mdb);
- -
- - if(_msg_config_read(cfgName,MAP_MAX_MSG,mdb->msg)!=0){ //an error occur
- - idb_remove(map_msg_db, lang); //@TRYME
- - aFree(mdb);
- - }
- - return 0;
- -}
- -const char* map_msg_txt(struct map_session_data *sd, int msg_number){
- - struct msg_data *mdb;
- - uint8 lang = 0; //default
- - if(sd && sd->langtype) lang = sd->langtype;
- -
- - if( (mdb = map_lang2msgdb(lang)) != NULL){
- - const char *tmp = _msg_txt(msg_number,MAP_MAX_MSG,mdb->msg);
- - if(strcmp(tmp,"??")) //to verify result
- - return tmp;
- - ShowDebug("Message #%d not found for langtype %d.\n",msg_number,lang);
- - }
- - ShowDebug("Selected langtype %d not loaded, trying fallback...\n",lang);
- - if(lang != 0 && (mdb = map_lang2msgdb(0)) != NULL) //fallback
- - return _msg_txt(msg_number,MAP_MAX_MSG,mdb->msg);
- - return "??";
- -}
- -
- /// Called when a terminate signal is received.
- void do_shutdown(void)
- @@ -4628,22 +4554,10 @@ int do_init(int argc, char *argv[])
- BATTLE_CONF_FILENAME = "conf/battle_athena.conf";
- ATCOMMAND_CONF_FILENAME = "conf/atcommand_athena.conf";
- SCRIPT_CONF_NAME = "conf/script_athena.conf";
- + MSG_CONF_NAME_EN = "conf/msg_conf/map_msg.conf";
- GRF_PATH_FILENAME = "conf/grf-files.txt";
- safestrncpy(console_log_filepath, "./log/map-msg_log.log", sizeof(console_log_filepath));
- - /* Multilanguage */
- - MSG_CONF_NAME_EN = "conf/msg_conf/map_msg.conf"; // English (default)
- - MSG_CONF_NAME_RUS = "conf/msg_conf/map_msg_rus.conf"; // Russian
- - MSG_CONF_NAME_SPN = "conf/msg_conf/map_msg_spn.conf"; // Spanish
- - MSG_CONF_NAME_GRM = "conf/msg_conf/map_msg_grm.conf"; // German
- - MSG_CONF_NAME_CHN = "conf/msg_conf/map_msg_chn.conf"; // Chinese
- - MSG_CONF_NAME_MAL = "conf/msg_conf/map_msg_mal.conf"; // Malaysian
- - MSG_CONF_NAME_IDN = "conf/msg_conf/map_msg_idn.conf"; // Indonesian
- - MSG_CONF_NAME_FRN = "conf/msg_conf/map_msg_frn.conf"; // French
- - MSG_CONF_NAME_POR = "conf/msg_conf/map_msg_por.conf"; // Brazilian Portuguese
- - MSG_CONF_NAME_THA = "conf/msg_conf/map_msg_tha.conf"; // Thai
- - /* Multilanguage */
- -
- // Default map
- safestrncpy(map_default.mapname, "prontera", MAP_NAME_LENGTH);
- map_default.x = 156;
- @@ -4681,6 +4595,7 @@ int do_init(int argc, char *argv[])
- chrif_setip(ip_str);
- }
- + msg_config_read(MSG_CONF_NAME_EN, false);
- battle_config_read(BATTLE_CONF_FILENAME);
- script_config_read(SCRIPT_CONF_NAME);
- inter_config_read(INTER_CONF_NAME);
- @@ -4714,8 +4629,7 @@ int do_init(int argc, char *argv[])
- add_timer_func_list(map_clearflooritem_timer, "map_clearflooritem_timer");
- add_timer_func_list(map_removemobs_timer, "map_removemobs_timer");
- add_timer_interval(gettick()+1000, map_freeblock_timer, 0, 0, 60*1000);
- -
- - map_do_init_msg();
- +
- do_init_atcommand();
- do_init_battle();
- do_init_instance();
- @@ -4746,6 +4660,14 @@ int do_init(int argc, char *argv[])
- npc_event_do_oninit(); // Init npcs (OnInit)
- + if (lang_export_fp) {
- + ShowInfo("Lang exported to '"CL_WHITE"%s"CL_RESET"'\n", lang_export_file);
- + fclose(lang_export_fp);
- + aFree(lang_export_file);
- + lang_export_file= NULL;
- + lang_export_fp = NULL;
- + }
- +
- if (battle_config.pk_mode)
- ShowNotice("Server is running on '"CL_WHITE"PK Mode"CL_RESET"'.\n");
- diff --git a/src/map/map.h b/src/map/map.h
- index 6bfa24d..daad808 100644
- --- a/src/map/map.h
- +++ b/src/map/map.h
- @@ -9,7 +9,6 @@
- #include "../common/mmo.h"
- #include "../common/mapindex.h"
- #include "../common/db.h"
- -#include "../common/msg_conf.h"
- #include "../config/core.h"
- @@ -26,14 +25,6 @@ enum E_MAPSERVER_ST {
- MAPSERVER_ST_LAST
- };
- -#define msg_config_read(cfgName,isnew) map_msg_config_read(cfgName,isnew)
- -#define msg_txt(sd,msg_number) map_msg_txt(sd,msg_number)
- -#define do_final_msg() map_do_final_msg()
- -int map_msg_config_read(char *cfgName,int lang);
- -const char* map_msg_txt(struct map_session_data *sd,int msg_number);
- -void map_do_final_msg(void);
- -void map_msg_reload(void);
- -
- #define MAX_NPC_PER_MAP 512
- #define AREA_SIZE battle_config.area_size
- #define DAMAGELOG_SIZE 30
- @@ -731,6 +722,7 @@ extern char motd_txt[];
- extern char help_txt[];
- extern char help2_txt[];
- extern char charhelp_txt[];
- +extern char language_conf[];
- extern char wisp_server_name[];
- @@ -906,16 +898,8 @@ extern char *ATCOMMAND_CONF_FILENAME;
- extern char *SCRIPT_CONF_NAME;
- extern char *MSG_CONF_NAME_EN;
- extern char *GRF_PATH_FILENAME;
- -//Other languages supported
- -char *MSG_CONF_NAME_RUS;
- -char *MSG_CONF_NAME_SPN;
- -char *MSG_CONF_NAME_GRM;
- -char *MSG_CONF_NAME_CHN;
- -char *MSG_CONF_NAME_MAL;
- -char *MSG_CONF_NAME_IDN;
- -char *MSG_CONF_NAME_FRN;
- -char *MSG_CONF_NAME_POR;
- -char *MSG_CONF_NAME_THA;
- +extern FILE *lang_export_fp;
- +extern char *lang_export_file;
- //Useful typedefs from jA [Skotlex]
- typedef struct map_session_data TBL_PC;
- @@ -958,6 +942,9 @@ extern Sql* mmysql_handle;
- extern Sql* qsmysql_handle;
- extern Sql* logmysql_handle;
- +extern const char *default_lang_str;
- +extern uint8 default_lang_id;
- +
- extern char buyingstores_db[32];
- extern char buyingstore_items_db[32];
- extern char item_db_db[32];
- diff --git a/src/map/npc.c b/src/map/npc.c
- index 7f9f224..8d6331a 100644
- --- a/src/map/npc.c
- +++ b/src/map/npc.c
- @@ -2906,7 +2906,9 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons
- if( end == NULL )
- return NULL;// (simple) parse error, don't continue
- + parser_current_npc_name = w3;
- script = parse_script(script_start, filepath, strline(buffer,script_start-buffer), SCRIPT_USE_LABEL_DB);
- + parser_current_npc_name = NULL;
- label_list = NULL;
- label_list_num = 0;
- if( script )
- @@ -3613,7 +3615,9 @@ static const char* npc_parse_function(char* w1, char* w2, char* w3, char* w4, co
- if( end == NULL )
- return NULL;// (simple) parse error, don't continue
- + parser_current_npc_name = w3;
- script = parse_script(script_start, filepath, strline(buffer,start-buffer), SCRIPT_RETURN_EMPTY_SCRIPT);
- + parser_current_npc_name = NULL;
- if( script == NULL )// parse error, continue
- return end;
- diff --git a/src/map/pc.c b/src/map/pc.c
- index d411e35..90cc1ef 100755
- --- a/src/map/pc.c
- +++ b/src/map/pc.c
- @@ -1338,9 +1338,7 @@ void pc_reg_received(struct map_session_data *sd)
- sd->change_level_3rd = pc_readglobalreg(sd, add_str("jobchange_level_3rd"));
- sd->die_counter = pc_readglobalreg(sd, add_str("PC_DIE_COUNTER"));
- - sd->langtype = pc_readaccountreg(sd, add_str("#langtype"));
- - if (msg_checklangtype(sd->langtype,true) < 0)
- - sd->langtype = 0; //invalid langtype reset to default
- + pc_set_language(sd, pc_readaccountreg(sd, add_str("#langtype")));
- // Cash shop
- sd->cashPoints = pc_readaccountreg(sd, add_str("#CASHPOINTS"));
- @@ -12025,6 +12023,33 @@ void pc_show_questinfo_reinit(struct map_session_data *sd) {
- #endif
- }
- +/**
- + * Set language for player
- + * @param sd
- + * @param lang_id
- + * @return True if changed successfully, False if failed.
- + **/
- +bool pc_set_language(struct map_session_data *sd, uint8 lang_id) {
- + nullpo_retr(false, sd);
- +
- + if (lang_id < max_lang_id) {
- + char output[CHAT_SIZE_MAX];
- + pc_setaccountreg(sd, add_str("#langtype"), lang_id);
- + sd->lang_id = lang_id;
- + sprintf(output,msg_txt(sd,461),languages[lang_id]); // Language is now set to %s.
- + clif_displaymessage(sd->fd,output);
- + return true;
- + }
- +
- + // On invalid
- + if (sd->lang_id != default_lang_id) {
- + pc_setaccountreg(sd, add_str("#langtype"), lang_id);
- + sd->lang_id = lang_id;
- + }
- +
- + clif_displaymessage(sd->fd,msg_txt(sd,462)); // This language is currently disabled.
- + return false;
- +}
- /*==========================================
- * pc Init/Terminate
- diff --git a/src/map/pc.h b/src/map/pc.h
- index 8bed5a3..b68302e 100644
- --- a/src/map/pc.h
- +++ b/src/map/pc.h
- @@ -267,7 +267,7 @@ struct map_session_data {
- unsigned int permissions;/* group permissions */
- int count_rewarp; //count how many time we being rewarped
- - int langtype;
- + uint8 lang_id;
- uint32 packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18
- struct mmo_charstatus status;
- @@ -1234,6 +1234,7 @@ bool pc_is_same_equip_index(enum equip_index eqi, short *equip_index, short inde
- int pc_autotrade_timer(int tid, unsigned int tick, int id, intptr_t data);
- void pc_validate_skill(struct map_session_data *sd);
- +bool pc_set_language(struct map_session_data *sd, uint8 lang_id);
- void pc_show_questinfo(struct map_session_data *sd);
- void pc_show_questinfo_reinit(struct map_session_data *sd);
- diff --git a/src/map/script.c b/src/map/script.c
- index 9441d78..3598839 100644
- --- a/src/map/script.c
- +++ b/src/map/script.c
- @@ -12,6 +12,7 @@
- #endif
- #include "../common/cbasetypes.h"
- +#include "../common/conf.h"
- #include "../common/malloc.h"
- #include "../common/md5calc.h"
- #include "../common/nullpo.h"
- @@ -156,6 +157,9 @@ static inline void SETVALUE(unsigned char* buf, int i, int n)
- static int str_size = 0; // size of the buffer
- static int str_pos = 0; // next position to be assigned
- +char **languages;
- +char **lang_motd_txt;
- +char **lang_help_txt;
- // Using a prime number for SCRIPT_HASH_SIZE should give better distributions
- #define SCRIPT_HASH_SIZE 1021
- @@ -176,6 +180,12 @@ static inline void SETVALUE(unsigned char* buf, int i, int n)
- static int buildin_callsub_ref = 0;
- static int buildin_callfunc_ref = 0;
- static int buildin_getelementofarray_ref = 0;
- +static int buildin_mes_offset = 0;
- +static int buildin_select_offset = 0;
- +static int buildin_menu_offset = 0;
- +static int buildin_dispbottom_offset = 0;
- +static int buildin_message_offset = 0;
- +static int buildin_lang_macro_offset = 0;
- // Caches compiled autoscript item code.
- // Note: This is not cleared when reloading itemdb.
- @@ -234,9 +244,14 @@ enum e_arglist
- int count;
- int flag;
- struct linkdb_node *case_label;
- - } curly[256]; // Information right parenthesis
- - int curly_count; // The number of right brackets
- - int index; // Number of the syntax used in the script
- + } curly[256]; // Information right parenthesis
- + int curly_count; // The number of right brackets
- + int index; // Number of the syntax used in the script
- + int last_func; // buildin index of the last parsed function
- + unsigned int nested_call; // Don't really know what to call this
- + bool lang_macro_active;
- + DBMap *strings; // string map parsed (used when exporting strings only)
- + DBMap *translation_db; // non-null if this NPC has any translated strings to be linked
- } syntax;
- const char* parse_curly_close(const char* p);
- @@ -364,6 +379,33 @@ enum {
- MF_SKILL_DAMAGE //60
- };
- +static inline void script_string_buf_ensure(struct script_string_buf *buf, size_t ensure)
- +{
- + if (buf->pos + ensure >= buf->size) {
- + do {
- + buf->size += 512;
- + } while (buf->pos+ensure >= buf->size);
- + RECREATE(buf->ptr, char, buf->size);
- + }
- +}
- +
- +static inline void script_string_buf_addb(struct script_string_buf *buf, uint8 b)
- +{
- + if (buf->pos + 1 >= buf->size) {
- + buf->size += 512;
- + RECREATE(buf->ptr, char, buf->size);
- + }
- +
- + buf->ptr[buf->pos++] = b;
- +}
- +
- +static inline void script_string_buf_destroy(struct script_string_buf *buf)
- +{
- + if (buf->ptr)
- + aFree(buf->ptr);
- + memset(buf, 0, sizeof(struct script_string_buf));
- +}
- +
- const char* script_op2name(int op)
- {
- #define RETURN_OP_NAME(type) case type: return #type
- @@ -382,6 +424,7 @@ const char* script_op2name(int op)
- RETURN_OP_NAME(C_RETINFO);
- RETURN_OP_NAME(C_USERFUNC);
- RETURN_OP_NAME(C_USERFUNC_POS);
- + RETURN_OP_NAME(C_LSTR);
- // operators
- RETURN_OP_NAME(C_OP3);
- @@ -912,15 +955,32 @@ static int add_word(const char* p)
- static
- const char* parse_callfunc(const char* p, int require_paren, int is_custom)
- {
- - const char* p2;
- - const char* arg=NULL;
- + const char *p2;
- + const char *arg = NULL;
- int func;
- + bool nested_call = false, macro = false;
- func = add_word(p);
- - if( str_data[func].type == C_FUNC ){
- - // buildin function
- - add_scriptl(func);
- - add_scriptc(C_ARG);
- + if (str_data[func].type == C_FUNC) {
- + /** only when unset (-1), valid values are >= 0 **/
- + if (syntax.last_func == -1 )
- + syntax.last_func = str_data[func].val;
- + else { //Nested function call
- + syntax.nested_call++;
- + nested_call = true;
- +
- + if (str_data[func].val == buildin_lang_macro_offset) {
- + syntax.lang_macro_active = true;
- + macro = true;
- + }
- + }
- +
- + if (!macro) {
- + // buildin function
- + add_scriptl(func);
- + add_scriptc(C_ARG);
- + }
- +
- arg = buildin_func[str_data[func].val].arg;
- } else if( str_data[func].type == C_USERFUNC || str_data[func].type == C_USERFUNC_POS ){
- // script defined function
- @@ -1002,8 +1062,19 @@ const char* parse_callfunc(const char* p, int require_paren, int is_custom)
- if( *p != ')' )
- disp_error_message("parse_callfunc: expected ')' to close argument list",p);
- ++p;
- +
- + if (str_data[func].val == buildin_lang_macro_offset)
- + syntax.lang_macro_active = false;
- }
- - add_scriptc(C_FUNC);
- +
- + if (nested_call)
- + syntax.nested_call--;
- +
- + if (!syntax.nested_call)
- + syntax.last_func = -1;
- +
- + if (!macro)
- + add_scriptc(C_FUNC);
- return p;
- }
- @@ -1199,6 +1270,25 @@ bool is_number(const char *p) {
- return false;
- }
- +/**
- + *
- + **/
- +int script_string_dup(char *str)
- +{
- + size_t len = strlen(str);
- + int pos = string_list_pos;
- +
- + while(pos + len + 1 >= string_list_size) {
- + string_list_size += (1024 * 1024) / 2;
- + RECREATE(string_list, char, string_list_size);
- + }
- +
- + safestrncpy(string_list + pos, str, len + 1);
- + string_list_pos += len + 1;
- +
- + return pos;
- +}
- +
- /*==========================================
- * Analysis section
- *------------------------------------------*/
- @@ -1240,28 +1330,147 @@ const char* parse_simpleexpr(const char *p)
- add_scripti((int)i);
- p=np;
- } else if(*p=='"'){
- - add_scriptc(C_STR);
- - p++;
- - while( *p && *p != '"' ){
- - if( (unsigned char)p[-1] <= 0x7e && *p == '\\' )
- - {
- - char buf[8];
- - size_t len = skip_escaped_c(p) - p;
- - size_t n = sv_unescape_c(buf, p, len);
- - if( n != 1 )
- - ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf);
- - p += len;
- - add_scriptb(*buf);
- - continue;
- + struct string_translation *st = NULL;
- + const char *start_point = p;
- + bool duplicate = true;
- + struct script_string_buf *sbuf = &parse_simpleexpr_str;
- +
- + do {
- + p++;
- + while(*p && *p != '"') {
- + if ((unsigned char)p[-1] <= 0x7e && *p == '\\') {
- + char buf[8];
- + size_t len = skip_escaped_c(p) - p;
- + size_t n = sv_unescape_c(buf, p, len);
- + if (n != 1)
- + ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf);
- + p += len;
- + script_string_buf_addb(sbuf, *buf);
- + continue;
- + } else if( *p == '\n' ) {
- + disp_error_message("parse_simpleexpr: unexpected newline @ string",p);
- + }
- + script_string_buf_addb(sbuf, *p++);
- + }
- + if (!*p)
- + disp_error_message("parse_simpleexpr: unexpected eof @ string",p);
- + p++; //'"'
- + p = skip_space(p);
- + } while(*p && *p == '"');
- +
- + script_string_buf_addb(sbuf, 0);
- +
- + if (!(syntax.translation_db && (st = strdb_get(syntax.translation_db, sbuf->ptr))) ) {
- + add_scriptc(C_STR);
- +
- + if (script_pos + sbuf->pos >= script_size) {
- + do {
- + script_size += SCRIPT_BLOCK_SIZE;
- + } while(script_pos + sbuf->pos >= script_size );
- + RECREATE(script_buf, unsigned char, script_size);
- }
- - else if( *p == '\n' )
- - disp_error_message("parse_simpleexpr: unexpected newline @ string",p);
- - add_scriptb(*p++);
- - }
- - if(!*p)
- - disp_error_message("parse_simpleexpr: unexpected eof @ string",p);
- - add_scriptb(0);
- - p++; //'"'
- +
- + memcpy(script_buf + script_pos, sbuf->ptr, sbuf->pos);
- + script_pos += sbuf->pos;
- + } else {
- + int expand = sizeof(int) + sizeof(uint8);
- + unsigned char j;
- + unsigned int st_cursor = 0;
- +
- + add_scriptc(C_LSTR);
- +
- + expand += (sizeof(char*) + sizeof(uint8)) * st->translations;
- +
- + while(script_pos + expand >= script_size) {
- + script_size += SCRIPT_BLOCK_SIZE;
- + RECREATE(script_buf, unsigned char, script_size);
- + }
- +
- + *((int *)(&script_buf[script_pos])) = st->string_id;
- + *((uint8 *)(&script_buf[script_pos + sizeof(int)])) = st->translations;
- +
- + script_pos += sizeof(int) + sizeof(uint8);
- +
- + for(j = 0; j < st->translations; j++) {
- + *((uint8 *)(&script_buf[script_pos])) = RBUFB(st->buf, st_cursor);
- + *((char **)(&script_buf[script_pos+sizeof(uint8)])) = &st->buf[st_cursor + sizeof(uint8)];
- + script_pos += sizeof(char*) + sizeof(uint8);
- + st_cursor += sizeof(uint8);
- + while(st->buf[st_cursor++]);
- + st_cursor += sizeof(uint8);
- + }
- + }
- +
- + //* When exporting we don't know what is a translation and what isn't
- + if (lang_export_fp && sbuf->pos > 1) { //sbuf->pos will always be at least 1 because of the '\0'
- + if (!syntax.strings) {
- + syntax.strings = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_ALLOW_NULL_DATA, 0);
- + }
- +
- + if (!strdb_exists(syntax.strings, sbuf->ptr)) {
- + strdb_put(syntax.strings, sbuf->ptr, NULL);
- + duplicate = false;
- + }
- + }
- +
- + if (lang_export_fp && !duplicate &&
- + (((syntax.last_func == buildin_mes_offset ||
- + syntax.last_func == buildin_select_offset ||
- + syntax.last_func == buildin_menu_offset ||
- + syntax.last_func == buildin_message_offset ||
- + syntax.last_func == buildin_dispbottom_offset) && !syntax.nested_call
- + ) || syntax.lang_macro_active)) {
- + const char *line_start = start_point;
- + const char *line_end = start_point;
- + struct script_string_buf *lbuf = &lang_export_line_buf;
- + struct script_string_buf *ubuf = &lang_export_unescaped_buf;
- + size_t line_length, cursor;
- +
- + while(line_start > parser_current_src) {
- + if (*line_start != '\n')
- + line_start--;
- + else
- + break;
- + }
- +
- + while(*line_end != '\n' && *line_end != '\0')
- + line_end++;
- +
- + line_length = (size_t)(line_end - line_start);
- +
- + if (line_length > 0) {
- + script_string_buf_ensure(lbuf,line_length + 1);
- +
- + memcpy(lbuf->ptr, line_start, line_length);
- + lbuf->pos = line_length;
- + script_string_buf_addb(lbuf, 0);
- +
- + normalize_name(lbuf->ptr, "\r\n\t ");
- + }
- +
- + for(cursor = 0; cursor < sbuf->pos; cursor++) {
- + if (sbuf->ptr[cursor] == '"')
- + script_string_buf_addb(ubuf, '\\');
- + script_string_buf_addb(ubuf, sbuf->ptr[cursor]);
- + }
- + script_string_buf_addb(ubuf, 0);
- +
- + fprintf(lang_export_fp, "#: %s\n"
- + "# %s\n"
- + "msgctxt \"%s\"\n"
- + "msgid \"%s\"\n"
- + "msgstr \"\"\n",
- + parser_current_file ? parser_current_file : "Unknown File",
- + lbuf->ptr,
- + parser_current_npc_name ? parser_current_npc_name : "Unknown NPC",
- + ubuf->ptr
- + );
- +
- + lbuf->pos = 0;
- + ubuf->pos = 0;
- + }
- +
- + sbuf->pos = 0;
- } else {
- int l;
- const char* pv;
- @@ -2151,8 +2360,14 @@ static void add_buildin_func(void)
- if (!strcmp(buildin_func[i].name, "set")) buildin_set_ref = n;
- else if (!strcmp(buildin_func[i].name, "callsub")) buildin_callsub_ref = n;
- else if (!strcmp(buildin_func[i].name, "callfunc")) buildin_callfunc_ref = n;
- - else if( !strcmp(buildin_func[i].name, "getelementofarray") ) buildin_getelementofarray_ref = n;
- - }
- + else if( !strcmp(buildin_func[i].name, "getelementofarray")) buildin_getelementofarray_ref = n;
- + else if( !strcmp(buildin_func[i].name, "mes")) buildin_mes_offset = i;
- + else if( !strcmp(buildin_func[i].name, "select")) buildin_select_offset = i;
- + else if( !strcmp(buildin_func[i].name, "menu")) buildin_menu_offset = i;
- + else if( !strcmp(buildin_func[i].name, "dispbottom")) buildin_dispbottom_offset = i;
- + else if( !strcmp(buildin_func[i].name, "message")) buildin_message_offset = i;
- + else if( !strcmp(buildin_func[i].name, "_")) buildin_lang_macro_offset = i;
- + }
- }
- }
- @@ -2333,14 +2548,28 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o
- if( src == NULL )
- return NULL;// empty script
- + if (parse_cleanup_timer_id == INVALID_TIMER)
- + parse_cleanup_timer_id = add_timer(gettick() + 10, script_parse_cleanup_timer, 0, 0);
- +
- + if (syntax.strings) // Used only when generating translation file
- + db_destroy(syntax.strings);
- +
- memset(&syntax,0,sizeof(syntax));
- - if(first){
- + if (first) {
- add_buildin_func();
- read_constdb();
- script_hardcoded_constants();
- first=0;
- }
- + syntax.last_func = -1; // As valid values are >= 0
- + if (parser_current_npc_name) {
- + if (!translation_db)
- + script_load_translations();
- + if (translation_db)
- + syntax.translation_db = strdb_get(translation_db, parser_current_npc_name);
- + }
- +
- script_buf=(unsigned char *)aMalloc(SCRIPT_BLOCK_SIZE*sizeof(unsigned char));
- script_pos=0;
- script_size=SCRIPT_BLOCK_SIZE;
- @@ -4054,14 +4283,40 @@ void run_script_main(struct script_state *st)
- push_str(stack,C_CONSTSTR,(char*)(st->script->script_buf+st->pos));
- while(st->script->script_buf[st->pos++]);
- break;
- + case C_LSTR:
- + {
- + int string_id = *((int *)(&st->script->script_buf[st->pos]));
- + uint8 translations = *((uint8 *)(&st->script->script_buf[st->pos+sizeof(int)]));
- + struct map_session_data *lsd = NULL;
- +
- + st->pos += sizeof(int) + sizeof(uint8);
- + if( (!st->rid || !(lsd = map_id2sd(st->rid)) || !lsd->lang_id) && !default_lang_id )
- + script_pushconststr(st, string_list + string_id);
- + else {
- + uint8 k, wlang_id = lsd ? lsd->lang_id : default_lang_id;
- + int offset = st->pos;
- +
- + for(k = 0; k < translations; k++) {
- + uint8 lang_id = *(uint8 *)(&st->script->script_buf[offset]);
- + offset += sizeof(uint8);
- + if (lang_id == wlang_id)
- + break;
- + offset += sizeof(char*);
- + }
- +
- + script_pushconststr(st, (k == translations) ? string_list + string_id : *(char**)(&st->script->script_buf[offset]));
- + }
- + st->pos += ((sizeof(char*) + sizeof(uint8)) * translations);
- + }
- + break;
- case C_FUNC:
- run_func(st);
- - if(st->state==GOTO){
- + if (st->state == GOTO) {
- st->state = RUN;
- - if( !st->freeloop && gotocount>0 && (--gotocount)<=0 ){
- - ShowError("script:run_script_main: infinity loop !\n");
- + if (!st->freeloop && gotocount>0 && (--gotocount)<=0) {
- + ShowError("run_script: infinity loop !\n");
- script_reportsrc(st);
- - st->state=END;
- + st->state = END;
- }
- }
- break;
- @@ -4653,7 +4908,412 @@ void do_final_script() {
- aFree(logThreadData.entry);
- #endif
- +
- + script_clear_translations(false);
- +
- + script_parser_clean_leftovers();
- +
- + if (lang_export_file)
- + aFree(lang_export_file);
- +}
- +
- +/**
- + *
- + **/
- +uint8 script_add_language(const char *name)
- +{
- + uint8 lang_id = max_lang_id;
- +
- + RECREATE(languages, char *, ++max_lang_id);
- + RECREATE(lang_motd_txt, char *, max_lang_id);
- + RECREATE(lang_help_txt, char *, max_lang_id);
- +
- + ShowInfo("Lang: '"CL_WHITE"%d"CL_RESET"': '"CL_WHITE"%s"CL_RESET"'.\n", lang_id, name);
- + languages[lang_id] = aStrdup(name);
- + lang_motd_txt[lang_id] = NULL;
- + lang_help_txt[lang_id] = NULL;
- +
- + return lang_id;
- +}
- +
- +static void script_add_motd_language(uint8 lang_id, const char *filepath) {
- + lang_motd_txt[lang_id] = aStrdup(filepath);
- }
- +
- +static void script_add_help_language(uint8 lang_id, const char *filepath) {
- + lang_help_txt[lang_id] = aStrdup(filepath);
- +}
- +
- +/**
- + * Parses conf/translations.conf file
- + **/
- +void script_load_translations(void) {
- + config_t translations_conf;
- + const char *config_filename = language_conf;
- + config_setting_t *languages_t = NULL;
- + int i, size;
- + uint32 total = 0;
- + uint8 k;
- +
- + translation_db = strdb_alloc(DB_OPT_DUP_KEY, NAME_LENGTH * 2 + 1);
- +
- + if (languages) {
- + for(i = 0; i < max_lang_id; i++)
- + aFree(languages[i]);
- + aFree(languages);
- + }
- + if (lang_motd_txt) {
- + for(i = 0; i < max_lang_id; i++)
- + aFree(lang_motd_txt[i]);
- + aFree(lang_motd_txt);
- + }
- + if (lang_help_txt) {
- + for(i = 0; i < max_lang_id; i++)
- + aFree(lang_help_txt[i]);
- + aFree(lang_help_txt);
- + }
- + languages = NULL;
- + lang_motd_txt = NULL;
- + lang_help_txt = NULL;
- + max_lang_id = 0;
- +
- + script_add_language("English"); // 0 is default, which is whatever is in the NPC files hardcoded (in our case, English)
- +
- + if (conf_read_file(&translations_conf, config_filename)) {
- + ShowError("script_load_translations: Can't read '%s'\n", config_filename);
- + return;
- + }
- +
- + if (config_lookup_string(&translations_conf, "default_language", &default_lang_str)) {
- + ShowInfo("Default language '%s'.\n", default_lang_str);
- + }
- +
- + if (!(languages_t = config_lookup(&translations_conf, "languages")) ) {
- + ShowError("script_load_translations: Invalid 'languages' format on '%s'\n", config_filename);
- + return;
- + }
- +
- + if (string_list)
- + aFree(string_list);
- +
- + string_list = NULL;
- + string_list_pos = 0;
- + string_list_size = 0;
- +
- + size = config_setting_length(languages_t);
- +
- + for(i = 0; i < size; i++) {
- + uint8 j, lang_id = 0;
- + int count = 0;
- + config_setting_t *language_t = NULL, *lang_files = NULL;
- + const char *lang_name = NULL, *str = NULL;
- +
- + if (!(language_t = config_setting_get_elem(languages_t, i)))
- + continue;
- +
- + lang_name = config_setting_name(language_t);
- + lang_id = script_add_language(lang_name);
- +
- + // MOTD
- + if (config_setting_lookup_string(language_t, "motd", &str)) {
- + script_add_motd_language(lang_id, str);
- + //ShowInfo("Added MOTD for %s: '"CL_WHITE"%s"CL_RESET"'\n", lang_name, str);
- + }
- +
- + // @help
- + if (config_setting_lookup_string(language_t, "help", &str)) {
- + script_add_help_language(lang_id, str);
- + //ShowInfo("Added @help for %s: '"CL_WHITE"%s"CL_RESET"'\n", lang_name, str);
- + }
- +
- + // Translations
- + if (!(lang_files = config_setting_get_member(language_t, "lang")) ) {
- + ShowError("script_load_translations: Invalid 'lang' format on '%s'\n", config_filename);
- + continue;
- + }
- +
- + count = config_setting_length(lang_files);
- + if (!count) {
- + ShowWarning("script_load_translations: Language '%s' is defined with no translation file. Skipping...\n", lang_name);
- + continue;
- + }
- +
- + for (j = 0; j < count; j++) {
- + const char *translation_file = config_setting_get_string_elem(lang_files, j);;
- + total += script_load_translation(lang_id, translation_file);
- + }
- + }
- +
- + if (total) {
- + DBIterator *main_iter;
- + DBIterator *sub_iter;
- + DBMap *string_db;
- + struct string_translation *st = NULL;
- + uint32 j = 0;
- +
- + CREATE(translation_buf, char *, total);
- + translation_buf_size = total;
- +
- + main_iter = db_iterator(translation_db);
- +
- + for(string_db = dbi_first(main_iter); dbi_exists(main_iter); string_db = dbi_next(main_iter) ) {
- + sub_iter = db_iterator(string_db);
- +
- + for(st = dbi_first(sub_iter); dbi_exists(sub_iter); st = dbi_next(sub_iter) ) {
- + translation_buf[j++] = st->buf;
- + }
- +
- + dbi_destroy(sub_iter);
- + }
- +
- + dbi_destroy(main_iter);
- + }
- +
- + for(k = 0; k < max_lang_id; k++) {
- + if( !strcmpi(languages[k], default_lang_str) ) {
- + break;
- + }
- + }
- +
- + if (k == max_lang_id) {
- + ShowError("script_load_translations: Map server 'default_language' setting '%s' is not a loaded language.\n", default_lang_str);
- + default_lang_id = 0;
- + } else
- + default_lang_id = k;
- +
- + config_destroy(&translations_conf);
- +}
- +
- +/**
- + * Parses a individual translation file
- + **/
- +uint32 script_load_translation(uint8 lang_id, const char *file) {
- + uint32 translations = 0;
- + char line[1024];
- + char msgctxt[NAME_LENGTH * 2 + 1] = { 0 };
- + DBMap *string_db;
- + size_t i;
- + FILE *fp;
- + struct script_string_buf msgid = { 0 }, msgstr = { 0 };
- +
- + if (file == NULL)
- + return 0;
- +
- + if (!(fp = fopen(file,"rb")) ) {
- + ShowError("script_load_translation: Failed to open '%s' for reading.\n", file);
- + return 0;
- + }
- +
- + if (lang_id >= max_message_table)
- + atcommand_expand_message_table();
- +
- + while(fgets(line, sizeof(line), fp)) {
- + size_t len = strlen(line), cursor = 0;
- +
- + if (len <= 1)
- + continue;
- +
- + if (line[0] == '#')
- + continue;
- +
- + if (strncasecmp(line,"msgctxt \"", 9) == 0) {
- + msgctxt[0] = '\0';
- + for(i = 9; i < len - 2; i++) {
- + if (line[i] == '\\' && line[i+1] == '"') {
- + msgctxt[cursor] = '"';
- + i++;
- + } else
- + msgctxt[cursor] = line[i];
- + if (++cursor >= sizeof(msgctxt) - 1)
- + break;
- + }
- + msgctxt[cursor] = '\0';
- + } else if (strncasecmp(line, "msgid \"", 7) == 0) {
- + msgid.pos = 0;
- + for(i = 7; i < len - 2; i++) {
- + if (line[i] == '\\' && line[i+1] == '"') {
- + script_string_buf_addb(&msgid, '"');
- + i++;
- + } else
- + script_string_buf_addb(&msgid, line[i]);
- + }
- + script_string_buf_addb(&msgid,0);
- + } else if (len > 9 && line[9] != '"' && strncasecmp(line, "msgstr \"",8) == 0) {
- + msgstr.pos = 0;
- + for(i = 8; i < len - 2; i++) {
- + if (line[i] == '\\' && line[i+1] == '"') {
- + script_string_buf_addb(&msgstr, '"');
- + i++;
- + } else
- + script_string_buf_addb(&msgstr, line[i]);
- + }
- + script_string_buf_addb(&msgstr,0);
- + }
- +
- + if( msgctxt[0] && msgid.pos > 1 && msgstr.pos > 1 ) {
- + size_t msgstr_len = msgstr.pos;
- + unsigned int inner_len = 1 + (uint32)msgstr_len + 1; //uint8 lang_id + msgstr_len + '\0'
- +
- + if (strcasecmp(msgctxt, "map_msg.conf") == 0) {
- + int k;
- +
- + for(k = 0; k < MAX_MSG; k++) {
- + if (msgid.ptr != NULL && msg_table[0][k] && strcmpi(msg_table[0][k],msgid.ptr) == 0) {
- + atcommand_msg_set(lang_id,k,msgstr.ptr);
- + break;
- + }
- + }
- + } else {
- + struct string_translation *st = NULL;
- +
- + if (!( string_db = strdb_get(translation_db, msgctxt))) {
- + string_db = strdb_alloc(DB_OPT_DUP_KEY, 0);
- +
- + strdb_put(translation_db, msgctxt, string_db);
- + }
- +
- + if (!(st = strdb_get(string_db, msgid.ptr))) {
- + CREATE(st, struct string_translation, 1);
- +
- + st->string_id = script_string_dup(msgid.ptr);
- +
- + strdb_put(string_db, msgid.ptr, st);
- + }
- +
- + RECREATE(st->buf, char, st->len + inner_len);
- +
- + WBUFB(st->buf, st->len) = lang_id;
- + safestrncpy((char*)WBUFP(st->buf, st->len + 1), msgstr.ptr, msgstr_len + 1);
- +
- + st->translations++;
- + st->len += inner_len;
- + }
- + msgctxt[0] = '\0';
- + msgid.pos = msgstr.pos = 0;
- + translations++;
- + }
- + }
- +
- + fclose(fp);
- +
- + script_string_buf_destroy(&msgid);
- + script_string_buf_destroy(&msgstr);
- +
- + ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' translations in '"CL_WHITE"%s"CL_RESET"'.\n", translations, file);
- + return translations;
- +}
- +
- +/**
- + *
- + **/
- +void script_clear_translations(bool reload)
- +{
- + uint32 i;
- +
- + if (string_list)
- + aFree(string_list);
- +
- + string_list = NULL;
- + string_list_pos = 0;
- + string_list_size = 0;
- +
- + if (translation_buf) {
- + for(i = 0; i < translation_buf_size; i++) {
- + aFree(translation_buf[i]);
- + }
- + aFree(translation_buf);
- + }
- +
- + translation_buf = NULL;
- + translation_buf_size = 0;
- +
- + if (languages) {
- + for(i = 0; i < max_lang_id; i++)
- + aFree(languages[i]);
- + aFree(languages);
- + }
- + if (lang_motd_txt) {
- + for(i = 0; i < max_lang_id; i++)
- + aFree(lang_motd_txt[i]);
- + aFree(lang_motd_txt);
- + }
- + if (lang_help_txt) {
- + for(i = 0; i < max_lang_id; i++)
- + aFree(lang_help_txt[i]);
- + aFree(lang_help_txt);
- + }
- + languages = NULL;
- + lang_motd_txt = NULL;
- + lang_help_txt = NULL;
- + max_lang_id = 0;
- +
- + if (translation_db) {
- + translation_db->clear(translation_db, script_translation_db_destroyer);
- + }
- +
- + if (reload)
- + script_load_translations();
- +}
- +
- +/**
- + *
- + **/
- +int script_translation_db_destroyer(DBKey key, DBData *data, va_list ap)
- +{
- + DBMap *string_db = db_data2ptr(data);
- +
- + if (db_size(string_db)) {
- + DBIterator *iter = db_iterator(string_db);
- + struct string_translation *st = NULL;
- +
- + for(st = dbi_first(iter); dbi_exists(iter); st = dbi_next(iter)) {
- + aFree(st);
- + }
- +
- + dbi_destroy(iter);
- + }
- +
- + db_destroy(string_db);
- + return 0;
- +}
- +
- +/**
- + *
- + **/
- +void script_parser_clean_leftovers(void) {
- +
- + //if (script_buf)
- + // aFree(script_buf);
- +
- + script_buf = NULL;
- + script_size = 0;
- +
- + if (translation_db) {
- + translation_db->destroy(translation_db, script_translation_db_destroyer);
- + translation_db = NULL;
- + }
- +
- + if (syntax.strings) { // Used only when generating translation file
- + db_destroy(syntax.strings);
- + syntax.strings = NULL;
- + }
- +
- + script_string_buf_destroy(&parse_simpleexpr_str);
- + script_string_buf_destroy(&lang_export_line_buf);
- + script_string_buf_destroy(&lang_export_unescaped_buf);
- +}
- +
- +/**
- + * Performs cleanup after all parsing is processed
- + **/
- +int script_parse_cleanup_timer(int tid, unsigned int tick, int id, intptr_t data)
- +{
- + script_parser_clean_leftovers();
- + parse_cleanup_timer_id = INVALID_TIMER;
- +
- + return 0;
- +}
- +
- /*==========================================
- * Initialization
- *------------------------------------------*/
- @@ -4662,6 +5322,7 @@ void do_init_script(void) {
- userfunc_db = strdb_alloc(DB_OPT_DUP_KEY,0);
- scriptlabel_db = strdb_alloc(DB_OPT_DUP_KEY,50);
- autobonus_db = strdb_alloc(DB_OPT_DUP_KEY,0);
- + parse_cleanup_timer_id = INVALID_TIMER;
- st_ers = ers_new(sizeof(struct script_state), "script.c::st_ers", ERS_CACHE_OPTIONS);
- stack_ers = ers_new(sizeof(struct script_stack), "script.c::script_stack", ERS_OPT_FLEX_CHUNK);
- @@ -4697,6 +5358,8 @@ void do_init_script(void) {
- add_timer_func_list(queryThread_timer, "queryThread_timer");
- #endif
- +
- + script_load_translations();
- }
- void script_reload(void) {
- @@ -4741,6 +5404,14 @@ void script_reload(void) {
- dbi_destroy(iter);
- db_clear(st_db);
- +
- + script_clear_translations(true);
- +
- + if (parse_cleanup_timer_id != INVALID_TIMER) {
- + delete_timer(parse_cleanup_timer_id, script_parse_cleanup_timer);
- + parse_cleanup_timer_id = INVALID_TIMER;
- + }
- +
- mapreg_reload();
- }
- @@ -21302,6 +21973,11 @@ static int atcommand_cleanfloor_sub(struct block_list *bl, va_list ap)
- #include "../custom/script.inc"
- +/** place holder for the translation macro **/
- +BUILDIN_FUNC(_) {
- + return true;
- +}
- +
- // declarations that were supposed to be exported from npc_chat.c
- #ifdef PCRE_SUPPORT
- BUILDIN_FUNC(defpattern);
- @@ -21877,5 +22553,7 @@ struct script_function buildin_func[] = {
- #include "../custom/script_def.inc"
- + BUILDIN_DEF(_, "s"),
- +
- {NULL,NULL,NULL},
- };
- diff --git a/src/map/script.h b/src/map/script.h
- index b121c6c..eea37de 100644
- --- a/src/map/script.h
- +++ b/src/map/script.h
- @@ -177,6 +177,7 @@ typedef enum c_op {
- C_USERFUNC, // internal script function
- C_USERFUNC_POS, // internal script function label
- C_REF, // the next call to c_op2 should push back a ref to the left operand
- + C_LSTR,
- // operators
- C_OP3, // a ? b : c
- @@ -291,6 +292,18 @@ struct script_array {
- unsigned int *members; ///< member list
- };
- +struct script_string_buf {
- + char *ptr;
- + size_t pos,size;
- +};
- +
- +struct string_translation {
- + int string_id;
- + uint8 translations;
- + unsigned int len;
- + char *buf;
- +};
- +
- enum script_parse_options {
- SCRIPT_USE_LABEL_DB = 0x1,// records labels in scriptlabel_db
- SCRIPT_IGNORE_EXTERNAL_BRACKETS = 0x2,// ignores the check for {} brackets around the script
- @@ -644,6 +657,24 @@ unsigned int next_id;
- struct eri *st_ers;
- struct eri *stack_ers;
- +/// Script String Storage
- +char *string_list;
- +int string_list_size;
- +int string_list_pos;
- +// Set and unset on npc_parse_script
- +char *parser_current_npc_name;
- +DBMap *translation_db; // npc_name => DBMap (strings)
- +char **translation_buf;
- +uint32 translation_buf_size;
- +extern char **languages;
- +extern char **lang_motd_txt;
- +extern char **lang_help_txt;
- +uint8 max_lang_id;
- +struct script_string_buf parse_simpleexpr_str;
- +struct script_string_buf lang_export_line_buf;
- +struct script_string_buf lang_export_unescaped_buf;
- +int parse_cleanup_timer_id;
- +
- const char* skip_space(const char* p);
- void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos);
- void script_warning(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos);
- @@ -685,6 +716,15 @@ int add_str(const char* p);
- const char* get_str(int id);
- void script_reload(void);
- +int string_dup(char *str);
- +void script_load_translations(void);
- +uint32 script_load_translation(uint8 lang_id, const char *file);
- +int script_translation_db_destroyer(DBKey key, DBData *data, va_list ap);
- +void script_clear_translations(bool reload);
- +int script_parse_cleanup_timer(int tid, unsigned int tick, int id, intptr_t data);
- +uint8 script_add_language(const char *name);
- +void script_parser_clean_leftovers(void);
- +
- // @commands (script based)
- void setd_sub(struct script_state *st, TBL_PC *sd, const char *varname, int elem, void *value, struct reg_db *ref);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement