diff options
-rw-r--r-- | doc/script_commands.txt | 11 | ||||
-rw-r--r-- | src/map/script.c | 88 |
2 files changed, 77 insertions, 22 deletions
diff --git a/doc/script_commands.txt b/doc/script_commands.txt index aa0fea31c..eb0f2a78a 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -3357,12 +3357,13 @@ Examples: --------------------------------------- -*getunits(<type>, <variable>, <limit>, "<map>"{, <x1>, <y1>, <x2>, <y2>}) +*getunits(<type>, <variable>, <limit>{, "<map>"{, <x1>, <y1>, <x2>, <y2>}}) This function searches a whole map or area for units and adds their GID to the provided <variable> array. It filters units by <type> and stops searching after <limit> units have been found. Set <limit> to false (0) if you wish to -disable the limit altogether. +disable the limit altogether. If <map> is omitted, this command will search +on the whole server (slow). Returns the number of units added to the array. Type is the type of unit to search for: @@ -3381,6 +3382,10 @@ Type is the type of unit to search for: ** Do NOT use UNITTYPE_ constants here, they have different values. +** If battle_config.dynamic_mobs is enabled and no player has entered the map + yet, the mobs will not have spawned in the map yet, so getunits() will be + unable to find them when searching for BL_MOB. + Example: .@count = getunits((BL_PC | BL_NPC), .@units, false, "prontera"); @@ -10007,4 +10012,4 @@ the available flags are: P_AIRSHIP_ITEM_NOT_ENOUGH P_AIRSHIP_ITEM_INVALID ----------------------------------------
\ No newline at end of file +--------------------------------------- diff --git a/src/map/script.c b/src/map/script.c index 67dd4214c..d4554227e 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -11222,10 +11222,15 @@ int buildin_getunits_sub(struct block_list *bl, va_list ap) uint32 limit = va_arg(ap, uint32); const char *name = va_arg(ap, const char *); struct reg_db *ref = va_arg(ap, struct reg_db *); + enum bl_type type = va_arg(ap, enum bl_type); uint32 index = start + *count; - if (index >= SCRIPT_MAX_ARRAYSIZE || *count > limit) { - return 1; + if ((bl->type & type) == 0) { + return 0; // type mismatch => skip + } + + if (index >= SCRIPT_MAX_ARRAYSIZE || *count >= limit) { + return -1; } script->set_reg(st, sd, reference_uid(id, index), name, @@ -11235,16 +11240,31 @@ int buildin_getunits_sub(struct block_list *bl, va_list ap) return 0; } +static int buildin_getunits_sub_pc(struct map_session_data *sd, va_list ap) +{ + return buildin_getunits_sub(&sd->bl, ap); +} + +static int buildin_getunits_sub_mob(struct mob_data *md, va_list ap) +{ + return buildin_getunits_sub(&md->bl, ap); +} + +static int buildin_getunits_sub_npc(struct npc_data *nd, va_list ap) +{ + return buildin_getunits_sub(&nd->bl, ap); +} + BUILDIN(getunits) { - const char *mapname, *name; - int16 m, x1, y1, x2, y2; + const char *name; int32 id; uint32 start; struct reg_db *ref; enum bl_type type = script_getnum(st, 2); struct script_data *data = script_getdata(st, 3); - uint32 count = 0, limit = script_getnum(st, 4); + uint32 count = 0; + uint32 limit = script_getnum(st, 4); struct map_session_data *sd = NULL; if (!data_isreference(data) || reference_toconstant(data)) { @@ -11277,20 +11297,50 @@ BUILDIN(getunits) limit = SCRIPT_MAX_ARRAYSIZE; } - mapname = script_getstr(st, 5); - m = map->mapname2mapid(mapname); - - if (script_hasdata(st, 9)) { - x1 = script_getnum(st, 6); - y1 = script_getnum(st, 7); - x2 = script_getnum(st, 8); - y2 = script_getnum(st, 9); - - map->foreachinarea(buildin_getunits_sub, m, x1, y1, x2, y2, type, - st, sd, id, start, &count, limit, name, ref); + if (script_hasdata(st, 5)) { + const char *mapname = script_getstr(st, 5); + int16 m = map->mapname2mapid(mapname); + + if (script_hasdata(st, 9)) { + int16 x1 = script_getnum(st, 6); + int16 y1 = script_getnum(st, 7); + int16 x2 = script_getnum(st, 8); + int16 y2 = script_getnum(st, 9); + + // FIXME: map_foreachinarea does NOT stop iterating when the callback + // function returns -1. we still limit the array size, but + // this doesn't break the loop. We need a foreach function + // that behaves like map_foreachiddb, but for areas + map->foreachinarea(buildin_getunits_sub, m, x1, y1, x2, y2, type, + st, sd, id, start, &count, limit, name, ref, type); + } else { + // FIXME: map_foreachinmap does NOT stop iterating when the callback + // function returns -1. we still limit the array size, but + // this doesn't break the loop. We need a foreach function + // that behaves like map_foreachiddb, but for maps + map->foreachinmap(buildin_getunits_sub, m, type, + st, sd, id, start, &count, limit, name, ref, type); + } } else { - map->foreachinmap(buildin_getunits_sub, m, type, - st, sd, id, start, &count, limit, name, ref); + // for faster lookup we try to reduce the scope of the search if possible + switch (type) { + case BL_PC: + map->foreachpc(buildin_getunits_sub_pc, + st, sd, id, start, &count, limit, name, ref, type); + break; + case BL_MOB: + map->foreachmob(buildin_getunits_sub_mob, + st, sd, id, start, &count, limit, name, ref, type); + break; + case BL_NPC: + map->foreachnpc(buildin_getunits_sub_npc, + st, sd, id, start, &count, limit, name, ref, type); + break; + default: + // fallback to global lookup (slowest option) + map->foreachiddb(buildin_getunits_sub, + st, sd, id, start, &count, limit, name, ref, type); + } } script_pushint(st, count); @@ -24318,7 +24368,7 @@ void script_parse_builtin(void) { BUILDIN_DEF(deltimer,"s?"), BUILDIN_DEF(addtimercount,"si?"), BUILDIN_DEF(gettimer,"i??"), - BUILDIN_DEF(getunits,"iris????"), + BUILDIN_DEF(getunits,"iri?????"), BUILDIN_DEF(initnpctimer,"??"), BUILDIN_DEF(stopnpctimer,"??"), BUILDIN_DEF(startnpctimer,"??"), |