Jump to content

Tokei

Members
  • Posts

    665
  • Joined

  • Last visited

  • Days Won

    90

Everything posted by Tokei

  1. You'll have to give more information or provide a way to reproduce your issue. I tried running your script and I didn't get any warnings...!
  2. I'd go with if (checkoption(0x2|0x4|0x4000|0x40)) { mes "Please unhide yourself."; close; } Where 0x2 = OPTION_HIDE 0x4 = OPTION_CLOAK 0x40 = OPTION_INVISIBLE (GM hide and feint bomb) 0x4000 = OPTION_CHASEWALK
  3. I doubt mono will work, you can give it a try but the application is meant for Windows.
  4. Well, the input is wrong: input .@e; if (.@e < 5 || .@e > countitem(757)) { mes "[Leon]"; mes "That's an invalid amount of Elunium"; mes "The Exchange rate is 5 rough elu to 1 Elunium"; close; } .@e = .@e / 5 * 5; mes "Are you sure you want to exchange ^0055FF" + .@e + " Rough Eluniums?^000000?"; if (select("Yes:No") == 2) close; mes "[Leon]"; mes "Okay come to me again if you want to exchange"; delitem 757, .@e; getitem 985, .@e/5; end; And so was the delitem line.
  5. Should be working fine now, using 1.8.2.2.
  6. Annnnd fixed! The TGA files were using the image type 10; they should be showing up fine now: 1.8.2.2 Also, I fixed GrfCL's issues. Thanks for the sample GRF @Anacondaqq!
  7. @Anacondaqq I'd need the image you're using for tarot (it seems to be custom and not made by Gravity). The TGA image type 2 flips the image horizontally and that is intended. I wasn't aware Gravity supported other TGA formats (all their TGA files use the same image type). As for the settings issue, I'm guessing Windows 10 changed the way they associate files. I'll simply move the exception for now. Updated: 1.8.2.1
  8. As others have pointed out, this is caused by the packet obfuscation not being set properly. You have to either enable it on both the client and the emulator, using the same exact packet keys, or disable it on both end (not recommended in the long run, but it's easier to do when starting). To disable packet obfuscation: Client side: Open NEMO and load the client. You'll find the "Disable packet obfuscation" patch that you must appl Server side: Open src\config\core.h and uncomment "#define PACKET_OBFUSCATION" so that you get //#define PACKET_OBFUSCATION Then recompile your server. Make sure you've set the correct client date in src\common\mmo.h > #define PACKETVER 20151104 (or whatever client version you're using, but 55 should be around that area). If you're using a client past 2015-11-04 and that you still can't connect, then it would mean you need a higher packet version (56 and above). Unfortunately, rAthena doesn't have those packets in their repo yet so you're out of luck! You'll have to use PEEK and figure those out.
  9. You should avoid resellers and instead go with hostings such as OVH/Limestone/etc. It's a harder learning curve but it's much more rewarding in the end as you'll have full control over your server. As for RagnaHosting, I hope you haven't invested too much in it =/.
  10. The OnTouch event triggers if you walk into the NPC's zone (not when clicking the NPC). You're describing exactly what OnTouch is meant and used for.
  11. Well, these look regular ontouch areas. lasa_fild01,132,374,3 script Sloth#doram 10167,5,5,{ end; OnTouch: npctalk "Hey, there! Get over here for a minute!"; end; }
  12. Can you describe what you're trying to do? It's almost impossible to help you based on that alone. What's special about the NPC?
  13. As it says in the instructions, you cannot rename the executable file name once you encrypt it.
  14. His GRF is being used by another software or he doesn't have write access to the directory (which is what the error says ;x).
  15. Well, as the error says: access to the file path is denied.
  16. Heya, this error can only happen when you try to extract a file. This is very common for encrypted files (they cannot be extracted) but it can also happen if the file inside the GRF is broken. The thread will keep extracting the rest of the files (if you're trying to extract more than one) and only the last error will be shown. You'd have to give us more details on what you're doing here.
  17. Yep, it's normal xD. It's from this line: if (map_mapname2mapid(map_name_new) >= 0) { // This will always show a warning in the console, but removing it is not worth the trouble. sprintf(atcmd_output, "Destination map already exists: %s", map_name_new); clif_displaymessage(fd, atcmd_output); return -1; } It simply checks if the map already exists and prevents you from copying on top of an already existing one. Since the map currently doesn't exist, it gives you a warning but it's what we're looking for. It's like when you use... "@warp unkonwnmap", which also gives you that warning. (Not something worth fixing ;P)
  18. Updated the diff, I'm surprised Visual Studio didn't have any issue with this line whatsoever.
  19. This feature allows you to clone maps ingame without having to reboot your server or do any changes client-side. The cloned maps will behave just like any other regular map, except players will not be able to save in them. If they log out, they will return to their spawn point. The commands: @clonemap source_map new_map @delmap new_map @clonemaplist This is useful if want to make an event on a map but there's an annoying NPC in the way (and you don't want to reboot your server just for that!). Another big upside being you don't need to send a patch for that either, the new map will be added 'live'. You can load scripts on these maps as well (they will be removed upon deleting the map). Usage example: @clonemap prontera newmap @addwarp newmap 150 150 test_portal (@unloadnpc test_portal) Have fun! These maps will be lost when rebooting the server. diff --git a/src/map/atcommand.c b/src/map/atcommand.c index d4b329c..25e4c8b 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -799,6 +799,11 @@ ACMD_FUNC(save) { nullpo_retr(-1, sd); + if( map[sd->bl.m].clone_id ) { + clif_displaymessage(fd, "You cannot create a savepoint in this map."); + return 1; + } + if( map[sd->bl.m].instance_id ) { clif_displaymessage(fd, msg_txt(sd,383)); // You cannot create a savepoint in an instance. return 1; @@ -5945,6 +5950,11 @@ ACMD_FUNC(autotrade) { return -1; } + if (map[sd->bl.m].clone_id) { + clif_displaymessage(fd, "You cannot use autotrade on cloned maps."); + return -1; + } + sd->state.autotrade = 1; if (battle_config.autotrade_monsterignore) sd->state.monster_ignore = 1; @@ -9937,6 +9947,111 @@ ACMD_FUNC(adopt) return -1; } +/** + * Clones an existing map without having to reboot your server. + * Usage: @clonemap <source_map_name> <new_map_name> + * @author Tokeiburu + */ +ACMD_FUNC(clonemap) +{ + int res; + char map_name_cur[MAP_NAME_LENGTH_EXT]; + char map_name_new[MAP_NAME_LENGTH_EXT]; + + memset(map_name_cur, '\0', sizeof(map_name_cur)); + memset(map_name_new, '\0', sizeof(map_name_new)); + + if (!message || !*message || sscanf(message, "%15s %15s", map_name_cur, map_name_new) < 2) { + clif_displaymessage(fd, "Usage: @clonemap <source_map_name> <new_map_name>"); + return -1; + } + + if (map_mapname2mapid(map_name_cur) < 0) { + sprintf(atcmd_output, "Source map not found: %s", map_name_cur); + clif_displaymessage(fd, atcmd_output); + return -1; + } + + if (map_mapname2mapid(map_name_new) >= 0) { // This will always show a warning in the console, but removing it is not worth the trouble. + sprintf(atcmd_output, "Destination map already exists: %s", map_name_new); + clif_displaymessage(fd, atcmd_output); + return -1; + } + + if ((res = map_addclonemap(map_name_cur, map_name_new)) < 0) { + sprintf(atcmd_output, "Failed to create a new map, error: %d", res); + clif_displaymessage(fd, atcmd_output); + return -1; + } + + sprintf(atcmd_output, "New map cloned (%s).", map_name_new); + clif_displaymessage(fd, atcmd_output); + return 0; +} + +/** + * Removes a cloned map. + * Usage: @delmap <map_name> + * @author Tokeiburu + */ +ACMD_FUNC(delmap) +{ + int res; + int m; + char map_name[MAP_NAME_LENGTH_EXT]; + + memset(map_name, '\0', sizeof(map_name)); + + if (!message || !*message || sscanf(message, "%15s", map_name) < 1) { + clif_displaymessage(fd, "Usage: @delmap <map_name>"); + return -1; + } + + if ((m = map_mapname2mapid(map_name)) < 0) { + sprintf(atcmd_output, "Map not found: %s", map_name); + clif_displaymessage(fd, atcmd_output); + return -1; + } + + if (map[m].clone_id == 0) { + clif_displaymessage(fd, "Only cloned maps can be removed."); + return -1; + } + + if ((res = map_delclonemap(map_name)) != 1) { + sprintf(atcmd_output, "Failed to remove map: %s", map_name); + clif_displaymessage(fd, atcmd_output); + return -1; + } + + sprintf(atcmd_output, "Cloned map removed (%s).", map_name); + clif_displaymessage(fd, atcmd_output); + return 0; +} + +/** + * Lists all cloned maps. + * Usage: @clonemaplist + * @author Tokeiburu + */ +ACMD_FUNC(clonemaplist) +{ + int i; + int count = 0; + + for (i = instance_start; i < MAX_MAP_PER_SERVER; i++) { + if (map[i].clone_id > 0) { + sprintf(atcmd_output, "%s (source: %s)", map[i].name, map[map[i].clone_id].name); + clif_displaymessage(fd, atcmd_output); + count++; + } + } + + sprintf(atcmd_output, "Found %d cloned map(s).", count); + clif_displaymessage(fd, atcmd_output); + return 0; +} + #include "../custom/atcommand.inc" /** @@ -10234,6 +10349,10 @@ void atcommand_basecommands(void) { ACMD_DEF(adopt), ACMD_DEF(agitstart3), ACMD_DEF(agitend3), + + ACMD_DEF(clonemap), + ACMD_DEF(delmap), + ACMD_DEF(clonemaplist), }; AtCommandInfo* atcommand; int i; diff --git a/src/map/chrif.c b/src/map/chrif.c index 4276434..cea689f 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -336,7 +336,7 @@ int chrif_save(struct map_session_data *sd, enum e_chrif_save_opt flag) { WFIFOB(char_fd,12) = (flag&CSAVE_QUIT) ? 1 : 0; //Flag to tell char-server this character is quitting. // If the user is on a instance map, we have to fake his current position - if( map[sd->bl.m].instance_id ){ + if( map[sd->bl.m].instance_id || map[sd->bl.m].clone_id ){ struct mmo_charstatus status; // Copy the whole status diff --git a/src/map/clif.c b/src/map/clif.c index 6c77044..97d8864 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -10352,7 +10352,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) #endif // Instances do not need their own channels - if( channel_config.map_enable && channel_config.map_autojoin && !map[sd->bl.m].flag.chmautojoin && !map[sd->bl.m].instance_id ) + if( channel_config.map_enable && channel_config.map_autojoin && !map[sd->bl.m].flag.chmautojoin && !map[sd->bl.m].instance_id && !map[sd->bl.m].clone_id ) channel_mjoin(sd); //join new map } else if (sd->guild && (battle_config.guild_notice_changemap == 2 || guild_notice)) clif_guild_notice(sd); // Displays at end diff --git a/src/map/map.c b/src/map/map.c index c7e326b..37d65f1 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -2095,7 +2095,7 @@ int map_quit(struct map_session_data *sd) { unit_remove_map_pc(sd,CLR_RESPAWN); - if( map[sd->bl.m].instance_id ) { // Avoid map conflicts and warnings on next login + if( map[sd->bl.m].instance_id || map[sd->bl.m].clone_id ) { // Avoid map conflicts and warnings on next login int16 m; struct point *pt; if( map[sd->bl.m].save.map ) @@ -2728,6 +2728,133 @@ int map_delinstancemap(int m) return 1; } +/*========================================== + * Add a cloned map + *------------------------------------------*/ +int map_addclonemap(const char *name, const char *newname) +{ + int src_m = map_mapname2mapid(name); + int dst_m = -1, i; + size_t num_cell, size; + + if(src_m < 0) + return -1; + + if(strlen(name) >= MAP_NAME_LENGTH) { + // against buffer overflow + ShowError("map_addclonemap: can't add long map name \"%s\"\n", name); + return -2; + } + + if(strlen(newname) >= MAP_NAME_LENGTH) { + // against buffer overflow + ShowError("map_addclonemap: can't add long map name \"%s\"\n", newname); + return -2; + } + + for(i = instance_start; i < MAX_MAP_PER_SERVER; i++) { + if(!map[i].name[0]) + break; + } + if(i < map_num) // Destination map value overwrites another + dst_m = i; + else if(i < MAX_MAP_PER_SERVER) // Destination map value increments to new map + dst_m = map_num++; + else { + // Out of bounds + ShowError("map_addclonemap failed. map_num(%d) > map_max(%d)\n",map_num, MAX_MAP_PER_SERVER); + return -3; + } + + if (map[src_m].clone_id) { + ShowError("map_addclonemap failed. Cannot clone a cloned map.\n",map_num, MAX_MAP_PER_SERVER); + return -4; + } + + // Copy the map + memcpy(&map[dst_m], &map[src_m], sizeof(struct map_data)); + + // Alter the name + // Due to this being custom we only worry about preserving as many characters as necessary for accurate map distinguishing + // This also allows us to maintain complete independence with main map functions + strncpy(map[dst_m].name,newname,MAP_NAME_LENGTH); + + map[dst_m].m = dst_m; + map[dst_m].clone_id = src_m; + map[dst_m].users = 0; + + memset(map[dst_m].npc, 0, sizeof(map[dst_m].npc)); + map[dst_m].npc_num = 0; + + // Reallocate cells + num_cell = map[dst_m].xs * map[dst_m].ys; + CREATE( map[dst_m].cell, struct mapcell, num_cell ); + memcpy( map[dst_m].cell, map[src_m].cell, num_cell * sizeof(struct mapcell) ); + + // Remove ontouch NPC cells + for (i = 0; i < num_cell; i++) { + map[dst_m].cell[i].npc = 0; + } + + // Remove all mob spawners (these should not be copied over) + for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) { + map[dst_m].moblist[i] = NULL; + } + + // Do not copy PVP nightmare drops + memset(map[dst_m].drop_list, 0, sizeof(map[dst_m].drop_list)); + + // Remove quest information from the map + map[dst_m].qi_data = NULL; + + size = map[dst_m].bxs * map[dst_m].bys * sizeof(struct block_list*); + map[dst_m].block = (struct block_list **)aCalloc(1,size); + map[dst_m].block_mob = (struct block_list **)aCalloc(1,size); + + map[dst_m].index = mapindex_addmap(-1, map[dst_m].name); + map[dst_m].channel = NULL; + map[dst_m].mob_delete_timer = INVALID_TIMER; + + map_addmap2db(&map[dst_m]); + + return dst_m; +} + +/*========================================== + * Deleting a cloned map + *------------------------------------------*/ +int map_delclonemap(const char* mapname) +{ + int m = map_mapname2mapid(mapname); + + if(m < 0 || map[m].instance_id || map[m].clone_id <= 0) + return 0; + + // Kick everyone out + map_foreachinmap(map_instancemap_leave, m, BL_PC); + + // Do the unit cleanup + map_foreachinmap(map_instancemap_clean, m, BL_ALL); + + if( map[m].mob_delete_timer != INVALID_TIMER ) + delete_timer(map[m].mob_delete_timer, map_removemobs_timer); + + // Free memory + aFree(map[m].cell); + map[m].cell = NULL; + aFree(map[m].block); + map[m].block = NULL; + aFree(map[m].block_mob); + map[m].block_mob = NULL; + map_free_questinfo(m); + + mapindex_removemap( map[m].index ); + map_removemapdb(&map[m]); + memset(&map[m], 0x00, sizeof(map[0])); + map[m].mob_delete_timer = INVALID_TIMER; + return 1; +} + /*========================================= * Dynamic Mobs [Wizputer] *-----------------------------------------*/ @@ -2838,6 +2965,10 @@ void map_removemobs(int16 m) *------------------------------------------*/ const char* map_mapid2mapname(int m) { + if (map[m].clone_id) { // Clone map check + return map[map[m].clone_id].name; + } + if (map[m].instance_id) { // Instance map check struct instance_data *im = &instance_data[map[m].instance_id]; diff --git a/src/map/map.h b/src/map/map.h index ec0b2b0..762ab2d 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -705,6 +705,9 @@ struct map_data { unsigned short instance_id; int instance_src_map; + // Clone map system + unsigned short clone_id; + /* rAthena Local Chat */ struct Channel *channel; @@ -820,6 +823,10 @@ int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, i int map_addinstancemap(const char *name, unsigned short instance_id); int map_delinstancemap(int m); +// Clone map system +int map_delclonemap(const char* mapname); +int map_addclonemap(const char *name, const char *newname); + // player to map session void map_addnickdb(int charid, const char* nick); void map_delnickdb(int charid, const char* nick); diff --git a/src/map/pc.c b/src/map/pc.c index 66a42f4..07d395b 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -5599,6 +5599,11 @@ bool pc_memo(struct map_session_data* sd, int pos) pos = 0; } + if( map[sd->bl.m].clone_id ) { + clif_displaymessage( sd->fd, "You cannot create a memo in this map." ); + return false; + } + if( map[sd->bl.m].instance_id ) { clif_displaymessage( sd->fd, msg_txt(sd,384) ); // You cannot create a memo in an instance. return false; diff --git a/src/map/script.c b/src/map/script.c index f53b799..9f59068 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -9607,6 +9607,11 @@ BUILDIN_FUNC(savepoint) y = script_getnum(st,4); m = map_mapindex2mapid(map_idx); + if( map[m].clone_id ) { + ShowError("buildin_savepoint: You cannot create a savepoint on this map %s.\n", str); + return SCRIPT_CMD_FAILURE; + } + if (cid_pos == 7) { int dx = script_getnum(st,5), dy = script_getnum(st,6), x1 = x + dx, y1 = y + dy, clonemap.diff
  20. You can check roBrowser's sprite loading; it's very clear and on point: https://github.com/vthibault/roBrowser/blob/master/src/Loaders/Sprite.js
  21. Did you update your sources to the latest revision? This commit should have fixed your issue : https://github.com/rathena/rathena/commit/afa880ebd6bd55c2ecaa479cf00697db568ab353 (I'm unable to reproduce your problem.) If that didn't work or if you're already fully up to date, could you provide a bigger script sample?
  22. Heya, I would like to suggest adding @reloadnpc as an official atcommand to reload scripts. Currently we have to use @unloadnpcfile followed by @loadnpc which is a bit awkward when you want to create a new script. With @reloadnpc, you'd be able to load and reload NPCs with the same command. ACMD_FUNC(reloadnpc) { if (!message || !*message) { clif_displaymessage(fd, "Usage: @reloadnpc <file name>"); return -1; } if (npc_unloadfile(message)) clif_displaymessage(fd, msg_txt(sd,1386)); // File unloaded. Be aware that mapflags and monsters spawned directly are not removed. if (!npc_addsrcfile(message) || !npc_parsesrcfile(message,true)) { clif_displaymessage(fd, msg_txt(sd,261)); return -1; } npc_read_event_script(); clif_displaymessage(fd, msg_txt(sd,262)); return 0; }
  23. 'party_id = getcharid(1); 'ins_rid = getattachedrid(); donpcevent instance_npcname("#ins_die_event_sub") + "::OnPartyCheck"; This works because the player is being attached to the script (it stops working if you detach the player, though). I really like your alternative solution actually! You wouldn't have to deal with the attach/detachrid uglyness. But how would you identify players leaving the party or being added to the party? (I don't think you can prevent a player from leaving a party.)
  24. There are no easy ways of doing this, OnPCDieEvent is annoying to use. I would try the following instead: 1@tower,0,0,0 script #ins_die_event_sub -1,{ end; OnPartyCheck: 'player_alive = 0; getpartymember 'party_id, 2; for (.@i = 0; .@i < $@partymembercount && !'player_alive; .@i++) { if (isloggedin($@partymemberaid[.@i]) && $@partymemberaid[.@i] != 'ins_rid) { // Logged out players are still active if (attachrid($@partymemberaid[.@i])) { if (HP > 0 && strcharinfo(3) == instance_mapname("1@tower")) { 'player_alive++; } detachrid(); } } } // Run code back on the instance if ('player_alive == 0) { announce "All players have died.", 0; } end; } - script #ins_die_event -1,{ end; OnPCDieEvent: OnPCLogoutEvent: if (strcharinfo(3) == instance_mapname("1@tower")) { // Do not perform the party checks here directly, this script // is never attached to the instance. 'party_id = getcharid(1); 'ins_rid = getattachedrid(); donpcevent instance_npcname("#ins_die_event_sub") + "::OnPartyCheck"; } end; } There are few things you have to be very careful with such scripts. OnPCDieEvent is a global event label which is not attached to your instance nor is it duplicated. All players who die on your server will trigger this event label, hence why you have to check if the player has the instance (and why you should not put the NPC in the instance map). The usage of a donpcevent is to force the script to run back on the instance after it's detached from the players' checks. You also cannot rely on $@partymembercount alone since some members could be offline in the party. Players can also leave the party or logout, which you have to take into account.
×
×
  • Create New...