Jump to content

Tokei

Members
  • Posts

    696
  • Joined

  • Last visited

  • Days Won

    102

Everything posted by Tokei

  1. Unfortunately, hat effects were implemented on the map_session_data (player) on the emulator, while it should have been on the unit_data structure instead, which is shared by all object types. So you'll need a lot of modifications to get this working on monsters. If you apply the changes below, you'll be able to add a hat effect with this command: monster "prontera", 154, 182, "--ja--", 1002, 1; hateffect 12, true, $@mobid; Changes: diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 991b5b1b8..f2fece16d 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -1733,7 +1733,7 @@ int clif_spawn( struct block_list *bl, bool walking ){ if (sd->status.robe) clif_refreshlook(bl,bl->id,LOOK_ROBE,sd->status.robe,AREA); clif_efst_status_change_sub(bl, bl, AREA); - clif_hat_effects(sd,bl,AREA); + clif_hat_effects(bl, bl, true, AREA); } break; case BL_MOB: @@ -1745,6 +1745,7 @@ int clif_spawn( struct block_list *bl, bool walking ){ clif_specialeffect(&md->bl,EF_BABYBODY2,AREA); if ( md->special_state.ai == AI_ABR || md->special_state.ai == AI_BIONIC ) clif_summon_init(*md); + clif_hat_effects(bl, bl, true, AREA); } break; case BL_NPC: @@ -1756,6 +1757,7 @@ int clif_spawn( struct block_list *bl, bool walking ){ clif_specialeffect(&nd->bl,EF_BABYBODY2,AREA); clif_efst_status_change_sub(bl, bl, AREA); clif_progressbar_npc_area(nd); + clif_hat_effects(bl, bl, true, AREA); } break; case BL_PET: @@ -5104,7 +5106,7 @@ void clif_getareachar_unit( map_session_data* sd,struct block_list *bl ){ if ( tsd->status.robe ) clif_refreshlook(&sd->bl,bl->id,LOOK_ROBE,tsd->status.robe,SELF); clif_efst_status_change_sub(&sd->bl, bl, SELF); - clif_hat_effects(sd,bl,SELF); + clif_hat_effects(&sd->bl, bl, true, SELF); } break; case BL_MER: // Devotion Effects @@ -5122,6 +5124,7 @@ void clif_getareachar_unit( map_session_data* sd,struct block_list *bl ){ clif_specialeffect_single(bl,EF_BABYBODY2,sd->fd); clif_efst_status_change_sub(&sd->bl, bl, SELF); clif_progressbar_npc(nd, sd); + clif_hat_effects(&sd->bl, bl, true, SELF); } break; case BL_MOB: @@ -5139,6 +5142,7 @@ void clif_getareachar_unit( map_session_data* sd,struct block_list *bl ){ clif_monster_hp_bar(md, sd->fd); } #endif + clif_hat_effects(&sd->bl, bl, true, SELF); } break; case BL_PET: @@ -21184,53 +21188,46 @@ void clif_navigateTo(map_session_data *sd, const char* mapname, uint16 x, uint16 /// Send hat effects to the client (ZC_HAT_EFFECT). /// 0A3B <Length>.W <AID>.L <Status>.B { <HatEffectId>.W } -void clif_hat_effects( map_session_data* sd, struct block_list* bl, enum send_target target ){ +void clif_hat_effects(struct block_list* src, struct block_list* bl, bool enable, enum send_target target ){ #if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) - map_session_data *tsd; - struct block_list* tbl; - - if( target == SELF ){ - tsd = BL_CAST(BL_PC,bl); - tbl = &sd->bl; - }else{ - tsd = sd; - tbl = bl; - } - - nullpo_retv( tsd ); - - if( tsd->hatEffects.empty() || map_getmapdata(tbl->m)->getMapFlag(MF_NOCOSTUME) ){ + struct unit_data* ud; + + if (!src || !bl || !(ud = unit_bl2ud(bl))) return; - } - + + if (ud->hatEffects.empty() || map_getmapdata(src->m)->getMapFlag(MF_NOCOSTUME)) { + return; + } + struct PACKET_ZC_EQUIPMENT_EFFECT* p = (struct PACKET_ZC_EQUIPMENT_EFFECT*)packet_buffer; - - p->packetType = HEADER_ZC_EQUIPMENT_EFFECT; - p->packetLength = (int16)( sizeof( struct PACKET_ZC_EQUIPMENT_EFFECT ) + sizeof( int16 ) * tsd->hatEffects.size() ); - p->aid = tsd->bl.id; - p->status = 1; - - for( size_t i = 0; i < tsd->hatEffects.size(); i++ ){ - p->effects[i] = tsd->hatEffects[i]; - } - - clif_send( p, p->packetLength, tbl, target ); + + p->packetType = HEADER_ZC_EQUIPMENT_EFFECT; + + p->packetLength = (int16)( sizeof( struct PACKET_ZC_EQUIPMENT_EFFECT ) + sizeof( int16 ) * ud->hatEffects.size() ); + p->aid = bl->id; + p->status = enable; + + for (size_t i = 0; i < ud->hatEffects.size(); i++) { + p->effects[i] = ud->hatEffects[i]; + } + + clif_send(p, p->packetLength, src, target); #endif } -void clif_hat_effect_single( map_session_data* sd, uint16 effectId, bool enable ){ +void clif_hat_effect_single( struct block_list* bl, uint16 effectId, bool enable ){ #if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) - nullpo_retv( sd ); - - struct PACKET_ZC_EQUIPMENT_EFFECT* p = (struct PACKET_ZC_EQUIPMENT_EFFECT*)packet_buffer; - - p->packetType = HEADER_ZC_EQUIPMENT_EFFECT; - p->packetLength = (int16)( sizeof( struct PACKET_ZC_EQUIPMENT_EFFECT ) + sizeof( int16 ) ); - p->aid = sd->bl.id; - p->status = enable; - p->effects[0] = effectId; - - clif_send( p, p->packetLength, &sd->bl, AREA ); + nullpo_retv(bl); + + struct PACKET_ZC_EQUIPMENT_EFFECT* p = (struct PACKET_ZC_EQUIPMENT_EFFECT*)packet_buffer; + + p->packetType = HEADER_ZC_EQUIPMENT_EFFECT; + p->packetLength = (int16)( sizeof( struct PACKET_ZC_EQUIPMENT_EFFECT ) + sizeof( int16 ) ); + p->aid = bl->id; + p->status = enable; + p->effects[0] = effectId; + + clif_send( p, p->packetLength, bl, AREA ); #endif } diff --git a/src/map/clif.hpp b/src/map/clif.hpp index 1bf10b262..f6ba450b2 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -801,8 +801,8 @@ void clif_item_repair_list(map_session_data *sd, map_session_data *dstsd, int lv void clif_item_repaireffect(map_session_data *sd, int idx, int flag); void clif_item_damaged(map_session_data* sd, unsigned short position); void clif_item_refine_list(map_session_data *sd); -void clif_hat_effects( map_session_data* sd, struct block_list* bl, enum send_target target ); -void clif_hat_effect_single( map_session_data* sd, uint16 effectId, bool enable ); +void clif_hat_effects(struct block_list* src, struct block_list* bl, bool enable, enum send_target target); +void clif_hat_effect_single(struct block_list* bl, uint16 effectId, bool enable); void clif_item_skill(map_session_data *sd,uint16 skill_id,uint16 skill_lv); diff --git a/src/map/pc.cpp b/src/map/pc.cpp index 1cb25fad6..c2e9b2cc2 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -2220,10 +2220,6 @@ bool pc_authok(map_session_data *sd, uint32 login_id2, time_t expiration_time, i // Initialize BG queue sd->bg_queue_id = 0; -#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) - sd->hatEffects = {}; -#endif - sd->catch_target_class = PET_CATCH_FAIL; // Check EXP overflow, since in previous revision EXP on Max Level can be more than 'official' Max EXP diff --git a/src/map/pc.hpp b/src/map/pc.hpp index c37cd4fb5..3f4ceb943 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -922,10 +922,6 @@ public: short setlook_head_top, setlook_head_mid, setlook_head_bottom, setlook_robe; ///< Stores 'setlook' script command values. -#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) - std::vector<int16> hatEffects; -#endif - struct{ int tid; uint16 skill_id; diff --git a/src/map/script.cpp b/src/map/script.cpp index 027587945..72a1382f1 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -24268,39 +24268,31 @@ BUILDIN_FUNC(recalculatestat) { BUILDIN_FUNC(hateffect){ #if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) - map_session_data* sd; - - if( !script_rid2sd(sd) ) - return SCRIPT_CMD_FAILURE; - - int16 effectID = script_getnum(st,2); - bool enable = script_getnum(st,3) ? true : false; - - if( effectID <= HAT_EF_MIN || effectID >= HAT_EF_MAX ){ - ShowError( "buildin_hateffect: unsupported hat effect id %d\n", effectID ); - return SCRIPT_CMD_FAILURE; - } - - auto it = util::vector_get( sd->hatEffects, effectID ); - - if( enable ){ - if( it != sd->hatEffects.end() ){ - return SCRIPT_CMD_SUCCESS; - } - - sd->hatEffects.push_back( effectID ); - }else{ - if( it == sd->hatEffects.end() ){ - return SCRIPT_CMD_SUCCESS; - } - - util::vector_erase_if_exists( sd->hatEffects, effectID ); - } - - if( !sd->state.connect_new ){ - clif_hat_effect_single( sd, effectID, enable ); - } - + int16 effectID = script_getnum(st,2); + bool enable = script_getnum(st,3) ? true : false; + + // This is unecessary and annoying half the time + //if( effectID <= HAT_EF_MIN || effectID >= HAT_EF_MAX ){ + // ShowError( "buildin_hateffect: unsupported hat effect id %d\n", effectID ); + // return SCRIPT_CMD_FAILURE; + //} + + struct block_list* bl; + bool send = true; + + if (script_hasdata(st, 4)) { + bl = map_id2bl(script_getnum(st, 4)); + } + else { + bl = map_id2bl(st->rid); + map_session_data* sd = BL_CAST(BL_PC, bl); + + if (sd && sd->state.connect_new) { + send = false; + } + } + + unit_hateffect(bl, effectID, enable, send); #endif return SCRIPT_CMD_SUCCESS; } @@ -27566,7 +27558,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(adopt,"vv"), BUILDIN_DEF(getexp2,"ii?"), BUILDIN_DEF(recalculatestat,""), - BUILDIN_DEF(hateffect,"ii"), + BUILDIN_DEF(hateffect,"ii?"), BUILDIN_DEF(getrandomoptinfo, "i"), BUILDIN_DEF(getequiprandomoption, "iii?"), BUILDIN_DEF(setrandomoption,"iiiii?"), diff --git a/src/map/unit.cpp b/src/map/unit.cpp index f90529926..f57fcde6c 100644 --- a/src/map/unit.cpp +++ b/src/map/unit.cpp @@ -2964,6 +2964,9 @@ void unit_dataset(struct block_list *bl) ud->attackabletime = ud->canact_tick = ud->canmove_tick = gettick(); +#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) + ud->hatEffects = {}; +#endif } /** @@ -3461,10 +3464,6 @@ int unit_free(struct block_list *bl, clr_type clrtype) sd->qi_display.clear(); -#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) - sd->hatEffects.clear(); -#endif - if (sd->achievement_data.achievements) achievement_free(sd); @@ -3645,6 +3644,14 @@ int unit_free(struct block_list *bl, clr_type clrtype) break; } } + + + if (ud) { +#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) + ud->hatEffects.clear(); +#endif + } + map_deliddb(bl); @@ -3656,6 +3663,39 @@ int unit_free(struct block_list *bl, clr_type clrtype) return 0; } + +void unit_hateffect(struct block_list* bl, int16 effectID, bool enable, bool send) +{ + struct unit_data* ud; + map_session_data* sd; + + if (!bl || !(ud = unit_bl2ud(bl))) + return; + + sd = BL_CAST(BL_PC, bl); + auto it = util::vector_get(ud->hatEffects, effectID); + + if (enable) { + if (it != ud->hatEffects.end()) { + return; + } + + ud->hatEffects.push_back(effectID); + } + else { + if (it == ud->hatEffects.end()) { + return; + } + + util::vector_erase_if_exists(ud->hatEffects, effectID); + } + + if (send || !enable) { + clif_hat_effect_single(bl, effectID, enable); + } +} + + static TIMER_FUNC(unit_shadowscar_timer) { block_list *bl = map_id2bl(id); diff --git a/src/map/unit.hpp b/src/map/unit.hpp index cfd932615..0964d7438 100644 --- a/src/map/unit.hpp +++ b/src/map/unit.hpp @@ -65,6 +65,9 @@ struct unit_data { int32 group_id; std::vector<int> shadow_scar_timer; +#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) + std::vector<int16> hatEffects; +#endif }; struct view_data { @@ -115,6 +118,7 @@ bool unit_run(struct block_list *bl, map_session_data *sd, enum sc_type type); int unit_calc_pos(struct block_list *bl, int tx, int ty, uint8 dir); TIMER_FUNC(unit_delay_walktoxy_timer); TIMER_FUNC(unit_delay_walktobl_timer); +void unit_hateffect(struct block_list* bl, int16 effectID, bool enable, bool send); // Causes the target object to stop moving. int unit_stop_walking(struct block_list *bl,int type); You'll be able to use hat effects on NPCs as well. Goodluck. Edit: Updated diff to fix an error with the script function. hateffects.diff
  2. Heya, There isn't anything you can do from the STR file itself. I remember ez2streffect.bson allowing you to change that behavior, but I believe you can't add custom skills in there. So that won't do. Your atlernative is to use hat effects, with "isRenderBeforeCharacter = true" (mini-guide below). Though that requires a lot more source edits on your end since there is currently no support for that behavior (and it's not that well known apparently?). To be clear, yes this can be used on a targeted cell and not only on top of your character. It creates a dummy hidden NPC on the cell and then show a hat effect on that NPC instead. Here's the end result: Mini-guide: Add the effect via data\luafiles514\lua files\hateffectinfo\hateffectinfo.lub using a "reasonable" ID (some IDs just don't work, who knows why). For example: [100] = { resourceFileName = "new_earthdrive\\new_earthdrive_bottom\\new_earthdrive_bottom.str", hatEffectPos = -4, hatEffectPosX = 0, isRenderBeforeCharacter = true, isIgnoreRiding = true, isAdjustPositionWhenShrinkState = false, isAdjustSizeWhenShrinkState = false }, Apply these changes: diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index cfd23900c..fbb59d834 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -46484,3 +46484,11 @@ Body: Duration1: 10000 Cooldown: 60000 Status: Emergency_Move + - Id: 10100 + Name: NPC_HATEFFECT_POS + Description: Hateffect Pos + MaxLevel: 1 + TargetType: Self + DamageFlags: + NoDamage: true + diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 991b5b1b8..1540d9083 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -5449,6 +5449,26 @@ void clif_getareachar_skillunit(struct block_list *bl, struct skill_unit *unit, if (bl->type == BL_PC) fd = ((TBL_PC*)bl)->fd; + if (unit->group->skill_id == NPC_HATEFFECT_POS) { + // Comment below if you want the effect to show up after using @refresh (not default behavior for skills, that's only the case for ground units) + if (target == SELF) { + return; + } + + struct npc_data npc; + + memset(&npc, 0, sizeof(struct npc_data)); + + npc.bl = unit->bl; + npc.bl.type = BL_NPC; + npc.name[0] = '\0'; + npc.vd.class_ = 139; + + clif_set_unit_idle(&npc.bl, false, target, bl); + clif_hat_effects2(bl, &unit->bl, unit->group->skill_lv, true, target); + return; + } + if (unit->group->state.guildaura) return; @@ -5564,6 +5584,14 @@ void clif_skill_delunit(struct skill_unit *unit) nullpo_retv(unit); + if (unit->group && unit->group->skill_id == NPC_HATEFFECT_POS) { + WBUFW(buf, 0) = 0x80; + WBUFL(buf, 2) = unit->bl.id; + WBUFB(buf, 6) = CLR_OUTSIGHT; + clif_send(buf, packet_len(0x80), &unit->bl, ALL_SAMEMAP); + return; + } + WBUFW(buf, 0)=0x120; WBUFL(buf, 2)=unit->bl.id; clif_send(buf,packet_len(0x120),&unit->bl,AREA); @@ -21218,6 +21246,25 @@ void clif_hat_effects( map_session_data* sd, struct block_list* bl, enum send_ta #endif } +/// Send hat effects to the client (ZC_HAT_EFFECT). +/// 0A3B <Length>.W <AID>.L <Status>.B { <HatEffectId>.W } +void clif_hat_effects2(struct block_list* src, struct block_list* effect_bl, uint16 effect_id, bool enable, enum send_target target) { +#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) + unsigned char buf[13]; + + if (!effect_bl || !src) + return; + + WBUFW(buf, 0) = 0xa3b; + WBUFW(buf, 2) = 13; + WBUFL(buf, 4) = effect_bl->id; + WBUFB(buf, 8) = enable; + WBUFL(buf, 9) = effect_id; + + clif_send(buf, 13, src, target); +#endif +} + void clif_hat_effect_single( map_session_data* sd, uint16 effectId, bool enable ){ #if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) nullpo_retv( sd ); diff --git a/src/map/clif.hpp b/src/map/clif.hpp index 1bf10b262..c8f47b049 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -802,6 +802,7 @@ void clif_item_repaireffect(map_session_data *sd, int idx, int flag); void clif_item_damaged(map_session_data* sd, unsigned short position); void clif_item_refine_list(map_session_data *sd); void clif_hat_effects( map_session_data* sd, struct block_list* bl, enum send_target target ); +void clif_hat_effects2(struct block_list* src, struct block_list* effect_bl, uint16 effect_id, bool enable, enum send_target target); void clif_hat_effect_single( map_session_data* sd, uint16 effectId, bool enable ); void clif_item_skill(map_session_data *sd,uint16 skill_id,uint16 skill_lv); diff --git a/src/map/script.cpp b/src/map/script.cpp index 027587945..a261e64ec 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -26896,6 +26896,36 @@ BUILDIN_FUNC(getfamerank) { return SCRIPT_CMD_SUCCESS; } +BUILDIN_FUNC(showhateffectpos) { + int hat_effect_id = script_getnum(st, 2); + int x = script_getnum(st, 3); + int y = script_getnum(st, 4); + int duration = script_getnum(st, 5); + struct block_list* bl; + + if (script_hasdata(st, 6)) { + bl = map_id2bl(script_getnum(st, 6)); + } + else { + bl = map_id2bl(st->oid); + } + + if (!bl) { + return SCRIPT_CMD_FAILURE; + } + + std::shared_ptr<s_skill_unit_group> group = skill_unitsetting(bl, NPC_HATEFFECT_POS, hat_effect_id, x, y, duration); + + if (group != NULL) { + script_pushint(st, group->group_id); + } + else { + script_pushint(st, -1); + } + + return SCRIPT_CMD_SUCCESS; +} + BUILDIN_FUNC(isdead) { map_session_data *sd; @@ -27680,6 +27710,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(getfame, "?"), BUILDIN_DEF(getfamerank, "?"), BUILDIN_DEF(isdead, "?"), + BUILDIN_DEF(showhateffectpos, "iiii?"), BUILDIN_DEF(macro_detector, "?"), #include <custom/script_def.inc> diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 611aff452..c9a695768 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -15058,6 +15058,12 @@ std::shared_ptr<s_skill_unit_group> skill_unitsetting(struct block_list *src, ui case WH_FLAMETRAP: limit += 3000 * (sd ? pc_checkskill(sd, WH_ADVANCED_TRAP) : 5); break; + case NPC_HATEFFECT_POS: + limit = flag; + interval = flag + 1000; + flag = 0; + val2 = skill_lv; + break; } // Init skill unit group @@ -15702,6 +15708,9 @@ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, t_t case UNT_DUMMYSKILL: switch (sg->skill_id) { + case NPC_HATEFFECT_POS: + // don't do anything for this skill + break; case SG_SUN_WARM: //SG skills [Komurka] case SG_MOON_WARM: case SG_STAR_WARM: { @@ -20500,6 +20509,9 @@ int skill_delunit(struct skill_unit* unit) tsc->getSCE(SC__MANHOLE)->val4 = 0; // Remove the Unit ID } break; + case NPC_HATEFFECT_POS: + clif_hat_effects2(&unit->bl, &unit->bl, unit->group->skill_lv, false, ALL_SAMEMAP); + break; } clif_skill_delunit(unit); @@ -20734,6 +20746,9 @@ int skill_delunitgroup_(std::shared_ptr<s_skill_unit_group> group, const char* f } } break; + case NPC_HATEFFECT_POS: + clif_hat_effects2(&group->unit->bl, &group->unit->bl, group->skill_lv, false, ALL_SAMEMAP); + break; } if (src->type==BL_PC && group->state.ammo_consume) diff --git a/src/map/skill.hpp b/src/map/skill.hpp index c22e0ad70..04dc782d9 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -2494,6 +2494,8 @@ enum e_skill { ABR_NET_REPAIR, ABR_NET_SUPPORT, ABR_INFINITY_BUSTER, + + NPC_HATEFFECT_POS = 10100, }; /// The client view ids for land skills. Call the effect from the source with something like this (100 being your effect ID): case NPC_CUSTOM_SKILL: // skill related stuff... skill_unitsetting(src, NPC_HATEFFECT_POS, 100, x, y, 2000); break; or if you want to test first with the script command .@effect_id = 100; .@x = 152; .@y = 187; .@duration = 2000; showhateffectpos .@effect_id, .@x, .@y, .@duration; The downside of this method is that the hat effects always loop. So you have to know in advance the duration of the effect. You also probably want to add some empty frames at the end of your STR animation for some buffer and prevent the loop from showing up at all. Best solution I can think of, goodluck! hateffectpos.diff
  3. Well, the error itself isn't too helpful. 0x8b3 is clif_showscript. So one of the possible cause is a script sending "showscript" (a script command) to a player too many times, probably in an infinity loop would be my guess. Though, the error message only shows the last packet that exceeded the length of the connection buffer, so a prior packet could be responsible too. It's hard to know for sure. If the error is somewhat new, then you should know if this rings a bell or not. If you still can't figure it out, then you could dump the last packet in your console (in socket.cpp): ShowDump(s->wdata + s->wdata_size, len); ShowError("WFIFOSET: Maximum write buffer size for client connection %d exceeded, most likely caused by packet 0x%04x (len=%" PRIuPTR ", ip=%lu.%lu.%lu.%lu).\n", fd, WFIFOW(fd,0), len, CONVIP(s->client_addr)); Then you can see what the string used by showscript was and track down the issue a bit more easily. If nothing else, then you'd have to dump the entire write butter, but hopefully you won't have to do that. Goodluck!
  4. Well your links are not uploaded properly, so I can't see. Though, the gif format doesn't allow transparency. Most people will simply pick a black background as it is what would yield the best results.
  5. Well, ground units (Land Protector and traps) do disappear by themselves when you change map. That is the default behavior and it's handled by this code in pc_setpos: if (battle_config.clear_unit_onwarp&BL_PC) skill_clear_unitgroup(&sd->bl); You can copy this code in pc_dead if you want to do make ground units disappear on player death as well.
  6. That's not really possible without rewritting most of the emulator. Everything is made with the assumption that the account ID is unique to a player. A simple example would be a script function like "rid2name". Which name should that return, since they both players have the same rid (the rid is the account ID, to be clear)? Plus, that's the easy part. Imagine using a storage on two characters in the same account; you'd be able to dupe items left and right and that's just one scenario.
  7. Well, it looks like it managed to read the GRF fine so someone probably just changed the header for whatever reason. It gives you that error because that's not what is expected at all from the GRF format. You can hex the file manually and change the start to "Master of Magic\0". You might be able to just save the GRF again as well and it will fix the issue...? It managed to load it anyway, so you could just keep using the GRF normally.
  8. The error says: "The entry is either corrupted or encrypted". If you want to read the file, decrypt the content first.
  9. This is a barter shop, not a normal shop. You should have a more readable format below with "barter_clear" and "barter_add", though those are custom functions (found here if interested). rAthena went with a yaml approach for these types of shops, so you'll have to convert those yourself. You can find an example in npc/re/merchants/barters/refine.yml. The tool can't produce a yaml output because it would need to convert item IDs to aegis names, and that's just a nightmare nobody wants to spend time on.
  10. Alright, I see what Gravity did there... Updated with 1.8.4.9, should fix the gat type problems.
  11. Heya, This tool parses replay files (rrf) and outputs a readable format. It is used to gather data from official replay files by extracting NPC dialogues, quest status, skill cast time, after-cast delay, etc. Say you want to replicate an official instance, you would get a replay of it and then extract the data using this tool. You'll be able to follow along the mes dialogues, the next statements, etc, when making the actual script. You can save a lot of time doing so. It is a tool I've been using heavily for many years now and it was made to fit my needs, so some stuff may not seem very useful to you. Because of this, I'm making the project public so you're free to add more features/information as you see fit. The source for the Replay format as far as I'm aware is from Dia (from Divine Pride), so huge thanks on that side. Download: https://github.com/Tokeiburu/Rrf-Parser/releases Git repo: https://github.com/Tokeiburu/Rrf-Parser Some replays may not work as this tool was made specifically for kRO replays (and I've been removing support for older versions throughout the years). How it works Change the replay path to your replay and click "Parse Replay" to start the process. Once that's done, you should get the output shown above. Choose the parsing options on the left if you don't want to include useless data. It may be useful for some though. The [output] options are files generated in your folder\output\file.txt. Main output NPC scripts. All the NPCs that have been seen (in order) will be put there with a pre-made script. It will also show "npc_avail" for some very specific NPCs. On kRO, some NPCs don't actually have a view ID but are actually characters with a style. This is what npc_avail is for (though it's not something rAthena supports at the moment). For example: 1@exnw,12,107,3 script ³ªÀÌÆ® ¿öÄ¡#nw3 npc_avail[4306,0,21,7,0,0,0,0,0,0,0],{ end; } Which corresponds to: p.job,p.sex,p.head,p.headpalette,p.weapon,p.shield,p.accessory,p.accessory2,p.accessory3,0,p.bodypalette Equipment. The next entry in the main output is the player equipment with the @item2 command to remake the gears quickly. This only includes equipped items. The other pieces of equipment in the inventory are not included there. Monster spawned. This part will include all the monsters that have spawned and it will not show them more than once. Packet output. That is the main part of the parsed script. It will give you the script lines, cutins, and much more. It will detect cloakoffnpcself, but don't trust the output blindly either. Sometimes kRO just hides a NPC for dumb reasons. Though usually it should be accurate. Parsing options Most of the fields are self-explanatory, so I'll only go over those that aren't. Show raw packets. Outputs the packets in hexadecimals rather than a readable format. Revert instance names. Looks for ###1@name in map names and reverts it back to the original map name. This also changes NPC names that got converted such as something#ins_0o1 to something#ins. Generic packet. This one contains pretty much everything not included in the other options. UnitWalk packet. This one is disabled by default, but you might consider turning it on if you're doing newer instances because kRO uses those a lot lately. When a NPC moves, it will be shown there. mob_data.conf A special file is generated alongside the replay in the output folder called "mob_data.conf" (though it's not a real libconf format). The mob_data.conf file contains data gathered from the mobs inside the replay such as mob level, speed, damage motion, attack motion, boss type, skills used and mob drops. As far as mob drops go however, be careful as the tool makes a lot of estimation there. For example: Mob Death: 148 Drops: 1000364,135 Dropped: 2 The above means the mob has been killed 148 times, and only one drop has been seen (1000364). The estimated drop rate is 1.35%. A drop is defined as an item dropped alongside the unit_dead packet. So it may be wrong, or if the mob is looter type, you'll get a bunch of invalid results. Though usually it's a fair estimation. Tool > Translation helper So if you put the following as the input: select("¾îµð·Î °¡¸é µÇÁÒ?:"); // TICK: 19263 ms, INTERVAL: 0, FORMAT: 0:19 mes "[¸¶¶÷]"; // TICK: 19746 ms, INTERVAL: 433, FORMAT: 0:19 mes "¾ÆÀÌ»þ°¡ ¾îµð¼­ »ì¾ÒÁö? ¾Æ! ÀÌ <NAVI>[À­ÂÊ]<INFO>wolfvill,99,178,0,101,0</INFO></NAVI>¿¡ °¡¸é ¾ÆÀÌ»þÀÇ ÁýÀÌ ÀÖ¾î¿ä."; // TICK: 19746 ms, INTERVAL: 0, FORMAT: 0:19 next; // TICK: 19746 ms, INTERVAL: 0, FORMAT: 0:19 mes "[½ºÄ«´Ï¾Æ]"; // TICK: 20129 ms, INTERVAL: 0, FORMAT: 0:20 mes "ÇÏÁö¸¸ ¸»Çصµ ¼Ò¿ë ¾øÀ» °É¿ä? ¼³µæÇÑ´Ù¸é, ´ç½Å¿¡ ´ëÇÑ ³» »ý°¢µµ ¹Ù²ÙÁÒ."; // TICK: 20129 ms, INTERVAL: 0, FORMAT: 0:20 npctalk "½ºÄ«´Ï¾Æ, ¸ðÇè°¡´ÔÀº ³× »ý°¢À¸·Î °¡Ä¡°¡ Æò°¡µÇ´Â ºÐÀÌ ¾Æ´Ï¼Å.", "¸¶¶÷#wms01"; // TICK: 20129 ms, INTERVAL: 0, FORMAT: 0:20 npctalk "´©°¡ ¹¹·¡? ÀßÇØÁÙ °Å¶ó°í.", "½ºÄ«´Ï¾Æ#wms01"; // TICK: 20129 ms, INTERVAL: 0, FORMAT: 0:20 next; // TICK: 20129 ms, INTERVAL: 0, FORMAT: 0:20 mes "[¸¶¶÷]"; // TICK: 20479 ms, INTERVAL: 33, FORMAT: 0:20 mes "¸ðÇè°¡´Ô, ±×·³ ´Ù³à¿À¼¼¿ä! ³ªµµ À̸¸ °¥°Ô!"; // TICK: 20479 ms, INTERVAL: 0, FORMAT: 0:20 setquest 17510; // State = 1, Time = 0 // TICK: 20479 ms, INTERVAL: 0, FORMAT: 0:20 close; // TICK: 20480 ms, INTERVAL: 1, FORMAT: 0:20 You would get the following as the output: 어디로 가면 되죠? [마람] 아이샤가 어디서 살았지? 아! 이 [윗쪽]에 가면 아이샤의 집이 있어요. <NAVI>[윗쪽]<INFO>wolfvill,99,178,0,101,0</INFO></NAVI> [윗쪽] next; [스카니아] 하지만 말해도 소용 없을 걸요? 설득한다면, 당신에 대한 내 생각도 바꾸죠. 스카니아, 모험가님은 네 생각으로 가치가 평가되는 분이 아니셔. 누가 뭐래? 잘해줄 거라고. next; [마람] 모험가님, 그럼 다녀오세요! 나도 이만 갈게! setquest 17510; close; It extracts the content and puts them in Korean for easier google/papago translate copy paste. The "select" content will be extracted, same for <NAVI> and a few other annoying cases where you end up spending more time removing the tags than actually translating. Tool > Replay simulation This one is meant to "transform" a replay into a script so that you can view it on your own server. The input account ID is your account id, on the test server you'll be on. This is required for the script to work correctly. You will get an output similar to - script REPLAY_SIMULATION -1,{ end; OnTimer1: attachrid(2000012); sendpacket("ff09620006aa010000000000002c0100000000040000008928000000000000000000000000000000000000000000000000000000000000000000000000000024472500000000000000ffffffffffffffff000000b9ccb8aebecf23657031385f7776"); sendpacket("ff09600006a9010000000000002c0100000000040000008828000000000000000000000000000000000000000000000000000000000000000000000000000023c72500000000000000ffffffffffffffff000000b8b6b6f723657031385f7776"); sendpacket("ff09620006ab010000000000002c0100000000000000008b28000000000000000000000000000000000000000000000000000000000000000000000000000023c71500000000000000ffffffffffffffff000000c0ccb9c7b8b123657031385f7776"); end; OnInit: initnpctimer; end; } Then load the script and your character will redo what the replay file was doing. You'll probably have to put your character where the replay starts as otherwise some weird stuff is gonna happen. You'll also need the following script command as well: BUILDIN_DEF(sendpacket,"s"), BUILDIN_FUNC(sendpacket) { const char *input = script_getstr(st,2); struct block_list *bl = map_id2bl(st->rid); unsigned char buf[10000]; int len = strlen(input); int i; for (i = 0; i < len; i += 2) { sscanf(&input[i], "%2hhx", &buf[i/2]); } if (!bl) { npc_timerevent_stop(map_id2nd(st->oid)); st->state = END; return SCRIPT_CMD_FAILURE; } clif_send(buf, len / 2, map_id2bl(st->rid), SELF); return SCRIPT_CMD_SUCCESS; } Notes This is a tool I do not plan on spending a whole lot of time on. It would be a nightmare to maintain in the first place. I will add requested features if any for a short time, but it would be much easier to add such features yourself if you are planning to use this as a base for your own needs. If you want to handle a new packet, simply go in RrfParser\Packet\PacketDecoder.cs and add it as you want. Only those I needed were handled. The display timers are both useful and annoying. Once I'm done with a script, I usually run a quick regex to wipe out all comments and that solves that problem. //.*
  12. These use a new GND format, weird. I've added support for those maps in 1.8.4.8. (There may be more problematic maps? Hopefully not, but let me know if there still are.)
  13. Fixed a couple months ago; please update to the latest version.
  14. Heya, It took me a while to figure out what you were trying to do. You're trying to edit the client aura effect using the NEMO/WARP patch for more aura options, instead of creating custom hat effects from the guide. That's why you don't see a STR file in your folder. Those are two completely different features and you can't really mix them. Now I can't help you if you're going with the first option since those effects are read directly by the client and therefore do not have STR files. You also can't edit them at all besides changing the BMP file. The guide you're following provided you with a zip in step 4 which contains what you actually need for making custom hat effects (not auras). So assuming you've followed the previous steps in the guide, and you defined your new hat effect as this... HAT_EF_Angelic_Aura = 181 //... [HatEFID.HAT_EF_Angelic_Aura] = { resourceFileName = "effect\\angelic_aura\\angelic_aura.str", hatEffectPos = -6, hatEffectPosX = 0, isIgnoreRiding = true, isAdjustPositionWhenShrinkState = true, isAdjustSizeWhenShrinkState = true } You now have two methods when creating a new hat effect: change the textures or customize it. If you're only going to change the textures, which is what most people end up doing, then it's fairly easy. Duplicate the aura from the example in step 4, then overwrite the textures with your own and that's pretty much it. If you want to do more edits and more "fancy" stuff with your auras, then here's an example below: Open an existing STR file (arcana_aura.str, from your guide's example). Do whatever changes you want in the STR. I'm adding a second layer that I duplicated and then used a random texture I found somewhere. Save the file to wherever you want, as long as it matches with your resourceFileName. (I'm ignoring the particle effect on top that they're showing you in the guide, that part is up to you.)
  15. Well, auras are str files to begin with, no? You could open an existing aura, change the textures to your files, and save it again for a quick custom aura.
  16. The quadtree isn't meant for something like that; it's a bandaid for BrowEdit 1 that didn't build the quadtree properly (related to black square issues). Though that's long been fixed by now. There's no such feature to convert to Blender or other common 3D formats from GRF Editor. Your best bet would be to look at BrowEdit 3 (https://github.com/Borf/BrowEdit3/releases), though... I don't think such a feature is available there either? Either way, GRF Editor is quite bare-bone and unoptimized as far as managing 3D assets goes. You may want to look into these projects:
  17. Heya, I've removed the GRF Editor sources from public view a long time ago, but I don't mind adding them back now. You'll have to set the startup project to GrfCL, you can find the project here: https://github.com/Tokeiburu/GRFEditor
  18. Hmm, which software was used to create these? The integer for the frame count is written in plain text with "Fram". That looks like a bug when saving the file. I can't really retrieve that value perfectly, but I can guess it while loading the file and make an estimate. __ Updated to 1.0.8: Fixed the issue above. Added Flip Horizontal/Vertical transformations. Added a new button "Group Edit" to modify multiple frames at the same time. The available transformations for Group Edit are translation, rotation and flip.
  19. Well, it looks like there are more than one error there...? Your output is very confusing. I'd probably recommend you make sure .net 3.5 and .net 4.0 are installed for starter.
  20. Well, you'd have to check which palette it actually uses in your case. If it's cardinal, then it's most likely using cardinal_³²_2.pal. The default kRO palettes for 4th classes are not compatible with the fixed sprites that private servers use (and not compatible with older sprites either, such as a GM sprite). So to make the 4th class palette compatible with your GM sprite, you'll have to use custom palettes instead. I'm not sure how's the progress on those; I made mine myself but I'm sure someone out there made them available to everyone by now. If you use custom palettes, then you'll obviously also have to use one of the "4th Jobs Corrected Sprites" pack available on rAthena.
  21. Unfortunately, that's not possible at all right now for various compatibility reasons. For saving it as a gif, the output would also have to be rendered with OpenGL, on an actual window, which a console application doesn't have.
  22. I mean, read the error message and it will tell you what the problem is/how to fix it.
  23. var path = @"C:\test\"; foreach (var actFile in Directory.GetFiles(path, "*.act")) { var sprFile = actFile.ReplaceExtension(".spr"); var act2 = new Act(actFile, sprFile); foreach (var action in act2) { action.Frames = action.Frames.Take(1).ToList(); } act2.SaveWithSprite(actFile, sprFile); }
  24. The palette used depends of your class, so it's somewhat unrealistic to give palettes for the GM sprite as it can be any class. Plus, each server has different palettes, so only your related server can create those.
  25. I don't understand your question, sorry. Can you phrase it differently?
×
×
  • Create New...