From b0be3f023a024a4f8cc9903d52add7c81961303a Mon Sep 17 00:00:00 2001 From: "Guilherme G. Menaldo" Date: Mon, 1 Oct 2018 13:48:10 -0300 Subject: Added option to make hunter traps invisible --- src/map/battle.c | 2 +- src/map/battle.h | 2 +- src/map/clif.c | 7 +++---- src/map/skill.c | 38 +++++++++++++++++++++++++++++--------- src/map/skill.h | 2 ++ 5 files changed, 36 insertions(+), 15 deletions(-) (limited to 'src/map') diff --git a/src/map/battle.c b/src/map/battle.c index fceb30be1..2cf04ffce 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -6966,7 +6966,7 @@ static const struct battle_data { { "player_damage_delay_rate", &battle_config.pc_damage_delay_rate, 100, 0, INT_MAX, }, { "defunit_not_enemy", &battle_config.defnotenemy, 0, 0, 1, }, { "gvg_traps_target_all", &battle_config.vs_traps_bctall, BL_PC, BL_NUL, BL_ALL, }, - { "traps_setting", &battle_config.traps_setting, 0, 0, 1, }, + { "trap_visibility", &battle_config.trap_visibility, 0, 0, 2, }, { "summon_flora_setting", &battle_config.summon_flora, 1|2, 0, 1|2, }, { "clear_skills_on_death", &battle_config.clear_unit_ondeath, BL_NUL, BL_NUL, BL_ALL, }, { "clear_skills_on_warp", &battle_config.clear_unit_onwarp, BL_ALL, BL_NUL, BL_ALL, }, diff --git a/src/map/battle.h b/src/map/battle.h index 9f5207e95..297768765 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -149,7 +149,7 @@ struct Battle_Config { int pc_damage_delay_rate; int defnotenemy; int vs_traps_bctall; - int traps_setting; + int trap_visibility; int summon_flora; //[Skotlex] int clear_unit_ondeath; //[Skotlex] int clear_unit_onwarp; //[Skotlex] diff --git a/src/map/clif.c b/src/map/clif.c index 76625f0ba..cb9bd3ee2 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -4904,9 +4904,8 @@ static void clif_getareachar_skillunit(struct block_list *bl, struct skill_unit p.xPos = su->bl.x; p.yPos = su->bl.y; - //Use invisible unit id for traps. - if ((battle_config.traps_setting&1 && skill->get_inf2(su->group->skill_id)&INF2_TRAP) || - (skill->get_unit_flag(su->group->skill_id) & UF_RANGEDSINGLEUNIT && !(su->val2 & UF_RANGEDSINGLEUNIT))) + // Use invisible unit id for some ground skills. + if (skill->get_unit_flag(su->group->skill_id) & UF_RANGEDSINGLEUNIT && !(su->val2 & UF_RANGEDSINGLEUNIT)) p.job = UNT_DUMMYSKILL; else p.job = su->group->unit_id; @@ -4915,7 +4914,7 @@ static void clif_getareachar_skillunit(struct block_list *bl, struct skill_unit p.RadiusRange = (unsigned char)su->range; #endif - p.isVisible = 1; + p.isVisible = su->visible; #if PACKETVER >= 20130731 p.level = (unsigned char)su->group->skill_lv; diff --git a/src/map/skill.c b/src/map/skill.c index 4eaab5457..2b31de6fc 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -4153,10 +4153,9 @@ static int skill_reveal_trap(struct block_list *bl, va_list ap) Assert_ret(bl->type == BL_SKILL); su = BL_UCAST(BL_SKILL, bl); - if (su->alive && su->group && skill->get_inf2(su->group->skill_id)&INF2_TRAP) { //Reveal trap. - //Change look is not good enough, the client ignores it as an actual trap still. [Skotlex] - //clif->changetraplook(bl, su->group->unit_id); - clif->getareachar_skillunit(&su->bl,su,AREA); + if (su->alive && su->group && skill->get_inf2(su->group->skill_id) & INF2_HIDDEN_TRAP) { //Reveal trap. + su->visible = true; + clif->skillunit_update(bl); return 1; } return 0; @@ -11040,9 +11039,10 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill map->foreachinarea(status->change_timer_sub, src->m, x-r, y-r, x+r,y+r,BL_CHAR, src,NULL,SC_SIGHT,tick); - if(battle_config.traps_setting&1) - map->foreachinarea(skill_reveal_trap, - src->m, x-r, y-r, x+r, y+r, BL_SKILL); + if (battle_config.trap_visibility != 0) { + map->foreachinarea(skill_reveal_trap, + src->m, x - r, y - r, x + r, y + r, BL_SKILL); + } break; case SR_RIDEINLIGHTNING: @@ -12759,6 +12759,11 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b ts->tick += sg->interval*(map->count_oncell(bl->m,bl->x,bl->y,BL_CHAR,0)-1); } + if (battle_config.trap_visibility != 0 && skill->get_inf2(sg->skill_id) & INF2_HIDDEN_TRAP) { + src->visible = true; + clif->skillunit_update(&src->bl); + } + switch (sg->unit_id) { case UNT_FIREWALL: case UNT_KAEN: { @@ -12911,10 +12916,11 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b clif->fixpos(bl); } sg->val2 = bl->id; - } else + } else { sec = 3000; //Couldn't trap it? + } + if( sg->unit_id == UNT_ANKLESNARE ) { - clif->skillunit_update(&src->bl); /** * If you're snared from a trap that was invisible this makes the trap be * visible again -- being you stepped on it (w/o this the trap remains invisible and you go "WTF WHY I CANT MOVE") @@ -17051,6 +17057,14 @@ static struct skill_unit *skill_initunit(struct skill_unit_group *group, int idx su->val1=val1; su->val2 = val2; su->prev = 0; + su->visible = true; + + if (skill->get_inf2(group->skill_id) & INF2_HIDDEN_TRAP + && ((battle_config.trap_visibility == 1 && map_flag_vs(group->map)) // invisible in PvP/GvG + || battle_config.trap_visibility == 2 // always invisible + )) { + su->visible = false; + } idb_put(skill->unit_db, su->bl.id, su); map->addiddb(&su->bl); @@ -20216,6 +20230,12 @@ static void skill_validate_skillinfo(struct config_setting_t *conf, struct s_ski } else { sk->inf2 &= ~INF2_ALLOW_REPRODUCE; } + } else if (strcmpi(type, "HiddenTrap") == 0) { + if (on) { + sk->inf2 |= INF2_HIDDEN_TRAP; + } else { + sk->inf2 &= ~INF2_HIDDEN_TRAP; + } } else if (strcmpi(type, "None") != 0) { skilldb_invalid_error(type, config_setting_name(t), sk->nameid); } diff --git a/src/map/skill.h b/src/map/skill.h index 97134224e..0c6ee8cae 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -124,6 +124,7 @@ enum e_skill_inf2 { INF2_FREE_CAST_REDUCED = 0x10000, INF2_SHOW_SKILL_SCALE = 0x20000, INF2_ALLOW_REPRODUCE = 0x40000, + INF2_HIDDEN_TRAP = 0x80000, // Traps that are hidden (based on trap_visiblity battle conf) }; @@ -1806,6 +1807,7 @@ struct skill_unit { int limit; int val1,val2; + bool visible; short alive,range; int prev; }; -- cgit v1.2.3-70-g09d2 From df3f7552e61d1dae45e83c55ff36b95ab95181bb Mon Sep 17 00:00:00 2001 From: gumi Date: Wed, 3 Oct 2018 21:02:01 -0400 Subject: add a warning for `traps_setting` in battle conf --- src/map/battle.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/map') diff --git a/src/map/battle.c b/src/map/battle.c index 2cf04ffce..895876300 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -7525,6 +7525,10 @@ static bool battle_config_read(const char *filename, bool imported) if (!imported) battle->config_set_defaults(); + if (libconfig->lookup(&config, "battle_configuration/traps_setting") != NULL) { + ShowError("The `traps_setting` battle conf option has been replaced by `trap_visibility`. Please see conf/map/battle/skill.conf.\n"); + } + for (i = 0; i < ARRAYLENGTH(battle_data); i++) { int type, val; char config_name[256]; -- cgit v1.2.3-70-g09d2 From 1e3d3a3c0bca912ebdf18ae337814b2f434ec442 Mon Sep 17 00:00:00 2001 From: "Guilherme G. Menaldo" Date: Sat, 20 Oct 2018 23:21:07 -0300 Subject: Added setting to keep traps invisible when triggered --- conf/map/battle/skill.conf | 15 ++++++++++++--- src/map/battle.c | 3 ++- src/map/battle.h | 1 + src/map/skill.c | 4 +++- 4 files changed, 18 insertions(+), 5 deletions(-) (limited to 'src/map') diff --git a/conf/map/battle/skill.conf b/conf/map/battle/skill.conf index 6923e729b..cb219740f 100644 --- a/conf/map/battle/skill.conf +++ b/conf/map/battle/skill.conf @@ -139,12 +139,21 @@ skill_nofootset: 1 gvg_traps_target_all: 1 // Hunter's traps visibility setting (with HiddenTrap: true on skill_db.conf) +// Here we have 2 configs: +// visibility stands to how traps are displayed by default: // 0: Traps are always visible to everyone (Hercules/Pre-renewal) // 1: Traps with HiddenTrap: true are hidden in versus maps (PvP/GvG/BG) // 2: Traps with HiddenTrap: true are always invisible (Renewal) (Default) -// Notes: Invisibility applies to players that are not in caster's party. -// Invisible traps can be made visible to everyone with Hunter's Detecting skill. -trap_visibility: 2 +// Notes: - Invisibility applies to players that are not in caster's party. +// - Invisible traps can be made visible to everyone with Hunter's Detecting skill. +// +// display_on_trigger tells if HiddenTraps should become visible once triggered +// 0: Do not make traps visible once triggered (except for Ankle Snare) (Aegis) +// 1: Always make traps visible once triggered (Hercules) +trap_options: { + visibility: 2 + display_on_trigger: 1 +} // Restrictions applied to the Alchemist's Summon Flora skill (add as necessary) // 1: Enable players to damage the floras outside of versus grounds. diff --git a/src/map/battle.c b/src/map/battle.c index 895876300..a784f6884 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -6966,7 +6966,8 @@ static const struct battle_data { { "player_damage_delay_rate", &battle_config.pc_damage_delay_rate, 100, 0, INT_MAX, }, { "defunit_not_enemy", &battle_config.defnotenemy, 0, 0, 1, }, { "gvg_traps_target_all", &battle_config.vs_traps_bctall, BL_PC, BL_NUL, BL_ALL, }, - { "trap_visibility", &battle_config.trap_visibility, 0, 0, 2, }, + { "trap_options/visibility", &battle_config.trap_visibility, 2, 0, 2, }, + { "trap_options/display_on_trigger", &battle_config.trap_trigger, 1, 0, 1, }, { "summon_flora_setting", &battle_config.summon_flora, 1|2, 0, 1|2, }, { "clear_skills_on_death", &battle_config.clear_unit_ondeath, BL_NUL, BL_NUL, BL_ALL, }, { "clear_skills_on_warp", &battle_config.clear_unit_onwarp, BL_ALL, BL_NUL, BL_ALL, }, diff --git a/src/map/battle.h b/src/map/battle.h index 297768765..007fbabd2 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -150,6 +150,7 @@ struct Battle_Config { int defnotenemy; int vs_traps_bctall; int trap_visibility; + int trap_trigger; int summon_flora; //[Skotlex] int clear_unit_ondeath; //[Skotlex] int clear_unit_onwarp; //[Skotlex] diff --git a/src/map/skill.c b/src/map/skill.c index 2b31de6fc..c320fe4b3 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -12759,7 +12759,9 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b ts->tick += sg->interval*(map->count_oncell(bl->m,bl->x,bl->y,BL_CHAR,0)-1); } - if (battle_config.trap_visibility != 0 && skill->get_inf2(sg->skill_id) & INF2_HIDDEN_TRAP) { + if (sg->skill_id == HT_ANKLESNARE + || (battle_config.trap_trigger == 1 && skill->get_inf2(sg->skill_id) & INF2_HIDDEN_TRAP) + ) { src->visible = true; clif->skillunit_update(&src->bl); } -- cgit v1.2.3-70-g09d2 From 582336dff2bffba506891a54ecd9360e4fbdd65c Mon Sep 17 00:00:00 2001 From: Carlos Henrique Date: Thu, 20 Sep 2018 16:08:47 -0300 Subject: Added mapflags 'nostorage' and 'nogstorage' nostorage 1 -- blocks only @storage nostorage 2 -- blocks only openstorage(); nostorage 3 -- blocks @storage and openstorage() nogstorage 1 -- blocks only @gstorage nogstorage 2 -- blocks only guildopenstorage(); nogstorage 3 -- blocks @gstorage and guildopenstorage() --- db/constants.conf | 2 ++ doc/constants.md | 2 ++ doc/permissions.md | 1 + doc/script_commands.txt | 9 +++++++++ src/map/atcommand.c | 15 +++++++++++++-- src/map/map.c | 26 ++++++++++++++++++++++++++ src/map/map.h | 2 ++ src/map/npc.c | 4 ++++ src/map/pc_groups.c | 1 + src/map/pc_groups.h | 1 + src/map/script.c | 19 +++++++++++++++++++ src/map/script.h | 4 +++- 12 files changed, 83 insertions(+), 3 deletions(-) (limited to 'src/map') diff --git a/db/constants.conf b/db/constants.conf index 08dc63b40..a6c74d56d 100644 --- a/db/constants.conf +++ b/db/constants.conf @@ -424,6 +424,8 @@ constants_db: { mf_noviewid: 56 mf_pairship_startable: 57 mf_pairship_endable: 58 + mf_nostorage: 59 + mf_nogstorage: 60 comment__: "Cell Properties" cell_walkable: 0 diff --git a/doc/constants.md b/doc/constants.md index 276881a43..cca12c59a 100644 --- a/doc/constants.md +++ b/doc/constants.md @@ -362,6 +362,8 @@ - `mf_noviewid`: 56 - `mf_pairship_startable`: 57 - `mf_pairship_endable`: 58 +- `mf_nostorage`: 59 +- `mf_nogstorage`: 60 ### Cell Properties diff --git a/doc/permissions.md b/doc/permissions.md index 7d29b59fd..a8794ecae 100644 --- a/doc/permissions.md +++ b/doc/permissions.md @@ -48,4 +48,5 @@ disable_pickup | Ability to disable the player from picking up any i disable_exp | Ability to disable the player from gaining any experience point. disable_store | Ability to disable the player from using/openning npc and player stores. disable_skill_usage | Ability to disable the player from using any skill. +bypass_nostorage | Ability to bypass the nostorage and nogstorage mapflag. diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 90d4d77cc..4d2517efa 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -5693,6 +5693,10 @@ storage window, to avoid any disruption when both windows overlap. openstorage(); end; +The mapflag 'nostorage' when set to type '2' (or 3), will not open the +account storage. Unless the character group has the permission 'bypass_nostorage'. +In case blocked by mapflag, returns 0. + --------------------------------------- *openmail() @@ -5754,6 +5758,10 @@ time. This will also fail and return 2 if the attached character does not belong to any guild. +The mapflag 'nogstorage' when set to type '2' (or 3), will not open the +guild storage. Unless the character group has the permission 'bypass_nostorage'. +In case blocked by mapflag, returns 1. + --------------------------------------- *guildchangegm(, ) @@ -8160,6 +8168,7 @@ Valid are: PERM_DISABLE_STORE PERM_DISABLE_EXP PERM_DISABLE_SKILL_USAGE + PERM_BYPASS_NOSTORAGE Example: diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 6cfa86163..b8a2670ec 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -871,6 +871,11 @@ ACMD(storage) if (sd->npc_id || sd->state.vending || sd->state.buyingstore || sd->state.trading || sd->state.storage_flag) return false; + if (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nostorage & 1)) { // mapflag nostorage already defined? can't open :c + clif->message(fd, msg_fd(fd, 1161)); // You currently cannot open your storage. + return false; + } + if (storage->open(sd) == 1) { //Already open. clif->message(fd, msg_fd(fd,250)); // You have already opened your storage. Close it first. return false; @@ -904,6 +909,11 @@ ACMD(guildstorage) return false; } + if (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nogstorage & 1)) { // mapflag nogstorage already defined? can't open :c + clif->message(fd, msg_fd(fd, 1161)); // You currently cannot open your storage. (there is no other messages...) + return false; + } + if( gstorage->open(sd) ) { clif->message(fd, msg_fd(fd,1201)); // Your guild's storage has already been opened by another member, try again later. return false; @@ -7739,6 +7749,7 @@ ACMD(mapflag) CHECKFLAG(nodrop); CHECKFLAG(novending); CHECKFLAG(loadevent); CHECKFLAG(nochat); CHECKFLAG(partylock); CHECKFLAG(guildlock); CHECKFLAG(src4instance); CHECKFLAG(notomb); CHECKFLAG(nocashshop); CHECKFLAG(noviewid); CHECKFLAG(town); + CHECKFLAG(nostorage); CHECKFLAG(nogstorage); clif->message(sd->fd," "); clif->message(sd->fd,msg_fd(fd,1312)); // Usage: "@mapflag monster_noteleport 1" (0=Off | 1=On) clif->message(sd->fd,msg_fd(fd,1313)); // Type "@mapflag available" to list the available mapflags. @@ -7780,7 +7791,7 @@ ACMD(mapflag) SETFLAG(nomvploot); SETFLAG(nightenabled); SETFLAG(nodrop); SETFLAG(novending); SETFLAG(loadevent); SETFLAG(nochat); SETFLAG(partylock); SETFLAG(guildlock); SETFLAG(src4instance); SETFLAG(notomb); SETFLAG(nocashshop); SETFLAG(noviewid); - SETFLAG(town); + SETFLAG(town); SETFLAG(nostorage); SETFLAG(nogstorage); clif->message(sd->fd, msg_fd(fd, 1314)); // Invalid flag name or flag. @@ -7793,7 +7804,7 @@ ACMD(mapflag) clif->message(sd->fd, "nozenypenalty, notrade, noskill, nowarp, nowarpto, noicewall, snow, clouds, clouds2,"); clif->message(sd->fd, "fog, fireworks, sakura, leaves, nobaseexp, nojobexp, nomobloot,"); clif->message(sd->fd, "nomvploot, nightenabled, nodrop, novending, loadevent, nochat, partylock,"); - clif->message(sd->fd, "guildlock, src4instance, notomb, nocashshop, noviewid"); + clif->message(sd->fd, "guildlock, src4instance, notomb, nocashshop, noviewid, nostorage, nogstorage"); #undef CHECKFLAG #undef SETFLAG diff --git a/src/map/map.c b/src/map/map.c index 93e86f80b..a0ac8cd95 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -5425,6 +5425,32 @@ static bool map_zone_mf_cache(int m, char *flag, char *params) else if( map->list[m].flag.nocashshop ) map_zone_mf_cache_add(m,"nocashshop"); } + } else if (strcmpi(flag, "nostorage") == 0) { + if (!state) { + if (map->list[m].flag.nostorage != 0) { + sprintf(rflag, "nostorage\t%d", map->list[m].flag.nostorage); + map_zone_mf_cache_add(m, rflag); + } + } + if (sscanf(params, "%d", &state) == 1) { + if (state != map->list[m].flag.nostorage) { + sprintf(rflag, "nostorage\t%d", state); + map_zone_mf_cache_add(m, rflag); + } + } + } else if (strcmpi(flag, "nogstorage") == 0) { + if (!state) { + if (map->list[m].flag.nogstorage != 0) { + sprintf(rflag, "nogstorage\t%d", map->list[m].flag.nogstorage); + map_zone_mf_cache_add(m, rflag); + } + } + if (sscanf(params, "%d", &state) == 1) { + if (state != map->list[m].flag.nogstorage) { + sprintf(rflag, "nogstorage\t%d", state); + map_zone_mf_cache_add(m, rflag); + } + } } return false; diff --git a/src/map/map.h b/src/map/map.h index 4267c2c88..25ff63edc 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -899,6 +899,8 @@ struct map_data { unsigned noautoloot : 1; unsigned pairship_startable : 1; unsigned pairship_endable : 1; + unsigned nostorage : 2; + unsigned nogstorage : 2; uint32 noviewid; ///< noviewid (bitmask - @see enum equip_pos) } flag; struct point save; diff --git a/src/map/npc.c b/src/map/npc.c index 7f57a9c50..fa277f382 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -4555,6 +4555,10 @@ static const char *npc_parse_mapflag(const char *w1, const char *w2, const char map->list[m].flag.pairship_startable = (state) ? 1 : 0; } else if (!strcmpi(w3, "pairship_endable")) { map->list[m].flag.pairship_endable = (state) ? 1 : 0; + } else if (!strcmpi(w3, "nostorage")) { + map->list[m].flag.nostorage = (state) ? cap_value(atoi(w4), 1, 3) : 0; + } else if (!strcmpi(w3, "nogstorage")) { + map->list[m].flag.nogstorage = (state) ? cap_value(atoi(w4), 1, 3) : 0; } else { npc->parse_unknown_mapflag(mapname, w3, w4, start, buffer, filepath, retval); } diff --git a/src/map/pc_groups.c b/src/map/pc_groups.c index 887c946e3..8d55897b8 100644 --- a/src/map/pc_groups.c +++ b/src/map/pc_groups.c @@ -449,6 +449,7 @@ static void do_init_pc_groups(void) { "disable_store", PC_PERM_DISABLE_STORE }, { "disable_exp", PC_PERM_DISABLE_EXP }, { "disable_skill_usage", PC_PERM_DISABLE_SKILL_USAGE }, + { "bypass_nostorage", PC_PERM_BYPASS_NOSTORAGE }, }; unsigned char i, len = ARRAYLENGTH(pc_g_defaults); diff --git a/src/map/pc_groups.h b/src/map/pc_groups.h index 6070809e0..f3994b9c4 100644 --- a/src/map/pc_groups.h +++ b/src/map/pc_groups.h @@ -57,6 +57,7 @@ enum e_pc_permission { PC_PERM_DISABLE_STORE = 0x1000000, PC_PERM_DISABLE_EXP = 0x2000000, PC_PERM_DISABLE_SKILL_USAGE = 0x4000000, + PC_PERM_BYPASS_NOSTORAGE = 0x8000000, }; // Cached config settings for quick lookup diff --git a/src/map/script.c b/src/map/script.c index 41f21cd72..e92c4c227 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -10561,6 +10561,12 @@ static BUILDIN(openstorage) return false; } + // Mapflag preventing from openstorage here + if (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nostorage & 2)) { + script_pushint(st, 0); + return true; + } + storage->open(sd); script_pushint(st, 1); // success flag. @@ -10574,6 +10580,12 @@ static BUILDIN(guildopenstorage) if (sd == NULL) return true; + // Mapflag preventing from openstorage here + if (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nogstorage & 2)) { + script_pushint(st, 1); + return true; + } + ret = gstorage->open(sd); script_pushint(st,ret); return true; @@ -13064,6 +13076,8 @@ static BUILDIN(getmapflag) case MF_NOVIEWID: script_pushint(st, map->list[m].flag.noviewid); break; case MF_PAIRSHIP_STARTABLE: script_pushint(st, map->list[m].flag.pairship_startable); break; case MF_PAIRSHIP_ENDABLE: script_pushint(st, map->list[m].flag.pairship_endable); break; + case MF_NOSTORAGE: script_pushint(st, map->list[m].flag.nostorage); break; + case MF_NOGSTORAGE: script_pushint(st, map->list[m].flag.nogstorage); break; } } @@ -13194,6 +13208,8 @@ static BUILDIN(setmapflag) case MF_NOVIEWID: map->list[m].flag.noviewid = (val <= 0) ? EQP_NONE : val; break; case MF_PAIRSHIP_STARTABLE: map->list[m].flag.pairship_startable = 1; break; case MF_PAIRSHIP_ENDABLE: map->list[m].flag.pairship_endable = 1; break; + case MF_NOSTORAGE: map->list[m].flag.nostorage = cap_value(val, 0, 3); break; + case MF_NOGSTORAGE: map->list[m].flag.nogstorage = cap_value(val, 0, 3); break; } } @@ -13285,6 +13301,8 @@ static BUILDIN(removemapflag) case MF_NOCASHSHOP: map->list[m].flag.nocashshop = 0; break; case MF_NOAUTOLOOT: map->list[m].flag.noautoloot = 0; break; case MF_NOVIEWID: map->list[m].flag.noviewid = EQP_NONE; break; + case MF_NOSTORAGE: map->list[m].flag.nostorage = 0; break; + case MF_NOGSTORAGE: map->list[m].flag.nogstorage = 0; break; } } @@ -25755,6 +25773,7 @@ static void script_hardcoded_constants(void) script->set_constant("PERM_DISABLE_STORE", PC_PERM_DISABLE_STORE, false, false); script->set_constant("PERM_DISABLE_EXP", PC_PERM_DISABLE_EXP, false, false); script->set_constant("PERM_DISABLE_SKILL_USAGE", PC_PERM_DISABLE_SKILL_USAGE, false, false); + script->set_constant("PERM_BYPASS_NOSTORAGE", PC_PERM_BYPASS_NOSTORAGE, false, false); script->constdb_comment("Data types"); script->set_constant("DATATYPE_NIL", DATATYPE_NIL, false, false); diff --git a/src/map/script.h b/src/map/script.h index 549ad3284..e577867ba 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -343,7 +343,9 @@ enum { MF_NOAUTOLOOT, MF_NOVIEWID, MF_PAIRSHIP_STARTABLE, - MF_PAIRSHIP_ENDABLE + MF_PAIRSHIP_ENDABLE, + MF_NOSTORAGE, + MF_NOGSTORAGE }; enum navigation_service { -- cgit v1.2.3-70-g09d2 From e4ea52c0651b7b204aad696d600e623ef1890016 Mon Sep 17 00:00:00 2001 From: AnnieRuru Date: Sat, 23 Mar 2019 06:08:29 +0800 Subject: Suppress ShowWarning for certain mapflags when @reloadscript --- src/map/npc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/map') diff --git a/src/map/npc.c b/src/map/npc.c index 61341744d..8a6b80175 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -4365,8 +4365,7 @@ static const char *npc_parse_mapflag(const char *w1, const char *w2, const char if (!strcmpi(w3, "nosave")) { char savemap[32]; int savex, savey; - if (state == 0) - ; //Map flag disabled. + if (state == 0); //Map flag disabled. else if (w4 && !strcmpi(w4, "SavePoint")) { map->list[m].save.map = 0; map->list[m].save.x = -1; @@ -4659,7 +4658,8 @@ static const char *npc_parse_mapflag(const char *w1, const char *w2, const char } } - if( modifier[0] == '\0' ) { + if (state == 0); //Map flag disabled. + else if (modifier[0] == '\0') { ShowWarning("npc_parse_mapflag: Missing 5th param for 'adjust_unit_duration' flag! removing flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer)); if (retval) *retval = EXIT_FAILURE; } else if( !( skill_id = skill->name2id(skill_name) ) || !skill->get_unit_id( skill->name2id(skill_name), 0) ) { @@ -4718,7 +4718,8 @@ static const char *npc_parse_mapflag(const char *w1, const char *w2, const char } } - if( modifier[0] == '\0' ) { + if (state == 0); //Map flag disabled. + else if (modifier[0] == '\0') { ShowWarning("npc_parse_mapflag: Missing 5th param for 'adjust_skill_damage' flag! removing flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer)); if (retval) *retval = EXIT_FAILURE; } else if( !( skill_id = skill->name2id(skill_name) ) ) { -- cgit v1.2.3-70-g09d2 From 9e934f3608a4a67667573514909911130493e174 Mon Sep 17 00:00:00 2001 From: Ibrahim Zidan Date: Mon, 8 Apr 2019 22:14:16 +0200 Subject: Fix an issue with named/brewed/forged items getting saved with the wrong character id The isue was triggered by the refactoring of old undocumented code that relied on the overflow behavior during assignment from int32 to int16 and from uint16 to int16, and by a subsequent clamping to SMALLINT range operated by the SQL server. Credits: Haru Fixes #2409 Signed-off-by: Ibrahim Zidan --- sql-files/main.sql | 1 + sql-files/upgrades/2019-04-08--21-52.sql | 29 +++++++++++++++++++++++++++++ sql-files/upgrades/index.txt | 1 + src/map/atcommand.c | 6 +++--- src/map/script.c | 12 ++++++------ 5 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 sql-files/upgrades/2019-04-08--21-52.sql (limited to 'src/map') diff --git a/sql-files/main.sql b/sql-files/main.sql index a08ec60ba..5d466f4cd 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -925,6 +925,7 @@ INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1532403228); -- 2018-07-2 INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1535865732); -- 2018-09-01--05-22.sql INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1544738447); -- 2018-12-14--01-02.sql INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1546059075); -- 2018-12-29--07-51.sql +INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1554760320); -- 2019-04-08--21-52.sql -- -- Table structure for table `storage` diff --git a/sql-files/upgrades/2019-04-08--21-52.sql b/sql-files/upgrades/2019-04-08--21-52.sql new file mode 100644 index 000000000..bd015acf8 --- /dev/null +++ b/sql-files/upgrades/2019-04-08--21-52.sql @@ -0,0 +1,29 @@ +#1554760320 + +-- This file is part of Hercules. +-- http://herc.ws - http://github.com/HerculesWS/Hercules +-- +-- Copyright (C) 2013-2019 Hercules Dev Team +-- +-- Hercules is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +UPDATE `auction` SET `card3` = `card2` >> 16, `card2` = `card2` % 65536 WHERE `card2` > 65536 AND (`card0` = 255 OR `card0` = 254); +UPDATE `cart_inventory` SET `card3` = `card2` >> 16, `card2` = `card2` % 65536 WHERE `card2` > 65536 AND (`card0` = 255 OR `card0` = 254); +UPDATE `inventory` SET `card3` = `card2` >> 16, `card2` = `card2` % 65536 WHERE `card2` > 65536 AND (`card0` = 255 OR `card0` = 254); +UPDATE `guild_storage` SET `card3` = `card2` >> 16, `card2` = `card2` % 65536 WHERE `card2` > 65536 AND (`card0` = 255 OR `card0` = 254); +UPDATE `mail` SET `card3` = `card2` >> 16, `card2` = `card2` % 65536 WHERE `card2` > 65536 AND (`card0` = 255 OR `card0` = 254); +UPDATE `rodex_items` SET `card3` = `card2` >> 16, `card2` = `card2` % 65536 WHERE `card2` > 65536 AND (`card0` = 255 OR `card0` = 254); +UPDATE `storage` SET `card3` = `card2` >> 16, `card2` = `card2` % 65536 WHERE `card2` > 65536 AND (`card0` = 255 OR `card0` = 254); + +INSERT INTO `sql_updates` (`timestamp`, `ignored`) VALUES (1554760320, 'No'); diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt index 3a2fc4c33..a77a7635f 100644 --- a/sql-files/upgrades/index.txt +++ b/sql-files/upgrades/index.txt @@ -52,3 +52,4 @@ 2018-09-01--05-22.sql 2018-12-14--01-02.sql 2018-12-29--07-51.sql +2019-04-08--21-52.sql diff --git a/src/map/atcommand.c b/src/map/atcommand.c index a9bbff7bd..b670d64dc 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -5599,9 +5599,9 @@ static void atcommand_getring(struct map_session_data *sd) memset(&item_tmp, 0, sizeof(item_tmp)); item_tmp.nameid = item_id; item_tmp.identify = 1; - item_tmp.card[0] = 255; - item_tmp.card[2] = sd->status.partner_id; - item_tmp.card[3] = sd->status.partner_id >> 16; + item_tmp.card[0] = CARD0_FORGE; + item_tmp.card[2] = GetWord(sd->status.partner_id, 0); + item_tmp.card[3] = GetWord(sd->status.partner_id, 1); if((flag = pc->additem(sd,&item_tmp,1,LOG_TYPE_COMMAND))) { clif->additem(sd,0,0,flag); diff --git a/src/map/script.c b/src/map/script.c index fe8638ac3..76b07fa37 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -8177,12 +8177,12 @@ static BUILDIN(getnameditem) } memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=nameid; - item_tmp.amount=1; - item_tmp.identify=1; - item_tmp.card[0]=CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus] - item_tmp.card[2]=tsd->status.char_id; - item_tmp.card[3]=tsd->status.char_id >> 16; + item_tmp.nameid = nameid; + item_tmp.amount = 1; + item_tmp.identify = 1; + item_tmp.card[0] = CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus] + item_tmp.card[2] = GetWord(tsd->status.char_id, 0); + item_tmp.card[3] = GetWord(tsd->status.char_id, 1); if(pc->additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT)) { script_pushint(st,0); return true; //Failed to add item, we will not drop if they don't fit -- cgit v1.2.3-70-g09d2 From 33d403c5b5bfeafc4966589594e8a0647b744b5f Mon Sep 17 00:00:00 2001 From: Emistry Haoyan Date: Tue, 9 Apr 2019 22:32:24 +0800 Subject: Expand *getinventorylist() script command - return an array `@inventorylist_favorite` - indicate whether an item in the inventory are located inside favorite tab or not. --- doc/script_commands.txt | 3 ++- src/map/script.c | 57 +++++++++++++++++++++++++++---------------------- 2 files changed, 34 insertions(+), 26 deletions(-) (limited to 'src/map') diff --git a/doc/script_commands.txt b/doc/script_commands.txt index b53dda2f6..fd593f17e 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -3129,7 +3129,8 @@ recreate these items perfectly if they are destroyed. Here's what you get: made by a specific craftsman. @inventorylist_expire[] - expire time (Unix time stamp). 0 means never expires. -@inventorylist_bound - whether it is an account bounded item or not. +@inventorylist_bound[] - whether it is an account bounded item or not. +@inventorylist_favorite[] - whether it is favorite (inside favorite tab) or not. @inventorylist_count - the number of items in these lists. This could be handy to save/restore a character's inventory, since no diff --git a/src/map/script.c b/src/map/script.c index fe8638ac3..dea94c6a3 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -14832,48 +14832,55 @@ static BUILDIN(petloot) * Set arrays with info of all sd inventory : * @inventorylist_id, @inventorylist_amount, @inventorylist_equip, * @inventorylist_refine, @inventorylist_identify, @inventorylist_attribute, - * @inventorylist_card(0..3), @inventorylist_expire + * @inventorylist_card(0..3), + * @inventorylist_opt_id(0..MAX_ITEM_OPTIONS), + * @inventorylist_opt_val(0..MAX_ITEM_OPTIONS), + * @inventorylist_opt_param(0..MAX_ITEM_OPTIONS), + * @inventorylist_expire, @inventorylist_bound, @inventorylist_favorite, + * @inventorylist_idx * @inventorylist_count = scalar *------------------------------------------*/ static BUILDIN(getinventorylist) { struct map_session_data *sd = script->rid2sd(st); - char card_var[SCRIPT_VARNAME_LENGTH]; + char script_var[SCRIPT_VARNAME_LENGTH]; + int j = 0, k = 0; - int j=0,k; - if(!sd) return true; + if (sd == NULL) + return true; - for (int i = 0;i < sd->status.inventorySize; i++) { - if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0) { - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_id"), j),sd->status.inventory[i].nameid); - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_amount"), j),sd->status.inventory[i].amount); - if(sd->status.inventory[i].equip) { - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_equip"), j),pc->equippoint(sd,i)); + for (int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0) { + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_id"), j), sd->status.inventory[i].nameid); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_amount"), j), sd->status.inventory[i].amount); + if (sd->status.inventory[i].equip != 0) { + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_equip"), j), pc->equippoint(sd, i)); } else { - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_equip"), j),0); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_equip"), j), 0); } - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_refine"), j),sd->status.inventory[i].refine); - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_identify"), j),sd->status.inventory[i].identify); - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_attribute"), j),sd->status.inventory[i].attribute); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_refine"), j), sd->status.inventory[i].refine); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_identify"), j), sd->status.inventory[i].identify); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_attribute"), j), sd->status.inventory[i].attribute); for (k = 0; k < MAX_SLOTS; k++) { - sprintf(card_var, "@inventorylist_card%d",k+1); - pc->setreg(sd,reference_uid(script->add_variable(card_var), j),sd->status.inventory[i].card[k]); + sprintf(script_var, "@inventorylist_card%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].card[k]); } for (k = 0; k < MAX_ITEM_OPTIONS; k++) { - sprintf(card_var, "@inventorylist_opt_id%d", k + 1); - pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.inventory[i].option[k].index); - sprintf(card_var, "@inventorylist_opt_val%d", k + 1); - pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.inventory[i].option[k].value); - sprintf(card_var, "@inventorylist_opt_param%d", k + 1); - pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.inventory[i].option[k].param); + sprintf(script_var, "@inventorylist_opt_id%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].option[k].index); + sprintf(script_var, "@inventorylist_opt_val%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].option[k].value); + sprintf(script_var, "@inventorylist_opt_param%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].option[k].param); } - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_expire"), j),sd->status.inventory[i].expire_time); - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_bound"), j),sd->status.inventory[i].bound); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_expire"), j), sd->status.inventory[i].expire_time); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_bound"), j), sd->status.inventory[i].bound); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_favorite"), j), sd->status.inventory[i].favorite); pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_idx"), j), i); j++; } } - pc->setreg(sd,script->add_variable("@inventorylist_count"),j); + pc->setreg(sd, script->add_variable("@inventorylist_count"), j); return true; } -- cgit v1.2.3-70-g09d2 From 37767f6505db9e7a4039621cfd77623b06d92606 Mon Sep 17 00:00:00 2001 From: Ibrahim Zidan Date: Mon, 8 Apr 2019 04:23:08 +0200 Subject: Rewrite clif_storageItems - The maximum packetsize is now decided during compile time depending on client version which fixes an issue started with clients supporting int32 as itemid where packet size would underflow - The function now have a single loop that is easier to read and understand Signed-off-by: Ibrahim Zidan --- src/map/clif.c | 42 ++++++++++++++++++------------------------ src/map/clif.h | 5 +++++ 2 files changed, 23 insertions(+), 24 deletions(-) (limited to 'src/map') diff --git a/src/map/clif.c b/src/map/clif.c index cd3131181..0f8469e64 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -2977,28 +2977,22 @@ static void clif_storageItems(struct map_session_data *sd, enum inventory_type t nullpo_retv(sd); nullpo_retv(items); - int i = 0; - struct item_data *id; - - do { - int normal = 0, equip = 0, k = 0; - - for( ; i < items_length && k < 500; i++, k++ ) { + for (int i = 0, normal_count = 0, equip_count = 0; i < items_length; ++i) { + struct item_data *itd; - if( items[i].nameid <= 0 ) - continue; + if (items[i].nameid == 0) + continue; - id = itemdb->search(items[i].nameid); + itd = itemdb->search(items[i].nameid); - if( !itemdb->isstackable2(id) ) //Non-stackable (Equippable) - clif->item_equip(i+1,&storelist_equip.list[equip++],&items[i],id,id->equip); - else //Stackable (Normal) - clif->item_normal(i+1,&storelist_normal.list[normal++],&items[i],id); - } + if (!itemdb->isstackable2(itd)) + clif->item_equip(i + 1, &storelist_equip.list[equip_count++], &items[i], itd, itd->equip); + else + clif->item_normal(i + 1, &storelist_normal.list[normal_count++], &items[i], itd); - if( normal ) { - storelist_normal.PacketType = storageListNormalType; - storelist_normal.PacketLength = ( sizeof( storelist_normal ) - sizeof( storelist_normal.list ) ) + (sizeof(struct NORMALITEM_INFO) * normal); + if (normal_count > 0 && (normal_count == MAX_STORAGE_ITEM_PACKET_NORMAL || i + 1 == items_length)) { + storelist_normal.PacketType = storageListNormalType; + storelist_normal.PacketLength = (sizeof(storelist_normal) - sizeof(storelist_normal.list)) + (sizeof(struct NORMALITEM_INFO) * normal_count); #if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 storelist_normal.invType = type; @@ -3008,11 +3002,12 @@ static void clif_storageItems(struct map_session_data *sd, enum inventory_type t #endif clif->send(&storelist_normal, storelist_normal.PacketLength, &sd->bl, SELF); + normal_count = 0; } - if( equip ) { - storelist_equip.PacketType = storageListEquipType; - storelist_equip.PacketLength = ( sizeof( storelist_equip ) - sizeof( storelist_equip.list ) ) + (sizeof(struct EQUIPITEM_INFO) * equip); + if (equip_count > 0 && (equip_count == MAX_STORAGE_ITEM_PACKET_EQUIP || i + 1 == items_length)) { + storelist_equip.PacketType = storageListEquipType; + storelist_equip.PacketLength = (sizeof(storelist_equip) - sizeof(storelist_equip.list)) + (sizeof(struct EQUIPITEM_INFO) * equip_count); #if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 storelist_equip.invType = type; @@ -3022,10 +3017,9 @@ static void clif_storageItems(struct map_session_data *sd, enum inventory_type t #endif clif->send(&storelist_equip, storelist_equip.PacketLength, &sd->bl, SELF); + equip_count = 0; } - - } while ( i < items_length ); - + } } static void clif_cartList(struct map_session_data *sd) diff --git a/src/map/clif.h b/src/map/clif.h index 6c9058cba..b3441f908 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -78,6 +78,11 @@ enum rodex_get_items; #define COLOR_YELLOW 0xffff00U #define COLOR_DEFAULT COLOR_GREEN +#define MAX_STORAGE_ITEM_PACKET_NORMAL ((INT16_MAX - (sizeof(struct ZC_STORE_ITEMLIST_NORMAL) - (sizeof(struct NORMALITEM_INFO) * MAX_ITEMLIST))) / sizeof(struct NORMALITEM_INFO)) +#define MAX_STORAGE_ITEM_PACKET_EQUIP ((INT16_MAX - (sizeof(struct ZC_STORE_ITEMLIST_EQUIP) - (sizeof(struct EQUIPITEM_INFO) * MAX_ITEMLIST))) / sizeof(struct EQUIPITEM_INFO)) +STATIC_ASSERT(MAX_STORAGE_ITEM_PACKET_NORMAL > 0, "Max items per storage item packet for normal items is less than 1, it's most likely to be a bug and shall not be ignored."); +STATIC_ASSERT(MAX_STORAGE_ITEM_PACKET_EQUIP > 0, "Max items per storage item packet for equip items is less than 1, it's most likely to be a bug and shall not be ignored."); + /** * Enumerations **/ -- cgit v1.2.3-70-g09d2 From 5410de9d04124ce34f58173d616c21088a1482ee Mon Sep 17 00:00:00 2001 From: Ibrahim Zidan Date: Mon, 8 Apr 2019 04:37:35 +0200 Subject: Addition of missing nullpo check in clif_send and an assertion check for zero/negatvie length values Signed-off-by: Ibrahim Zidan --- src/map/clif.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/map') diff --git a/src/map/clif.c b/src/map/clif.c index 0f8469e64..53c2c6b23 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -431,8 +431,13 @@ static int clif_send_actual(int fd, void *buf, int len) *------------------------------------------*/ static bool clif_send(const void *buf, int len, struct block_list *bl, enum send_target type) { + if (type != ALL_CLIENT) + nullpo_retr(false, bl); + nullpo_retr(false, buf); + Assert_retr(false, len > 0); + int i; - struct map_session_data *sd, *tsd; + struct map_session_data *sd = BL_CAST(BL_PC, bl), *tsd; struct party_data *p = NULL; struct guild *g = NULL; struct battleground_data *bgd = NULL; @@ -440,11 +445,6 @@ static bool clif_send(const void *buf, int len, struct block_list *bl, enum send struct s_mapiterator* iter; int area_size; - if( type != ALL_CLIENT ) - nullpo_ret(bl); - - sd = BL_CAST(BL_PC, bl); - if (sd != NULL && pc_isinvisible(sd)) { if (type == AREA || type == BG || type == BG_AREA) type = SELF; -- cgit v1.2.3-70-g09d2 From 112b19df8acb95e5599927fc4b505d4ca2e610a4 Mon Sep 17 00:00:00 2001 From: Ibrahim Zidan Date: Mon, 8 Apr 2019 04:42:12 +0200 Subject: Fix direct calls to clif_send in clif.c Signed-off-by: Ibrahim Zidan --- src/map/clif.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/map') diff --git a/src/map/clif.c b/src/map/clif.c index 53c2c6b23..38d0215d3 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -2978,12 +2978,10 @@ static void clif_storageItems(struct map_session_data *sd, enum inventory_type t nullpo_retv(items); for (int i = 0, normal_count = 0, equip_count = 0; i < items_length; ++i) { - struct item_data *itd; - if (items[i].nameid == 0) continue; - itd = itemdb->search(items[i].nameid); + struct item_data *itd = itemdb->search(items[i].nameid); if (!itemdb->isstackable2(itd)) clif->item_equip(i + 1, &storelist_equip.list[equip_count++], &items[i], itd, itd->equip); @@ -7125,7 +7123,7 @@ static void clif_party_job_and_level(struct map_session_data *sd) WBUFW(buf, 6) = sd->status.class; WBUFW(buf, 8) = sd->status.base_level; - clif_send(buf, packet_len(0xabd), &sd->bl, PARTY); + clif->send(buf, packet_len(0xabd), &sd->bl, PARTY); #endif } @@ -21630,7 +21628,7 @@ static void clif_hat_effect_single(struct block_list *bl, uint16 effectId, bool WBUFB(buf,8) = enable; WBUFL(buf,9) = effectId; - clif_send(buf, 13, bl, AREA); + clif->send(buf, 13, bl, AREA); #endif } -- cgit v1.2.3-70-g09d2 From 55567b93ee5b287c632ff18327df6408f0cf4692 Mon Sep 17 00:00:00 2001 From: AnnieRuru Date: Thu, 11 Apr 2019 00:12:18 +0800 Subject: Modernize coding style --- src/map/script.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) (limited to 'src/map') diff --git a/src/map/script.c b/src/map/script.c index fe8638ac3..2a4cbd4f2 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -6947,54 +6947,52 @@ static BUILDIN(jobname) return true; } -/// Get input from the player. -/// For numeric inputs the value is capped to the range [min,max]. Returns 1 if -/// the value was higher than 'max', -1 if lower than 'min' and 0 otherwise. -/// For string inputs it returns 1 if the string was longer than 'max', -1 is -/// shorter than 'min' and 0 otherwise. -/// -/// input({,{,}}) -> +/* + * Get input from the player. + * For numeric inputs the value is capped to the range [min,max]. Returns 1 if + * the value was higher than 'max', -1 if lower than 'min' and 0 otherwise. + * For string inputs it returns 1 if the string was longer than 'max', -1 is + * shorter than 'min' and 0 otherwise. + * + * input({,{,}}) -> + */ static BUILDIN(input) { - struct script_data* data; - int64 uid; - const char* name; - int min; - int max; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - data = script_getdata(st,2); - if( !data_isreference(data) ) { + struct script_data *data = script_getdata(st, 2); + if (!data_isreference(data)) { ShowError("script:input: not a variable\n"); script->reportdata(data); st->state = END; return false; } - uid = reference_getuid(data); - name = reference_getname(data); - min = (script_hasdata(st,3) ? script_getnum(st,3) : script->config.input_min_value); - max = (script_hasdata(st,4) ? script_getnum(st,4) : script->config.input_max_value); + + int64 uid = reference_getuid(data); + const char *name = reference_getname(data); + int min = (script_hasdata(st, 3) ? script_getnum(st, 3) : script->config.input_min_value); + int max = (script_hasdata(st, 4) ? script_getnum(st, 4) : script->config.input_max_value); #ifdef SECURE_NPCTIMEOUT sd->npc_idle_type = NPCT_WAIT; #endif - if( !sd->state.menu_or_input ) { + if (!sd->state.menu_or_input) { // first invocation, display npc input box sd->state.menu_or_input = 1; st->state = RERUNLINE; - if( is_string_variable(name) ) - clif->scriptinputstr(sd,st->oid); + if (is_string_variable(name)) + clif->scriptinputstr(sd, st->oid); else - clif->scriptinput(sd,st->oid); + clif->scriptinput(sd, st->oid); } else { // take received text/value and store it in the designated variable sd->state.menu_or_input = 0; if (is_string_variable(name)) { int len = (int)strlen(sd->npc_str); - script->set_reg(st, sd, uid, name, sd->npc_str, script_getref(st,2)); + script->set_reg(st, sd, uid, name, sd->npc_str, script_getref(st, 2)); script_pushint(st, (len > max ? 1 : len < min ? -1 : 0)); } else { int amount = sd->npc_amount; -- cgit v1.2.3-70-g09d2 From 797464b89910c4a04d0381a831001f4a7591ad28 Mon Sep 17 00:00:00 2001 From: AnnieRuru Date: Thu, 11 Apr 2019 00:50:28 +0800 Subject: Add 3 pc variables just for the input limit ... sd->npc_amount_min and sd->npc_amount_max to limit input range and sd->npc_input_capped_range because the documentation ask for it --- src/map/clif.c | 15 ++++++++++++--- src/map/pc.h | 3 +++ src/map/script.c | 9 ++++++--- 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'src/map') diff --git a/src/map/clif.c b/src/map/clif.c index cd3131181..037e168d0 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -12587,10 +12587,19 @@ static void clif_parse_NpcAmountInput(int fd, struct map_session_data *sd) int npcid = RFIFOL(fd,2); int amount = RFIFOL(fd,6); - if (amount >= 0) + if (amount < sd->npc_amount_min) { + sd->npc_amount = sd->npc_amount_min; + sd->npc_input_capped_range = -1; + } + else if (amount > sd->npc_amount_max) { + sd->npc_amount = sd->npc_amount_max; + sd->npc_input_capped_range = 1; + } + else { sd->npc_amount = amount; - else - sd->npc_amount = 0; + sd->npc_input_capped_range = 0; + } + npc->scriptcont(sd, npcid, false); } diff --git a/src/map/pc.h b/src/map/pc.h index b2069d4df..41384b484 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -273,6 +273,9 @@ struct map_session_data { int npc_item_flag; //Marks the npc_id with which you can change equipments during interactions with said npc (see script command enable_itemuse) int npc_menu; // internal variable, used in npc menu handling int npc_amount; + int npc_amount_min; + int npc_amount_max; + int npc_input_capped_range; struct script_state *st; char npc_str[CHATBOX_SIZE]; // for passing npc input box text to script engine int npc_timer_id; //For player attached npc timers. [Skotlex] diff --git a/src/map/script.c b/src/map/script.c index 2a4cbd4f2..391fb34c4 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -6983,10 +6983,13 @@ static BUILDIN(input) // first invocation, display npc input box sd->state.menu_or_input = 1; st->state = RERUNLINE; - if (is_string_variable(name)) + if (is_string_variable(name)) { clif->scriptinputstr(sd, st->oid); - else + } else { + sd->npc_amount_min = min; + sd->npc_amount_max = max; clif->scriptinput(sd, st->oid); + } } else { // take received text/value and store it in the designated variable sd->state.menu_or_input = 0; @@ -6997,7 +7000,7 @@ static BUILDIN(input) } else { int amount = sd->npc_amount; script->set_reg(st, sd, uid, name, (const void *)h64BPTRSIZE(cap_value(amount,min,max)), script_getref(st,2)); - script_pushint(st, (amount > max ? 1 : amount < min ? -1 : 0)); + script_pushint(st, sd->npc_input_capped_range); } st->state = RUN; } -- cgit v1.2.3-70-g09d2 From bc7f9019e8ec1ed3802c49baeaa35fdd20be2ab3 Mon Sep 17 00:00:00 2001 From: AnnieRuru Date: Thu, 11 Apr 2019 11:19:09 +0800 Subject: Standardize to "all" in script commands --- doc/script_commands.txt | 2 +- src/map/script.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src/map') diff --git a/doc/script_commands.txt b/doc/script_commands.txt index b53dda2f6..8f114fbac 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -6502,7 +6502,7 @@ This command will kill all monsters that were spawned with monster() or areamonster() and have a specified event label attached to them. Commonly used to get rid of remaining quest monsters once the quest is complete. -If the label is given as "All", all monsters which have their respawn +If the label is given as "all", all monsters which have their respawn times set to -1 (like all the monsters summoned with 'monster' or 'areamonster' script command, and all monsters summoned with GM commands, but no other ones - that is, all non-permanent monsters) on the specified diff --git a/src/map/script.c b/src/map/script.c index fe8638ac3..01245a617 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -11043,10 +11043,16 @@ static BUILDIN(killmonster) int16 m,allflag=0; mapname=script_getstr(st,2); event=script_getstr(st,3); - if(strcmp(event,"All")==0) + + if (strcmpi(event, "all") == 0) { + if (strcmp(event, "all") != 0) { + ShowWarning("buildin_killmonster: \"%s\" deprecated! Please use \"all\" instead.\n", event); + script->reportsrc(st); + } allflag = 1; - else + } else { script->check_event(st, event); + } if( (m=map->mapname2mapid(mapname))<0 ) return true; -- cgit v1.2.3-70-g09d2 From 1259ea8e1787a5e9677bcc7cf21338fc81da90f1 Mon Sep 17 00:00:00 2001 From: Emistry Haoyan Date: Thu, 11 Apr 2019 00:13:10 +0800 Subject: Added NULL unit data validation for NPC. - script command `npcspeed`, `npcwalkto`, `npcstop`, `unitwalk`, `unitwarp`, `unitstop` are not applicable for floating npc (without coordinate/location). - Fix map-crash due to unit_data `NULL`. --- src/map/script.c | 108 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 46 deletions(-) (limited to 'src/map') diff --git a/src/map/script.c b/src/map/script.c index fe8638ac3..e99547fc7 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -15910,37 +15910,41 @@ static BUILDIN(npctalk) // change npc walkspeed [Valaris] static BUILDIN(npcspeed) { - struct npc_data* nd; - int speed; - - speed = script_getnum(st,2); - nd = map->id2nd(st->oid); + struct npc_data *nd = map->id2nd(st->oid); + int speed = script_getnum(st, 2); if (nd != NULL) { unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit + if (nd->ud == NULL) { + ShowWarning("buildin_npcspeed: floating NPC don't have unit data.\n"); + return false; + } nd->speed = speed; nd->ud->state.speed_changed = 1; } return true; } + // make an npc walk to a position [Valaris] static BUILDIN(npcwalkto) { struct npc_data *nd = map->id2nd(st->oid); - int x=0,y=0; - - x=script_getnum(st,2); - y=script_getnum(st,3); + int x = script_getnum(st, 2); + int y = script_getnum(st, 3); if (nd != NULL) { unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit + if (nd->ud == NULL) { + ShowWarning("buildin_npcwalkto: floating NPC don't have unit data.\n"); + return false; + } if (!nd->status.hp) { status_calc_npc(nd, SCO_FIRST); } else { status_calc_npc(nd, SCO_NONE); } - unit->walktoxy(&nd->bl,x,y,0); + unit->walktoxy(&nd->bl, x, y, 0); } return true; @@ -15952,6 +15956,10 @@ static BUILDIN(npcstop) if (nd != NULL) { unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit + if (nd->ud == NULL) { + ShowWarning("buildin_npcstop: floating NPC don't have unit data.\n"); + return false; + } unit->stop_walking(&nd->bl, STOPWALKING_FLAG_FIXPOS|STOPWALKING_FLAG_NEXTCELL); } @@ -20372,24 +20380,28 @@ static BUILDIN(setunitname) /// unitwalk(,) -> static BUILDIN(unitwalk) { - struct block_list* bl; + struct block_list *bl = map->id2bl(script_getnum(st, 2)); - bl = map->id2bl(script_getnum(st,2)); - if( bl == NULL ) { + if (bl == NULL) { script_pushint(st, 0); return true; } - if( bl->type == BL_NPC ) { - unit->bl2ud2(bl); // ensure the ((struct npc_data*)bl)->ud is safe to edit + if (bl->type == BL_NPC) { + struct unit_data *ud = unit->bl2ud2(bl); // ensure the ((struct npc_data*)bl)->ud is safe to edit + if (ud == NULL) { + ShowWarning("buildin_unitwalk: floating NPC don't have unit data.\n"); + return false; + } } - if( script_hasdata(st,4) ) { - int x = script_getnum(st,3); - int y = script_getnum(st,4); - script_pushint(st, unit->walktoxy(bl,x,y,0));// We'll use harder calculations. - } else { - int target_id = script_getnum(st,3); - script_pushint(st, unit->walktobl(bl,map->id2bl(target_id),1,1)); + if (script_hasdata(st, 4)) { + int x = script_getnum(st, 3); + int y = script_getnum(st, 4); + script_pushint(st, unit->walktoxy(bl, x, y, 0));// We'll use harder calculations. + } + else { + int target_id = script_getnum(st, 3); + script_pushint(st, unit->walktobl(bl, map->id2bl(target_id), 1, 1)); } return true; @@ -20413,32 +20425,34 @@ static BUILDIN(unitkill) /// unitwarp(,"",,) -> static BUILDIN(unitwarp) { - int unit_id; + int unit_id = script_getnum(st, 2); + const char *mapname = script_getstr(st, 3); + short x = (short)script_getnum(st, 4); + short y = (short)script_getnum(st, 5); int mapid; - short x; - short y; - struct block_list* bl; - const char *mapname; - - unit_id = script_getnum(st,2); - mapname = script_getstr(st, 3); - x = (short)script_getnum(st,4); - y = (short)script_getnum(st,5); + struct block_list *bl; if (!unit_id) //Warp the script's runner bl = map->id2bl(st->rid); else bl = map->id2bl(unit_id); - if( strcmp(mapname,"this") == 0 ) - mapid = bl?bl->m:-1; + if (strcmp(mapname, "this") == 0) + mapid = bl ? bl->m : -1; else mapid = map->mapname2mapid(mapname); - if( mapid >= 0 && bl != NULL ) { - unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit - script_pushint(st, unit->warp(bl,mapid,x,y,CLR_OUTSIGHT)); - } else { + if (mapid >= 0 && bl != NULL) { + struct unit_data *ud = unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit + if (bl->type == BL_NPC) { + if (ud == NULL) { + ShowWarning("buildin_unitwarp: floating NPC don't have unit data.\n"); + return false; + } + } + script_pushint(st, unit->warp(bl, mapid, x, y, CLR_OUTSIGHT)); + } + else { script_pushint(st, 0); } @@ -20509,17 +20523,19 @@ static BUILDIN(unitattack) /// unitstop ; static BUILDIN(unitstop) { - int unit_id; - struct block_list* bl; - - unit_id = script_getnum(st,2); + struct block_list *bl = map->id2bl(script_getnum(st, 2)); - bl = map->id2bl(unit_id); - if( bl != NULL ) { - unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit + if (bl != NULL) { + struct unit_data *ud = unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit + if (bl->type == BL_NPC) { + if (ud == NULL) { + ShowWarning("buildin_unitstop: floating NPC don't have unit data.\n"); + return false; + } + } unit->stop_attack(bl); unit->stop_walking(bl, STOPWALKING_FLAG_NEXTCELL); - if( bl->type == BL_MOB ) + if (bl->type == BL_MOB) BL_UCAST(BL_MOB, bl)->target_id = 0; } -- cgit v1.2.3-70-g09d2 From 5495b64907b0c5987ef8efeffbf7e559c49c2cf9 Mon Sep 17 00:00:00 2001 From: Emistry Haoyan Date: Sun, 14 Apr 2019 21:59:17 +0800 Subject: Disable chatroom creation if cell_chknochat - doesn't allow chatroom creation/switch if the coordinated cell are set with cell_chknochat. - chatroom will be auto deleted. --- src/map/chat.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/map') diff --git a/src/map/chat.c b/src/map/chat.c index d9b642219..77a12a560 100644 --- a/src/map/chat.c +++ b/src/map/chat.c @@ -255,6 +255,14 @@ static int chat_leavechat(struct map_session_data *sd, bool kicked) } if( leavechar == 0 && cd->owner->type == BL_PC ) { + + // check if new location are CELL_CHKNOCHAT + if (map->getcell(cd->usersd[0]->bl.m, NULL, cd->usersd[0]->bl.x, cd->usersd[0]->bl.y, CELL_CHKNOCHAT)) { + for (i = (cd->users - 1); i >= 0; i--) + chat->leave(cd->usersd[i], false); + return 2; + } + // Set and announce new owner cd->owner = &cd->usersd[0]->bl; clif->changechatowner(cd, cd->usersd[0]); -- cgit v1.2.3-70-g09d2 From d34df93e14582b1c2ad43763666d674b7e8440ca Mon Sep 17 00:00:00 2001 From: "Guilherme G. Menaldo" Date: Mon, 1 Oct 2018 13:52:15 -0300 Subject: Added support for mobs to drop items with Random Options --- conf/map/battle/drops.conf | 5 + db/mob_db2.conf | 2 + db/option_drop_groups.conf | 53 +++++++ db/pre-re/mob_db.conf | 2 + db/re/mob_db.conf | 2 + doc/mob_db.txt | 68 +++++++-- doc/option_drop_group.md | 97 ++++++++++++ src/map/battle.c | 1 + src/map/battle.h | 2 + src/map/mob.c | 357 ++++++++++++++++++++++++++++++++++++++++++++- src/map/mob.h | 46 +++++- 11 files changed, 609 insertions(+), 26 deletions(-) create mode 100644 db/option_drop_groups.conf create mode 100644 doc/option_drop_group.md (limited to 'src/map') diff --git a/conf/map/battle/drops.conf b/conf/map/battle/drops.conf index eb7d94f13..51280702b 100644 --- a/conf/map/battle/drops.conf +++ b/conf/map/battle/drops.conf @@ -146,3 +146,8 @@ drops_by_luk2: 0 // 1: Only marine spheres drop items. // 2: All alchemist summons drop items. alchemist_summon_reward: 1 + +// The maximum number of full iterations that server can do when dropping an item with options. +// When picking random options for a dropped item, it does lots of iterations to choose the option to be set, +// this value limits the number of iterations to avoid making the server hang in a long loop. +option_drop_max_loop: 10 diff --git a/db/mob_db2.conf b/db/mob_db2.conf index 8d3e67904..e2894a719 100644 --- a/db/mob_db2.conf +++ b/db/mob_db2.conf @@ -89,6 +89,8 @@ mob_db: ( } Drops: { AegisName: chance (string: int) + // or + AegisName: (chance, "Option Drop Group") // ... } }, diff --git a/db/option_drop_groups.conf b/db/option_drop_groups.conf new file mode 100644 index 000000000..b293be19a --- /dev/null +++ b/db/option_drop_groups.conf @@ -0,0 +1,53 @@ +//================= Hercules Database ===================================== +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2018 Hercules Dev Team +//= +//= Hercules is free software: you can redistribute it and/or modify +//= it under the terms of the GNU General Public License as published by +//= the Free Software Foundation, either version 3 of the License, or +//= (at your option) any later version. +//= +//= This program is distributed in the hope that it will be useful, +//= but WITHOUT ANY WARRANTY; without even the implied warranty of +//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//= GNU General Public License for more details. +//= +//= You should have received a copy of the GNU General Public License +//= along with this program. If not, see . +//========================================================================= +//= Random Option Drop Group Database +//========================================================================= + +option_drop_group_db: ( +{ +/************************************************************************** + ************* Entry structure ******************************************** + ************************************************************************** + : ( + { // Option Slot 1 + Rate: (int) chance of filling option slot 1 (100 = 1%) + + // Possible options for slot 1 + // min/max value : int, defaults to 0 + // chance : int, 100 = 1% if not set, will be 100%/number of possibiltiies + OptionName: value + // or + OptionName: [min value, max value] + // or + OptionName: [min value, max value, chance] + // ... (as many as you want) + }, + // ... (up to MAX_ITEM_OPTION) + ), +**************************************************************************/ +} +) diff --git a/db/pre-re/mob_db.conf b/db/pre-re/mob_db.conf index 40635cd68..553593cea 100644 --- a/db/pre-re/mob_db.conf +++ b/db/pre-re/mob_db.conf @@ -89,6 +89,8 @@ mob_db: ( } Drops: { AegisName: chance (string: int) + // or + AegisName: (chance, "Option Drop Group") // ... } }, diff --git a/db/re/mob_db.conf b/db/re/mob_db.conf index e90b478e7..f787d5478 100644 --- a/db/re/mob_db.conf +++ b/db/re/mob_db.conf @@ -89,6 +89,8 @@ mob_db: ( } Drops: { AegisName: chance (string: int) + // or + AegisName: (chance, "Option Drop Group") // ... } }, diff --git a/doc/mob_db.txt b/doc/mob_db.txt index 29d2ab465..d62181048 100644 --- a/doc/mob_db.txt +++ b/doc/mob_db.txt @@ -63,10 +63,14 @@ mob_db: ( MvpExp: mvp experience (int, defaults to 0) MvpDrops: { AegisName: chance (string: int) + // or + AegisName: (chance, "Option Drop Group") // ... } Drops: { - AegisName: chance (string: int) + AegisName: chance (string: int) + // or + AegisName: (chance, "Option Drop Group") // ... } }, @@ -199,21 +203,55 @@ MvpExp: Base Experience given by the monster to the player who inflict more atta MvpDrops: Sets monster mvp drops list. Requires to have MvpExp to trigger. - Accepted values are AegisName as defined on item_db.conf and a chance. + There are two ways to define a drop: + 1) The first one is used for simple drops and uses the item AegisName + as defined on item_db.conf and a chance. + Format: + AegisName: chance Chance is an integer from 1 to 10000 (10000 = 100%). - Required format: - MvpDrops: { - AegisName: chance - // ... - } - When not specified, becomes false. + + 2) The second way to define a drop allows setting a random option drop + group to be used by this drop. + Format: + AegisName: (chance, "Option Drop Group") + + The item drop chance refers to the chance of dropping this item, same as chance in the first option. + the "Option Drop Group" parameter refers to an entry on option_drop_group database file. The specified + entry will be used when this item is dropped in order to add random options to the dropped equipment. + + A monster drop list may use both format for different items. + Required Format: + Drops: { + AegisName: chance + // or + AegisName: (chance, "Option Drop Group") + } + + When not specified, becomes false (no drops). Drops: Sets monster drops list. - Accepted values are AegisName as defined on item_db.conf and a chance. + There are two ways to define a drop: + 1) The first one is used for simple drops and uses the item AegisName + as defined on item_db.conf and a chance. + Format: + AegisName: chance Chance is an integer from 1 to 10000 (10000 = 100%). - Required format: - Drops: { - AegisName: chance - // ... - } - When not specified, becomes false. + + 2) The second way to define a drop allows setting a random option drop + group to be used by this drop. + Format: + AegisName: (chance, "Option Drop Group") + + The item drop chance refers to the chance of dropping this item, same as chance in the first option. + the "Option Drop Group" parameter refers to an entry on option_drop_group database file. The specified + entry will be used when this item is dropped in order to add random options to the dropped equipment. + + A monster drop list may use both format for different items. + Required Format: + Drops: { + AegisName: chance + // or + AegisName: (chance, "Option Drop Group") + } + + When not specified, becomes false (no drops). diff --git a/doc/option_drop_group.md b/doc/option_drop_group.md new file mode 100644 index 000000000..325cf9fe2 --- /dev/null +++ b/doc/option_drop_group.md @@ -0,0 +1,97 @@ +# Option Drop Group Database + +## Description +Explanation of the `db/option_drop_groups.conf` file and structure. + +This database file allows the creation of groups of random options +that will be added to certain equipments when dropped. After creating +a group in this database file, you may set up drops in `mob_db` to use +it in order to get items with these options. For more information on +adding option drop groups to `mob_db`, check `doc/mob_db.txt` documentation file. + +Each item may have up to `MAX_ITEM_OPTION` options at the same time, +in this document, each of these independent options will be called +`option slot`. One drop group will define the possibilities of random +options for each of these slots. + +## Entries Format + +``` +: ( + { // Option Slot 1 + Rate: (int) chance of filling option slot 1 (100 = 1%) + + // Possible options for slot 1 + // min/max value : int, defaults to 0 + // chance : int, 100 = 1% if not set, will be 100%/number of possibiltiies + OptionName: value + // or + OptionName: [min value, max value] + // or + OptionName: [min value, max value, chance] + // ... (as many as you want) + }, + // ... (up to MAX_ITEM_OPTION) +), +``` + +### `Group Name Constant` +This is the group name, it is how this group is referenced in other files +(e.g. mob_db). It must be globally unique, as it is a server constant, and +must contain only letters, numbers and " _ ". + +### `Rate` +This is the chance of this option slot to drop. In other words, this is the +chance of getting this slot filled with something, where something is given +by the list of `OptionName` that follows. + +Rate is an integer value where 100 means 1%. + +### `OptionName` +Adds `OptionName` as one option that may fill this slot when it drops. + +The details of this option may be specified in one of 3 ways: + +#### `OptionName: value` +The chance of this option being picked is auto calculated (see below), +and if this option is chosen, its value will be `value`. + +#### `OptionName: [min, max]` +The chance of this option being picked is auto calculated (see below), +and if this option is chosen, its value will be a random integer between +`min` and `max` (both included). + +#### `OptionName: [min, max, chance]` +The chance of this option being picked is `chance`, and if this option is chosen, +its value will be a random integer between `min` and `max` (both included). + +#### Auto calculated chances +When chance is not specified in an option, it will be auto calculated by +the server as being `100%/num`, when `num` is the number of possibilities +in this option slot. + +For example, if you specify 3 possible options, all of them without +a `chance` defined, all of them will have 33.33% chance of being +picked (100%/3). If you set the chance of one of them to 50%, you +will have one option with 50% chance, and each of the others with +33.33% chance. + +## Example +``` +MYITEM: ( + { // Option Slot 1 + Rate: 10000 // It has 100% of chance of being filled + + // This slot may have one of the following options: + WEAPON_ATTR_WIND: 5, // WEAPON_ATTR_WIND Lv5 (33.33%) + WEAPON_ATTR_GROUND: [2, 4] // WEAPON_ATTR_GROUND Lv 2~4 (33.33%) + WEAPON_ATTR_POISON: [1, 4, 8000] // WEAPON_ATTR_POISON Lv 1~4 (80%) + }, + { // Option Slot 2 + Rate: 5000 // It has 50% of chance of being filled + + // If filled, may have one of the following options: + WEAPON_ATTR_WATER: 4 // WEAPON_ATTR_WATER Lv4 (100%) + } +) +``` diff --git a/src/map/battle.c b/src/map/battle.c index fe7a64b51..ba7c1130d 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -7417,6 +7417,7 @@ static const struct battle_data { { "features/enable_achievement_system", &battle_config.feature_enable_achievement, 1, 0, 1, }, { "ping_timer_inverval", &battle_config.ping_timer_interval, 30, 0, 99999999, }, { "ping_time", &battle_config.ping_time, 20, 0, 99999999, }, + { "option_drop_max_loop", &battle_config.option_drop_max_loop, 10, 1, 100000, }, }; static bool battle_set_value_sub(int index, int value) diff --git a/src/map/battle.h b/src/map/battle.h index 723a86874..a99e95c86 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -583,6 +583,8 @@ struct Battle_Config { int ping_timer_interval; int ping_time; + + int option_drop_max_loop; }; /* criteria for battle_config.idletime_critera */ diff --git a/src/map/mob.c b/src/map/mob.c index fed4d6c60..a72e3bc42 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -1900,15 +1900,53 @@ static int mob_ai_hard(int tid, int64 tick, int id, intptr_t data) return 0; } +/** + * Adds random options of a given options drop group into item. + * + * @param item : item receiving random options + * @param options : Random Option Drop Group to be used + */ +static void mob_setdropitem_options(struct item *item, struct optdrop_group *options) +{ + nullpo_retv(item); + nullpo_retv(options); + + for (int i = 0; i < options->optslot_count; i++) { + if (rnd() % 10000 >= options->optslot_rate[i]) + continue; + + // count avoids a too long loop that would cause lag. + // if after option_drop_max_loop full iterations (running through all possibilities) + // it still fails to pick one, it'll stop at one random index in the next iteration + int count = battle_config.option_drop_max_loop * options->optslot[i].option_count + (rnd() % options->optslot[i].option_count); + int idx = 0; + while (count > 0 && rnd() % 10000 >= options->optslot[i].options[idx].rate) { + idx = (idx + 1) % options->optslot[i].option_count; + --count; + } + + item->option[i].index = options->optslot[i].options[idx].id; + + int min = options->optslot[i].options[idx].min; + int max = options->optslot[i].options[idx].max; + item->option[i].value = min + (rnd() % (max - min + 1)); + } +} + /*========================================== * Initializes the delay drop structure for mob-dropped items. *------------------------------------------*/ -static struct item_drop *mob_setdropitem(int nameid, int qty, struct item_data *data) +static struct item_drop *mob_setdropitem(int nameid, struct optdrop_group *options, int qty, struct item_data *data) { struct item_drop *drop = ers_alloc(item_drop_ers, struct item_drop); drop->item_data.nameid = nameid; drop->item_data.amount = qty; drop->item_data.identify = data ? itemdb->isidentified2(data) : itemdb->isidentified(nameid); + + // Set item options [KirieZ] + if (options != NULL) + mob->setdropitem_options(&drop->item_data, options); + drop->showdropeffect = true; drop->next = NULL; return drop; @@ -2521,7 +2559,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) continue; } - ditem = mob->setdropitem(md->db->dropitem[i].nameid, 1, it); + ditem = mob->setdropitem(md->db->dropitem[i].nameid, md->db->dropitem[i].options, 1, it); // Official Drop Announce [Jedzkie] if (mvp_sd != NULL) { @@ -2538,7 +2576,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) // Ore Discovery [Celest] if (sd == mvp_sd && pc->checkskill(sd,BS_FINDINGORE) > 0) { if( (temp = itemdb->chain_item(itemdb->chain_cache[ECC_ORE],&i)) ) { - ditem = mob->setdropitem(temp, 1, NULL); + ditem = mob->setdropitem(temp, NULL, 1, NULL); mob->item_drop(md, dlist, ditem, 0, i, homkillonly); } } @@ -2570,7 +2608,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) continue; itemid = (!sd->add_drop[i].is_group) ? sd->add_drop[i].id : itemdb->chain_item(sd->add_drop[i].id, &drop_rate); if( itemid ) - mob->item_drop(md, dlist, mob->setdropitem(itemid,1,NULL), 0, drop_rate, homkillonly); + mob->item_drop(md, dlist, mob->setdropitem(itemid, NULL, 1, NULL), 0, drop_rate, homkillonly); } } @@ -2632,6 +2670,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) struct { int nameid; int p; + struct optdrop_group *options; } mdrop[MAX_MVP_DROP] = { { 0 } }; for (i = 0; i < MAX_MVP_DROP; i++) { @@ -2644,6 +2683,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) mdrop[rpos].nameid = md->db->mvpitem[i].nameid; mdrop[rpos].p = md->db->mvpitem[i].p; + mdrop[rpos].options = md->db->mvpitem[i].options; } for (i = 0; i < MAX_MVP_DROP; i++) { @@ -2663,6 +2703,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) item.nameid = mdrop[i].nameid; item.identify = itemdb->isidentified2(data); + mob->setdropitem_options(&item, mdrop[i].options); clif->mvp_item(mvp_sd, item.nameid); log_mvp[0] = item.nameid; @@ -3860,6 +3901,212 @@ static inline int mob_parse_dbrow_cap_value(int class_, int min, int max, int va return value; } +/** + * Reads one possible option for a option slot in a option drop group + * @param option : Libconfig entry + * @param entry : memory db entry for current slot + * @param idx : index of entry where this option should be inserted at + * @param calc_rate : if rates should be recalculated after reading all entries + * @param slot : option group slot being read (for messages) + * @param group : option group being read (for messages) + * @return true if it successfully read the entry, false otherwise + */ +static bool mob_read_optdrops_option(struct config_setting_t *option, struct optdrop_group_optslot *entry, int *idx, bool *calc_rate, int slot, const char *group) +{ + nullpo_retr(false, option); + nullpo_retr(false, entry); + nullpo_retr(false, idx); + nullpo_retr(false, calc_rate); + nullpo_retr(false, group); + + const char *name = config_setting_name(option); + int opt_id; + + if (strncmp(name, "Rate", 4) == 0) + return true; + + if (script->get_constant(name, &opt_id) == false) { + ShowWarning("mob_read_optdrops_option: Invalid option \"%s\" for option slot %d of %s group, skipping.\n", name, slot, group); + return false; + } + + int min = 0, max = 0, opt_rate = 0; + if (config_setting_is_number(option)) { + // OptionName: value + min = libconfig->setting_get_int(option); + } else if (config_setting_is_array(option)) { + // OptionName: [min, max] + // OptionName: [min, max, rate] + int slen = libconfig->setting_length(option); + + if (slen >= 2) { + // [min, max,...] + min = libconfig->setting_get_int_elem(option, 0); + max = libconfig->setting_get_int_elem(option, 1); + } + + if (slen == 3) { + // [min, max, rate] + opt_rate = libconfig->setting_get_int_elem(option, 2); + } + } else { + ShowWarning("mob_read_optdrops_option: Invalid value \"%s\" for option slot %d of %s group, skipping.\n", name, slot, group); + return false; + } + + if (max < min) + max = min; + + entry->options[*idx].id = opt_id; + entry->options[*idx].min = min; + entry->options[*idx].max = max; + entry->options[*idx].rate = opt_rate; + + if (entry->options[*idx].rate == 0) + *calc_rate = true; + + (*idx)++; + + return true; +} + +/** + * Reads the settings for one random option slot of a random option drop group. + * @param optslot : The slot entry from config file + * @param n : slot index + * @param group_id : Group index + * @param group : group name (used in messages) + * @return true if it succesfully read, false otherwise + */ +static bool mob_read_optdrops_optslot(struct config_setting_t *optslot, int n, int group_id, const char *group) +{ + nullpo_retr(false, optslot); + nullpo_retr(false, group); + Assert_retr(false, group_id >= 0 && group_id < mob->opt_drop_groups_count); + Assert_retr(false, n >= 0 && n < MAX_ITEM_OPTIONS); + + // Structure: + // { + // Rate: chance of option 1 (int) + // OptionName1: value + // OptionName2: [min, max] + // OptionName3: [min, max, rate] + // .... + // } + + int drop_rate; // The rate for this option to be dropped (Rate field) + if (libconfig->setting_lookup_int(optslot, "Rate", &drop_rate) == CONFIG_FALSE) { + ShowWarning("mob_read_optdrops_optslot: Missing option %d rate in group %s, skipping.\n", n, group); + return false; + } + + int count = libconfig->setting_length(optslot); + if (count <= 1) { // 1 = Rate + ShowWarning("mob_read_optdrops_optslot: Option %d of %s group doesn't contain any possible options, skipping.\n", n, group); + return false; + } + + struct optdrop_group_optslot *entry = &(mob->opt_drop_groups[group_id].optslot[n]); + entry->options = aCalloc(sizeof(struct optdrop_group_option), count); + + int idx = 0; + int i = 0; + struct config_setting_t *opt = NULL; + bool calc_rate = false; + while (i < count && (opt = libconfig->setting_get_elem(optslot, i)) != NULL) { + ++i; + mob->read_optdrops_option(opt, entry, &idx, &calc_rate, n, group); + } + entry->option_count = idx; + mob->opt_drop_groups[group_id].optslot_count++; + mob->opt_drop_groups[group_id].optslot_rate[n] = drop_rate; + + // If there're empty rates, calculate them + if (calc_rate == true) { + for (int j = 0; j < idx; ++j) { + if (entry->options[j].rate == 0) + entry->options[j].rate = 10000 / idx; + } + } + + return true; +} + +/** + * Reads one random option drop group. + * @param group : Drop Group entry from config file + * @param n : group index + * @return true if it successfuly read, false otherwise + */ +static bool mob_read_optdrops_group(struct config_setting_t *group, int n) +{ + /* Structure: + : ( + {