summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/script_commands.txt11
-rw-r--r--src/map/script.c88
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,"??"),