-
Posts
700 -
Joined
-
Last visited
-
Days Won
109
Content Type
Profiles
Forums
Downloads
Jobs Available
Server Database
Third-Party Services
Top Guides
Store
Everything posted by Tokei
-
Care to elaborate on that? I'm always up for improving GRF Editor, but without any information, this doesn't really mean anything for me.
-
Heya, I've been receiving a lot of messages regarding encryption (again), so I wanted to clarify a few things: Your PMs regarding encryption matters will be redirected to this post. I will not be making a paid version of GRF Editor for encryption. Yes, I am aware there is someone selling a decryption tool. No, this has nothing to do with GRF Editor being open source. GRF Editor has been open source for a very long time. No, I will not be updating the encryption DLL for the foreseeable future. GRF Editor now has an updated encryption tool to block this decryption tool. GRF Editor is open source (https://github.com/Tokeiburu/GRFEditor), there's no need to pay to see the source (a few people made this request already...). As of GRF Editor 1.8.7.2 and above, it is now possible to make your own custom encryption library and link it with GRF Editor (this is meant for developers). Here is the project file: comp_x86.rar This is a C++ project made for Visual Studio 2022. You can use a lower toolset for older clients as it was originally made using Visual Studio 2010, which should be more than enough. The respective Visual C++ Redistributables should be installed (x86 for the client and x64 for GRF Editor). When making the encrypt.dll for GRF Editor: Use "Release" and "x64". The DLL will be compiled at comp_x86\Release\x64_GrfEditor\encrypt.dll "#define GRF_EDITOR" in cps.h must be defined in cps.h. This exposes the encrypt/decrypt functions for GRF Editor. The DLL can then be loaded in GRF Editor from Tools > Settings > Application > Encryption method... GRF Editor uses these methods for encryption/decryption: DLL int encrypt(BYTE* key, UInt32 key_len, BYTE* compressed_data, UInt32 compressed_len, UInt32 uncompressed_len); DLL int decrypt(BYTE* key, UInt32 key_len, BYTE* compressed_data, UInt32 compressed_len, UInt32 uncompressed_len); The compressed_data length cannot be modified. The uncompressed_len can be used for your encryption algorithm, but otherwise it isn't useful for anything. The encryption happens after the compression (raw data > zlib/lzma compression > encryption). It is the final method applied on the data. You can make a custom compression library to change the compression data length. The "key" parameter is the one used by GRF Editor when loading an encryption key from the software. You're free to ignore this parameter if you're not going to use this. The key from GRF Editor is 256 bytes in length. When making the cps.dll for your client: Use "Release" and "Win32". The DLL will be compiled at comp_x86\Release\x86_Client\cps.dll "#define GRF_EDITOR" in cps.h must NOT be defined. Comment it out if it isn't automatically undefined (using Win32 should undefine it by default). The most important function is "void decryptSub(BYTE* key, UInt32 key_len, BYTE* data, UInt32 data_len, UInt32 seed)" in cps.cpp. The function decrypts the data in the GRF using the key provided by GetGrfEncryptionKey(). This part needs to be defined on your end as the key provided is just a dummy one. Hide it, do whatever you want. By default, the encryption uses an RSA encryption method. This project has no protection on the DLL whatsoever. That part is entirely up to you. Make sure the client-side version of your cps.dll cannot be loaded in GRF Editor directly as a custom compression library as this would... make the whole thing rather pointless. The methods of interest for the client side would be: DLL int uncompress(BYTE* output_data, UInt32* output_len, const BYTE* compressed_data, UInt32 compressed_len) int _encryptedUncompress(BYTE* uncomp, UInt32* uncompLength, const BYTE* comp, UInt32 compLength) If you're a developer and you think your version is better than the one provided by GRF Editor, feel free to PM me as I'll make it as a default option (I will request to see the source however). Likewise, you're also free to sell those encryption DLLs if you want to, that's entirely up to you (I will not be responsible for issues related to third party encryption libraries).
-
Script command 'callfunc' returned failure.
Tokei replied to Dolphin86's question in Scripting Support
You're trying to define a function within a script; those two should be separated. Usually you'd put functions in a different script file, but that's me nitpicking a little. Your script would look like this: script function F_res... NPC scripts below You have to put the script functions before the NPC scripts otherwise they won't be recognized while parsing the script. But... clearly, you wanted to use callsub rather than callfunc, which is slightly different. "callfunc" functions are global and can be used by all scripts, in different files, while "callsub" are functions that can be used within the current script only. A callsub would look like this: //============================ // functions //============================ // F_restore F_res: .@rental_item = getarg(0); .@item_1 = getarg(1); .@item_2 = getarg(2); .@item_3 = getarg(3); if(rentalcountitem(.@rental_item) < 1){ //... return; // Then you call the function with: callsub "F_res",40081,40225,40016,40082; Your script also relies way too much on gotos. If the goto isn't used more than once, then it has no reason to be. It's harder to follow your script the way it is now than simply putting the code in the right place it belongs to. Well, not that it matters too much, just a general suggestion. Anyway, using callsub, your script would look like this: moc_para01,46,36,4 script Guild Blacksmith 826,{ if(countitem(40056) < 1){ goto notmember; } // Text //============================= soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Oh hello there, how can I help you ?"; switch(select("- Restore:- Upgrade Item Grade:- Cancel")){ case 1: goto restore1; case 2: goto upgradeclass; case 3: end; } end; // Restore //============================= restore1: clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Oh, you need restore?"; switch(select("- Restore Information:- Restore Item:- Cancel")){ case 1: clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Well, your item usually comes"; mes "with a 7-day rental period,"; mes "but if you wish to extend"; mes "that rental period,"; mes "you can come to me"; mes "for a complete restore."; next; clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "To restore weapons or armor, I will need"; mes "- 1 Restore Token"; mes "- 10 Durengo Coins"; next; clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Just for the record, I will only"; mes "restore Tier 2 and above items."; next; clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "For tools, I will just need"; mes "- 1 Restore Token"; mes "- 5 Durengo Coins"; close; case 2: clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Ok then, what item do you need to restore?"; switch(select("- Weapon:- Armor:- Tools:- Cancel")){ case 1: goto wep_restore; case 2: goto armor_restore; case 3: goto tool_restore; case 4: end; } case 3: end; } wep_restore: clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Select Weapon Tier"; switch(select("- Tier 2:- Cancel")){ case 1: clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Select the weapon you wish to restore"; switch(select("- Improvise Dagger:- Cancel")){ case 1: goto improdaggres; case 2: end; } case 2: end; } end; //============================ // functions //============================ // F_restore F_res: .@rental_item = getarg(0); .@item_1 = getarg(1); .@item_2 = getarg(2); .@item_3 = getarg(3); if(rentalcountitem(.@rental_item) < 1){ clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "You don't have a "; mes ""+ getitemname(.@rental_item) +"."; close; } if(countitem(.@item_1) < 1){ clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "You don't have a "+ getitemname(.@item_1) +"."; close; } if(countitem(.@item_2) < 10){ clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "You don't have 10 "+ getitemname(.@item_2) +"."; close; } clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Great, here is your new "+ getitemname(.@item_3) +"."; delitem (.@rental_item, 1); delitem (.@item_1, 1); delitem (.@item_2, 10); getitem (.@item_3, 1); close; improdaggres: clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Select Dagger Grade"; switch(select("- Grade A:- Grade B:- Grade C:- Grade D:- Grade E:- Cancel")){ case 1: callsub "F_res",40081,40225,40016,40082; break; case 2: end; case 3: end; case 4: end; case 5: end; case 6: end; } armor_restore: clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Work In Progress"; close; tool_restore: clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Work In Progress"; close; // Upgrade //============================= upgradeclass: clear; soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "Work In Progress"; close; // Members //============================= notmember: soundeffect "menu.wav",0; mes "^ce7e00 === Hafiz === ^000000"; mes "You need to be a member before I can assist you."; close; } -
This is related to an attempted fix for position lag, and therefore it is intended. It comes from the "clif_fixpos" packet which is triggered after the player uses a skill, as you've noticed. It synchronizes the client position with the server position. You will only see the character moving back if the client and server positions are different (so... it's a good thing?). The above won't help you too much though, sorry! There's a bigger issue at play here, which is to fix the difference between the client and server position instead of relying on the clif_fixpos packet. Even if you stop sending the clif_posfix packet so that the character doesn't change position, the position between the server and client still won't be a match. So fixing the "walking back" issue will just end up moving the problem elsewhere. If you still want to have a feel for it, you can change the line below with a 0, that way your character will no longer walk back. unit_stop_walking(src, 1); // Even though this is not how official works but this will do the trick. bugreport:6829 to unit_stop_walking(src, 0); // Even though this is not how official works but this will do the trick. bugreport:6829 But I don't recommend it.
-
Need help with monster summoning inside a custom instance
Tokei replied to Ruhn's question in Scripting Support
Well, normally you'd use what you did "--en--", or "--ja--". But if you want to pick the names yourself, you can do this: prontera,99,101,1 script Demon Tomb 565,{ mes "stuff..."; setarray .@mobIds, 1002, 1003, 1004, 1005, 1006; setarray .@mobNames$, "Poring 1", "Poring 2", "Poring 3", "Poring 4", "Poring 5"; .@mobIdx = rand(getarraysize(.@mobIds)); .@mobId = .@mobIds[.@mobIdx]; .@mobName$ = .@mobNames[.@mobIdx]; monster .@map$,99,107,.@mobName$,.@mobId,1,.@label_boss$,2; end; } If you prefer, you can use this approach below as well, same thing (I find it easier to handle in larger scripts, but there's really no difference). prontera,99,101,1 script Demon Tomb 565,{ mes "stuff..."; setarray .@mobIds, 1002, 1003, 1004, 1005, 1006; .@mobId = .@mobIds[rand(getarraysize(.@mobIds))]; monster .@map$,99,107,'mobNames$[.@mobId],.@mobId,1,.@label_boss$,2; end; OnInstanceInit: 'mobNames$[1002] = "Poring 1"; 'mobNames$[1003] = "Poring 2"; 'mobNames$[1004] = "Poring 3"; 'mobNames$[1005] = "Poring 4"; 'mobNames$[1006] = "Poring 5"; end; } -
Need help with monster summoning inside a custom instance
Tokei replied to Ruhn's question in Scripting Support
A couple things to learn here: OnInit applies when the script is loaded, while OnInstanceInit applies when the instance is created. So in your case, you want to use OnInstanceInit. "demon_lair" is the base map, but that's not the map inside your instance. You want to use instance_mapname("demon_lair"); Likewise, when you're setting up the event label, it is attempting to call it on the base NPC, not the instanced NPC. So you want to use instance_npcname(strnpcinfo(0)) + "::OnKilledAzhura" "set" is a very much outdated command, use "=" instead. "$AzhuraSpawned" is a global, permanent, variable. It is kept when your server restarts and it is also shared between instances. You want to use 'variable instead. So your script would look something like this (and using menu is weird): demon_lair,99,101,1 script Demon Tomb 565,{ mes "[Tomb]"; mes "This is the resting place of a great demon named Azhura."; next; mes "[Tomb]"; mes "You can summon him through Nightmarish Ornament and Evil Energy."; next; mes "[Tomb]"; mes "Put the Nightmarish Ornament and Evil Energy."; next; switch(select("Yes.:No.")) { case 1: if ('AzhuraSpawned) { mes "The Great Demon Azhura is already here!"; close; } if (countitem(41001) < 2) { mes "You don't have the required items!"; close; } delitem 41001, 2; mapannounce .Map$, "Prepare for the unleashed evil!!", bc_map; 'AzhuraSpawned = 1; monster .Map$, 99, 106, "Great Demon Azhura", 1973, 1, instance_npcname(strnpcinfo(0)) + "::OnKilledAzhura"; close; case 2: mes "[Tomb]"; mes "Come back when you have decided."; close; } end; OnKilledAzhura: 'AzhuraSpawned = 0; end; OnInstanceInit: .Map$ = instance_mapname("demon_lair"); end; } -
Goto Cmd Did Not Work On My Custom Script
Tokei replied to Dolphin86's question in Scripting Support
Hmm, you probably wanted to write countitem(40039) > 0 Since > 1 means that you need 2 items in your inventory for the condition to be true. -
I haven't tested this, but you could just remove the 4th job flag (instead of adding the 3rd job flag): changebase roclass(eaclass()&~EAJL_FOURTH); Though I'm sure you'll run into issues somewhere. You could also just use the class costumes, though it's definitely more annoying to setup.
-
It's a silly error either way. A signed value is converted to an unsigned value in this scenario, and since there is a comparison of n > -1 right prior the other check, then the comparison cannot fail (as both values are positive). Plus it's not like a string length would be 2B+ in length to begin with... you'd probably have to be more concerned about memory usage at this point. As @Chaos92 mentioned, the issue is on rAthena. It'll get fixed eventually.
-
That is... a strange issue, I'm not able to reproduce it. If you opened GRF Editor without the "Always reopen latest opened GRF", you will get a template GRF as such: As you can see, the window title is "GRF Editor - new *", with a star at the end, meaning it hasn't been saved yet. It also has a "data" folder in it. In your screenshot, you have neither. That means you've opened an actual GRF, but that also doesn't make sense since an opened GRF will display the full path in the title. As much as I'd like to fix this issue, it doesn't seem possible to reproduce. The error shows that the magic string is empty instead of "Master of Magic". That looks like an empty file...? That also doesn't make much sense. I would need additional information (well, assuming that error is worth fixing since it doesn't seem to trigger normally at all).
-
Fixed in 1.8.7.3. The issue comes from selecting "red objects/textures", objects/textures that cannot normally be selected because their resources aren't found in your current GRF(s). But by clicking on the "guildhall.gnd" node, it selects everything anyway. If you want to extract everything, I'd recommend adding more resource GRFs: (Version 1.8.7.3 also allows to read Alpha GRFs, for anyone interested...)
-
Please use "Copy exception" and paste the result here; I'm unable to reproduce.
-
A few things to note: "next" always comes before the switch/select statement, not after the "case" branches. If you absolutely want to do this, then you might want to use "clear" before your "mes" dialogues instead. When you use delitem, you should always change the status right afterwards. Be it a quest or a variable, something needs to be done after a delitem. Here's a very likely scenario in your case: the player registers, but then the script ends there or the player uses "cancel" in the next menu. He'll then have to pay with the coin again. While that's probably not a huge deal in this particular situation, that's something you should look out for. It could lead to some serious issues later on. The usage of labels is a tad excessive. You'd normally only want to use labels if the code is repeated. Otherwise it doesn't make much sense to make things more complicated. "countitem(id) > 1" means that will only trigger if the player has 2 items in his inventory. You wanted > 0 there. The "mastery" variable doesn't work well here; that's a permanent player variable and it's way too easy to break out of the script in your case. And since the mastery amount is saved, speaking with the NPC again will cause issues since it's already set to 3 (which is the main bug you were having since you probably tested the script more than 3 times at least). I'd personally use a flag variable instead, but since you're already doing checks using a specific item, might as well use that. Well, I'd still recommend using a flag, but I don't know how this script will be used. Since the dialogues were all repeats, I added an array instead with "OnInit". That makes it easier to add more masteries later on and it's easier to read/modify. moc_para01,27,35,4 script FARHANA A 532,{ soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Hello, welcome to GUILD HALL,"; mes "how may i help you ?"; next; switch(select("- Benefit become a member ?:- I wish to register as guild member:- Nothing")){ case 1: soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Becoming a member of GUILD HALL"; mes "will enable you to boost your"; mes "MASTERY, take guild quest, and"; mes "many more..."; next; switch(select("- MASTERY ?:- Guild Quest ?:- Nothing")) { case 1: soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Yes mastery, such as gathering,"; mes "cooking, weapon and armor Crafting"; mes "as you progress on doing more of"; mes "those activity you will gain"; mes "more points to unlock more"; mes "higher tier stuff.."; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "But sadly.. you can only have"; mes "3 main mastery to choose for."; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "as for the rest.."; mes "they will remain as tier 1"; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "I would highly recomend to have"; mes "friends with diffrent mastery"; mes "so you can support each others"; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "i guess that will that be all,"; mes "for now, see you soon.."; close3; case 2: soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Guild Quest is a great help,"; mes "each member start from low rank"; mes "such a [D Rank] but you can"; mes "higher rank as you completed"; mes "more quest, higher quest get's"; mes "better reward!"; close3; case 3: goto L_Cancel; } end; case 2: if (mastery_paid_price) goto L_MasteryMenuSelect2; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Ahh.. you wish to register,"; mes "sure i can do that, but i will"; mes "need 1 Durengo Coin"; next; switch(select("- I have it:- I need time")) { case 1: if (countitem(40016) < 1) { soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "I dont think you have"; mes "1 Durengo Coin..."; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "You can exchange 1000 zeny"; mes "for 1 Durengo Coin from"; mes "my friend next to me"; close3; } delitem 40016, 1; mastery_paid_price = 1; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "I will take your Durengo Coin now"; mes "oh yeah even if you cancel at any"; mes "part of these process, you will not"; mes "getting back that Durengo Coin"; mes "as it is require for precess fee"; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Great, now remember you can"; mes "only select 3 Mastery"; mes "and only those Mastery will be able"; mes "to level up, and as for the rest"; mes "it will remain as Tier 1.."; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Now before you pick a Mastery"; mes "if you are new to the server"; mes "i would highly sugguest to pick"; mes "GATHERING, PROCESSING and 1 of"; mes "your own pick."; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "But.. if you know what you are doing"; mes "then go for it.."; next; L_MasteryMenuSelect2: soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Now please select your"; mes "1st MASTERY"; mes "desire Mastery"; next; L_MasteryMenuSelect: .@menu$ = ""; for (.@i = 0; .@i < .mastery_count; .@i++) { .@menu$ += "- " + .mastery_names$[.@i] + ":"; } .@menu$ += "- I need time"; .@midx = select(.@menu$) - 1; if (.@midx >= .mastery_count) goto L_Cancel; goto L_MasteryConfirm; end; case 2: goto L_Cancel; } end; case 3: goto L_Cancel; } end; L_Cancel: soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "I shall see you soon.."; mes "Good Bye.."; close3; L_MasteryConfirm: // The mastery count is used by checking the amount of items the player has instead of using the 'mastery' variable. .@count = 0; for (.@i = 0; .@i < .mastery_count; .@i++) { .@count += countitem(.mastery_items[.@i]); } if (.@count >= 3) { soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Great now you have 3 main"; mes "MASTERY, remember if you"; mes "in lost or need help"; mes "ask in discord"; close3; } if (countitem(.mastery_items[.@midx]) > 0) { soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "You have already picked"; mes strtoupper(.@mastery_names$[.@midx]) + " MASTERY."; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Now select again.."; next; goto L_MasteryMenuSelect; } soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes strtoupper(.mastery_names$[.@midx]) + "...."; mes "nice choice, are you sure"; mes "you want to get"; mes strtoupper(.mastery_names$[.@midx]) + " MASTERY ?"; next; switch(select("- YES:- NO")){ case 2: goto L_Cancel; } soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Great pick!"; getitem .mastery_items[.@midx], 1; next; soundeffect "menu.wav",0; mes "^ce7e00 === FARHANA === ^000000"; mes "Now select again.."; next; goto L_MasteryMenuSelect; OnInit: // Could just use setarray, but this is easier to read for me. .@i = 0; .mastery_names$[.@i] = "Gathering"; .mastery_items[.@i] = 40008; .@i++; .mastery_names$[.@i] = "Weapon And Tool Crafting"; .mastery_items[.@i] = 40039; .@i++; .mastery_names$[.@i] = "Armor Crafting"; .mastery_items[.@i] = 40040; .@i++; .mastery_names$[.@i] = "Cooking"; .mastery_items[.@i] = 40038; .@i++; .mastery_names$[.@i] = "Slaughter"; .mastery_items[.@i] = 40041; .@i++; .mastery_names$[.@i] = "Processing"; .mastery_items[.@i] = 40007; .@i++; .mastery_count = .@i; end; }
-
Unfortunately, that's not an str effect. You cannot edit the Evil Land because it's a 3D ground effect. It also happens to be hard coded in the client. You can change the texture (data\texture\effect\evilland\demonground.tga), but that won't do much. I presume bRO is the official server, correct? Then it's a bit weird that you're lagging. Evil Land isn't a fancy effect at all, it's a single texture shown on the floor: I looked at rAthena's db/source and it's using Layout 1 instead of 0, which makes the effect being sent 9 times instead of 1, which is wrong of course. That would normally be the reason for your lag, but since it's bRO... then it doesn't make sense. I made a basic gray looking evil land if you want to give it a shot nonetheless. demonground.tga demonground_alpha_translucent.tga Well, unless bRO still uses a very old client, then there's really nothing you can do...
-
Fixed in 1.8.7.1. There was an issue with UV texture coordinates being all set to 0 on new tiles.
-
Hmm, there is an approximation that can be done which will probably be good enough. The point 2 is misleading; what is seen in the UI is a blend of the background + layers. You can't separate them as it's already a final image, if that makes sense. But you could in theory extract a transparency value by using a black and white background version of the composite image. Then do some math magic and approximate the bgra values. I've added the option in 1.1.0, though it's a bit awkward to use (found in File > Export as PNG...).
-
The purpose of encrypting the file table is to not be able to see the files anymore. While possible, retrieving the file listing would be... quite counter productive? Perhaps this feature is something you don't want to use as it doesn't seem to fit what your needs.
-
Can someone tell me how to add sounds with the Act Editor?
Tokei replied to roaddict's question in Third Party Support
There are two methods for doing that: Once you have the current frame, click on: Then add the name of the file, like "test.wav". The file should be in data\wav\test.wav You can add a bunch of sound names by editing the list instead: Then select the sound for the frame from the list. If the sound doesn't play when testing the sprite, then it's just because it wasn't found in the resources. That's not really important, but you can add more resources from File > Settings > Sound: -
Hmm, you'd definitely want to add more checks on this, but here: - script Mob Helper -1,{ OnNPCKillEvent: // Fixed improper usage of rand for "chance" if (500 < rand(10000)) end; if (mkTimed && gettimetick(2) <= mkTimed) end; // Don't use "set" anymore, that's way outdated. // Using getmonsterinfo isn't a bad idea, but it's somewhat useless/broken now. You'd have to expand on it in the source to make it useful. getunitdata(killedgid, .@mobdata); // There's no reason to use player variables in this case, the variable has no purpose outside of the NPC block code. Therefore it should be a NPC variable. .@MobMode = .@mobdata[UMOB_MODE]; .@MobClass = .@mobdata[UMOB_CLASS]; if (!(.@MobMode & MD_CANMOVE)) end; if (!(.@MobMode & MD_CANATTACK)) end; // MD_BOSS/32 isn't a thing anymore either, so that code won't work. if (.@MobClass == CLASS_BOSS) end; // Too much wrong with these lines. //set @WoE[0],1288|1285|1830|1949|1950|1286|1287|1899|1829; //WoE IDs //for(set @n,0; @n < getarraysize(@WoE); set @n,@n+1){ if(@WoE[@n] == killedrid){ end; } } //WoE // You probably meant to use "setarray", and you also probably to use commas instead of separators: setarray @woe, 1288, 1285, ..; // Either way, that's inefficient considering this code runs on all killed monsters. Honestly I'd do the whole thing via the source instead since running a script everytime you kill a monster is overkill. Anyhow, use a dictionary seach: if (.badMob[killedrid]) end; .@nTime = (300 + rand(0, 120)) - getmonsterinfo(killedrid, MOB_LV); // The main reason why your script didn't work: "summon" uses miliseconds, not seconds. .@nTime *= 1000; // s to ms summon "Ajudante " + getmonsterinfo(killedrid, MOB_NAME), killedrid, .@nTime; mkTimed = gettimetick(2) + .@nTime + rand(0, 60); message strcharinfo(0), "[" + getmonsterinfo(killedrid, MOB_NAME) + "] Hello " + strcharinfo(0) + " I will help you for a few minutes!"; end; OnInit: // Define bad mobs here .badMob[1288] = 1; .badMob[1285] = 1; .badMob[1830] = 1; .badMob[1949] = 1; .badMob[1950] = 1; .badMob[1286] = 1; .badMob[1287] = 1; .badMob[1899] = 1; .badMob[1829] = 1; end; }
-
Heya, The HP/SP bars are data\texture\À¯ÀúÀÎÅÍÆäÀ̽º\basic_interface\gzeblue_left.bmp gzeblue_mid gzeblue_right And the AP bar is gzered_left / gzered_mid / gzered_right.
-
I fixed the hyperlink color, along with the brush selection and the search brush (same download link as before). As for the 4 GB limit, that's a structural limit with GRF files and there's nothing that can be done about it unless Gravity updates their file format. For more context, the file table offset is defined as an unsigned integer and the entries within the file table are also defined as unsigned integers. Four bytes of data cannot go beyond 4294967295; if they do, they'll wrap back to 0 and starts corrupting data in your GRF. I'm afraid this is technically impossible. If your GRF reached 5+ GB in size, then it means you've lost data within your GRF without you knowing about it. As I mentioned above, if you go past 4 GB with your file offsets, the data wraps back to 0. The fact that your GRF loads means that the file table offset was indeed beyond the limit and is probably written right in the middle of your GRF instead of at the end of it. The file table has overwritten some of your files within your GRF and some indexes are linking to invalid data (and not by a small amount, about 1/5th of your GRF is unreadable). When that happens, the client won't be able to decompress the data as it won't be a valid zlib entry, and it will attempt to read from the next GRF listed.
-
Updated to 1.8.6.6, this update mostly targets the map rendering: Added/fixed the RSM2 to RSM1 option when right-clicking a RSM2 file. The "anim" option will keep the rotation key frames, but it is not a perfect conversion; the scale/texture/offset key frames cannot be converted. The "flat" option will remove any kind of animation but will always be a perfect match with the original. I know this feature isn't that useful anymore since most clients support RSM2 files just fine, but I still hear people wanting to downgrade RSM2 files. You'll need to set the scale to (-1,1,1) in BrowEdit if you want to keep original model direction. Added the "Downgrade" option when right-clicking a RSW file. This will set the version of the RSW to 0x201 and GND to 0x107. All RSM2 files inside the RSW file will be converted to RSM1 and added to your GRF. The RSM2 models are also placed in their correct positions after conversion. Fixed the FPS counter. Added a "minimap" option to create minimaps: Added a face culling option to reflect the client's behavior more accurately. Added a copy/paste feature meant for BrowEdit 3 by using shift-left-click: Please keep your drama/trolling out of this thread, thank you. This forum is primarily dedicated towards developers, not players. I'd refer you to RMS, reddit or your own server forum instead.
-
They shouldn't be encrypted, no. Unless Gravity revamped the rrf format, this tool should still work. You don't have to handle newer packets unless you want some information from them. For example, if there is a newer packet for displaying NPC messages, then they would simply not show up in the tool anymore until you added a parsing method for it. Anyway, you'd have to share your replay with me in private to look into what's happening. It's too vague as it is.
-
You forgot the semi-colon after the first line: ALTER TABLE `char` ADD `achievement_points` INT to ALTER TABLE `char` ADD `achievement_points` INT; Other than that, it ran fine for me. (Also you may want to use a proper SQL editor like HeidiSQL instead of phpmyadmin...)
-
Well that's a bit of a vague error, but usually that means either one of these two: You do not have the .net 3.5 and .net 4.0 properly installed. Or you have conflicting dlls in your folder where GRF Editor is running from (doesn't seem likely though?). So for starter, I'd recommend reinstalling .net 4.0, then .net 3.5, and go from there I suppose.