Jump to content

Tokei

Members
  • Posts

    700
  • Joined

  • Last visited

  • Days Won

    108

Everything posted by Tokei

  1. Heya, The issue is that act files must be loaded with their sprite counterpart to function properly. The act file stores the width and height of the image for each layer and this data is loss otherwise (causing your position issues). The script works fine otherwise. foreach (var file in Directory.GetFiles(@"C:\Users\medakas\data\sprite\¸ó½ºÅÍ\", "*.act")) { var act1 = new Act(file, file.ReplaceExtension(".spr")); act1.AnimationExecute(4, action => { action.Frames = new List<Frame> { new Frame() }; }); act1.Save(file); } Edit: Actually, I looked more into it and that explanation turns out to be somewhat incorrect. At some point, the behavior was changed so that a Sprite object is automatically created if an Act object is created without specifying the Sprite path. The problem with that is that the saving function rewrites the Width/Height values if a Sprite object exists. Act Editor abstracts the Width/Height properties of the layers as that'd just be too annoying for the endusers to setup in the first place. But this is a bug; if no image is associated with the layer with a Sprite present, it should not default to 0 Width/Height.
  2. Heya, fixed just now, in 1.8.4.1.
  3. Heya, There are multiple approaches for this issue. The first one is "lazy" but it is usually good enough for most people and it is much simpler. You run a script on all players to remove their achievement and then you run a SQL command to remove all achievements. So something along these lines: OnClock0000: // At midnight, everyday donpcevent strnpcinfo(0) + "::OnResetAchievement"; end; OnResetAchievement: donpcevent strnpcinfo(0) + "::OnResetAchievementSub"; query_sql("DELETE FROM `achievement` WHERE `id` = 100"); end; OnResetAchievementSub: addrid 0; achievementremove 100; end; The issue with the above is that the query is ran on the map-server and therefore will lag you depending on the size of your achievement table (the same goes for any query ran on the map-server). This solution is also not "atomic" and can fail in some situations where a player is logging on while the script is being ran, and the char-server has already sent the achievement data and hasn't been received by the player yet. The chances of the last scenario happening are very low though. An alternative would be to keep the achievements in the database, but only delete them when the player logs on. You would still have to delete them on the online players though. So something like this: OnClock0000: donpcevent strnpcinfo(0) + "::OnResetAchievement"; $ach_100_start = gettimetick(2); end; OnPCLoginEvent: .@aid = 100; .@res = achievementinfo(.@aid, ACHIEVEINFO_COMPLETEDATE); if (.@res > 0 && .@res < $ach_100_start) { achievementremove 100; } end; OnResetAchievement: addrid 0; achievementremove 100; end; The above works relatively well. It doesn't have concurrency issues, it won't lag your server either. The downside there would be that the achievements would still exist in your sql table. Also, both of the above need to attach a script to the player, which will cause issues if a player is already talking to a NPC (it will terminate the previously ran script). The proper solution would be to run the SQL query on the char-server instead using an inter-server packet (or using a SQL thread, if that PR is merged). Then you would run a custom script command that iterates through online players and removes their achievement with achievement_remove without ever attaching a script to them. The last solution is the best and will work in all scenarios with no in-game lag. Though it is somewhat annoying to code I suppose. (Those scripts were not tested, so you may have to fix the errors yourself.)
  4. Updated to 1.8.4.0: Added proper support for previewing RSM2 file format up to version 0x203. Enabled animations for RSM2 models. Support translation animations. Support texture animations.
  5. Heya, You need to install the softwares you want to use yourself. The default guide from rAthena's wiki should be a good place to start with: https://github.com/rathena/rathena/wiki/Install-on-Debian I wouldn't recommend using phpMyAdmin, but there are plenty of guides around. This one includes steps for phpMyAdmin and fits what you plan on doing:
  6. The costume property in the iteminfo is for the placement of the items in the storage (there is a special costume tab dedicated for this).
  7. You can also generate thor files directly with GRF Editor, which you might find easier to use. I know you already found a solution, but I thought I'd mention it...!
  8. Hello, This can happen if you have mixed DLLs where the executable is. GRF Editor uses embedded DLLs, so you can delete all other DLLs in the same folder as where you installed GRF Editor. If that doesn't solve the issue... I'd suggest to make sure you're at the latest version (currently at 1.8.3.7) and have both .NET 3.5 and 4.0 installed.
  9. Updated to 1.8.3.7: Fixed an issue where directory/uncompressed data files would not be visible when loading a GPF file.
  10. Heya, Hmmm, first of all, you can't convert a RSM2 file to RSM just by changing its extension. GRF Editor shows the model properly because it reads the file header and sees it as a RSM2 file regardless of the extension. You need to either actually downgrade the models or simply update your client to a more recent version. As for ba_lost working using this method, that is just because the client works the same; it reads the header of the file, not the extension.I don't know if 2019-06-26's client is able to read RSM2 files or not. If we assume it does, then your issue is with the RSW map format, you'd have to downgrade it (which is fairly straightforward to do). Updating the client is the easier option either way though, and a long term one at that. You'll have to do it sooner or later and then it will just work out by itself: (I need to update GRF Editor to read the newest RSW format in nif_dun02 map apparently... not that anything has really changed.) Best of luck!
  11. Well in that case it'd probably be best to limit the amount of announces to 1 per account. You'd avoid a whole bunch of issues with that. The following resolves your server restart issues, though I'm... too lazy to make a GM handling of those announces. prontera,187,210,3 script Broadcaster#1::BC 894,{ .@npcname$ = "^FF9300 Broadcaster ^000000"; .@header$ = "[^0000ff" + .@npcname$ + "^000000]"; mes .@header$; mes "Hi, I'm the Broadcaster."; mes "I can Broadcast a message for you."; mes " "; mes " "; mes " "; mes "It costs ^ff0000" + .broadcastfee + "^000000 zeny."; next; mes .@header$; mes "Would you like to Broadcast?"; next; switch (select("Yes:Nevermind:Auto-broadcast:")) { case 1: if (Broadcast > gettimetick(2)) { mes .@header$; mes "Sorry you have to wait for 1 min."; close; } if (Zeny < .broadcastfee) { goto L_NotEnoughZeny; } mes .@header$; mes "Please input your message."; next; input .@broadcast$; Zeny -= .broadcastfee; announce "Shout from " + strcharinfo(0) + ": " + .@broadcast$ + "", 0, 0x5AFF00; // Edit 5AFF00 for color code HTML Color Code Broadcast = gettimetick(2) + 60; //Timer 60 = 1 minute/s dispbottom "Broadcaster: Please wait for 1min until next broadcast to avoid flooding."; end; case 2: mes .@header$; mes "Suit yourself."; close; case 3: mes .@header$; mes "Hi, I can automatically broadcast messages for you!"; mes "It will cost you ^ff0000" + .auto_broadcastfee + "^000000 zeny per broadcast."; next; .@aid = getcharid(3); switch(select("Proceed:Check status:Exit")) { case 1: if ($bc_announces_delay[.@aid]) { mes .@header$; mes "You already have an auto announce for this account."; close; } mes .@header$; mes "Please input your message."; next; input .@broadcast$; mes .@header$; mes "How many times do you want to broadcast?"; mes "Min: 1"; mes "Max: 100"; next; input .@repeat, 1, 100; if (.@repeat < 1 || .@repeat > 100) { mes .@header$; mes "Suit yourself."; close; } .@delay = 3; //mes .@header$; //mes "Delay between announces?"; //mes "Min: 3"; //mes "Max: 20"; //next; //input .@delay, 3, 20; // //if (.@delay < 3 || .@delay > 20) { // mes .@header$; // mes "Suit yourself."; // close; //} .@cost = .auto_broadcastfee * .@repeat; mes .@header$; mes "You want to broadcast:"; mes "^ff0000" + .@broadcast$ + "^000000"; mes "Every 3 minutes for ^00ff00" + .@repeat + "^000000 time(s)?"; mes "It will cost you a total of ^ff0000" + .@cost + "^000000 zeny."; next; switch(select("Proceed:Cancel")) { case 2: mes .@header$; mes "Suit yourself."; close; } if (Zeny < .@cost) { goto L_NotEnoughZeny; } Zeny -= .@cost; $bc_announces_timer[.@aid] = 0; $bc_announces_repeat[.@aid] = .@repeat; $bc_announces_delay[.@aid] = .@delay * 60; $bc_announces_mes$[.@aid] = "Shout from " + strcharinfo(0) + ": " + .@broadcast$; $bc_announces_aid2idx[.@aid] = $bc_announces_count; $bc_announces_idx2aid[$bc_announces_count] = .@aid; $bc_announces_count++; close; case 2: if ($bc_announces_delay[.@aid] == 0) { mes .@header$; mes "You currently have no auto announces."; close; } mes .@header$; mes "Your current announce is as follow:"; mes "^ff0000" + $bc_announces_mes$[.@aid] + "^000000"; mes "It will be announced again in " + $bc_announces_timer[.@aid] + " second(s)."; mes "It will repeat " + $bc_announces_repeat[.@aid] + " more time(s)."; next; switch(select("Okay:Cancel announce:")) { case 1: mes .@header$; mes "..."; close; case 2: mes .@header$; mes "Your zeny will not be refunded, are you sure you want to cancel?"; next; switch(select("No:Yes")) { case 1: mes .@header$; mes "Suit yourself."; close; } if ($bc_announces_delay[.@aid] == 0) { // It already ended? end; } callsub L_RemoveAutoAnnounce, .@aid; mes .@header$; mes "All done."; close; } end; case 3: mes .@header$; mes "Suit yourself."; close; case 4: close; } close; } end; L_NotEnoughZeny: mes .@header$; mes "You don't have enough zeny."; close; OnTimer1000: freeloop(1); for (.@i = 0; .@i < $bc_announces_count; .@i++) { .@aid = $bc_announces_idx2aid[.@i]; $bc_announces_timer[.@aid]--; if ($bc_announces_timer[.@aid] <= 0 && .@didannounce == false) { announce $bc_announces_mes$[.@aid], 0, 0x5AFF00; $bc_announces_repeat[.@aid]--; $bc_announces_timer[.@aid] = $bc_announces_delay[.@aid]; if ($bc_announces_repeat[.@aid] <= 0) { callsub L_RemoveAutoAnnounce, .@aid; .@i--; } .@didannounce = true; // Prevents overlapping of announces } } freeloop(0); initnpctimer; end; L_RemoveAutoAnnounce: .@aid = getarg(0); .@idx = $bc_announces_aid2idx[.@aid]; $bc_announces_timer[.@aid] = 0; $bc_announces_repeat[.@aid] = 0; $bc_announces_delay[.@aid] = 0; $bc_announces_mes$[.@aid] = ""; .@last_aid = $bc_announces_idx2aid[$bc_announces_count - 1]; $bc_announces_idx2aid[.@idx] = .@last_aid; $bc_announces_idx2aid[$bc_announces_count - 1] = 0; $bc_announces_aid2idx[.@last_aid] = .@idx; $bc_announces_count--; return; OnInit: .broadcastfee = 3000000; .auto_broadcastfee = 500000; initnpctimer; end; }
  12. Your initial script has some oddities (regarding variable scope). Hmm, I see a few things you'd probably to worry about with the proposed solution: A player can do multiple auto announces. There is no way of tracking the active announcements. Once the auto announce is bought, it is fully detached from the player and cannot be linked back to him. The announces will get lost on script reload/server reboots. You'd probably want to use global arrays and have the announces displayed with a OnTimer1000 checks instead. With that being said, the following is probably what you're looking for. I personally prefer to have the loop on a separate event, but the end result is very similar in this case. prontera,187,210,3 script Broadcaster#1::BC 894,{ .@npcname$ = "^FF9300 Broadcaster ^000000"; .@header$ = "[^0000ff" + .@npcname$ + "^000000]"; mes .@header$; mes "Hi, I'm the Broadcaster."; mes "I can Broadcast a message for you."; mes " "; mes " "; mes " "; mes "It costs ^ff0000" + .broadcastfee + "^000000 zeny."; next; mes .@header$; mes "Would you like to Broadcast?"; next; switch (select("Yes:Nevermind:Auto-broadcast")) { case 1: if (Broadcast > gettimetick(2)) { mes .@header$; mes "Sorry you have to wait for 1 min."; close; } if (Zeny < .broadcastfee) { goto L_NotEnoughZeny; } mes .@header$; mes "Please input your message."; next; input .@broadcast$; Zeny -= .broadcastfee; announce "Shout from " + strcharinfo(0) + ": " + .@broadcast$ + "", 0, 0x5AFF00; // Edit 5AFF00 for color code HTML Color Code Broadcast = gettimetick(2) + 60; //Timer 60 = 1 minute/s dispbottom "Broadcaster: Please wait for 1min until next broadcast to avoid flooding."; end; case 2: mes .@header$; mes "Suit yourself."; close; case 3: mes .@header$; mes "Hi, I can automatically broadcast messages for you!"; mes "It will cost you ^ff0000" + .auto_broadcastfee + "^000000 zeny per broadcast."; next; switch(select("Proceed:Check status:Exit")) { case 1: mes .@header$; mes "Please input your message."; next; input .@broadcast$; mes .@header$; mes "How many times do you want to broadcast?"; mes "Min: 1"; mes "Max: 100"; next; input .@repeat, 1, 100; if (.@repeat < 1 || .@repeat > 100) { mes .@header$; mes "Suit yourself."; close; } .@cost = .auto_broadcastfee * .@repeat; mes .@header$; mes "You want to broadcast:"; mes "^ff0000" + .@broadcast$ + "^000000"; mes "Every 3 minutes for ^00ff00" + .@repeat + "^000000 time(s)?"; mes "It will cost you a total of ^ff0000" + .@cost + "^000000 zeny."; next; switch(select("Proceed:Cancel")) { case 2: mes .@header$; mes "Suit yourself."; close; } if (Zeny < .@cost) { goto L_NotEnoughZeny; } Zeny -= .@cost; $@bc_announce$ = "Shout from " + strcharinfo(0) + ": " + .@broadcast$; $@repeat = .@repeat; donpcevent strnpcinfo(3) + "::OnAutoAnnounce"; close; case 2: // ?? end; case 3: mes .@header$; mes "Suit yourself."; close; } close; } end; L_NotEnoughZeny: mes .@header$; mes "You don't have enough zeny."; close; OnAutoAnnounce: .@repeat = $@repeat; .@broadcast$ = $@bc_announce$; while (.@repeat > 0) { announce .@broadcast$, 0, 0x5AFF00; // Edit 5AFF00 for color code HTML Color Code sleep 180000; .@repeat--; } end; OnInit: .broadcastfee = 3000000; .auto_broadcastfee = 500000; end; }
  13. Heya, You can use mob_drop.txt in your db folder, and make drops protected from steal: // Monster Drop Database // Add drop item to monster // // Structure: // <mobid>,<itemid>,<rate>{,<randopt_groupid>,<flag>} // // <mobid> : Monster ID. See db/[pre-]re/mob_db.txt // <itemid> : Item ID. // <rate> : 1 = 0.01% // 100 = 1% // 10000 = 100% // Just like rate in mob_db.txt, adjusted by battle_config. // To remove original drop from monster, use 0 as rate. // Optional: // <randopt_groupid> : If set, the dropped item will be modified by Random Option Group based on db/[pre-]re/item_randomopt_group.txt // <flag> : 1 - The item is protected from steal. // 2 - As MVP Reward 1063,1102,100,RDMOPTG_None,1
  14. ? is used for regex searches, such as *col??ction* That's why you can't do a search for this symbol. Try searching for this instead: 뮜* Or *collection\뮜*
  15. Heya, If the server host is on your machine, this won't work. You'll need to connect to your server using your local IP regardless of your settings. Others will be able to connect to your server through your WAN IP though.
  16. Well the group is being chosen there: int rnd_value = rnd() % (randomopt_group->total_weight); Using a debugger would show you where the issue is. Otherwise, you can add debug lines, I suppose: int rnd_value = rnd() % (randomopt_group->total_weight); int total = 0; int j; ShowDebug("itemdb_add_randomopt: total_weight '%d', total '%d', rnd_value '%d'\n", randomopt_group->total_weight, randomopt_group->total, rnd_value); If the total_weight isn't 4, then it wasn't read properly and something's missing near g->total_weight += rate; g->total++;
  17. Right-click the sprite you want to start at and use "Replace..."
  18. What Kreustoo said will fix most of your issues. For example: prontera,156,174,3 script Event Warper 77,{ if (!$arenac_gate) { mes "The event is closed."; end; } mes "Do you want to go in?"; next; switch(select("Yes.:No.:")) { case 1: // This is where you need to add your checks if (!$arenac_gate) { mes "The event is closed."; end; } warp "que_qaru01", 0, 0; end; } end; } However this is not perfect either. A player could still enter after the event is closed because there is a delay between warping a player on the map and the player landing on the map. That delay is the time the player takes to load the map (which can vary greatly depending of the player). What you could do is add an "OnTouch" area where players are warped. For instance: que_qaru01,155,155,3 script #event_check -1,2,2,{ end; OnTouch: if (!$arenac_gate) { dispbottom "You've entered the event too late, sorry!"; warp "prontera", 160, 184; end; } end; } Another approach is to disable the entrance warper NPC a good minute prior to your event starting. Then you start your event with some dialogues, for a good minute as well. Then you start your actual event. This ensures that players who are loading slowly get on the map in time for the main event. The previous solution is safer, though. As for players getting items that they shouldn't, you could add checks there as well. Not sure how your script works though, hard to say if that's feasible or not in your case. if (!$arenac_gate) { dispbottom "The event ended, you can't get rewards!"; warp "prontera", 160, 184; end; } getitem 501, 10; ...
  19. Heya, Could you show an example of the issue? It's not normal for the color to change in-game as it would indicate a new unknown behavior with indexed images in the client (and that's not very likely). If you do not want to share the file in public, please send it privately to me. Are you building the sprite file from multiple BMP files in a folder? If so, that would be the problem as you have different palettes in each one of your BMPs. You cannot do that as it's not really possible to mix palettes. If you use Act Editor, it should give you a warning that the palettes aren't matching and it will attempt to fix the issue for you by converting the image to something as close as possible as your image. Depending of the option you select, you can retain 100% of the image quality. If you choose to find the closest color in the sprite palette instead, then it will end up picking #001000 as it's the closest color match to #001603. Ideally you want to stick with one palette and not change colors in your BMP files though. You can also choose to convert the image to Bgra32 directly and that will solve the issue, with compression loss however.
  20. Try installing all VC++ redistributables from 2010 to 2015, x86. Should do the trick.
  21. Heya, Where to start... "sd" is a variable. What you're attempting to do is similar to... int x; int y; y = x + 5; It's impossible to assign a value to y if you don't know what the value of x is first. In your code, sd is the variable, but it is never really assigned. It is set to NULL as default (which is good practice), but NULL doesn't mean anything. If you look more closely, the value is actually assigned with "sd = map_charid2sd(script_getnum(st, 3))". It is commented with the "//" in front of it; you need to remove all of those "//" in front of the code. The "write access violation" comes from you attempting to do sd->guild_invite = guild_id, which in reality corresponds to NULL->guild_invite = guild_id. Obviously NULL doesn't "exist" and you can't assign guild_id to nothing. It's called a violation because you do not have the rights to write to NULL (to keep things simple anyway). sd isn't a cache, sd is the "map session data". It's what holds the information about your player's current session. You should not be doing queries to your database to update your character (unless your server is offline), that will not work and it's a bad approach. I don't know what you're trying to update though.
  22. Heya, Your main issue is that you're using "@rdice" instead of ".@rdice". The dot in front of the variable is for npc variables, while no dot is for a player variable. As a... recommendation, you should be using a donpcevent "NPCName::OnStart" after the close2 and move all your code there instead. You're trying to run the rest of the script on the player and that's not necessary here. Since the player was no longer attached to the script or was offline, the script failed to get the variable from the player.
  23. Only one error will be displayed even if there are more; in your case, the maps are simply encrypted so it's skipping all of them. Hence the 1kb maps.grf output file.
  24. Heya, I'll have a look later; the function itself can be decompiled from the labels: -- Function #0 GetLayerPriority = function(direction, layer, acc_id) local Items_List = TB_Layer_Priority.Items_List if nil ~= Items_List then local Item_Tb = Items_List[acc_id] if nil ~= Item_Tb then if nil ~= Item_Tb.Direction and nil ~= Item_Tb.Direction[direction] then return Item_Tb.Direction[direction] elseif nil ~= Item_Tb.Default then return Item_Tb.Default end end end if 2 == layer then return TB_Layer_Priority.Default_Bottom elseif 3 == layer then return TB_Layer_Priority.Default_Top elseif 4 == layer then return TB_Layer_Priority.Default_Mid elseif 8 == layer then return TB_Layer_Priority.Default_Robe end return -1 end
  25. Try unchecking "Change the ground textures for custom ones".
×
×
  • Create New...