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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 2195 additions and 225 deletions

View File

@ -107,11 +107,13 @@ Body:
- Id: 37
Dialog: Is there anyone waiting for you outside of here? Throw them all away, you are mine now...
- Id: 38
Dialog: Discard your life and stay confined here. You will yearn for freedom in captivity !!
Dialog: Discard your life and stay confined here. You will yearn for freedom in captivity !!
- Id: 39
Dialog: How much will the outside world change if you stay here in solitude for one thousand years?
- Id: 40
Dialog: Yes! Yearn for your freedom from this confined place, your captivity here will be permanent !!
- Id: 41
Dialog: Arrival!
Footer:
Imports:

View File

@ -198,18 +198,18 @@ Body:
Map: 1@glast
X: 367
Y: 304
# - Id: 22
# Name: Wave Mode - Forest
# Enter:
# Map: 1@def01
# X: 50
# Y: 21
# - Id: 23
# Name: Wave Mode - Sky
# Enter:
# Map: 1@def02
# X: 29
# Y: 35
- Id: 22
Name: Wave Mode - Forest
Enter:
Map: 1@def01
X: 50
Y: 21
- Id: 23
Name: Wave Mode - Sky
Enter:
Map: 1@def02
X: 29
Y: 35
- Id: 24
Name: Nightmarish Jitterbug
Enter:

View File

@ -19,6 +19,7 @@
// 1024 - restricted in zone 6
// 2048 - restricted in zone 7
// 4096 - restricted in zone 8
// 8192 - restricted in zone 9
//
// Passing negative value as flag will unset the flag instead.
//
@ -240,45 +241,45 @@
//----------------------------------------------------------------------------
// WoE:TE Items - Only can be used in WoE:TE Castles (All except &16)
//----------------------------------------------------------------------------
1299,8175 // TE_Woe_Katar
1319,8175 // TE_Woe_Axe
1399,8175 // TE_Woe_Two_Handed_Axe
1437,8175 // TE_Woe_Pike
1495,8175 // TE_Woe_Lance
1591,8175 // TE_Woe_Book
1667,8175 // TE_Woe_Staff
1834,8175 // TE_Woe_Fist
1932,8175 // TE_Woe_Guitar
1987,8175 // TE_Woe_Rope
2019,8175 // TE_Woe_Two_Hand_Staff
2178,8175 // TE_Woe_Buckler
2179,8175 // TE_Woe_Shield
2180,8175 // TE_Woe_Magic_Guard
2496,8175 // TE_Woe_Shoes
2497,8175 // TE_Woe_Boots
2498,8175 // TE_Woe_Magic_Sandal
2944,8175 // TE_Ring_Of_Protection
2945,8175 // TE_Ring_Of_Rage
2946,8175 // TE_Ring_Of_Defiance
11557,8175 // TE_White_Potion
11558,8175 // TE_White_Slim_Potion
13083,8175 // TE_Woe_Knife
13117,8175 // TE_Woe_Pistol
13184,8175 // TE_Woe_Rifle
13185,8175 // TE_Woe_Gatling
13186,8175 // TE_Woe_Shotgun
13187,8175 // TE_Woe_Grenade
13317,8175 // TE_Woe_Huuma
13439,8175 // TE_Woe_Sword
15062,8175 // TE_Woe_Coat
15063,8175 // TE_Woe_Chain_Mail
15064,8175 // TE_Woe_Mage_Coat
16025,8175 // TE_Woe_Mace
18118,8175 // TE_Woe_Bow
18732,8175 // TE_Woe_Cap
18733,8175 // TE_Woe_Bone_Helm
18734,8175 // TE_Woe_Magic_Eyes
20702,8175 // TE_Woe_Muffler
20703,8175 // TE_Woe_Manteau
20704,8175 // TE_Woe_Magic_Manteau
21006,8175 // TE_Woe_Two_Hand_Sword
1299,16367 // TE_Woe_Katar
1319,16367 // TE_Woe_Axe
1399,16367 // TE_Woe_Two_Handed_Axe
1437,16367 // TE_Woe_Pike
1495,16367 // TE_Woe_Lance
1591,16367 // TE_Woe_Book
1667,16367 // TE_Woe_Staff
1834,16367 // TE_Woe_Fist
1932,16367 // TE_Woe_Guitar
1987,16367 // TE_Woe_Rope
2019,16367 // TE_Woe_Two_Hand_Staff
2178,16367 // TE_Woe_Buckler
2179,16367 // TE_Woe_Shield
2180,16367 // TE_Woe_Magic_Guard
2496,16367 // TE_Woe_Shoes
2497,16367 // TE_Woe_Boots
2498,16367 // TE_Woe_Magic_Sandal
2944,16367 // TE_Ring_Of_Protection
2945,16367 // TE_Ring_Of_Rage
2946,16367 // TE_Ring_Of_Defiance
11557,16367 // TE_White_Potion
11558,16367 // TE_White_Slim_Potion
13083,16367 // TE_Woe_Knife
13117,16367 // TE_Woe_Pistol
13184,16367 // TE_Woe_Rifle
13185,16367 // TE_Woe_Gatling
13186,16367 // TE_Woe_Shotgun
13187,16367 // TE_Woe_Grenade
13317,16367 // TE_Woe_Huuma
13439,16367 // TE_Woe_Sword
15062,16367 // TE_Woe_Coat
15063,16367 // TE_Woe_Chain_Mail
15064,16367 // TE_Woe_Mage_Coat
16025,16367 // TE_Woe_Mace
18118,16367 // TE_Woe_Bow
18732,16367 // TE_Woe_Cap
18733,16367 // TE_Woe_Bone_Helm
18734,16367 // TE_Woe_Magic_Eyes
20702,16367 // TE_Woe_Muffler
20703,16367 // TE_Woe_Manteau
20704,16367 // TE_Woe_Magic_Manteau
21006,16367 // TE_Woe_Two_Hand_Sword

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
// 1024 - cannot be used in zone 6 maps
// 2048 - cannot be used in zone 7 maps
// 4096 - cannot be used in zone 8 maps
// 8192 - cannot be used in zone 9 maps
//
// Example:
// 8,6 // Endure cannot be used in PvP and GvG maps (2+4)
@ -229,3 +230,17 @@
409,4096 // WE_CALLPARENT
410,4096 // WE_CALLBABY
5063,4096 //WE_CALLALLFAMILY
//----------------------------------------------------------------------------
// Zone 9 - Wave Mode
//----------------------------------------------------------------------------
26,8192 //AL_TELEPORT
87,8192 //WZ_ICEWALL
197,8192 //NPC_EMOTION
219,8192 //RG_INTIMIDATE
405,8192 //PF_SPIDERWEB
474,8192 //NPC_EMOTION_ON
674,8192 //NPC_EXPULSION
2284,8192 //SC_FATALMENACE
2294,8192 //SC_IGNORANCE
2300,8192 //SC_DIMENSIONDOOR

View File

@ -143,7 +143,7 @@ to move away from their specified spawn region.
Monster name is the name the monsters will have on screen, and has no relation
whatsoever to their names anywhere else. It's the mob id that counts, which
identifies monster record in 'mob_db.txt' database of monsters. If the mob name
identifies monster record in 'mob_db.yml' database of monsters. If the mob name
is given as "--ja--", the 'japanese name' field from the monster database is
used, (which, in rAthena, actually contains an English name) if it's "--en--",
it's the 'english name' from the monster database (which contains an uppercase
@ -168,6 +168,7 @@ player who triggers the script must be within 'trigger' range for the event to
work.
There are two optional fields for monster size and AI.
Natural enemies for AI monsters are normal monsters.
<mob size> can be:
Size_Small (0)
@ -182,6 +183,7 @@ There are two optional fields for monster size and AI.
AI_ZANZOU (4) (Kagerou/Oboro skill)
AI_LEGION (5) (Sera skill)
AI_FAW (6) (Mechanic skill)
AI_WAVEMODE (7) Normal monsters will ignore attack from AI_WAVEMODE monsters
Alternately, a monster spawned using 'boss_monster' instead of 'monster' is able
to be detected on the map with the SC_BOSSMAPINFO status (used by Convex Mirror).
@ -246,7 +248,7 @@ it's facing 5.)
Sprite ID is the sprite number or constant used to display this particular NPC.
You may also use a monster's ID instead to display a monster sprite for this NPC.
It is possible to use a job sprite as well, but you must first define it as a
monster sprite in 'mob_avail.txt', a full description on how to do this is not
monster sprite in 'mob_avail.yml', a full description on how to do this is not
in the scope of this manual.
A '-1' Sprite ID will make the NPC invisible (and unclickable).
A '111' Sprite ID will make an NPC which does not have a sprite, but is still
@ -2954,7 +2956,7 @@ recreate these items perfectly if they are destroyed. Here's what you get:
@inventorylist_option_id5[] - fifth array of random option IDs
@inventorylist_option_value5[] - fifth array of random option values
@inventorylist_option_parameter5[] - fifth array of random option parameters
@inventorylist_tradable - Returns if an item is tradable or not (Pass item_trade.txt, bound, and rental restrictions).
@inventorylist_tradable - Returns if an item is tradable or not (Pass item_db.yml, bound, and rental restrictions).
@inventorylist_favorite - Returns if an item is favorite or not
This could be handy to save/restore a character's inventory, since no other
@ -3085,7 +3087,7 @@ Examples
*mergeitem2({"<item name>"{,<char_id>}});
Merge all stackable items that separated by GUID flags
(either by flag 4 item_flag.txt or GUID in item_group).
(UniqueId in item_db or in item_group).
If no item ID/name given, all possible items in player's inventory will be merged.
---------------------------------------
@ -3435,7 +3437,7 @@ of his/her guild, or, if a guild ID is specified, of that guild.
*getcastlename("<map name>")
This function returns the name of the castle when given the map name for that
castle. The data is read from 'db/castle_db.txt'.
castle. The data is read from 'db/castle_db.yml'.
---------------------------------------
@ -4781,7 +4783,7 @@ eggs, and may hatch from either, although, I'm not sure what kind of a mess will
this really cause.
'getitem3' is advance version of 'getitem2' that also use Item Random Option as additional values.
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.txt
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.yml
<RandomValueArray> : Array variable of item random option's value.
<RandomParamArray> : Array variable of item random option's param.
@ -4823,7 +4825,7 @@ some cases cannot be traded or stored.
For a list of bound types see 'getitembound'.
'getitembound3' is advance version of 'getitembound2' that also use Item Random Option as additional values.
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.txt
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.yml
<RandomValueArray> : Array variable of item random option's value.
<RandomParamArray> : Array variable of item random option's param.
@ -4889,7 +4891,7 @@ in <time> seconds and be automatically deleted. See 'rentitem' for further detai
See 'getitem2' for an explanation of the expanded parameters.
'rentitem3' is advance version of 'rentitem2' that also use Item Random Option as additional values.
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.txt
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.yml
<RandomValueArray> : Array variable of item random option's value.
<RandomParamArray> : Array variable of item random option's param.
@ -4929,7 +4931,7 @@ further details.
See 'getitem2' for an explanation of the expanded parameters.
'makeitem3' is advance version of 'makeitem2' that also use Item Random Option as additional values.
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.txt
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.yml
<RandomValueArray> : Array variable of item random option's value.
<RandomParamArray> : Array variable of item random option's param.
@ -5015,7 +5017,7 @@ This command will remove a specified amount of items from the invoking/target ch
See 'getitem2' for an explanation of the expanded parameters.
'delitem3' is advance version of 'delitem2' that also use Item Random Option as criteria.
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.txt
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.yml
<RandomValueArray> : Array variable of item random option's value.
<RandomParamArray> : Array variable of item random option's param.
@ -5109,7 +5111,7 @@ other parameters that the invoking character has in the inventory.
See 'getitem2' for an explanation of the expanded parameters.
'countitem3' is advance version of 'countitem2' that also use Item Random Option as criteria.
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.txt
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.yml
<RandomValueArray> : Array variable of item random option's value.
<RandomParamArray> : Array variable of item random option's param.
@ -5150,7 +5152,7 @@ other parameters that the invoking character has in the inventory.
See 'getitem2' for an explanation of the expanded parameters.
'rentalcountitem3' is advance version of 'rentalcountitem2' that also use Item Random Option as criteria.
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.txt
<RandomIDArray> : Array variable of ID for item random option, see db/[pre-]re/item_randomopt_db.yml
<RandomValueArray> : Array variable of item random option's value.
<RandomParamArray> : Array variable of item random option's param.
@ -5178,7 +5180,7 @@ Example:
*groupranditem <group id>{,<sub_group>};
Returns the item_id of a random item picked from the group specified. The
different groups and their group number are specified in 'db/(pre-)re/item_group_db.txt'.
different groups and their group number are specified in 'db/(pre-)re/item_group_db.yml'.
When used in conjunction with other functions, you can get a random item. For
example, for a random pet lure:
@ -5196,7 +5198,7 @@ More info, see doc/item_group.txt.
Similar to the above example, this command allows players to obtain the specified
quantity of a random item from the group "<group id>". The different groups and
their group number are specified in db/(pre-)re/item_group_db.txt
their group number are specified in db/(pre-)re/item_group_db.yml
If 'quantity' is not defined or 0, it will uses defined amount from Item Group list.
@ -6417,6 +6419,7 @@ The variable 'killedgid' is set to the ID (unique mob game ID) of the monster ki
AI_ZANZOU (4) (Kagerou/Oboro skill)
AI_LEGION (5) (Sera skill)
AI_FAW (6) (Mechanic skill)
AI_WAVEMODE (7) Normal monsters will ignore attack from AI_WAVEMODE monsters
monster "place",60,100,"Poring",1002,1,"NPCNAME::OnLabel";
@ -6520,7 +6523,7 @@ command, simply use 1 for type. Any other number won't be recognized.
*strmobinfo(<type>,<monster id>);
This function will return information about a monster record in the database, as
per 'db/(pre-)re/mob_db.txt'. Type is the kind of information returned. Valid types are:
per 'db/(pre-)re/mob_db.yml'. Type is the kind of information returned. Valid types are:
It will return 0 if there is no such monster (or the type value is invalid),
or an empty string if you requested the monster's name.
@ -6623,6 +6626,22 @@ Examples:
---------------------------------------
*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.
Example:
monster "prontera",0,0,"Quest Poring",1002,1;
mob_setidleevent $@mobid[0], "NPC NAME::OnIdle";
end;
OnIdle:
mobchat getattachedrid(),0,0x00FF00,"I'm IDLE!";
end;
---------------------------------------
*disablenpc {"<NPC object name>"};
*enablenpc {"<NPC object name>"};
@ -7636,7 +7655,7 @@ unnerving.
Only a few NPC sprites have walking animations, and those that do, do not get
the animation invoked when moving the NPC, due to the problem in the NPC walking
code, which looks a bit silly. You might have better success by defining a job-
sprite based sprite id in 'db/mob_avail.txt' with this.
sprite based sprite id in 'db/mob_avail.yml' with this.
---------------------------------------
@ -8068,10 +8087,10 @@ flag: Specify target
---------------------------------------
*unitskilluseid <GID>,<skill id>,<skill lvl>{,<target id>,<casttime>};
*unitskilluseid <GID>,"<skill name>",<skill lvl>{,<target id>,<casttime>};
*unitskillusepos <GID>,<skill id>,<skill lvl>,<x>,<y>{,<casttime>};
*unitskillusepos <GID>,"<skill name>",<skill lvl>,<x>,<y>{,<casttime>};
*unitskilluseid <GID>,<skill id>,<skill lvl>{,<target id>,<casttime>,<cancel>,<Line_ID>};
*unitskilluseid <GID>,"<skill name>",<skill lvl>{,<target id>,<casttime>,<cancel>,<Line_ID>};
*unitskillusepos <GID>,<skill id>,<skill lvl>,<x>,<y>{,<casttime>,<cancel>,<Line_ID>};
*unitskillusepos <GID>,"<skill name>",<skill lvl>,<x>,<y>{,<casttime>,<cancel>,<Line_ID>};
This is the replacement of the older commands, these use the same values for
GID as the other unit* commands (See 'GID').
@ -8081,6 +8100,12 @@ Cast time is the amount of seconds to add or remove from the skill. Use a positi
add and negative value to subtract. Using 0 or no value will use the default skill cast time.
For the position, the x and y are given in the UnitSkillUsePos.
<cancel> defines if the cast can be interrupted when hit (true/false).
CastCancel from skill_db.yml is the default value of <cancel>.
If <Line_ID> is defined (positive number, default 0) the monster will say the message from 'Line_ID'
in mob_chat_db.yml when casting the skill.
---------------------------------------
*unitexists <GID>;
@ -8208,6 +8233,7 @@ Parameters (indexes) for monsters are:
UMOB_ROBE
UMOB_BODY2
UMOB_GROUP_ID
UMOB_IGNORE_CELL_STACK_LIMIT
-----

View File

@ -0,0 +1,808 @@
//===== rAthena Script =======================================
//= Wave Mode
//===== Description: =========================================
//= [Walkthrough Conversion]
//= Wave Mode Forest and Sky Instances
//- Officially monsters can use their skills
// (NPC_SUMMONSLAVE, at least). NPC_EMOTION and
// NPC_EMOTION_ON have been blocked since it disrupts the
// walk system of the instance on rAthena.
//- Note: The instance is currently disabled on KRO.
//===== Changelogs: ==========================================
//= 1.0 First version. [Capuche]
//============================================================
// Simple function to move the monster when idle.
// Move the monster to the next coordinates + end the script on successful; kill the monster + return on failure
// callfunc( "F_mobidle", <npc name + idle label>, <size coord>, <defined x array>, <x shift>, <defined y array>, <defined spot-spot distance array> );
function script F_mobidle {
sleep2 1000; // stop if no RID
.@game_id = getattachedrid();
.@dist_min = 300;
.@size = getarg(1);
.@dx = getarg(3);
getunitdata .@game_id, .@data;
for ( .@i = 0; .@i < .@size; .@i++ ) {
.@dist_to_spot[.@i] = distance( .@data[UMOB_X], .@data[UMOB_Y], (getelementofarray( getarg(2),.@i ) + .@dx), getelementofarray( getarg(4),.@i ) );
if (.@dist_min >= .@dist_to_spot[.@i]) {
.@dist_min = .@dist_to_spot[.@i];
.@index = .@i;
}
}
if (.@dist_min <= 1) {
.@index++;
if (.@index >= .@size) {
unitskilluseid .@game_id,301,1,.@game_id,1,false,41; // SA_INSTANTDEATH
return;
}
}
else if (.@index < (.@size -1)) {
.@total[0] = .@dist_min + getelementofarray( getarg(5), .@index );
.@total[1] = .@dist_to_spot[.@index + 1] + getelementofarray( getarg(5), .@index+1 );
if (.@total[0] > .@total[1])
.@index = .@index + 1;
}
if (.@dist_to_spot[.@index] > 14) {
unitkill .@game_id;
end;
}
if (!.@data[UMOB_TARGETID]) {
unitwalk .@game_id, (getelementofarray( getarg(2),.@index ) + .@dx), getelementofarray( getarg(4),.@index );
sleep2 50; // for now a delay between unitwalk and mob_setidleevent is needed
}
mob_setidleevent .@game_id, getarg(0);
end;
}
prontera,146,75,1 script Zonda Rep#pron 4_F_ZONDAGIRL,{
.@player_name$ = strcharinfo(0);
getmapxy .@map_name$,.@x,.@y, BL_PC;
if (is_party_leader() == true) {
.@party_id = getcharid(1);
getpartymember .@party_id, 1;
getpartymember .@party_id, 2;
for ( .@i = 0; .@i < $@partymembercount; .@i++ ) {
if (isloggedin($@partymemberaid[.@i], $@partymembercid[.@i]) == 1)
.@count_online++;
}
.@menu_entry$ = "Request entry.";
}
mes "[Belka]";
mes "Welcome to Zonda, where innovation begins. What can I do for you?";
next;
switch( select( "Why are you here?", .@menu_entry$, "Enter ^6B9900<Wave Mode - Forest>^000000.", "Enter ^6B9900<Wave Mode - Sky>^000000.", "Cancel." ) ) {
case 1:
mes "[Belka]";
mes "We now offer a new service. Have you heard about the dimensional rifts?";
next;
mes "[Belka]";
mes "In case you haven't, many organizations and scientists study the rifts formed between dimensions.";
next;
mes "[Belka]";
mes "One of them is my company, Zonda, and after long, painstaking research...";
next;
mes "[Belka]";
mes "We've developed two different dimensional rifts that we call Wave Modes - Forest and Sky!";
next;
mes "[Belka]";
mes "Do you have any questions about Zonda's ambitious new project, Wave Mode?";
while(true) {
next;
switch( select( "About ^6B9900<Wave Mode - Forest>^000000", "About ^6B9900<Wave Mode - Sky>^000000", "No." ) ) {
case 1:
mes "[Belka]";
mes "The ^6B9900<Wave Mode - Forest>^000000";
mes "Introducing the first Wave Mode service, Zonda's objective is to work with your party members to keep monster waves from reaching the other side of the bridge.";
next;
mes "[Belka]";
mes "Monsters will advance without fighting, but the party will fail if 20 monsters are let go..";
break;
case 2:
mes "[Belka]";
mes "The ^6B9900<Wave Mode - Sky>^000000";
mes "Available once a day. Like the other Wave Mode, keep the monsters away from reaching the other side of the map.";
next;
mes "[Belka]";
mes "Feel free to come back with your party.";
break;
case 3:
mes "[Belka]";
mes "Thank you for visiting Zonda, where innovation begins.";
close;
}
next;
mes "[Belka]";
mes "Any other questions?";
}
case 2:
mes "[Belka]";
mes "Which Wave Mode would you like to enter: Forest or Sky?";
next;
.@s = select( "Enter ^6B9900<Wave Mode - Forest>^000000.", "Enter ^6B9900<Wave Mode - Sky>^000000." ) - 1;
setarray .@instance_name$[0], "Wave Mode - Forest", "Wave Mode - Sky";
mes "[Belka]";
mes "" + .@count_online + " party " + (.@count_online == 1 ? "member" : "members") + " found " + .@player_name$ + ", right? To enter the ^6B9900<" + .@instance_name$[.@s] + ">^000000, please sign here.";
next;
select("Sign.");
mes "[Belka]";
mes "" + .@player_name$ + " has requested to enter the ^6B9900<" + .@instance_name$[.@s] + ">^000000. Please come back when it's your turn to enter.";
if (instance_create(.@instance_name$[.@s]) < 0) {
mes "Party Name: " + getpartyname( getcharid(1) );
mes "Party Leader: " + strcharinfo(0);
mes "^0000ff" + .@instance_name$[.@s] + " ^000000 - Reservation Failed.";
close;
}
close;
case 3:
switch( instance_enter("Wave Mode - Forest") ) {
case IE_OTHER:
mes "[Belka]";
mes "An unknown error has occurred.";
close;
case IE_NOINSTANCE:
case IE_NOMEMBER:
mes "[Belka]";
mes "Please wait for ^6B9900<Wave Mode - Forest>^000000 stabilization.";
close;
case IE_OK:
mapannounce .@map_name$, "" + .@player_name$ + " of the party " + getpartyname(getcharid(1)) + " is entering <Wave Mode - Forest>.", bc_map,0xFF99;
// warp "1@def01",50,21;
break;
}
break;
case 4:
switch( instance_enter("Wave Mode - Sky") ) {
case IE_OTHER:
mes "[Belka]";
mes "An unknown error has occurred.";
close;
case IE_NOINSTANCE:
case IE_NOMEMBER:
mes "[Belka]";
mes "Currently the ^6B9900<Wave Mode - Sky>^000000 is being stabilized for your convenience. Please wait.";
close;
case IE_OK:
mapannounce .@map_name$, "" + .@player_name$ + " of the party " + getpartyname(getcharid(1)) + " is entering <Wave Mode - Sky>.", bc_map,0xFF99;
// warp "1@def02",29,35;
break;
}
break;
case 5:
mes "[Belka]";
mes "Thank you for visiting Zonda, where innovation begins.";
close;
}
wave_mode_map$ = .@map_name$;
wave_mode_x = .@x;
wave_mode_y = .@y;
end;
}
payon,166,98,1 duplicate(Zonda Rep#pron) Zonda Rep#pay 4_F_ZONDAGIRL
moc_para01,45,89,3 duplicate(Zonda Rep#pron) Zonda Rep#para 4_F_ZONDAGIRL
morocc,168,271,4 duplicate(Zonda Rep#pron) Zonda Rep#mor 4_F_ZONDAGIRL
// Wave mode forest
1@def01,50,21,0 script #wave_mode_forest_entrance HIDDEN_WARP_NPC,1,1,{
end;
OnTouch:
disablenpc instance_npcname("#wave_mode_forest_entrance");
initnpctimer;
end;
OnTimer2000:
mapannounce 'map_def01$, "We would like to thank all loyal customers of the Cool Event Corporation.", bc_map;
end;
OnTimer7000:
mapannounce 'map_def01$, "<Wave mode - Forest> prevent monsters from reaching the other side of the bridge.", bc_map;
end;
OnTimer12000:
mapannounce 'map_def01$, "If 20 monsters reach the other side, the game is over.", bc_map;
end;
OnTimer17000:
mapannounce 'map_def01$, "Ok, here they come.", bc_map;
end;
OnTimer22000:
stopnpctimer;
donpcevent instance_npcname("#wave_mode_forest_system") + "::OnStart";
end;
}
1@def01,1,1,0 script #wave_mode_forest_system -1,{
end;
OnStart:
initnpctimer;
end;
OnTimer1000:
mapannounce 'map_def01$, "3", bc_map;
end;
OnTimer2000:
mapannounce 'map_def01$, "2", bc_map;
end;
OnTimer3000:
mapannounce 'map_def01$, "1", bc_map;
end;
OnTimer4000:
'wave++;
if ('wave % 5)
mapannounce 'map_def01$, "-- Wave " + 'wave + " --", bc_map;
else
mapannounce 'map_def01$, "!! Champions summoned !!", bc_map;
donpcevent 'npc_name$ + "::OnSpawn";
end;
OnTimer24000:
mapannounce 'map_def01$, "Next monsters will come out soon. Get ready.", bc_map;
initnpctimer;
end;
OnStop:
stopnpctimer;
end;
}
1@def01,1,1,0 script #wave_mode_forest_spawn -1,{
end;
OnSpawn:
switch( 'wave % 70 ) {
case 1:
'mob_id = 2401; // G_PORING
break;
case 2:
'mob_id = 2582; // G_LUNATIC
break;
case 3:
'mob_id = 2573; // G_CHONCHON
break;
case 4:
'mob_id = 2590; // G_ROCKER
break;
case 5:
'mob_id = 2699; // C1_PORING
break;
case 6:
'mob_id = 2577; // G_FABRE
break;
case 7:
'mob_id = 1747; // G_SNAKE
break;
case 8:
'mob_id = 2595; // G_STAINER
break;
case 9:
'mob_id = 2576; // G_CREAMY
break;
case 10:
'mob_id = 2678; // C3_RODA_FROG
break;
case 11:
'mob_id = 2572; // G_CARAMEL
break;
case 12:
'mob_id = 1603; // G_BIGFOOT
break;
case 13:
'mob_id = 2589; // G_POPORING
break;
case 14:
'mob_id = 2578; // G_HORN
break;
case 15:
'mob_id = 2670; // C5_SCORPION
break;
case 16:
'mob_id = 2601; // G_YOYO
break;
case 17:
'mob_id = 2575; // G_COCO
break;
case 18:
'mob_id = 2583; // G_MARTIN
break;
case 19:
'mob_id = 2600; // G_WOLF
break;
case 20:
'mob_id = 2705; // C2_POISON_SPORE
break;
case 21:
'mob_id = 1430; // G_ARGOS
break;
case 22:
'mob_id = 2597; // G_STEEL_CHONCHON
break;
case 23:
'mob_id = 1431; // G_BAPHOMET_
break;
case 24:
'mob_id = 2591; // G_SAVAGE
break;
case 25:
'mob_id = 2857; // C5_DENIRO
break;
case 26:
'mob_id = 1457; // G_MANTIS
break;
case 27:
'mob_id = 1424; // G_SIDE_WINDER
break;
case 28:
'mob_id = 1429; // G_ARGIOPE
break;
case 29:
'mob_id = 1441; // G_PENOMENA
break;
case 30:
'mob_id = 2648; // C3_SOLDIER_SKELETON
break;
case 31:
'mob_id = 1422; // G_HUNTER_FLY
break;
case 32:
'mob_id = 2585; // G_MOLE
break;
case 33:
'mob_id = 2592; // G_SIORAVA
break;
case 34:
'mob_id = 2571; // G_BUTOIJO
break;
case 35:
'mob_id = 2673; // C3_SAVAGE
break;
case 36:
'mob_id = 2574; // G_CIVIL_SERVANT
break;
case 37:
'mob_id = 1459; // G_MARIONETTE
break;
case 38:
'mob_id = 1565; // G_WILD_GINSENG
break;
case 39:
'mob_id = 2602; // G_ZIPPER_BEAR
break;
case 40:
'mob_id = 2644; // C4_STALACTIC_GOLEM
break;
case 41:
'mob_id = 2588; // G_PITMAN
break;
case 42:
'mob_id = 1624; // G_WASTE_STOVE
break;
case 43:
'mob_id = 2570; // G_BREEZE
break;
case 44:
'mob_id = 1573; // G_ELDER
break;
case 45:
'mob_id = 2811; // C3_GRAND_PECO
break;
case 46:
'mob_id = 2598; // G_UNGOLIANT
break;
case 47:
'mob_id = 1606; // G_GARM_BABY
break;
case 48:
'mob_id = 1794; // G_ROWEEN
break;
case 49:
'mob_id = 2596; // G_STAPO
break;
case 50:
'mob_id = 2838; // C5_EVIL_DRUID
break;
case 51:
'mob_id = 2569; // G_ANOPHELES
break;
case 52:
'mob_id = 2584; // G_MINERAL
break;
case 53:
'mob_id = 2599; // G_WILD_RIDER
break;
case 54:
'mob_id = 1531; // G_EVIL_CLOUD_HERMIT
break;
case 55:
'mob_id = 2612; // C5_WOOD_GOBLIN
break;
case 56:
'mob_id = 2587; // G_OBSIDIAN
break;
case 57:
'mob_id = 1564; // G_WICKED_NYMPH
break;
case 58:
'mob_id = 2586; // G_NERAID
break;
case 59:
'mob_id = 1483; // G_RYBIO
break;
case 60:
'mob_id = 2888; // C2_BANASPATY
break;
case 61:
'mob_id = 2593; // G_SIROMA
break;
case 62:
'mob_id = 2580; // G_KAHO
break;
case 63:
'mob_id = 1600; // G_HEATER
break;
case 64:
'mob_id = 1791; // G_GALION
break;
case 65:
'mob_id = 2629; // C3_UNGOLIANT
break;
case 66:
'mob_id = 2581; // G_LUDE
break;
case 67:
'mob_id = 2579; // G_HYLOZOIST
break;
case 68:
'mob_id = 1549; // G_LAVA_GOLEM
break;
case 69:
'mob_id = 2594; // G_SNOWIER
break;
default:
'mob_id = 2730; // C2_NOVUS
break;
}
initnpctimer;
end;
OnTimer1000:
stopnpctimer;
if (('wave % 5) == 0) { // champion
donpcevent 'npc_name$ + "::OnSpawn0";
donpcevent 'npc_name$ + "::OnSpawn1";
donpcevent 'npc_name$ + "::OnSpawn2";
donpcevent 'npc_name$ + "::OnSpawn3";
donpcevent 'npc_name$ + "::OnSpawn4";
end;
}
if ('wave == 1) // first wave at x = 51
.@dx = 3;
else
.@dx = 2;
for ( .@i = 0; .@i < 24; .@i++ ) {
donpcevent 'npc_name$ + "::OnSpawn" + .@dx;
sleep 300;
}
// total ~7 secs
end;
OnSpawn0: callsub( S_Spawn, 0 );
OnSpawn1: callsub( S_Spawn, 1 );
OnSpawn2: callsub( S_Spawn, 2 );
OnSpawn3: callsub( S_Spawn, 3 );
OnSpawn4: callsub( S_Spawn, 4 );
S_Spawn:
.@x = 48 + getarg(0);
monster 'map_def01$,.@x,74, "Invader!", 'mob_id,1;
.@gid = $@mobid[0];
setunitdata .@gid, UMOB_MODE, ( MD_CANMOVE|MD_NORANDOMWALK );
setunitdata .@gid, UMOB_IGNORE_CELL_STACK_LIMIT, true;
mob_setidleevent .@gid, 'npc_name$ + "::OnIdle" + getarg(0);
end;
OnIdle0: callsub( S_Idle, 0 );
OnIdle1: callsub( S_Idle, 1 );
OnIdle2: callsub( S_Idle, 2 );
OnIdle3: callsub( S_Idle, 3 );
OnIdle4: callsub( S_Idle, 4 );
S_Idle:
callfunc( "F_mobidle", ('npc_name$ + "::OnIdle" + getarg(0)), 'size_coord, 'x_mob, getarg(0), 'y_mob, 'dist_spot_AZ );
'mob_escaped++;
if ('mob_escaped <= 20)
mapannounce 'map_def01$, "" + 'mob_escaped + " " + ('mob_escaped == 1 ? "monster has" : "monsters have") + " escaped.", bc_map;
if ('mob_escaped == 20)
donpcevent instance_npcname("#wave_mode_forest_out") + "::OnFail";
end;
}
// 1@def01,50,23,0 script #wave_mode_forest_warp WARPNPC,2,2,{
1@def01,50,30,0 script #wave_mode_forest_warp WARPNPC,2,2,{// official warp out
end;
OnTouch:
if (wave_mode_map$ == "")
warp "prontera",0,0;
else {
warp wave_mode_map$, wave_mode_x, wave_mode_y;
wave_mode_map$ = "";
wave_mode_x = wave_mode_y = 0;
}
end;
}
1@def01,1,1,0 script #wave_mode_forest_out -1,{
end;
OnFail:
donpcevent instance_npcname("#wave_mode_forest_system") + "::OnStop";
mapannounce 'map_def01$, "You have failed the <Wave mode - Forest> challenge.", bc_map;
enablenpc instance_npcname("#wave_mode_forest_warp");
initnpctimer;
end;
OnTimer1000:
mapannounce 'map_def01$, "<Wave mode - Forest> service has closed. You will be returned to the place you entered if you use the warp at the entrance.", bc_map;
end;
OnTimer30000:
stopnpctimer;
instance_destroy();
end;
OnInstanceInit:
'wave = 'mob_escaped = 0;
'map_def01$ = instance_mapname("1@def01");
'npc_name$ = instance_npcname("#wave_mode_forest_spawn");
disablenpc instance_npcname("#wave_mode_forest_system");
disablenpc instance_npcname("#wave_mode_forest_warp");
disablenpc instance_npcname("#wave_mode_forest_out");
setarray 'x_mob[0], 48, 48, 48, 48, 48, 48, 48;
setarray 'y_mob[0], 74, 65, 56, 48, 40, 31, 23;
'size_coord = getarraysize('y_mob);
for ( .@i = 0; .@i < 'size_coord -1; .@i++ ) {
.@dist_mob[.@i+1] = distance( 'x_mob[.@i], 'y_mob[.@i], 'x_mob[.@i+1], 'y_mob[.@i+1] );
.@total_mob += .@dist_mob[.@i+1];
}
for ( .@i = 0; .@i < 'size_coord -1; .@i++ )
'dist_spot_AZ[.@i] = .@total_mob - .@dist_mob[.@i];
end;
}
// Wave mode sky
1@def02,30,35,0 script #wave_mode_sky_entrance WARPNPC,1,1,{
end;
OnTouch:
if ('status_instance == 1)
end;
else if ('status_instance == 0) {
'status_instance = 1;
initnpctimer;
disablenpc instance_npcname("#wave_mode_sky_entrance");
}
else if ('status_instance == 2) {
if (wave_mode_map$ == "")
warp "prontera",0,0;
else {
warp wave_mode_map$, wave_mode_x, wave_mode_y;
wave_mode_map$ = "";
wave_mode_x = wave_mode_y = 0;
}
}
end;
OnTimer4000:
stopnpctimer;
donpcevent instance_npcname("#wave_mode_sky_system") + "::OnStart";
end;
}
1@def02,1,1,0 script #wave_mode_sky_system -1,{
end;
OnStart:
initnpctimer;
mapannounce 'map_def02$, "We would like to thank all costumers who always use Zonda Agency.", bc_map;
end;
OnTimer1000:
mapannounce 'map_def02$, "3", bc_map;
end;
OnTimer2000:
mapannounce 'map_def02$, "2", bc_map;
end;
OnTimer3000:
mapannounce 'map_def02$, "1", bc_map;
end;
OnTimer4000:
'wave_num++;
if ('wave_num % 5) {
donpcevent 'npc_name_mob$ + "::OnSpawn";
donpcevent 'npc_name_mercenary$ + "::OnStart";
donpcevent 'npc_name_mercenary$ + "::OnStart";
}
else {
donpcevent 'npc_name_treasure$ + "::OnStart";
}
end;
OnTimer24000:
mapannounce 'map_def02$, "Next monster wave will come out soon. Get ready.", bc_map;
initnpctimer;
end;
OnStop:
stopnpctimer;
end;
}
1@def02,1,1,0 script #wave_mode_sky_treasure -1,{
end;
OnStart:
if ('treasure_num < 5)
'treasure_num++;
mapannounce 'map_def02$, "!! " + 'treasure_num + " treasure box appeared !!", bc_map;
for ( .@i = 0; .@i < 'treasure_num; .@i++ ) {
monster 'map_def02$,0,0, "Treasure box",3075,1; // WA_TREASURE
'treasure_gid[.@i] = $@mobid[0];
}
initnpctimer;
end;
OnTimer16500: callsub( S_Dice, ET_DICE3 );
OnTimer17500: callsub( S_Dice, ET_DICE2 );
OnTimer18500: callsub( S_Dice, ET_DICE1 );
OnTimer19500:
for ( .@i = 0; .@i < 'treasure_num; .@i++ ) {
if (unitexists('treasure_gid[.@i]))
unitskilluseid 'treasure_gid[.@i],301,1,'treasure_gid[.@i],-1; // SA_INSTANTDEATH
}
donpcevent 'npc_name_treasure$ + "::OnStop";
end;
S_Dice:
.@emotion_num = getarg(0);
for ( .@i = 0; .@i < 'treasure_num; .@i++ ) {
if (unitexists('treasure_gid[.@i]))
emotion .@emotion_num, 'treasure_gid[.@i];
}
end;
OnStop:
deletearray 'treasure_gid[0], 'treasure_num;
stopnpctimer;
end;
}
1@def02,1,1,0 script #wave_mode_sky_mercenary -1,{
end;
OnStart:
monster 'map_def02$,29,35, "Mercenary", 3086,1, "",0,AI_WAVEMODE; // WA_MERCENARY
.@gid = $@mobid[0];
emotion ET_SURPRISE, .@gid;
setunitdata .@gid, UMOB_IGNORE_CELL_STACK_LIMIT, true;
mob_setidleevent .@gid, 'npc_name_mercenary$ + "::OnIdle";
sleep 45000;
if (unitexists(.@gid))
unitskilluseid .@gid,301,1,.@gid,1,false; // SA_INSTANTDEATH
end;
OnIdle:
callfunc( "F_mobidle", ('npc_name_mercenary$ + "::OnIdle"), 'size_coord, 'x_merc, 0, 'y_merc, 'dist_spot_ZA );
end;
}
1@def02,1,1,0 script #wave_mode_sky_mob_spawn -1,{
end;
OnSpawn:
mapannounce 'map_def02$, "-- Wave " + 'wave_num + " --", bc_map;
.@i = ('wave_num % 10) - 1;
setarray .@mob_list[0],
3076, // WA_MONSTER_1
3077, // WA_MONSTER_2
3078, // WA_MONSTER_3
3079, // WA_MONSTER_4
3080, // WA_MONSTER_5 (skipped)
3081, // WA_MONSTER_6
3082, // WA_MONSTER_7
3083, // WA_MONSTER_8
3084, // WA_MONSTER_9
3085; // WA_MONSTER_10 (skipped)
'mob_id = .@mob_list[.@i];
initnpctimer;
end;
OnTimer1000: callsub( S_Spawn, true ); // aggressive
OnTimer1500: callsub( S_Spawn, false ); // passive
OnTimer2000: callsub( S_Spawn, true );
OnTimer2500: callsub( S_Spawn, false );
OnTimer3000: callsub( S_Spawn, true );
OnTimer3500: callsub( S_Spawn, false );
OnTimer4000: callsub( S_Spawn, true );
OnTimer4500: callsub( S_Spawn, false );
OnTimer5000: callsub( S_Spawn, true );
OnTimer5500: callsub( S_Spawn, false );
OnTimer6000: callsub( S_Spawn, true );
OnTimer6500: callsub( S_Spawn, false );
OnTimer7000: callsub( S_Spawn, true );
OnTimer7500: callsub( S_Spawn, false );
OnTimer8000: callsub( S_Spawn, true );
OnTimer8500:
stopnpctimer;
end;
S_Spawn:
monster 'map_def02$,48,67, "Go!", 'mob_id,1;
if (getarg(0) == true)
setunitdata $@mobid[0], UMOB_MODE, ( MD_CANMOVE|MD_NORANDOMWALK|MD_AGGRESSIVE|MD_CANATTACK );
.@gid = $@mobid[0];
emotion ET_SURPRISE, .@gid;
setunitdata .@gid, UMOB_IGNORE_CELL_STACK_LIMIT, true;
mob_setidleevent .@gid, 'npc_name_mob$ + "::OnIdle";
end;
OnIdle:
// monster are walking to the nearest next spot
callfunc( "F_mobidle", ('npc_name_mob$ + "::OnIdle"), 'size_coord, 'x_mob, 0, 'y_mob, 'dist_spot_AZ );
'mob_escaped++;
if ('mob_escaped <= 20)
mapannounce 'map_def02$, "" + 'mob_escaped + " " + ('mob_escaped == 1 ? "monster has" : "monsters have") + " escaped.", bc_map;
if ('mob_escaped == 20)
donpcevent instance_npcname("#wave_mode_sky_out") + "::OnFail";
end;
}
1@def02,1,1,0 script #wave_mode_sky_out -1,{
end;
OnFail:
// monsters timers continue
donpcevent instance_npcname("#wave_mode_sky_system") + "::OnStop";
mapannounce 'map_def02$, "You have failed the <Wave mode - Sky> challenge.", bc_map;
'status_instance = 2;
enablenpc instance_npcname("#wave_mode_sky_entrance");
initnpctimer;
end;
OnTimer1000:
mapannounce 'map_def02$, "<Wave mode - Sky> service has closed. You will be returned to the place you entered if you use the warp at the entrance.", bc_map;
end;
OnTimer30000:
stopnpctimer;
instance_destroy();
end;
OnInstanceInit:
deletearray 'treasure_gid[0], 'treasure_num;
'wave_num = 'treasure_num = 'status_instance = 'mob_escaped = 0;
'map_def02$ = instance_mapname("1@def02");
'npc_name_mob$ = instance_npcname("#wave_mode_sky_mob_spawn");
'npc_name_treasure$ = instance_npcname("#wave_mode_sky_treasure");
'npc_name_mercenary$ = instance_npcname("#wave_mode_sky_mercenary");
disablenpc instance_npcname("#wave_mode_sky_out");
setarray 'x_mob[0], 48, 38, 30, 30, 29, 38, 47, 51, 50, 42, 33, 29;
setarray 'y_mob[0], 67, 67, 64, 58, 52, 52, 51, 45, 36, 36, 36, 35;
setarray 'x_merc[0], 29, 33, 42, 50, 51, 47, 38, 29, 30, 30, 38, 47;
setarray 'y_merc[0], 35, 36, 36, 36, 45, 51, 52, 52, 58, 64, 67, 67;
'size_coord = getarraysize('x_mob);
for ( .@i = 0; .@i < 'size_coord -1; .@i++ ) {
.@dist_mob[.@i+1] = distance( 'x_mob[.@i], 'y_mob[.@i], 'x_mob[.@i+1], 'y_mob[.@i+1] );
.@total_mob += .@dist_mob[.@i+1];
.@dist_merc[.@i+1] = distance( 'x_merc[.@i], 'y_merc[.@i], 'x_merc[.@i+1], 'y_merc[.@i+1] );
.@total_merc += .@dist_merc[.@i+1];
}
for ( .@i = 0; .@i < 'size_coord -1; .@i++ ) {
'dist_spot_AZ[.@i] = .@total_mob - .@dist_mob[.@i];
'dist_spot_ZA[.@i] = .@total_merc - .@dist_merc[.@i];
}
end;
}

View File

@ -25,9 +25,9 @@
2@gl_k mapflag restricted 6
// Wave Mode Memorial Dungeon ====
1@def01 mapflag restricted 6
1@def02 mapflag restricted 6
1@def03 mapflag restricted 6
1@def01 mapflag restricted 9
1@def02 mapflag restricted 9
1@def03 mapflag restricted 9
// Heroes' Trail - Part 1 ========
1@face mapflag restricted 6

View File

@ -87,7 +87,7 @@ npc: npc/re/instances/RoomOfConsciousness.txt
npc: npc/re/instances/SarahAndFenrir.txt
npc: npc/re/instances/SaraMemory.txt
npc: npc/re/instances/SkyFortress.txt
//npc: npc/re/instances/WaveMode.txt
npc: npc/re/instances/WernerLaboratoryCentralRoom.txt
npc: npc/re/instances/WolfchevLaboratory.txt
//npc: npc/custom/official/GeffenMagicTournament.txt

View File

@ -8424,9 +8424,9 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
{ //Normal mobs
if(
( target->type == BL_MOB && t_bl->type == BL_PC && ( ((TBL_MOB*)target)->special_state.ai != AI_ZANZOU && ((TBL_MOB*)target)->special_state.ai != AI_ATTACK ) ) ||
( t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai )
( t_bl->type == BL_MOB && (((TBL_MOB*)t_bl)->special_state.ai == AI_NONE || ((TBL_MOB*)t_bl)->special_state.ai == AI_WAVEMODE ))
)
state |= BCT_PARTY; //Normal mobs with no ai are friends.
state |= BCT_PARTY; //Normal mobs with no ai or with AI_WAVEMODE are friends.
else
state |= BCT_ENEMY; //However, all else are enemies.
}

View File

@ -381,6 +381,7 @@ enum mob_ai {
AI_LEGION,
AI_FAW,
AI_GUILD,
AI_WAVEMODE,
AI_MAX
};

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;

View File

@ -333,6 +333,7 @@ struct mob_data {
int8 skill_idx; // Index of last used skill from db->skill[]
t_tick skilldelay[MAX_MOBSKILL];
char npc_event[EVENT_NAME_LENGTH];
char idle_event[EVENT_NAME_LENGTH];
/**
* Did this monster summon something?
* Used to flag summon deletions, saves a worth amount of memory
@ -487,6 +488,7 @@ int mob_class_change(struct mob_data *md,int mob_id);
int mob_warpslave(struct block_list *bl, int range);
int mob_linksearch(struct block_list *bl,va_list ap);
bool mob_chat_display_message (mob_data &md, uint16 msg_id);
int mobskill_use(struct mob_data *md,t_tick tick,int event);
int mobskill_event(struct mob_data *md,struct block_list *src,t_tick tick, int flag);
int mob_summonslave(struct mob_data *md2,int *value,int amount,uint16 skill_id);

View File

@ -18157,6 +18157,7 @@ BUILDIN_FUNC(getunitdata)
getunitdata_sub(UMOB_ROBE, md->vd->robe);
getunitdata_sub(UMOB_BODY2, md->vd->body_style);
getunitdata_sub(UMOB_GROUP_ID, md->ud.group_id);
getunitdata_sub(UMOB_IGNORE_CELL_STACK_LIMIT, md->ud.state.ignore_cell_stack_limit);
break;
case BL_HOM:
@ -18561,6 +18562,7 @@ BUILDIN_FUNC(setunitdata)
case UMOB_ROBE: clif_changelook(bl, LOOK_ROBE, (unsigned short)value); break;
case UMOB_BODY2: clif_changelook(bl, LOOK_BODY2, (unsigned short)value); break;
case UMOB_GROUP_ID: md->ud.group_id = value; unit_refresh(bl); break;
case UMOB_IGNORE_CELL_STACK_LIMIT: md->ud.state.ignore_cell_stack_limit = value > 0; break;
default:
ShowError("buildin_setunitdata: Unknown data identifier %d for BL_MOB.\n", type);
return SCRIPT_CMD_FAILURE;
@ -19284,8 +19286,8 @@ BUILDIN_FUNC(unitemote)
/// Makes the unit cast the skill on the target or self if no target is specified.
///
/// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>,<casttime>};
/// unitskilluseid <unit_id>,"<skill name>",<skill_lv>{,<target_id>,<casttime>};
/// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>,<casttime>,<cancel>,<Line_ID>};
/// unitskilluseid <unit_id>,"<skill name>",<skill_lv>{,<target_id>,<casttime>,<cancel>,<Line_ID>};
BUILDIN_FUNC(unitskilluseid)
{
int unit_id, target_id, casttime;
@ -19311,15 +19313,26 @@ BUILDIN_FUNC(unitskilluseid)
skill_lv = script_getnum(st,4);
target_id = ( script_hasdata(st,5) ? script_getnum(st,5) : unit_id );
casttime = ( script_hasdata(st,6) ? script_getnum(st,6) : 0 );
bool cancel = ( script_hasdata(st,7) ? script_getnum(st,7) > 0 : skill_get_castcancel(skill_id) );
int msg_id = (script_hasdata(st, 8) ? script_getnum(st, 8) : 0);
if(script_rid2bl(2,bl)){
if (msg_id > 0) {
if (bl->type != BL_MOB) {
ShowError("buildin_unitskilluseid: Msg can only be used for monster.\n");
return SCRIPT_CMD_FAILURE;
}
TBL_MOB* md = map_id2md(bl->id);
if (md)
mob_chat_display_message(*md, static_cast<uint16>(msg_id));
}
if (bl->type == BL_NPC) {
if (!((TBL_NPC*)bl)->status.hp)
status_calc_npc(((TBL_NPC*)bl), SCO_FIRST);
else
status_calc_npc(((TBL_NPC*)bl), SCO_NONE);
}
unit_skilluse_id2(bl, target_id, skill_id, skill_lv, (casttime * 1000) + skill_castfix(bl, skill_id, skill_lv), skill_get_castcancel(skill_id));
unit_skilluse_id2(bl, target_id, skill_id, skill_lv, (casttime * 1000) + skill_castfix(bl, skill_id, skill_lv), cancel);
}
return SCRIPT_CMD_SUCCESS;
@ -19327,8 +19340,8 @@ BUILDIN_FUNC(unitskilluseid)
/// Makes the unit cast the skill on the target position.
///
/// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>{,<casttime>};
/// unitskillusepos <unit_id>,"<skill name>",<skill_lv>,<target_x>,<target_y>{,<casttime>};
/// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>{,<casttime>,<cancel>,<Line_ID>};
/// unitskillusepos <unit_id>,"<skill name>",<skill_lv>,<target_x>,<target_y>{,<casttime>,<cancel>,<Line_ID>};
BUILDIN_FUNC(unitskillusepos)
{
int skill_x, skill_y, casttime;
@ -19354,15 +19367,26 @@ BUILDIN_FUNC(unitskillusepos)
skill_x = script_getnum(st,5);
skill_y = script_getnum(st,6);
casttime = ( script_hasdata(st,7) ? script_getnum(st,7) : 0 );
bool cancel = ( script_hasdata(st,8) ? script_getnum(st,8) > 0 : skill_get_castcancel(skill_id) );
int msg_id = (script_hasdata(st, 9) ? script_getnum(st, 9) : 0);
if(script_rid2bl(2,bl)){
if (msg_id > 0) {
if (bl->type != BL_MOB) {
ShowError("buildin_unitskilluseid: Msg can only be used for monster.\n");
return SCRIPT_CMD_FAILURE;
}
TBL_MOB* md = map_id2md(bl->id);
if (md)
mob_chat_display_message(*md, static_cast<uint16>(msg_id));
}
if (bl->type == BL_NPC) {
if (!((TBL_NPC*)bl)->status.hp)
status_calc_npc(((TBL_NPC*)bl), SCO_FIRST);
else
status_calc_npc(((TBL_NPC*)bl), SCO_NONE);
}
unit_skilluse_pos2(bl, skill_x, skill_y, skill_id, skill_lv, (casttime * 1000) + skill_castfix(bl, skill_id, skill_lv), skill_get_castcancel(skill_id));
unit_skilluse_pos2(bl, skill_x, skill_y, skill_id, skill_lv, (casttime * 1000) + skill_castfix(bl, skill_id, skill_lv), cancel);
}
return SCRIPT_CMD_SUCCESS;
@ -25256,6 +25280,33 @@ BUILDIN_FUNC(getenchantgrade){
return SCRIPT_CMD_SUCCESS;
}
/*==========================================
* mob_setidleevent( <monster game ID>, "<event label>" )
*------------------------------------------*/
BUILDIN_FUNC(mob_setidleevent){
struct block_list* bl;
if( !script_rid2bl( 2, bl ) ){
return SCRIPT_CMD_FAILURE;
}
if( bl->type != BL_MOB ){
ShowError( "buildin_mob_setidleevent: the target GID was not a monster.\n" );
return SCRIPT_CMD_FAILURE;
}
struct mob_data* md = (struct mob_data*)bl;
if (md == nullptr)
return SCRIPT_CMD_FAILURE;
const char* idle_event = script_getstr( st, 3 );
check_event( st, idle_event );
safestrncpy( md->idle_event, idle_event, EVENT_NAME_LENGTH );
return SCRIPT_CMD_SUCCESS;
}
#include "../custom/script.inc"
// declarations that were supposed to be exported from npc_chat.cpp
@ -25709,8 +25760,8 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(unitstopwalk,"i?"),
BUILDIN_DEF(unittalk,"is?"),
BUILDIN_DEF_DEPRECATED(unitemote,"ii","20170811"),
BUILDIN_DEF(unitskilluseid,"ivi??"), // originally by Qamera [Celest]
BUILDIN_DEF(unitskillusepos,"iviii?"), // [Celest]
BUILDIN_DEF(unitskilluseid,"ivi????"), // originally by Qamera [Celest]
BUILDIN_DEF(unitskillusepos,"iviii???"), // [Celest]
// <--- [zBuffer] List of unit control commands
BUILDIN_DEF(sleep,"i"),
BUILDIN_DEF(sleep2,"i"),
@ -25950,6 +26001,7 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(getenchantgrade, ""),
BUILDIN_DEF(mob_setidleevent, "is"),
#include "../custom/script_def.inc"
{NULL,NULL,NULL},

View File

@ -480,6 +480,7 @@ enum unitdata_mobtypes {
UMOB_ROBE,
UMOB_BODY2,
UMOB_GROUP_ID,
UMOB_IGNORE_CELL_STACK_LIMIT,
};
enum unitdata_homuntypes {

View File

@ -3847,6 +3847,7 @@
export_constant(AI_LEGION);
export_constant(AI_FAW);
export_constant(AI_GUILD);
export_constant(AI_WAVEMODE);
/* battle flags */
export_constant(BF_NONE);
@ -4380,6 +4381,7 @@
export_constant(UMOB_ROBE);
export_constant(UMOB_BODY2);
export_constant(UMOB_GROUP_ID);
export_constant(UMOB_IGNORE_CELL_STACK_LIMIT);
/* unit control - homunculus */
export_constant(UHOM_SIZE);

View File

@ -628,7 +628,8 @@ static TIMER_FUNC(unit_walktoxy_timer)
ud->to_x = bl->x;
ud->to_y = bl->y;
if(battle_config.official_cell_stack_limit > 0
if (!ud->state.ignore_cell_stack_limit
&& battle_config.official_cell_stack_limit > 0
&& map_count_oncell(bl->m, x, y, BL_CHAR|BL_NPC, 1) > battle_config.official_cell_stack_limit) {
//Walked on occupied cell, call unit_walktoxy again
if(ud->steptimer != INVALID_TIMER) {

View File

@ -57,6 +57,7 @@ struct unit_data {
unsigned walk_script : 1;
unsigned blockedmove : 1;
unsigned blockedskill : 1;
unsigned ignore_cell_stack_limit : 1;
} state;
char walk_done_event[EVENT_NAME_LENGTH];
char title[NAME_LENGTH];