Wave mode instances - walkthrough conversions (#3884)

* Wave mode instances - walkthrough conversions

* NPC_EMOTION and NPC_EMOTION_ON disrupted the walking system and have been restricted on the maps.
* Implemented AI_SPECIALs. AI and normal monsters can naturally fight each others. However monsters with AI_SPECIAL can't be hit by normal monsters.

* Implemented mob_setidleevent command.
`mob_setidleevent <GID>,<event>;`
This command will attach an event label to the monster with the given <GID> which will execute when the <GID> is idle.

* Added parameters to unitskilluseid and unitskillusepos
-- `<cancel>`: define if the skill can be interrupted when hit (by default the cancel value was 'castcancel' from skill_db.txt)
-- `<Line_ID>` : the monster will say the message from 'Line_ID' in mob_chat_db.yml when casting the skill

* Added `UMOB_IGNORE_CELL_STACK_LIMIT` for setunitdata/getunitdata script command.
When true, the monster will ignore the stack limit (max number of characters that can stack within a single cell) defined by 'official_cell_stack_limit' in misc.conf

* The script is disabled by default like on KRO

Thanks to @Lemongrass3110 @aleos89 @Badarosk0 @sigtus @Questune09 !
This commit is contained in:
Atemo
2021-10-26 14:56:47 +02:00
committed by GitHub
parent eda43a6295
commit 3abc86e02d
18 changed files with 2195 additions and 225 deletions

View File

@@ -1516,6 +1516,10 @@ int mob_unlocktarget(struct mob_data *md, t_tick tick)
//Because it is not unset when the mob finishes walking.
md->state.skillstate = MSS_IDLE;
case MSS_IDLE:
if( md->ud.walktimer == INVALID_TIMER && md->idle_event[0] && npc_event_do_id( md->idle_event, md->bl.id ) > 0 ){
md->idle_event[0] = 0;
break;
}
// Idle skill.
if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mobskill_use(md, tick, -1))
break;
@@ -1543,7 +1547,8 @@ int mob_unlocktarget(struct mob_data *md, t_tick tick)
md->ud.target_to = 0;
unit_set_target(&md->ud, 0);
}
if (battle_config.official_cell_stack_limit > 0
if (!md->ud.state.ignore_cell_stack_limit && battle_config.official_cell_stack_limit > 0
&& (md->min_chase == md->db->range3 || battle_config.mob_ai & 0x8)
&& map_count_oncell(md->bl.m, md->bl.x, md->bl.y, BL_CHAR | BL_NPC, 1) > battle_config.official_cell_stack_limit) {
unit_walktoxy(&md->bl, md->bl.x, md->bl.y, 8);
@@ -2045,6 +2050,15 @@ static int mob_ai_sub_lazy(struct mob_data *md, va_list args)
return 0;
}
if (md->ud.walktimer == INVALID_TIMER) {
// Because it is not unset when the mob finishes walking.
md->state.skillstate = MSS_IDLE;
if (md->idle_event[0] && npc_event_do_id( md->idle_event, md->bl.id ) > 0) {
md->idle_event[0] = 0;
return 0;
}
}
if( DIFF_TICK(md->next_walktime,tick) < 0 && status_has_mode(&md->status,MD_CANMOVE) && unit_can_move(&md->bl) )
{
// Move probability for mobs away from players
@@ -2056,9 +2070,6 @@ static int mob_ai_sub_lazy(struct mob_data *md, va_list args)
}
else if( md->ud.walktimer == INVALID_TIMER )
{
//Because it is not unset when the mob finishes walking.
md->state.skillstate = MSS_IDLE;
// Probability for mobs far from players from doing their IDLE skill.
// In Aegis, this is 100% for mobs that have been activated by players and none otherwise.
if( mob_is_spotted(md) &&
@@ -3657,6 +3668,24 @@ struct mob_data *mob_getfriendstatus(struct mob_data *md,int cond1,int cond2)
return fr;
}
// Display message from mob_chat_db.yml
bool mob_chat_display_message(mob_data &md, uint16 msg_id) {
std::shared_ptr<s_mob_chat> mc = mob_chat_db.find(msg_id);
if (mc != nullptr) {
std::string name = md.name, output;
std::size_t unique = name.find("#");
if (unique != std::string::npos)
name = name.substr(0, unique); // discard extra name identifier if present [Daegaladh]
output = name + " : " + mc->msg;
clif_messagecolor(&md.bl, mc->color, output.c_str(), true, AREA_CHAT_WOC);
return true;
}
return false;
}
/*==========================================
* Skill use judging
*------------------------------------------*/
@@ -3856,18 +3885,7 @@ int mobskill_use(struct mob_data *md, t_tick tick, int event)
}
//Skill used. Post-setups...
if ( ms[i]->msg_id ){ //Display color message [SnakeDrak]
std::shared_ptr<s_mob_chat> mc = mob_chat_db.find(ms[i]->msg_id);
if (mc) {
std::string name = md->name, output;
std::size_t unique = name.find("#");
if (unique != std::string::npos)
name = name.substr(0, unique); // discard extra name identifier if present [Daegaladh]
output = name + " : " + mc->msg;
clif_messagecolor(&md->bl, mc->color, output.c_str(), true, AREA_CHAT_WOC);
}
mob_chat_display_message(*md, ms[i]->msg_id);
}
if(!(battle_config.mob_ai&0x200)) { //pass on delay to same skill.
for (j = 0; j < ms.size(); j++)
@@ -4446,7 +4464,7 @@ uint64 MobDatabase::parseBodyNode(const YAML::Node &node) {
mob->status.rhw.atk2 = 0;
#endif
}
if (this->nodeExists(node, "Defense")) {
uint16 def;
@@ -4463,7 +4481,7 @@ uint64 MobDatabase::parseBodyNode(const YAML::Node &node) {
if (!exists)
mob->status.def = 0;
}
if (this->nodeExists(node, "MagicDefense")) {
uint16 def;
@@ -4480,7 +4498,7 @@ uint64 MobDatabase::parseBodyNode(const YAML::Node &node) {
if (!exists)
mob->status.mdef = 0;
}
if (this->nodeExists(node, "Str")) {
uint16 stat;
@@ -4552,7 +4570,7 @@ uint64 MobDatabase::parseBodyNode(const YAML::Node &node) {
if (!exists)
mob->status.luk = 1;
}
if (this->nodeExists(node, "AttackRange")) {
uint16 range;