From 506a41d6926405b2753894f0b40130b4077828b3 Mon Sep 17 00:00:00 2001 From: wushin Date: Tue, 9 Jun 2015 01:06:22 -0500 Subject: Remove old Magic --- src/map/clif.cpp | 13 +- src/map/fwd.hpp | 18 - src/map/globals.cpp | 19 +- src/map/globals.hpp | 14 +- src/map/magic-expr-eval.hpp | 54 -- src/map/magic-expr.cpp | 1874 ------------------------------------ src/map/magic-expr.hpp | 107 -- src/map/magic-expr.py | 38 - src/map/magic-interpreter-base.cpp | 553 ----------- src/map/magic-interpreter-base.hpp | 84 -- src/map/magic-interpreter.hpp | 630 ------------ src/map/magic-interpreter.py | 215 ----- src/map/magic-interpreter.t.hpp | 85 -- src/map/magic-stmt.cpp | 1547 ----------------------------- src/map/magic-stmt.hpp | 93 -- src/map/magic-stmt.py | 37 - src/map/magic-v2.cpp | 1295 ------------------------- src/map/magic-v2.hpp | 37 - src/map/magic.cpp | 130 --- src/map/magic.hpp | 48 - src/map/map.cpp | 20 +- src/map/map.hpp | 15 +- src/map/map.t.hpp | 1 - src/map/npc.cpp | 58 +- src/map/pc.cpp | 9 +- src/map/script-fun.cpp | 38 +- src/map/skill.cpp | 13 - src/mmo/cxxstdio_enums.hpp | 7 - src/mmo/skill.t.hpp | 4 - 29 files changed, 61 insertions(+), 6995 deletions(-) delete mode 100644 src/map/magic-expr-eval.hpp delete mode 100644 src/map/magic-expr.cpp delete mode 100644 src/map/magic-expr.hpp delete mode 100644 src/map/magic-expr.py delete mode 100644 src/map/magic-interpreter-base.cpp delete mode 100644 src/map/magic-interpreter-base.hpp delete mode 100644 src/map/magic-interpreter.hpp delete mode 100644 src/map/magic-interpreter.py delete mode 100644 src/map/magic-interpreter.t.hpp delete mode 100644 src/map/magic-stmt.cpp delete mode 100644 src/map/magic-stmt.hpp delete mode 100644 src/map/magic-stmt.py delete mode 100644 src/map/magic-v2.cpp delete mode 100644 src/map/magic-v2.hpp delete mode 100644 src/map/magic.cpp delete mode 100644 src/map/magic.hpp (limited to 'src') diff --git a/src/map/clif.cpp b/src/map/clif.cpp index a2a2a33..33de3e6 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -62,8 +62,6 @@ #include "globals.hpp" #include "intif.hpp" #include "itemdb.hpp" -#include "magic.hpp" -#include "magic-stmt.hpp" #include "map.hpp" #include "map_conf.hpp" #include "npc.hpp" @@ -2641,12 +2639,6 @@ void clif_getareachar(dumb_ptr bl, dumb_ptr sd) case BL::ITEM: clif_getareachar_item(sd, bl->is_item()); break; - case BL::SPELL: - // spell objects are not visible - // (at least, I *think* that's what this code is for) - // in any case, this is not a behavior change, just silencing - // the below warning - break; default: if (battle_config.error_log) PRINTF("get area char ??? %d\n"_fmt, @@ -3792,7 +3784,6 @@ RecvResult clif_parse_GetCharNameRequest(Session *s, dumb_ptr send_fpacket<0x0095, 30>(s, fixed_95); } break; - // case BL::SPELL default: if (battle_config.error_log) PRINTF("clif_parse_GetCharNameRequest : bad type %d (%d)\n"_fmt, @@ -4273,8 +4264,8 @@ RecvResult clif_parse_TakeItem(Session *s, dumb_ptr sd) || abs(sd->bl_y - fitem->bl_y) >= 2) return rv; // too far away to pick up - if (sd->state.shroud_active && sd->state.shroud_disappears_on_pickup) - magic::magic_unshroud(sd); +// if (sd->state.shroud_active && sd->state.shroud_disappears_on_pickup) +// magic_unshroud(sd); pc_takeitem(sd, fitem); diff --git a/src/map/fwd.hpp b/src/map/fwd.hpp index 911d566..de65216 100644 --- a/src/map/fwd.hpp +++ b/src/map/fwd.hpp @@ -57,7 +57,6 @@ struct map_session_data; struct npc_data; struct mob_data; struct flooritem_data; -//struct magic::invocation; struct map_local; class npc_data_script; class npc_data_shop; @@ -71,22 +70,5 @@ struct ScriptState; struct str_data_t; class SIR; -namespace magic -{ -struct fun_t; -struct op_t; -struct expr_t; -struct val_t; -struct location_t; -struct area_t; -struct spell_t; -struct invocation; -struct teleport_anchor_t; -struct env_t; -struct magic_conf_t; -struct component_t; -struct effect_set_t; -struct proc_t; -} // namespace magic } // namespace map } // namespace tmwa diff --git a/src/map/globals.cpp b/src/map/globals.cpp index 4a54843..c96037e 100644 --- a/src/map/globals.cpp +++ b/src/map/globals.cpp @@ -27,7 +27,6 @@ #include "battle_conf.hpp" #include "itemdb.hpp" #include "quest.hpp" -#include "magic-interpreter.hpp" #include "map_conf.hpp" #include "mob.hpp" #include "npc-internal.hpp" @@ -51,17 +50,6 @@ namespace tmwa std::map resnametable; Map item_db; Map quest_db; - namespace magic - { - // Global magic conf - magic_conf_t magic_conf; - env_t magic_default_env = { &magic_conf, nullptr }; - namespace magic_v2 - { - std::map procs; - std::map const_defm; - } // namespace magic_v2 - } // namespace magic DMap> id_db; UPMap maps_db; @@ -85,7 +73,8 @@ namespace tmwa BlockId npc_id = START_NPC_NUM; Map ev_db; DMap> npcs_by_name; - DMap spells_by_name; + DMap spells_by_name; + DMap spells_by_events; // used for clock-based event triggers // only tm_min, tm_hour, and tm_mday are used tm ev_tm_b = @@ -142,9 +131,5 @@ namespace tmwa // BuiltinFunction builtin_functions[]; // src/map/clif.cpp: // func_table clif_parse_func_table[0x0220]; - // src/map/magic-expr.cpp: - // std::map functions; - // src/map/magic-stmt.cpp: - // std::map operations; } // namespace map } // namespace tmwa diff --git a/src/map/globals.hpp b/src/map/globals.hpp index 5a4ec82..84e4765 100644 --- a/src/map/globals.hpp +++ b/src/map/globals.hpp @@ -49,17 +49,6 @@ namespace tmwa extern std::map resnametable; extern Map item_db; extern Map quest_db; - namespace magic - { - // Global magic conf - extern magic_conf_t magic_conf; - extern env_t magic_default_env; - namespace magic_v2 - { - extern std::map procs; - extern std::map const_defm; - } // namespace magic_v2 - } // namespace magic extern DMap> id_db; extern UPMap maps_db; extern DMap> nick_db; @@ -79,7 +68,8 @@ namespace tmwa extern BlockId npc_id; extern Map ev_db; extern DMap> npcs_by_name; - extern DMap spells_by_name; + extern DMap spells_by_name; + extern DMap spells_by_events; extern tm ev_tm_b; extern Map party_db; extern std::map gm_accountm; diff --git a/src/map/magic-expr-eval.hpp b/src/map/magic-expr-eval.hpp deleted file mode 100644 index e8ed4aa..0000000 --- a/src/map/magic-expr-eval.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -// magic-expr-eval.hpp - Utilities for evaluating magic. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "fwd.hpp" - -#include "../strings/zstring.hpp" - -#include "magic-interpreter.t.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -// TODO soon kill this unlike I killed VAR -#define ARGINT(x) args[x].get_if()->v_int -#define ARGDIR(x) args[x].get_if()->v_dir -#define ARGSTR(x) ZString(args[x].get_if()->v_string) -#define ARGENTITY(x) args[x].get_if()->v_entity -#define ARGLOCATION(x) args[x].get_if()->v_location -#define ARGAREA(x) args[x].get_if()->v_area -#define ARGSPELL(x) args[x].get_if()->v_spell -#define ARGINVOCATION(x) args[x].get_if()->v_invocation - -#define ENTITY_TYPE(x) ARGENTITY(x)->bl_type - -#define ARGPC(x) (ARGENTITY(x)->is_player()) -#define ARGNPC(x) (ARGENTITY(x)->is_npc()) -#define ARGMOB(x) (ARGENTITY(x)->is_mob()) - -#define ARG_MAY_BE_AREA(x) (args[x].is() || args[x].is()) -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-expr.cpp b/src/map/magic-expr.cpp deleted file mode 100644 index 197727e..0000000 --- a/src/map/magic-expr.cpp +++ /dev/null @@ -1,1874 +0,0 @@ -#include "magic-expr.hpp" -// magic-expr.cpp - Pure functions for the old magic backend. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include - -#include - -#include "../strings/mstring.hpp" -#include "../strings/astring.hpp" -#include "../strings/zstring.hpp" -#include "../strings/vstring.hpp" -#include "../strings/literal.hpp" - -#include "../generic/dumb_ptr.hpp" -#include "../generic/random.hpp" - -#include "../io/cxxstdio.hpp" - -#include "../mmo/cxxstdio_enums.hpp" - -#include "battle.hpp" -#include "itemdb.hpp" -#include "magic-expr-eval.hpp" -#include "magic-interpreter.hpp" -#include "magic-interpreter-base.hpp" -#include "npc.hpp" -#include "pc.hpp" -#include "script-call.hpp" - -#include "../poison.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -static -void free_area(dumb_ptr area) -{ - if (!area) - return; - - MATCH_BEGIN (*area) - { - MATCH_CASE (const AreaUnion&, a) - { - free_area(a.a_union[0]); - free_area(a.a_union[1]); - } - } - MATCH_END (); - - area.delete_(); -} - -static -dumb_ptr dup_area(dumb_ptr area) -{ - MATCH_BEGIN (*area) - { - MATCH_CASE (const location_t&, loc) - { - return dumb_ptr::make(loc); - } - MATCH_CASE (const AreaUnion&, a) - { - AreaUnion u; - u.a_union[0] = dup_area(a.a_union[0]); - u.a_union[1] = dup_area(a.a_union[1]); - return dumb_ptr::make(u); - } - MATCH_CASE (const AreaRect&, rect) - { - return dumb_ptr::make(rect); - } - MATCH_CASE (const AreaBar&, bar) - { - return dumb_ptr::make(bar); - } - } - MATCH_END (); - - abort(); -} - -void magic_copy_var(val_t *dest, const val_t *src) -{ - MATCH_BEGIN (*src) - { - MATCH_DEFAULT () - { - abort(); - } - MATCH_CASE (const ValUndef&, s) - { - *dest = s; - } - MATCH_CASE (const ValInt&, s) - { - *dest = s; - } - MATCH_CASE (const ValDir&, s) - { - *dest = s; - } - MATCH_CASE (const ValString&, s) - { - *dest = ValString{s.v_string}; - } - MATCH_CASE (const ValEntityInt&, s) - { - *dest = s; - } - MATCH_CASE (const ValEntityPtr&, s) - { - *dest = s; - } - MATCH_CASE (const ValLocation&, s) - { - *dest = s; - } - MATCH_CASE (const ValArea&, s) - { - *dest = ValArea{dup_area(s.v_area)}; - } - MATCH_CASE (const ValSpell&, s) - { - *dest = s; - } - MATCH_CASE (const ValInvocationInt&, s) - { - *dest = s; - } - MATCH_CASE (const ValInvocationPtr&, s) - { - *dest = s; - } - MATCH_CASE (const ValFail&, s) - { - *dest = s; - } - MATCH_CASE (const ValNegative1&, s) - { - *dest = s; - } - } - MATCH_END (); -} - -void magic_clear_var(val_t *v) -{ - MATCH_BEGIN (*v) - { - MATCH_CASE (ValString&, s) - { - (void)s; - } - MATCH_CASE (const ValArea&, a) - { - free_area(a.v_area); - } - } - MATCH_END (); -} - -static -AString show_entity(dumb_ptr entity) -{ - switch (entity->bl_type) - { - case BL::PC: - return entity->is_player()->status_key.name.to__actual(); - case BL::NPC: - return entity->is_npc()->name; - case BL::MOB: - return entity->is_mob()->name; - case BL::ITEM: - assert (0 && "There is no way this code did what it was supposed to do!"_s); - /* Sorry about this one... */ - // WTF? item_data is a Item, not a struct item_data - // return ((struct item_data *) (&entity->is_item()->item_data))->name; - abort(); - case BL::SPELL: - return "%invocation(ERROR:this-should-not-be-an-entity)"_s; - default: - return "%unknown-entity"_s; - } -} - -static -void stringify(val_t *v) -{ - static earray dirs //= - {{ - "south"_s, "south-west"_s, - "west"_s, "north-west"_s, - "north"_s, "north-east"_s, - "east"_s, "south-east"_s, - }}; - AString buf; - - MATCH_BEGIN (*v) - { - MATCH_DEFAULT () - { - abort(); - } - MATCH_CASE (const ValUndef&, x) - { - (void)x; - buf = "UNDEF"_s; - } - MATCH_CASE (const ValInt&, x) - { - buf = STRPRINTF("%i"_fmt, x.v_int); - } - MATCH_CASE (const ValString&, x) - { - (void)x; - return; - } - MATCH_CASE (const ValDir&, x) - { - buf = dirs[x.v_dir]; - } - MATCH_CASE (const ValEntityPtr&, x) - { - buf = show_entity(x.v_entity); - } - MATCH_CASE (const ValLocation&, x) - { - buf = STRPRINTF("<\"%s\", %d, %d>"_fmt, - x.v_location.m->name_, - x.v_location.x, - x.v_location.y); - } - MATCH_CASE (const ValArea&, x) - { - buf = "%area"_s; - free_area(x.v_area); - } - MATCH_CASE (const ValSpell&, x) - { - buf = x.v_spell->name; - } - MATCH_CASE (const ValInvocationInt&, x) - { - dumb_ptr invocation_ = - map_id2bl(x.v_iid)->is_spell(); - buf = invocation_->spell->name; - } - MATCH_CASE (const ValInvocationPtr&, x) - { - dumb_ptr invocation_ = - x.v_invocation; - buf = invocation_->spell->name; - } - } - MATCH_END (); - - *v = ValString{buf}; -} - -static -void intify(val_t *v) -{ - if (v->is()) - return; - - magic_clear_var(v); - *v = ValInt{1}; -} - -static -dumb_ptr area_union(dumb_ptr area, dumb_ptr other_area) -{ - AreaUnion a; - a.a_union[0] = area; - a.a_union[1] = other_area; - return dumb_ptr::make(a); -} - -/** - * Turns location into area, leaves other types untouched - */ -static -void make_area(val_t *v) -{ - if (ValLocation *l = v->get_if()) - { - auto a = dumb_ptr::make(l->v_location); - *v = ValArea{a}; - } -} - -static -void make_location(val_t *v) -{ - if (ValArea *a = v->get_if()) - { - MATCH_BEGIN (*a->v_area) - { - MATCH_CASE (const location_t&, location) - { - free_area(a->v_area); - *v = ValLocation{location}; - } - } - MATCH_END (); - } -} - -static -void make_spell(val_t *v) -{ - assert(!v->is()); - if (ValInvocationPtr *p = v->get_if()) - { - dumb_ptr invoc = p->v_invocation; - if (!invoc) - { - *v = ValFail{}; - } - else - { - *v = ValSpell{invoc->spell}; - } - } -} - -static -int fun_add(dumb_ptr, val_t *result, Slice args) -{ - if (args[0].is() && args[1].is()) - { - /* Integer addition */ - *result = ValInt{ARGINT(0) + ARGINT(1)}; - } - else if (ARG_MAY_BE_AREA(0) && ARG_MAY_BE_AREA(1)) - { - /* Area union */ - make_area(&args[0]); - make_area(&args[1]); - *result = ValArea{area_union(ARGAREA(0), ARGAREA(1))}; - ARGAREA(0) = nullptr; args[0] = ValUndef{}; - ARGAREA(1) = nullptr; args[1] = ValUndef{}; - } - else - { - /* Anything else -> string concatenation */ - stringify(&args[0]); - stringify(&args[1]); - /* Yes, we could speed this up. */ - // ugh - MString m; - m += ARGSTR(0); - m += ARGSTR(1); - *result = ValString{AString(m)}; - } - return 0; -} - -static -int fun_sub(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{ARGINT(0) - ARGINT(1)}; - return 0; -} - -static -int fun_mul(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{ARGINT(0) * ARGINT(1)}; - return 0; -} - -static -int fun_div(dumb_ptr, val_t *result, Slice args) -{ - if (!ARGINT(1)) - return 1; /* division by zero */ - *result = ValInt{ARGINT(0) / ARGINT(1)}; - return 0; -} - -static -int fun_mod(dumb_ptr, val_t *result, Slice args) -{ - if (!ARGINT(1)) - return 1; /* division by zero */ - *result = ValInt{ARGINT(0) % ARGINT(1)}; - return 0; -} - -static -int fun_or(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{ARGINT(0) || ARGINT(1)}; - return 0; -} - -static -int fun_and(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{ARGINT(0) && ARGINT(1)}; - return 0; -} - -static -int fun_not(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{!ARGINT(0)}; - return 0; -} - -static -int fun_neg(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{~ARGINT(0)}; - return 0; -} - -static -int fun_gte(dumb_ptr, val_t *result, Slice args) -{ - if (args[0].is() || args[1].is()) - { - stringify(&args[0]); - stringify(&args[1]); - *result = ValInt{ARGSTR(0) >= ARGSTR(1)}; - } - else - { - intify(&args[0]); - intify(&args[1]); - *result = ValInt{ARGINT(0) >= ARGINT(1)}; - } - return 0; -} - -static -int fun_lt(dumb_ptr env, val_t *result, Slice args) -{ - fun_gte(env, result, args); - result->get_if()->v_int ^= 1; - return 0; -} - -static -int fun_gt(dumb_ptr, val_t *result, Slice args) -{ - if (args[0].is() || args[1].is()) - { - stringify(&args[0]); - stringify(&args[1]); - *result = ValInt{ARGSTR(0) > ARGSTR(1)}; - } - else - { - intify(&args[0]); - intify(&args[1]); - *result = ValInt{ARGINT(0) > ARGINT(1)}; - } - return 0; -} - -static -int fun_lte(dumb_ptr env, val_t *result, Slice args) -{ - fun_gt(env, result, args); - result->get_if()->v_int ^= 1; - return 0; -} - -static -int fun_eq(dumb_ptr, val_t *result, Slice args) -{ - if (args[0].is() || args[1].is()) - { - stringify(&args[0]); - stringify(&args[1]); - *result = ValInt{ARGSTR(0) == ARGSTR(1)}; - } - else if (args[0].is() && args[1].is()) - *result = ValInt{ARGDIR(0) == ARGDIR(1)}; - else if (args[0].is() && args[1].is()) - *result = ValInt{ARGENTITY(0) == ARGENTITY(1)}; - else if (args[0].is() && args[1].is()) - *result = ValInt{(ARGLOCATION(0).x == ARGLOCATION(1).x - && ARGLOCATION(0).y == ARGLOCATION(1).y - && ARGLOCATION(0).m == ARGLOCATION(1).m)}; - else if (args[0].is() && args[1].is()) - *result = ValInt{ARGAREA(0) == ARGAREA(1)}; /* Probably not that great an idea... */ - else if (args[0].is() && args[1].is()) - *result = ValInt{ARGSPELL(0) == ARGSPELL(1)}; - else if (args[0].is() && args[1].is()) - *result = ValInt{ARGINVOCATION(0) == ARGINVOCATION(1)}; - else - { - intify(&args[0]); - intify(&args[1]); - *result = ValInt{ARGINT(0) == ARGINT(1)}; - } - return 0; -} - -static -int fun_ne(dumb_ptr env, val_t *result, Slice args) -{ - fun_eq(env, result, args); - result->get_if()->v_int ^= 1; - return 0; -} - -static -int fun_bitand(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{ARGINT(0) & ARGINT(1)}; - return 0; -} - -static -int fun_bitor(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{ARGINT(0) | ARGINT(1)}; - return 0; -} - -static -int fun_bitxor(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{ARGINT(0) ^ ARGINT(1)}; - return 0; -} - -static -int fun_bitshl(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{ARGINT(0) << ARGINT(1)}; - return 0; -} - -static -int fun_bitshr(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{ARGINT(0) >> ARGINT(1)}; - return 0; -} - -static -int fun_max(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{std::max(ARGINT(0), ARGINT(1))}; - return 0; -} - -static -int fun_min(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{std::min(ARGINT(0), ARGINT(1))}; - return 0; -} - -static -int fun_if_then_else(dumb_ptr, val_t *result, Slice args) -{ - if (ARGINT(0)) - magic_copy_var(result, &args[1]); - else - magic_copy_var(result, &args[2]); - return 0; -} - -Borrowed magic_area_rect(int *x, int *y, int *width, int *height, - area_t& area_) -{ - MATCH_BEGIN (area_) - { - MATCH_CASE (const AreaUnion&, a) - { - (void)a; - abort(); - } - MATCH_CASE (const location_t&, a_loc) - { - P m = a_loc.m; - *x = a_loc.x; - *y = a_loc.y; - *width = 1; - *height = 1; - return m; - } - MATCH_CASE (const AreaRect&, a_rect) - { - P m = a_rect.loc.m; - *x = a_rect.loc.x; - *y = a_rect.loc.y; - *width = a_rect.width; - *height = a_rect.height; - return m; - } - MATCH_CASE (const AreaBar&, a_bar) - { - int tx = a_bar.loc.x; - int ty = a_bar.loc.y; - int twidth = a_bar.width; - int tdepth = a_bar.width; - P m = a_bar.loc.m; - - switch (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"_fmt); - *x = tx; - *y = ty; - *width = *height = 1; - } - return m; - } - } - MATCH_END (); - abort(); -} - -int magic_location_in_area(Borrowed m, int x, int y, dumb_ptr area) -{ - MATCH_BEGIN (*area) - { - MATCH_CASE (const AreaUnion&, a) - { - return magic_location_in_area(m, x, y, a.a_union[0]) - || magic_location_in_area(m, x, y, a.a_union[1]); - } - MATCH_CASE (const location_t&, a_loc) - { - (void)a_loc; - // TODO this can be simplified - int ax, ay, awidth, aheight; - P am = magic_area_rect(&ax, &ay, &awidth, &aheight, *area); - return (am == m - && (x >= ax) && (y >= ay) - && (x < ax + awidth) && (y < ay + aheight)); - } - MATCH_CASE (const AreaRect&, a_rect) - { - (void)a_rect; - // TODO this is too complicated - int ax, ay, awidth, aheight; - P am = magic_area_rect(&ax, &ay, &awidth, &aheight, *area); - return (am == m - && (x >= ax) && (y >= ay) - && (x < ax + awidth) && (y < ay + aheight)); - } - MATCH_CASE (const AreaBar&, a_bar) - { - (void)a_bar; - // TODO this is wrong - int ax, ay, awidth, aheight; - P am = magic_area_rect(&ax, &ay, &awidth, &aheight, *area); - return (am == m - && (x >= ax) && (y >= ay) - && (x < ax + awidth) && (y < ay + aheight)); - } - } - MATCH_END (); - abort(); -} - -static -int fun_is_in(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{magic_location_in_area(ARGLOCATION(0).m, - ARGLOCATION(0).x, - ARGLOCATION(0).y, ARGAREA(1))}; - return 0; -} - -static -int fun_skill(dumb_ptr, val_t *result, Slice args) -{ - if (ENTITY_TYPE(0) != BL::PC - // don't convert to enum until after the range check - // (actually it would be okay, I checked) - || ARGINT(1) < 0 - || ARGINT(1) >= static_cast(MAX_SKILL)) - { - *result = ValInt{0}; - } - else - { - SkillID id = static_cast(ARGINT(1)); - *result = ValInt{ARGPC(0)->status.skill[id].lv}; - } - return 0; -} - -static -int fun_his_shroud(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{(ENTITY_TYPE(0) == BL::PC && ARGPC(0)->state.shroud_active)}; - return 0; -} - -#define BATTLE_GETTER(name) \ -static \ -int fun_get_##name(dumb_ptr, val_t *result, Slice args) \ -{ \ - *result = ValInt{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) -static -int fun_get_dir(dumb_ptr, val_t *result, Slice args) -{ - *result = ValDir{battle_get_dir(ARGENTITY(0))}; - return 0; -} - -#define MMO_GETTER(name) \ -static \ -int fun_get_##name(dumb_ptr, val_t *result, Slice args) \ -{ \ - if (ENTITY_TYPE(0) == BL::PC) \ - *result = ValInt{ARGPC(0)->status.name}; \ - else \ - *result = ValInt{0}; \ - return 0; \ -} - -MMO_GETTER(sp) -MMO_GETTER(max_sp) - -static -int fun_name_of(dumb_ptr, val_t *result, Slice args) -{ - if (args[0].is()) - { - *result = ValString{show_entity(ARGENTITY(0))}; - return 0; - } - else if (args[0].is()) - { - *result = ValString{ARGSPELL(0)->name}; - return 0; - } - else if (args[0].is()) - { - *result = ValString{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(dumb_ptr, val_t *result, Slice args) -{ - if (ENTITY_TYPE(0) != BL::MOB) - return 1; - *result = ValInt{unwrap(ARGMOB(0)->mob_class)}; - return 0; -} - -inline -void COPY_LOCATION(block_list& dest, location_t& src) -{ - dest.bl_x = src.x; - dest.bl_y = src.y; - dest.bl_m = src.m; -} - -inline -void COPY_LOCATION(location_t& dest, block_list& src) -{ - dest.x = src.bl_x; - dest.y = src.bl_y; - dest.m = src.bl_m; -} - -static -int fun_location(dumb_ptr, val_t *result, Slice args) -{ - location_t loc; - COPY_LOCATION(loc, *(ARGENTITY(0))); - *result = ValLocation{loc}; - return 0; -} - -static -int fun_random(dumb_ptr, val_t *result, Slice args) -{ - int delta = ARGINT(0); - if (delta < 0) - delta = -delta; - if (delta == 0) - { - *result = ValInt{0}; - return 0; - } - *result = ValInt{random_::to(delta)}; - - if (ARGINT(0) < 0) - result->get_if()->v_int *= -1; - return 0; -} - -static -int fun_random_dir(dumb_ptr, val_t *result, Slice args) -{ - if (ARGINT(0)) - *result = ValDir{random_::choice({DIR::S, DIR::SW, DIR::W, DIR::NW, DIR::N, DIR::NE, DIR::E, DIR::SE})}; - else - *result = ValDir{random_::choice({DIR::S, DIR::W, DIR::N, DIR::E})}; - return 0; -} - -static -int fun_hash_entity(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{static_cast(unwrap(ARGENTITY(0)->bl_id))}; - return 0; -} - -// ret -1: not a string, ret 1: no such item, ret 0: OK -int magic_find_item(Slice args, int index, Item *item_, int *stackable) -{ - Option> item_data_ = None; - int must_add_sequentially; - - if (args[index].is()) - item_data_ = itemdb_exists(wrap(static_cast(ARGINT(index)))); - else if (args[index].is()) - item_data_ = itemdb_searchname(ARGSTR(index)); - else - return -1; - - P item_data = TRY_UNWRAP(item_data_, return 1); - - // Very elegant. - must_add_sequentially = ( - item_data->type == ItemType::WEAPON - || item_data->type == ItemType::ARMOR - || item_data->type == ItemType::_7 - || item_data->type == ItemType::_8); - - if (stackable) - *stackable = !must_add_sequentially; - - *item_ = Item(); - item_->nameid = item_data->nameid; - - return 0; -} - -static -int fun_count_item(dumb_ptr, val_t *result, Slice args) -{ - dumb_ptr chr = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : nullptr; - int stackable; - Item item; - - GET_ARG_ITEM(1, item, stackable); - - if (!chr) - return 1; - - *result = ValInt{pc_count_all_items(chr, item.nameid)}; - return 0; -} - -static -int fun_is_equipped(dumb_ptr, val_t *result, Slice args) -{ - dumb_ptr chr = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : nullptr; - int stackable; - Item item; - bool retval = false; - - GET_ARG_ITEM(1, item, stackable); - - if (!chr) - return 1; - - for (EQUIP i : EQUIPs) - { - IOff0 idx = chr->equip_index_maybe[i]; - if (idx.ok() && chr->status.inventory[idx].nameid == item.nameid) - { - retval = true; - break; - } - } - - *result = ValInt{retval}; - return 0; -} - -static -int fun_is_married(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{(ENTITY_TYPE(0) == BL::PC && ARGPC(0)->status.partner_id)}; - return 0; -} - -static -int fun_is_dead(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{(ENTITY_TYPE(0) == BL::PC && pc_isdead(ARGPC(0)))}; - return 0; -} - -static -int fun_is_pc(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{(ENTITY_TYPE(0) == BL::PC)}; - return 0; -} - -static -int fun_partner(dumb_ptr, val_t *result, Slice args) -{ - if (ENTITY_TYPE(0) == BL::PC && ARGPC(0)->status.partner_id) - { - *result = - ValEntityPtr{map_nick2sd(map_charid2nick(ARGPC(0)->status.partner_id))}; - return 0; - } - else - return 1; -} - -static -int fun_awayfrom(dumb_ptr, val_t *result, Slice args) -{ - location_t *loc = &ARGLOCATION(0); - int dx = dirx[ARGDIR(1)]; - int dy = diry[ARGDIR(1)]; - int distance = ARGINT(2); - while (distance-- - && !bool(read_gatp(loc->m, loc->x + dx, loc->y + dy) - & MapCell::UNWALKABLE)) - { - loc->x += dx; - loc->y += dy; - } - - *result = ValLocation{*loc}; - return 0; -} - -static -int fun_failed(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{args[0].is()}; - return 0; -} - -static -int fun_npc(dumb_ptr, val_t *result, Slice args) -{ - NpcName name = stringish(ARGSTR(0)); - dumb_ptr npc = npc_name2id(name); - *result = ValEntityPtr{npc}; - return npc == nullptr; -} - -static -int fun_pc(dumb_ptr, val_t *result, Slice args) -{ - CharName name = stringish(ARGSTR(0)); - dumb_ptr chr = map_nick2sd(name); - *result = ValEntityPtr{chr}; - return chr == nullptr; -} - -static -int fun_distance(dumb_ptr, val_t *result, Slice args) -{ - if (ARGLOCATION(0).m != ARGLOCATION(1).m) - *result = ValInt{0x7fffffff}; - else - *result = ValInt{std::max(abs(ARGLOCATION(0).x - ARGLOCATION(1).x), - abs(ARGLOCATION(0).y - ARGLOCATION(1).y))}; - return 0; -} - -static -int fun_rdistance(dumb_ptr, val_t *result, Slice args) -{ - if (ARGLOCATION(0).m != ARGLOCATION(1).m) - *result = ValInt{0x7fffffff}; - else - { - int dx = ARGLOCATION(0).x - ARGLOCATION(1).x; - int dy = ARGLOCATION(0).y - ARGLOCATION(1).y; - *result = ValInt{static_cast(sqrt((dx * dx) + (dy * dy)))}; - } - return 0; -} - -static -int fun_anchor(dumb_ptr env, val_t *result, Slice args) -{ - dumb_ptr anchor = magic_find_anchor(ARGSTR(0)); - - if (!anchor) - return 1; - - magic_eval(env, result, anchor->location); - - make_area(result); - if (!result->is()) - { - magic_clear_var(result); - return 1; - } - - return 0; -} - -static -int fun_line_of_sight(dumb_ptr, val_t *result, Slice args) -{ - block_list e1, e2; - - COPY_LOCATION(e1, ARGLOCATION(0)); - COPY_LOCATION(e2, ARGLOCATION(1)); - - *result = ValInt{battle_check_range(dumb_ptr(&e1), dumb_ptr(&e2), 0)}; - - return 0; -} - -void magic_random_location(location_t *dest, dumb_ptr area) -{ - MATCH_BEGIN (*area) - { - MATCH_CASE (const AreaUnion&, a) - { - if (random_::chance({a.a_union[0]->size, area->size})) - magic_random_location(dest, a.a_union[0]); - else - magic_random_location(dest, a.a_union[1]); - } - MATCH_CASE (const location_t&, a_loc) - { - (void)a_loc; - // TODO this can be simplified - int x, y, w, h; - P m = magic_area_rect(&x, &y, &w, &h, *area); - - if (w <= 1) - w = 1; - - if (h <= 1) - h = 1; - - // This is not exactly the same as the old logic, - // but it's better. - auto pair = map_randfreecell(m, x, y, w, h); - - dest->m = m; - dest->x = pair.first; - dest->y = pair.second; - } - MATCH_CASE (const AreaRect&, a_rect) - { - (void)a_rect; - // TODO this can be simplified - int x, y, w, h; - P m = magic_area_rect(&x, &y, &w, &h, *area); - - if (w <= 1) - w = 1; - - if (h <= 1) - h = 1; - - // This is not exactly the same as the old logic, - // but it's better. - auto pair = map_randfreecell(m, x, y, w, h); - - dest->m = m; - dest->x = pair.first; - dest->y = pair.second; - } - MATCH_CASE (const AreaBar&, a_bar) - { - (void)a_bar; - // TODO this is wrong - int x, y, w, h; - P m = magic_area_rect(&x, &y, &w, &h, *area); - - if (w <= 1) - w = 1; - - if (h <= 1) - h = 1; - - // This is not exactly the same as the old logic, - // but it's better. - auto pair = map_randfreecell(m, x, y, w, h); - - dest->m = m; - dest->x = pair.first; - dest->y = pair.second; - } - } - MATCH_END (); -} - -static -int fun_pick_location(dumb_ptr, val_t *result, Slice args) -{ - location_t loc; - magic_random_location(&loc, ARGAREA(0)); - *result = ValLocation{loc}; - return 0; -} - -static -int fun_read_script_int(dumb_ptr, val_t *result, Slice args) -{ - dumb_ptr subject_p = ARGENTITY(0); - VarName var_name = stringish(ARGSTR(1)); - int array_index = 0; - - if (subject_p->bl_type != BL::PC) - return 1; - - *result = ValInt{get_script_var_i(subject_p->is_player(), var_name, array_index)}; - return 0; -} - -static -int fun_read_script_str(dumb_ptr, val_t *result, Slice args) -{ - dumb_ptr subject_p = ARGENTITY(0); - VarName var_name = stringish(ARGSTR(1)); - int array_index = 0; - - if (subject_p->bl_type != BL::PC) - return 1; - - *result = ValString{get_script_var_s(subject_p->is_player(), var_name, array_index)}; - return 0; -} - -static -int fun_rbox(dumb_ptr, val_t *result, Slice args) -{ - location_t loc = ARGLOCATION(0); - int radius = ARGINT(1); - - AreaRect a_rect; - a_rect.loc.m = loc.m; - a_rect.loc.x = loc.x - radius; - a_rect.loc.y = loc.y - radius; - a_rect.width = radius * 2 + 1; - a_rect.height = radius * 2 + 1; - *result = ValArea{dumb_ptr::make(a_rect)}; - - return 0; -} - -static -int fun_running_status_update(dumb_ptr, val_t *result, Slice args) -{ - if (ENTITY_TYPE(0) != BL::PC && ENTITY_TYPE(0) != BL::MOB) - return 1; - - StatusChange sc = static_cast(ARGINT(1)); - *result = ValInt{bool(battle_get_sc_data(ARGENTITY(0))[sc].timer)}; - return 0; -} - -static -int fun_status_option(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{(bool((ARGPC(0))->status.option & static_cast(ARGINT(1))))}; - return 0; -} - -static -int fun_element(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{static_cast(battle_get_element(ARGENTITY(0)).element)}; - return 0; -} - -static -int fun_element_level(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{battle_get_element(ARGENTITY(0)).level}; - return 0; -} - -static -int fun_is_exterior(dumb_ptr, val_t *result, Slice args) -{ -#warning "Evil assumptions!" - *result = ValInt{ARGLOCATION(0).m->name_[4] == '1'}; - return 0; -} - -static -int fun_contains_string(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{nullptr != strstr(ARGSTR(0).c_str(), ARGSTR(1).c_str())}; - return 0; -} - -static -int fun_strstr(dumb_ptr, val_t *result, Slice args) -{ - const char *offset = strstr(ARGSTR(0).c_str(), ARGSTR(1).c_str()); - *result = ValInt{static_cast(offset - ARGSTR(0).c_str())}; - return offset == nullptr; -} - -static -int fun_strlen(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{static_cast(strlen(ARGSTR(0).c_str()))}; - return 0; -} - -static -int fun_substr(dumb_ptr, val_t *result, Slice args) -{ - RString src = ARGSTR(0); - int offset = ARGINT(1); - int len = ARGINT(2); - - if (len < 0) - len = 0; - if (offset < 0) - offset = 0; - - if (offset > src.size()) - offset = src.size(); - - if (offset + len > src.size()) - len = src.size() - offset; - - auto begin = src.begin() + offset; - auto end = begin + len; - *result = ValString{RString(begin, end)}; - - return 0; -} - -static -int fun_sqrt(dumb_ptr, val_t *result, Slice args) -{ - *result = ValInt{static_cast(sqrt(ARGINT(0)))}; - return 0; -} - -static -int fun_map_level(dumb_ptr, val_t *result, Slice args) -{ -#warning "Evil assumptions!" - *result = ValInt{ARGLOCATION(0).m->name_[4] - '0'}; - return 0; -} - -static -int fun_map_nr(dumb_ptr, val_t *result, Slice args) -{ -#warning "Evil assumptions!" - MapName mapname = ARGLOCATION(0).m->name_; - - *result = ValInt{((mapname[0] - '0') * 100) - + ((mapname[1] - '0') * 10) + ((mapname[2] - '0'))}; - return 0; -} - -static -int fun_dir_towards(dumb_ptr, val_t *result, Slice 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(2)) - { - /* 8-direction mode */ - if (abs(dx) > abs(dy) * 2) - { /* east or west */ - if (dx < 0) - *result = ValDir{DIR::W}; - else - *result = ValDir{DIR::E}; - } - else if (abs(dy) > abs(dx) * 2) - { /* north or south */ - if (dy > 0) - *result = ValDir{DIR::S}; - else - *result = ValDir{DIR::N}; - } - else if (dx < 0) - { /* north-west or south-west */ - if (dy < 0) - *result = ValDir{DIR::NW}; - else - *result = ValDir{DIR::SW}; - } - else - { /* north-east or south-east */ - if (dy < 0) - *result = ValDir{DIR::NE}; - else - *result = ValDir{DIR::SE}; - } - } - else - { - /* 4-direction mode */ - if (abs(dx) > abs(dy)) - { /* east or west */ - if (dx < 0) - *result = ValDir{DIR::W}; - else - *result = ValDir{DIR::E}; - } - else - { /* north or south */ - if (dy > 0) - *result = ValDir{DIR::S}; - else - *result = ValDir{DIR::N}; - } - } - - return 0; -} - -static -int fun_extract_healer_xp(dumb_ptr, val_t *result, Slice args) -{ - dumb_ptr sd = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : nullptr; - - if (!sd) - *result = ValInt{0}; - else - *result = ValInt{pc_extract_healer_exp(sd, ARGINT(1))}; - return 0; -} - -#define MAGIC_FUNCTION(name, args, ret, impl) {name, {name, args, ret, impl}} -#define MAGIC_FUNCTION1(name, args, ret) MAGIC_FUNCTION(#name##_s, args, ret, fun_##name) -static // should be LString, but no heterogenous lookup yet -std::map functions = -{ - MAGIC_FUNCTION("+"_s, ".."_s, '.', fun_add), - MAGIC_FUNCTION("-"_s, "ii"_s, 'i', fun_sub), - MAGIC_FUNCTION("*"_s, "ii"_s, 'i', fun_mul), - MAGIC_FUNCTION("/"_s, "ii"_s, 'i', fun_div), - MAGIC_FUNCTION("%"_s, "ii"_s, 'i', fun_mod), - MAGIC_FUNCTION("||"_s, "ii"_s, 'i', fun_or), - MAGIC_FUNCTION("&&"_s, "ii"_s, 'i', fun_and), - MAGIC_FUNCTION("<"_s, ".."_s, 'i', fun_lt), - MAGIC_FUNCTION(">"_s, ".."_s, 'i', fun_gt), - MAGIC_FUNCTION("<="_s, ".."_s, 'i', fun_lte), - MAGIC_FUNCTION(">="_s, ".."_s, 'i', fun_gte), - MAGIC_FUNCTION("=="_s, ".."_s, 'i', fun_eq), - MAGIC_FUNCTION("!="_s, ".."_s, 'i', fun_ne), - MAGIC_FUNCTION("|"_s, ".."_s, 'i', fun_bitor), - MAGIC_FUNCTION("&"_s, "ii"_s, 'i', fun_bitand), - MAGIC_FUNCTION("^"_s, "ii"_s, 'i', fun_bitxor), - MAGIC_FUNCTION("<<"_s, "ii"_s, 'i', fun_bitshl), - MAGIC_FUNCTION(">>"_s, "ii"_s, 'i', fun_bitshr), - MAGIC_FUNCTION1(not, "i"_s, 'i'), - MAGIC_FUNCTION1(neg, "i"_s, 'i'), - MAGIC_FUNCTION1(max, "ii"_s, 'i'), - MAGIC_FUNCTION1(min, "ii"_s, 'i'), - MAGIC_FUNCTION1(is_in, "la"_s, 'i'), - MAGIC_FUNCTION1(if_then_else, "i__"_s, '_'), - MAGIC_FUNCTION1(skill, "ei"_s, 'i'), - MAGIC_FUNCTION("str"_s, "e"_s, 'i', fun_get_str), - MAGIC_FUNCTION("agi"_s, "e"_s, 'i', fun_get_agi), - MAGIC_FUNCTION("vit"_s, "e"_s, 'i', fun_get_vit), - MAGIC_FUNCTION("dex"_s, "e"_s, 'i', fun_get_dex), - MAGIC_FUNCTION("luk"_s, "e"_s, 'i', fun_get_luk), - MAGIC_FUNCTION("int"_s, "e"_s, 'i', fun_get_int), - MAGIC_FUNCTION("level"_s, "e"_s, 'i', fun_get_lv), - MAGIC_FUNCTION("mdef"_s, "e"_s, 'i', fun_get_mdef), - MAGIC_FUNCTION("def"_s, "e"_s, 'i', fun_get_def), - MAGIC_FUNCTION("hp"_s, "e"_s, 'i', fun_get_hp), - MAGIC_FUNCTION("max_hp"_s, "e"_s, 'i', fun_get_max_hp), - MAGIC_FUNCTION("sp"_s, "e"_s, 'i', fun_get_sp), - MAGIC_FUNCTION("max_sp"_s, "e"_s, 'i', fun_get_max_sp), - MAGIC_FUNCTION("dir"_s, "e"_s, 'd', fun_get_dir), - MAGIC_FUNCTION1(name_of, "."_s, 's'), - MAGIC_FUNCTION1(mob_id, "e"_s, 'i'), - MAGIC_FUNCTION1(location, "e"_s, 'l'), - MAGIC_FUNCTION1(random, "i"_s, 'i'), - MAGIC_FUNCTION1(random_dir, "i"_s, 'd'), - MAGIC_FUNCTION1(hash_entity, "e"_s, 'i'), - MAGIC_FUNCTION1(is_married, "e"_s, 'i'), - MAGIC_FUNCTION1(partner, "e"_s, 'e'), - MAGIC_FUNCTION1(awayfrom, "ldi"_s, 'l'), - MAGIC_FUNCTION1(failed, "_"_s, 'i'), - MAGIC_FUNCTION1(pc, "s"_s, 'e'), - MAGIC_FUNCTION1(npc, "s"_s, 'e'), - MAGIC_FUNCTION1(distance, "ll"_s, 'i'), - MAGIC_FUNCTION1(rdistance, "ll"_s, 'i'), - MAGIC_FUNCTION1(anchor, "s"_s, 'a'), - MAGIC_FUNCTION("random_location"_s, "a"_s, 'l', fun_pick_location), - MAGIC_FUNCTION("script_int"_s, "es"_s, 'i', fun_read_script_int), - MAGIC_FUNCTION("script_str"_s, "es"_s, 's', fun_read_script_str), - MAGIC_FUNCTION1(rbox, "li"_s, 'a'), - MAGIC_FUNCTION1(count_item, "e."_s, 'i'), - MAGIC_FUNCTION1(line_of_sight, "ll"_s, 'i'), - MAGIC_FUNCTION1(running_status_update, "ei"_s, 'i'), - MAGIC_FUNCTION1(status_option, "ei"_s, 'i'), - MAGIC_FUNCTION1(element, "e"_s, 'i'), - MAGIC_FUNCTION1(element_level, "e"_s, 'i'), - MAGIC_FUNCTION1(his_shroud, "e"_s, 'i'), - MAGIC_FUNCTION1(is_equipped, "e."_s, 'i'), - MAGIC_FUNCTION1(is_exterior, "l"_s, 'i'), - MAGIC_FUNCTION1(contains_string, "ss"_s, 'i'), - MAGIC_FUNCTION1(strstr, "ss"_s, 'i'), - MAGIC_FUNCTION1(strlen, "s"_s, 'i'), - MAGIC_FUNCTION1(substr, "sii"_s, 's'), - MAGIC_FUNCTION1(sqrt, "i"_s, 'i'), - MAGIC_FUNCTION1(map_level, "l"_s, 'i'), - MAGIC_FUNCTION1(map_nr, "l"_s, 'i'), - MAGIC_FUNCTION1(dir_towards, "lli"_s, 'd'), - MAGIC_FUNCTION1(is_dead, "e"_s, 'i'), - MAGIC_FUNCTION1(is_pc, "e"_s, 'i'), - MAGIC_FUNCTION("extract_healer_experience"_s, "ei"_s, 'i', fun_extract_healer_xp), -}; - -fun_t *magic_get_fun(ZString name) -{ - auto it = functions.find(name); - if (it == functions.end()) - return nullptr; - return &it->second; -} - -// 1 on failure -static -int eval_location(dumb_ptr env, location_t *dest, const 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 (m.is() - && x.is() && y.is()) - { - MapName name = VString<15>(ZString(m.get_if()->v_string)); - magic_clear_var(&m); - P map_id = TRY_UNWRAP(map_mapname2mapid(name), return 1); - dest->m = map_id; - dest->x = x.get_if()->v_int; - dest->y = y.get_if()->v_int; - return 0; - } - else - { - magic_clear_var(&m); - magic_clear_var(&x); - magic_clear_var(&y); - return 1; - } -} - -static -dumb_ptr eval_area(dumb_ptr env, const e_area_t& expr_) -{ - MATCH_BEGIN (expr_) - { - MATCH_CASE (const e_location_t&, a_loc) - { - location_t loc; - if (eval_location(env, &loc, &a_loc)) - { - return nullptr; - } - else - { - return dumb_ptr::make(loc); - } - } - MATCH_CASE (const ExprAreaUnion&, a) - { - AreaUnion u; - bool fail = false; - for (int i = 0; i < 2; i++) - { - u.a_union[i] = eval_area(env, *a.a_union[i]); - if (!u.a_union[i]) - fail = true; - } - - if (fail) - { - for (int i = 0; i < 2; i++) - { - if (u.a_union[i]) - free_area(u.a_union[i]); - } - return nullptr; - } - return dumb_ptr::make(u); - } - MATCH_CASE (const ExprAreaRect&, a_rect) - { - val_t width, height; - magic_eval(env, &width, a_rect.width); - magic_eval(env, &height, a_rect.height); - - AreaRect a_rect_; - if (width.is() - && height.is() - && !eval_location(env, &(a_rect_.loc), - &a_rect.loc)) - { - a_rect_.width = width.get_if()->v_int; - a_rect_.height = height.get_if()->v_int; - - magic_clear_var(&width); - magic_clear_var(&height); - return dumb_ptr::make(a_rect_); - } - else - { - magic_clear_var(&width); - magic_clear_var(&height); - return nullptr; - } - } - MATCH_CASE (const ExprAreaBar&, a_bar) - { - val_t width, depth, dir; - magic_eval(env, &width, a_bar.width); - magic_eval(env, &depth, a_bar.depth); - magic_eval(env, &dir, a_bar.dir); - - AreaBar a_bar_; - if (width.is() - && depth.is() - && dir.is() - && !eval_location(env, &a_bar_.loc, - &a_bar.loc)) - { - a_bar_.width = width.get_if()->v_int; - a_bar_.depth = depth.get_if()->v_int; - a_bar_.dir = dir.get_if()->v_dir; - - magic_clear_var(&width); - magic_clear_var(&depth); - magic_clear_var(&dir); - return dumb_ptr::make(a_bar_); - } - else - { - magic_clear_var(&width); - magic_clear_var(&depth); - magic_clear_var(&dir); - return nullptr; - } - } - } - MATCH_END (); - abort(); -} - -// This is called on arguments with begin=true, -// and on the return value with begin=false. -// In both cases, the ambiguous types are in pointer mode. -static -bool type_key_matches(char ty_key, val_t *arg, bool begin) -{ - switch (ty_key) - { - case 'i': - if (begin) - intify(arg); - return arg->is(); - case 'd': - return arg->is(); - case 's': - if (begin) - stringify(arg); - return arg->is(); - case 'e': - return arg->is(); - case 'l': - if (begin) - make_location(arg); - return arg->is(); - case 'a': - if (begin) - make_area(arg); - return arg->is(); - case 'S': - if (begin) - make_spell(arg); - return arg->is(); - case 'I': - return arg->is(); - default: - return true; - } -} - -int magic_signature_check(ZString opname, ZString funname, ZString signature, - Slice args, int line, int column) -{ - int i; - for (i = 0; i < args.size(); i++) - { - val_t *arg = &args[i]; - - // whoa, it turns out the second p *does* shadow this one - if (ValEntityInt *p1 = arg->get_if()) - { - /* Dereference entities in preparation for calling function */ - dumb_ptr ent = map_id2bl(p1->v_eid); - if (ent) - { - *arg = ValEntityPtr{ent}; - } - else - { - *arg = ValFail{}; - } - } - else if (ValInvocationInt *p2 = arg->get_if()) - { - dumb_ptr invoc = map_id2bl(p2->v_iid)->is_spell(); - if (invoc) - { - *arg = ValInvocationPtr{invoc}; - } - else - { - *arg = ValFail(); - } - } - - char ty_key = signature[i]; - if (!ty_key) - { - FPRINTF(stderr, - "[magic-eval]: L%d:%d: Too many arguments (%zu) to %s `%s'\n"_fmt, - line, column, args.size(), opname, funname); - return 1; - } - - if (arg->is() && ty_key != '_') - return 1; /* Fail `in a sane way': This is a perfectly permissible error */ - - // this also does conversions now - if (type_key_matches(ty_key, arg, true)) - continue; - - if (arg->is()) - { - FPRINTF(stderr, - "[magic-eval]: L%d:%d: Argument #%d to %s `%s' undefined\n"_fmt, - line, column, i + 1, opname, funname); - return 1; - } - - - { /* Coercion failed? */ - if (!arg->is()) - { - FPRINTF(stderr, - "[magic-eval]: L%d:%d: Argument #%d to %s `%s' of incorrect type (sorry, types aren't integers anymore)\n"_fmt, - line, column, i + 1, opname, funname); - } - return 1; - } - } - - return 0; -} - -void magic_eval(dumb_ptr env, val_t *dest, dumb_ptr expr) -{ - MATCH_BEGIN (*expr) - { - MATCH_CASE (const val_t&, e_val) - { - magic_copy_var(dest, &e_val); - } - - MATCH_CASE (const e_location_t&, e_location) - { - location_t loc; - if (eval_location(env, &loc, &e_location)) - *dest = ValFail(); - else - *dest = ValLocation{loc}; - } - MATCH_CASE (const e_area_t&, e_area) - { - if (dumb_ptr area = eval_area(env, e_area)) - *dest = ValArea{area}; - else - *dest = ValFail(); - } - MATCH_CASE (const ExprFunApp&, e_funapp) - { - val_t arguments[MAX_ARGS]; - int args_nr = e_funapp.args_nr; - int i; - fun_t *f = e_funapp.funp; - - for (i = 0; i < args_nr; ++i) - magic_eval(env, &arguments[i], e_funapp.args[i]); - if (magic_signature_check("function"_s, f->name, f->signature, Slice(arguments, args_nr), - e_funapp.line_nr, e_funapp.column) - || f->fun(env, dest, Slice(arguments, args_nr))) - *dest = ValFail(); - else - { - assert (!dest->is()); - assert (!dest->is()); - assert (!dest->is()); - assert (type_key_matches(f->ret_ty, dest, false)); - - /* translate entity back into persistent int */ - if (ValEntityPtr *ent = dest->get_if()) - { - if (ent->v_entity) - *dest = ValEntityInt{ent->v_entity->bl_id}; - else - *dest = ValFail(); - } - // what about invocation? - } - - for (i = 0; i < args_nr; ++i) - magic_clear_var(&arguments[i]); - } - MATCH_CASE (const ExprId&, e) - { - val_t& v = env->VAR(e.e_id); - magic_copy_var(dest, &v); - } - MATCH_CASE (const ExprField&, e_field) - { - val_t v; - int id = e_field.id; - magic_eval(env, &v, e_field.expr); - - assert(!v.is()); - if (ValInvocationInt *ii = v.get_if()) - { - dumb_ptr t = map_id2bl(ii->v_iid)->is_spell(); - - if (!t) - *dest = ValUndef(); - else - { - val_t& val = t->env->VAR(id); - magic_copy_var(dest, &val); - } - } - else - { - FPRINTF(stderr, - "[magic] Attempt to access field %s on non-spell\n"_fmt, - env->base_env->varv[id].name); - *dest = ValFail(); - } - } - } - MATCH_END (); -} - -int magic_eval_int(dumb_ptr env, dumb_ptr expr) -{ - val_t result; - magic_eval(env, &result, expr); - - if (result.is() || result.is()) - return 0; - - intify(&result); - - return result.get_if()->v_int; -} - -AString magic_eval_str(dumb_ptr env, dumb_ptr expr) -{ - val_t result; - magic_eval(env, &result, expr); - - if (result.is() || result.is()) - return "?"_s; - - stringify(&result); - - return result.get_if()->v_string; -} -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-expr.hpp b/src/map/magic-expr.hpp deleted file mode 100644 index 055f37b..0000000 --- a/src/map/magic-expr.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once -// magic-expr.hpp - Pure functions for the old magic backend. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "fwd.hpp" - -#include "../strings/zstring.hpp" -#include "../strings/literal.hpp" - -#include "magic-interpreter.t.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -/* - * Argument types: - * i : int - * d : dir - * s : string - * e : entity - * l : location - * a : area - * S : spell - * I : invocation - * . : any, except for fail/undef - * _ : any, including fail, but not undef - */ -struct fun_t -{ - LString name; - LString signature; - char ret_ty; - int (*fun)(dumb_ptr env, val_t *result, Slice arga); -}; - -/** - * Retrieves a function by name - * @param name The name to look up - * @return A function of that name, or nullptr. - */ -fun_t *magic_get_fun(ZString name); - -/** - * Evaluates an expression and stores the result in `dest' - */ -void magic_eval(dumb_ptr env, val_t *dest, dumb_ptr expr); - -/** - * Evaluates an expression and coerces the result into an integer - */ -int magic_eval_int(dumb_ptr env, dumb_ptr expr); - -/** - * Evaluates an expression and coerces the result into a string - */ -AString magic_eval_str(dumb_ptr env, dumb_ptr expr); - -void magic_clear_var(val_t *v); - -void magic_copy_var(val_t *dest, const val_t *src); - -void magic_random_location(location_t *dest, dumb_ptr area); - -// ret -1: not a string, ret 1: no such item, ret 0: OK -int magic_find_item(Slice args, int index, Item *item, int *stackable); - -#define GET_ARG_ITEM(index, dest, stackable) \ - switch (magic_find_item(args, index, &dest, &stackable)) \ - { \ - case -1: return 1; \ - case 1: return 0; \ - default: break; \ - } - -int magic_location_in_area(Borrowed m, int x, int y, dumb_ptr area); - -/* Helper definitions for dealing with functions and operations */ - -int magic_signature_check(ZString opname, ZString funname, ZString signature, - Slice args, int line, int column); - -Borrowed magic_area_rect(int *x, int *y, int *width, int *height, - area_t& area); -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-expr.py b/src/map/magic-expr.py deleted file mode 100644 index f53ddc8..0000000 --- a/src/map/magic-expr.py +++ /dev/null @@ -1,38 +0,0 @@ -class fun_t(object): - __slots__ = ('_value') - - name = 'tmwa::map::magic::fun_t' - depth = 1 - enabled = True - - def __init__(self, value): - if not value: - value = None - self._value = value - - def to_string(self): - value = self._value - if value is None: - return '(fun_t *) nullptr' - return '(fun_t *)' - - def children(self): - value = self._value - if value is None: - return - value = value.dereference() - yield '->name', value['name'] - yield '->signature', value['signature'] - yield '->ret_ty', value['ret_ty'] - yield '->fun', value['fun'] - - test_extra = ''' - using tmwa::operator "" _s; - ''' - - tests = [ - ('static_cast(nullptr)', - '(fun_t *) nullptr'), - ('new tmwa::map::magic::fun_t{"name"_s, "sig"_s, \'\\0\', nullptr}', - '(fun_t *) = {->name = "name", ->signature = "sig", ->ret_ty = 0 \'\\000\', ->fun = nullptr}'), - ] diff --git a/src/map/magic-interpreter-base.cpp b/src/map/magic-interpreter-base.cpp deleted file mode 100644 index c2be363..0000000 --- a/src/map/magic-interpreter-base.cpp +++ /dev/null @@ -1,553 +0,0 @@ -#include "magic-interpreter-base.hpp" -// magic-interpreter-base.cpp - Core of the old magic system. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include - -#include "../strings/astring.hpp" -#include "../strings/xstring.hpp" - -#include "../io/cxxstdio.hpp" - -#include "../mmo/cxxstdio_enums.hpp" - -#include "../net/timer.hpp" - -#include "globals.hpp" -#include "magic.hpp" -#include "magic-expr.hpp" -#include "magic-interpreter.hpp" -#include "pc.hpp" - -#include "../poison.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -static -void set_int(val_t *v, int i) -{ - *v = ValInt{i}; -} - -static __attribute__((unused)) -void set_dir(val_t *v, DIR d) -{ - *v = ValDir{d}; -} - - -static -void set_string(val_t *v, RString x) -{ - *v = ValString{x}; -} - -static -void set_entity(val_t *v, dumb_ptr e) -{ - *v = ValEntityInt{e->bl_id}; -} - -static -void set_invocation(val_t *v, dumb_ptr i) -{ - *v = ValInvocationInt{i->bl_id}; -} - -static -void set_spell(val_t *v, dumb_ptr x) -{ - *v = ValSpell{x}; -} - -AString magic_find_invocation(XString spellname) -{ - auto it = magic_conf.spells_by_name.find(spellname); - if (it != magic_conf.spells_by_name.end()) - return it->second->invocation; - - return AString(); -} - -dumb_ptr magic_find_spell(XString invocation) -{ - auto it = magic_conf.spells_by_invocation.find(invocation); - if (it != magic_conf.spells_by_invocation.end()) - return it->second; - - return nullptr; -} - -/* -------------------------------------------------------------------------------- */ -/* Spell anchors */ -/* -------------------------------------------------------------------------------- */ - -AString magic_find_anchor_invocation(XString anchor_name) -{ - auto it = magic_conf.anchors_by_name.find(anchor_name); - - if (it != magic_conf.anchors_by_name.end()) - return it->second->invocation; - - return AString(); -} - -dumb_ptr magic_find_anchor(XString name) -{ - auto it = magic_conf.anchors_by_invocation.find(name); - if (it != magic_conf.anchors_by_invocation.end()) - return it->second; - - return nullptr; -} - -/* -------------------------------------------------------------------------------- */ -/* Spell guard checks */ -/* -------------------------------------------------------------------------------- */ - -static -dumb_ptr alloc_env(magic_conf_t *conf) -{ - auto env = dumb_ptr::make(); - env->varu = make_unique(conf->varv.size()); - env->base_env = conf; - return env; -} - -static -dumb_ptr clone_env(dumb_ptr src) -{ - dumb_ptr retval = alloc_env(src->base_env); - - for (int i = 0; i < src->base_env->varv.size(); i++) - magic_copy_var(&retval->varu[i], &src->varu[i]); - - return retval; -} - -void magic_free_env(dumb_ptr env) -{ - for (int i = 0; i < env->base_env->varv.size(); i++) - magic_clear_var(&env->varu[i]); - // handled by std::unique_ptr now. Was a memory leak before. - // delete[] env->vars; - env.delete_(); -} - -dumb_ptr spell_create_env(magic_conf_t *conf, dumb_ptr spell, - dumb_ptr caster, int spellpower, XString param) -{ - dumb_ptr env = alloc_env(conf); - - switch (spell->spellarg_ty) - { - - case SPELLARG::STRING: - set_string(&env->varu[spell->arg], param); - break; - - case SPELLARG::PC: - { - CharName name = stringish(param); - dumb_ptr subject = map_nick2sd(name); - if (!subject) - subject = caster; - set_entity(&env->varu[spell->arg], subject); - break; - } - - case SPELLARG::NONE: - break; - - default: - FPRINTF(stderr, "Unexpected spellarg type %d\n"_fmt, - spell->spellarg_ty); - } - - set_entity(&env->varu[VAR_CASTER], caster); - set_int(&env->varu[VAR_SPELLPOWER], spellpower); - set_spell(&env->varu[VAR_SPELL], spell); - - return env; -} - -static -void free_components(dumb_ptr *component_holder) -{ - if (*component_holder == nullptr) - return; - free_components(&(*component_holder)->next); - (*component_holder).delete_(); - *component_holder = nullptr; -} - -void magic_add_component(dumb_ptr *component_holder, ItemNameId id, int count) -{ - if (count <= 0) - return; - - if (*component_holder == nullptr) - { - auto component = dumb_ptr::make(); - component->next = nullptr; - component->item_id = id; - component->count = count; - *component_holder = component; - } - else - { - dumb_ptr component = *component_holder; - if (component->item_id == id) - { - component->count += count; - return; - } - else - magic_add_component(&component->next, id, count); - /* Tail-recurse; gcc can optimise this. Not that it matters. */ - } -} - -static -void copy_components(dumb_ptr *component_holder, dumb_ptr component) -{ - if (component == nullptr) - return; - - magic_add_component(component_holder, component->item_id, component->count); - copy_components(component_holder, component->next); -} - -typedef struct spellguard_check -{ - dumb_ptr catalysts, components; - int mana; - interval_t casttime; -} spellguard_check_t; - -static -int check_prerequisites(dumb_ptr caster, dumb_ptr component) -{ - while (component) - { - if (pc_count_all_items(caster, component->item_id) < component->count) - return 0; /* insufficient */ - - component = component->next; - } - - return 1; -} - -static -void consume_components(dumb_ptr caster, dumb_ptr component) -{ - while (component) - { - pc_remove_items(caster, component->item_id, component->count); - component = component->next; - } -} - -static -int spellguard_can_satisfy(spellguard_check_t *check, dumb_ptr caster, - dumb_ptr env, int *near_miss) -{ - tick_t tick = gettick(); - - int retval = check_prerequisites(caster, check->catalysts); - - if (retval && near_miss) - *near_miss = 1; // close enough! - - retval = retval && caster->cast_tick <= tick /* Hasn't cast a spell too recently */ - && check->mana <= caster->status.sp - && check_prerequisites(caster, check->components); - - if (retval) - { - interval_t casttime = check->casttime; - - if (ValInt *v = env->VAR(VAR_MIN_CASTTIME).get_if()) - { - casttime = std::max(casttime, static_cast(v->v_int)); - } - - caster->cast_tick = tick + casttime; /* Make sure not to cast too frequently */ - - consume_components(caster, check->components); - pc_heal(caster, 0, -check->mana); - } - - return retval; -} - -static -const effect_set_t *spellguard_check_sub(spellguard_check_t *check, - dumb_ptr guard, - dumb_ptr caster, - dumb_ptr env, - int *near_miss) -{ - if (guard == nullptr) - return nullptr; - - MATCH_BEGIN (*guard) - { - MATCH_CASE (const GuardCondition&, s) - { - if (!magic_eval_int(env, s.s_condition)) - return nullptr; - } - MATCH_CASE (const GuardComponents&, s) - { - copy_components(&check->components, s.s_components); - } - MATCH_CASE (const GuardCatalysts&, s) - { - copy_components(&check->catalysts, s.s_catalysts); - } - MATCH_CASE (const GuardChoice&, s) - { - spellguard_check_t altcheck = *check; - const effect_set_t *retval; - - altcheck.components = nullptr; - altcheck.catalysts = nullptr; - - copy_components(&altcheck.catalysts, check->catalysts); - copy_components(&altcheck.components, check->components); - - retval = - spellguard_check_sub(&altcheck, guard->next, caster, env, - near_miss); - free_components(&altcheck.catalysts); - free_components(&altcheck.components); - if (retval) - return retval; - else - return spellguard_check_sub(check, s.s_alt, caster, - env, near_miss); - } - MATCH_CASE (const GuardMana&, s) - { - check->mana += magic_eval_int(env, s.s_mana); - } - MATCH_CASE (const GuardCastTime&, s) - { - check->casttime += static_cast(magic_eval_int(env, s.s_casttime)); - } - MATCH_CASE (const effect_set_t&, s_effect) - { - if (spellguard_can_satisfy(check, caster, env, near_miss)) - return &s_effect; - else - return nullptr; - } - } - MATCH_END (); - - return spellguard_check_sub(check, guard->next, caster, env, near_miss); -} - -static -const effect_set_t *check_spellguard(dumb_ptr guard, - dumb_ptr caster, dumb_ptr env, - int *near_miss) -{ - spellguard_check_t check; - const effect_set_t *retval; - check.catalysts = nullptr; - check.components = nullptr; - check.mana = 0; - check.casttime = interval_t::zero(); - - retval = spellguard_check_sub(&check, guard, caster, env, near_miss); - - free_components(&check.catalysts); - free_components(&check.components); - - return retval; -} - -/* -------------------------------------------------------------------------------- */ -/* Public API */ -/* -------------------------------------------------------------------------------- */ - -const effect_set_t *spell_trigger(dumb_ptr spell, dumb_ptr caster, - dumb_ptr env, int *near_miss) -{ - dumb_ptr guard = spell->spellguard; - - if (near_miss) - *near_miss = 0; - - for (letdef_t& ld : spell->letdefv) - magic_eval(env, &env->varu[ld.id], ld.expr); - - return check_spellguard(guard, caster, env, near_miss); -} - -static -void spell_set_location(dumb_ptr invocation, dumb_ptr entity) -{ - magic_clear_var(&invocation->env->varu[VAR_LOCATION]); - ValLocation v; - v.v_location.m = entity->bl_m; - v.v_location.x = entity->bl_x; - v.v_location.y = entity->bl_y; - invocation->env->varu[VAR_LOCATION] = v; -} - -void spell_update_location(dumb_ptr invocation) -{ - if (bool(invocation->spell->flags & SPELL_FLAG::LOCAL)) - return; - else - { - dumb_ptr owner_bl = map_id2bl(invocation->subject); - if (!owner_bl) - return; - dumb_ptr owner = owner_bl->is_player(); - - spell_set_location(invocation, owner); - } -} - -dumb_ptr spell_instantiate(const effect_set_t *effect_set, dumb_ptr env) -{ - dumb_ptr retval; - retval.new_(); - dumb_ptr caster; - - retval->env = env; - - retval->caster = env->VAR(VAR_CASTER).get_if()->v_eid; - retval->spell = env->VAR(VAR_SPELL).get_if()->v_spell; - retval->current_effect = effect_set->effect; - retval->trigger_effect = effect_set->at_trigger; - retval->end_effect = effect_set->at_end; - - caster = map_id2bl(retval->caster); // must still exist - retval->bl_id = map_addobject(retval); - retval->bl_type = BL::SPELL; - retval->bl_m = caster->bl_m; - retval->bl_x = caster->bl_x; - retval->bl_y = caster->bl_y; - - map_addblock(retval); - set_invocation(&env->varu[VAR_INVOCATION], retval); - - return retval; -} - -dumb_ptr spell_clone_effect(dumb_ptr base) -{ - dumb_ptr retval; - retval.new_(); - - // block_list in general is not copyable - // since this is the only call site, it is expanded here - //*retval = *base; - - retval->next_invocation = nullptr; - retval->flags = INVOCATION_FLAG::ZERO; - dumb_ptr env = retval->env = clone_env(base->env); - retval->spell = base->spell; - retval->caster = base->caster; - retval->subject = BlockId(); - // retval->timer = 0; - // retval->stack = undef; - retval->script_pos = 0; - // huh? - retval->current_effect = base->trigger_effect; - retval->trigger_effect = base->trigger_effect; - retval->end_effect = nullptr; - // retval->status_change_refs = nullptr; - - retval->bl_id = BlockId(); - retval->bl_prev = nullptr; - retval->bl_next = nullptr; - retval->bl_m = base->bl_m; - retval->bl_x = base->bl_x; - retval->bl_y = base->bl_y; - retval->bl_type = base->bl_type; - - retval->bl_id = map_addobject(retval); - set_invocation(&env->varu[VAR_INVOCATION], retval); - - return retval; -} - -void spell_bind(dumb_ptr subject, dumb_ptr invocation) -{ - /* Only bind nonlocal spells */ - - if (!bool(invocation->spell->flags & SPELL_FLAG::LOCAL)) - { - if (bool(invocation->flags & INVOCATION_FLAG::BOUND) - || invocation->subject || invocation->next_invocation) - { - int *i = nullptr; - FPRINTF(stderr, - "[magic] INTERNAL ERROR: Attempt to re-bind spell invocation `%s'\n"_fmt, - invocation->spell->name); - *i = 1; - return; - } - - invocation->next_invocation = subject->active_spells; - subject->active_spells = invocation; - invocation->flags |= INVOCATION_FLAG::BOUND; - invocation->subject = subject->bl_id; - } - - spell_set_location(invocation, subject); -} - -int spell_unbind(dumb_ptr subject, dumb_ptr invocation_) -{ - dumb_ptr *seeker = &subject->active_spells; - - while (*seeker) - { - if (*seeker == invocation_) - { - *seeker = invocation_->next_invocation; - - invocation_->flags &= ~INVOCATION_FLAG::BOUND; - invocation_->next_invocation = nullptr; - invocation_->subject = BlockId(); - - return 0; - } - seeker = &((*seeker)->next_invocation); - } - - return 1; -} -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-interpreter-base.hpp b/src/map/magic-interpreter-base.hpp deleted file mode 100644 index 7c00db0..0000000 --- a/src/map/magic-interpreter-base.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once -// magic-interpreter-base.hpp - Core of the old magic system. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "fwd.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -/** - * Adds a component selection to a component holder (which may initially be nullptr) - */ -void magic_add_component(dumb_ptr *component_holder, ItemNameId id, int count); - -/** - * Identifies the invocation used to trigger a spell - * - * Returns empty string if not found - */ -AString magic_find_invocation(XString spellname); - -/** - * Identifies the invocation used to denote a teleport location - * - * Returns empty string if not found - */ -AString magic_find_anchor_invocation(XString teleport_location); - -dumb_ptr magic_find_anchor(XString name); - -dumb_ptr spell_create_env(magic_conf_t *conf, dumb_ptr spell, - dumb_ptr caster, int spellpower, XString param); - -void magic_free_env(dumb_ptr env); - -/** - * near_miss is set to nonzero iff the spell only failed due to ephemereal issues (spell delay in effect, out of mana, out of components) - */ -const effect_set_t *spell_trigger(dumb_ptr spell, - dumb_ptr caster, - dumb_ptr env, int *near_miss); - -dumb_ptr spell_instantiate(const effect_set_t *effect, dumb_ptr env); - -/** - * Bind a spell to a subject (this is a no-op for `local' spells). - */ -void spell_bind(dumb_ptr subject, dumb_ptr invocation); - -// 1 on failure -int spell_unbind(dumb_ptr subject, dumb_ptr invocation); - -/** - * Clones a spell to run the at_effect field - */ -dumb_ptr spell_clone_effect(dumb_ptr source); - -dumb_ptr magic_find_spell(XString invocation); - -void spell_update_location(dumb_ptr invocation); -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-interpreter.hpp b/src/map/magic-interpreter.hpp deleted file mode 100644 index cbd92a9..0000000 --- a/src/map/magic-interpreter.hpp +++ /dev/null @@ -1,630 +0,0 @@ -#pragma once -// magic-interpreter.hpp - Old magic. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "magic-interpreter.t.hpp" - -#include "fwd.hpp" - -#include - -#include - -#include "../strings/rstring.hpp" - -#include "../sexpr/variant.hpp" - -#include "../net/timer.t.hpp" - -#include "../mmo/ids.hpp" - -#include "map.hpp" -#include "script-buffer.hpp" -#include "../mmo/skill.t.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -struct location_t -{ - Borrowed m; - int x, y; - - // This constructor exists solely to work around the design constraints - // of sexpr::Variant<>. See comments in variant.tcc for future plans. - __attribute__((deprecated)) - location_t() noexcept : m(borrow(undefined_gat)), x(), y() {} - location_t(Borrowed m_, int x_, int y_) : m(m_), x(x_), y(y_) {} -}; - -struct AreaUnion -{ - dumb_ptr a_union[2]; -}; -struct AreaRect -{ - location_t loc; - int width, height; -}; -struct AreaBar -{ - location_t loc; - int width, depth; - DIR dir; -}; - -using AreaVariantBase = Variant< - location_t, - AreaUnion, - AreaRect, - AreaBar ->; - -struct area_t : AreaVariantBase -{ - int size; - - area_t() = delete; - area_t(area_t&&) = default; - area_t(const area_t&) = delete; - area_t& operator = (area_t&&) = default; - area_t& operator = (const area_t&) = delete; - - area_t(location_t v) : AreaVariantBase(std::move(v)), size(1) {} - area_t(AreaUnion v) : AreaVariantBase(std::move(v)), size(v.a_union[0]->size + v.a_union[1]->size) {} - area_t(AreaRect v) : AreaVariantBase(std::move(v)), size(v.width * v.height) {} - area_t(AreaBar v) : AreaVariantBase(std::move(v)), size((v.width * 2 + 1) * v.depth) {} -}; - -struct ValUndef -{ -}; -struct ValInt -{ - int v_int; -}; -struct ValDir -{ - DIR v_dir; -}; -struct ValString -{ - RString v_string; -}; -struct ValEntityInt -{ - BlockId v_eid; -}; -struct ValEntityPtr -{ - dumb_ptr v_entity; -}; -struct ValLocation -{ - location_t v_location; -}; -struct ValArea -{ - dumb_ptr v_area; -}; -struct ValSpell -{ - dumb_ptr v_spell; -}; -struct ValInvocationInt -{ - BlockId v_iid; -}; -struct ValInvocationPtr -{ - dumb_ptr v_invocation; -}; -struct ValFail -{ -}; -struct ValNegative1 -{ -}; - -using ValVariantBase = Variant< - ValUndef, - ValInt, - ValDir, - ValString, - ValEntityInt, - ValEntityPtr, - ValLocation, - ValArea, - ValSpell, - ValInvocationInt, - ValInvocationPtr, - ValFail, - ValNegative1 ->; -struct val_t : ValVariantBase -{ - val_t() noexcept : ValVariantBase(ValUndef{}) {} - val_t(val_t&&) = default; - val_t(const val_t&) = delete; - val_t& operator = (val_t&&) = default; - val_t& operator = (const val_t&) = delete; - - val_t(ValUndef v) : ValVariantBase(std::move(v)) {} - val_t(ValInt v) : ValVariantBase(std::move(v)) {} - val_t(ValDir v) : ValVariantBase(std::move(v)) {} - val_t(ValString v) : ValVariantBase(std::move(v)) {} - val_t(ValEntityInt v) : ValVariantBase(std::move(v)) {} - val_t(ValEntityPtr v) : ValVariantBase(std::move(v)) {} - val_t(ValLocation v) : ValVariantBase(std::move(v)) {} - val_t(ValArea v) : ValVariantBase(std::move(v)) {} - val_t(ValSpell v) : ValVariantBase(std::move(v)) {} - val_t(ValInvocationInt v) : ValVariantBase(std::move(v)) {} - val_t(ValInvocationPtr v) : ValVariantBase(std::move(v)) {} - val_t(ValFail v) : ValVariantBase(std::move(v)) {} - val_t(ValNegative1 v) : ValVariantBase(std::move(v)) {} -}; - - -/* ----------- */ -/* Expressions */ -/* ----------- */ - -#define MAX_ARGS 7 /* Max. # of args used in builtin primitive functions */ - -struct e_area_t; - -struct e_location_t -{ - dumb_ptr m, x, y; - - e_location_t() noexcept : m(), x(), y() {} -}; -struct ExprAreaUnion -{ - dumb_ptr a_union[2]; -}; -struct ExprAreaRect -{ - e_location_t loc; - dumb_ptr width, height; -}; -struct ExprAreaBar -{ - e_location_t loc; - dumb_ptr width, depth, dir; -}; - -using ExprAreaVariantBase = Variant< - e_location_t, - ExprAreaUnion, - ExprAreaRect, - ExprAreaBar ->; - -struct e_area_t : ExprAreaVariantBase -{ - e_area_t() = delete; - e_area_t(e_area_t&&) = default; - e_area_t(const e_area_t&) = delete; - e_area_t& operator = (e_area_t&&) = default; - e_area_t& operator = (const e_area_t&) = delete; - - e_area_t(e_location_t v) : ExprAreaVariantBase(std::move(v)) {} - e_area_t(ExprAreaUnion v) : ExprAreaVariantBase(std::move(v)) {} - e_area_t(ExprAreaRect v) : ExprAreaVariantBase(std::move(v)) {} - e_area_t(ExprAreaBar v) : ExprAreaVariantBase(std::move(v)) {} -}; - -struct ExprFunApp -{ - fun_t *funp; - int line_nr, column; - int args_nr; - dumb_ptr args[MAX_ARGS]; -}; -struct ExprId -{ - int e_id; -}; -struct ExprField -{ - dumb_ptr expr; - int id; -}; - -using ExprVariantBase = Variant< - val_t, - e_location_t, - e_area_t, - ExprFunApp, - ExprId, - ExprField ->; -struct expr_t : ExprVariantBase -{ - expr_t() = delete; - expr_t(expr_t&&) = default; - expr_t(const expr_t&) = delete; - expr_t& operator = (expr_t&&) = default; - expr_t& operator = (const expr_t&) = delete; - - expr_t(val_t v) : ExprVariantBase(std::move(v)) {} - expr_t(e_location_t v) : ExprVariantBase(std::move(v)) {} - expr_t(e_area_t v) : ExprVariantBase(std::move(v)) {} - expr_t(ExprFunApp v) : ExprVariantBase(std::move(v)) {} - expr_t(ExprId v) : ExprVariantBase(std::move(v)) {} - expr_t(ExprField v) : ExprVariantBase(std::move(v)) {} -}; - - -struct effect_t; - -struct EffectSkip -{ -}; -struct EffectAbort -{ -}; -struct EffectAssign -{ - int id; - dumb_ptr expr; -}; -struct EffectForEach -{ - int id; - dumb_ptr area; - dumb_ptr body; - FOREACH_FILTER filter; -}; -struct EffectFor -{ - int id; - dumb_ptr start, stop; - dumb_ptr body; -}; -struct EffectIf -{ - dumb_ptr cond; - dumb_ptr true_branch, false_branch; -}; -struct EffectSleep -{ - dumb_ptr e_sleep; /* sleep time */ -}; -struct EffectScript -{ - dumb_ptr e_script; -}; -struct EffectBreak -{ -}; -struct EffectOp -{ - op_t *opp; - int args_nr; - int line_nr, column; - dumb_ptr args[MAX_ARGS]; -}; -struct EffectEnd -{ -}; -struct EffectCall -{ - std::vector *formalv; - dumb_ptr>> actualvp; - dumb_ptr body; -}; - -using EffectVariantBase = Variant< - EffectSkip, - EffectAbort, - EffectAssign, - EffectForEach, - EffectFor, - EffectIf, - EffectSleep, - EffectScript, - EffectBreak, - EffectOp, - EffectEnd, - EffectCall ->; -struct effect_t : EffectVariantBase -{ - dumb_ptr next; - - effect_t() = delete; - effect_t(effect_t&&) = default; - effect_t(const effect_t&) = delete; - effect_t& operator = (effect_t&&) = default; - effect_t& operator = (const effect_t&) = delete; - - effect_t(EffectSkip v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectAbort v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectAssign v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectForEach v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectFor v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectIf v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectSleep v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectScript v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectBreak v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectOp v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectEnd v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} - effect_t(EffectCall v, dumb_ptr n) : EffectVariantBase(std::move(v)), next(n) {} -}; - -/* ---------- */ -/* Components */ -/* ---------- */ - -struct component_t -{ - dumb_ptr next; - ItemNameId item_id; - int count; -}; - - -struct spellguard_t; -struct GuardCondition -{ - dumb_ptr s_condition; -}; -struct GuardMana -{ - dumb_ptr s_mana; -}; -struct GuardCastTime -{ - dumb_ptr s_casttime; -}; -struct GuardComponents -{ - dumb_ptr s_components; -}; -struct GuardCatalysts -{ - dumb_ptr s_catalysts; -}; -struct GuardChoice -{ - dumb_ptr s_alt; /* either `next' or `s.s_alt' */ -}; -struct effect_set_t -{ - dumb_ptr effect, at_trigger, at_end; -}; - -using SpellGuardVariantBase = Variant< - GuardCondition, - GuardMana, - GuardCastTime, - GuardComponents, - GuardCatalysts, - GuardChoice, - effect_set_t ->; -struct spellguard_t : SpellGuardVariantBase -{ - dumb_ptr next; - - spellguard_t() = delete; - spellguard_t(spellguard_t&&) = default; - spellguard_t(const spellguard_t&) = delete; - spellguard_t& operator = (spellguard_t&&) = default; - spellguard_t& operator = (const spellguard_t&) = delete; - - spellguard_t(GuardCondition v, dumb_ptr n) : SpellGuardVariantBase(std::move(v)), next(n) {} - spellguard_t(GuardMana v, dumb_ptr n) : SpellGuardVariantBase(std::move(v)), next(n) {} - spellguard_t(GuardCastTime v, dumb_ptr n) : SpellGuardVariantBase(std::move(v)), next(n) {} - spellguard_t(GuardComponents v, dumb_ptr n) : SpellGuardVariantBase(std::move(v)), next(n) {} - spellguard_t(GuardCatalysts v, dumb_ptr n) : SpellGuardVariantBase(std::move(v)), next(n) {} - spellguard_t(GuardChoice v, dumb_ptr n) : SpellGuardVariantBase(std::move(v)), next(n) {} - spellguard_t(effect_set_t v, dumb_ptr n) : SpellGuardVariantBase(std::move(v)), next(n) {} -}; - -/* ------ */ -/* Spells */ -/* ------ */ - -struct letdef_t -{ - int id; - dumb_ptr expr; -}; - -struct spell_t -{ - RString name; - RString invocation; - SPELL_FLAG flags; - int arg; - SPELLARG spellarg_ty; - - std::vector letdefv; - - dumb_ptr spellguard; -}; - -/* ------- */ -/* Anchors */ -/* ------- */ - -struct teleport_anchor_t -{ - RString name; - RString invocation; - dumb_ptr location; -}; - -/* ------------------- */ -/* The big config blob */ -/* ------------------- */ - -struct magic_conf_t -{ - struct mcvar - { - RString name; - val_t val; - }; - // This should probably be done by a dedicated "intern pool" class - std::vector varv; - - std::map> spells_by_name, spells_by_invocation; - - std::map> anchors_by_name, anchors_by_invocation; -}; - -/* Execution environment */ - -// these are not an enum they're a nasty intern hack -#define VAR_MIN_CASTTIME 0 -#define VAR_OBSCURE_CHANCE 1 -#define VAR_CASTER 2 -#define VAR_SPELLPOWER 3 -#define VAR_SPELL 4 -#define VAR_INVOCATION 5 -#define VAR_TARGET 6 -#define VAR_SCRIPTTARGET 7 -#define VAR_LOCATION 8 - -struct env_t -{ - magic_conf_t *base_env; - std::unique_ptr varu; - - val_t& VAR(size_t i) - { - assert (varu); - if (varu[i].is()) - return base_env->varv[i].val; - else - return varu[i]; - } - -}; - -struct CarForEach -{ - int id; - bool ty_is_spell_not_entity; - dumb_ptr body; - dumb_ptr> entities_vp; - int index; -}; -struct CarFor -{ - int id; - dumb_ptr body; - int current; - int stop; -}; -struct CarProc -{ - int args_nr; - int *formalap; - dumb_ptr old_actualpa; -}; - -using CarVariantBase = Variant< - CarForEach, - CarFor, - CarProc ->; - -struct cont_activation_record_t : CarVariantBase -{ - dumb_ptr return_location; - - cont_activation_record_t() = delete; - cont_activation_record_t(cont_activation_record_t&&) = default; - cont_activation_record_t(const cont_activation_record_t&) = delete; - cont_activation_record_t& operator = (cont_activation_record_t&&) = default; - cont_activation_record_t& operator = (const cont_activation_record_t&) = delete; - - cont_activation_record_t(CarForEach v, dumb_ptr rl) : CarVariantBase(std::move(v)), return_location(rl) {} - cont_activation_record_t(CarFor v, dumb_ptr rl) : CarVariantBase(std::move(v)), return_location(rl) {} - cont_activation_record_t(CarProc v, dumb_ptr rl) : CarVariantBase(std::move(v)), return_location(rl) {} -}; - -struct status_change_ref_t -{ - StatusChange sc_type; - BlockId bl_id; -}; - -struct invocation : block_list -{ - dumb_ptr next_invocation; /* used for spells directly associated with a caster: they form a singly-linked list */ - INVOCATION_FLAG flags; - - dumb_ptr env; - dumb_ptr spell; - BlockId caster; /* this is the person who originally invoked the spell */ - BlockId subject; /* when this person dies, the spell dies with it */ - - Timer timer; /* spell timer, if any */ - - std::vector stack; - - int script_pos; /* Script position; if nonzero, resume the script we were running. */ - dumb_ptr current_effect; - dumb_ptr trigger_effect; /* If non-nullptr, this is used to spawn a cloned effect based on the same environment */ - dumb_ptr end_effect; /* If non-nullptr, this is executed when the spell terminates naturally, e.g. when all status changes have run out or all delays are over. */ - - /* Status change references: for status change updates, keep track of whom we updated where */ - std::vector status_change_refv; - -}; -} // namespace magic - -// inlines for map.hpp -inline dumb_ptr block_list::as_spell() { return dumb_ptr(static_cast(this)); } -inline dumb_ptr block_list::is_spell() { return bl_type == BL::SPELL ? as_spell() : nullptr; } - -namespace magic -{ -/* The following is used only by the parser: */ -struct args_rec_t -{ - dumb_ptr>> argvp; -}; - -struct proc_t -{ - RString name; - std::vector argv; - dumb_ptr body; - - proc_t() - : name() - , argv() - , body() - {} -}; -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-interpreter.py b/src/map/magic-interpreter.py deleted file mode 100644 index 520ab37..0000000 --- a/src/map/magic-interpreter.py +++ /dev/null @@ -1,215 +0,0 @@ -class AreaUnion(object): - __slots__ = ('_value') - name = 'tmwa::map::magic::AreaUnion' - enabled = True - - def __init__(self, value): - self._value = value - - def display_hint(self): - return 'array' - - def to_string(self): - return None - - def children(self): - v = self._value - for i in [0, 1]: - yield '[%d]', v['a_union'][i]['impl'].dereference() - - tests = [] - -class area_t(object): - enabled = True - - test_extra = ''' - #include "../strings/fwd.hpp" - using tmwa::operator "" _s; - - inline - tmwa::Borrowed fake_map_local_x_dup_for_area_t(tmwa::ZString name) - { - auto *p = new tmwa::map::map_local{}; - p->name_ = tmwa::stringish(name); - return tmwa::borrow(*p); - } - ''' - - tests = [ - ('tmwa::map::magic::area_t(tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456})', - '{> = {(tmwa::map::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}}, size = 1}'), - ('tmwa::map::magic::area_t(tmwa::map::magic::AreaUnion{{tmwa::dumb_ptr::make(tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456}), tmwa::dumb_ptr::make(tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 321, 654})}})', - '{> = {(tmwa::map::magic::AreaUnion) = {{> = {(tmwa::map::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}}, size = 1}, {> = {(tmwa::map::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 321, y = 654}}, size = 1}}}, size = 2}'), - ('tmwa::map::magic::area_t(tmwa::map::magic::AreaRect{tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456}, 789, 102})', - '{> = {(tmwa::map::magic::AreaRect) = {loc = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}, width = 789, height = 102}}, size = 80478}'), - ('tmwa::map::magic::area_t(tmwa::map::magic::AreaBar{tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 42, 43}, 123, 456, tmwa::DIR::NW})', - '{> = {(tmwa::map::magic::AreaBar) = {loc = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 42, y = 43}, width = 123, depth = 456, dir = tmwa::DIR::NW}}, size = 112632}'), - ] - - -class val_t(object): - enabled = True - - test_extra = ''' - #include "../strings/fwd.hpp" - using tmwa::operator "" _s; - - inline - tmwa::Borrowed fake_map_local_x_dup_for_val_t(tmwa::ZString name) - { - auto *p = new tmwa::map::map_local{}; - p->name_ = tmwa::stringish(name); - return tmwa::borrow(*p); - } - ''' - - tests = [ - ('tmwa::map::magic::val_t(tmwa::map::magic::ValUndef{})', - '{> = {(tmwa::map::magic::ValUndef) = {}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValInt{42})', - '{> = {(tmwa::map::magic::ValInt) = {v_int = 42}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValDir{tmwa::DIR::NW})', - '{> = {(tmwa::map::magic::ValDir) = {v_dir = tmwa::DIR::NW}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValString{"Hello"_s})', - '{> = {(tmwa::map::magic::ValString) = {v_string = "Hello"}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValEntityInt{tmwa::wrap(123)})', - '{> = {(tmwa::map::magic::ValEntityInt) = {v_eid = 123}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValEntityPtr{tmwa::dumb_ptr()})', - '{> = {(tmwa::map::magic::ValEntityPtr) = {v_entity = 0x0}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValLocation{tmwa::map::magic::location_t{fake_map_local_x_dup_for_val_t("map"_s), 42, 123}})', - '{> = {(tmwa::map::magic::ValLocation) = {v_location = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 42, y = 123}}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValArea{tmwa::dumb_ptr()})', - '{> = {(tmwa::map::magic::ValArea) = {v_area = 0x0}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValSpell{tmwa::dumb_ptr()})', - '{> = {(tmwa::map::magic::ValSpell) = {v_spell = 0x0}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValInvocationInt{tmwa::wrap(123)})', - '{> = {(tmwa::map::magic::ValInvocationInt) = {v_iid = 123}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValInvocationPtr{})', - '{> = {(tmwa::map::magic::ValInvocationPtr) = {v_invocation = 0x0}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValFail{})', - '{> = {(tmwa::map::magic::ValFail) = {}}, }'), - ('tmwa::map::magic::val_t(tmwa::map::magic::ValNegative1{})', - '{> = {(tmwa::map::magic::ValNegative1) = {}}, }'), - ] - - -class ExprAreaUnion(object): - __slots__ = ('_value') - name = 'tmwa::map::magic::ExprAreaUnion' - enabled = True - - def __init__(self, value): - self._value = value - - def display_hint(self): - return 'array' - - def to_string(self): - return None - - def children(self): - v = self._value - for i in [0, 1]: - yield '[%d]', v['a_union'][i]['impl'].dereference() - - tests = [] - - -class e_area_t(object): - enabled = True - - tests = [ - ('tmwa::map::magic::e_area_t(tmwa::map::magic::e_location_t())', - '{> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, }'), - ('tmwa::map::magic::e_area_t(tmwa::map::magic::ExprAreaUnion{{tmwa::dumb_ptr::make(tmwa::map::magic::e_location_t()), tmwa::dumb_ptr::make(tmwa::map::magic::e_location_t())}})', - '{> = {(tmwa::map::magic::ExprAreaUnion) = {{> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, }, {> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, }}}, }'), - ('tmwa::map::magic::e_area_t(tmwa::map::magic::ExprAreaRect{tmwa::map::magic::e_location_t(), tmwa::dumb_ptr(), tmwa::dumb_ptr()})', - '{> = {(tmwa::map::magic::ExprAreaRect) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, height = 0x0}}, }'), - ('tmwa::map::magic::e_area_t(tmwa::map::magic::ExprAreaBar{tmwa::map::magic::e_location_t(), tmwa::dumb_ptr(), tmwa::dumb_ptr(), tmwa::dumb_ptr()})', - '{> = {(tmwa::map::magic::ExprAreaBar) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, depth = 0x0, dir = 0x0}}, }'), - ] - - - -class expr_t(object): - enabled = True - - tests = [ - ('tmwa::map::magic::expr_t(tmwa::map::magic::val_t(tmwa::map::magic::ValUndef()))', - '{> = {(tmwa::map::magic::val_t) = {> = {(tmwa::map::magic::ValUndef) = {}}, }}, }'), - ('tmwa::map::magic::expr_t(tmwa::map::magic::e_location_t())', - '{> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, }'), - ('tmwa::map::magic::expr_t(tmwa::map::magic::e_area_t(tmwa::map::magic::e_location_t()))', - '{> = {(tmwa::map::magic::e_area_t) = {> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, }}, }'), - ('tmwa::map::magic::expr_t(tmwa::map::magic::ExprFunApp())', - '{> = {(tmwa::map::magic::ExprFunApp) = {funp = (fun_t *) nullptr, line_nr = 0, column = 0, args_nr = 0, args = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, }'), - ('tmwa::map::magic::expr_t(tmwa::map::magic::ExprId{123})', - '{> = {(tmwa::map::magic::ExprId) = {e_id = 123}}, }'), - ('tmwa::map::magic::expr_t(tmwa::map::magic::ExprField{tmwa::dumb_ptr(), 42})', - '{> = {(tmwa::map::magic::ExprField) = {expr = 0x0, id = 42}}, }'), - ] - - -class effect_t(object): - enabled = True - - tests = [ - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectSkip{}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectSkip) = {}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectAbort{}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectAbort) = {}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectAssign{42, tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectAssign) = {id = 42, expr = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectForEach{123, tmwa::dumb_ptr(), tmwa::dumb_ptr(), tmwa::map::magic::FOREACH_FILTER::PC}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectForEach) = {id = 123, area = 0x0, body = 0x0, filter = tmwa::map::magic::FOREACH_FILTER::PC}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectFor{42, tmwa::dumb_ptr(), tmwa::dumb_ptr(), tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectFor) = {id = 42, start = 0x0, stop = 0x0, body = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectIf{tmwa::dumb_ptr(), tmwa::dumb_ptr(), tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectIf) = {cond = 0x0, true_branch = 0x0, false_branch = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectSleep{tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectSleep) = {e_sleep = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectScript{tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectScript) = {e_script = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectBreak{}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectBreak) = {}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectOp(), tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectOp) = {opp = (op_t *) nullptr, args_nr = 0, line_nr = 0, column = 0, args = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectEnd{}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectEnd) = {}}, next = 0x0}'), - ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectCall{nullptr, tmwa::dumb_ptr>>(), tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::EffectCall) = {formalv = nullptr, actualvp = 0x0, body = 0x0}}, next = 0x0}'), - ] - - -class spellguard_t(object): - enabled = True - - tests = [ - ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardCondition{tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::GuardCondition) = {s_condition = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardMana{tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::GuardMana) = {s_mana = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardCastTime{tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::GuardCastTime) = {s_casttime = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardComponents{tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::GuardComponents) = {s_components = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardCatalysts{tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::GuardCatalysts) = {s_catalysts = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardChoice{tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::GuardChoice) = {s_alt = 0x0}}, next = 0x0}'), - ('tmwa::map::magic::spellguard_t(tmwa::map::magic::effect_set_t{tmwa::dumb_ptr(), tmwa::dumb_ptr(), tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::effect_set_t) = {effect = 0x0, at_trigger = 0x0, at_end = 0x0}}, next = 0x0}'), - ] - - -class cont_activation_record_t(object): - enabled = True - - tests = [ - ('tmwa::map::magic::cont_activation_record_t(tmwa::map::magic::CarForEach{42, true, tmwa::dumb_ptr(), tmwa::dumb_ptr>(), 123}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::CarForEach) = {id = 42, ty_is_spell_not_entity = true, body = 0x0, entities_vp = 0x0, index = 123}}, return_location = 0x0}'), - ('tmwa::map::magic::cont_activation_record_t(tmwa::map::magic::CarFor{42, tmwa::dumb_ptr(), 123, 456}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::CarFor) = {id = 42, body = 0x0, current = 123, stop = 456}}, return_location = 0x0}'), - ('tmwa::map::magic::cont_activation_record_t(tmwa::map::magic::CarProc{123, nullptr, tmwa::dumb_ptr()}, tmwa::dumb_ptr())', - '{> = {(tmwa::map::magic::CarProc) = {args_nr = 123, formalap = nullptr, old_actualpa = 0x0 = {sz = 0}}}, return_location = 0x0}'), - ] diff --git a/src/map/magic-interpreter.t.hpp b/src/map/magic-interpreter.t.hpp deleted file mode 100644 index e302354..0000000 --- a/src/map/magic-interpreter.t.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once -// magic-interpreter.t.hpp - Old magic. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "fwd.hpp" - -#include "../generic/enum.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -enum class SPELLARG : uint8_t -{ - NONE, - PC, - STRING, -}; - -enum class FOREACH_FILTER : uint8_t -{ - MOB, - PC, - ENTITY, - TARGET, - SPELL, - NPC, -}; - -namespace e -{ -enum class SPELL_FLAG : uint8_t -{ - ZERO = 0, - - // spell associated not with caster but with place - LOCAL = 1 << 0, - // spell invocation never uttered - SILENT = 1 << 1, - // `magic word' only: don't require spellcasting ability - NONMAGIC = 1 << 2, -}; -ENUM_BITWISE_OPERATORS(SPELL_FLAG) -} -using e::SPELL_FLAG; - -namespace e -{ -enum class INVOCATION_FLAG : uint8_t -{ - ZERO = 0, - - // Bound directly to the caster (i.e., ignore its location) - BOUND = 1 << 0, - // Used `abort' to terminate - ABORTED = 1 << 1, - // On magical attacks: if we run out of steam, stop attacking altogether - STOPATTACK = 1 << 2, -}; -ENUM_BITWISE_OPERATORS(INVOCATION_FLAG) -} -using e::INVOCATION_FLAG; -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-stmt.cpp b/src/map/magic-stmt.cpp deleted file mode 100644 index 1a8085b..0000000 --- a/src/map/magic-stmt.cpp +++ /dev/null @@ -1,1547 +0,0 @@ -#include "magic-stmt.hpp" -// magic-stmt.cpp - Imperative commands for the magic backend. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include - -#include "../compat/attr.hpp" -#include "../compat/fun.hpp" - -#include "../strings/zstring.hpp" - -#include "../generic/random2.hpp" - -#include "../io/cxxstdio.hpp" - -#include "../mmo/cxxstdio_enums.hpp" - -#include "../net/timer.hpp" - -#include "battle.hpp" -#include "clif.hpp" -#include "magic.hpp" -#include "magic-expr.hpp" -#include "magic-expr-eval.hpp" -#include "magic-interpreter.hpp" -#include "magic-interpreter-base.hpp" -#include "mob.hpp" -#include "npc.hpp" -#include "npc-parse.hpp" -#include "pc.hpp" -#include "script-call.hpp" -#include "skill.hpp" - -#include "../poison.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -/* used for local spell effects */ -constexpr Species INVISIBLE_NPC = wrap(127); - -static -void clear_activation_record(cont_activation_record_t *ar) -{ - MATCH_BEGIN (*ar) - { - MATCH_CASE (CarForEach&, c_foreach) - { - c_foreach.entities_vp.delete_(); - } - MATCH_CASE (CarProc&, c_proc) - { - c_proc.old_actualpa.delete_(); - } - } - MATCH_END (); -} - -static -void invocation_timer_callback(TimerData *, tick_t, BlockId id) -{ - dumb_ptr invocation = map_id_is_spell(id); - - assert (invocation); - { - spell_execute(invocation); - } -} - -static -void clear_stack(dumb_ptr invocation_) -{ - int i; - - for (i = 0; i < invocation_->stack.size(); i++) - clear_activation_record(&invocation_->stack[i]); - - invocation_->stack.clear(); -} - -void spell_free_invocation(dumb_ptr invocation_) -{ - invocation_->status_change_refv.clear(); - - if (bool(invocation_->flags & INVOCATION_FLAG::BOUND)) - { - dumb_ptr e = map_id_is_player(invocation_->subject); - if (e) - spell_unbind(e, invocation_); - } - - clear_stack(invocation_); - - invocation_->timer.cancel(); - - magic_free_env(invocation_->env); - - map_delblock(invocation_); - map_delobject(invocation_->bl_id, BL::SPELL); // also frees the object -// free(invocation_); -} - -static -void char_set_weapon_icon(dumb_ptr subject, int count, - StatusChange icon, ItemNameId look) -{ - const StatusChange old_icon = subject->attack_spell_icon_override; - - subject->attack_spell_icon_override = icon; - subject->attack_spell_look_override = look; - - if (old_icon != StatusChange::ZERO && old_icon != icon) - clif_status_change(subject, old_icon, 0); - - clif_fixpcpos(subject); - if (count) - { - clif_changelook(subject, LOOK::WEAPON, unwrap(look)); - if (icon != StatusChange::ZERO) - clif_status_change(subject, icon, 1); - } - else - { - /* Set it to `normal' */ - clif_changelook(subject, LOOK::WEAPON, - static_cast(subject->status.weapon)); - } -} - -static -void char_set_attack_info(dumb_ptr subject, interval_t speed, int range) -{ - subject->attack_spell_delay = speed; - subject->attack_spell_range = range; - - if (speed == interval_t::zero()) - { - pc_calcstatus(subject, 1); - clif_updatestatus(subject, SP::ASPD); - clif_updatestatus(subject, SP::ATTACKRANGE); - } - else - { - subject->aspd = speed; - clif_updatestatus(subject, SP::ASPD); - clif_updatestatus(subject, SP::ATTACKRANGE); - } -} - -void magic_stop_completely(dumb_ptr c) -{ - // Zap all status change references to spells - for (StatusChange i : erange(StatusChange(), StatusChange::MAX_STATUSCHANGE)) - c->sc_data[i].spell_invocation = BlockId(); - - while (c->active_spells) - spell_free_invocation(c->active_spells); - - if (c->attack_spell_override) - { - dumb_ptr attack_spell = map_id_is_spell(c->attack_spell_override); - if (attack_spell) - spell_free_invocation(attack_spell); - c->attack_spell_override = BlockId(); - char_set_weapon_icon(c, 0, StatusChange::ZERO, ItemNameId()); - char_set_attack_info(c, interval_t::zero(), 0); - } -} - -/* Spell execution has finished normally or we have been notified by a finished skill timer */ -static -void try_to_finish_invocation(dumb_ptr invocation) -{ - if (invocation->status_change_refv.empty() && !invocation->current_effect) - { - if (invocation->end_effect) - { - clear_stack(invocation); - invocation->current_effect = invocation->end_effect; - invocation->end_effect = nullptr; - spell_execute(invocation); - } - else - spell_free_invocation(invocation); - } -} - -static -BlockId trigger_spell(BlockId subject, BlockId spell) -{ - dumb_ptr invocation_ = map_id_is_spell(spell); - - if (!invocation_) - return BlockId(); - - invocation_ = spell_clone_effect(invocation_); - - spell_bind(map_id_is_player(subject), invocation_); - magic_clear_var(&invocation_->env->varu[VAR_CASTER]); - invocation_->env->varu[VAR_CASTER] = ValEntityInt{subject}; - - return invocation_->bl_id; -} - -static -void entity_warp(dumb_ptr target, Borrowed destm, int destx, int desty); - -static -void char_update(dumb_ptr character) -{ - entity_warp(character, character->bl_m, character->bl_x, - character->bl_y); -} - -static -void timer_callback_effect(TimerData *, tick_t, BlockId id, int data) -{ - dumb_ptr target = map_id2bl(id); - if (target) - clif_misceffect(target, data); -} - -static -void entity_effect(dumb_ptr entity, int effect_nr, interval_t delay) -{ - Timer(gettick() + delay, - std::bind(&timer_callback_effect, ph::_1, ph::_2, - entity->bl_id, effect_nr) - ).detach(); -} - -void magic_unshroud(dumb_ptr other_char) -{ - other_char->state.shroud_active = 0; - // Now warp the caster out of and back into here to refresh everyone's display - char_update(other_char); - clif_displaymessage(other_char->sess, "Your shroud has been dispelled!"_s); -// entity_effect(other_char, MAGIC_EFFECT_REVEAL); -} - -static -void timer_callback_effect_npc_delete(TimerData *, tick_t, BlockId npc_id) -{ - dumb_ptr effect_npc = map_id_is_npc(npc_id); - npc_free(effect_npc); -} - -static -dumb_ptr local_spell_effect(Borrowed m, int x, int y, int effect, - interval_t tdelay) -{ - /* 1 minute should be enough for all interesting spell effects, I hope */ - std::chrono::seconds delay = 30_s; - dumb_ptr effect_npc = npc_spawn_text(m, x, y, - INVISIBLE_NPC, NpcName(), "?"_s); - BlockId effect_npc_id = effect_npc->bl_id; - - entity_effect(effect_npc, effect, tdelay); - Timer(gettick() + delay, - std::bind(timer_callback_effect_npc_delete, ph::_1, ph::_2, - effect_npc_id) - ).detach(); - - return effect_npc; -} - -static -int op_sfx(dumb_ptr, Slice args) -{ - interval_t delay = static_cast(ARGINT(2)); - - if (args[0].is()) - { - entity_effect(ARGENTITY(0), ARGINT(1), delay); - } - else if (args[0].is()) - { - local_spell_effect(ARGLOCATION(0).m, - ARGLOCATION(0).x, - ARGLOCATION(0).y, ARGINT(1), delay); - } - else - return 1; - - return 0; -} - -static -int op_instaheal(dumb_ptr env, Slice args) -{ - assert (!env->VAR(VAR_CASTER).is()); - ValEntityInt *caster_id = env->VAR(VAR_CASTER).get_if(); - dumb_ptr caster = caster_id - ? map_id2bl(caster_id->v_eid) : nullptr; - dumb_ptr subject = ARGENTITY(0); - if (!caster) - caster = subject; - - if (caster->bl_type == BL::PC && subject->bl_type == BL::PC) - { - dumb_ptr caster_pc = caster->is_player(); - dumb_ptr subject_pc = subject->is_player(); - MAP_LOG_PC(caster_pc, "SPELLHEAL-INSTA PC%d FOR %d"_fmt, - subject_pc->status_key.char_id, ARGINT(1)); - } - - battle_heal(caster, subject, ARGINT(1), ARGINT(2), 0); - return 0; -} - -static -int op_itemheal(dumb_ptr env, Slice args) -{ - dumb_ptr subject = ARGENTITY(0); - if (subject->bl_type == BL::PC) - { - pc_itemheal(subject->is_player(), - ARGINT(1), ARGINT(2)); - } - else - return op_instaheal(env, args); - - return 0; -} - -namespace e -{ -enum class Shroud -{ - HIDE_NAME_TALKING_FLAG = 1 << 0, - DISAPPEAR_ON_PICKUP_FLAG = 1 << 1, - DISAPPEAR_ON_TALK_FLAG = 1 << 2, -}; -ENUM_BITWISE_OPERATORS(Shroud) -} -using e::Shroud; - -// differs from ARGPC by checking -#define ARGCHAR(n) (ARGENTITY(n)->is_player()) - -static -int op_shroud(dumb_ptr, Slice args) -{ - dumb_ptr subject = ARGCHAR(0); - Shroud arg = static_cast(ARGINT(1)); - - if (!subject) - return 0; - - subject->state.shroud_active = 1; - subject->state.shroud_hides_name_talking = - bool(arg & Shroud::HIDE_NAME_TALKING_FLAG); - subject->state.shroud_disappears_on_pickup = - bool(arg & Shroud::DISAPPEAR_ON_PICKUP_FLAG); - subject->state.shroud_disappears_on_talk = - bool(arg & Shroud::DISAPPEAR_ON_TALK_FLAG); - return 0; -} - -static -int op_reveal(dumb_ptr, Slice args) -{ - dumb_ptr subject = ARGCHAR(0); - - if (subject && subject->state.shroud_active) - magic_unshroud(subject); - - return 0; -} - -static -int op_message(dumb_ptr, Slice args) -{ - dumb_ptr subject = ARGCHAR(0); - - if (subject) - clif_displaymessage(subject->sess, ARGSTR(1)); - - return 0; -} - -static -void timer_callback_kill_npc(TimerData *, tick_t, BlockId npc_id) -{ - dumb_ptr npc = map_id_is_npc(npc_id); - if (npc) - npc_free(npc); -} - -static -int op_messenger_npc(dumb_ptr, Slice args) -{ - dumb_ptr npc; - location_t *loc = &ARGLOCATION(0); - - NpcName npcname = stringish(ARGSTR(2)); - npc = npc_spawn_text(loc->m, loc->x, loc->y, - wrap(static_cast(ARGINT(1))), npcname, ARGSTR(3)); - - Timer(gettick() + static_cast(ARGINT(4)), - std::bind(timer_callback_kill_npc, ph::_1, ph::_2, - npc->bl_id) - ).detach(); - - return 0; -} - -static -void entity_warp(dumb_ptr target, Borrowed destm, int destx, int desty) -{ - if (target->bl_type == BL::PC || target->bl_type == BL::MOB) - { - - switch (target->bl_type) - { - case BL::PC: - { - dumb_ptr character = target->is_player(); - clif_clearchar(character, BeingRemoveWhy::WARPED); - map_delblock(character); - character->bl_x = destx; - character->bl_y = desty; - character->bl_m = destm; - - pc_touch_all_relevant_npcs(character); - - // Note that touching NPCs may have triggered warping and thereby updated x and y: - MapName map_name = character->bl_m->name_; - - // Warp part #1: update relevant data, interrupt trading etc.: - pc_setpos(character, map_name, character->bl_x, character->bl_y, BeingRemoveWhy::GONE); - // Warp part #2: now notify the client - clif_changemap(character, map_name, - character->bl_x, character->bl_y); - break; - } - case BL::MOB: - target->bl_x = destx; - target->bl_y = desty; - target->bl_m = destm; - clif_fixmobpos(target->is_mob()); - break; - } - } -} - -static -int op_move(dumb_ptr, Slice args) -{ - dumb_ptr subject = ARGENTITY(0); - DIR dir = ARGDIR(1); - - int newx = subject->bl_x + dirx[dir]; - int newy = subject->bl_y + diry[dir]; - - if (!bool(map_getcell(subject->bl_m, newx, newy) & MapCell::UNWALKABLE)) - entity_warp(subject, subject->bl_m, newx, newy); - - return 0; -} - -static -int op_warp(dumb_ptr, Slice args) -{ - dumb_ptr subject = ARGENTITY(0); - location_t *loc = &ARGLOCATION(1); - - entity_warp(subject, loc->m, loc->x, loc->y); - - return 0; -} - -static -int op_banish(dumb_ptr, Slice args) -{ - dumb_ptr subject = ARGENTITY(0); - - if (subject->bl_type == BL::MOB) - { - dumb_ptr mob = subject->is_mob(); - - if (bool(mob->mode & MobMode::SUMMONED)) - mob_catch_delete(mob, BeingRemoveWhy::WARPED); - } - - return 0; -} - -static -void record_status_change(dumb_ptr invocation_, BlockId bl_id, - StatusChange sc_id) -{ - status_change_ref_t cr {}; - cr.sc_type = sc_id; - cr.bl_id = bl_id; - - invocation_->status_change_refv.push_back(cr); -} - -static -int op_status_change(dumb_ptr env, Slice args) -{ - dumb_ptr subject = ARGENTITY(0); - assert (!env->VAR(VAR_INVOCATION).is()); - ValInvocationInt *ii = env->VAR(VAR_INVOCATION).get_if(); - BlockId invocation_id = ii - ? ii->v_iid : BlockId(); - dumb_ptr invocation_ = map_id_is_spell(invocation_id); - - assert (!ARGINT(3)); - assert (!ARGINT(4)); - assert (!ARGINT(5)); - skill_status_effect(subject, static_cast(ARGINT(1)), - ARGINT(2), - static_cast(ARGINT(6)), invocation_id); - - if (invocation_ && subject->bl_type == BL::PC) - record_status_change(invocation_, subject->bl_id, static_cast(ARGINT(1))); - - return 0; -} - -static -int op_stop_status_change(dumb_ptr, Slice args) -{ - dumb_ptr subject = ARGENTITY(0); - - StatusChange sc = static_cast(ARGINT(1)); - skill_status_change_end(subject, sc, nullptr); - - return 0; -} - -static -int op_override_attack(dumb_ptr env, Slice args) -{ - dumb_ptr psubject = ARGENTITY(0); - int charges = ARGINT(1); - interval_t attack_delay = static_cast(ARGINT(2)); - int attack_range = ARGINT(3); - StatusChange icon = StatusChange(ARGINT(4)); - ItemNameId look = wrap(static_cast(ARGINT(5))); - int stopattack = ARGINT(6); - dumb_ptr subject; - - if (psubject->bl_type != BL::PC) - return 0; - - subject = psubject->is_player(); - - if (subject->attack_spell_override) - { - dumb_ptr old_invocation = map_id_is_spell(subject->attack_spell_override); - if (old_invocation) - spell_free_invocation(old_invocation); - } - - ValInvocationInt *ii = env->VAR(VAR_INVOCATION).get_if(); - subject->attack_spell_override = - trigger_spell(subject->bl_id, ii->v_iid); - subject->attack_spell_charges = charges; - - if (subject->attack_spell_override) - { - dumb_ptr attack_spell = map_id_is_spell(subject->attack_spell_override); - if (attack_spell && stopattack) - attack_spell->flags |= INVOCATION_FLAG::STOPATTACK; - - char_set_weapon_icon(subject, charges, icon, look); - char_set_attack_info(subject, attack_delay, attack_range); - } - - return 0; -} - -static -int op_create_item(dumb_ptr, Slice args) -{ - Item item; - dumb_ptr entity = ARGENTITY(0); - dumb_ptr subject; - int stackable; - int count = ARGINT(2); - if (count <= 0) - return 0; - - if (entity->bl_type == BL::PC) - subject = entity->is_player(); - else - return 0; - - GET_ARG_ITEM(1, item, stackable); - - if (!stackable) - while (count--) - pc_additem(subject, &item, 1); - else - pc_additem(subject, &item, count); - - return 0; -} - -inline -bool AGGRAVATION_MODE_ATTACKS_CASTER(int n) -{ - return n == 0 || n == 2; -} -inline -bool AGGRAVATION_MODE_MAKES_AGGRESSIVE(int n) -{ - return n > 0; -} - -static -int op_aggravate(dumb_ptr, Slice args) -{ - dumb_ptr victim = ARGENTITY(2); - int mode = ARGINT(1); - dumb_ptr target = ARGENTITY(0); - dumb_ptr other; - - if (target->bl_type == BL::MOB) - other = target->is_mob(); - else - return 0; - - mob_target(other, victim, battle_get_range(victim)); - - if (AGGRAVATION_MODE_MAKES_AGGRESSIVE(mode)) - other->mode = MobMode::war | (other->mode & MobMode::SENSIBLE_MASK); - - if (AGGRAVATION_MODE_ATTACKS_CASTER(mode)) - { - other->target_id = victim->bl_id; - other->attacked_id = victim->bl_id; - } - - return 0; -} - -enum class MonsterAttitude -{ - HOSTILE = 0, - FRIENDLY = 1, - SERVANT = 2, - FROZEN = 3, -}; - -static -int op_spawn(dumb_ptr, Slice args) -{ - dumb_ptr area = ARGAREA(0); - dumb_ptr owner_e = ARGENTITY(1); - Species monster_id = wrap(ARGINT(2)); - MonsterAttitude monster_attitude = static_cast(ARGINT(3)); - int monster_count = ARGINT(4); - interval_t monster_lifetime = static_cast(ARGINT(5)); - int i; - - dumb_ptr owner = nullptr; - if (monster_attitude == MonsterAttitude::SERVANT - && owner_e->bl_type == BL::PC) - owner = owner_e->is_player(); - - for (i = 0; i < monster_count; i++) - { - location_t loc; - magic_random_location(&loc, area); - - BlockId mob_id; - dumb_ptr mob; - - mob_id = mob_once_spawn(owner, loc.m->name_, loc.x, loc.y, JAPANESE_NAME, // Is that needed? - monster_id, 1, NpcEvent()); - - mob = map_id_is_mob(mob_id); - - if (mob) - { - mob->mode = get_mob_db(monster_id).mode; - - switch (monster_attitude) - { - case MonsterAttitude::SERVANT: - mob->state.special_mob_ai = 1; - mob->mode |= MobMode::AGGRESSIVE; - break; - - case MonsterAttitude::FRIENDLY: - mob->mode = MobMode::CAN_ATTACK | (mob->mode & MobMode::CAN_MOVE); - break; - - case MonsterAttitude::HOSTILE: - mob->mode = MobMode::CAN_ATTACK | MobMode::AGGRESSIVE | (mob->mode & MobMode::CAN_MOVE); - if (owner) - { - mob->target_id = owner->bl_id; - mob->attacked_id = owner->bl_id; - } - break; - - case MonsterAttitude::FROZEN: - mob->mode = MobMode::ZERO; - break; - } - - mob->mode |= - MobMode::SUMMONED | MobMode::TURNS_AGAINST_BAD_MASTER; - - mob->deletetimer = Timer(gettick() + monster_lifetime, - std::bind(mob_timer_delete, ph::_1, ph::_2, - mob_id)); - - if (owner) - { - mob->master_id = owner->bl_id; - mob->master_dist = 6; - } - } - } - - return 0; -} - -static -ZString get_invocation_name(dumb_ptr env) -{ - assert (!env->VAR(VAR_INVOCATION).is()); - - ValInvocationInt *ii = env->VAR(VAR_INVOCATION).get_if(); - if (!ii) - return "?"_s; - - dumb_ptr invocation_; - invocation_ = map_id_is_spell(ii->v_iid); - - if (invocation_) - return invocation_->spell->name; - else - return "??"_s; -} - -static -int op_injure(dumb_ptr env, Slice args) -{ - dumb_ptr caster = ARGENTITY(0); - dumb_ptr target = ARGENTITY(1); - int damage_caused = ARGINT(2); - int mp_damage = ARGINT(3); - int target_hp = battle_get_hp(target); - int mdef = battle_get_mdef(target); - - if (target->bl_type == BL::PC // target is player - && !target->bl_m->flag.get(MapFlag::PVP) // there is no pvpon flag - && (caster->bl_type == BL::PC) // caster is player - && ((target->is_player()->state.pvpchannel == 0) - || ((caster->is_player()->state.pvpchannel > 0) - && (target->is_player()->state.pvpchannel != caster->is_player()->state.pvpchannel)))) - return 0; /* Cannot damage other players outside of pvp */ - - if (target != caster) - { - /* Not protected against own spells */ - damage_caused = (damage_caused * (100 - mdef)) / 100; - mp_damage = (mp_damage * (100 - mdef)) / 100; - } - - damage_caused = (damage_caused > target_hp) ? target_hp : damage_caused; - - if (damage_caused < 0) - damage_caused = 0; - - // display damage first, because dealing damage may deallocate the target. - clif_damage(caster, target, - gettick(), interval_t::zero(), interval_t::zero(), - damage_caused, 0, DamageType::NORMAL); - - if (caster->bl_type == BL::PC) - { - dumb_ptr caster_pc = caster->is_player(); - if (target->bl_type == BL::MOB) - { - dumb_ptr mob = target->is_mob(); - - MAP_LOG_PC(caster_pc, "SPELLDMG MOB%d %d FOR %d BY %s"_fmt, - mob->bl_id, mob->mob_class, damage_caused, - get_invocation_name(env)); - } - } - battle_damage(caster, target, damage_caused, mp_damage); - - return 0; -} - -static -int op_emote(dumb_ptr, Slice args) -{ - dumb_ptr victim = ARGENTITY(0); - int emotion = ARGINT(1); - clif_emotion(victim, emotion); - - return 0; -} - -static -int op_set_script_variable(dumb_ptr, Slice args) -{ - dumb_ptr c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : nullptr; - VarName varname = stringish(ARGSTR(1)); - int array_index = 0; - - if (!c) - return 1; - - set_script_var_i(c, varname, array_index, ARGINT(2)); - - return 0; -} - -static -int op_set_script_str(dumb_ptr, Slice args) -{ - dumb_ptr c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : nullptr; - VarName varname = stringish(ARGSTR(1)); - int array_index = 0; - - if (!c) - return 1; - - set_script_var_s(c, varname, array_index, ARGSTR(2)); - - return 0; -} - -static -int op_set_hair_colour(dumb_ptr, Slice args) -{ - dumb_ptr c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : nullptr; - - if (!c) - return 1; - - pc_changelook(c, LOOK::HAIR_COLOR, ARGINT(1)); - - return 0; -} - -static -int op_set_hair_style(dumb_ptr, Slice args) -{ - dumb_ptr c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : nullptr; - - if (!c) - return 1; - - pc_changelook(c, LOOK::HAIR, ARGINT(1)); - - return 0; -} - -static -int op_drop_item_for (dumb_ptr, Slice args) -{ - Item item; - int stackable; - location_t *loc = &ARGLOCATION(0); - int count = ARGINT(2); - interval_t interval = static_cast(ARGINT(3)); - dumb_ptr c = ((args.size() > 4) && (ENTITY_TYPE(4) == BL::PC)) ? ARGPC(4) : nullptr; - interval_t delay = (args.size() > 5) ? static_cast(ARGINT(5)) : interval_t::zero(); - interval_t delaytime[3] = { delay, delay, delay }; - dumb_ptr owners[3] = { c, nullptr, nullptr }; - - GET_ARG_ITEM(1, item, stackable); - - if (stackable) - { - map_addflooritem_any(&item, count, loc->m, loc->x, loc->y, - owners, delaytime, interval, 0); - } - else - { - while (count-- > 0) - map_addflooritem_any(&item, 1, loc->m, loc->x, loc->y, - owners, delaytime, interval, 0); - } - - return 0; -} - -static -int op_gain_exp(dumb_ptr, Slice args) -{ - dumb_ptr c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : nullptr; - - if (!c) - return 1; - - pc_gainexp_reason(c, ARGINT(1), ARGINT(2), - static_cast(ARGINT(3))); - return 0; -} - -#define MAGIC_OPERATION(name, args, impl) {{name}, {{name}, {args}, impl}} -#define MAGIC_OPERATION1(name, args) MAGIC_OPERATION(#name##_s, args, op_##name) -static -std::map operations = -{ - MAGIC_OPERATION1(sfx, ".ii"_s), - MAGIC_OPERATION1(instaheal, "eii"_s), - MAGIC_OPERATION1(itemheal, "eii"_s), - MAGIC_OPERATION1(shroud, "ei"_s), - MAGIC_OPERATION("unshroud"_s, "e"_s, op_reveal), - MAGIC_OPERATION1(message, "es"_s), - MAGIC_OPERATION1(messenger_npc, "lissi"_s), - MAGIC_OPERATION1(move, "ed"_s), - MAGIC_OPERATION1(warp, "el"_s), - MAGIC_OPERATION1(banish, "e"_s), - MAGIC_OPERATION1(status_change, "eiiiiii"_s), - MAGIC_OPERATION1(stop_status_change, "ei"_s), - MAGIC_OPERATION1(override_attack, "eiiiiii"_s), - MAGIC_OPERATION1(create_item, "e.i"_s), - MAGIC_OPERATION1(aggravate, "eie"_s), - MAGIC_OPERATION1(spawn, "aeiiii"_s), - MAGIC_OPERATION1(injure, "eeii"_s), - MAGIC_OPERATION1(emote, "ei"_s), - MAGIC_OPERATION1(set_script_variable, "esi"_s), - MAGIC_OPERATION1(set_script_str, "ess"_s), - MAGIC_OPERATION1(set_hair_colour, "ei"_s), - MAGIC_OPERATION1(set_hair_style, "ei"_s), - MAGIC_OPERATION("drop_item"_s, "l.ii"_s, op_drop_item_for), - MAGIC_OPERATION1(drop_item_for, "l.iiei"_s), - MAGIC_OPERATION("gain_experience"_s, "eiii"_s, op_gain_exp), -}; - -op_t *magic_get_op(ZString name) -{ - auto it = operations.find(name); - if (it == operations.end()) - return nullptr; - return &it->second; -} - -void spell_effect_report_termination(BlockId invocation_id, BlockId bl_id, - StatusChange sc_id, int) -{ - dumb_ptr invocation_ = map_id_is_spell(invocation_id); - - if (!invocation_ || invocation_->bl_type != BL::SPELL) - return; - - for (status_change_ref_t& cr : invocation_->status_change_refv) - { - if (cr.sc_type == sc_id && cr.bl_id == bl_id) - { - if (&cr != &invocation_->status_change_refv.back()) - std::swap(cr, invocation_->status_change_refv.back()); - invocation_->status_change_refv.pop_back(); - - try_to_finish_invocation(invocation_); - return; - } - } - - { - dumb_ptr entity = map_id2bl(bl_id); - if (entity->bl_type == BL::PC) - FPRINTF(stderr, - "[magic] INTERNAL ERROR: spell-effect-report-termination: tried to terminate on unexpected bl %d, sc %d\n"_fmt, - bl_id, sc_id); - return; - } - -} - -static -dumb_ptr return_to_stack(dumb_ptr invocation_) -{ - if (invocation_->stack.empty()) - return nullptr; - else - { - cont_activation_record_t *ar = - &invocation_->stack.back(); - MATCH_BEGIN (*ar) - { - MATCH_CASE (const CarProc&, c_proc) - { - dumb_ptr ret = ar->return_location; - for (int i = 0; i < c_proc.args_nr; i++) - { - val_t *var = - &invocation_->env->varu[c_proc.formalap[i]]; - magic_clear_var(var); - *var = std::move(c_proc.old_actualpa[i]); - } - - // pop the stack - clear_activation_record(ar); - invocation_->stack.pop_back(); - - return ret; - } - MATCH_CASE (CarForEach&, c_foreach) - { - BlockId entity_id; - val_t *var = &invocation_->env->varu[c_foreach.id]; - - do - { - // This >= is really supposed to be a ==, but - // I have no clue if it's actually safe to change it. - if (c_foreach.index >= c_foreach.entities_vp->size()) - { - // pop the stack - dumb_ptr ret = ar->return_location; - clear_activation_record(ar); - invocation_->stack.pop_back(); - return ret; - } - - entity_id = - (*c_foreach.entities_vp)[c_foreach.index++]; - } - while (!entity_id || !map_id2bl(entity_id)); - - magic_clear_var(var); - if (c_foreach.ty_is_spell_not_entity) - *var = ValInvocationInt{entity_id}; - else - *var = ValEntityInt{entity_id}; - - return c_foreach.body; - } - MATCH_CASE (CarFor&, c_for) - { - if (c_for.current > c_for.stop) - { - dumb_ptr ret = ar->return_location; - // pop the stack - clear_activation_record(ar); - invocation_->stack.pop_back(); - return ret; - } - - magic_clear_var(&invocation_->env->varu[c_for.id]); - invocation_->env->varu[c_for.id] = ValInt{c_for.current++}; - - return c_for.body; - } - } - MATCH_END (); - abort(); - } -} - -static -void find_entities_in_area_c(dumb_ptr target, - std::vector *entities_vp, - FOREACH_FILTER filter) -{ - switch (target->bl_type) - { - - case BL::PC: - if (filter == FOREACH_FILTER::PC - || filter == FOREACH_FILTER::ENTITY - || (filter == FOREACH_FILTER::TARGET - && target->bl_m->flag.get(MapFlag::PVP))) - break; - else if (filter == FOREACH_FILTER::SPELL) - { /* Check all spells bound to the caster */ - dumb_ptr invoc = target->is_player()->active_spells; - /* Add all spells locked onto thie PC */ - - while (invoc) - { - entities_vp->push_back(invoc->bl_id); - invoc = invoc->next_invocation; - } - } - return; - - case BL::MOB: - if (filter == FOREACH_FILTER::MOB - || filter == FOREACH_FILTER::ENTITY - || filter == FOREACH_FILTER::TARGET) - break; - else - return; - - case BL::SPELL: - if (filter == FOREACH_FILTER::SPELL) - { - dumb_ptr invocation = target->is_spell(); - - /* Check whether the spell is `bound'-- if so, we'll consider it iff we see the caster(case BL::PC). */ - if (bool(invocation->flags & INVOCATION_FLAG::BOUND)) - return; - else - break; /* Add the spell */ - } - else - return; - - case BL::NPC: - if (filter == FOREACH_FILTER::NPC) - break; - else - return; - - default: - return; - } - - entities_vp->push_back(target->bl_id); -} - -static -void find_entities_in_area(area_t& area_, - std::vector *entities_vp, - FOREACH_FILTER filter) -{ - MATCH_BEGIN (area_) - { - MATCH_CASE (const AreaUnion&, a) - { - find_entities_in_area(*a.a_union[0], entities_vp, filter); - find_entities_in_area(*a.a_union[1], entities_vp, filter); - } - MATCH_CASE (const location_t&, a_loc) - { - (void)a_loc; - // TODO this can be simplified - int x, y, width, height; - Borrowed m = magic_area_rect(&x, &y, &width, &height, area_); - map_foreachinarea(std::bind(find_entities_in_area_c, ph::_1, entities_vp, filter), - m, - x, y, - x + width, y + height, - BL::NUL /* filter elsewhere */); - } - MATCH_CASE (const AreaRect&, a_rect) - { - (void)a_rect; - // TODO this can be simplified - int x, y, width, height; - Borrowed m = magic_area_rect(&x, &y, &width, &height, area_); - map_foreachinarea(std::bind(find_entities_in_area_c, ph::_1, entities_vp, filter), - m, - x, y, - x + width, y + height, - BL::NUL /* filter elsewhere */); - } - MATCH_CASE (const AreaBar&, a_bar) - { - (void)a_bar; - // TODO this is wrong - int x, y, width, height; - Borrowed m = magic_area_rect(&x, &y, &width, &height, area_); - map_foreachinarea(std::bind(find_entities_in_area_c, ph::_1, entities_vp, filter), - m, - x, y, - x + width, y + height, - BL::NUL /* filter elsewhere */); - } - } - MATCH_END (); -} - -static -dumb_ptr run_foreach(dumb_ptr invocation, - const EffectForEach *foreach, - dumb_ptr return_location) -{ - const EffectForEach& e_foreach = *foreach; - - val_t area; - FOREACH_FILTER filter = e_foreach.filter; - int id = e_foreach.id; - dumb_ptr body = e_foreach.body; - - magic_eval(invocation->env, &area, e_foreach.area); - - auto va = area.get_if(); - if (!va) - { - magic_clear_var(&area); - FPRINTF(stderr, - "[magic] Error in spell `%s': FOREACH loop over non-area\n"_fmt, - invocation->spell->name); - return return_location; - } - - { - std::vector entities_v; - find_entities_in_area(*va->v_area, - &entities_v, filter); - entities_v.shrink_to_fit(); - // iterator_pair will go away when this gets properly containerized. - random_::shuffle(entities_v); - - CarForEach c_foreach; - c_foreach.id = id; - c_foreach.body = body; - c_foreach.index = 0; - c_foreach.entities_vp.new_(std::move(entities_v)); - c_foreach.ty_is_spell_not_entity = - (filter == FOREACH_FILTER::SPELL); - invocation->stack.emplace_back(c_foreach, return_location); - - magic_clear_var(&area); - - return return_to_stack(invocation); - } -} - -static -dumb_ptr run_for (dumb_ptr invocation, - const EffectFor *for_, - dumb_ptr return_location) -{ - const EffectFor& e_for = *for_; - - int id = e_for.id; - val_t start; - val_t stop; - - magic_eval(invocation->env, &start, e_for.start); - magic_eval(invocation->env, &stop, e_for.stop); - - if (!start.is() || !stop.is()) - { - magic_clear_var(&start); - magic_clear_var(&stop); - FPRINTF(stderr, - "[magic] Error in spell `%s': FOR loop start or stop point is not an integer\n"_fmt, - invocation->spell->name); - return return_location; - } - - CarFor c_for; - - c_for.id = id; - c_for.current = start.get_if()->v_int; - c_for.stop = stop.get_if()->v_int; - c_for.body = e_for.body; - invocation->stack.emplace_back(c_for, return_location); - - return return_to_stack(invocation); -} - -static -dumb_ptr run_call(dumb_ptr invocation, - const EffectCall *call, - dumb_ptr return_location) -{ - const EffectCall& e_call = *call; - - int args_nr = e_call.formalv->size(); - int *formals = e_call.formalv->data(); - auto old_actuals = dumb_ptr::make(args_nr); - - CarProc c_proc; - c_proc.args_nr = args_nr; - c_proc.formalap = formals; - c_proc.old_actualpa = old_actuals; - invocation->stack.emplace_back(c_proc, return_location); - - for (int i = 0; i < args_nr; i++) - { - val_t *env_val = &invocation->env->varu[formals[i]]; - magic_copy_var(&old_actuals[i], env_val); - magic_eval(invocation->env, env_val, (*e_call.actualvp)[i]); - } - - return e_call.body; -} - -/** - * Execute a spell invocation until we abort, finish, or hit the next `sleep'. - * - * Use spell_execute() to automate handling of timers - * - * Returns: 0 if finished (all memory is freed implicitly) - * >1 if we hit `sleep'; the result is the number of ticks we should sleep for. - * -1 if we paused to wait for a user action (via script interaction) - */ -static -interval_t spell_run(dumb_ptr invocation_, int allow_delete) -{ - const BlockId invocation_id = invocation_->bl_id; -#define REFRESH_INVOCATION invocation_ = map_id_is_spell(invocation_id); if (!invocation_) return interval_t::zero(); - - while (invocation_->current_effect) - { - dumb_ptr e = invocation_->current_effect; - dumb_ptr next = e->next; - int i; - - MATCH_BEGIN (*e) - { - MATCH_CASE (const EffectSkip&, e_) - { - (void)e_; - } - MATCH_CASE (const EffectAbort&, e_) - { - (void)e_; - invocation_->flags |= INVOCATION_FLAG::ABORTED; - invocation_->end_effect = nullptr; - clear_stack(invocation_); - next = nullptr; - } - MATCH_CASE (const EffectEnd&, e_) - { - (void)e_; - clear_stack(invocation_); - next = nullptr; - } - MATCH_CASE (const EffectAssign&, e_assign) - { - magic_eval(invocation_->env, - &invocation_->env->varu[e_assign.id], - e_assign.expr); - } - MATCH_CASE (const EffectForEach&, e_foreach) - { - next = run_foreach(invocation_, &e_foreach, next); - } - MATCH_CASE (const EffectFor&, e_for) - { - next = run_for (invocation_, &e_for, next); - } - MATCH_CASE (const EffectIf&, e_if) - { - if (magic_eval_int(invocation_->env, e_if.cond)) - next = e_if.true_branch; - else - next = e_if.false_branch; - } - MATCH_CASE (const EffectSleep&, e_) - { - interval_t sleeptime = static_cast( - magic_eval_int(invocation_->env, e_.e_sleep)); - invocation_->current_effect = next; - if (sleeptime > interval_t::zero()) - return sleeptime; - } - MATCH_CASE (const EffectScript&, e_) - { - dumb_ptr caster = map_id_is_player(invocation_->caster); - if (caster) - { - dumb_ptr env = invocation_->env; - ZString caster_name = (caster ? caster->status_key.name : CharName()).to__actual(); - argrec_t arg[1] = - { - {"@caster_name$"_s, caster_name}, - }; - assert (!env->VAR(VAR_SCRIPTTARGET).is()); - ValEntityInt *ei = env->VAR(VAR_SCRIPTTARGET).get_if(); - BlockId message_recipient = - ei - ? ei->v_eid - : invocation_->caster; - dumb_ptr recipient = map_id_is_player(message_recipient); - - if (recipient->npc_id - && recipient->npc_id != invocation_->bl_id) - goto break_match; /* Don't send multiple message boxes at once */ - - if (!invocation_->script_pos) // first time running this script? - clif_spawn_fake_npc_for_player(recipient, - invocation_->bl_id); - // We have to do this or otherwise the client won't think that it's - // dealing with an NPC - - int newpos = run_script_l( - ScriptPointer(borrow(*e_.e_script), invocation_->script_pos), - message_recipient, invocation_->bl_id, - arg); - /* Returns the new script position, or -1 once the script is finished */ - if (newpos != -1) - { - /* Must set up for continuation */ - recipient->npc_id = invocation_->bl_id; - recipient->npc_pos = invocation_->script_pos = newpos; - return static_cast(-1); /* Signal `wait for script' */ - } - else - invocation_->script_pos = 0; - clif_clearchar_id(invocation_->bl_id, BeingRemoveWhy::DEAD, caster->sess); - } - REFRESH_INVOCATION; // Script may have killed the caster - } - MATCH_CASE (const EffectBreak&, e_) - { - (void)e_; - next = return_to_stack(invocation_); - } - MATCH_CASE (const EffectOp&, e_op) - { - op_t *op = e_op.opp; - val_t args[MAX_ARGS]; - - for (i = 0; i < e_op.args_nr; i++) - magic_eval(invocation_->env, &args[i], e_op.args[i]); - - if (!magic_signature_check("effect"_s, op->name, op->signature, - Slice(args, e_op.args_nr), - e_op.line_nr, - e_op.column)) - op->op(invocation_->env, Slice(args, e_op.args_nr)); - - for (i = 0; i < e_op.args_nr; i++) - magic_clear_var(&args[i]); - - REFRESH_INVOCATION; // Effect may have killed the caster - } - MATCH_CASE (const EffectCall&, e_call) - { - next = run_call(invocation_, &e_call, next); - } - } - MATCH_END (); - - break_match: - if (!next) - next = return_to_stack(invocation_); - - invocation_->current_effect = next; - } - - if (allow_delete) - try_to_finish_invocation(invocation_); - return interval_t::zero(); -#undef REFRESH_INVOCATION -} - -static -void spell_execute_d(dumb_ptr invocation, int allow_deletion) -{ - spell_update_location(invocation); - interval_t delta = spell_run(invocation, allow_deletion); - - if (delta > interval_t::zero()) - { - assert (!invocation->timer); - invocation->timer = Timer(gettick() + delta, - std::bind(invocation_timer_callback, ph::_1, ph::_2, - invocation->bl_id)); - } - - /* If 0, the script cleaned itself. If -1(wait-for-script), we must wait for the user. */ -} - -void spell_execute(dumb_ptr invocation) -{ - spell_execute_d(invocation, 1); -} - -void spell_execute_script(dumb_ptr invocation) -{ - if (invocation->script_pos) - spell_execute_d(invocation, 1); - /* Otherwise the script-within-the-spell has been terminated by some other means. - * In practice this happens when the script doesn't wait for user input: the client - * may still notify the server that it's done. Without the above check, we'd be - * running the same spell twice! */ -} - -int spell_attack(BlockId caster_id, BlockId target_id) -{ - dumb_ptr caster = map_id_is_player(caster_id); - dumb_ptr invocation_; - int stop_attack = 0; - - if (!caster) - return 0; - - invocation_ = map_id_is_spell(caster->attack_spell_override); - - if (invocation_ && bool(invocation_->flags & INVOCATION_FLAG::STOPATTACK)) - stop_attack = 1; - - if (invocation_ && caster->attack_spell_charges > 0) - { - magic_clear_var(&invocation_->env->varu[VAR_TARGET]); - invocation_->env->varu[VAR_TARGET] = ValEntityInt{target_id}; - - invocation_->current_effect = invocation_->trigger_effect; - invocation_->flags &= ~INVOCATION_FLAG::ABORTED; - spell_execute_d(invocation_, - 0 /* don't delete the invocation if done */ ); - - // If the caster died, we need to refresh here: - invocation_ = map_id_is_spell(caster->attack_spell_override); - - if (invocation_ && !bool(invocation_->flags & INVOCATION_FLAG::ABORTED)) // If we didn't abort: - caster->attack_spell_charges--; - } - - if (invocation_ && caster->attack_spell_override != invocation_->bl_id) - { - /* Attack spell changed / was refreshed */ - // spell_free_invocation(invocation); // [Fate] This would be a double free. - } - else if (!invocation_ || caster->attack_spell_charges <= 0) - { - caster->attack_spell_override = BlockId(); - char_set_weapon_icon(caster, 0, StatusChange::ZERO, ItemNameId()); - char_set_attack_info(caster, interval_t::zero(), 0); - - if (stop_attack) - pc_stopattack(caster); - - if (invocation_) - spell_free_invocation(invocation_); - } - - return 1; -} -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-stmt.hpp b/src/map/magic-stmt.hpp deleted file mode 100644 index 3b04fe3..0000000 --- a/src/map/magic-stmt.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once -// magic-stmt.hpp - Imperative commands for the magic backend. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "fwd.hpp" - -#include "../strings/zstring.hpp" - -#include "../mmo/skill.t.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -struct op_t -{ - LString name; - LString signature; - int (*op)(dumb_ptr env, Slice arga); -}; - -/** - * Retrieves an operation by name - * @param name The name to look up - * @return An operation of that name, or nullptr, and a function index - */ -op_t *magic_get_op(ZString name); - -/** - * Removes the shroud from a character - * - * \param character The character to remove the shroud from - */ -void magic_unshroud(dumb_ptr character); - -/** - * Notifies a running spell that a status_change timer triggered by the spell has expired - * - * \param invocation The invocation to notify - * \param bl_id ID of the PC for whom this happened - * \param sc_id ID of the status change entry that finished - * \param supplanted Whether the status_change finished normally (0) or was supplanted by a new status_change (1) - */ -void spell_effect_report_termination(BlockId invocation, BlockId bl_id, - StatusChange sc_id, int supplanted); - -/** - * Execute a spell invocation and sets up timers to finish - */ -void spell_execute(dumb_ptr invocation); - -/** - * Continue an NPC script embedded in a spell - */ -void spell_execute_script(dumb_ptr invocation); - -/** - * Stops all magic bound to the specified character - * - */ -void magic_stop_completely(dumb_ptr c); - -/** - * Attacks with a magical spell charged to the character - * - * Returns 0 if there is no charged spell or the spell is depleted. - */ -int spell_attack(BlockId caster, BlockId target); - -void spell_free_invocation(dumb_ptr invocation); -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-stmt.py b/src/map/magic-stmt.py deleted file mode 100644 index 7cc43d0..0000000 --- a/src/map/magic-stmt.py +++ /dev/null @@ -1,37 +0,0 @@ -class op_t(object): - __slots__ = ('_value') - - name = 'tmwa::map::magic::op_t' - depth = 1 - enabled = True - - def __init__(self, value): - if not value: - value = None - self._value = value - - def to_string(self): - value = self._value - if value is None: - return '(op_t *) nullptr' - return '(op_t *)' - - def children(self): - value = self._value - if value is None: - return - value = value.dereference() - yield '->name', value['name'] - yield '->signature', value['signature'] - yield '->op', value['op'] - - test_extra = ''' - using tmwa::operator "" _s; - ''' - - tests = [ - ('static_cast(nullptr)', - '(op_t *) nullptr'), - ('new tmwa::map::magic::op_t{"name"_s, "sig"_s, nullptr}', - '(op_t *) = {->name = "name", ->signature = "sig", ->op = nullptr}'), - ] diff --git a/src/map/magic-v2.cpp b/src/map/magic-v2.cpp deleted file mode 100644 index 52b1b8f..0000000 --- a/src/map/magic-v2.cpp +++ /dev/null @@ -1,1295 +0,0 @@ -#include "magic-v2.hpp" -// magic-v2.cpp - second generation magic parser -// -// Copyright © 2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include - -#include -#include -#include - -#include "../range/slice.hpp" - -#include "../strings/rstring.hpp" -#include "../strings/literal.hpp" - -#include "../generic/dumb_ptr.hpp" - -#include "../io/cxxstdio.hpp" -#include "../io/line.hpp" - -#include "../sexpr/parser.hpp" - -#include "../ast/script.hpp" - -#include "globals.hpp" -#include "itemdb.hpp" -#include "magic-expr.hpp" -#include "magic-interpreter.hpp" -#include "magic-interpreter-base.hpp" -#include "magic-stmt.hpp" -#include "script-parse.hpp" - -#include "../poison.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -namespace magic_v2 -{ - static - size_t intern_id(ZString id_name) - { - // TODO use InternPool - size_t i; - for (i = 0; i < magic_conf.varv.size(); i++) - if (id_name == magic_conf.varv[i].name) - return i; - - // i = magic_conf.varv.size(); - /* Must add new */ - magic_conf_t::mcvar new_var {}; - new_var.name = id_name; - new_var.val = ValUndef(); - magic_conf.varv.push_back(std::move(new_var)); - - return i; - } - inline - bool INTERN_ASSERT(ZString name, int id) - { - int zid = intern_id(name); - if (zid != id) - { - FPRINTF(stderr, - "[magic-conf] INTERNAL ERROR: Builtin special var %s interned to %d, not %d as it should be!\n"_fmt, - name, zid, id); - } - return zid == id; - } - - static - bool init0() - { - bool ok = true; - - ok &= INTERN_ASSERT("min_casttime"_s, VAR_MIN_CASTTIME); - ok &= INTERN_ASSERT("obscure_chance"_s, VAR_OBSCURE_CHANCE); - ok &= INTERN_ASSERT("caster"_s, VAR_CASTER); - ok &= INTERN_ASSERT("spellpower"_s, VAR_SPELLPOWER); - ok &= INTERN_ASSERT("self_spell"_s, VAR_SPELL); - ok &= INTERN_ASSERT("self_invocation"_s, VAR_INVOCATION); - ok &= INTERN_ASSERT("target"_s, VAR_TARGET); - ok &= INTERN_ASSERT("script_target"_s, VAR_SCRIPTTARGET); - ok &= INTERN_ASSERT("location"_s, VAR_LOCATION); - - return ok; - } - - - static - bool bind_constant(io::LineSpan span, RString name, val_t val) - { - if (!const_defm.insert(std::make_pair(name, std::move(val))).second) - { - span.error(STRPRINTF("Redefinition of constant '%s'"_fmt, name)); - return false; - } - return true; - } - static - const val_t *find_constant(RString name) - { - auto it = const_defm.find(name); - if (it != const_defm.end()) - return &it->second; - - return nullptr; - } - static - dumb_ptr set_effect_continuation(dumb_ptr src, dumb_ptr continuation) - { - dumb_ptr retval = src; - /* This function is completely analogous to `spellguard_implication' above; read the control flow implications above first before pondering it. */ - - if (src == continuation) - return retval; - - /* For FOR and FOREACH, we use special stack handlers and thus don't have to set - * the continuation. It's only IF that we need to handle in this fashion. */ - MATCH_BEGIN (*src) - { - MATCH_CASE (EffectIf&, e_if) - { - set_effect_continuation(e_if.true_branch, continuation); - set_effect_continuation(e_if.false_branch, continuation); - } - } - MATCH_END (); - - if (src->next) - set_effect_continuation(src->next, continuation); - else - src->next = continuation; - - return retval; - } - static - dumb_ptr spellguard_implication(dumb_ptr a, dumb_ptr b) - { - dumb_ptr retval = a; - - if (a == b) - { - /* This can happen due to reference sharing: - * e.g., - * (R0 -> (R1 | R2)) => (R3) - * yields - * (R0 -> (R1 -> R3 | R2 -> R3)) - * - * So if we now add => R4 to that, we want - * (R0 -> (R1 -> R3 -> R4 | R2 -> R3 -> R4)) - * - * but we only need to add it once, because the R3 reference is shared. - */ - return retval; - } - - /* If the premise is a disjunction, b is the continuation of _all_ branches */ - MATCH_BEGIN (*a) - { - MATCH_CASE (const GuardChoice&, s) - { - spellguard_implication(s.s_alt, b); - } - } - MATCH_END (); - - if (a->next) - spellguard_implication(a->next, b); - else - // this is the important bit - a->next = b; - - return retval; - } - - - static - bool add_spell(io::LineSpan span, dumb_ptr spell) - { - auto pair1 = magic_conf.spells_by_name.insert({spell->name, spell}); - if (!pair1.second) - { - span.error(STRPRINTF("Attempt to redefine spell '%s'"_fmt, spell->name)); - return false; - } - - auto pair2 = magic_conf.spells_by_invocation.insert({spell->invocation, spell}); - if (!pair2.second) - { - span.error(STRPRINTF("Attempt to redefine spell invocation '%s'"_fmt, spell->invocation)); - magic_conf.spells_by_name.erase(pair1.first); - return false; - } - return true; - } - static - bool add_teleport_anchor(io::LineSpan span, dumb_ptr anchor) - { - auto pair1 = magic_conf.anchors_by_name.insert({anchor->name, anchor}); - if (!pair1.second) - { - span.error(STRPRINTF("Attempt to redefine teleport anchor '%s'"_fmt, anchor->name)); - return false; - } - - auto pair2 = magic_conf.anchors_by_invocation.insert({anchor->name, anchor}); - if (!pair2.second) - { - span.error(STRPRINTF("Attempt to redefine anchor invocation '%s'"_fmt, anchor->invocation)); - magic_conf.anchors_by_name.erase(pair1.first); - return false; - } - return true; - } - - static - bool install_proc(io::LineSpan span, dumb_ptr proc) - { - RString name = proc->name; - if (!procs.insert({name, std::move(*proc)}).second) - { - span.error("procedure already exists"_s); - return false; - } - return true; - } - static - bool call_proc(io::LineSpan span, ZString name, dumb_ptr>> argvp, dumb_ptr& retval) - { - auto pi = procs.find(name); - if (pi == procs.end()) - { - span.error(STRPRINTF("Unknown procedure '%s'"_fmt, name)); - return false; - } - - proc_t *p = &pi->second; - - if (p->argv.size() != argvp->size()) - { - span.error(STRPRINTF("Procedure %s/%zu invoked with %zu parameters"_fmt, - name, p->argv.size(), argvp->size())); - return false; - } - - EffectCall e_call; - e_call.body = p->body; - e_call.formalv = &p->argv; - e_call.actualvp = argvp; - retval = dumb_ptr::make(e_call, nullptr); - return true; - } - static - bool op_effect(io::LineSpan span, ZString name, Slice> argv, dumb_ptr& effect) - { - op_t *op = magic_get_op(name); - if (!op) - { - span.error(STRPRINTF("Unknown operation '%s'"_fmt, name)); - return false; - } - if (op->signature.size() != argv.size()) - { - span.error(STRPRINTF("Incorrect number of arguments to operation '%s': Expected %zu, found %zu"_fmt, - name, op->signature.size(), argv.size())); - return false; - } - - EffectOp e_op; - e_op.line_nr = span.begin.line; - e_op.column = span.begin.column; - e_op.opp = op; - assert (argv.size() <= MAX_ARGS); - e_op.args_nr = argv.size(); - - std::copy(argv.begin(), argv.end(), e_op.args); - effect = dumb_ptr::make(e_op, nullptr); - return true; - } - - static - dumb_ptr dot_expr(dumb_ptr expr, int id) - { - ExprField e_field; - e_field.id = id; - e_field.expr = expr; - dumb_ptr retval = dumb_ptr::make(e_field); - - return retval; - } - static - bool fun_expr(io::LineSpan span, ZString name, Slice> argv, dumb_ptr& expr) - { - fun_t *fun = magic_get_fun(name); - if (!fun) - { - span.error(STRPRINTF("Unknown function '%s'"_fmt, name)); - return false; - } - if (fun->signature.size() != argv.size()) - { - span.error(STRPRINTF("Incorrect number of arguments to function '%s': Expected %zu, found %zu"_fmt, - name, fun->signature.size(), argv.size())); - return false; - } - ExprFunApp e_funapp; - e_funapp.line_nr = span.begin.line; - e_funapp.column = span.begin.column; - e_funapp.funp = fun; - - assert (argv.size() <= MAX_ARGS); - e_funapp.args_nr = argv.size(); - - std::copy(argv.begin(), argv.end(), e_funapp.args); - expr = dumb_ptr::make(e_funapp); - return true; - } - static - dumb_ptr BIN_EXPR(io::LineSpan span, ZString name, dumb_ptr left, dumb_ptr right) - { - dumb_ptr e[2]; - e[0] = left; - e[1] = right; - dumb_ptr rv; - if (!fun_expr(span, name, e, rv)) - abort(); - return rv; - } - - static - bool fail(const sexpr::SExpr& s, ZString msg) - { - s._span.error(msg); - return false; - } -} - -namespace magic_v2 -{ - using sexpr::SExpr; - - static - bool parse_expression(const SExpr& x, dumb_ptr& out); - static - bool parse_effect(const SExpr& s, dumb_ptr& out); - static - bool parse_spellguard(const SExpr& s, dumb_ptr& out); - static - bool parse_spellbody(const SExpr& s, dumb_ptr& out); - - // Note: anything with dumb_ptr leaks memory on failure - // once this is all done, we can convert it to unique_ptr - // (may require bimaps somewhere) - static - bool is_comment(const SExpr& s) - { - if (s._type == sexpr::STRING) - return true; - if (s._type != sexpr::LIST) - return false; - if (s._list.empty()) - return false; - if (s._list[0]._type != sexpr::TOKEN) - return false; - return s._list[0]._str == "DISABLED"_s; - } - - static - bool parse_loc(const SExpr& s, e_location_t& loc) - { - if (s._type != sexpr::LIST) - return fail(s, "loc not list"_s); - if (s._list.size() != 4) - return fail(s, "loc not 3 args"_s); - if (s._list[0]._type != sexpr::TOKEN) - return fail(s._list[0], "loc cmd not tok"_s); - if (s._list[0]._str != "@"_s) - return fail(s._list[0], "loc cmd not cmd"_s); - return parse_expression(s._list[1], loc.m) - && parse_expression(s._list[2], loc.x) - && parse_expression(s._list[3], loc.y); - } - - static - bool parse_expression(const SExpr& x, dumb_ptr& out) - { - switch (x._type) - { - case sexpr::INT: - { - val_t val; - val = ValInt{static_cast(x._int)}; - if (val.get_if()->v_int != x._int) - return fail(x, "integer too large"_s); - - out = dumb_ptr::make(std::move(val)); - return true; - } - case sexpr::STRING: - { - val_t val; - val = ValString{x._str}; - - out = dumb_ptr::make(std::move(val)); - return true; - } - case sexpr::TOKEN: - { - earray dirs //= - {{ - "S"_s, "SW"_s, "W"_s, "NW"_s, - "N"_s, "NE"_s, "E"_s, "SE"_s, - }}; - auto begin = std::begin(dirs); - auto end = std::end(dirs); - auto it = std::find(begin, end, x._str); - if (it != end) - { - val_t val; - val = ValDir{static_cast(it - begin)}; - - out = dumb_ptr::make(std::move(val)); - return true; - } - } - { - if (const val_t *val = find_constant(x._str)) - { - val_t copy; - magic_copy_var(©, val); - out = dumb_ptr::make(std::move(copy)); - return true; - } - else - { - ExprId e; - e.e_id = intern_id(x._str); - out = dumb_ptr::make(e); - return true; - } - } - break; - case sexpr::LIST: - if (x._list.empty()) - return fail(x, "empty list"_s); - { - if (x._list[0]._type != sexpr::TOKEN) - return fail(x._list[0], "op not token"_s); - ZString op = x._list[0]._str; - // area - if (op == "@"_s) - { - e_location_t loc; - if (!parse_loc(x, loc)) - return false; - out = dumb_ptr::make(loc); - return true; - } - if (op == "@+"_s) - { - e_location_t loc; - dumb_ptr width; - dumb_ptr height; - if (!parse_loc(x._list[1], loc)) - return false; - if (!parse_expression(x._list[2], width)) - return false; - if (!parse_expression(x._list[3], height)) - return false; - ExprAreaRect a_rect; - a_rect.loc = loc; - a_rect.width = width; - a_rect.height = height; - out = dumb_ptr::make(a_rect); - return true; - } - if (op == "TOWARDS"_s) - { - e_location_t loc; - dumb_ptr dir; - dumb_ptr width; - dumb_ptr depth; - if (!parse_loc(x._list[1], loc)) - return false; - if (!parse_expression(x._list[2], dir)) - return false; - if (!parse_expression(x._list[3], width)) - return false; - if (!parse_expression(x._list[4], depth)) - return false; - ExprAreaBar a_bar; - a_bar.loc = loc; - a_bar.dir = dir; - a_bar.width = width; - a_bar.depth = depth; - out = dumb_ptr::make(a_bar); - return true; - } - if (op == "."_s) - { - if (x._list.size() != 3) - return fail(x, ". not 2"_s); - dumb_ptr expr; - if (!parse_expression(x._list[1], expr)) - return false; - if (x._list[2]._type != sexpr::TOKEN) - return fail(x._list[2], ".elem not name"_s); - ZString elem = x._list[2]._str; - out = dot_expr(expr, intern_id(elem)); - return true; - } - static // TODO LString - std::set ops = - { - "<"_s, ">"_s, "<="_s, ">="_s, "=="_s, "!="_s, - "+"_s, "-"_s, "*"_s, "%"_s, "/"_s, - "&"_s, "^"_s, "|"_s, "<<"_s, ">>"_s, - "&&"_s, "||"_s, - }; - // TODO implement unary operators - if (ops.count(op)) - { - // operators are n-ary and left-associative - if (x._list.size() < 3) - return fail(x, "operator not at least 2 args"_s); - auto begin = x._list.begin() + 1; - auto end = x._list.end(); - if (!parse_expression(*begin, out)) - return false; - ++begin; - for (; begin != end; ++begin) - { - dumb_ptr tmp; - if (!parse_expression(*begin, tmp)) - return false; - out = BIN_EXPR(x._span, op, out, tmp); - } - return true; - } - std::vector> argv; - for (auto it = x._list.begin() + 1, end = x._list.end(); it != end; ++it) - { - dumb_ptr expr; - if (!parse_expression(*it, expr)) - return false; - argv.push_back(expr); - } - return fun_expr(x._span, op, argv, out); - } - break; - } - abort(); - } - - static - bool parse_item(const SExpr& s, ItemNameId& id, int& count) - { - if (s._type == sexpr::STRING) - { - count = 1; - - Borrowed item = TRY_UNWRAP(itemdb_searchname(s._str), - return fail(s, "no such item"_s) - ); - id = item->nameid; - return true; - } - if (s._type != sexpr::LIST) - return fail(s, "item not string or list"_s); - if (s._list.size() != 2) - return fail(s, "item list is not pair"_s); - if (s._list[0]._type != sexpr::INT) - return fail(s._list[0], "item pair first not int"_s); - count = s._list[0]._int; - if (s._list[1]._type != sexpr::STRING) - return fail(s._list[1], "item pair second not name"_s); - - Borrowed item = TRY_UNWRAP(itemdb_searchname(s._list[1]._str), - return fail(s, "no such item"_s) - ); - id = item->nameid; - return true; - } - - static - bool parse_spellguard(const SExpr& s, dumb_ptr& out) - { - if (s._type != sexpr::LIST) - return fail(s, "not list"_s); - if (s._list.empty()) - return fail(s, "empty list"_s); - if (s._list[0]._type != sexpr::TOKEN) - return fail(s._list[0], "not token"_s); - ZString cmd = s._list[0]._str; - if (cmd == "OR"_s) - { - auto begin = s._list.begin() + 1; - auto end = s._list.end(); - if (begin == end) - return fail(s, "missing arguments"_s); - if (!parse_spellguard(*begin, out)) - return false; - ++begin; - for (; begin != end; ++begin) - { - dumb_ptr alt; - if (!parse_spellguard(*begin, alt)) - return false; - GuardChoice choice; - auto next = out; - choice.s_alt = alt; - out = dumb_ptr::make(choice, next); - } - return true; - } - if (cmd == "GUARD"_s) - { - auto begin = s._list.begin() + 1; - auto end = s._list.end(); - while (is_comment(end[-1])) - --end; - if (begin == end) - return fail(s, "missing arguments"_s); - if (!parse_spellguard(end[-1], out)) - return false; - --end; - for (; begin != end; --end) - { - if (is_comment(end[-1])) - continue; - dumb_ptr implier; - if (!parse_spellguard(end[-1], implier)) - return false; - out = spellguard_implication(implier, out); - } - return true; - } - if (cmd == "REQUIRE"_s) - { - if (s._list.size() != 2) - return fail(s, "not one argument"_s); - dumb_ptr condition; - if (!parse_expression(s._list[1], condition)) - return false; - GuardCondition cond; - cond.s_condition = condition; - out = dumb_ptr::make(cond, nullptr); - return true; - } - if (cmd == "MANA"_s) - { - if (s._list.size() != 2) - return fail(s, "not one argument"_s); - dumb_ptr mana; - if (!parse_expression(s._list[1], mana)) - return false; - GuardMana sp; - sp.s_mana = mana; - out = dumb_ptr::make(sp, nullptr); - return true; - } - if (cmd == "CASTTIME"_s) - { - if (s._list.size() != 2) - return fail(s, "not one argument"_s); - dumb_ptr casttime; - if (!parse_expression(s._list[1], casttime)) - return false; - GuardCastTime ct; - ct.s_casttime = casttime; - out = dumb_ptr::make(ct, nullptr); - return true; - } - if (cmd == "CATALYSTS"_s) - { - dumb_ptr items = nullptr; - for (auto it = s._list.begin() + 1, end = s._list.end(); it != end; ++it) - { - ItemNameId id; - int count; - if (!parse_item(*it, id, count)) - return false; - magic_add_component(&items, id, count); - } - GuardCatalysts cat; - cat.s_catalysts = items; - out = dumb_ptr::make(cat, nullptr); - return true; - } - if (cmd == "COMPONENTS"_s) - { - dumb_ptr items = nullptr; - for (auto it = s._list.begin() + 1, end = s._list.end(); it != end; ++it) - { - ItemNameId id; - int count; - if (!parse_item(*it, id, count)) - return false; - magic_add_component(&items, id, count); - } - GuardComponents comp; - comp.s_components = items; - out = dumb_ptr::make(comp, nullptr); - return true; - } - return fail(s._list[0], "unknown guard"_s); - } - - static - bool build_effect_list(std::vector::const_iterator begin, - std::vector::const_iterator end, dumb_ptr& out) - { - // these backward lists could be forward by keeping the reference - // I know this is true because Linus said so - out = dumb_ptr::make(EffectSkip{}, nullptr); - while (end != begin) - { - const SExpr& s = *--end; - if (is_comment(s)) - continue; - dumb_ptr chain; - if (!parse_effect(s, chain)) - return false; - out = set_effect_continuation(chain, out); - } - return true; - } - - static - bool parse_effect(const SExpr& s, dumb_ptr& out) - { - if (s._type != sexpr::LIST) - return fail(s, "not list"_s); - if (s._list.empty()) - return fail(s, "empty list"_s); - if (s._list[0]._type != sexpr::TOKEN) - return fail(s._list[0], "not token"_s); - ZString cmd = s._list[0]._str; - if (cmd == "BLOCK"_s) - { - return build_effect_list(s._list.begin() + 1, s._list.end(), out); - } - if (cmd == "SET"_s) - { - if (s._list.size() != 3) - return fail(s, "not 2 args"_s); - if (s._list[1]._type != sexpr::TOKEN) - return fail(s._list[1], "not token"_s); - ZString name = s._list[1]._str; - if (find_constant(name)) - return fail(s._list[1], "assigning to constant"_s); - dumb_ptr expr; - if (!parse_expression(s._list[2], expr)) - return false; - - EffectAssign e_assign; - e_assign.id = intern_id(name); - e_assign.expr = expr; - out = dumb_ptr::make(e_assign, nullptr); - return true; - } - if (cmd == "SCRIPT"_s) - { - if (s._list.size() != 2) - return fail(s, "not 1 arg"_s); - if (s._list[1]._type != sexpr::STRING) - return fail(s._list[1], "not string"_s); - ZString body = s._list[1]._str; - auto begin = s._list[1]._span.begin; - io::LineCharReader lr(io::from_string, begin.filename, body, begin.line, begin.column); - ast::script::ScriptOptions opt; - opt.implicit_start = true; - opt.implicit_end = true; - opt.no_event = true; - auto code_res = ast::script::parse_script_body(lr, opt); - if (code_res.get_failure()) - { - PRINTF("%s\n"_fmt, code_res.get_failure()); - } - auto code = TRY_UNWRAP(code_res.get_success(), - return fail(s._list[1], "script does not compile"_s)); - std::unique_ptr script = compile_script(STRPRINTF("script magic %s:%d"_fmt, begin.filename, begin.line), code, true); - if (!script) - return fail(s._list[1], "script does not compile"_s); - EffectScript e; - e.e_script = dumb_ptr(script.release()); - out = dumb_ptr::make(e, nullptr); - return true; - } - if (cmd == "SKIP"_s) - { - if (s._list.size() != 1) - return fail(s, "not 0 arg"_s); - out = dumb_ptr::make(EffectSkip{}, nullptr); - return true; - } - if (cmd == "ABORT"_s) - { - if (s._list.size() != 1) - return fail(s, "not 0 arg"_s); - out = dumb_ptr::make(EffectAbort{}, nullptr); - return true; - } - if (cmd == "END"_s) - { - if (s._list.size() != 1) - return fail(s, "not 0 arg"_s); - out = dumb_ptr::make(EffectEnd{}, nullptr); - return true; - } - if (cmd == "BREAK"_s) - { - if (s._list.size() != 1) - return fail(s, "not 0 arg"_s); - out = dumb_ptr::make(EffectBreak{}, nullptr); - return true; - } - if (cmd == "FOREACH"_s) - { - if (s._list.size() != 5) - return fail(s, "not 4 arg"_s); - if (s._list[1]._type != sexpr::TOKEN) - return fail(s._list[1], "foreach type not token"_s); - ZString type = s._list[1]._str; - FOREACH_FILTER filter; - if (type == "PC"_s) - filter = FOREACH_FILTER::PC; - else if (type == "MOB"_s) - filter = FOREACH_FILTER::MOB; - else if (type == "ENTITY"_s) - filter = FOREACH_FILTER::ENTITY; - else if (type == "SPELL"_s) - filter = FOREACH_FILTER::SPELL; - else if (type == "TARGET"_s) - filter = FOREACH_FILTER::TARGET; - else if (type == "NPC"_s) - filter = FOREACH_FILTER::NPC; - else - return fail(s._list[1], "unknown foreach filter"_s); - if (s._list[2]._type != sexpr::TOKEN) - return fail(s._list[2], "foreach var not token"_s); - ZString var = s._list[2]._str; - dumb_ptr area; - dumb_ptr effect; - if (!parse_expression(s._list[3], area)) - return false; - if (!parse_effect(s._list[4], effect)) - return false; - - EffectForEach e_foreach; - e_foreach.id = intern_id(var); - e_foreach.area = area; - e_foreach.body = effect; - e_foreach.filter = filter; - out = dumb_ptr::make(e_foreach, nullptr); - return true; - } - if (cmd == "FOR"_s) - { - if (s._list.size() != 5) - return fail(s, "not 4 arg"_s); - if (s._list[1]._type != sexpr::TOKEN) - return fail(s._list[1], "for var not token"_s); - ZString var = s._list[1]._str; - dumb_ptr low; - dumb_ptr high; - dumb_ptr effect; - if (!parse_expression(s._list[2], low)) - return false; - if (!parse_expression(s._list[3], high)) - return false; - if (!parse_effect(s._list[4], effect)) - return false; - - EffectFor e_for; - e_for.id = intern_id(var); - e_for.start = low; - e_for.stop = high; - e_for.body = effect; - out = dumb_ptr::make(e_for, nullptr); - return true; - } - if (cmd == "IF"_s) - { - if (s._list.size() != 3 && s._list.size() != 4) - return fail(s, "not 2 or 3 args"_s); - dumb_ptr cond; - dumb_ptr if_true; - dumb_ptr if_false; - if (!parse_expression(s._list[1], cond)) - return false; - if (!parse_effect(s._list[2], if_true)) - return false; - if (s._list.size() == 4) - { - if (!parse_effect(s._list[3], if_false)) - return false; - } - else - if_false = dumb_ptr::make(EffectSkip{}, nullptr); - - EffectIf e_if; - e_if.cond = cond; - e_if.true_branch = if_true; - e_if.false_branch = if_false; - out = dumb_ptr::make(e_if, nullptr); - return true; - } - if (cmd == "WAIT"_s) - { - if (s._list.size() != 2) - return fail(s, "not 1 arg"_s); - dumb_ptr expr; - if (!parse_expression(s._list[1], expr)) - return false; - EffectSleep e; - e.e_sleep = expr; - out = dumb_ptr::make(e, nullptr); - return true; - } - if (cmd == "CALL"_s) - { - if (s._list.size() < 2) - return fail(s, "call what?"_s); - if (s._list[1]._type != sexpr::TOKEN) - return fail(s._list[1], "call token please"_s); - ZString func = s._list[1]._str; - auto argvp = dumb_ptr>>::make(); - for (auto it = s._list.begin() + 2, end = s._list.end(); it != end; ++it) - { - dumb_ptr expr; - if (!parse_expression(*it, expr)) - return false; - argvp->push_back(expr); - } - return call_proc(s._span, func, argvp, out); - } - auto argv = std::vector>(); - for (auto it = s._list.begin() + 1, end = s._list.end(); it != end; ++it) - { - dumb_ptr expr; - if (!parse_expression(*it, expr)) - return false; - argv.push_back(expr); - } - return op_effect(s._span, cmd, argv, out); - } - - static - bool parse_spellbody(const SExpr& s, dumb_ptr& out) - { - if (s._type != sexpr::LIST) - return fail(s, "not list"_s); - if (s._list.empty()) - return fail(s, "empty list"_s); - if (s._list[0]._type != sexpr::TOKEN) - return fail(s._list[0], "not token"_s); - ZString cmd = s._list[0]._str; - if (cmd == "=>"_s) - { - if (s._list.size() != 3) - return fail(s, "list does not have exactly 2 arguments"_s); - dumb_ptr guard; - if (!parse_spellguard(s._list[1], guard)) - return false; - dumb_ptr body; - if (!parse_spellbody(s._list[2], body)) - return false; - out = spellguard_implication(guard, body); - return true; - } - if (cmd == "|"_s) - { - if (s._list.size() == 1) - return fail(s, "spellbody choice empty"_s); - auto begin = s._list.begin() + 1; - auto end = s._list.end(); - if (!parse_spellbody(*begin, out)) - return false; - ++begin; - for (; begin != end; ++begin) - { - dumb_ptr alt; - if (!parse_spellbody(*begin, alt)) - return false; - auto tmp = out; - GuardChoice choice; - choice.s_alt = alt; - out = dumb_ptr::make(choice, tmp); - } - return true; - } - if (cmd == "EFFECT"_s) - { - auto begin = s._list.begin() + 1; - auto end = s._list.end(); - - dumb_ptr effect, attrig, atend; - - // decreasing end can never pass begin, since we know that - // begin[-1] is token EFFECT - while (is_comment(end[-1])) - --end; - if (end[-1]._type == sexpr::LIST && !end[-1]._list.empty() - && end[-1]._list[0]._type == sexpr::TOKEN - && end[-1]._list[0]._str == "ATEND"_s) - { - auto atb = end[-1]._list.begin() + 1; - auto ate = end[-1]._list.end(); - if (!build_effect_list(atb, ate, atend)) - return false; - --end; - - while (is_comment(end[-1])) - --end; - } - else - { - atend = nullptr; - } - if (end[-1]._type == sexpr::LIST && !end[-1]._list.empty() - && end[-1]._list[0]._type == sexpr::TOKEN - && end[-1]._list[0]._str == "ATTRIGGER"_s) - { - auto atb = end[-1]._list.begin() + 1; - auto ate = end[-1]._list.end(); - if (!build_effect_list(atb, ate, attrig)) - return false; - --end; - } - else - { - attrig = nullptr; - } - if (!build_effect_list(begin, end, effect)) - return false; - effect_set_t s_effect; - s_effect.effect = effect; - s_effect.at_trigger = attrig; - s_effect.at_end = atend; - out = dumb_ptr::make(s_effect, nullptr); - return true; - } - return fail(s._list[0], "unknown spellbody"_s); - } - - static - bool parse_top_set(const std::vector& in) - { - if (in.size() != 3) - return fail(in[0], "not 2 arguments"_s); - ZString name = in[1]._str; - dumb_ptr expr; - if (!parse_expression(in[2], expr)) - return false; - if (find_constant(name)) - return fail(in[1], "assign constant"_s); - size_t var_id = intern_id(name); - magic_eval(dumb_ptr(&magic_default_env), &magic_conf.varv[var_id].val, expr); - return true; - } - static - bool parse_const(io::LineSpan span, const std::vector& in) - { - if (in.size() != 3) - return fail(in[0], "not 2 arguments"_s); - if (in[1]._type != sexpr::TOKEN) - return fail(in[1], "not token"_s); - ZString name = in[1]._str; - dumb_ptr expr; - if (!parse_expression(in[2], expr)) - return false; - val_t tmp; - magic_eval(dumb_ptr(&magic_default_env), &tmp, expr); - return bind_constant(span, name, std::move(tmp)); - } - static - bool parse_anchor(io::LineSpan span, const std::vector& in) - { - if (in.size() != 4) - return fail(in[0], "not 3 arguments"_s); - auto anchor = dumb_ptr::make(); - if (in[1]._type != sexpr::TOKEN) - return fail(in[1], "not token"_s); - anchor->name = in[1]._str; - if (in[2]._type != sexpr::STRING) - return fail(in[2], "not string"_s); - anchor->invocation = in[2]._str; - dumb_ptr expr; - if (!parse_expression(in[3], expr)) - return false; - anchor->location = expr; - return add_teleport_anchor(span, anchor); - } - static - bool parse_proc(io::LineSpan span, const std::vector& in) - { - if (in.size() < 4) - return fail(in[0], "not at least 3 arguments"_s); - auto proc = dumb_ptr::make(); - if (in[1]._type != sexpr::TOKEN) - return fail(in[1], "name not token"_s); - proc->name = in[1]._str; - if (in[2]._type != sexpr::LIST) - return fail(in[2], "args not list"_s); - for (const SExpr& arg : in[2]._list) - { - if (arg._type != sexpr::TOKEN) - return fail(arg, "arg not token"_s); - proc->argv.push_back(intern_id(arg._str)); - } - if (!build_effect_list(in.begin() + 3, in.end(), proc->body)) - return false; - return install_proc(span, proc); - } - static - bool parse_spell(io::LineSpan span, const std::vector& in) - { - if (in.size() < 6) - return fail(in[0], "not at least 5 arguments"_s); - if (in[1]._type != sexpr::LIST) - return fail(in[1], "flags not list"_s); - - auto spell = dumb_ptr::make(); - - for (const SExpr& s : in[1]._list) - { - if (s._type != sexpr::TOKEN) - return fail(s, "flag not token"_s); - SPELL_FLAG flag = SPELL_FLAG::ZERO; - if (s._str == "LOCAL"_s) - flag = SPELL_FLAG::LOCAL; - else if (s._str == "NONMAGIC"_s) - flag = SPELL_FLAG::NONMAGIC; - else if (s._str == "SILENT"_s) - flag = SPELL_FLAG::SILENT; - else - return fail(s, "unknown flag"_s); - if (bool(spell->flags & flag)) - return fail(s, "duplicate flag"_s); - spell->flags |= flag; - } - if (in[2]._type != sexpr::TOKEN) - return fail(in[2], "name not token"_s); - spell->name = in[2]._str; - if (in[3]._type != sexpr::STRING) - return fail(in[3], "invoc not string"_s); - spell->invocation = in[3]._str; - if (in[4]._type != sexpr::LIST) - return fail(in[4], "spellarg not list"_s); - if (in[4]._list.size() == 0) - { - spell->spellarg_ty = SPELLARG::NONE; - } - else - { - if (in[4]._list.size() != 2) - return fail(in[4], "spellarg not empty list or pair"_s); - if (in[4]._list[0]._type != sexpr::TOKEN) - return fail(in[4]._list[0], "spellarg type not token"_s); - if (in[4]._list[1]._type != sexpr::TOKEN) - return fail(in[4]._list[1], "spellarg name not token"_s); - ZString ty = in[4]._list[0]._str; - if (ty == "PC"_s) - spell->spellarg_ty = SPELLARG::PC; - else if (ty == "STRING"_s) - spell->spellarg_ty = SPELLARG::STRING; - else - return fail(in[4]._list[0], "unknown spellarg type"_s); - ZString an = in[4]._list[1]._str; - spell->arg = intern_id(an); - } - std::vector::const_iterator it = in.begin() + 5; - for (;; ++it) - { - if (it == in.end()) - return fail(it[-1], "end of list scanning LET defs"_s); - if (is_comment(*it)) - continue; - if (it->_type != sexpr::LIST || it->_list.empty()) - break; - if (it->_list[0]._type != sexpr::TOKEN || it->_list[0]._str != "LET"_s) - break; - - if (it->_list[1]._type != sexpr::TOKEN) - return fail(it->_list[1], "let name not token"_s); - ZString name = it->_list[1]._str; - if (find_constant(name)) - return fail(it->_list[1], "constant exists"_s); - dumb_ptr expr; - if (!parse_expression(it->_list[2], expr)) - return false; - letdef_t let; - let.id = intern_id(name); - let.expr = expr; - spell->letdefv.push_back(let); - } - if (it + 1 != in.end()) - return fail(*it, "expected only one body entry besides LET"_s); - - // formally, 'guard' only refers to the first argument of '=>' - // but internally, spellbodies use the same thing - dumb_ptr guard; - if (!parse_spellbody(*it, guard)) - return false; - spell->spellguard = guard; - return add_spell(span, spell); - } - - static - bool parse_top(io::LineSpan span, const std::vector& vs) - { - if (vs.empty()) - { - span.error("Empty list at top"_s); - return false; - } - if (vs[0]._type != sexpr::TOKEN) - return fail(vs[0], "top not token"_s); - ZString cmd = vs[0]._str; - if (cmd == "CONST"_s) - return parse_const(span, vs); - if (cmd == "PROCEDURE"_s) - return parse_proc(span, vs); - if (cmd == "SET"_s) - return parse_top_set(vs); - if (cmd == "SPELL"_s) - return parse_spell(span, vs); - if (cmd == "TELEPORT-ANCHOR"_s) - return parse_anchor(span, vs); - return fail(vs[0], "Unknown top-level command"_s); - } - - static - bool loop(sexpr::Lexer& in) - { - SExpr s; - while (sexpr::parse(in, s)) - { - if (is_comment(s)) - continue; - if (s._type != sexpr::LIST) - return fail(s, "top-level entity not a list or comment"_s); - if (!parse_top(s._span, s._list)) - return false; - } - // handle low-level errors - if (in.peek() != sexpr::TOK_EOF) - { - in.span().error("parser gave up before end of file"_s); - return false; - } - return true; - } -} // namespace magic_v2 - -bool magic_init0() -{ - return magic_v2::init0(); -} - -bool load_magic_file_v2(ZString filename) -{ - sexpr::Lexer in(filename); - bool rv = magic_v2::loop(in); - if (!rv) - { - in.span().error(STRPRINTF("next token: %s '%s'"_fmt, sexpr::token_name(in.peek()), in.val_string())); - } - return rv; -} -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic-v2.hpp b/src/map/magic-v2.hpp deleted file mode 100644 index fac2773..0000000 --- a/src/map/magic-v2.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -// magic-v2.hpp - second generation magic parser -// -// Copyright © 2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "fwd.hpp" - -#include "../strings/zstring.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -bool magic_init0(); -// must be called after itemdb initialization -bool load_magic_file_v2(ZString filename); -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic.cpp b/src/map/magic.cpp deleted file mode 100644 index 418312a..0000000 --- a/src/map/magic.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "magic.hpp" -// magic.cpp - Entry to the magic system. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include -#include - -#include "../strings/xstring.hpp" - -#include "../generic/dumb_ptr.hpp" - -#include "../io/cxxstdio.hpp" - -#include "globals.hpp" -#include "magic-expr.hpp" -#include "magic-interpreter.hpp" -#include "magic-interpreter-base.hpp" -#include "magic-stmt.hpp" -#include "map.hpp" -#include "pc.hpp" - -#include "../poison.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -/// Return a pair of strings, {spellname, parameter} -/// Parameter may be empty. -static -std::pair magic_tokenise(XString src) -{ - auto seeker = std::find(src.begin(), src.end(), ' '); - - if (seeker == src.end()) - { - return {src, XString()}; - } - else - { - XString rv1 = src.xislice_h(seeker); - ++seeker; - - while (seeker != src.end() && *seeker == ' ') - ++seeker; - - // Note: this very well could be empty - XString rv2 = src.xislice_t(seeker); - return {rv1, rv2}; - } -} - -int magic_message(dumb_ptr caster, XString source_invocation) -{ - if (pc_isdead(caster)) - return 0; - if (bool(caster->status.option & Opt0::HIDE)) - return 0; // No spellcasting while hidden - - int power = caster->matk1; - - // This was the only thing worth saving from magic_preprocess_message. - // May it rest only, and never rise again. - // For more information on how this code worked, travel through time - // and watch all the comments I wrote for myself while trying to figure - // out if it was safe to delete. - if (caster->state.shroud_active && caster->state.shroud_disappears_on_talk) - magic_unshroud(caster); - - auto pair = magic_tokenise(source_invocation); - XString spell_invocation = pair.first; - XString parameter = pair.second; - - dumb_ptr spell = magic_find_spell(spell_invocation); - - if (spell) - { - int near_miss; - dumb_ptr env = - spell_create_env(&magic_conf, spell, caster, power, parameter); - const effect_set_t *effects; - - if (bool(spell->flags & SPELL_FLAG::NONMAGIC) || (power >= 1)) - effects = spell_trigger(spell, caster, env, &near_miss); - else - effects = nullptr; - - MAP_LOG_PC(caster, "CAST %s %s"_fmt, - spell->name, effects ? "SUCCESS"_s : "FAILURE"_s); - - if (effects) - { - dumb_ptr invocation = spell_instantiate(effects, env); - - spell_bind(caster, invocation); - spell_execute(invocation); - - return bool(spell->flags & SPELL_FLAG::SILENT) ? -1 : 1; - } - else - magic_free_env(env); - - return 1; - } - - return 0; /* Not a spell */ -} -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/magic.hpp b/src/map/magic.hpp deleted file mode 100644 index 70d40dc..0000000 --- a/src/map/magic.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -// magic.hpp - Entry to the magic system. -// -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "fwd.hpp" - -#include "map.t.hpp" -#include "../mmo/skill.t.hpp" - - -namespace tmwa -{ -namespace map -{ -namespace magic -{ -/** - * Try to cast magic. - * - * As an intended side effect, the magic message may be distorted (text only). - * No, it can't. Thank God. - * - * \param caster Player attempting to cast magic - * \param source_invocation The prospective incantation - * \return 1 or -1 if the input message was magic and was handled by this function, 0 otherwise. -1 is returned when the - * message should not be repeated. - */ -int magic_message(dumb_ptr caster, XString source_invocation); -} // namespace magic -} // namespace map -} // namespace tmwa diff --git a/src/map/map.cpp b/src/map/map.cpp index d40977f..362d5d2 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -69,9 +69,6 @@ #include "globals.hpp" #include "grfio.hpp" #include "itemdb.hpp" -#include "magic-interpreter.hpp" // for is_spell inline body -#include "magic-stmt.hpp" -#include "magic-v2.hpp" #include "map_conf.hpp" #include "mob.hpp" #include "quest.hpp" @@ -1454,9 +1451,6 @@ void cleanup_sub(dumb_ptr bl) case BL::ITEM: map_clearflooritem(bl->bl_id); break; - case BL::SPELL: - magic::spell_free_invocation(bl->is_spell()); - break; } } @@ -1497,8 +1491,6 @@ bool map_confs(io::Spanned key, io::Spanned value) return mob_readskilldb(value.data); if (key.data == "skill_db"_s) return skill_readdb(value.data); - if (key.data == "magic_conf"_s) - return magic::load_magic_file_v2(value.data); if (key.data == "resnametable"_s) return load_resnametable(value.data); @@ -1515,16 +1507,7 @@ int map_scriptcont(dumb_ptr sd, BlockId id) if (!bl) return 0; - switch (bl->bl_type) - { - case BL::NPC: - return npc_scriptcont(sd, id); - case BL::SPELL: - magic::spell_execute_script(bl->is_spell()); - break; - } - - return 0; + return npc_scriptcont(sd, id); } } // namespace map @@ -1571,7 +1554,6 @@ int do_init(Slice argv) using namespace tmwa::map; ZString argv0 = argv.pop_front(); - runflag &= magic::magic_init0(); bool loaded_config_yet = false; while (argv) diff --git a/src/map/map.hpp b/src/map/map.hpp index a2f2ff2..66253ca 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -89,13 +89,11 @@ private: dumb_ptr as_npc(); dumb_ptr as_mob(); dumb_ptr as_item(); - dumb_ptr as_spell(); public: dumb_ptr is_player(); dumb_ptr is_npc(); dumb_ptr is_mob(); dumb_ptr is_item(); - dumb_ptr is_spell(); }; struct walkpath_data @@ -107,7 +105,6 @@ struct status_change { Timer timer; int val1; - BlockId spell_invocation; /* [Fate] If triggered by a spell, record here */ }; struct quick_regeneration @@ -205,8 +202,7 @@ struct map_session_data : block_list, SessionData // used by @hugo and @linus BlockId followtarget; - tick_t cast_tick; // [Fate] Next tick at which spellcasting is allowed - dumb_ptr active_spells; // [Fate] Singly-linked list of active spells linked to this PC + //tick_t cast_tick; // [Fate] Next tick at which spellcasting is allowed BlockId attack_spell_override; // [Fate] When an attack spell is active for this player, they trigger it NpcEvent magic_attack; // like a weapon. Check pc_attack_timer() for details. @@ -639,13 +635,6 @@ dumb_ptr map_id_is_item(BlockId id) dumb_ptr bl = map_id2bl(id); return bl ? bl->is_item() : nullptr; } -inline -dumb_ptr map_id_is_spell(BlockId id) -{ - dumb_ptr bl = map_id2bl(id); - return bl ? bl->is_spell() : nullptr; -} - Option> map_mapname2mapid(MapName); int map_mapname2ipport(MapName, Borrowed, Borrowed); @@ -679,13 +668,11 @@ inline dumb_ptr block_list::as_player() { return dumb_ptr block_list::as_npc() { return dumb_ptr(static_cast(this)) ; } inline dumb_ptr block_list::as_mob() { return dumb_ptr(static_cast(this)) ; } inline dumb_ptr block_list::as_item() { return dumb_ptr(static_cast(this)) ; } -//inline dumb_ptr block_list::as_spell() { return dumb_ptr(static_cast(this)) ; } inline dumb_ptr block_list::is_player() { return bl_type == BL::PC ? as_player() : nullptr; } inline dumb_ptr block_list::is_npc() { return bl_type == BL::NPC ? as_npc() : nullptr; } inline dumb_ptr block_list::is_mob() { return bl_type == BL::MOB ? as_mob() : nullptr; } inline dumb_ptr block_list::is_item() { return bl_type == BL::ITEM ? as_item() : nullptr; } -//inline dumb_ptr block_list::is_spell() { return bl_type == BL::SPELL ? as_spell() : nullptr; } // struct invocation is defined in another header diff --git a/src/map/map.t.hpp b/src/map/map.t.hpp index 267c049..89b9a87 100644 --- a/src/map/map.t.hpp +++ b/src/map/map.t.hpp @@ -43,7 +43,6 @@ enum class BL : uint8_t NPC, MOB, ITEM, - SPELL, }; enum class NpcSubtype : uint8_t { diff --git a/src/map/npc.cpp b/src/map/npc.cpp index 0175916..eaf54ec 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -146,14 +146,23 @@ dumb_ptr npc_name2id(NpcName name) } /*========================================== - * NPCを名前で探す + * NPC Spells *------------------------------------------ */ -NpcEvent spell_name2id(RString name) +NpcName spell_name2id(RString name) { return spells_by_name.get(name); } +/*========================================== + * NPC Spells Events + *------------------------------------------ + */ +NpcEvent spell_event2id(RString name) +{ + return spells_by_events.get(name); +} + /*========================================== * Spell Toknise * Return a pair of strings, {spellname, parameter} @@ -189,26 +198,45 @@ std::pair magic_tokenise(XString src) */ int magic_message(dumb_ptr caster, XString source_invocation) { - if (pc_isdead(caster)) - return 0; - if (bool(caster->status.option & Opt0::HIDE)) - return 0; - if (caster->cast_tick > gettick()) - return 0; - auto pair = magic_tokenise(source_invocation); // Spell Cast - NpcName spell_name = stringish(pair.first); + NpcName spell_name = spell_name2id(pair.first); + NpcEvent spell_event = spell_event2id(pair.first); + PRINTF("Cast: %s\n"_fmt, RString(pair.first)); + RString spell_params = pair.second; - NpcEvent event = spell_name2id(spell_name); + dumb_ptr nd = npc_name2id(spell_name); - if (event) + if (nd) { - PRINTF("Cast: %s\n"_fmt, spell_name); - PRINTF("event: %s\n"_fmt, event); + PRINTF("NPC: %s %d\n"_fmt, nd->name, nd->bl_id); PRINTF("Params: %s\n"_fmt, spell_params); - npc_event(caster, event, 0); + caster->npc_id = nd->bl_id; + dumb_ptr map_bl = map_id2bl(nd->bl_id); + if (!map_bl) + map_addnpc(caster->bl_m, nd); + argrec_t arg[1] = + { + {"@args$"_s, spell_params}, + }; + caster->npc_pos = run_script_l(ScriptPointer(borrow(*nd->is_script()->scr.script), 0), caster->bl_id, nd->bl_id, arg); + return 1; + } + if (spell_event.label) + { + dumb_ptr nd = npc_name2id(spell_event.npc); + PRINTF("NPC: %s %d\n"_fmt, nd->name, nd->bl_id); + PRINTF("Params: %s\n"_fmt, spell_params); + caster->npc_id = nd->bl_id; + dumb_ptr map_bl = map_id2bl(nd->bl_id); + if (!map_bl) + map_addnpc(caster->bl_m, nd); + argrec_t arg[1] = + { + {"@args$"_s, spell_params}, + }; + caster->npc_pos = npc_event_do_l(spell_event, caster->bl_id, arg); return 1; } return 0; diff --git a/src/map/pc.cpp b/src/map/pc.cpp index c4c3ad9..7f7512c 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -54,7 +54,6 @@ #include "globals.hpp" #include "intif.hpp" #include "itemdb.hpp" -#include "magic-stmt.hpp" #include "map.hpp" #include "map_conf.hpp" #include "npc.hpp" @@ -742,7 +741,7 @@ int pc_authok(AccountId id, int login_id2, ClientVersion client_version, // The above is no longer accurate now that we use , but // I'm still not reverting this. // -o11c - sd->cast_tick = tick; // + pc_readglobalreg (sd, "MAGIC_CAST_TICK"_s); + //sd->cast_tick = tick; // + pc_readglobalreg (sd, "MAGIC_CAST_TICK"_s); // アカウント変数の送信要求 intif_request_accountreg(sd); @@ -3272,8 +3271,8 @@ int pc_damage(dumb_ptr src, dumb_ptr sd, clif_updatestatus(sd, SP::HP); pc_calcstatus(sd, 0); // [Fate] Reset magic - sd->cast_tick = gettick(); - magic::magic_stop_completely(sd); + //sd->cast_tick = gettick(); + //magic_stop_completely(sd); if (battle_config.death_penalty_type > 0 && sd->status.base_level >= 20) { @@ -4992,7 +4991,7 @@ void do_init_pc(void) void pc_cleanup(dumb_ptr sd) { - magic::magic_stop_completely(sd); + //magic_stop_completely(sd); } void pc_invisibility(dumb_ptr sd, int enabled) diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp index 35e119b..2d9ac23 100644 --- a/src/map/script-fun.cpp +++ b/src/map/script-fun.cpp @@ -47,7 +47,6 @@ #include "globals.hpp" #include "intif.hpp" #include "itemdb.hpp" -#include "magic-interpreter-base.hpp" #include "map.hpp" #include "mob.hpp" #include "npc.hpp" @@ -259,7 +258,6 @@ void builtin_menu(ScriptState *st) buf += choice_str; buf += ':'; } - clif_scriptmenu(script_rid2sd(st), st->oid, AString(buf)); } else @@ -2767,18 +2765,6 @@ void builtin_getitemlink(ScriptState *st) push_str(st->stack, buf); } -static -void builtin_getspellinvocation(ScriptState *st) -{ - RString name = conv_str(st, &AARG(0)); - - AString invocation = magic::magic_find_invocation(name); - if (!invocation) - invocation = "..."_s; - - push_str(st->stack, invocation); -} - static void builtin_getpartnerid2(ScriptState *st) { @@ -3328,18 +3314,6 @@ void builtin_npctalk(ScriptState *st) clif_message(nd, str); } -/*========================================== - * casttime - *------------------------------------------ - */ -static -void builtin_casttime(ScriptState *st) -{ - dumb_ptr sd = script_rid2sd(st); - interval_t tick = static_cast(conv_num(st, &AARG(0))); - sd->cast_tick = gettick() + tick; -} - /*========================================== * register cmd *------------------------------------------ @@ -3347,13 +3321,15 @@ void builtin_casttime(ScriptState *st) static void builtin_registercmd(ScriptState *st) { - dumb_ptr nd = map_id_is_npc(st->oid); RString evoke = conv_str(st, &AARG(0)); + NpcName npcname = stringish(conv_str(st, &AARG(1))); ZString event_ = conv_str(st, &AARG(1)); NpcEvent event; extract(event_, &event); - - spells_by_name.put(evoke, event); + if (event.label) + spells_by_events.put(evoke, event); + else + spells_by_name.put(evoke, npcname); } /*========================================== @@ -3826,7 +3802,6 @@ BuiltinFunction builtin_functions[] = BUILTIN(marriage, "P"_s, 'i'), BUILTIN(divorce, ""_s, 'i'), BUILTIN(getitemlink, "I"_s, 's'), - BUILTIN(getspellinvocation, "s"_s, 's'), BUILTIN(getpartnerid2, ""_s, 'i'), BUILTIN(explode, "Nss"_s, '\0'), BUILTIN(getinventorylist, ""_s, '\0'), @@ -3843,8 +3818,7 @@ BuiltinFunction builtin_functions[] = BUILTIN(npcareawarp, "xyxyis"_s, '\0'), BUILTIN(message, "Ps"_s, '\0'), BUILTIN(npctalk, "ss?"_s, '\0'), - BUILTIN(casttime, "i"_s, '\0'), - BUILTIN(registercmd, "sE"_s, '\0'), + BUILTIN(registercmd, "ss"_s, '\0'), BUILTIN(title, "s"_s, '\0'), BUILTIN(smsg, "e??"_s, '\0'), BUILTIN(remotecmd, "s?"_s, '\0'), diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 8a397a3..6066a0d 100644 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -51,7 +51,6 @@ #include "battle_conf.hpp" #include "clif.hpp" #include "globals.hpp" -#include "magic-stmt.hpp" #include "mob.hpp" #include "pc.hpp" @@ -822,13 +821,6 @@ void skill_status_change_timer(TimerData *tid, tick_t tick, BlockId id, StatusCh if (bl->bl_type == BL::PC) sd = bl->is_player(); - if (sc_data[type].spell_invocation) - { // Must report termination - magic::spell_effect_report_termination(sc_data[type].spell_invocation, - bl->bl_id, type, 0); - sc_data[type].spell_invocation = BlockId(); - } - switch (type) { case StatusChange::SC_POISON: @@ -1050,11 +1042,6 @@ int skill_status_effect(dumb_ptr bl, StatusChange type, clif_changeoption(bl); sc_data[type].val1 = val1; - if (sc_data[type].spell_invocation) // Supplant by newer spell - magic::spell_effect_report_termination(sc_data[type].spell_invocation, - bl->bl_id, type, 1); - - sc_data[type].spell_invocation = spell_invocation; /* タイマー設定 */ sc_data[type].timer = Timer(gettick() + tick, diff --git a/src/mmo/cxxstdio_enums.hpp b/src/mmo/cxxstdio_enums.hpp index 28a8a14..01e8842 100644 --- a/src/mmo/cxxstdio_enums.hpp +++ b/src/mmo/cxxstdio_enums.hpp @@ -64,13 +64,6 @@ auto decay_for_printf(BF v) -> typename remove_enum::type { return inline auto decay_for_printf(MapCell v) -> typename remove_enum::type { return typename remove_enum::type(v); } } // namespace map::e -namespace magic -{ -enum class SPELLARG : uint8_t; - -inline -auto decay_for_printf(SPELLARG v) -> typename remove_enum::type { return typename remove_enum::type(v); } -} // namespace map::magic enum class BL : uint8_t; enum class ByteCode : uint8_t; diff --git a/src/mmo/skill.t.hpp b/src/mmo/skill.t.hpp index 21e4059..df9c40c 100644 --- a/src/mmo/skill.t.hpp +++ b/src/mmo/skill.t.hpp @@ -41,11 +41,7 @@ enum class StatusChange : uint16_t // these ones are used by clif_status_change, // e.g. by the magic system ZERO = 0, - ATTACK_ICON_GENERIC = 2000, - ATTACK_ICON_SHEARING = 2001, - CART = 0x0c, CLIF_OPTION_SC_INVISIBILITY = 0x1000, - CLIF_OPTION_SC_SCRIBE = 0x1001, // the rest are the normal effects SC_SLOWPOISON = 14, // item script -- cgit v1.2.3-60-g2f50