Jump to content

Tokei

Members
  • Posts

    661
  • Joined

  • Last visited

  • Days Won

    90

Everything posted by Tokei

  1. There are many ways to achieve that: foreach (var action in act) { action.Frames = action.Frames.Take(1).ToList(); } If you need more... "control" with indexes and which animations to remove exactly, you can do it this way too: for (int aid = 0; aid < act.Actions.Count; aid++) { for (int fid = act.Actions[aid].Frames.Count - 1; fid >= 1; fid--) { act[aid].Frames.RemoveAt(fid); } } If you want to apply this to a batch of files, you can do it this way too: var path = @"C:\Sprites\"; foreach (var file in Directory.GetFiles(path, "*.act")) { var actFile = new Act(file); actFile.Actions.ForEach(p => p.Frames = p.Frames.Take(1).ToList()); actFile.Save(file); }
  2. You need more space on your Windows partition drive (normally C) as it writes temporary files in "%appdata%/GRF Editor". It can require to write the entire file to the temporary folder (and then move it to your actual desired location) if GRF Editor "defragments" your GRF while saving.
  3. Your error is too vague, it's impossible to know the issue from it. As for the size, it changes because it removes unused space from the file.
  4. Heya, Well, as the error says, your table index is nil, meaning either EFST_PAD_READER_KNIGHT or EFST_PAD_READER_CRUSADER is not defined (probably both and more below too). It's an issue with your lub, make sure your effectid.lub matches properly. This has nothing to do with NEMO.
  5. Hmm, you could just replace the gat files with the original ones. That should achieve the results you're looking for.
  6. Heya, Make sure you have the increase guild tax limit patch when you made your client. The restriction is client-sided, not server-sided.
  7. Heya, The error message tells you the error: if (.msg_die) message .@victimaid, "You have been killed by "+ .@killername$; if (.msg_kill) message .@killeraid, "You just killed "+ .@victimname$; The "message" script command on rAthena requires two strings as parameters. First parameter is the player name (which is pretty weird, not gonna lie), so you should change those to: if (.msg_die) message .@victimname$, "You have been killed by "+ .@killername$; if (.msg_kill) message .@killername$, "You just killed "+ .@victimname$; Though clearly this script wasn't meant for rAthena.
  8. Heya, Well there's a big amount of information missing here, but if it's activating stone curse, it's usually because the status given to sc_start is 0. This happens because your new status is not defined as a constant that can be used in your scripts. So go in map/script_constants.hpp, and make sure that your variable is listed, for example: export_constant(SC_CUSTOMSTATUS); Also make sure that the status used in your item script matches the name of the status correctly.
  9. if (rand(1,100) < 1){ ^ is an impossible condition. Use if (rand(100) < 1) { instead.
  10. It's a client issue. Change the clif_add_random_options function to skip empty entries. Something like... /// Fills in part of the item buffers that calls for variable bonuses data. [Napster] /// A maximum of 5 random options can be supported. static uint8 clif_add_random_options( struct ItemOptions buf[MAX_ITEM_RDM_OPT], struct item* it ){ nullpo_retr( 0, it ); uint8 count = 0; memset(buf, 0, sizeof(struct ItemOptions) * MAX_ITEM_RDM_OPT); for( int i = 0; i < MAX_ITEM_RDM_OPT; i++ ){ if( it->option[i].id ){ buf[count].index = it->option[i].id; // OptIndex buf[count].value = it->option[i].value; // Value buf[count].param = it->option[i].param; // Param1 count++; } } #if MAX_ITEM_RDM_OPT < 5 for( ; i < 5; i++ ){ buf[i].index = 0; // OptIndex buf[i].value = 0; // Value buf[i].param = 0; // Param1 } #endif return count; }
  11. It will affect your server load, yes. You would need to add a restriction in the source such as: if (src && src->type == BL_PC && bl->type == BL_MOB && damage > 0) { struct mob_data *md = (struct mob_data *)bl; if (md->id == 1002) { mapreg_setreg(reference_uid(add_str("$@attacked_mid"), 0), md->id); mapreg_setreg(reference_uid(add_str("$@attacked_gid"), 0), md->bl.id); npc_event_do_id("MyNPC::OnEvent", src->id); } } That on its own would be more reasonable, but if you have... I don't know, 10 people attacking the mob with 193 aspd, I'd be concerned. You'd probably want to do it all in the source instead, or at the very least put the chance of drop in the source. Something like... if (src && src->type == BL_PC && bl->type == BL_MOB && damage > 0) { struct mob_data *md = (struct mob_data *)bl; struct map_session_data *sd = (struct map_session_data *)src; if (md->id == 1002 && (rnd() % 100) < 5) { // 5% chance struct item it; t_itemid nameid = 501; int flag; memset(&it,0,sizeof(it)); it.nameid = nameid; it.identify = 1; it.bound = BOUND_NONE; // Assuming it is not a pet egg, you can skip some checks. Up to you at this point. if ((flag = pc_additem(sd, &it, 1, LOG_TYPE_SCRIPT))) { clif_additem(sd, 0, 0, flag); } } }
  12. Personally though, I'd say you should make a yaml conf file where you define drop groups with a chance, then assign a mob to a drop group in your mob_db.yml file instead. It's more work, but it's also much more effecient than running a script. Then again, that requires more source knowledge I suppose.
  13. You'd still need to modify your source to do that. You can be more restrictive as well and attach more "parameters". killerid won't work as it isn't added to the script yet. So something like... if (src && src->type == BL_PC && bl->type == BL_MOB && damage > 0) { struct mob_data *md = (struct mob_data *)bl; mapreg_setreg(reference_uid(add_str("$@attacked_mid"), 0), md->id); mapreg_setreg(reference_uid(add_str("$@attacked_gid"), 0), md->bl.id); npc_event_do_id("MyNPC::OnEvent", src->id); } OnEvent: switch($@attacked_mid) { case 1002: if (rand(100) < 5) { getitem 501, 1; } break; } end;
  14. It's possible, but it's also... very intensive for your server and I would highly recommend against it. In battle.cpp, in battle_calc_damage, add the NPC call: if (bl->type == BL_PC) { npc_event_do_id("MyNPC::OnEvent", bl->id); } And with the matching script: - script MyNPC -1,{ end; OnEvent: //callfunc("F_Function"); dispbottom "You have attacked."; end; } It would be much better to add code in the source for what you want to do instead.
  15. I loaded the maps you provided and there are no objects. So nothing to do with GRF Editor, but an issue with your local files or your client.
  16. Heya, You would have to share the map files you're trying to modify as there should be no issues with the settings you've picked.
  17. Well, the question is hard to answer because there is nothing wrong with the script. The announcements are clearly wrong, and it doesn't account for server reboots where the rates would be reset to Friday's rates, but besides that... it works as you'd expect. What issue do you have in particular?
  18. The job sprite does not get smaller. Neither in-game nor in Act Editor. The wing size has gotten smaller, though.
  19. Have you tried using Magnify (with anchor points)? If that's not what you're looking for, you'll have to use math to do what you want. Act files only use 1 anchor per frame.
  20. Well you can get the full command used with something like: OnNavigate: .@cmd$ = implode(.@atcmd_parameters$, " "); if (isloggedin(getcharid(3, .@cmd$)) != 1) { message strcharinfo(0), "Target player '" + .@cmd$ + "' is offline or do not exist."; } sleep2 1; unitwalkto getcharid(3), getcharid(3, .@cmd$); end; Though, unitwalkto is very limited and will most likely fail in most scenarios.
  21. Heya, Have you tried Scripts > Magnify? Use 0.5 to reduce the size by half. It's a decimal value that has no range limit; you can use -0.5 if you wanted to reduce by half and inverse the image too.
  22. Well, they are misaligned in the RSW file because RSM models are centered using a bounding box, while RSM2 are not centered by their bounding box. So to align them within the RSW file, you need to calculate the difference between the two. Calculate the bounding box for the RSM2 file, calculate the offset between the center of it with the location in the RSW. Calculate the bounding box for the RSM file, add the previous offset to the RSW file to re-align the model­. But technically speaking, the converted RSM model is working as intended.
  23. Heya, I just wanted to add there that this is actually incorrect (unless there was a major update on rAthena I'm not aware of). The script engine does not allocate memory for the 0 to 4000 values in your example. Technically speaking, arrays are emulated and not actually arrays in the source. When you use ".array[4001]", the engine does the following: ".array" is a string that gets indexed. The string is given a unique number, say 9048. From then on, everytime the string ".array" is used, it will be refered to as 9048. This is called the id. 4001 is the index. uid (id and index): Both of the values above combined as one (32bit for the id, 32bit for the index, making up 64bit for the uid). The uid with its value is then stored in a big collection for the whole NPC script. This big collection also contains the values of other non-array variables such as ".temp = 20;". They are all stored in the same place. If you had written the variable as ".array_4001", it would have created a new unique value for the string such as 9049 and the index would have been 0. As you need to index the string everytime you create a new variable, using setd with a big amount of variables is usually not a good practice. Using .array[4001] is handled better source wise. Quick note, but this is also why .@var = 5 is identical to .@var[0] = 5 and both would return the same value. They have the same uid. What rAthena also does when you use an index greater than 0 is that it starts keeping track of the array keys (members). So say you used .array[4001], it will keep track that 4001 is a member of the array for the .array variable. As for getarraysize(), it should be used very carefully and never in the condition of the for loop. getarraysize() does the following: Checks if the .array value exists in the big collection. If not, adds 0 as a member of the array. (This is more of a hack, as it's impossible to know otherwise if [0] is part of the array or not.) Go through all the members of the array and find the highest key, then does + 1. So in your case, using getarraysize(.array) would indeed return 4002 as 4001 is the only member of the array. Using arrays instead of getd/setd would be much faster (and cleaner) in your script sample as a result: prontera,155,181,5 script Card Buyer 757,{ mes "["+strnpcinfo(1)+"]"; mes "You have any card to sell ? <3"; next; getinventorylist; for (.@i = 0; .@i < @inventorylist_count; .@i++) { .@price = .card[@inventorylist_id[.@i]]; if (.@price) .@menu$ = .@menu$ + getitemname(@inventorylist_id[.@i]) + " ^FF0000"+F_InsertComma(.@price)+" Zeny^000000"; .@menu$ = .@menu$ + ":"; } .@i = select(.@menu$) - 1; clear; .@price = .card[@inventorylist_id[.@i]]; mes "["+strnpcinfo(1)+"]"; mes "Sell "+getitemname(@inventorylist_id[.@i])+" for ^FF0000"+F_InsertComma(.@price)+" Zeny^000000?"; if (select("Confirm", "Cancel") == 1) { delitem @inventorylist_id[.@i], 1; Zeny += .@price; clear; mes "["+strnpcinfo(1)+"]"; mes "You have sold "+getitemname(@inventorylist_id[.@i])+" for ^FF0000"+F_InsertComma(.@price)+" Zeny^000000."; } close; function AddCard { .@price = getarg(0, 0); .@getargcount = getargcount(); for (.@i = 1; .@i <= .@getargcount; .@i++) .card[getarg(.@i, 0)] = .@price; return; } OnInit: // AddCard( <zeny>, <card_id>...); AddCard( 10000, 4001, 4002, 4003, 4004); AddCard(100000, 4011, 4012, 4013, 4014); AddCard(500000, 4021, 4022, 4023, 4024, 4025); AddCard(999999, 4031, 4032, 4033, 4034, 4035, 4036); end; } Edit: This of course doesn't hold true if you would start iterating through the whole array with for (.@i = 0; .@i < getarraysize(.array); .@i++). This would give horrendous performance. We use the array there as a dictionary rather than a list.
  24. You have a few ways of doing that. Your script doesn't cover all possible abusable ways even with logout. It is possible to cancel a script without logging out. One trick is to "abuse" the addtimer behavior. While a script is running, the timed event will not run until the current script is finished (it is queued). As for the logging out issue, you can simply use OnPCLogoutEvent. One drawback from this is that you need to delete the timer as otherwise it will revert whenever you exit the NPC. So I added another menu option to confirm your style, " ~ I want this style". I'd probably remove the " ~ Revert to original" if I were you as it's not needed at all. Cancelling will do that for you and that way you can keep 4 menu options and keep things clean. prontera,76,96,1 script Stylist#custom_stylist 122,{ setarray .@Styles[1], getbattleflag("max_cloth_color"), getbattleflag("max_hair_style"), getbattleflag("max_hair_color"); setarray .@Look[1], LOOK_CLOTHES_COLOR, LOOK_HAIR, LOOK_HAIR_COLOR; set .@s, select(" ~ Cloth color: ~ Hairstyle: ~ Hair color"); set .@Revert, getlook(.@Look[.@s]); set .@Style,1; @stylist_look_type = .@Look[.@s]; @stylist_look_value = getlook(@stylist_look_type); addtimer 1, strnpcinfo(0) + "::OnPCLogoutEvent"; while(1) { setlook .@Look[.@s], .@Style; message strcharinfo(0),"This is style #"+.@Style+"."; set .@menu$, " ~ Next (^0055FF"+((.@Style!=.@Styles[.@s])?.@Style+1:1)+"^000000): ~ Previous (^0055FF"+((.@Style!=1)?.@Style-1:.@Styles[.@s])+"^000000): ~ Jump to...: ~ I want this style"; switch(prompt(.@menu$)) { case 1: set .@Style, ((.@Style != .@Styles[.@s]) ? .@Style+1 : 1); break; case 2: set .@Style, ((.@Style != 1) ? .@Style-1 : .@Styles[.@s]); break; case 3: message strcharinfo(0),"Choose a style between 1 - "+.@Styles[.@s]+"."; input .@Style,0,.@Styles[.@s]; if (!.@Style) set .@Style, rand(1,.@Styles[.@s]); break; case 4: // You have to set the values to 0 and remove the timer event once the colors are chosen and confirmed // Your code currently doesn't have a way out of your loops, so I added this one. @stylist_look_type = @stylist_look_value = 0; deltimer strnpcinfo(0) + "::OnPCLogoutEvent"; end; default: set .@Style, .@Revert; setlook .@Look[.@s], .@Revert; end; } } end; OnPCLogoutEvent: if (@stylist_look_type != 0) { setlook @stylist_look_type, @stylist_look_value; } deltimer strnpcinfo(0) + "::OnPCLogoutEvent"; end; }
  25. Heya, The Reins of Mount has special exceptions in the source. By default, while you are on a mount, you cannot use any item, and this includes the Reins of Mount itself. That is why there is an exception made for the item, and why you need to add an exception for your custom item as well. In pc.cpp, look for "ITEMID_REINS_OF_MOUNT" in the pc_useitem function. Change if( nameid != ITEMID_REINS_OF_MOUNT && sd->sc.data[SC_ALL_RIDING] ) to if( nameid != ITEMID_REINS_OF_MOUNT && nameid != 33014 && sd->sc.data[SC_ALL_RIDING] ) (Or in any other way you'd prefer, this is the most straightforward one I suppose.) You may also want to look at other exceptions from ITEMID_REINS_OF_MOUNT and apply them too for yours. Goodluck!
×
×
  • Create New...