Jump to content

ckx_

Members
  • Posts

    24
  • Joined

  • Last visited

  • Days Won

    1

Everything posted by ckx_

  1. Try playing with these values: The "canmove" value & and the "125" in the clif_damage call. If you mess with them a bit you might get better results. What you're describing might be less about client version and more about server tick, but I haven't tested newer clients yet. Maybe I'll get to it over the weekend. Also I just realized the multihit_diff.patch file is only has the changes from the second commit, so it won't do anything. I removed the diff from the post.
  2. I've only tested with a 2020 client. I'll test with a late 2022 or early 2023 client soon and post back. Did you get any changed behavior at all, or was it the same as before you applied the patch?
  3. I created a series of 2 mailbox patches (from format-patch). The mailbox patches probably work better than a diff (and gives a formal commit for the patch) The patches are titled 0001-hacky-mutlihit-auto-attack-animation-fix-sonic-blow-.patch and 0002-add-status_delay_delete-to-cleanup-mobs-when-killed-.patch. I split it into two commits because I am not entirely confident my solution to freeing memory is the best, more on that in "Freeing unit memory" below. Applying the mailbox patch From within your rA directory (stat outputs some info, check is a dry run for validation): git apply --stat 0001-hacky-mutlihit-auto-attack-animation-fix-sonic-blow-.patch git apply --check 0001-hacky-mutlihit-auto-attack-animation-fix-sonic-blow-.patch git am 0001-hacky-mutlihit-auto-attack-animation-fix-sonic-blow-.patch git am 0002-add-status_delay_delete-to-cleanup-mobs-when-killed-.patch Freeing unit memory - Not cleaining up properly? Part of this fix is to add a new function, status_delay_delete, which checks if a mob was killed by Arrow Vulcan or Sonic Blow. It delays the cleanup of a dead mob, so that they are properly cleaned up after being killed by SB or AV. Right now this delayed deletion only applies to mobs, not to players. I have not tested this fix in any PVP environment and it is possible that players and other units are not cleaned up properly in some cases. If this is the case, you will need to create a pc_timer_delete function and expand status_delay_delete to work with PCs. I am not motivated to do this right now. If someone tests this out in a pvp environment and finds that map blocks aren't being released properly, let me know, and I will probably do something about it. Animation Timings The code is commented with some info on how to change stuff like the animation timings, but to be clear, I did not do any comparison between official's classic Sonic Blow / AV and my default values. I just put what felt good to me. If someone else wants to get the official Sonic Blow timings and put them here, go for it. Adding Multihit Skills This patch has only been tested on pre-renewal with the skills Sonic Blow and Arrow Vulcan. If you want it to apply to other skills, you'll need to add them to the switch statement commented "//Display damage." in skill_attack (skill.cpp). Follow the case statements for CG_ARROWVULCAN / AS_SONICBLOW. You'll also need to add it to the conditional in status_delay_delete (status.cpp). Quick NPC test script Below is an npc script you can copy paste to test stuff out real fast: @warp new_3-1 49, 109 new_3-1,53,95,1,4 monster Phen 1158,30,3600000,1800000 new_3-1,49,107,1,4 script BLOW IT 617,{ BaseLevel += 99; jobchange(12); skill(136,10,SKILL_PERM_GRANT); statusup2(bDex,99); getitem(1250,1); equip(1250); return; } new_3-1,48,106,1,4 script VULCAN IT 617,{ BaseLevel += 99; jobchange(4020); skill(394,10,SKILL_PERM_GRANT); statusup2(bDex,99,getcharid(0)); getitem(1953,1); getitem(1750,100); equip(1953); equip(1750); return; } 0001-hacky-mutlihit-auto-attack-animation-fix-sonic-blow-.patch 0002-add-status_delay_delete-to-cleanup-mobs-when-killed-.patch
  4. For those coming here through search, Chaos92 is right. I ran into the same issue on a small test instance. I temporarily resized from 2GB to 4GB and it compiled fine. If that's not an option for you, try making sure swap is enabled and seeing if you can squeeze by with that.
  5. My fork of rA is heavily modified, so a diff isn't easy for me to slap together. Sometime in the next few days I will find time to implement this fix on a clean rA and make a diff from there. I'll post another message here when I get around to it.
  6. What skill mods do you want to implement?
  7. If you want it to reload on `@reloadstatusdb` or any other reload command of your choosing, you can just add the line `battleground_db.reload();` to the desired spot in the reload function: https://github.com/rathena/rathena/blob/master/src/map/atcommand.cpp#L4201 For status db you'd add it after line 4281.
  8. It's possible, but usually messy. You would need to get a reference to the NPC calling the script down to the C layer, and that can be messy, especially if instances are involved. What functionality do you want? It's likely that a source mod is more straightforward. You can add a statement somewhere in skill.cpp's `skill_additional_effect` function if you want something to happen after Bash hits a target, or you can add it under the SM_BASH case label in skill.cpp's `skill_castend_damage_id` if you want to happen as soon as Bash is used.
  9. So... you want to make it so that when you have a Katar equipped, and you swap to a Dagger, you gain a damage buff? If you give more details about your intended functionality and your level of technical ability to make code changes I can adjust my response to help you better. For now I'll just throw out some starting points for you to investigate. I would make a custom status for this by adding an entry in status_db.yml, then setup the status in source by adding it to the `sc_type` enum in status.hpp. You'll also want to export the enum in script_constants.hpp. Where you add a check for the SC to actually increase the damage is up to how you want it to stack. For starters, you can add a check for the SC in `battle_calc_cardfix` (if you want card style damage) or `battle_addmastery` (if you want it to add mastery style attack). It seems most straightforward to give the player the status somewhere in pc_equipitem (pc.cpp). You have a few options for implementation here. I'm going to say what I think is simplest to get you started then you can improve it through testing. 1) Store a reference near the top of the function to the player's current weapon type, e.g: `int oldweapon = sd->weapontype1` 2) Near the bottom of the function, sometime after sd->weapontype1 gets updated to id->subtype, do a simple check: if (oldweapon == W_KATAR && sd->weapontype1 == W_DAGGER) { sc_start(...) } I would throw this right after the assignment happens probably, so around here. Don't forget to brace the unbraced if statement, recompile, and try it out. Note that if you want this effect to happen for specific daggers as opposed to every dagger, then I would favor an approach that calls a buildin_func through the dagger's Equip Script.
  10. In Can_Rebirth add this after line 80: if (Zeny < 1000000) { return false; } You can experiment with putting it somewhere else if you want; this is the guard that checks for the zeny value. You should probably show a message somewhere saying that the player needs 1mil, too. In Job_Change replace line 311-317 with this: That's it. You might want to add a dispbottom line or something alerting the player 1mil was taken from them. https://github.com/rathena/rathena/blob/master/doc/script_commands.txt#L4246
  11. For questions 1 and 2, have you already read this? https://github.com/rathena/rathena/blob/master/doc/script_commands.txt#L6650 If so I can try to explain it in a different way, but do some testing and try to figure it out for a bit. For an instance with multiple levels all you need is to define your extra levels as "AdditionalMaps" in the instance_db.yml: https://github.com/rathena/rathena/blob/master/db/pre-re/instance_db.yml#L37 Then there are a number of ways to warp to your next map, the most conventional being simply defining a warp on the base map that goes to a defined AdditionalMap. If you let me know how you want your level flow to look, I can give some more specific pointers here.
  12. For item descriptions, it's purely clientside. That data is set up in ItemInfo.lua—Item descriptions never get any information from the server. If you edit the ATK of an item on the server, you must make a corresponding edit to the client description. The entire tbl{ } in ItemInfo.lua is loaded into memory at launch time. In your initial post, you show properties for Random Options, which are separate from the item descriptions. If you _really_ want dynamic item descriptions from the server, you could try to find a way to call AddItemIdentifiedDesc which is a function referenced in main() at the bottom of ItemInfo.lua. But as this doesn't appear to be a global function, and the item descriptions are set in memory at launch time, it will require you figuring out a clever injection point that has access to this function to see if you can overwrite the runtime value. I propose a few alternatives; If you are making a server where item stats can "grow", e.g. you get a Club and its ATK can be evolved, then I think you're best off making separate Item IDs for each Club iteration and making npc script to copy overall the properties. If you are making a server where the ATK of an item can change dynamically, frequently, then I think you're best off making an item that checks the stat of your items via npcscript, e.g. a usable called "Stat Sheet" that calls a mes window, and that shows you the dynamic properties on your items in an item function that uses getitemdata. If you are doing one-off edits then I think editing ItemInfo.lua is best, you could write a quick script that crawls your yaml item db and inserts the correct values into the lua file for you. Otherwise you might consider enchant or ropt slots for this data, which do read from the server.
  13. I hackily solved this on the source side. A side effect of my solution is that it shows extra damage numbers (but the multihit total is still correct, and the extra damage numbers are visual only). My solution: In the "display damage" switch block in skill_attack: case CG_ARROWVULCAN: case AS_SONICBLOW: { dmg.dmotion = clif_skill_damage(dsrc, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag & SD_LEVEL ? -1 : skill_lv, dmg_type, dmg.crit); struct unit_data* ud = unit_bl2ud(dsrc); if (ud) { ud->dmg = dmg; ud->sb_animation = 0; ud->sb_target = bl->id; ud->sb_timer = add_timer(gettick()+20, skill_sonicblow_animation, dsrc->id, 0); } break; } ↑ This plays the default SB animation, sets up some data, then adds a new timer function. Here's the code for the timer_func it adds: TIMER_FUNC(skill_sonicblow_animation){ struct block_list *target, *src; struct unit_data *ud; struct status_change *sc = NULL; int flag = 0; src = map_id2bl(id); if(src == NULL) { return 0; } ud = unit_bl2ud(src); if(ud == NULL) { return 0; } target = map_id2bl(ud->sb_target); if (!target || ud->sb_animation >= ud->dmg.div_ || ud->sb_timer == INVALID_TIMER) { ud->sb_animation = 0; ud->sb_timer = INVALID_TIMER; return 0; } int div_ = ud->dmg.div_; if (div_ < 1) { div_ = 1; } t_tick canmove = tick_diff(ud->canmove_tick, tick); canmove /= (div_-ud->sb_animation); if (canmove > 175) { canmove = 175; } clif_damage(src, target, tick, 125, ud->dmg.dmotion/div_, ud->dmg.damage/div_, div_, DMG_NORMAL, ud->dmg.damage2/div_, ud->dmg.isspdamage); ud->sb_animation++; ud->sb_timer = add_timer(tick+canmove, skill_sonicblow_animation, id, data); return 1; } ↑ This just sets up some damage then calls clif_damage, and sets up another timer func to keep it going until sb_animation reaches skill's div count. To be clear this will work for any multiattack that you want to have spam auto attacks, not just SB. Sorry for the naming scheme. The extra damage numbers are customizable by changing what you pass to the clif_damage call. You can set it to all 1s, or all misses, or anything else. Unfortunately I never found a way to implement this without the extra numbers—I'm not sure how to force the client to arbitrarily play an auto attack animation with no corresponding damage numbers. If someone knows, I'd be interested. and in the unit_data struct: int sb_animation, sb_target, sb_timer; // hacky sb animation fix ↑ just some state data, ud seemed like the best place to put it so that it works on both mobs and players. This produces a sonic blow like in the attached file. also viewable here: https://mikomiko.org/files/Screencast_20240512_005716.webm It's not perfect but I'm happy enough with it for now. Screencast_20240512_005716.webm
  14. Hi, Does anyone know of a method to retrieve the id of a block_list in the client lua? In particular I want to know the ID of a map_session_data object when it attacks. Failing this, I'd even like to know how to obtain a player's job ID around the time they attack. The closest I've gotten is retrieving the jobID via ValidateShieldID from Neo-Mind's Custom Shield's patch, which lives in ShieldTable_F.lub. ValidateShieldID seems to get called whenever a player comes insight or swaps equipment. I suppose I'm generally a little mystified as to how some patches are able to hook into these types of events to begin with, and how they get arguments passed to them.
  15. Does anyone how how to get rid of the "ATK 0 - 0 DEF: 0 - 0" pop-up in shops? Shown in the bottom right. Thanks.
  16. I have custom refine costs setup in refine_db.yml. When I use the RefineUI, it shows me the correct cost for refining on the initial screen, the one where you select a weapon. Then, after each refine, it shows the previous levels cost under the option to continue refining. Is this normal? To illustrate what I mean, I've made a short video: http://tanasinn.one/pix/Screencast_20240226_115239-2.webm After the first refine, it shows a cost of 1000, but really +1->+2 is 2000. This pattern continues throughout the refine process, e.g. if level +2->+3 is 3,000z, it will show the previous level's cost of 2,000z. Then it'll update to 3,000z for +3->+4, even if that cost is higher still. The Refine UI always shows the next level's refine cost as the previous level's. Note that if you press "Back" and go back to the initial refine window where you select materials, the cost is displayed correctly. Here's an example of my first two refine levels for level 1 weapons in the yaml DB: - Group: Weapon Levels: - Level: 1 RefineLevels: - Level: 1 Bonus: 200 Chances: - Type: Normal Rate: 10000 Price: 1000 Material: RGX_Steel - Level: 2 Bonus: 400 Chances: - Type: Normal Rate: 10000 Price: 2000 Material: RGX_Steel I think the cost is sent to the client in clif.cpp's clif_refineui_info function. Near the bottom of that function is a for loop that setups the packet, with the following block of interest: if( cost != nullptr ){ p->req[count].itemId = client_nameid( cost->nameid ); p->req[count].chance = (uint8)( cost->chance / 100 ); p->req[count].zeny = cost->zeny; p->packetLength += sizeof( struct PACKET_ZC_REFINING_MATERIAL_LIST_SUB ); count++; } clif_refineui_info is called after every press of the "Refine" button, so I would have expected this to be where the client receives the price for the next refine. Debugging shows that the correct cost is being sent to the client on each Refine press. So now I'm thinking the issue must be client side. Anyone got any insight into this?
  17. Interesting... So you can confirm with 100% certainty that the bug doesn't occur when running without other applications in Admin mode & never alt tabbing? When you run another process as admin, does it trigger even if you don't alt-tab? And vice versa, does it occur if you alt-tab with no other admin processes open? The client I play with is 2020-07-15bRagexe. Thanks for your input. EDIT: Hmm, I booted into Windows and did some playtesting. I got this bug on Payon Dungeon F1 without ever having lost focus, on a fresh boot with nothing else open. For me I feel like it happens sometimes when effects get spammy, but it's like.... really inconsistent... I just don't know...
  18. Thanks for the anecdote, but I play via Proton on Fedora without any special privileges involved. I'm not convinced process privileges are a factor, and it can occur even on a fresh client without having alt tabbed. Fullscreen vs Windowed might be a relevant thing, I'm not sure, but I want mitigation techniques that aren't tied to user behavior. @refresh is a server-side command for synchronization of client view data with the server-side source of truth; it doesn't have anything to do with the purely client-side lightmap rendering (and doesn't help). I'm more wondering about solutions/workarounds that might be a part of the map lighting, or if we understand the trigger criteria. I notice that I can tell when the lightmap glitch will occur because I get some 3D artifacting right before it happens, but beyond that I don't have much insight into it myself.
  19. I'm sure we're all familiar with the bug in the attached screenshot; in which your client's lightmap rendering has some catastrophic failure and turns all map textures into a pure white flash bang. The common workaround for this is to type /lightmap to disable lightmaps, then live with no lighting until you can do a client restart. But do we know what triggers this state? Are there any known workarounds to mitigate it (perhaps to a map's shadow or lighting data)? Are we forever forsaken to /lightmap eating a macro slot and needing to find a moment to restart the client?
  20. If you ever find the time & inclination, some pointers on what to do for player attacks would be appreciated, too; I've figured it out server-side, but have not yet started investigation on the clientside. Thank you.
  21. I made a custom Act Editor script to assist in the trivial cases where setting SoundId to "atk" is enough. I figured I'd post it here to save anyone else a few moments, if they ever decide to take up the task of more accurate feeling damage timings: using System; using ErrorManager; using GRF.FileFormats.ActFormat; using GRF.Image; namespace Scripts { public class Script : IActScript { public object DisplayName { get { return "Sound ID Replication"; } } public string Group { get { return "Custom Scripts"; } } public string InputGesture { get { return "Ctrl-Alt-Shift-A"; } } public string Image { get { return "settings.png"; } } public void Execute(Act act, int selectedActionIndex, int selectedFrameIndex, int[] selectedLayerIndexes) { if (act == null) return; string errorString = string.Empty; try { act.Commands.Begin(); System.Collections.Generic.List<int> soundIds = new System.Collections.Generic.List<int>(); foreach (var frame in act[selectedActionIndex].Frames) { soundIds.Add(frame.SoundId); } int start_index = selectedActionIndex; for (int i = selectedActionIndex; (i%8)!=0;i++) { start_index = i-7; } int end_index = start_index+7; for (int i = start_index; i < end_index+1; i++) { int actionIndex = i; if (actionIndex == selectedActionIndex) { continue; } GRF.FileFormats.ActFormat.Action action = act[actionIndex]; if (action.NumberOfFrames != soundIds.Count) { errorString += "Frame count mismatch on action index " +actionIndex+ ". Expected " + soundIds.Count + " frames, but got " + action.NumberOfFrames + "." + System.Environment.NewLine; continue; } for (int j = 0; j < action.Frames.Count; j++) { int frameIndex = j; act.Commands.SetSoundId(actionIndex, frameIndex, soundIds[frameIndex]); } } if (errorString != string.Empty) { System.Windows.Forms.MessageBox.Show(errorString, "Frame count mismatch", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation); } } catch (Exception err) { act.Commands.CancelEdit(); ErrorHandler.HandleException(err, ErrorLevel.Warning); } finally { act.Commands.End(); act.InvalidateVisual(); act.InvalidateSpriteVisual(); } } public bool CanExecute(Act act, int selectedActionIndex, int selectedFrameIndex, int[] selectedLayerIndexes) { return act != null; } } } The intention is to setup your selected Action Index as your "base" index, and run the script. It will replicate the SoundIds for each frame over to the other relevant action indices—If a frame count mismatch occurs between the base index and another action index (relatively rare, but happens), it skips all mismatched indices and throws a message, so you can handle 'em manually afterwards. I am not backing up the act here, as I have my own backup flow going on, so you might want to re-add the act backup command from the sample script if you use this. EDIT: Generalized script to make it work on any action type, not just attack actions at indices 16~23.
  22. Thank you for the detailed breakdown. This is exactly what I was looking for. I had noticed the behavior of the damage sound file, but still found it inconsistent; the rest of your post clarifies it greatly. I've done a little work on that front for my server (currently still in development). I added an "AmotionActive" property to monsters and skills, an int value that gets used by battle_delay_damage to determine when _sub should get called. It is used to calculate the percentage of an AttackMotion where the damage should be "active", i.e. at what point in the attack animation should HP be deducted from a player. The vanilla timer for delayed damage looks like this: add_timer(tick+amotion, battle_delay_damage_sub, 0, (intptr_t)dat); My modified call looks more like this (omitted safety checks for brevity): int dmgdelay = amotion; if (dmgdelay > 0) { int a_active; if (src->type==BL_MOB && !skill_id) { // Mob normals are handled on a case by case basis a_active = ((TBL_MOB*)src)->db->amotion_active; } else if (skill_id > 0) { a_active = skill_get_amotionactive(skill_id); } else { // General cases get amotion reduced by the default amotion active value. a_active = AMOTION_ACTIVE; } dmgdelay = (a_active * dmgdelay)/100; add_timer(tick+dmgdelay, battle_delay_damage_sub, 0, (intptr_t)dat); So if Willow's full amotion is 700, and I set its AmotionActive property to 33, that will put the battle_delay_damage_sub timer at tick+231 (33% of 700), causing the HP to be subtracted at roughly that point in the animation. This is fairly simple, and allows me to specify an arbitrary point of an amotion where damage is actually dealt—It works well to eliminate "laggy" feeling damage (whether server-side damage is too early, or too late compared to the animation). I've also modified some of the flow around when clif_damage gets called to make things feel more responsive for this work. That's all good, but up until now I've been restricted by my inability to define at what point the client displays the hitstun/damage, so I've just been doing my best to match up the server-side damage delays with the flinches defined by Gravity; but with this newfound knowledge, I'll have control of both ends of the equation, and be able to create more responsive feeling combat where things deal damage when they actually hit you. Thanks a bunch, and thanks for the tooling that makes this stuff simple.
  23. After you call clif_damage on the server, display of animations and damages seems to be in the client's hands, unless I've missed something. So my question is: With any given attack animation, how does one determine what frame of the attack should trigger the client to do a flinch animation for the target, & display the damage number? For example, if you spawn a Willow, you might notice that it deals damage around the time the animation begins, while the willow is still winding up. In contrast, if you spawn a Condor, you'll notice that damage does not happen until nearly the end of the animation. I took a look in Act Editor to see if there were any relevant properties, but I didn't find anything. Any ideas? Thanks.
  24. I'd like to add the old Sonic Blow / Arrow Vulcan animations back to newer clients. Does anyone know where to begin on this? I believe it's a client-side change, but I am not entirely positive. Background: I'm working on a pre-renewal server that uses a lot of newer renewal client features (market shops, randomopts, achievements, etc). So far I don't have any issues other than these skill animations being removed. Removal happened in kRO on 2018/12/19 (patch notes). There are some old threads on the forums about this, but they're all dead ends. I'll link them anyway: Ref1, Ref2. The Herc Forums also have a thread for this. If anyone has any info on this, let me know. Likewise I'll update the thread if I get anywhere myself.
×
×
  • Create New...