From 41974ae5265fbc23a06f276f9e008d5dad020e0b Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Thu, 30 Aug 2012 16:16:25 -0700 Subject: Rename files for C++ conversion. Does not compile. After updating, you can remove these files, as shown in 'git status': Untracked files: (use "git add ..." to include in what will be committed) src/map/magic-interpreter-lexer.c src/map/magic-interpreter-parser.c src/map/magic-interpreter-parser.h --- src/map/magic-expr.cpp | 1655 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1655 insertions(+) create mode 100644 src/map/magic-expr.cpp (limited to 'src/map/magic-expr.cpp') diff --git a/src/map/magic-expr.cpp b/src/map/magic-expr.cpp new file mode 100644 index 0000000..d75042d --- /dev/null +++ b/src/map/magic-expr.cpp @@ -0,0 +1,1655 @@ +#include "magic-expr.hpp" +#include "magic-expr-eval.hpp" +#include "itemdb.hpp" +#include + +#include "../common/mt_rand.hpp" + +#define IS_SOLID(c) ((c) == 1 || (c) == 5) + +int map_is_solid (int m, int x, int y) +{ + return (IS_SOLID (map_getcell (m, x, y))); +} + +#undef IS_SOLID + +static void free_area (area_t * area) +{ + if (!area) + return; + + switch (area->ty) + { + case AREA_UNION: + free_area (area->a.a_union[0]); + free_area (area->a.a_union[1]); + break; + default: + break; + } + + free (area); +} + +static area_t *dup_area (area_t * area) +{ + area_t *retval = (area_t *)malloc (sizeof (area_t)); + *retval = *area; + + switch (area->ty) + { + case AREA_UNION: + retval->a.a_union[0] = dup_area (retval->a.a_union[0]); + retval->a.a_union[1] = dup_area (retval->a.a_union[1]); + break; + default: + break; + } + + return retval; +} + +void magic_copy_var (val_t * dest, val_t * src) +{ + *dest = *src; + + switch (dest->ty) + { + case TY_STRING: + dest->v.v_string = strdup (dest->v.v_string); + break; + case TY_AREA: + dest->v.v_area = dup_area (dest->v.v_area); + break; + default: + break; + } + +} + +void magic_clear_var (val_t * v) +{ + switch (v->ty) + { + case TY_STRING: + free (v->v.v_string); + break; + case TY_AREA: + free_area (v->v.v_area); + break; + default: + break; + } +} + +static char *show_entity (entity_t * entity) +{ + switch (entity->type) + { + case BL_PC: + return ((struct map_session_data *) entity)->status.name; + case BL_NPC: + return ((struct npc_data *) entity)->name; + case BL_MOB: + return ((struct mob_data *) entity)->name; + case BL_ITEM: + /* Sorry about this one... */ + return ((struct item_data + *) (&((struct flooritem_data *) entity)-> + item_data))->name; + case BL_SKILL: + return "%skill"; + case BL_SPELL: + return "%invocation(ERROR:this-should-not-be-an-entity)"; + default: + return "%unknown-entity"; + } +} + +static void stringify (val_t * v, int within_op) +{ + static char *dirs[8] = + { "south", "south-west", "west", "north-west", "north", "north-east", + "east", "south-east" + }; + char *buf; + + switch (v->ty) + { + case TY_UNDEF: + buf = strdup ("UNDEF"); + break; + + case TY_INT: + buf = (char *)malloc (32); + sprintf (buf, "%i", v->v.v_int); + break; + + case TY_STRING: + return; + + case TY_DIR: + buf = strdup (dirs[v->v.v_int]); + break; + + case TY_ENTITY: + buf = strdup (show_entity (v->v.v_entity)); + break; + + case TY_LOCATION: + buf = (char *) malloc (128); + sprintf (buf, "<\"%s\", %d, %d>", map[v->v.v_location.m].name, + v->v.v_location.x, v->v.v_location.y); + break; + + case TY_AREA: + buf = strdup ("%area"); + free_area (v->v.v_area); + break; + + case TY_SPELL: + buf = strdup (v->v.v_spell->name); + break; + + case TY_INVOCATION: + { + invocation_t *invocation = within_op + ? v->v.v_invocation : (invocation_t *) map_id2bl (v->v.v_int); + buf = strdup (invocation->spell->name); + } + break; + + default: + fprintf (stderr, "[magic] INTERNAL ERROR: Cannot stringify %d\n", + v->ty); + return; + } + + v->v.v_string = buf; + v->ty = TY_STRING; +} + +static void intify (val_t * v) +{ + if (v->ty == TY_INT) + return; + + magic_clear_var (v); + v->ty = TY_INT; + v->v.v_int = 1; +} + +area_t *area_new (int ty) +{ + area_t *retval; + CREATE (retval, area_t, 1); + retval->ty = ty; + return retval; +} + +area_t *area_union (area_t * area, area_t * other_area) +{ + area_t *retval = area_new (AREA_UNION); + retval->a.a_union[0] = area; + retval->a.a_union[1] = other_area; + retval->size = area->size + other_area->size; /* Assume no overlap */ + return retval; +} + +/** + * Turns location into area, leaves other types untouched + */ +static void make_area (val_t * v) +{ + if (v->ty == TY_LOCATION) + { + area_t *a = (char *)malloc (sizeof (area_t)); + v->ty = TY_AREA; + a->ty = AREA_LOCATION; + a->a.a_loc = v->v.v_location; + v->v.v_area = a; + } +} + +static void make_location (val_t * v) +{ + if (v->ty == TY_AREA && v->v.v_area->ty == AREA_LOCATION) + { + location_t location = v->v.v_area->a.a_loc; + free_area (v->v.v_area); + v->ty = TY_LOCATION; + v->v.v_location = location; + } +} + +static void make_spell (val_t * v) +{ + if (v->ty == TY_INVOCATION) + { + invocation_t *invoc = v->v.v_invocation; //(invocation_t *) map_id2bl(v->v.v_int); + if (!invoc) + v->ty = TY_FAIL; + else + { + v->ty = TY_SPELL; + v->v.v_spell = invoc->spell; + } + } +} + +static int fun_add (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_INT && TY (1) == TY_INT) + { + /* Integer addition */ + RESULTINT = ARGINT (0) + ARGINT (1); + result->ty = TY_INT; + } + else if (ARG_MAY_BE_AREA (0) && ARG_MAY_BE_AREA (1)) + { + /* Area union */ + make_area (&args[0]); + make_area (&args[1]); + RESULTAREA = area_union (ARGAREA (0), ARGAREA (1)); + ARGAREA (0) = NULL; + ARGAREA (1) = NULL; + result->ty = TY_AREA; + } + else + { + /* Anything else -> string concatenation */ + stringify (&args[0], 1); + stringify (&args[1], 1); + /* Yes, we could speed this up. */ + RESULTSTR = + (char *) malloc (1 + strlen (ARGSTR (0)) + strlen (ARGSTR (1))); + strcpy (RESULTSTR, ARGSTR (0)); + strcat (RESULTSTR, ARGSTR (1)); + result->ty = TY_STRING; + } + return 0; +} + +static int fun_sub (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) - ARGINT (1); + return 0; +} + +static int fun_mul (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) * ARGINT (1); + return 0; +} + +static int fun_div (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (!ARGINT (1)) + return 1; /* division by zero */ + RESULTINT = ARGINT (0) / ARGINT (1); + return 0; +} + +static int fun_mod (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (!ARGINT (1)) + return 1; /* division by zero */ + RESULTINT = ARGINT (0) % ARGINT (1); + return 0; +} + +static int fun_or (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) || ARGINT (1); + return 0; +} + +static int fun_and (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) && ARGINT (1); + return 0; +} + +static int fun_not (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = !ARGINT (0); + return 0; +} + +static int fun_neg (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ~ARGINT (0); + return 0; +} + +static int fun_gte (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_STRING || TY (1) == TY_STRING) + { + stringify (&args[0], 1); + stringify (&args[1], 1); + RESULTINT = strcmp (ARGSTR (0), ARGSTR (1)) >= 0; + } + else + { + intify (&args[0]); + intify (&args[1]); + RESULTINT = ARGINT (0) >= ARGINT (1); + } + return 0; +} + +static int fun_gt (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_STRING || TY (1) == TY_STRING) + { + stringify (&args[0], 1); + stringify (&args[1], 1); + RESULTINT = strcmp (ARGSTR (0), ARGSTR (1)) > 0; + } + else + { + intify (&args[0]); + intify (&args[1]); + RESULTINT = ARGINT (0) > ARGINT (1); + } + return 0; +} + +static int fun_eq (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_STRING || TY (1) == TY_STRING) + { + stringify (&args[0], 1); + stringify (&args[1], 1); + RESULTINT = strcmp (ARGSTR (0), ARGSTR (1)) == 0; + } + else if (TY (0) == TY_DIR && TY (1) == TY_DIR) + RESULTINT = ARGDIR (0) == ARGDIR (1); + else if (TY (0) == TY_ENTITY && TY (1) == TY_ENTITY) + RESULTINT = ARGENTITY (0) == ARGENTITY (1); + else if (TY (0) == TY_LOCATION && TY (1) == TY_LOCATION) + RESULTINT = (ARGLOCATION (0).x == ARGLOCATION (1).x + && ARGLOCATION (0).y == ARGLOCATION (1).y + && ARGLOCATION (0).m == ARGLOCATION (1).m); + else if (TY (0) == TY_AREA && TY (1) == TY_AREA) + RESULTINT = ARGAREA (0) == ARGAREA (1); /* Probably not that great an idea... */ + else if (TY (0) == TY_SPELL && TY (1) == TY_SPELL) + RESULTINT = ARGSPELL (0) == ARGSPELL (1); + else if (TY (0) == TY_INVOCATION && TY (1) == TY_INVOCATION) + RESULTINT = ARGINVOCATION (0) == ARGINVOCATION (1); + else + { + intify (&args[0]); + intify (&args[1]); + RESULTINT = ARGINT (0) == ARGINT (1); + } + return 0; +} + +static int fun_bitand (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) & ARGINT (1); + return 0; +} + +static int fun_bitor (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) | ARGINT (1); + return 0; +} + +static int fun_bitxor (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) ^ ARGINT (1); + return 0; +} + +static int fun_bitshl (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) << ARGINT (1); + return 0; +} + +static int fun_bitshr (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) >> ARGINT (1); + return 0; +} + +static int fun_max (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = MAX (ARGINT (0), ARGINT (1)); + return 0; +} + +static int fun_min (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = MIN (ARGINT (0), ARGINT (1)); + return 0; +} + +static int +fun_if_then_else (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ARGINT (0)) + magic_copy_var (result, &args[1]); + else + magic_copy_var (result, &args[2]); + return 0; +} + +void +magic_area_rect (int *m, int *x, int *y, int *width, int *height, + area_t * area) +{ + switch (area->ty) + { + case AREA_UNION: + break; + + case AREA_LOCATION: + *m = area->a.a_loc.m; + *x = area->a.a_loc.x; + *y = area->a.a_loc.y; + *width = 1; + *height = 1; + break; + + case AREA_RECT: + *m = area->a.a_rect.loc.m; + *x = area->a.a_rect.loc.x; + *y = area->a.a_rect.loc.y; + *width = area->a.a_rect.width; + *height = area->a.a_rect.height; + break; + + case AREA_BAR: + { + int tx = area->a.a_bar.loc.x; + int ty = area->a.a_bar.loc.y; + int twidth = area->a.a_bar.width; + int tdepth = area->a.a_bar.width; + *m = area->a.a_bar.loc.m; + + switch (area->a.a_bar.dir) + { + case DIR_S: + *x = tx - twidth; + *y = ty; + *width = twidth * 2 + 1; + *height = tdepth; + break; + + case DIR_W: + *x = tx - tdepth; + *y = ty - twidth; + *width = tdepth; + *height = twidth * 2 + 1; + break; + + case DIR_N: + *x = tx - twidth; + *y = ty - tdepth; + *width = twidth * 2 + 1; + *height = tdepth; + break; + + case DIR_E: + *x = tx; + *y = ty - twidth; + *width = tdepth; + *height = twidth * 2 + 1; + break; + + default: + fprintf (stderr, + "Error: Trying to compute area of NE/SE/NW/SW-facing bar"); + *x = tx; + *y = ty; + *width = *height = 1; + } + break; + } + } +} + +int magic_location_in_area (int m, int x, int y, area_t * area) +{ + switch (area->ty) + { + case AREA_UNION: + return magic_location_in_area (m, x, y, area->a.a_union[0]) + || magic_location_in_area (m, x, y, area->a.a_union[1]); + case AREA_LOCATION: + case AREA_RECT: + case AREA_BAR: + { + int am; + int ax, ay, awidth, aheight; + magic_area_rect (&am, &ax, &ay, &awidth, &aheight, area); + return (am == m + && (x >= ax) && (y >= ay) + && (x < ax + awidth) && (y < ay + aheight)); + } + default: + fprintf (stderr, "INTERNAL ERROR: Invalid area\n"); + return 0; + } +} + +static int fun_is_in (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = magic_location_in_area (ARGLOCATION (0).m, + ARGLOCATION (0).x, + ARGLOCATION (0).y, ARGAREA (1)); + return 0; +} + +static int fun_skill (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ETY (0) != BL_PC + || ARGINT (1) < 0 + || ARGINT (1) >= MAX_SKILL + || ARGPC (0)->status.skill[ARGINT (1)].id != ARGINT (1)) + RESULTINT = 0; + else + RESULTINT = ARGPC (0)->status.skill[ARGINT (1)].lv; + return 0; +} + +static int +fun_has_shroud (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (ETY (0) == BL_PC && ARGPC (0)->state.shroud_active); + return 0; +} + +#define BATTLE_GETTER(name) static int fun_get_##name(env_t *env, int args_nr, val_t *result, val_t *args) { RESULTINT = battle_get_##name(ARGENTITY(0)); return 0; } + +BATTLE_GETTER (str); +BATTLE_GETTER (agi); +BATTLE_GETTER (vit); +BATTLE_GETTER (dex); +BATTLE_GETTER (luk); +BATTLE_GETTER (int); +BATTLE_GETTER (lv); +BATTLE_GETTER (hp); +BATTLE_GETTER (mdef); +BATTLE_GETTER (def); +BATTLE_GETTER (max_hp); +BATTLE_GETTER (dir); + +#define MMO_GETTER(name) static int fun_get_##name(env_t *env, int args_nr, val_t *result, val_t *args) { \ + if (ETY(0) == BL_PC) \ + RESULTINT = ARGPC(0)->status.name; \ + else \ + RESULTINT = 0; \ + return 0; } + +MMO_GETTER (sp); +MMO_GETTER (max_sp); + +static int +fun_name_of (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_ENTITY) + { + RESULTSTR = strdup (show_entity (ARGENTITY (0))); + return 0; + } + else if (TY (0) == TY_SPELL) + { + RESULTSTR = strdup (ARGSPELL (0)->name); + return 0; + } + else if (TY (0) == TY_INVOCATION) + { + RESULTSTR = strdup (ARGINVOCATION (0)->spell->name); + return 0; + } + return 1; +} + +/* [Freeyorp] I'm putting this one in as name_of seems to have issues with summoned or spawned mobs. */ +static int +fun_mob_id (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ETY (0) != BL_MOB) return 1; + RESULTINT = ((struct mob_data *) (ARGENTITY(0)))->mob_class; + return 0; +} + +#define COPY_LOCATION(dest, src) (dest).x = (src).x; (dest).y = (src).y; (dest).m = (src).m; + +static int +fun_location (env_t * env, int args_nr, val_t * result, val_t * args) +{ + COPY_LOCATION (RESULTLOCATION, *(ARGENTITY (0))); + return 0; +} + +static int fun_random (env_t * env, int args_nr, val_t * result, val_t * args) +{ + int delta = ARGINT (0); + if (delta < 0) + delta = -delta; + if (delta == 0) + { + RESULTINT = 0; + return 0; + } + RESULTINT = MRAND (delta); + + if (ARGINT (0) < 0) + RESULTINT = -RESULTINT; + return 0; +} + +static int +fun_random_dir (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ARGINT (0)) + RESULTDIR = mt_random () & 0x7; + else + RESULTDIR = (mt_random () & 0x3) * 2; + return 0; +} + +static int +fun_hash_entity (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGENTITY (0)->id; + return 0; +} + +int // ret -1: not a string, ret 1: no such item, ret 0: OK +magic_find_item (val_t * args, int index, struct item *item, int *stackable) +{ + struct item_data *item_data; + int must_add_sequentially; + + if (TY (index) == TY_INT) + item_data = itemdb_exists (ARGINT (index)); + else if (TY (index) == TY_STRING) + item_data = itemdb_searchname (ARGSTR (index)); + else + return -1; + + if (!item_data) + return 1; + + must_add_sequentially = (item_data->type == 4 || item_data->type == 5 || item_data->type == 7 || item_data->type == 8); /* Very elegant. */ + + if (stackable) + *stackable = !must_add_sequentially; + + memset (item, 0, sizeof (struct item)); + item->nameid = item_data->nameid; + item->identify = 1; + + return 0; +} + +static int +fun_count_item (env_t * env, int args_nr, val_t * result, val_t * args) +{ + character_t *chr = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + int stackable; + struct item item; + + GET_ARG_ITEM (1, item, stackable); + + if (!chr) + return 1; + + RESULTINT = pc_count_all_items (chr, item.nameid); + return 0; +} + +static int +fun_is_equipped (env_t * env, int args_nr, val_t * result, val_t * args) +{ + character_t *chr = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + int stackable; + struct item item; + int i; + int retval = 0; + + GET_ARG_ITEM (1, item, stackable); + + if (!chr) + return 1; + + for (i = 0; i < 11; i++) + if (chr->equip_index[i] >= 0 + && chr->status.inventory[chr->equip_index[i]].nameid == + item.nameid) + { + retval = i + 1; + break; + } + + RESULTINT = retval; + return 0; +} + +static int +fun_is_married (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (ETY (0) == BL_PC && ARGPC (0)->status.partner_id); + return 0; +} + +static int +fun_is_dead (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (ETY (0) == BL_PC && pc_isdead (ARGPC (0))); + return 0; +} + +static int fun_is_pc (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (ETY (0) == BL_PC); + return 0; +} + +static int +fun_partner (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ETY (0) == BL_PC && ARGPC (0)->status.partner_id) + { + RESULTENTITY = + (entity_t *) + map_nick2sd (map_charid2nick (ARGPC (0)->status.partner_id)); + return 0; + } + else + return 1; +} + +static int +fun_awayfrom (env_t * env, int args_nr, val_t * result, val_t * args) +{ + location_t *loc = &ARGLOCATION (0); + int dx = heading_x[ARGDIR (1)]; + int dy = heading_y[ARGDIR (1)]; + int distance = ARGINT (2); + while (distance-- && !map_is_solid (loc->m, loc->x + dx, loc->y + dy)) + { + loc->x += dx; + loc->y += dy; + } + + RESULTLOCATION = *loc; + return 0; +} + +static int fun_failed (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = TY (0) == TY_FAIL; + return 0; +} + +static int fun_npc (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTENTITY = (entity_t *) npc_name2id (ARGSTR (0)); + return RESULTENTITY == NULL; +} + +static int fun_pc (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTENTITY = (entity_t *) map_nick2sd (ARGSTR (0)); + return RESULTENTITY == NULL; +} + +static int +fun_distance (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ARGLOCATION (0).m != ARGLOCATION (1).m) + RESULTINT = INT_MAX; + else + RESULTINT = MAX (abs (ARGLOCATION (0).x - ARGLOCATION (1).x), + abs (ARGLOCATION (0).y - ARGLOCATION (1).y)); + return 0; +} + +static int +fun_rdistance (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ARGLOCATION (0).m != ARGLOCATION (1).m) + RESULTINT = INT_MAX; + else + { + int dx = ARGLOCATION (0).x - ARGLOCATION (1).x; + int dy = ARGLOCATION (0).y - ARGLOCATION (1).y; + RESULTINT = (int) (sqrt ((dx * dx) + (dy * dy))); + } + return 0; +} + +static int fun_anchor (env_t * env, int args_nr, val_t * result, val_t * args) +{ + teleport_anchor_t *anchor = magic_find_anchor (ARGSTR (0)); + + if (!anchor) + return 1; + + magic_eval (env, result, anchor->location); + + make_area (result); + if (result->ty != TY_AREA) + { + magic_clear_var (result); + return 1; + } + + return 0; +} + +static int +fun_line_of_sight (env_t * env, int args_nr, val_t * result, val_t * args) +{ + entity_t e1, e2; + + COPY_LOCATION (e1, ARGLOCATION (0)); + COPY_LOCATION (e2, ARGLOCATION (1)); + + RESULTINT = battle_check_range (&e1, &e2, 0); + + return 0; +} + +void magic_random_location (location_t * dest, area_t * area) +{ + switch (area->ty) + { + case AREA_UNION: + { + int rv = MRAND (area->size); + if (rv < area->a.a_union[0]->size) + magic_random_location (dest, area->a.a_union[0]); + else + magic_random_location (dest, area->a.a_union[1]); + break; + } + + case AREA_LOCATION: + case AREA_RECT: + case AREA_BAR: + { + int m, x, y, w, h; + magic_area_rect (&m, &x, &y, &w, &h, area); + + if (w <= 1) + w = 1; + + if (h <= 1) + h = 1; + + x += MRAND (w); + y += MRAND (h); + + if (!map_is_solid (m, x, y)) + { + int start_x = x; + int start_y = y; + int i; + int initial_dir = mt_random () & 0x7; + int dir = initial_dir; + + /* try all directions, up to a distance to 10, for a free slot */ + do + { + x = start_x; + y = start_y; + + for (i = 0; i < 10 && map_is_solid (m, x, y); i++) + { + x += heading_x[dir]; + y += heading_y[dir]; + } + + dir = (dir + 1) & 0x7; + } + while (map_is_solid (m, x, y) && dir != initial_dir); + + } + /* We've tried our best. If the map is still solid, the engine will automatically randomise the target location if we try to warp. */ + + dest->m = m; + dest->x = x; + dest->y = y; + break; + } + + default: + fprintf (stderr, "Unknown area type %d\n", area->ty); + } +} + +static int +fun_pick_location (env_t * env, int args_nr, val_t * result, val_t * args) +{ + magic_random_location (&result->v.v_location, ARGAREA (0)); + return 0; +} + +static int +fun_read_script_int (env_t * env, int args_nr, val_t * result, val_t * args) +{ + entity_t *subject_p = ARGENTITY (0); + char *var_name = ARGSTR (1); + + if (subject_p->type != BL_PC) + return 1; + + RESULTINT = pc_readglobalreg ((character_t *) subject_p, var_name); + return 0; +} + +static int fun_rbox (env_t * env, int args_nr, val_t * result, val_t * args) +{ + location_t loc = ARGLOCATION (0); + int radius = ARGINT (1); + + RESULTAREA = area_new (AREA_RECT); + RESULTAREA->a.a_rect.loc.m = loc.m; + RESULTAREA->a.a_rect.loc.x = loc.x - radius; + RESULTAREA->a.a_rect.loc.y = loc.y - radius; + RESULTAREA->a.a_rect.width = radius * 2 + 1; + RESULTAREA->a.a_rect.height = radius * 2 + 1; + + return 0; +} + +static int +fun_running_status_update (env_t * env, int args_nr, val_t * result, + val_t * args) +{ + if (ETY (0) != BL_PC && ETY (0) != BL_MOB) + return 1; + + RESULTINT = battle_get_sc_data (ARGENTITY (0))[ARGINT (1)].timer != -1; + return 0; +} + +static int +fun_status_option (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = + ((((struct map_session_data *) ARGENTITY (0))-> + status.option & ARGINT (0)) != 0); + return 0; +} + +static int +fun_element (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = battle_get_element (ARGENTITY (0)) % 10; + return 0; +} + +static int +fun_element_level (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = battle_get_element (ARGENTITY (0)) / 10; + return 0; +} + +static int fun_index (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGSPELL (0)->index; + return 0; +} + +static int +fun_is_exterior (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = map[ARGLOCATION (0).m].name[4] == '1'; + return 0; +} + +static int +fun_contains_string (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = NULL != strstr (ARGSTR (0), ARGSTR (1)); + return 0; +} + +static int fun_strstr (env_t * env, int args_nr, val_t * result, val_t * args) +{ + char *offset = strstr (ARGSTR (0), ARGSTR (1)); + RESULTINT = offset - ARGSTR (0); + return offset == NULL; +} + +static int fun_strlen (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = strlen (ARGSTR (0)); + return 0; +} + +static int fun_substr (env_t * env, int args_nr, val_t * result, val_t * args) +{ + const char *src = ARGSTR (0); + const int slen = strlen (src); + int offset = ARGINT (1); + int len = ARGINT (2); + + if (len < 0) + len = 0; + if (offset < 0) + offset = 0; + + if (offset > slen) + offset = slen; + + if (offset + len > slen) + len = slen - offset; + + RESULTSTR = (char *) calloc (1, 1 + len); + memcpy (RESULTSTR, src + offset, len); + + return 0; +} + +static int fun_sqrt (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (int) sqrt (ARGINT (0)); + return 0; +} + +static int +fun_map_level (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = map[ARGLOCATION (0).m].name[4] - '0'; + return 0; +} + +static int fun_map_nr (env_t * env, int args_nr, val_t * result, val_t * args) +{ + const char *mapname = map[ARGLOCATION (0).m].name; + + RESULTINT = ((mapname[0] - '0') * 100) + + ((mapname[1] - '0') * 10) + ((mapname[2] - '0')); + return 0; +} + +static int +fun_dir_towards (env_t * env, int args_nr, val_t * result, val_t * args) +{ + int dx; + int dy; + + if (ARGLOCATION (0).m != ARGLOCATION (1).m) + return 1; + + dx = ARGLOCATION (1).x - ARGLOCATION (0).x; + dy = ARGLOCATION (1).y - ARGLOCATION (0).y; + + if (ARGINT (1)) + { + /* 8-direction mode */ + if (abs (dx) > abs (dy) * 2) + { /* east or west */ + if (dx < 0) + RESULTINT = 2 /* west */ ; + else + RESULTINT = 6 /* east */ ; + } + else if (abs (dy) > abs (dx) * 2) + { /* north or south */ + if (dy > 0) + RESULTINT = 0 /* south */ ; + else + RESULTINT = 4 /* north */ ; + } + else if (dx < 0) + { /* north-west or south-west */ + if (dy < 0) + RESULTINT = 3 /* north-west */ ; + else + RESULTINT = 1 /* south-west */ ; + } + else + { /* north-east or south-east */ + if (dy < 0) + RESULTINT = 5 /* north-east */ ; + else + RESULTINT = 7 /* south-east */ ; + } + } + else + { + /* 4-direction mode */ + if (abs (dx) > abs (dy)) + { /* east or west */ + if (dx < 0) + RESULTINT = 2 /* west */ ; + else + RESULTINT = 6 /* east */ ; + } + else + { /* north or south */ + if (dy > 0) + RESULTINT = 0 /* south */ ; + else + RESULTINT = 4 /* north */ ; + } + } + + return 0; +} + +static int +fun_extract_healer_xp (env_t * env, int args_nr, val_t * result, val_t * args) +{ + character_t *sd = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + + if (!sd) + RESULTINT = 0; + else + RESULTINT = pc_extract_healer_exp (sd, ARGINT (1)); + return 0; +} + +#define BATTLE_RECORD2(sname, name) { sname, "e", 'i', fun_get_##name } +#define BATTLE_RECORD(name) BATTLE_RECORD2(#name, name) +static fun_t functions[] = { + {"+", "..", '.', fun_add}, + {"-", "ii", 'i', fun_sub}, + {"*", "ii", 'i', fun_mul}, + {"/", "ii", 'i', fun_div}, + {"%", "ii", 'i', fun_mod}, + {"||", "ii", 'i', fun_or}, + {"&&", "ii", 'i', fun_and}, + {">", "..", 'i', fun_gt}, + {">=", "..", 'i', fun_gte}, + {"=", "..", 'i', fun_eq}, + {"|", "..", 'i', fun_bitor}, + {"&", "ii", 'i', fun_bitand}, + {"^", "ii", 'i', fun_bitxor}, + {"<<", "ii", 'i', fun_bitshl}, + {">>", "ii", 'i', fun_bitshr}, + {"not", "i", 'i', fun_not}, + {"neg", "i", 'i', fun_neg}, + {"max", "ii", 'i', fun_max}, + {"min", "ii", 'i', fun_min}, + {"is_in", "la", 'i', fun_is_in}, + {"if_then_else", "i__", '_', fun_if_then_else}, + {"skill", "ei", 'i', fun_skill}, + BATTLE_RECORD (str), + BATTLE_RECORD (agi), + BATTLE_RECORD (vit), + BATTLE_RECORD (dex), + BATTLE_RECORD (luk), + BATTLE_RECORD (int), + BATTLE_RECORD2 ("level", lv), + BATTLE_RECORD (mdef), + BATTLE_RECORD (def), + BATTLE_RECORD (hp), + BATTLE_RECORD (max_hp), + BATTLE_RECORD (sp), + BATTLE_RECORD (max_sp), + {"dir", "e", 'd', fun_get_dir}, + {"name_of", ".", 's', fun_name_of}, + {"mob_id", "e", 'i', fun_mob_id}, + {"location", "e", 'l', fun_location}, + {"random", "i", 'i', fun_random}, + {"random_dir", "i", 'd', fun_random_dir}, + {"hash_entity", "e", 'i', fun_hash_entity}, + {"is_married", "e", 'i', fun_is_married}, + {"partner", "e", 'e', fun_partner}, + {"awayfrom", "ldi", 'l', fun_awayfrom}, + {"failed", "_", 'i', fun_failed}, + {"pc", "s", 'e', fun_pc}, + {"npc", "s", 'e', fun_npc}, + {"distance", "ll", 'i', fun_distance}, + {"rdistance", "ll", 'i', fun_rdistance}, + {"anchor", "s", 'a', fun_anchor}, + {"random_location", "a", 'l', fun_pick_location}, + {"script_int", "es", 'i', fun_read_script_int}, + {"rbox", "li", 'a', fun_rbox}, + {"count_item", "e.", 'i', fun_count_item}, + {"line_of_sight", "ll", 'i', fun_line_of_sight}, + {"running_status_update", "ei", 'i', fun_running_status_update}, + {"status_option", "ei", 'i', fun_status_option}, + {"element", "e", 'i', fun_element}, + {"element_level", "e", 'i', fun_element_level}, + {"has_shroud", "e", 'i', fun_has_shroud}, + {"is_equipped", "e.", 'i', fun_is_equipped}, + {"spell_index", "S", 'i', fun_index}, + {"is_exterior", "l", 'i', fun_is_exterior}, + {"contains_string", "ss", 'i', fun_contains_string}, + {"strstr", "ss", 'i', fun_strstr}, + {"strlen", "s", 'i', fun_strlen}, + {"substr", "sii", 's', fun_substr}, + {"sqrt", "i", 'i', fun_sqrt}, + {"map_level", "l", 'i', fun_map_level}, + {"map_nr", "l", 'i', fun_map_nr}, + {"dir_towards", "lli", 'd', fun_dir_towards}, + {"is_dead", "e", 'i', fun_is_dead}, + {"is_pc", "e", 'i', fun_is_pc}, + {"extract_healer_experience", "ei", 'i', fun_extract_healer_xp}, + {NULL, NULL, '.', NULL} +}; + +static int functions_are_sorted = 0; + +int compare_fun (const void *lhs, const void *rhs) +{ + return strcmp (((fun_t *) lhs)->name, ((fun_t *) rhs)->name); +} + +fun_t *magic_get_fun (const char *name, int *index) +{ + static int functions_nr; + fun_t *result; + fun_t key; + + if (!functions_are_sorted) + { + fun_t *it = functions; + + while (it->name) + ++it; + functions_nr = it - functions; + + qsort (functions, functions_nr, sizeof (fun_t), compare_fun); + functions_are_sorted = 1; + } + + key.name = name; + result = (fun_t *) bsearch (&key, functions, functions_nr, sizeof (fun_t), + compare_fun); + + if (result && index) + *index = result - functions; + + return result; +} + +static int // 1 on failure +eval_location (env_t * env, location_t * dest, e_location_t * expr) +{ + val_t m, x, y; + magic_eval (env, &m, expr->m); + magic_eval (env, &x, expr->x); + magic_eval (env, &y, expr->y); + + if (CHECK_TYPE (&m, TY_STRING) + && CHECK_TYPE (&x, TY_INT) && CHECK_TYPE (&y, TY_INT)) + { + int map_id = map_mapname2mapid (m.v.v_string); + magic_clear_var (&m); + if (map_id < 0) + return 1; + dest->m = map_id; + dest->x = x.v.v_int; + dest->y = y.v.v_int; + return 0; + } + else + { + magic_clear_var (&m); + magic_clear_var (&x); + magic_clear_var (&y); + return 1; + } +} + +static area_t *eval_area (env_t * env, e_area_t * expr) +{ + area_t *area = (area_t *)malloc (sizeof (area_t)); + area->ty = expr->ty; + + switch (expr->ty) + { + case AREA_LOCATION: + area->size = 1; + if (eval_location (env, &area->a.a_loc, &expr->a.a_loc)) + { + free (area); + return NULL; + } + else + return area; + + case AREA_UNION: + { + int i, fail = 0; + for (i = 0; i < 2; i++) + { + area->a.a_union[i] = eval_area (env, expr->a.a_union[i]); + if (!area->a.a_union[i]) + fail = 1; + } + + if (fail) + { + for (i = 0; i < 2; i++) + { + if (area->a.a_union[i]) + free_area (area->a.a_union[i]); + } + free (area); + return NULL; + } + area->size = area->a.a_union[0]->size + area->a.a_union[1]->size; + return area; + } + + case AREA_RECT: + { + val_t width, height; + magic_eval (env, &width, expr->a.a_rect.width); + magic_eval (env, &height, expr->a.a_rect.height); + + area->a.a_rect.width = width.v.v_int; + area->a.a_rect.height = height.v.v_int; + + if (CHECK_TYPE (&width, TY_INT) + && CHECK_TYPE (&height, TY_INT) + && !eval_location (env, &(area->a.a_rect.loc), + &expr->a.a_rect.loc)) + { + area->size = area->a.a_rect.width * area->a.a_rect.height; + magic_clear_var (&width); + magic_clear_var (&height); + return area; + } + else + { + free (area); + magic_clear_var (&width); + magic_clear_var (&height); + return NULL; + } + } + + case AREA_BAR: + { + val_t width, depth, dir; + magic_eval (env, &width, expr->a.a_bar.width); + magic_eval (env, &depth, expr->a.a_bar.depth); + magic_eval (env, &dir, expr->a.a_bar.dir); + + area->a.a_bar.width = width.v.v_int; + area->a.a_bar.depth = depth.v.v_int; + area->a.a_bar.dir = dir.v.v_int; + + if (CHECK_TYPE (&width, TY_INT) + && CHECK_TYPE (&depth, TY_INT) + && CHECK_TYPE (&dir, TY_DIR) + && !eval_location (env, &area->a.a_bar.loc, + &expr->a.a_bar.loc)) + { + area->size = + (area->a.a_bar.width * 2 + 1) * area->a.a_bar.depth; + magic_clear_var (&width); + magic_clear_var (&depth); + magic_clear_var (&dir); + return area; + } + else + { + free (area); + magic_clear_var (&width); + magic_clear_var (&depth); + magic_clear_var (&dir); + return NULL; + } + } + + default: + fprintf (stderr, "INTERNAL ERROR: Unknown area type %d\n", + area->ty); + free (area); + return NULL; + } +} + +static int type_key (char ty_key) +{ + switch (ty_key) + { + case 'i': + return TY_INT; + case 'd': + return TY_DIR; + case 's': + return TY_STRING; + case 'e': + return TY_ENTITY; + case 'l': + return TY_LOCATION; + case 'a': + return TY_AREA; + case 'S': + return TY_SPELL; + case 'I': + return TY_INVOCATION; + default: + return -1; + } +} + +int +magic_signature_check (char *opname, char *funname, char *signature, + int args_nr, val_t * args, int line, int column) +{ + int i; + for (i = 0; i < args_nr; i++) + { + val_t *arg = &args[i]; + char ty_key = signature[i]; + int ty = arg->ty; + int desired_ty = type_key (ty_key); + + if (ty == TY_ENTITY) + { + /* Dereference entities in preparation for calling function */ + arg->v.v_entity = map_id2bl (arg->v.v_int); + if (!arg->v.v_entity) + ty = arg->ty = TY_FAIL; + } + else if (ty == TY_INVOCATION) + { + arg->v.v_invocation = (invocation_t *) map_id2bl (arg->v.v_int); + if (!arg->v.v_entity) + ty = arg->ty = TY_FAIL; + } + + if (!ty_key) + { + fprintf (stderr, + "[magic-eval]: L%d:%d: Too many arguments (%d) to %s `%s'\n", + line, column, args_nr, opname, funname); + return 1; + } + + if (ty == TY_FAIL && ty_key != '_') + return 1; /* Fail `in a sane way': This is a perfectly permissible error */ + + if (ty == desired_ty || desired_ty < 0 /* `dontcare' */ ) + continue; + + if (ty == TY_UNDEF) + { + fprintf (stderr, + "[magic-eval]: L%d:%d: Argument #%d to %s `%s' undefined\n", + line, column, i + 1, opname, funname); + return 1; + } + + /* If we are here, we have a type mismatch but no failure _yet_. Try to coerce. */ + switch (desired_ty) + { + case TY_INT: + intify (arg); + break; /* 100% success rate */ + case TY_STRING: + stringify (arg, 1); + break; /* 100% success rate */ + case TY_AREA: + make_area (arg); + break; /* Only works for locations */ + case TY_LOCATION: + make_location (arg); + break; /* Only works for some areas */ + case TY_SPELL: + make_spell (arg); + break; /* Only works for still-active invocatoins */ + default: + break; /* We'll fail right below */ + } + + ty = arg->ty; + if (ty != desired_ty) + { /* Coercion failed? */ + if (ty != TY_FAIL) + fprintf (stderr, + "[magic-eval]: L%d:%d: Argument #%d to %s `%s' of incorrect type (%d)\n", + line, column, i + 1, opname, funname, ty); + return 1; + } + } + + return 0; +} + +void magic_eval (env_t * env, val_t * dest, expr_t * expr) +{ + switch (expr->ty) + { + case EXPR_VAL: + magic_copy_var (dest, &expr->e.e_val); + break; + + case EXPR_LOCATION: + if (eval_location (env, &dest->v.v_location, &expr->e.e_location)) + dest->ty = TY_FAIL; + else + dest->ty = TY_LOCATION; + break; + + case EXPR_AREA: + if ((dest->v.v_area = eval_area (env, &expr->e.e_area))) + dest->ty = TY_AREA; + else + dest->ty = TY_FAIL; + break; + + case EXPR_FUNAPP: + { + val_t arguments[MAX_ARGS]; + int args_nr = expr->e.e_funapp.args_nr; + int i; + fun_t *f = functions + expr->e.e_funapp.id; + + for (i = 0; i < args_nr; ++i) + magic_eval (env, &arguments[i], expr->e.e_funapp.args[i]); + if (magic_signature_check + ("function", f->name, f->signature, args_nr, arguments, + expr->e.e_funapp.line_nr, expr->e.e_funapp.column) + || f->fun (env, args_nr, dest, arguments)) + dest->ty = TY_FAIL; + else + { + int dest_ty = type_key (f->ret_ty); + if (dest_ty != -1) + dest->ty = dest_ty; + + /* translate entity back into persistent int */ + if (dest->ty == TY_ENTITY) + { + if (dest->v.v_entity) + dest->v.v_int = dest->v.v_entity->id; + else + dest->ty = TY_FAIL; + } + } + + for (i = 0; i < args_nr; ++i) + magic_clear_var (&arguments[i]); + break; + } + + case EXPR_ID: + { + val_t v = VAR (expr->e.e_id); + magic_copy_var (dest, &v); + break; + } + + case EXPR_SPELLFIELD: + { + val_t v; + int id = expr->e.e_field.id; + magic_eval (env, &v, expr->e.e_field.expr); + + if (v.ty == TY_INVOCATION) + { + invocation_t *t = (invocation_t *) map_id2bl (v.v.v_int); + + if (!t) + dest->ty = TY_UNDEF; + else + { + env_t *env = t->env; + val_t v = VAR (id); + magic_copy_var (dest, &v); + } + } + else + { + fprintf (stderr, + "[magic] Attempt to access field %s on non-spell\n", + env->base_env->var_name[id]); + dest->ty = TY_FAIL; + } + break; + } + + default: + fprintf (stderr, + "[magic] INTERNAL ERROR: Unknown expression type %d\n", + expr->ty); + break; + } +} + +int magic_eval_int (env_t * env, expr_t * expr) +{ + val_t result; + magic_eval (env, &result, expr); + + if (result.ty == TY_FAIL || result.ty == TY_UNDEF) + return 0; + + intify (&result); + + return result.v.v_int; +} + +char *magic_eval_str (env_t * env, expr_t * expr) +{ + val_t result; + magic_eval (env, &result, expr); + + if (result.ty == TY_FAIL || result.ty == TY_UNDEF) + return strdup ("?"); + + stringify (&result, 0); + + return result.v.v_string; +} + +expr_t *magic_new_expr (int ty) +{ + expr_t *expr = (expr_t *) malloc (sizeof (expr_t)); + expr->ty = ty; + return expr; +} -- cgit v1.2.3-60-g2f50