From 93595818978b50428c311d5ba20f2b8f102b6a4b Mon Sep 17 00:00:00 2001 From: gumi Date: Fri, 23 Aug 2019 14:06:56 -0400 Subject: allow to summon multiple mobs with @summon --- src/map/atcommand.cpp | 84 +++++++++++++++++++++++++++++++++++++++------------ src/map/battle.cpp | 4 +-- src/map/map.hpp | 3 +- src/map/mob.cpp | 29 ++++++++++-------- src/map/skill.cpp | 2 +- 5 files changed, 86 insertions(+), 36 deletions(-) diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index a9774f0..868daa6 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -4523,37 +4523,81 @@ ATCE atcommand_leaves(Session *, dumb_ptr sd, } static -ATCE atcommand_summon(Session *, dumb_ptr sd, +ATCE atcommand_summon(Session *s, dumb_ptr sd, ZString message) { MobName name; Species mob_id; - int x = 0; - int y = 0; - tick_t tick = gettick(); + int number = 0; + int minutes = 0; - if (!extract(message, &name) || !name) + if (!extract(message, record<' ', 1>(&name, &number, &minutes))) return ATCE::USAGE; - if ((mob_id = wrap(static_cast(atoi(name.c_str())))) == Species()) - mob_id = mobdb_searchname(name); + if ((mob_id = mobdb_searchname(name)) == Species()) + mob_id = mobdb_checkid(wrap(atoi(name.c_str()))); + if (mob_id == Species()) return ATCE::EXIST; - x = sd->bl_x + random_::in(-5, 4); - y = sd->bl_y + random_::in(-5, 4); + if (number <= 0) + number = 1; + + if (minutes <= 0) + minutes = 10; + + if (battle_config.atcommand_spawn_quantity_limit >= 1 + && number > battle_config.atcommand_spawn_quantity_limit) + number = battle_config.atcommand_spawn_quantity_limit; + + int count = 0; + int range = (sqrt(number) / 2) * 2 + 5; + + for (int i = 0; i < number; i++) + { + int j = 0; + BlockId k; + + while (j++ < 8 && !k) + { + // try 8 times to spawn the monster (needed for close area) + int mx = sd->bl_x + random_::in(-range / 2, range / 2 ); + int my = sd->bl_y + random_::in(-range / 2, range / 2); + k = mob_once_spawn(sd, MOB_THIS_MAP, mx, my, MobName(), mob_id, 1, NpcEvent()); + } + + if (k) + { + dumb_ptr md = map_id_is_mob(k); + tick_t tick = gettick(); + + md->master_id = sd->bl_id; + md->state.special_mob_ai = 1; + md->mode = get_mob_db(md->mob_class).mode | MobMode::AGGRESSIVE; + md->deletetimer = Timer(tick + (std::max(minutes, 120) * 1_min), + std::bind(mob_timer_delete, ph::_1, ph::_2, + k)); + count++; + } + } - BlockId id = mob_once_spawn(sd, MOB_THIS_MAP, x, y, JAPANESE_NAME, mob_id, 1, NpcEvent()); - dumb_ptr md = map_id_is_mob(id); - if (md) + if (count != 0) { - md->master_id = sd->bl_id; - md->state.special_mob_ai = 1; - md->mode = get_mob_db(md->mob_class).mode | MobMode::AGGRESSIVE; - md->deletetimer = Timer(tick + 1_min, - std::bind(mob_timer_delete, ph::_1, ph::_2, - id)); - clif_misceffect(md, 344); + if (number == count) + { + clif_displaymessage(s, "All monster summoned!"_s); + } + else + { + AString output = STRPRINTF("%d monster(s) summoned!"_fmt, + count); + clif_displaymessage(s, output); + } + } + else + { + clif_displaymessage(s, "Invalid monster ID or name."_s); + return ATCE::EXIST; } return ATCE::OKAY; @@ -5548,7 +5592,7 @@ Map atcommand_info = {"leaves"_s, {""_s, 98, atcommand_leaves, "Enable the leaves mapflag"_s}}, - {"summon"_s, {""_s, + {"summon"_s, {" [count] [minutes]"_s, 50, atcommand_summon, "Summon a slave monster temporarily"_s}}, {"adjgmlvl"_s, {" "_s, diff --git a/src/map/battle.cpp b/src/map/battle.cpp index df01a5c..a4596e9 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -2070,9 +2070,9 @@ int battle_check_target(dumb_ptr src, dumb_ptr target, if (src->bl_type == BL::MOB) { dumb_ptr md = src->is_mob(); - if (md && md->master_id) + if (md && (md->master_id || md->parent_id)) { - if (md->master_id == target->bl_id) // 主なら肯定 + if (md->master_id == target->bl_id || md->parent_id == target->bl_id) // 主なら肯定 return 1; if (md->state.special_mob_ai) { diff --git a/src/map/map.hpp b/src/map/map.hpp index eddbfad..42ca151 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -482,7 +482,8 @@ struct mob_data : block_list struct mob_skill *skillidx; std::unique_ptr skilldelayup; // [MAX_MOBSKILL]; LevelElement def_ele; - BlockId master_id; + BlockId parent_id; // monster that spawned this mob + BlockId master_id; // player that owns the parent BlockId last_master_id; int master_dist; int exclusion_src, exclusion_party; diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 0da946a..1614fe5 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -1152,6 +1152,7 @@ int mob_spawn(BlockId id) md->stats[mob_stat::SPEED] = get_mob_db(md->mob_class).speed.count(); md->def_ele = get_mob_db(md->mob_class).element; md->master_id = BlockId(); + md->parent_id = BlockId(); md->master_dist = 0; md->state.state = MS::IDLE; @@ -1573,13 +1574,13 @@ int mob_ai_sub_hard_slavemob(dumb_ptr md, tick_t tick) nullpo_retz(md); - if ((bl = map_id2bl(md->master_id)) != nullptr) + if ((bl = map_id2bl(md->parent_id)) != nullptr) mmd = bl->is_mob(); mode = get_mob_db(md->mob_class).mode; // It is not main monster/leader. - if (!mmd || mmd->bl_type != BL::MOB || mmd->bl_id != md->master_id) + if (!mmd || mmd->bl_type != BL::MOB || mmd->bl_id != md->parent_id) return 0; // Since it is in the map on which the master is not, teleport is carried out and it pursues. @@ -1861,6 +1862,7 @@ void mob_ai_sub_hard(dumb_ptr bl, tick_t tick) if (((bl = map_id2bl(md->last_master_id)) != nullptr && md->bl_m != bl->bl_m) || (bl = map_id2bl(md->last_master_id)) == nullptr) { md->last_master_id = BlockId(); + md->parent_id = BlockId(); md->state.special_mob_ai = 0; md->mode = get_mob_db(md->mob_class).mode; md->target_id = BlockId(); @@ -1868,7 +1870,7 @@ void mob_ai_sub_hard(dumb_ptr bl, tick_t tick) } } // Processing of slave monster - if (md->master_id && md->state.special_mob_ai == 0) + if (md->parent_id && md->state.special_mob_ai == 0) mob_ai_sub_hard_slavemob(md, tick); // アクティヴモンスターの策敵 (?? of a bitter taste TIVU monster) @@ -2071,7 +2073,7 @@ void mob_ai_sub_hard(dumb_ptr bl, tick_t tick) // mobs that are not slaves can random-walk if (bool(mode & MobMode::CAN_MOVE) && mob_can_move(md) - && (!md->master_id || md->state.special_mob_ai + && (!md->parent_id || md->state.special_mob_ai || md->master_dist > 10)) { // if walktime is more than 7 seconds in the future, @@ -2162,7 +2164,7 @@ void mob_ai_sub_lazy(dumb_ptr bl, tick_t tick) // MOB which is not not the summons MOB but BOSS, either sometimes reboils. else if (random_::chance(MOB_LAZYWARPPERC) && md->spawn.x0 <= 0 - && md->master_id + && md->parent_id && !bool(get_mob_db(md->mob_class).mode & MobMode::BOSS)) mob_spawn(md->bl_id); @@ -2174,7 +2176,7 @@ void mob_ai_sub_lazy(dumb_ptr bl, tick_t tick) // MOB which is not BOSS which is not Summons MOB, either -- a case -- sometimes -- leaping if (random_::chance(MOB_LAZYWARPPERC) && md->spawn.x0 <= 0 - && md->master_id + && md->parent_id && !bool(get_mob_db(md->mob_class).mode & MobMode::BOSS)) mob_warp(md, nullptr, -1, -1, BeingRemoveWhy::NEGATIVE1); } @@ -2334,7 +2336,7 @@ void mob_deleteslave_sub(dumb_ptr bl, BlockId id) nullpo_retv(bl); md = bl->is_mob(); - if (md->master_id && md->master_id == id) + if (md->parent_id && md->parent_id == id) mob_damage(nullptr, md, md->hp, 1); } @@ -2381,6 +2383,7 @@ int mob_damage(dumb_ptr src, dumb_ptr md, int damage, { /* If the master hits a monster, have the monster turn against him */ md->last_master_id = md->master_id; + //md->parent_id = BlockId(); md->master_id = BlockId(); md->mode = MobMode::war; /* Regular war mode */ md->target_id = src->bl_id; @@ -2757,7 +2760,7 @@ void mob_warpslave_sub(dumb_ptr bl, BlockId id, int x, int y) { dumb_ptr md = bl->is_mob(); - if (md->master_id == id) + if (md->parent_id == id) { mob_warp(md, nullptr, x, y, BeingRemoveWhy::QUIT); } @@ -2873,7 +2876,7 @@ void mob_countslave_sub(dumb_ptr bl, BlockId id, int *c) nullpo_retv(bl); md = bl->is_mob(); - if (md->master_id == id) + if (md->parent_id == id) (*c)++; } @@ -2969,8 +2972,10 @@ int mob_summonslave(dumb_ptr md2, int *value_, int amount, int flag) map_addiddb(md); mob_spawn(md->bl_id); - if (flag) - md->master_id = md2->bl_id; + if (flag) { + md->master_id = md2->master_id; // tell the subslave who actually owns them + md->parent_id = md2->bl_id; // tell the subslave who spawned them + } } } return 0; @@ -3292,7 +3297,7 @@ int mobskill_use(dumb_ptr md, tick_t tick, if (battle_config.mob_skill_use == 0 || md->skilltimer) return 0; - if (md->state.special_mob_ai) + if (md->state.special_mob_ai && md->parent_id) return 0; for (mob_skill& msii : ms) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index d9a7717..789dd8a 100644 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -532,7 +532,7 @@ int skill_castend_nodamage_id(dumb_ptr src, dumb_ptr bl, switch (skillid) { case SkillID::NPC_SUMMONSLAVE: - if (md && !md->master_id) + if (md && !md->parent_id) { mob_summonslave(md, md->skillidx->val, -- cgit v1.2.3-60-g2f50