diff options
Diffstat (limited to 'src/map')
92 files changed, 9944 insertions, 10806 deletions
diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 0d70b36..7739966 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -24,8 +24,6 @@ #include <algorithm> -#include "../conf/version.hpp" - #include "../compat/nullpo.hpp" #include "../compat/fun.hpp" @@ -39,31 +37,40 @@ #include "../generic/random.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" +#include "../io/extract.hpp" #include "../io/read.hpp" #include "../io/write.hpp" #include "../net/socket.hpp" #include "../net/timer.hpp" +#include "../net/timestamp-utils.hpp" #include "../mmo/config_parse.hpp" -#include "../mmo/core.hpp" -#include "../mmo/extract.hpp" +#include "../mmo/cxxstdio_enums.hpp" #include "../mmo/extract_enums.hpp" #include "../mmo/human_time_diff.hpp" #include "../mmo/ids.hpp" -#include "../mmo/mmo.hpp" -#include "../mmo/utils.hpp" #include "../mmo/version.hpp" +#include "../high/core.hpp" +#include "../high/extract_mmo.hpp" +#include "../high/mmo.hpp" +#include "../high/utils.hpp" + +#include "../ast/npc.hpp" + #include "battle.hpp" +#include "battle_conf.hpp" #include "chrif.hpp" #include "clif.hpp" +#include "globals.hpp" #include "intif.hpp" #include "itemdb.hpp" #include "map.hpp" +#include "map_conf.hpp" #include "mob.hpp" #include "npc.hpp" +#include "npc-parse.hpp" #include "party.hpp" #include "pc.hpp" #include "skill.hpp" @@ -76,6 +83,8 @@ namespace tmwa { +namespace map +{ enum class ATCE { OKAY, @@ -105,7 +114,7 @@ Map<XString, AtCommandInfo> atcommand_info; static -AtCommandInfo *atcommand(XString cmd); +Option<Borrowed<AtCommandInfo>> atcommand(XString cmd); // These @commands are used within other @commands. static @@ -203,9 +212,7 @@ void log_atcommand(dumb_ptr<map_session_data> sd, ZString cmd) return; timestamp_seconds_buffer tmpstr; stamp_time(tmpstr); - MapName map = (sd->bl_m - ? sd->bl_m->name_ - : stringish<MapName>("undefined.gat"_s)); + MapName map = (sd->bl_m->name_); FPRINTF(*fp, "[%s] %s(%d,%d) %s(%d) : %s\n"_fmt, tmpstr, map, sd->bl_x, sd->bl_y, @@ -213,11 +220,9 @@ void log_atcommand(dumb_ptr<map_session_data> sd, ZString cmd) cmd); } -AString gm_log; - io::AppendFile *get_gm_log() { - if (!gm_log) + if (!map_conf.gm_log) return nullptr; struct tm ctime = TimeT::now(); @@ -233,7 +238,7 @@ io::AppendFile *get_gm_log() last_logfile_nr = logfile_nr; AString fullname = STRPRINTF("%s.%04d-%02d"_fmt, - gm_log, year, month); + map_conf.gm_log, year, month); if (gm_logfile) gm_logfile.reset(); @@ -243,7 +248,7 @@ io::AppendFile *get_gm_log() if (!gm_logfile) { perror("GM log file"); - gm_log = AString(); + map_conf.gm_log = AString(); } return gm_logfile.get(); } @@ -260,8 +265,6 @@ bool is_atcommand(Session *s, dumb_ptr<map_session_data> sd, ZString arg; asplit(message, &command, &arg); - AtCommandInfo *info = atcommand(command); - if (!gmlvl) gmlvl = pc_isGM(sd); if (battle_config.atcommand_gm_only != 0 && !gmlvl) @@ -271,14 +274,16 @@ bool is_atcommand(Session *s, dumb_ptr<map_session_data> sd, clif_displaymessage(s, output); return true; } - if (!info) + + Option<P<AtCommandInfo>> info_ = atcommand(command); + P<AtCommandInfo> info = TRY_UNWRAP(info_, { AString output = STRPRINTF("GM command not found: %s"_fmt, AString(command)); clif_displaymessage(s, output); return true; // don't show in chat - } + }); if (!(gmlvl.satisfies(info->level))) { AString output = STRPRINTF("GM command is level %d, but you are level %d: %s"_fmt, @@ -320,15 +325,15 @@ bool is_atcommand(Session *s, dumb_ptr<map_session_data> sd, } } -AtCommandInfo *atcommand(XString cmd) +Option<Borrowed<AtCommandInfo>> atcommand(XString cmd) { if (cmd.startswith('@')) { XString command = cmd.xslice_t(1); - AtCommandInfo *it = atcommand_info.search(command); + Option<P<AtCommandInfo>> it = atcommand_info.search(command); return it; } - return nullptr; + return None; } static @@ -344,51 +349,42 @@ void atkillmonster_sub(dumb_ptr<block_list> bl, int flag) } static -AtCommandInfo *get_atcommandinfo_byname(XString name) +Option<Borrowed<AtCommandInfo>> get_atcommandinfo_byname(XString name) { return atcommand_info.search(name); } -bool atcommand_config_read(ZString cfgName) +static +bool atcommand_config(io::Spanned<XString> w1, io::Spanned<ZString> w2) { - io::ReadFile in(cfgName); - if (!in.is_open()) - { - PRINTF("At commands configuration file not found: %s\n"_fmt, cfgName); - return false; - } - bool rv = true; - AString line; - while (in.getline(line)) { - if (is_comment(line)) - continue; - XString w1; - ZString w2; - if (!config_split(line, &w1, &w2)) + Option<P<AtCommandInfo>> p_ = get_atcommandinfo_byname(w1.data); + OMATCH_BEGIN (p_) { - PRINTF("Bad config line: %s\n"_fmt, line); - rv = false; - continue; - } - AtCommandInfo *p = get_atcommandinfo_byname(w1); - if (p != nullptr) - { - p->level = GmLevel::from(static_cast<uint32_t>(atoi(w2.c_str()))); - } - else if (w1 == "import"_s) - rv &= atcommand_config_read(w2); - else - { - PRINTF("%s: bad line: %s\n"_fmt, cfgName, line); - rv = false; + OMATCH_CASE_SOME (p) + { + p->level = GmLevel::from(static_cast<uint32_t>(atoi(w2.data.c_str()))); + } + OMATCH_CASE_NONE () + { + { + w1.span.error("Unknown @command for permission level config."_s); + rv = false; + } + } } + OMATCH_END (); } return rv; } +bool atcommand_config_read(ZString cfgName) +{ + return load_config_file(cfgName, atcommand_config); +} + /// @ command processing functions static @@ -421,9 +417,7 @@ ATCE atcommand_help(Session *s, dumb_ptr<map_session_data>, if (message.startswith('@')) { ZString cmd = message.xslice_t(1); - const AtCommandInfo *info = atcommand_info.search(cmd); - if (!info) - return ATCE::EXIST; + P<AtCommandInfo> info = TRY_UNWRAP(atcommand_info.search(cmd), return ATCE::EXIST); clif_displaymessage(s, STRPRINTF("Usage: @%s %s"_fmt, cmd, info->args)); clif_displaymessage(s, info->help); return ATCE::OKAY; @@ -534,16 +528,16 @@ ATCE atcommand_charwarp(Session *s, dumb_ptr<map_session_data> sd, // you can rura+ only lower or same GM level if (x > 0 && x < 800 && y > 0 && y < 800) { - map_local *m = map_mapname2mapid(map_name); - if (m != nullptr && m->flag.get(MapFlag::NOWARPTO) - && !pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level)))) + Option<P<map_local>> m = map_mapname2mapid(map_name); + if (m.map([](P<map_local> m_){ return m_->flag.get(MapFlag::NOWARPTO); }).copy_or(false) + && !pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level)) { clif_displaymessage(s, "You are not authorised to warp someone to this map."_s); return ATCE::PERM; } - if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARP) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (pl_sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp this player from its actual map."_s); @@ -603,16 +597,16 @@ ATCE atcommand_warp(Session *s, dumb_ptr<map_session_data> sd, if (x > 0 && x < 800 && y > 0 && y < 800) { - map_local *m = map_mapname2mapid(map_name); - if (m != nullptr && m->flag.get(MapFlag::NOWARPTO) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + Option<P<map_local>> m = map_mapname2mapid(map_name); + if (m.map([](P<map_local> m_){ return m_->flag.get(MapFlag::NOWARPTO); }).copy_or(false) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you to this map."_s); return ATCE::PERM; } - if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you from your actual map."_s); @@ -645,7 +639,7 @@ ATCE atcommand_where(Session *s, dumb_ptr<map_session_data> sd, dumb_ptr<map_session_data> pl_sd = character.to__actual() ? map_nick2sd(character) : sd; if (pl_sd != nullptr && !((battle_config.hide_GM_session - || bool(pl_sd->status.option & Option::HIDE)) + || bool(pl_sd->status.option & Opt0::HIDE)) && !(pc_isGM(sd).detects(pc_isGM(pl_sd))))) { // you can look only lower or same level @@ -679,15 +673,15 @@ ATCE atcommand_goto(Session *s, dumb_ptr<map_session_data> sd, dumb_ptr<map_session_data> pl_sd = map_nick2sd(character); if (pl_sd != nullptr) { - if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARPTO) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (pl_sd->bl_m->flag.get(MapFlag::NOWARPTO) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you to the map of this player."_s); return ATCE::PERM; } - if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you from your actual map."_s); @@ -707,6 +701,69 @@ ATCE atcommand_goto(Session *s, dumb_ptr<map_session_data> sd, } static +ATCE atcommand_npc(Session *s, dumb_ptr<map_session_data> sd, + ZString message) +{ + NpcName npc; + + if (!asplit(message, &npc)) + { + clif_displaymessage(s, + "Please, enter a npc name (usage: @npc/@warptonpc/@gotonpc <npc>)."_s); + return ATCE::USAGE; + } + + dumb_ptr<npc_data> nd = npc_name2id(npc); + if (nd != nullptr) + { + if (nd->bl_m->flag.get(MapFlag::NOWARPTO) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) + { + clif_displaymessage(s, + "You are not authorised to warp you to the map of this npc."_s); + return ATCE::PERM; + } + if (sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) + { + clif_displaymessage(s, + "You are not authorised to warp you from your actual map."_s); + return ATCE::PERM; + } + + int x = nd->bl_x, y = nd->bl_y, x0 = (x >= 5)? (x - 5): 0, j = 0, + y0 = (y >= 5)? (y - 5): 0, x1 = (x + 5), y1 = (y + 5), max; + max = (y1 - y0 + 1) * (x1 - x0 + 1) * 3; + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(nd->bl_m->name_), return ATCE::OKAY); + if (max > 1000) + max = 1000; + if (bool(map_getcell(m, x, y) & MapCell::UNWALKABLE)){ + do + { + x = random_::in(x0, x1); + y = random_::in(y0, y1); + } + while (bool(map_getcell(m, x, y) & MapCell::UNWALKABLE) + && (++j) < max); + if (j >= max) + { + return ATCE::OKAY; // Since reference of the place which boils first went wrong, it stops. + } + } + pc_setpos(sd, nd->bl_m->name_, x, y, BeingRemoveWhy::WARPED); + AString output = STRPRINTF("Jump to %s"_fmt, npc); + clif_displaymessage(s, output); + } + else + { + clif_displaymessage(s, "Npc not found."_s); + return ATCE::EXIST; + } + + return ATCE::OKAY; +} + +static ATCE atcommand_jump(Session *s, dumb_ptr<map_session_data> sd, ZString message) { @@ -720,15 +777,15 @@ ATCE atcommand_jump(Session *s, dumb_ptr<map_session_data> sd, y = random_::in(1, 399); if (x > 0 && x < 800 && y > 0 && y < 800) { - if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARPTO) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (sd->bl_m->flag.get(MapFlag::NOWARPTO) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you to your actual map."_s); return ATCE::PERM; } - if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you from your actual map."_s); @@ -768,7 +825,7 @@ ATCE atcommand_who(Session *s, dumb_ptr<map_session_data> sd, GmLevel pl_gm_level = pc_isGM(pl_sd); if (! ((battle_config.hide_GM_session - || bool(pl_sd->status.option & Option::HIDE)) + || bool(pl_sd->status.option & Opt0::HIDE)) && !(gm_level.detects(pl_gm_level)))) { // you can look only lower or same level @@ -812,7 +869,6 @@ ATCE atcommand_whogroup(Session *s, dumb_ptr<map_session_data> sd, ZString message) { int count; - PartyPair p; VString<23> match_text = message; match_text = match_text.to_lower(); @@ -830,7 +886,7 @@ ATCE atcommand_whogroup(Session *s, dumb_ptr<map_session_data> sd, GmLevel pl_gm_level = pc_isGM(pl_sd); if (! ((battle_config.hide_GM_session - || bool(pl_sd->status.option & Option::HIDE)) + || bool(pl_sd->status.option & Opt0::HIDE)) && (!(gm_level.detects(pl_gm_level))))) { // you can look only lower or same level @@ -838,8 +894,8 @@ ATCE atcommand_whogroup(Session *s, dumb_ptr<map_session_data> sd, if (player_name.contains_seq(match_text)) { // search with no case sensitive - p = party_search(pl_sd->status.party_id); - PartyName temp0 = p ? p->name : stringish<PartyName>("None"_s); + Option<PartyPair> p_ = party_search(pl_sd->status.party_id); + PartyName temp0 = p_.pmd_pget(&PartyMost::name).move_or(stringish<PartyName>("None"_s)); AString output; if (pl_gm_level) output = STRPRINTF( @@ -870,15 +926,14 @@ ATCE atcommand_whomap(Session *s, dumb_ptr<map_session_data> sd, ZString message) { int count; - map_local *map_id; - { + Borrowed<map_local> map_id = + ({ MapName map_name; extract(message, &map_name); - map_id = map_mapname2mapid(map_name); - if (map_id == nullptr) - map_id = sd->bl_m; - } + + map_mapname2mapid(map_name).copy_or(sd->bl_m); + }); count = 0; GmLevel gm_level = pc_isGM(sd); @@ -893,7 +948,7 @@ ATCE atcommand_whomap(Session *s, dumb_ptr<map_session_data> sd, GmLevel pl_gm_level = pc_isGM(pl_sd); if (! ((battle_config.hide_GM_session - || bool(pl_sd->status.option & Option::HIDE)) + || bool(pl_sd->status.option & Opt0::HIDE)) && (!(gm_level.detects(pl_gm_level))))) { // you can look only lower or same level @@ -929,16 +984,14 @@ ATCE atcommand_whomapgroup(Session *s, dumb_ptr<map_session_data> sd, ZString message) { int count; - PartyPair p; - map_local *map_id; - { + P<map_local> map_id = + ({ MapName map_name; extract(message, &map_name); - map_id = map_mapname2mapid(map_name); - if (map_id == nullptr) - map_id = sd->bl_m; - } + + map_mapname2mapid(map_name).copy_or(sd->bl_m); + }); count = 0; GmLevel gm_level = pc_isGM(sd); @@ -953,14 +1006,14 @@ ATCE atcommand_whomapgroup(Session *s, dumb_ptr<map_session_data> sd, GmLevel pl_gm_level = pc_isGM(pl_sd); if (! ((battle_config.hide_GM_session - || bool(pl_sd->status.option & Option::HIDE)) + || bool(pl_sd->status.option & Opt0::HIDE)) && (!(gm_level.detects(pl_gm_level))))) { // you can look only lower or same level if (pl_sd->bl_m == map_id) { - p = party_search(pl_sd->status.party_id); - PartyName temp0 = p ? p->name : stringish<PartyName>("None"_s); + Option<PartyPair> p_ = party_search(pl_sd->status.party_id); + PartyName temp0 = p_.pmd_pget(&PartyMost::name).copy_or(stringish<PartyName>("None"_s)); AString output; if (pl_gm_level) output = STRPRINTF("Name: %s (GM:%d) | Party: '%s'"_fmt, @@ -994,7 +1047,6 @@ ATCE atcommand_whogm(Session *s, dumb_ptr<map_session_data> sd, ZString message) { int count; - PartyPair p; VString<23> match_text = message; match_text = match_text.to_lower(); @@ -1014,7 +1066,7 @@ ATCE atcommand_whogm(Session *s, dumb_ptr<map_session_data> sd, { if (! ((battle_config.hide_GM_session - || bool(pl_sd->status.option & Option::HIDE)) + || bool(pl_sd->status.option & Opt0::HIDE)) && (!(gm_level.detects(pl_gm_level))))) { // you can look only lower or same level @@ -1034,8 +1086,8 @@ ATCE atcommand_whogm(Session *s, dumb_ptr<map_session_data> sd, "Novice/Human"_s, pl_sd->status.job_level); clif_displaymessage(s, output); - p = party_search(pl_sd->status.party_id); - PartyName temp0 = p ? p->name : stringish<PartyName>("None"_s); + Option<PartyPair> p_ = party_search(pl_sd->status.party_id); + PartyName temp0 = p_.pmd_pget(&PartyMost::name).copy_or(stringish<PartyName>("None"_s)); output = STRPRINTF( " Party: '%s'"_fmt, temp0); @@ -1076,16 +1128,16 @@ static ATCE atcommand_load(Session *s, dumb_ptr<map_session_data> sd, ZString) { - map_local *m = map_mapname2mapid(sd->status.save_point.map_); - if (m != nullptr && m->flag.get(MapFlag::NOWARPTO) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + Option<P<map_local>> m = map_mapname2mapid(sd->status.save_point.map_); + if (m.map([](P<map_local> m_){ return m_->flag.get(MapFlag::NOWARPTO); }).copy_or(false) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you to your save map."_s); return ATCE::PERM; } - if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you from your actual map."_s); @@ -1148,16 +1200,13 @@ static ATCE atcommand_storage(Session *s, dumb_ptr<map_session_data> sd, ZString) { - Storage *stor; - if (sd->state.storage_open) { clif_displaymessage(s, "msg_table[250]"_s); return ATCE::EXIST; } - if ((stor = account2storage2(sd->status_key.account_id)) != nullptr - && stor->storage_status == 1) + if (account2storage2(sd->status_key.account_id).pmd_pget(&Storage::storage_status).copy_or(0) == 1) { clif_displaymessage(s, "msg_table[250]"_s); return ATCE::EXIST; @@ -1174,7 +1223,7 @@ ATCE atcommand_option(Session *s, dumb_ptr<map_session_data> sd, { Opt1 param1 = Opt1::ZERO; Opt2 param2 = Opt2::ZERO; - Option param3 = Option::ZERO; + Opt0 param3 = Opt0::ZERO; if (!extract(message, record<',', 1>(¶m1, ¶m2, ¶m3))) return ATCE::USAGE; @@ -1194,14 +1243,14 @@ static ATCE atcommand_hide(Session *s, dumb_ptr<map_session_data> sd, ZString) { - if (bool(sd->status.option & Option::HIDE)) + if (bool(sd->status.option & Opt0::HIDE)) { - sd->status.option &= ~Option::HIDE; + sd->status.option &= ~Opt0::HIDE; clif_displaymessage(s, "Invisible: Off."_s); } else { - sd->status.option |= Option::HIDE; + sd->status.option |= Opt0::HIDE; clif_displaymessage(s, "Invisible: On."_s); } clif_changeoption(sd); @@ -1259,8 +1308,8 @@ ATCE atcommand_alive(Session *s, dumb_ptr<map_session_data> sd, sd->status.hp = sd->status.max_hp; sd->status.sp = sd->status.max_sp; pc_setstand(sd); - if (static_cast<interval_t>(battle_config.player_invincible_time) > interval_t::zero()) - pc_setinvincibletimer(sd, static_cast<interval_t>(battle_config.player_invincible_time)); + if (battle_config.player_invincible_time > interval_t::zero()) + pc_setinvincibletimer(sd, battle_config.player_invincible_time); clif_updatestatus(sd, SP::HP); clif_updatestatus(sd, SP::SP); clif_resurrection(sd, 1); @@ -1312,7 +1361,7 @@ ATCE atcommand_heal(Session *s, dumb_ptr<map_session_data> sd, if (hp < 0) // display like damage - clif_damage(sd, sd, gettick(), interval_t::zero(), interval_t::zero(), -hp, 0, DamageType::RETURNED, 0); + clif_damage(sd, sd, gettick(), interval_t::zero(), interval_t::zero(), -hp, 0, DamageType::RETURNED); if (hp != 0 || sp != 0) { @@ -1332,13 +1381,27 @@ ATCE atcommand_heal(Session *s, dumb_ptr<map_session_data> sd, } static +Option<P<struct item_data>> extract_item_opt(XString item_name) +{ + Option<P<struct item_data>> item_data = itemdb_searchname(item_name); + if (item_data.is_some()) + return item_data; + + ItemNameId item_id; + if (extract(item_name, &item_id)) + { + item_data = itemdb_exists(item_id); + return item_data; + } + return None; +} + +static ATCE atcommand_item(Session *s, dumb_ptr<map_session_data> sd, ZString message) { XString item_name; int number = 0; - ItemNameId item_id; - struct item_data *item_data = nullptr; int get_count, i; if (!extract(message, record<' ', 1>(&item_name, &number))) @@ -1351,14 +1414,10 @@ ATCE atcommand_item(Session *s, dumb_ptr<map_session_data> sd, if (number <= 0) number = 1; - if ((item_data = itemdb_searchname(item_name)) != nullptr) - item_id = item_data->nameid; - else if (extract(item_name, &item_id) && (item_data = itemdb_exists(item_id)) != nullptr) - item_id = item_data->nameid; - else - return ATCE::EXIST; + P<struct item_data> item_data = TRY_UNWRAP(extract_item_opt(item_name), return ATCE::EXIST); + ItemNameId item_id = item_data->nameid; + assert (item_id); - if (item_id) { get_count = number; if (item_data->type == ItemType::WEAPON @@ -1379,11 +1438,6 @@ ATCE atcommand_item(Session *s, dumb_ptr<map_session_data> sd, } clif_displaymessage(s, "Item created."_s); } - else - { - clif_displaymessage(s, "Invalid item ID or name."_s); - return ATCE::EXIST; - } return ATCE::OKAY; } @@ -1537,25 +1591,6 @@ ATCE atcommand_joblevelup(Session *s, dumb_ptr<map_session_data> sd, } static -ATCE atcommand_gm(Session *s, dumb_ptr<map_session_data> sd, - ZString message) -{ - if (!message) - return ATCE::USAGE; - - if (pc_isGM(sd)) - { - // a GM can not use this function. only a normal player (become gm is not for gm!) - clif_displaymessage(s, "You already have some GM powers."_s); - return ATCE::PERM; - } - else - chrif_changegm(sd->status_key.account_id, message); - - return ATCE::OKAY; -} - -static ATCE atcommand_pvpoff(Session *s, dumb_ptr<map_session_data> sd, ZString) { @@ -1595,6 +1630,25 @@ ATCE atcommand_pvpoff(Session *s, dumb_ptr<map_session_data> sd, } static +ATCE atcommand_exprate(Session *s, dumb_ptr<map_session_data>, + ZString message) +{ + int rate; + + if (!extract(message, &rate) || !rate) + { + clif_displaymessage(s, + "Please, enter a rate adjustement (usage: @exprate <percent>)."_s); + return ATCE::USAGE; + } + battle_config.base_exp_rate = rate; + battle_config.job_exp_rate = rate; + AString output = STRPRINTF("All Xp at %d percent"_fmt, rate); + clif_displaymessage(s, output); + return ATCE::OKAY; +} + +static ATCE atcommand_pvpon(Session *s, dumb_ptr<map_session_data> sd, ZString) { @@ -1621,7 +1675,6 @@ ATCE atcommand_pvpon(Session *s, dumb_ptr<map_session_data> sd, pl_sd->pvp_timer = Timer(gettick() + 200_ms, std::bind(pc_calc_pvprank_timer, ph::_1, ph::_2, pl_sd->bl_id)); pl_sd->pvp_rank = 0; - pl_sd->pvp_lastusers = 0; pl_sd->pvp_point = 5; } } @@ -1809,14 +1862,13 @@ static void atcommand_killmonster_sub(Session *s, dumb_ptr<map_session_data> sd, ZString message, const int drop) { - map_local *map_id; - { + P<map_local> map_id = + ({ MapName map_name; extract(message, &map_name); - map_id = map_mapname2mapid(map_name); - if (map_id == nullptr) - map_id = sd->bl_m; - } + + map_mapname2mapid(map_name).copy_or(sd->bl_m); + }); map_foreachinarea(std::bind(atkillmonster_sub, ph::_1, drop), map_id, @@ -2080,15 +2132,15 @@ ATCE atcommand_recall(Session *s, dumb_ptr<map_session_data> sd, if (pc_isGM(sd).overwhelms(pc_isGM(pl_sd))) { // you can recall only lower or same level - if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARPTO) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (sd->bl_m->flag.get(MapFlag::NOWARPTO) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp somenone to your actual map."_s); return ATCE::PERM; } - if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARP) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (pl_sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp this player from its actual map."_s); @@ -2127,8 +2179,8 @@ ATCE atcommand_revive(Session *s, dumb_ptr<map_session_data> sd, { pl_sd->status.hp = pl_sd->status.max_hp; pc_setstand(pl_sd); - if (static_cast<interval_t>(battle_config.player_invincible_time) > interval_t::zero()) - pc_setinvincibletimer(sd, static_cast<interval_t>(battle_config.player_invincible_time)); + if (battle_config.player_invincible_time > interval_t::zero()) + pc_setinvincibletimer(sd, battle_config.player_invincible_time); clif_updatestatus(pl_sd, SP::HP); clif_updatestatus(pl_sd, SP::SP); clif_resurrection(pl_sd, 1); @@ -2257,7 +2309,7 @@ ATCE atcommand_character_option(Session *s, dumb_ptr<map_session_data> sd, { Opt1 opt1; Opt2 opt2; - Option opt3; + Opt0 opt3; CharName character; if (!asplit(message, &opt1, &opt2, &opt3, &character)) return ATCE::USAGE; @@ -2403,16 +2455,15 @@ ATCE atcommand_character_save(Session *s, dumb_ptr<map_session_data> sd, if (pc_isGM(sd).overwhelms(pc_isGM(pl_sd))) { // you can change save point only to lower or same gm level - map_local *m = map_mapname2mapid(map_name); - if (m == nullptr) + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(map_name), { clif_displaymessage(s, "Map not found."_s); return ATCE::EXIST; - } - else + }); + { - if (m != nullptr && m->flag.get(MapFlag::NOWARPTO) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (m->flag.get(MapFlag::NOWARPTO) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to set this map as a save map."_s); @@ -2976,7 +3027,6 @@ ATCE atcommand_idsearch(Session *s, dumb_ptr<map_session_data>, { ItemName item_name; int match; - struct item_data *item; if (!extract(message, &item_name) || !item_name) return ATCE::USAGE; @@ -2986,8 +3036,8 @@ ATCE atcommand_idsearch(Session *s, dumb_ptr<map_session_data>, match = 0; for (ItemNameId i = wrap<ItemNameId>(0); i < wrap<ItemNameId>(-1); i = next(i)) { - if ((item = itemdb_exists(i)) != nullptr - && item->jname.contains_seq(item_name)) + P<struct item_data> item = TRY_UNWRAP(itemdb_exists(i), continue); + if (item->jname.contains_seq(item_name)) { match++; output = STRPRINTF("%s: %d"_fmt, item->jname, item->nameid); @@ -3350,8 +3400,8 @@ ATCE atcommand_recallall(Session *s, dumb_ptr<map_session_data> sd, { int count; - if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARPTO) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (sd->bl_m->flag.get(MapFlag::NOWARPTO) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp somenone to your actual map."_s); @@ -3371,8 +3421,8 @@ ATCE atcommand_recallall(Session *s, dumb_ptr<map_session_data> sd, && pc_isGM(sd).overwhelms(pc_isGM(pl_sd))) { // you can recall only lower or same level - if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARP) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (pl_sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) count++; else pc_setpos(pl_sd, sd->mapname_, sd->bl_x, sd->bl_y, BeingRemoveWhy::QUIT); @@ -3396,57 +3446,62 @@ ATCE atcommand_partyrecall(Session *s, dumb_ptr<map_session_data> sd, ZString message) { PartyName party_name; - PartyPair p; int count; if (!extract(message, &party_name) || !party_name) return ATCE::USAGE; - if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARPTO) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (sd->bl_m->flag.get(MapFlag::NOWARPTO) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp somenone to your actual map."_s); return ATCE::PERM; } - if ((p = party_searchname(party_name)) || - // name first to avoid error when name begin with a number - (p = party_search(wrap<PartyId>(static_cast<uint32_t>(atoi(message.c_str())))))) + // name first to avoid error when name begin with a number + Option<PartyPair> p_ = party_searchname(party_name); + if (p_.is_none()) + p_ = party_search(wrap<PartyId>(static_cast<uint32_t>(atoi(message.c_str())))); + OMATCH_BEGIN (p_) { - count = 0; - for (io::FD i : iter_fds()) + OMATCH_CASE_SOME (p) { - Session *s2 = get_session(i); - if (!s2) - continue; - dumb_ptr<map_session_data> pl_sd = dumb_ptr<map_session_data>(static_cast<map_session_data *>(s2->session_data.get())); - if (pl_sd && pl_sd->state.auth - && sd->status_key.account_id != pl_sd->status_key.account_id - && pl_sd->status.party_id == p.party_id) + count = 0; + for (io::FD i : iter_fds()) { - if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARP) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) - count++; - else - pc_setpos(pl_sd, sd->mapname_, sd->bl_x, sd->bl_y, BeingRemoveWhy::QUIT); + Session *s2 = get_session(i); + if (!s2) + continue; + dumb_ptr<map_session_data> pl_sd = dumb_ptr<map_session_data>(static_cast<map_session_data *>(s2->session_data.get())); + if (pl_sd && pl_sd->state.auth + && sd->status_key.account_id != pl_sd->status_key.account_id + && pl_sd->status.party_id == p.party_id) + { + if (pl_sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) + count++; + else + pc_setpos(pl_sd, sd->mapname_, sd->bl_x, sd->bl_y, BeingRemoveWhy::QUIT); + } + } + AString output = STRPRINTF("All online characters of the %s party are near you."_fmt, p->name); + clif_displaymessage(s, output); + if (count) + { + output = STRPRINTF( + "Because you are not authorised to warp from some maps, %d player(s) have not been recalled."_fmt, + count); + clif_displaymessage(s, output); } } - AString output = STRPRINTF("All online characters of the %s party are near you."_fmt, p->name); - clif_displaymessage(s, output); - if (count) + OMATCH_CASE_NONE () { - output = STRPRINTF( - "Because you are not authorised to warp from some maps, %d player(s) have not been recalled."_fmt, - count); - clif_displaymessage(s, output); + clif_displaymessage(s, "Incorrect name or ID, or no one from the party is online."_s); + return ATCE::EXIST; } } - else - { - clif_displaymessage(s, "Incorrect name or ID, or no one from the party is online."_s); - return ATCE::EXIST; - } + OMATCH_END (); return ATCE::OKAY; } @@ -3468,9 +3523,7 @@ ATCE atcommand_mapinfo(Session *s, dumb_ptr<map_session_data> sd, if (!map_name) map_name = sd->mapname_; - map_local *m_id = map_mapname2mapid(map_name); - if (m_id != nullptr) - return ATCE::EXIST; + P<map_local> m_id = TRY_UNWRAP(map_mapname2mapid(map_name), return ATCE::EXIST); clif_displaymessage(s, "------ Map Info ------"_s); AString output = STRPRINTF("Map Name: %s"_fmt, map_name); @@ -3592,29 +3645,34 @@ ATCE atcommand_partyspy(Session *s, dumb_ptr<map_session_data> sd, if (!extract(message, &party_name)) return ATCE::USAGE; - PartyPair p; - if ((p = party_searchname(party_name)) || - // name first to avoid error when name begin with a number - (p = party_search(wrap<PartyId>(static_cast<uint32_t>(atoi(message.c_str())))))) + // name first to avoid error when name begin with a number + Option<PartyPair> p_ = party_searchname(party_name); + if (p_.is_none()) + p_ = party_search(wrap<PartyId>(static_cast<uint32_t>(atoi(message.c_str())))); + OMATCH_BEGIN (p_) { - if (sd->partyspy == p.party_id) + OMATCH_CASE_SOME (p) { - sd->partyspy = PartyId(); - AString output = STRPRINTF("No longer spying on the %s party."_fmt, p->name); - clif_displaymessage(s, output); + if (sd->partyspy == p.party_id) + { + sd->partyspy = PartyId(); + AString output = STRPRINTF("No longer spying on the %s party."_fmt, p->name); + clif_displaymessage(s, output); + } + else + { + sd->partyspy = p.party_id; + AString output = STRPRINTF("Spying on the %s party."_fmt, p->name); + clif_displaymessage(s, output); + } } - else + OMATCH_CASE_NONE () { - sd->partyspy = p.party_id; - AString output = STRPRINTF("Spying on the %s party."_fmt, p->name); - clif_displaymessage(s, output); + clif_displaymessage(s, "Incorrect name or ID, or no one from the party is online."_s); + return ATCE::EXIST; } } - else - { - clif_displaymessage(s, "Incorrect name or ID, or no one from the party is online."_s); - return ATCE::EXIST; - } + OMATCH_END (); return ATCE::OKAY; } @@ -3684,19 +3742,15 @@ ATCE atcommand_chardelitem(Session *s, dumb_ptr<map_session_data> sd, CharName character; XString item_name; int i, number = 0; - ItemNameId item_id; int count; - struct item_data *item_data; if (!asplit(message, &item_name, &number, &character) || number < 1) return ATCE::USAGE; - if ((item_data = itemdb_searchname(item_name)) != nullptr) - item_id = item_data->nameid; - else if (extract(item_name, &item_id) && (item_data = itemdb_exists(item_id)) != nullptr) - item_id = item_data->nameid; + P<struct item_data> item_data = TRY_UNWRAP(extract_item_opt(item_name), return ATCE::EXIST); + ItemNameId item_id = item_data->nameid; + assert (item_id); - if (item_id) { dumb_ptr<map_session_data> pl_sd = map_nick2sd(character); if (pl_sd != nullptr) @@ -3744,11 +3798,6 @@ ATCE atcommand_chardelitem(Session *s, dumb_ptr<map_session_data> sd, return ATCE::EXIST; } } - else - { - clif_displaymessage(s, "Invalid item ID or name."_s); - return ATCE::RANGE; - } return ATCE::OKAY; } @@ -3855,7 +3904,6 @@ static ATCE atcommand_character_item_list(Session *s, dumb_ptr<map_session_data> sd, ZString message) { - struct item_data *item_data = nullptr; int count, counter; CharName character; @@ -3872,10 +3920,10 @@ ATCE atcommand_character_item_list(Session *s, dumb_ptr<map_session_data> sd, count = 0; for (IOff0 i : IOff0::iter()) { - if (pl_sd->status.inventory[i].nameid - && (item_data = - itemdb_search(pl_sd->status.inventory[i].nameid)) != - nullptr) + if (!pl_sd->status.inventory[i].nameid) + continue; + P<struct item_data> item_data = TRY_UNWRAP(itemdb_exists(pl_sd->status.inventory[i].nameid), continue); + { counter = counter + pl_sd->status.inventory[i].amount; count++; @@ -3966,8 +4014,6 @@ static ATCE atcommand_character_storage_list(Session *s, dumb_ptr<map_session_data> sd, ZString message) { - Storage *stor; - struct item_data *item_data = nullptr; int count, counter; CharName character; @@ -3980,50 +4026,56 @@ ATCE atcommand_character_storage_list(Session *s, dumb_ptr<map_session_data> sd, if (pc_isGM(sd).overwhelms(pc_isGM(pl_sd))) { // you can look items only lower or same level - if ((stor = account2storage2(pl_sd->status_key.account_id)) != nullptr) + Option<P<Storage>> stor_ = account2storage2(pl_sd->status_key.account_id); + OMATCH_BEGIN (stor_) { - counter = 0; - count = 0; - for (SOff0 i : SOff0::iter()) + OMATCH_CASE_SOME (stor) { - if (stor->storage_[i].nameid - && (item_data = - itemdb_search(stor->storage_[i].nameid)) != nullptr) + counter = 0; + count = 0; + for (SOff0 i : SOff0::iter()) { - counter = counter + stor->storage_[i].amount; - count++; - if (count == 1) + if (!stor->storage_[i].nameid) + continue; + P<struct item_data> item_data = TRY_UNWRAP(itemdb_exists(stor->storage_[i].nameid), continue); + { - AString output = STRPRINTF( - "------ Storage items list of '%s' ------"_fmt, - pl_sd->status_key.name); + counter = counter + stor->storage_[i].amount; + count++; + if (count == 1) + { + AString output = STRPRINTF( + "------ Storage items list of '%s' ------"_fmt, + pl_sd->status_key.name); + clif_displaymessage(s, output); + } + AString output; + if (true) + output = STRPRINTF("%d %s (%s, id: %d)"_fmt, + stor->storage_[i].amount, + item_data->name, item_data->jname, + stor->storage_[i].nameid); clif_displaymessage(s, output); } - AString output; - if (true) - output = STRPRINTF("%d %s (%s, id: %d)"_fmt, - stor->storage_[i].amount, - item_data->name, item_data->jname, - stor->storage_[i].nameid); + } + if (count == 0) + clif_displaymessage(s, + "No item found in the storage of this player."_s); + else + { + AString output = STRPRINTF( + "%d item(s) found in %d kind(s) of items."_fmt, + counter, count); clif_displaymessage(s, output); } } - if (count == 0) - clif_displaymessage(s, - "No item found in the storage of this player."_s); - else + OMATCH_CASE_NONE () { - AString output = STRPRINTF( - "%d item(s) found in %d kind(s) of items."_fmt, - counter, count); - clif_displaymessage(s, output); + clif_displaymessage(s, "This player has no storage."_s); + return ATCE::OKAY; } } - else - { - clif_displaymessage(s, "This player has no storage."_s); - return ATCE::OKAY; - } + OMATCH_END (); } else { @@ -4041,81 +4093,40 @@ ATCE atcommand_character_storage_list(Session *s, dumb_ptr<map_session_data> sd, } static -ATCE atcommand_killer(Session *s, dumb_ptr<map_session_data> sd, +ATCE atcommand_pvp(Session *s, dumb_ptr<map_session_data> sd, ZString) { - sd->special_state.killer = !sd->special_state.killer; - - if (sd->special_state.killer) - clif_displaymessage(s, "You be a killa..."_s); - else - clif_displaymessage(s, "You gonna be own3d..."_s); - - return ATCE::OKAY; -} - -static -ATCE atcommand_charkiller(Session *s, dumb_ptr<map_session_data>, - ZString message) -{ - CharName character; - - if (!asplit(message, &character)) - return ATCE::USAGE; - - dumb_ptr<map_session_data> pl_sd = map_nick2sd(character); - if (pl_sd == nullptr) - return ATCE::EXIST; - - pl_sd->special_state.killer = !pl_sd->special_state.killer; + int chan = sd->state.pvpchannel; + if (sd->pvp_timer || (chan > 1)) + return ATCE::OKAY; - if (pl_sd->special_state.killer) - { - clif_displaymessage(s, "The player is now a killer"_s); - clif_displaymessage(pl_sd->sess, "You are now a killer"_s); - } - else - { - clif_displaymessage(s, "The player is no longer a killer"_s); - clif_displaymessage(pl_sd->sess, "You are no longer a killer"_s); + if (chan < 1) { + sd->state.pvpchannel = 1; + clif_displaymessage(s, "##3PvP : ##BOn"_s); + } else { + sd->state.pvpchannel = 0; + clif_displaymessage(s, "##3PvP : ##BOff"_s); } + pc_setpvptimer(sd, battle_config.player_pvp_time); return ATCE::OKAY; } static -ATCE atcommand_killable(Session *s, dumb_ptr<map_session_data> sd, - ZString) -{ - sd->special_state.killable = !sd->special_state.killable; - - if (sd->special_state.killable) - clif_displaymessage(s, "You gonna be own3d..."_s); - else - clif_displaymessage(s, "You be a killa..."_s); - - return ATCE::OKAY; -} - -static -ATCE atcommand_charkillable(Session *s, dumb_ptr<map_session_data>, +ATCE atcommand_charpvp(Session *, dumb_ptr<map_session_data>, ZString message) { CharName character; + int channel; - if (!asplit(message, &character)) + if (!extract(message, record<' '>(&character, &channel))) return ATCE::USAGE; dumb_ptr<map_session_data> pl_sd = map_nick2sd(character); if (pl_sd == nullptr) return ATCE::EXIST; - pl_sd->special_state.killable = !pl_sd->special_state.killable; - - if (pl_sd->special_state.killable) - clif_displaymessage(s, "The player is now killable"_s); - else - clif_displaymessage(s, "The player is no longer killable"_s); + pl_sd->state.pvpchannel = channel; return ATCE::OKAY; } @@ -4155,13 +4166,21 @@ ATCE atcommand_addwarp(Session *s, dumb_ptr<map_session_data> sd, if (!extract(message, record<' '>(&mapname, &x, &y))) return ATCE::USAGE; - AString w1 = STRPRINTF("%s,%d,%d"_fmt, sd->mapname_, sd->bl_x, sd->bl_y); AString w3 = STRPRINTF("%s%d%d%d%d"_fmt, mapname, sd->bl_x, sd->bl_y, x, y); - AString w4 = STRPRINTF("1,1,%s.gat,%d,%d"_fmt, mapname, x, y); - NpcName w3name = stringish<NpcName>(w3); - int ret = npc_parse_warp(w1, "warp"_s, w3name, w4); - if (ret) + + ast::npc::Warp warp; + warp.m.data = sd->mapname_; + warp.x.data = sd->bl_x; + warp.y.data = sd->bl_y; + warp.name.data = w3name; + warp.xs.data = 1+2; + warp.ys.data = 1+2; + warp.to_m.data = mapname; + warp.to_x.data = x; + warp.to_y.data = y; + + if (!npc_load_warp(warp)) // warp failed return ATCE::RANGE; @@ -4433,14 +4452,15 @@ ATCE atcommand_adjcmdlvl(Session *s, dumb_ptr<map_session_data>, return ATCE::USAGE; } - AtCommandInfo *it = atcommand_info.search(cmd); + Option<P<AtCommandInfo>> it_ = atcommand_info.search(cmd); { - if (it) + OMATCH_BEGIN_SOME (it, it_) { it->level = newlev; clif_displaymessage(s, "@command level changed."_s); return ATCE::OKAY; } + OMATCH_END (); } clif_displaymessage(s, "@command not found."_s); @@ -4665,15 +4685,15 @@ ATCE atcommand_jump_iterate(Session *s, dumb_ptr<map_session_data> sd, pl_sd = get_start(); } - if (pl_sd->bl_m && pl_sd->bl_m->flag.get(MapFlag::NOWARPTO) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (pl_sd->bl_m->flag.get(MapFlag::NOWARPTO) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you to the map of this player."_s); return ATCE::PERM; } - if (sd->bl_m && sd->bl_m->flag.get(MapFlag::NOWARP) - && !(pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.any_warp_GM_min_level))))) + if (sd->bl_m->flag.get(MapFlag::NOWARP) + && !(pc_isGM(sd).satisfies(battle_config.any_warp_GM_min_level))) { clif_displaymessage(s, "You are not authorised to warp you from your actual map."_s); @@ -4744,11 +4764,11 @@ ATCE atcommand_skillpool_info(Session *s, dumb_ptr<map_session_data>, clif_displaymessage(s, buf); } - buf = STRPRINTF("Learned skills out of %d for %s:"_fmt, - skill_pool_skills_size, character); + buf = STRPRINTF("Learned skills out of %zu for %s:"_fmt, + skill_pool_skills.size(), character); clif_displaymessage(s, buf); - for (i = 0; i < skill_pool_skills_size; ++i) + for (i = 0; i < skill_pool_skills.size(); ++i) { const RString& name = skill_name(skill_pool_skills[i]); int lvl = pl_sd->status.skill[skill_pool_skills[i]].lv; @@ -4919,14 +4939,10 @@ static ATCE atcommand_source(Session *s, dumb_ptr<map_session_data>, ZString) { - clif_displaymessage(s, - "This server code consists of Free Software under GPL3&AGPL3"_s); - clif_displaymessage(s, - "This is commit " VERSION_HASH ", also known as " VERSION_FULL ""_s); - clif_displaymessage(s, - "The version is " VERSION_STRING ""_s); - clif_displaymessage(s, - "For source, see " VENDOR_SOURCE ""_s); + clif_displaymessage(s, VERSION_INFO_HEADER); + clif_displaymessage(s, VERSION_INFO_COMMIT); + clif_displaymessage(s, VERSION_INFO_NUMBER); + clif_displaymessage(s, VERSION_INFO_URL); return ATCE::OKAY; } @@ -4954,6 +4970,9 @@ Map<XString, AtCommandInfo> atcommand_info = {"goto"_s, {"<charname>"_s, 40, atcommand_goto, "Warp yourself to another character"_s}}, + {"npc"_s, {"<npc>"_s, + 40, atcommand_npc, + "Warp yourself to a npc"_s}}, {"jump"_s, {"[x] [y]"_s, 40, atcommand_jump, "Warp yourself within a map"_s}}, @@ -5023,12 +5042,12 @@ Map<XString, AtCommandInfo> atcommand_info = {"jlvl"_s, {"<delta>"_s, 60, atcommand_joblevelup, "Adjust your job level"_s}}, - {"gm"_s, {"<password>"_s, - 100, atcommand_gm, - "Receive GM powers"_s}}, {"pvpoff"_s, {""_s, 60, atcommand_pvpoff, "Enable PvP on your map"_s}}, + {"exprate"_s, {"<percent>"_s, + 60, atcommand_exprate, + "Set base job/exp rate"_s}}, {"pvpon"_s, {""_s, 60, atcommand_pvpon, "Disable PvP on your map"_s}}, @@ -5242,21 +5261,15 @@ Map<XString, AtCommandInfo> atcommand_info = {"addwarp"_s, {"<mapname> <x> <y>"_s, 80, atcommand_addwarp, "Create a new permanent warp"_s}}, - {"killer"_s, {""_s, - 60, atcommand_killer, - "Toggle whether you are a killer"_s}}, - {"charkiller"_s, {"<charname>"_s, - 60, atcommand_charkiller, - "Toggle whether a player is a killer"_s}}, + {"pvp"_s, {""_s, + 0, atcommand_pvp, + "Toggle your pvp flag"_s}}, {"npcmove"_s, {"<x> <y> <npc-name>"_s, 80, atcommand_npcmove, "Force an NPC to move on the map"_s}}, - {"killable"_s, {""_s, - 60, atcommand_killable, - "Toggle whether you are killable"_s}}, - {"charkillable"_s, {"<charname>"_s, - 60, atcommand_charkillable, - "Toggle whether a player is killable"_s}}, + {"charpvp"_s, {"<charname> <channel>"_s, + 40, atcommand_charpvp, + "Set the pvp channel of another player"_s}}, {"chareffect"_s, {"<type> <target>"_s, 40, atcommand_chareffect, "Apply effect type with arg 0 to a player"_s}}, @@ -5357,4 +5370,5 @@ Map<XString, AtCommandInfo> atcommand_info = 0, atcommand_source, "Legal information about source code (must be a level 0 command!)"_s}}, }; +} // namespace map } // namespace tmwa diff --git a/src/map/atcommand.hpp b/src/map/atcommand.hpp index 4bf5277..4c0e421 100644 --- a/src/map/atcommand.hpp +++ b/src/map/atcommand.hpp @@ -22,17 +22,11 @@ #include "fwd.hpp" -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" - -#include "../net/fwd.hpp" - -#include "../mmo/fwd.hpp" - namespace tmwa { +namespace map +{ bool is_atcommand(Session *s, dumb_ptr<map_session_data> sd, ZString message, GmLevel gmlvl); @@ -40,8 +34,6 @@ bool atcommand_config_read(ZString cfgName); void log_atcommand(dumb_ptr<map_session_data> sd, ZString cmd); -// only used by map.cpp -extern AString gm_log; - void atcommand_config_write(ZString cfgName); +} // namespace map } // namespace tmwa diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 856408c..5b63acc 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -32,12 +32,17 @@ #include "../generic/random.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" #include "../io/read.hpp" +#include "../io/span.hpp" #include "../mmo/config_parse.hpp" +#include "../mmo/cxxstdio_enums.hpp" +#include "../high/utils.hpp" + +#include "battle_conf.hpp" #include "clif.hpp" +#include "globals.hpp" #include "itemdb.hpp" #include "map.hpp" #include "mob.hpp" @@ -50,13 +55,8 @@ namespace tmwa { -static Battle_Config init_battle_config(); - -DIAG_PUSH(); -DIAG_I(shadow); -struct Battle_Config battle_config = init_battle_config(); -DIAG_POP(); - +namespace map +{ /*========================================== * 自分をロックしている対象の数を返す(汎用) * 戻りは整数で0以上 @@ -482,21 +482,6 @@ int battle_get_atk(dumb_ptr<block_list> bl) } /*========================================== - * 対象の左手Atkを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -static -int battle_get_atk_(dumb_ptr<block_list> bl) -{ - nullpo_retz(bl); - if (bl->bl_type == BL::PC) - return bl->is_player()->watk_; - else - return 0; -} - -/*========================================== * 対象のAtk2を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ @@ -520,21 +505,6 @@ int battle_get_atk2(dumb_ptr<block_list> bl) } /*========================================== - * 対象の左手Atk2を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -static -int battle_get_atk_2(dumb_ptr<block_list> bl) -{ - nullpo_retz(bl); - if (bl->bl_type == BL::PC) - return bl->is_player()->watk_2; - else - return 0; -} - -/*========================================== * 対象のMAtk1を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ @@ -756,7 +726,7 @@ interval_t battle_get_adelay(dumb_ptr<block_list> bl) if (aspd_rate != 100) adelay = adelay * aspd_rate / 100; - return std::max(adelay, static_cast<interval_t>(battle_config.monster_max_aspd) * 2); + return std::max(adelay, battle_config.monster_max_aspd * 2); } } @@ -771,7 +741,7 @@ interval_t battle_get_amotion(dumb_ptr<block_list> bl) interval_t amotion = 2_s; int aspd_rate = 100; if (bl->bl_type == BL::MOB) - amotion = static_cast<interval_t>(get_mob_db(bl->is_mob()->mob_class).amotion); + amotion = get_mob_db(bl->is_mob()->mob_class).amotion; if (sc_data) { @@ -783,7 +753,7 @@ interval_t battle_get_amotion(dumb_ptr<block_list> bl) if (aspd_rate != 100) amotion = amotion * aspd_rate / 100; - return std::max(amotion, static_cast<interval_t>(battle_config.monster_max_aspd)); + return std::max(amotion, battle_config.monster_max_aspd); } } @@ -792,7 +762,7 @@ interval_t battle_get_dmotion(dumb_ptr<block_list> bl) nullpo_retr(interval_t::zero(), bl); if (bl->bl_type == BL::MOB) { - return static_cast<interval_t>(get_mob_db(bl->is_mob()->mob_class).dmotion); + return get_mob_db(bl->is_mob()->mob_class).dmotion; } else if (bl->bl_type == BL::PC) { @@ -884,16 +854,6 @@ eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> battle_ return nullptr; } -short *battle_get_sc_count(dumb_ptr<block_list> bl) -{ - nullpo_retr(nullptr, bl); - if (bl->bl_type == BL::MOB) - return &bl->is_mob()->sc_count; - else if (bl->bl_type == BL::PC) - return &bl->is_player()->sc_count; - return nullptr; -} - Opt1 *battle_get_opt1(dumb_ptr<block_list> bl) { nullpo_retn(bl); @@ -930,7 +890,7 @@ Opt3 *battle_get_opt3(dumb_ptr<block_list> bl) return nullptr; } -Option *battle_get_option(dumb_ptr<block_list> bl) +Opt0 *battle_get_option(dumb_ptr<block_list> bl) { nullpo_retn(bl); if (bl->bl_type == BL::MOB) @@ -1023,17 +983,6 @@ int battle_stopattack(dumb_ptr<block_list> bl) return 0; } -// 移動停止 -int battle_stopwalking(dumb_ptr<block_list> bl, int type) -{ - nullpo_retz(bl); - if (bl->bl_type == BL::MOB) - return mob_stop_walking(bl->is_mob(), type); - else if (bl->bl_type == BL::PC) - return pc_stop_walking(bl->is_player(), type); - return 0; -} - /*========================================== * ダメージ最終計算 *------------------------------------------ @@ -1082,7 +1031,7 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src, int def2 = battle_get_def2(target); int t_vit = battle_get_vit(target); struct Damage wd {}; - int damage, damage2 = 0; + int damage; DamageType type; int div_; BF flag; @@ -1112,7 +1061,7 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src, || battle_config.vit_penaly_type > 0) target_count += battle_counttargeted(target, src, - ATK(battle_config.agi_penaly_count_lv)); // FIXME + battle_config.agi_penaly_count_lv); if (battle_config.agi_penaly_type > 0) { if (target_count >= battle_config.agi_penaly_count) @@ -1198,7 +1147,7 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src, int t_def; target_count = 1 + battle_counttargeted(target, src, - ATK(battle_config.vit_penaly_count_lv)); // FIXME + battle_config.vit_penaly_count_lv); if (battle_config.vit_penaly_type > 0) { if (target_count >= battle_config.vit_penaly_count) @@ -1273,7 +1222,7 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src, if (type == DamageType::NORMAL && !random_::chance({hitrate, 100})) { - damage = damage2 = 0; + damage = 0; dmg_lv = ATK::FLEE; } else @@ -1312,7 +1261,6 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src, skill_num, skill_lv, flag); wd.damage = damage; - wd.damage2 = 0; wd.type = type; wd.div_ = div_; wd.amotion = battle_get_amotion(src); @@ -1357,14 +1305,13 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, int def2 = battle_get_def2(target); int t_vit = battle_get_vit(target); struct Damage wd {}; - int damage, damage2; + int damage; DamageType type; int div_; BF flag; ATK dmg_lv = ATK::ZERO; eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data, t_sc_data; - int atkmax_ = 0, atkmin_ = 0; //二刀流用 - int watk, watk_; + int watk; bool da = false; int ac_flag = 0; int target_distance; @@ -1392,7 +1339,7 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, flee = battle_get_flee(target); if (battle_config.agi_penaly_type > 0 || battle_config.vit_penaly_type > 0) //AGI、VITペナルティ設定が有効 target_count += battle_counttargeted(target, src, - ATK(battle_config.agi_penaly_count_lv)); //対象の数を算出 + battle_config.agi_penaly_count_lv); //対象の数を算出 if (battle_config.agi_penaly_type > 0) { if (target_count >= battle_config.agi_penaly_count) @@ -1428,13 +1375,12 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, dex = battle_get_dex(src); //DEX watk = battle_get_atk(src); //ATK - watk_ = battle_get_atk_(src); //ATK左手 type = DamageType::NORMAL; div_ = 1; // single attack { - damage = damage2 = battle_get_baseatk(sd); //damega,damega2初登場、base_atkの取得 + damage = battle_get_baseatk(sd); //damega,damega2初登場、base_atkの取得 } if (sd->attackrange > 2) { // [fate] ranged weapon? @@ -1443,22 +1389,21 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, damage * (256 + ((range_damage_bonus * target_distance) / sd->attackrange)) >> 8; - damage2 = - damage2 * (256 + - ((range_damage_bonus * target_distance) / - sd->attackrange)) >> 8; } - atkmin = atkmin_ = dex; //最低ATKはDEXで初期化? + atkmin = dex; //最低ATKはDEXで初期化? sd->state.arrow_atk = 0; //arrow_atk初期化 IOff0 widx = sd->equip_index_maybe[EQUIP::WEAPON]; - IOff0 sidx = sd->equip_index_maybe[EQUIP::SHIELD]; - if (widx.ok() && sd->inventory_data[widx]) - atkmin = atkmin * (80 + sd->inventory_data[widx]->wlv * 20) / 100; - if (sidx.ok() && sd->inventory_data[sidx]) - atkmin_ = atkmin_ * (80 + sd->inventory_data[sidx]->wlv * 20) / 100; + if (widx.ok()) + { + OMATCH_BEGIN_SOME (sdidw, sd->inventory_data[widx]) + { + atkmin = atkmin * (80 + sdidw->wlv * 20) / 100; + } + OMATCH_END (); + } if (sd->status.weapon == ItemLook::BOW) { //武器が弓矢の場合 atkmin = watk * ((atkmin < watk) ? atkmin : watk) / 100; //弓用最低ATK計算 @@ -1468,13 +1413,10 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, { atkmax = watk; - atkmax_ = watk_; } if (atkmin > atkmax && !(sd->state.arrow_atk)) atkmin = atkmax; //弓は最低が上回る場合あり - if (atkmin_ > atkmax_) - atkmin_ = atkmax_; if (sd->double_rate > 0 && skill_num == SkillID::ZERO && skill_lv >= 0) da = random_::chance({sd->double_rate, 100}); @@ -1486,9 +1428,6 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, if (sd->state.arrow_atk) cri += sd->arrow_cri; - if (sd->status.weapon == ItemLook::_16) - // カタールの場合、クリティカルを倍に - cri <<= 1; cri -= battle_get_luk(target) * 3; if (ac_flag) cri = 1000; @@ -1503,11 +1442,9 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, && random_::chance({cri, 1000})) { damage += atkmax; - damage2 += atkmax_; if (sd->atk_rate != 100) { damage = (damage * sd->atk_rate) / 100; - damage2 = (damage2 * sd->atk_rate) / 100; } if (sd->state.arrow_atk) damage += sd->arrow_atk; @@ -1521,14 +1458,9 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, damage += random_::in(atkmin, atkmax); else damage += atkmin; - if (atkmax_ > atkmin_) - damage2 += random_::in(atkmin_, atkmax_); - else - damage2 += atkmin_; if (sd->atk_rate != 100) { damage = (damage * sd->atk_rate) / 100; - damage2 = (damage2 * sd->atk_rate) / 100; } if (sd->state.arrow_atk) @@ -1551,7 +1483,7 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, int t_def; target_count = 1 + battle_counttargeted(target, src, - ATK(battle_config.vit_penaly_count_lv)); // FIXME + battle_config.vit_penaly_count_lv); if (battle_config.vit_penaly_type > 0) { if (target_count >= battle_config.vit_penaly_count) @@ -1614,28 +1546,17 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, damage -= random_::in(0, vitbonusmax); } } - { - { - damage2 = damage2 * (100 - def1) / 100; - damage2 -= t_def; - if (vitbonusmax > 0) - damage2 -= random_::in(0, vitbonusmax); - } - } } } } // 精錬ダメージの追加 { //DEF, VIT無視 damage += battle_get_atk2(src); - damage2 += battle_get_atk_2(src); } // 0未満だった場合1に補正 if (damage < 1) damage = 1; - if (damage2 < 1) - damage2 = 1; // スキル修正2(修練系) // 修練ダメージ(右手のみ) ソニックブロー時は別処理(1撃に付き1/8適応) @@ -1652,7 +1573,7 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, hitrate = (hitrate < 5) ? 5 : hitrate; if (type == DamageType::NORMAL && !random_::chance({hitrate, 100})) { - damage = damage2 = 0; + damage = 0; dmg_lv = ATK::FLEE; } else @@ -1662,37 +1583,6 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, if (damage < 0) damage = 0; - if (damage2 < 0) - damage2 = 0; - - // >二刀流の左右ダメージ計算誰かやってくれぇぇぇぇえええ! - // >map_session_data に左手ダメージ(atk,atk2)追加して - // >pc_calcstatus()でやるべきかな? - // map_session_data に左手武器(atk,atk2,ele,star,atkmods)追加して - // pc_calcstatus()でデータを入力しています - - //左手のみ武器装備 - if (sd->weapontype1 == ItemLook::NONE - && sd->weapontype2 != ItemLook::NONE) - { - damage = damage2; - damage2 = 0; - } - // 右手、左手修練の適用 - if (sd->status.weapon >= ItemLook::SINGLE_HANDED_COUNT) - { // 二刀流か? - int dmg = damage, dmg2 = damage2; - // 右手修練(60% 〜 100%) 右手全般 - damage = damage * 50 / 100; - if (dmg > 0 && damage < 1) - damage = 1; - // 左手修練(40% 〜 80%) 左手全般 - damage2 = damage2 * 30 / 100; - if (dmg2 > 0 && damage2 < 1) - damage2 = 1; - } - else //二刀流でなければ左手ダメージは0 - damage2 = 0; // 右手,短剣のみ if (da) @@ -1702,19 +1592,11 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, type = DamageType::DOUBLED; } - if (sd->status.weapon == ItemLook::_16) - { - // カタール追撃ダメージ - damage2 = damage * 1 / 100; - if (damage > 0 && damage2 < 1) - damage2 = 1; - } - // 完全回避の判定 if (skill_num == SkillID::ZERO && skill_lv >= 0 && tsd != nullptr && div_ < 255 && random_::chance({battle_get_flee2(target), 1000})) { - damage = damage2 = 0; + damage = 0; type = DamageType::FLEE2; dmg_lv = ATK::LUCKY; } @@ -1725,7 +1607,7 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, if (skill_num == SkillID::ZERO && skill_lv >= 0 && tmd != nullptr && div_ < 255 && random_::chance({battle_get_flee2(target), 1000})) { - damage = damage2 = 0; + damage = 0; type = DamageType::FLEE2; dmg_lv = ATK::LUCKY; } @@ -1736,35 +1618,18 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src, { if (damage > 0) damage = 1; - if (damage2 > 0) - damage2 = 1; } - if (damage > 0 || damage2 > 0) + if (damage > 0) { - if (damage2 < 1) // ダメージ最終修正 + { damage = battle_calc_damage(src, target, damage, div_, skill_num, skill_lv, flag); - else if (damage < 1) // 右手がミス? - damage2 = - battle_calc_damage(src, target, damage2, div_, skill_num, - skill_lv, flag); - else - { // 両 手/カタールの場合はちょっと計算ややこしい - int d1 = damage + damage2, d2 = damage2; - damage = - battle_calc_damage(src, target, damage + damage2, div_, - skill_num, skill_lv, flag); - damage2 = (d2 * 100 / d1) * damage / 100; - if (damage > 1 && damage2 < 1) - damage2 = 1; - damage -= damage2; } } wd.damage = damage; - wd.damage2 = damage2; wd.type = type; wd.div_ = div_; wd.amotion = battle_get_amotion(src); @@ -1872,7 +1737,6 @@ struct Damage battle_calc_magic_attack(dumb_ptr<block_list> bl, md.div_ = div_; md.amotion = battle_get_amotion(bl); md.dmotion = battle_get_dmotion(target); - md.damage2 = 0; md.type = DamageType::NORMAL; md.flag = aflag; @@ -1937,7 +1801,6 @@ struct Damage battle_calc_misc_attack(dumb_ptr<block_list> bl, md.div_ = div_; md.amotion = battle_get_amotion(bl); md.dmotion = battle_get_dmotion(target); - md.damage2 = 0; md.type = DamageType::NORMAL; md.flag = aflag; return md; @@ -2048,13 +1911,7 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target, { clif_damage(src, target, tick, wd.amotion, wd.dmotion, - wd.damage, wd.div_, wd.type, wd.damage2); - if (sd - && (sd->status.weapon == ItemLook::_16 - || sd->status.weapon >= ItemLook::SINGLE_HANDED_COUNT) - && wd.damage2 == 0) - clif_damage(src, target, tick + 10_ms, - wd.amotion, wd.dmotion, 0, 1, DamageType::NORMAL, 0); + wd.damage, wd.div_, wd.type); } MapBlockLock lock; @@ -2063,9 +1920,17 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target, { IOff0 weapon_index = sd->equip_index_maybe[EQUIP::WEAPON]; ItemNameId weapon; - if (weapon_index.ok() && sd->inventory_data[weapon_index] - && bool(sd->status.inventory[weapon_index].equip & EPOS::WEAPON)) - weapon = sd->inventory_data[weapon_index]->nameid; + if (weapon_index.ok()) + { + OMATCH_BEGIN_SOME (sdidw, sd->inventory_data[weapon_index]) + { + if (bool(sd->status.inventory[weapon_index].equip & EPOS::WEAPON)) + { + weapon = sdidw->nameid; + } + } + OMATCH_END (); + } MAP_LOG("PC%d %s:%d,%d WPNDMG %s%d %d FOR %d WPN %d"_fmt, sd->status_key.char_id, src->bl_m->name_, src->bl_x, src->bl_y, @@ -2074,7 +1939,7 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target, ? unwrap<CharId>(target->is_player()->status_key.char_id) : unwrap<BlockId>(target->bl_id), battle_get_class(target), - wd.damage + wd.damage2, weapon); + wd.damage, weapon); } if (target->bl_type == BL::PC) @@ -2087,16 +1952,16 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target, ? unwrap<CharId>(src->is_player()->status_key.char_id) : unwrap<BlockId>(src->bl_id), battle_get_class(src), - wd.damage + wd.damage2); + wd.damage); } - battle_damage(src, target, (wd.damage + wd.damage2), 0); + battle_damage(src, target, (wd.damage), 0); if (target->bl_prev != nullptr && (target->bl_type != BL::PC || (target->bl_type == BL::PC && !pc_isdead(target->is_player())))) { - if (wd.damage > 0 || wd.damage2 > 0) + if (wd.damage > 0) { skill_additional_effect(src, target, SkillID::ZERO, 0); } @@ -2105,7 +1970,7 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target, { if (bool(wd.flag & BF::WEAPON) && src != target - && (wd.damage > 0 || wd.damage2 > 0)) + && (wd.damage > 0)) { int hp = 0, sp = 0; if (sd->hp_drain_rate && wd.damage > 0 @@ -2113,21 +1978,11 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target, { hp += (wd.damage * sd->hp_drain_per) / 100; } - if (sd->hp_drain_rate_ && wd.damage2 > 0 - && random_::chance({sd->hp_drain_rate_, 100})) - { - hp += (wd.damage2 * sd->hp_drain_per_) / 100; - } if (sd->sp_drain_rate && wd.damage > 0 && random_::chance({sd->sp_drain_rate, 100})) { sp += (wd.damage * sd->sp_drain_per) / 100; } - if (sd->sp_drain_rate_ && wd.damage2 > 0 - && random_::chance({sd->sp_drain_rate_, 100})) - { - sp += (wd.damage2 * sd->sp_drain_per_) / 100; - } if (hp || sp) pc_heal(sd, hp, sp); } @@ -2310,380 +2165,5 @@ int battle_check_range(dumb_ptr<block_list> src, dumb_ptr<block_list> bl, return (path_search(&wpd, src->bl_m, src->bl_x + dx, src->bl_y + dy, bl->bl_x - dx, bl->bl_y - dy, 0x10001) != -1) ? 1 : 0; } - -Battle_Config init_battle_config() -{ - DIAG_PUSH(); - DIAG_I(shadow); - Battle_Config battle_config; - DIAG_POP(); - { - battle_config.warp_point_debug = 0; - battle_config.enemy_critical = 0; - battle_config.enemy_critical_rate = 100; - battle_config.enemy_str = 1; - battle_config.enemy_perfect_flee = 0; - battle_config.casting_rate = 100; - battle_config.delay_rate = 100; - battle_config.delay_dependon_dex = 0; - battle_config.skill_delay_attack_enable = 0; - battle_config.monster_skill_add_range = 0; - battle_config.player_damage_delay = 1; - battle_config.flooritem_lifetime = std::chrono::duration_cast<std::chrono::milliseconds>(LIFETIME_FLOORITEM).count(); - battle_config.item_auto_get = 0; - battle_config.drop_pickup_safety_zone = 20; - battle_config.item_first_get_time = 3000; - battle_config.item_second_get_time = 1000; - battle_config.item_third_get_time = 1000; - - battle_config.base_exp_rate = 100; - battle_config.job_exp_rate = 100; - battle_config.death_penalty_type = 0; - battle_config.death_penalty_base = 0; - battle_config.death_penalty_job = 0; - battle_config.restart_hp_rate = 0; - battle_config.restart_sp_rate = 0; - battle_config.monster_hp_rate = 100; - battle_config.monster_max_aspd = 199; - battle_config.atcommand_gm_only = 0; - battle_config.gm_all_equipment = 0; - battle_config.monster_active_enable = 1; - battle_config.mob_skill_use = 1; - battle_config.mob_count_rate = 100; - battle_config.basic_skill_check = 1; - battle_config.player_invincible_time = 5000; - battle_config.skill_min_damage = 0; - battle_config.natural_healhp_interval = 6000; - battle_config.natural_healsp_interval = 8000; - battle_config.natural_heal_skill_interval = 10000; - battle_config.natural_heal_weight_rate = 50; - battle_config.itemheal_regeneration_factor = 1; - battle_config.arrow_decrement = 1; - battle_config.max_aspd = 199; - battle_config.max_hp = 32500; - battle_config.max_sp = 32500; - battle_config.max_lv = 99; // [MouseJstr] - battle_config.max_parameter = 99; - battle_config.monster_skill_log = 0; - battle_config.battle_log = 0; - battle_config.save_log = 0; - battle_config.error_log = 1; - battle_config.etc_log = 1; - battle_config.save_clothcolor = 0; - battle_config.undead_detect_type = 0; - battle_config.agi_penaly_type = 0; - battle_config.agi_penaly_count = 3; - battle_config.agi_penaly_num = 0; - battle_config.agi_penaly_count_lv = static_cast<int>(ATK::FLEE); // FIXME - battle_config.vit_penaly_type = 0; - battle_config.vit_penaly_count = 3; - battle_config.vit_penaly_num = 0; - battle_config.vit_penaly_count_lv = static_cast<int>(ATK::DEF); // FIXME - battle_config.mob_changetarget_byskill = 0; - battle_config.player_attack_direction_change = 1; - battle_config.monster_attack_direction_change = 1; - battle_config.display_delay_skill_fail = 1; - battle_config.dead_branch_active = 0; - battle_config.show_steal_in_same_party = 0; - battle_config.hide_GM_session = 0; - battle_config.invite_request_check = 1; - battle_config.disp_experience = 0; - battle_config.prevent_logout = 1; // Added by RoVeRT - battle_config.maximum_level = 255; // Added by Valaris - battle_config.drops_by_luk = 0; // [Valaris] - battle_config.pk_mode = 0; // [Valaris] - battle_config.multi_level_up = 0; // [Valaris] - battle_config.hack_info_GM_level = 60; // added by [Yor] (default: 60, GM level) - battle_config.any_warp_GM_min_level = 20; // added by [Yor] - battle_config.min_hair_style = 0; - battle_config.max_hair_style = 20; - battle_config.min_hair_color = 0; - battle_config.max_hair_color = 9; - battle_config.min_cloth_color = 0; - battle_config.max_cloth_color = 4; - - battle_config.castrate_dex_scale = 150; - - battle_config.area_size = 14; - - battle_config.chat_lame_penalty = 2; - battle_config.chat_spam_threshold = 10; - battle_config.chat_spam_flood = 10; - battle_config.chat_spam_ban = 1; - battle_config.chat_spam_warn = 8; - battle_config.chat_maxline = 255; - - battle_config.packet_spam_threshold = 2; - battle_config.packet_spam_flood = 30; - battle_config.packet_spam_kick = 1; - - battle_config.mask_ip_gms = 1; - - battle_config.mob_splash_radius = -1; - } - return battle_config; -} - -bool battle_config_read(ZString cfgName) -{ - bool rv = true; - io::ReadFile in(cfgName); - if (!in.is_open()) - { - PRINTF("file not found: %s\n"_fmt, cfgName); - return false; - } - - AString line; - while (in.getline(line)) - { -#define BATTLE_CONFIG_VAR(name) {#name##_s, &battle_config.name} - const struct - { - LString str; - int *val; - } data[] = - { - BATTLE_CONFIG_VAR(warp_point_debug), - BATTLE_CONFIG_VAR(enemy_critical), - BATTLE_CONFIG_VAR(enemy_critical_rate), - BATTLE_CONFIG_VAR(enemy_str), - BATTLE_CONFIG_VAR(enemy_perfect_flee), - BATTLE_CONFIG_VAR(casting_rate), - BATTLE_CONFIG_VAR(delay_rate), - BATTLE_CONFIG_VAR(delay_dependon_dex), - BATTLE_CONFIG_VAR(skill_delay_attack_enable), - BATTLE_CONFIG_VAR(monster_skill_add_range), - BATTLE_CONFIG_VAR(player_damage_delay), - BATTLE_CONFIG_VAR(flooritem_lifetime), - BATTLE_CONFIG_VAR(item_auto_get), - BATTLE_CONFIG_VAR(drop_pickup_safety_zone), - BATTLE_CONFIG_VAR(item_first_get_time), - BATTLE_CONFIG_VAR(item_second_get_time), - BATTLE_CONFIG_VAR(item_third_get_time), - BATTLE_CONFIG_VAR(base_exp_rate), - BATTLE_CONFIG_VAR(job_exp_rate), - BATTLE_CONFIG_VAR(death_penalty_type), - BATTLE_CONFIG_VAR(death_penalty_base), - BATTLE_CONFIG_VAR(death_penalty_job), - BATTLE_CONFIG_VAR(restart_hp_rate), - BATTLE_CONFIG_VAR(restart_sp_rate), - BATTLE_CONFIG_VAR(monster_hp_rate), - BATTLE_CONFIG_VAR(monster_max_aspd), - BATTLE_CONFIG_VAR(atcommand_gm_only), - BATTLE_CONFIG_VAR(atcommand_spawn_quantity_limit), - BATTLE_CONFIG_VAR(gm_all_equipment), - BATTLE_CONFIG_VAR(monster_active_enable), - BATTLE_CONFIG_VAR(mob_skill_use), - BATTLE_CONFIG_VAR(mob_count_rate), - BATTLE_CONFIG_VAR(basic_skill_check), - BATTLE_CONFIG_VAR(player_invincible_time), - BATTLE_CONFIG_VAR(skill_min_damage), - BATTLE_CONFIG_VAR(natural_healhp_interval), - BATTLE_CONFIG_VAR(natural_healsp_interval), - BATTLE_CONFIG_VAR(natural_heal_skill_interval), - BATTLE_CONFIG_VAR(natural_heal_weight_rate), - BATTLE_CONFIG_VAR(itemheal_regeneration_factor), - BATTLE_CONFIG_VAR(arrow_decrement), - BATTLE_CONFIG_VAR(max_aspd), - BATTLE_CONFIG_VAR(max_hp), - BATTLE_CONFIG_VAR(max_sp), - BATTLE_CONFIG_VAR(max_lv), - BATTLE_CONFIG_VAR(max_parameter), - BATTLE_CONFIG_VAR(monster_skill_log), - BATTLE_CONFIG_VAR(battle_log), - BATTLE_CONFIG_VAR(save_log), - BATTLE_CONFIG_VAR(error_log), - BATTLE_CONFIG_VAR(etc_log), - BATTLE_CONFIG_VAR(save_clothcolor), - BATTLE_CONFIG_VAR(undead_detect_type), - BATTLE_CONFIG_VAR(agi_penaly_type), - BATTLE_CONFIG_VAR(agi_penaly_count), - BATTLE_CONFIG_VAR(agi_penaly_num), - BATTLE_CONFIG_VAR(agi_penaly_count_lv), - BATTLE_CONFIG_VAR(vit_penaly_type), - BATTLE_CONFIG_VAR(vit_penaly_count), - BATTLE_CONFIG_VAR(vit_penaly_num), - BATTLE_CONFIG_VAR(vit_penaly_count_lv), - BATTLE_CONFIG_VAR(mob_changetarget_byskill), - BATTLE_CONFIG_VAR(player_attack_direction_change), - BATTLE_CONFIG_VAR(monster_attack_direction_change), - BATTLE_CONFIG_VAR(display_delay_skill_fail), - BATTLE_CONFIG_VAR(dead_branch_active), - BATTLE_CONFIG_VAR(show_steal_in_same_party), - BATTLE_CONFIG_VAR(hide_GM_session), - BATTLE_CONFIG_VAR(invite_request_check), - BATTLE_CONFIG_VAR(disp_experience), - BATTLE_CONFIG_VAR(prevent_logout), // Added by RoVeRT - BATTLE_CONFIG_VAR(alchemist_summon_reward), // [Valaris] - BATTLE_CONFIG_VAR(maximum_level), // [Valaris] - BATTLE_CONFIG_VAR(drops_by_luk), // [Valaris] - BATTLE_CONFIG_VAR(monsters_ignore_gm), // [Valaris] - BATTLE_CONFIG_VAR(pk_mode), // [Valaris] - BATTLE_CONFIG_VAR(multi_level_up), // [Valaris] - BATTLE_CONFIG_VAR(hack_info_GM_level), // added by [Yor] - BATTLE_CONFIG_VAR(any_warp_GM_min_level), // added by [Yor] - BATTLE_CONFIG_VAR(min_hair_style), // added by [MouseJstr] - BATTLE_CONFIG_VAR(max_hair_style), // added by [MouseJstr] - BATTLE_CONFIG_VAR(min_hair_color), // added by [MouseJstr] - BATTLE_CONFIG_VAR(max_hair_color), // added by [MouseJstr] - BATTLE_CONFIG_VAR(min_cloth_color), // added by [MouseJstr] - BATTLE_CONFIG_VAR(max_cloth_color), // added by [MouseJstr] - BATTLE_CONFIG_VAR(castrate_dex_scale), // added by [MouseJstr] - BATTLE_CONFIG_VAR(area_size), // added by [MouseJstr] - BATTLE_CONFIG_VAR(chat_lame_penalty), - BATTLE_CONFIG_VAR(chat_spam_threshold), - BATTLE_CONFIG_VAR(chat_spam_flood), - BATTLE_CONFIG_VAR(chat_spam_ban), - BATTLE_CONFIG_VAR(chat_spam_warn), - BATTLE_CONFIG_VAR(chat_maxline), - BATTLE_CONFIG_VAR(packet_spam_threshold), - BATTLE_CONFIG_VAR(packet_spam_flood), - BATTLE_CONFIG_VAR(packet_spam_kick), - BATTLE_CONFIG_VAR(mask_ip_gms), - BATTLE_CONFIG_VAR(mob_splash_radius), - }; - - if (is_comment(line)) - continue; - XString w1; - ZString w2; - if (!config_split(line, &w1, &w2)) - { - PRINTF("Bad config line: %s\n"_fmt, line); - rv = false; - continue; - } - - if (w1 == "import"_s) - { - battle_config_read(w2); - continue; - } - - for (auto datum : data) - if (w1 == datum.str) - { - *datum.val = config_switch(w2); - goto continue_outer; - } - - PRINTF("WARNING: unknown battle conf key: %s\n"_fmt, AString(w1)); - rv = false; - - continue_outer: - ; - } - - return rv; -} - -void battle_config_check() -{ - { - if (static_cast<interval_t>(battle_config.flooritem_lifetime) < 1_s) - battle_config.flooritem_lifetime = std::chrono::duration_cast<std::chrono::milliseconds>(LIFETIME_FLOORITEM).count(); - if (battle_config.restart_hp_rate < 0) - battle_config.restart_hp_rate = 0; - else if (battle_config.restart_hp_rate > 100) - battle_config.restart_hp_rate = 100; - if (battle_config.restart_sp_rate < 0) - battle_config.restart_sp_rate = 0; - else if (battle_config.restart_sp_rate > 100) - battle_config.restart_sp_rate = 100; - if (battle_config.natural_healhp_interval < NATURAL_HEAL_INTERVAL.count()) - battle_config.natural_healhp_interval = NATURAL_HEAL_INTERVAL.count(); - if (battle_config.natural_healsp_interval < NATURAL_HEAL_INTERVAL.count()) - battle_config.natural_healsp_interval = NATURAL_HEAL_INTERVAL.count(); - if (battle_config.natural_heal_skill_interval < NATURAL_HEAL_INTERVAL.count()) - battle_config.natural_heal_skill_interval = NATURAL_HEAL_INTERVAL.count(); - if (battle_config.natural_heal_weight_rate < 50) - battle_config.natural_heal_weight_rate = 50; - if (battle_config.natural_heal_weight_rate > 101) - battle_config.natural_heal_weight_rate = 101; - battle_config.monster_max_aspd = - 2000 - battle_config.monster_max_aspd * 10; - if (battle_config.monster_max_aspd < 10) - battle_config.monster_max_aspd = 10; - if (battle_config.monster_max_aspd > 1000) - battle_config.monster_max_aspd = 1000; - battle_config.max_aspd = 2000 - battle_config.max_aspd * 10; - if (battle_config.max_aspd < 10) - battle_config.max_aspd = 10; - if (battle_config.max_aspd > 1000) - battle_config.max_aspd = 1000; - if (battle_config.max_hp > 1000000) - battle_config.max_hp = 1000000; - if (battle_config.max_hp < 100) - battle_config.max_hp = 100; - if (battle_config.max_sp > 1000000) - battle_config.max_sp = 1000000; - if (battle_config.max_sp < 100) - battle_config.max_sp = 100; - if (battle_config.max_parameter < 10) - battle_config.max_parameter = 10; - if (battle_config.max_parameter > 10000) - battle_config.max_parameter = 10000; - - if (battle_config.agi_penaly_count < 2) - battle_config.agi_penaly_count = 2; - if (battle_config.vit_penaly_count < 2) - battle_config.vit_penaly_count = 2; - - if (battle_config.hack_info_GM_level < 0) // added by [Yor] - battle_config.hack_info_GM_level = 0; - else if (battle_config.hack_info_GM_level > 100) - battle_config.hack_info_GM_level = 100; - - if (battle_config.any_warp_GM_min_level < 0) // added by [Yor] - battle_config.any_warp_GM_min_level = 0; - else if (battle_config.any_warp_GM_min_level > 100) - battle_config.any_warp_GM_min_level = 100; - - if (battle_config.chat_spam_ban < 0) - battle_config.chat_spam_ban = 0; - else if (battle_config.chat_spam_ban > 32767) - battle_config.chat_spam_ban = 32767; - - if (battle_config.chat_spam_flood < 0) - battle_config.chat_spam_flood = 0; - else if (battle_config.chat_spam_flood > 32767) - battle_config.chat_spam_flood = 32767; - - if (battle_config.chat_spam_warn < 0) - battle_config.chat_spam_warn = 0; - else if (battle_config.chat_spam_warn > 32767) - battle_config.chat_spam_warn = 32767; - - if (battle_config.chat_spam_threshold < 0) - battle_config.chat_spam_threshold = 0; - else if (battle_config.chat_spam_threshold > 32767) - battle_config.chat_spam_threshold = 32767; - - if (battle_config.chat_maxline < 1) - battle_config.chat_maxline = 1; - else if (battle_config.chat_maxline > 512) - battle_config.chat_maxline = 512; - - if (battle_config.packet_spam_threshold < 0) - battle_config.packet_spam_threshold = 0; - else if (battle_config.packet_spam_threshold > 32767) - battle_config.packet_spam_threshold = 32767; - - if (battle_config.packet_spam_flood < 0) - battle_config.packet_spam_flood = 0; - else if (battle_config.packet_spam_flood > 32767) - battle_config.packet_spam_flood = 32767; - - if (battle_config.packet_spam_kick < 0) - battle_config.packet_spam_kick = 0; - else if (battle_config.packet_spam_kick > 1) - battle_config.packet_spam_kick = 1; - - if (battle_config.mask_ip_gms < 0) - battle_config.mask_ip_gms = 0; - else if (battle_config.mask_ip_gms > 1) - battle_config.mask_ip_gms = 1; - } -} +} // namespace map } // namespace tmwa diff --git a/src/map/battle.hpp b/src/map/battle.hpp index 97a4a86..1a13420 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -20,27 +20,25 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include "fwd.hpp" - #include "battle.t.hpp" -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" +#include "fwd.hpp" #include "../net/timer.t.hpp" -#include "clif.t.hpp" +#include "../mmo/clif.t.hpp" #include "map.t.hpp" -#include "skill.t.hpp" +#include "../mmo/skill.t.hpp" namespace tmwa { +namespace map +{ // ダメージ struct Damage { - int damage, damage2; + int damage; DamageType type; int div_; interval_t amotion, dmotion; @@ -62,7 +60,6 @@ int battle_heal(dumb_ptr<block_list> bl, dumb_ptr<block_list> target, int hp, // 攻撃や移動を止める int battle_stopattack(dumb_ptr<block_list> bl); -int battle_stopwalking(dumb_ptr<block_list> bl, int type); // 通常攻撃処理まとめ ATK battle_weapon_attack(dumb_ptr<block_list> bl, dumb_ptr<block_list> target, @@ -101,128 +98,15 @@ MobMode battle_get_mode(dumb_ptr<block_list> bl); int battle_get_stat(SP stat_id, dumb_ptr<block_list> bl); eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> battle_get_sc_data(dumb_ptr<block_list> bl); -short *battle_get_sc_count(dumb_ptr<block_list> bl); Opt1 *battle_get_opt1(dumb_ptr<block_list> bl); Opt2 *battle_get_opt2(dumb_ptr<block_list> bl); Opt3 *battle_get_opt3(dumb_ptr<block_list> bl); -Option *battle_get_option(dumb_ptr<block_list> bl); +Opt0 *battle_get_option(dumb_ptr<block_list> bl); bool battle_check_undead(Race race, Element element); int battle_check_target(dumb_ptr<block_list> src, dumb_ptr<block_list> target, BCT flag); int battle_check_range(dumb_ptr<block_list> src, dumb_ptr<block_list> bl, int range); - -extern struct Battle_Config -{ - int warp_point_debug; - int enemy_critical; - int enemy_critical_rate; - int enemy_str; - int enemy_perfect_flee; - int casting_rate, delay_rate, delay_dependon_dex; - int skill_delay_attack_enable; - int monster_skill_add_range; - int player_damage_delay; - int flooritem_lifetime; - int item_auto_get; - int item_first_get_time; - int item_second_get_time; - int item_third_get_time; - int base_exp_rate, job_exp_rate; - int death_penalty_type; - int death_penalty_base, death_penalty_job; - int restart_hp_rate; - int restart_sp_rate; - int monster_hp_rate; - int monster_max_aspd; - int atcommand_gm_only; - int atcommand_spawn_quantity_limit; - int gm_all_equipment; - int monster_active_enable; - int mob_skill_use; - int mob_count_rate; - int basic_skill_check; - int player_invincible_time; - int skill_min_damage; - int natural_healhp_interval; - int natural_healsp_interval; - int natural_heal_skill_interval; - int natural_heal_weight_rate; - int arrow_decrement; - int max_aspd; - int max_hp; - int max_sp; - int max_lv; - int max_parameter; - int monster_skill_log; - int battle_log; - int save_log; - int error_log; - int etc_log; - int save_clothcolor; - int undead_detect_type; - int agi_penaly_type; - int agi_penaly_count; - int agi_penaly_num; - int vit_penaly_type; - int vit_penaly_count; - int vit_penaly_num; - int mob_changetarget_byskill; - int player_attack_direction_change; - int monster_attack_direction_change; - int display_delay_skill_fail; - int dead_branch_active; - int show_steal_in_same_party; - - int prevent_logout; - - int alchemist_summon_reward; - int maximum_level; - int drops_by_luk; - int monsters_ignore_gm; - int multi_level_up; - int pk_mode; - - int agi_penaly_count_lv; - int vit_penaly_count_lv; - - int hide_GM_session; - int invite_request_check; - int disp_experience; - - int hack_info_GM_level; - int any_warp_GM_min_level; - - int min_hair_style; - int max_hair_style; - int min_hair_color; - int max_hair_color; - int min_cloth_color; - int max_cloth_color; - - int castrate_dex_scale; - int area_size; - - int chat_lame_penalty; - int chat_spam_threshold; - int chat_spam_flood; - int chat_spam_ban; - int chat_spam_warn; - int chat_maxline; - - int packet_spam_threshold; - int packet_spam_flood; - int packet_spam_kick; - - int mask_ip_gms; - - int drop_pickup_safety_zone; - int itemheal_regeneration_factor; - - int mob_splash_radius; -} battle_config; - -bool battle_config_read(ZString cfgName); -void battle_config_check(); +} // namespace map } // namespace tmwa diff --git a/src/map/battle.t.hpp b/src/map/battle.t.hpp index 53c34ff..4759b68 100644 --- a/src/map/battle.t.hpp +++ b/src/map/battle.t.hpp @@ -29,6 +29,8 @@ namespace tmwa { +namespace map +{ namespace e { enum class BF : uint16_t @@ -241,4 +243,5 @@ earray<Races, Race, Race::COUNT> race_shift //= Races::boss, Races::other, }}; +} // namespace map } // namespace tmwa diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp index 0748f43..2606911 100644 --- a/src/map/chrif.cpp +++ b/src/map/chrif.cpp @@ -29,21 +29,25 @@ #include "../io/cxxstdio.hpp" #include "../net/ip.hpp" -#include "../net/packets.hpp" #include "../net/socket.hpp" #include "../net/timer.hpp" +#include "../net/timestamp-utils.hpp" #include "../proto2/char-map.hpp" #include "../mmo/human_time_diff.hpp" -#include "../mmo/mmo.hpp" -#include "../mmo/utils.hpp" +#include "../high/mmo.hpp" + +#include "../wire/packets.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "clif.hpp" +#include "globals.hpp" #include "intif.hpp" #include "itemdb.hpp" #include "map.hpp" +#include "map_conf.hpp" #include "npc.hpp" #include "pc.hpp" #include "storage.hpp" @@ -53,60 +57,8 @@ namespace tmwa { -Session *char_session; -static -IP4Address char_ip; -static -int char_port = 6121; -static -AccountName userid; -static -AccountPass passwd; -static -int chrif_state; - -// 設定ファイル読み込み関係 -/*========================================== - * - *------------------------------------------ - */ -void chrif_setuserid(AccountName id) -{ - userid = id; -} - -/*========================================== - * - *------------------------------------------ - */ -void chrif_setpasswd(AccountPass pwd) -{ - passwd = pwd; -} - -AccountPass chrif_getpasswd(void) -{ - return passwd; -} - -/*========================================== - * - *------------------------------------------ - */ -void chrif_setip(IP4Address ip) +namespace map { - char_ip = ip; -} - -/*========================================== - * - *------------------------------------------ - */ -void chrif_setport(int port) -{ - char_port = port; -} - /*========================================== * *------------------------------------------ @@ -151,11 +103,11 @@ static int chrif_connect(Session *s) { Packet_Fixed<0x2af8> fixed_f8; - fixed_f8.account_name = userid; - fixed_f8.account_pass = passwd; + fixed_f8.account_name = map_conf.userid; + fixed_f8.account_pass = map_conf.passwd; fixed_f8.unused = 0; - fixed_f8.ip = clif_getip(); - fixed_f8.port = clif_getport(); + fixed_f8.ip = map_conf.map_ip; + fixed_f8.port = map_conf.map_port; send_fpacket<0x2af8, 60>(s, fixed_f8); return 0; @@ -266,7 +218,7 @@ int chrif_changemapserverack(Session *, const Packet_Fixed<0x2b06>& fixed) if (fixed.error == 1) { if (battle_config.error_log) - PRINTF("map server change failed.\n"_fmt); + PRINTF("Changing the map server failed.\n"_fmt); pc_authfail(sd->status_key.account_id); return 0; } @@ -289,7 +241,7 @@ int chrif_connectack(Session *s, const Packet_Fixed<0x2af9>& fixed) { if (fixed.code) { - PRINTF("Connected to char-server failed %d.\n"_fmt, fixed.code); + PRINTF("Connecting to char-server failed %d.\n"_fmt, fixed.code); exit(1); } PRINTF("Connected to char-server (connection #%d).\n"_fmt, s); @@ -297,11 +249,6 @@ int chrif_connectack(Session *s, const Packet_Fixed<0x2af9>& fixed) chrif_sendmap(s); - PRINTF("chrif: OnCharIfInit event done. (%d events)\n"_fmt, - npc_event_doall(stringish<ScriptLabel>("OnCharIfInit"_s))); - PRINTF("chrif: OnInterIfInit event done. (%d events)\n"_fmt, - npc_event_doall(stringish<ScriptLabel>("OnInterIfInit"_s))); - return 0; } @@ -314,13 +261,11 @@ int chrif_sendmapack(Session *, Packet_Fixed<0x2afb> fixed) { if (fixed.unknown) //impossible { - PRINTF("chrif : send map list to char server failed %d\n"_fmt, + PRINTF("chrif: sending the map list to char-server failed %d\n"_fmt, fixed.unknown); exit(1); } - wisp_server_name = fixed.whisper_name; - chrif_state = 2; return 0; @@ -395,23 +340,6 @@ int chrif_charselectreq(dumb_ptr<map_session_data> sd) } /*========================================== - * GMに変化要求 - *------------------------------------------ - */ -void chrif_changegm(AccountId id, ZString pass) -{ - if (!char_session) - return; - - if (battle_config.etc_log) - PRINTF("chrif_changegm: account: %d, password: '%s'.\n"_fmt, id, pass); - - Packet_Head<0x2b0a> head_0a; - head_0a.account_id = id; - send_vpacket<0x2b0a, 8, 1>(char_session, head_0a, pass); -} - -/*========================================== * Change Email *------------------------------------------ */ @@ -455,7 +383,7 @@ void chrif_char_ask_name(AccountId id, CharName character_name, short operation_ fixed_0e.operation = operation_type; // type of operation if (operation_type == 2) fixed_0e.ban_add = modif; - PRINTF("chrif : sended 0x2b0e\n"_fmt); + PRINTF("chrif: sent 0x2b0e\n"_fmt); send_fpacket<0x2b0e, 44>(char_session, fixed_0e); } @@ -469,7 +397,7 @@ void chrif_char_ask_name(AccountId id, CharName character_name, short operation_ * 4: unban * 5: changesex * type of answer: - * 0: login-server resquest done + * 0: login-server request done * 1: player not found * 2: gm level too low * 3: login-server offline @@ -486,7 +414,7 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed) { AString output; if (fixed.error == 1) // player not found - output = STRPRINTF("The player '%s' doesn't exist."_fmt, + output = STRPRINTF("The player, '%s,' doesn't exist."_fmt, player_name); else { @@ -495,20 +423,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed) case 1: // block switch (fixed.error) { - case 0: // login-server resquest done + case 0: // login-server request done output = STRPRINTF( - "Login-server has been asked to block the player '%s'."_fmt, + "Login-server has been asked to block '%s'."_fmt, player_name); break; //case 1: // player not found case 2: // gm level too low output = STRPRINTF( - "Your GM level don't authorise you to block the player '%s'."_fmt, + "Your GM level doesn't authorize you to block the player '%s'."_fmt, player_name); break; case 3: // login-server offline output = STRPRINTF( - "Login-server is offline. Impossible to block the the player '%s'."_fmt, + "Login-server is offline, so it's impossible to block '%s'."_fmt, player_name); break; } @@ -516,20 +444,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed) case 2: // ban switch (fixed.error) { - case 0: // login-server resquest done + case 0: // login-server request done output = STRPRINTF( - "Login-server has been asked to ban the player '%s'."_fmt, + "Login-server has been asked to ban '%s'."_fmt, player_name); break; //case 1: // player not found case 2: // gm level too low output = STRPRINTF( - "Your GM level don't authorise you to ban the player '%s'."_fmt, + "Your GM level doesn't authorize you to ban '%s'."_fmt, player_name); break; case 3: // login-server offline output = STRPRINTF( - "Login-server is offline. Impossible to ban the the player '%s'."_fmt, + "Login-server is offline, so it's impossible to ban '%s'."_fmt, player_name); break; } @@ -537,20 +465,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed) case 3: // unblock switch (fixed.error) { - case 0: // login-server resquest done + case 0: // login-server request done output = STRPRINTF( - "Login-server has been asked to unblock the player '%s'."_fmt, + "Login-server has been asked to unblock '%s'."_fmt, player_name); break; //case 1: // player not found case 2: // gm level too low output = STRPRINTF( - "Your GM level don't authorise you to unblock the player '%s'."_fmt, + "Your GM level doesn't authorize you to unblock '%s'."_fmt, player_name); break; case 3: // login-server offline output = STRPRINTF( - "Login-server is offline. Impossible to unblock the the player '%s'."_fmt, + "Login-server is offline, so it's impossible to unblock '%s'."_fmt, player_name); break; } @@ -558,20 +486,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed) case 4: // unban switch (fixed.error) { - case 0: // login-server resquest done + case 0: // login-server request done output = STRPRINTF( - "Login-server has been asked to unban the player '%s'."_fmt, + "Login-server has been asked to unban '%s'."_fmt, player_name); break; //case 1: // player not found case 2: // gm level too low output = STRPRINTF( - "Your GM level don't authorise you to unban the player '%s'."_fmt, + "Your GM level doesn't authorize you to unban '%s'."_fmt, player_name); break; case 3: // login-server offline output = STRPRINTF( - "Login-server is offline. Impossible to unban the the player '%s'."_fmt, + "Login-server is offline, so it's impossible to unban '%s'."_fmt, player_name); break; } @@ -579,20 +507,20 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed) case 5: // changesex switch (fixed.error) { - case 0: // login-server resquest done + case 0: // login-server request done output = STRPRINTF( - "Login-server has been asked to change the sex of the player '%s'."_fmt, + "Login-server has been asked to change the sex of '%s'."_fmt, player_name); break; //case 1: // player not found case 2: // gm level too low output = STRPRINTF( - "Your GM level don't authorise you to change the sex of the player '%s'."_fmt, + "Your GM level doesn't authorize you to change the sex of '%s'."_fmt, player_name); break; case 3: // login-server offline output = STRPRINTF( - "Login-server is offline. Impossible to change the sex of the the player '%s'."_fmt, + "Login-server is offline, so it's impossible to change the sex of '%s'."_fmt, player_name); break; } @@ -603,36 +531,12 @@ int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed) clif_displaymessage(sd->sess, output); } else - PRINTF("chrif_char_ask_name_answer failed - player not online.\n"_fmt); + PRINTF("chrif_char_ask_name_answer failed because the player is not online.\n"_fmt); return 0; } /*========================================== - * End of GM change(@GM) (modified by Yor) - *------------------------------------------ - */ -static -void chrif_changedgm(Session *, const Packet_Fixed<0x2b0b>& fixed) -{ - AccountId acc = fixed.account_id; - GmLevel level = fixed.gm_level; - - dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(acc)); - - if (battle_config.etc_log) - PRINTF("chrif_changedgm: account: %d, GM level 0 -> %d.\n"_fmt, acc, - level); - if (sd != nullptr) - { - if (level) - clif_displaymessage(sd->sess, "GM modification success."_s); - else - clif_displaymessage(sd->sess, "Failure of GM modification."_s); - } -} - -/*========================================== * 性別変化終了 (modified by Yor) *------------------------------------------ */ @@ -659,15 +563,14 @@ void chrif_changedsex(Session *, const Packet_Fixed<0x2b0d>& fixed) { if (sd->status.inventory[i].nameid && bool(sd->status.inventory[i].equip)) - pc_unequipitem(sd, i, CalcStatus::NOW); + pc_unequipitem(sd, i, CalcStatus::LATER); } + pc_calcstatus(sd, 0); // save character chrif_save(sd); sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it) - clif_displaymessage(sd->sess, - "Your sex has been changed (need disconexion by the server)..."_s); - clif_setwaitclose(sd->sess); // forced to disconnect for the change + clif_fixpcpos(sd); // use clif_set0078_main_1d8 to send new sex to the client } } else @@ -750,18 +653,13 @@ int chrif_divorce(CharId char_id, CharId partner_id) if (sd && sd->status.partner_id == partner_id) { sd->status.partner_id = CharId(); - - if (sd->npc_flags.divorce) - { - sd->npc_flags.divorce = 0; - map_scriptcont(sd, sd->npc_id); - } } sd = map_nick2sd(map_charid2nick(partner_id)); - nullpo_retz(sd); - if (sd->status.partner_id == char_id) + if (sd && sd->status.partner_id == char_id) + { sd->status.partner_id = CharId(); + } return 0; } @@ -801,14 +699,14 @@ int chrif_accountdeletion(Session *, const Packet_Fixed<0x2b13>& fixed) { sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters clif_displaymessage(sd->sess, - "Your account has been deleted (disconnection)..."_s); + "Your account has been deleted. You will now be disconnected..."_s); clif_setwaitclose(sd->sess); // forced to disconnect for the change } } else { if (sd != nullptr) - PRINTF("chrif_accountdeletion failed - player not online.\n"_fmt); + PRINTF("chrif_accountdeletion failed because the player is not online.\n"_fmt); } return 0; @@ -838,11 +736,11 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed) { // status or final date of a banishment case 1: // 0 = Unregistered ID clif_displaymessage(sd->sess, - "Your account has 'Unregistered'."_s); + "Your account has an unregistered ID."_s); break; case 2: // 1 = Incorrect Password clif_displaymessage(sd->sess, - "Your account has an 'Incorrect Password'..."_s); + "Your password is incorrect."_s); break; case 3: // 2 = This ID is expired clif_displaymessage(sd->sess, @@ -850,7 +748,7 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed) break; case 4: // 3 = Rejected from Server clif_displaymessage(sd->sess, - "Your account has been rejected from server."_s); + "Your account has been rejected by the server."_s); break; case 5: // 4 = You have been blocked by the GM Team clif_displaymessage(sd->sess, @@ -858,19 +756,19 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed) break; case 6: // 5 = Your Game's EXE file is not the latest version clif_displaymessage(sd->sess, - "Your Game's EXE file is not the latest version."_s); + "You need to update your client."_s); break; case 7: // 6 = Your are Prohibited to log in until %s clif_displaymessage(sd->sess, - "Your account has been prohibited to log in."_s); + "Your account has been prohibited from logging in."_s); break; case 8: // 7 = Server is jammed due to over populated clif_displaymessage(sd->sess, - "Server is jammed due to over populated."_s); + "The server is overpopulated."_s); break; case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this) clif_displaymessage(sd->sess, - "Your account has not more authorised."_s); + "Your account must be authorized."_s); break; case 100: // 99 = This ID has been totally erased clif_displaymessage(sd->sess, @@ -878,7 +776,7 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed) break; default: clif_displaymessage(sd->sess, - "Your account has not more authorised."_s); + "Your account must be authorized."_s); break; } } @@ -897,7 +795,7 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed) else { if (sd != nullptr) - PRINTF("chrif_accountban failed - player not online.\n"_fmt); + PRINTF("chrif_accountban failed because the player is not online.\n"_fmt); } return 0; @@ -910,137 +808,17 @@ int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed) static int chrif_recvgmaccounts(Session *s, const std::vector<Packet_Repeat<0x2b15>>& repeat) { - PRINTF("From login-server: receiving of %d GM accounts information.\n"_fmt, + PRINTF("Receiving information on %d GM accounts from login-server.\n"_fmt, pc_read_gm_account(s, repeat)); return 0; } -/*========================================== - * Request to reload GM accounts and their levels: send to char-server by [Yor] - *------------------------------------------ - */ -int chrif_reloadGMdb(void) -{ - if (!char_session) - return -1; - - Packet_Fixed<0x2af7> fixed_f7; - send_fpacket<0x2af7, 2>(char_session, fixed_f7); - - return 0; -} - -/*======================================== - * Map item IDs - *---------------------------------------- - */ - -static -void ladmin_itemfrob_fix_item(ItemNameId source, ItemNameId dest, Item *item) -{ - if (item && item->nameid == source) - { - item->nameid = dest; - item->equip = EPOS::ZERO; - } -} - -static -void ladmin_itemfrob_c2(dumb_ptr<block_list> bl, ItemNameId source_id, ItemNameId dest_id) -{ -#define IFIX(v) if (v == source_id) {v = dest_id; } -#define FIX(item) ladmin_itemfrob_fix_item(source_id, dest_id, &item) - - if (!bl) - return; - - switch (bl->bl_type) - { - case BL::PC: - { - dumb_ptr<map_session_data> pc = bl->is_player(); - Storage *stor = account2storage2(pc->status_key.account_id); - - for (IOff0 j : IOff0::iter()) - IFIX(pc->status.inventory[j].nameid); - // cart is no longer supported - // IFIX(pc->status.weapon); - IFIX(pc->status.shield); - IFIX(pc->status.head_top); - IFIX(pc->status.head_mid); - IFIX(pc->status.head_bottom); - - if (stor) - { - for (SOff0 j : SOff0::iter()) - FIX(stor->storage_[j]); - } - - for (IOff0 j : IOff0::iter()) - { - struct item_data *item = pc->inventory_data[j]; - if (item && item->nameid == source_id) - { - item->nameid = dest_id; - if (bool(item->equip)) - pc_unequipitem(pc, j, CalcStatus::NOW); - item->nameid = dest_id; - } - } - - break; - } - - case BL::MOB: - { - dumb_ptr<mob_data> mob = bl->is_mob(); - for (Item& itm : mob->lootitemv) - FIX(itm); - break; - } - - case BL::ITEM: - { - dumb_ptr<flooritem_data> item = bl->is_item(); - FIX(item->item_data); - break; - } - } -#undef FIX -#undef IFIX -} - -static -void ladmin_itemfrob_c(dumb_ptr<block_list> bl, ItemNameId source_id, ItemNameId dest_id) -{ - ladmin_itemfrob_c2(bl, source_id, dest_id); -} - -static -void ladmin_itemfrob(Session *, const Packet_Fixed<0x2afa>& fixed) -{ - ItemNameId source_id = fixed.source_item_id; - ItemNameId dest_id = fixed.dest_item_id; - dumb_ptr<block_list> bl = map_get_first_session(); - - // flooritems - map_foreachobject(std::bind(ladmin_itemfrob_c, ph::_1, source_id, dest_id), - BL::NUL /* any object */); - - // player characters (and, hopefully, mobs) - while (bl->bl_next) - { - ladmin_itemfrob_c2(bl, source_id, dest_id); - bl = bl->bl_next; - } -} - static void chrif_delete(Session *s) { assert (s == char_session); - PRINTF("Map-server can't connect to char-server (connection #%d).\n"_fmt, + PRINTF("map-server can't connect to char-server (connection #%d).\n"_fmt, s); char_session = nullptr; } @@ -1070,16 +848,6 @@ void chrif_parse(Session *s) chrif_connectack(s, fixed); break; } - case 0x2afa: - { - Packet_Fixed<0x2afa> fixed; - rv = recv_fpacket<0x2afa, 10>(s, fixed); - if (rv != RecvResult::Complete) - break; - - ladmin_itemfrob(s, fixed); - break; - } case 0x2afb: { Packet_Fixed<0x2afb> fixed; @@ -1099,12 +867,11 @@ void chrif_parse(Session *s) AccountId id = payload.account_id; int login_id2 = payload.login_id2; - TimeT connect_until_time = payload.connect_until; short tmw_version = payload.packet_tmw_version; CharKey st_key = payload.char_key; CharData st_data = payload.char_data; pc_authok(id, login_id2, - connect_until_time, tmw_version, + tmw_version, &st_key, &st_data); break; } @@ -1159,16 +926,6 @@ void chrif_parse(Session *s) chrif_changemapserverack(s, fixed); break; } - case 0x2b0b: - { - Packet_Fixed<0x2b0b> fixed; - rv = recv_fpacket<0x2b0b, 10>(s, fixed); - if (rv != RecvResult::Complete) - break; - - chrif_changedgm(s, fixed); - break; - } case 0x2b0d: { Packet_Fixed<0x2b0d> fixed; @@ -1250,7 +1007,7 @@ void chrif_parse(Session *s) return; if (battle_config.error_log) - PRINTF("chrif_parse : unknown packet %d %d\n"_fmt, s, + PRINTF("chrif_parse: unknown packet %d %d\n"_fmt, s, packet_id); s->set_eof(); return; @@ -1283,7 +1040,7 @@ void send_users_tochar(TimerData *, tick_t) if (sd && sd->state.auth && !((battle_config.hide_GM_session || sd->state.shroud_active - || bool(sd->status.option & Option::HIDE)) && pc_isGM(sd))) + || bool(sd->status.option & Opt0::HIDE)) && pc_isGM(sd))) { Packet_Repeat<0x2aff> info; info.char_id = sd->status_key.char_id; @@ -1304,9 +1061,9 @@ void check_connect_char_server(TimerData *, tick_t) { if (!char_session) { - PRINTF("Attempt to connect to char-server...\n"_fmt); + PRINTF("Attempting to connect to char-server...\n"_fmt); chrif_state = 0; - char_session = make_connection(char_ip, char_port, + char_session = make_connection(map_conf.char_ip, map_conf.char_port, SessionParsers{.func_parse= chrif_parse, .func_delete= chrif_delete}); if (!char_session) return; @@ -1331,4 +1088,5 @@ void do_init_chrif(void) 5_s ).detach(); } +} // namespace map } // namespace tmwa diff --git a/src/map/chrif.hpp b/src/map/chrif.hpp index 4711bc5..655103d 100644 --- a/src/map/chrif.hpp +++ b/src/map/chrif.hpp @@ -22,24 +22,11 @@ #include "fwd.hpp" -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" - -#include "../net/fwd.hpp" - -#include "../mmo/fwd.hpp" - namespace tmwa { -void chrif_setuserid(AccountName); -void chrif_setpasswd(AccountPass); -AccountPass chrif_getpasswd(void); - -void chrif_setip(IP4Address); -void chrif_setport(int); - +namespace map +{ int chrif_isconnect(void); int chrif_authreq(dumb_ptr<map_session_data>); @@ -55,12 +42,8 @@ void chrif_changeemail(AccountId id, AccountEmail actual_email, AccountEmail new void chrif_char_ask_name(AccountId id, CharName character_name, short operation_type, HumanTimeDiff modif); int chrif_saveaccountreg2(dumb_ptr<map_session_data> sd); -int chrif_reloadGMdb(void); int chrif_send_divorce(CharId char_id); void do_init_chrif(void); - -// only used by intif.cpp -// and clif.cpp for the new on_delete stuff ... -extern Session *char_session; +} // namespace map } // namespace tmwa diff --git a/src/map/clif.cpp b/src/map/clif.cpp index b47bf28..243ffaf 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -36,30 +36,36 @@ #include "../strings/xstring.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" +#include "../io/extract.hpp" #include "../io/write.hpp" #include "../net/ip.hpp" -#include "../net/packets.hpp" #include "../net/socket.hpp" #include "../net/timer.hpp" +#include "../net/timestamp-utils.hpp" #include "../proto2/any-user.hpp" #include "../proto2/char-map.hpp" #include "../proto2/map-user.hpp" -#include "../mmo/md5more.hpp" -#include "../mmo/utils.hpp" +#include "../mmo/cxxstdio_enums.hpp" #include "../mmo/version.hpp" +#include "../high/md5more.hpp" + +#include "../wire/packets.hpp" + #include "atcommand.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "chrif.hpp" +#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" #include "party.hpp" #include "pc.hpp" @@ -73,6 +79,8 @@ namespace tmwa { +namespace map +{ constexpr int EMOTE_IGNORED = 0x0e; // functions list. Rate is how many milliseconds are required between @@ -117,11 +125,6 @@ enum class SendWho }; static -IP4Address map_ip; -static -int map_port = 5121; - -static int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val, dumb_ptr<map_session_data> dstsd); static @@ -149,42 +152,6 @@ void clif_delete(Session *s) /*========================================== - * map鯖のip設定 - *------------------------------------------ - */ -void clif_setip(IP4Address ip) -{ - map_ip = ip; -} - -/*========================================== - * map鯖のport設定 - *------------------------------------------ - */ -void clif_setport(int port) -{ - map_port = port; -} - -/*========================================== - * map鯖のip読み出し - *------------------------------------------ - */ -IP4Address clif_getip(void) -{ - return map_ip; -} - -/*========================================== - * map鯖のport読み出し - *------------------------------------------ - */ -int clif_getport(void) -{ - return map_port; -} - -/*========================================== * *------------------------------------------ */ @@ -231,11 +198,6 @@ int is_deaf(dumb_ptr<block_list> bl) return sd->special_state.deaf; } -static -void clif_emotion_towards(dumb_ptr<block_list> bl, - dumb_ptr<block_list> target, int type); - - enum class ChatType { Party, @@ -295,7 +257,6 @@ void clif_send_sub(dumb_ptr<block_list> bl, const Buffer& buf, static int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type) { - PartyPair p; int x0 = 0, x1 = 0, y0 = 0, y1 = 0; if (type != SendWho::ALL_CLIENT) @@ -305,7 +266,7 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type) if (bl->bl_type == BL::PC) { dumb_ptr<map_session_data> sd2 = bl->is_player(); - if (bool(sd2->status.option & Option::INVISIBILITY)) + if (bool(sd2->status.option & Opt0::INVISIBILITY)) { // Obscure hidden GMs @@ -384,20 +345,22 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type) case SendWho::PARTY_WOS: // 自分以外の全パーティーメンバに送信 case SendWho::PARTY_SAMEMAP: // 同じマップの全パーティーメンバに送信 case SendWho::PARTY_SAMEMAP_WOS: // 自分以外の同じマップの全パーティーメンバに送信 + { + Option<PartyPair> p_ = None; if (bl->bl_type == BL::PC) { dumb_ptr<map_session_data> sd = bl->is_player(); if (sd->partyspy) { - p = party_search(sd->partyspy); + p_ = party_search(sd->partyspy); } else { if (sd->status.party_id) - p = party_search(sd->status.party_id); + p_ = party_search(sd->status.party_id); } } - if (p) + OMATCH_BEGIN_SOME (p, p_) { for (int i = 0; i < MAX_PARTY; i++) { @@ -436,6 +399,8 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type) } } } + OMATCH_END (); + } break; case SendWho::SELF: { @@ -617,37 +582,6 @@ int clif_clearchar(dumb_ptr<block_list> bl, BeingRemoveWhy type) return 0; } -static -void clif_clearchar_delay_sub(TimerData *, tick_t, - dumb_ptr<block_list> bl, BeingRemoveWhy type) -{ - clif_clearchar(bl, type); - MapBlockLock::freeblock(bl); -} - -int clif_clearchar_delay(tick_t tick, - dumb_ptr<block_list> bl, BeingRemoveWhy type) -{ - dumb_ptr<block_list> tmpbl; - tmpbl.new_(); - - // yikes! - tmpbl->bl_next = bl->bl_next; - tmpbl->bl_prev = bl->bl_prev; - tmpbl->bl_id = bl->bl_id; - tmpbl->bl_m = bl->bl_m; - tmpbl->bl_x = bl->bl_x; - tmpbl->bl_y = bl->bl_y; - tmpbl->bl_type = bl->bl_type; - - Timer(tick, - std::bind(clif_clearchar_delay_sub, ph::_1, ph::_2, - tmpbl, type) - ).detach(); - - return 0; -} - /*========================================== * *------------------------------------------ @@ -684,14 +618,14 @@ void clif_set0078_main_1d8(dumb_ptr<map_session_data> sd, Buffer& buf) fixed_1d8.weapon = sd->attack_spell_look_override; else { - if (widx.ok() && sd->inventory_data[widx]) + if (widx.ok() && sd->inventory_data[widx].is_some()) { fixed_1d8.weapon = sd->status.inventory[widx].nameid; } else fixed_1d8.weapon = ItemNameId(); } - if (sidx.ok() && sidx != widx && sd->inventory_data[sidx]) + if (sidx.ok() && sidx != widx && sd->inventory_data[sidx].is_some()) { fixed_1d8.shield = sd->status.inventory[sidx].nameid; } @@ -738,14 +672,14 @@ void clif_set0078_alt_1d9(dumb_ptr<map_session_data> sd, Buffer& buf) fixed_1d8.weapon = sd->attack_spell_look_override; else { - if (widx.ok() && sd->inventory_data[widx]) + if (widx.ok() && sd->inventory_data[widx].is_some()) { fixed_1d8.weapon = sd->status.inventory[widx].nameid; } else fixed_1d8.weapon = ItemNameId(); } - if (sidx.ok() && sidx != widx && sd->inventory_data[sidx]) + if (sidx.ok() && sidx != widx && sd->inventory_data[sidx].is_some()) { fixed_1d8.shield = sd->status.inventory[sidx].nameid; } @@ -791,13 +725,13 @@ void clif_set007b(dumb_ptr<map_session_data> sd, Buffer& buf) fixed_1da.hair_style = sd->status.hair; IOff0 widx = sd->equip_index_maybe[EQUIP::WEAPON]; IOff0 sidx = sd->equip_index_maybe[EQUIP::SHIELD]; - if (widx.ok() && sd->inventory_data[widx]) + if (widx.ok() && sd->inventory_data[widx].is_some()) { fixed_1da.weapon = sd->status.inventory[widx].nameid; } else fixed_1da.weapon = ItemNameId(); - if (sidx.ok() && sidx != widx && sd->inventory_data[sidx]) + if (sidx.ok() && sidx != widx && sd->inventory_data[sidx].is_some()) { fixed_1da.shield = sd->status.inventory[sidx].nameid; } @@ -969,7 +903,7 @@ int clif_spawnnpc(dumb_ptr<npc_data> nd) { nullpo_retz(nd); - if (nd->npc_class == NEGATIVE_SPECIES || nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS) + if (nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS) return 0; Packet_Fixed<0x007c> fixed_7c; @@ -1002,7 +936,7 @@ int clif_spawn_fake_npc_for_player(dumb_ptr<map_session_data> sd, BlockId fake_n fixed_7c.speed = interval_t(); fixed_7c.opt1 = Opt1::ZERO; fixed_7c.opt2 = Opt2::ZERO; - fixed_7c.option = Option::ZERO; + fixed_7c.option = Opt0::ZERO; fixed_7c.species = FAKE_NPC_CLASS; fixed_7c.pos.x = sd->bl_x; fixed_7c.pos.y = sd->bl_y; @@ -1013,7 +947,7 @@ int clif_spawn_fake_npc_for_player(dumb_ptr<map_session_data> sd, BlockId fake_n fixed_78.speed = interval_t(); fixed_78.opt1 = Opt1::ZERO; fixed_78.opt2 = Opt2::ZERO; - fixed_78.option = Option::ZERO; + fixed_78.option = Opt0::ZERO; fixed_78.species = FAKE_NPC_CLASS; fixed_78.unused_head_bottom_or_species_again = unwrap<Species>(FAKE_NPC_CLASS); fixed_78.pos.x = sd->bl_x; @@ -1224,7 +1158,6 @@ int clif_npcbuysell(dumb_ptr<map_session_data> sd, BlockId id) */ int clif_buylist(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data_shop> nd) { - struct item_data *id; int i, val; nullpo_retz(sd); @@ -1234,7 +1167,7 @@ int clif_buylist(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data_shop> nd) std::vector<Packet_Repeat<0x00c6>> repeat_c6(nd->shop_items.size()); for (i = 0; i < nd->shop_items.size(); i++) { - id = itemdb_search(nd->shop_items[i].nameid); + P<struct item_data> id = itemdb_search(nd->shop_items[i].nameid); val = nd->shop_items[i].value; repeat_c6[i].base_price = val; // base price repeat_c6[i].actual_price = val; // actual price @@ -1258,9 +1191,11 @@ int clif_selllist(dumb_ptr<map_session_data> sd) std::vector<Packet_Repeat<0x00c7>> repeat_c7; for (IOff0 i : IOff0::iter()) { - if (sd->status.inventory[i].nameid && sd->inventory_data[i]) + if (!sd->status.inventory[i].nameid) + continue; + OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[i]) { - int val = sd->inventory_data[i]->value_sell; + int val = sdidi->value_sell; if (val < 0) continue; Packet_Repeat<0x00c7> info; @@ -1269,6 +1204,7 @@ int clif_selllist(dumb_ptr<map_session_data> sd) info.actual_price = val; repeat_c7.push_back(info); } + OMATCH_END (); } send_packet_repeatonly<0x00c7, 4, 10>(s, repeat_c7); @@ -1379,9 +1315,9 @@ int clif_additem(dumb_ptr<map_session_data> sd, IOff0 n, int amount, PickupFail } else { - if (!n.ok() || !sd->status.inventory[n].nameid - || sd->inventory_data[n] == nullptr) + if (!n.ok() || !sd->status.inventory[n].nameid) return 1; + auto sdidn = TRY_UNWRAP(sd->inventory_data[n], return 1); fixed_a0.ioff2 = n.shift(); fixed_a0.amount = amount; @@ -1396,9 +1332,9 @@ int clif_additem(dumb_ptr<map_session_data> sd, IOff0 n, int amount, PickupFail fixed_a0.card3 = 0; } fixed_a0.epos = pc_equippoint(sd, n); - fixed_a0.item_type = (sd->inventory_data[n]->type == ItemType::_7 + fixed_a0.item_type = (sdidn->type == ItemType::_7 ? ItemType::WEAPON - : sd->inventory_data[n]->type); + : sdidn->type); fixed_a0.pickup_fail = fail; } @@ -1435,17 +1371,18 @@ void clif_itemlist(dumb_ptr<map_session_data> sd) std::vector<Packet_Repeat<0x01ee>> repeat_1ee; for (IOff0 i : IOff0::iter()) { - if (!sd->status.inventory[i].nameid - || sd->inventory_data[i] == nullptr - || itemdb_isequip2(sd->inventory_data[i])) + if (!sd->status.inventory[i].nameid) + continue; + auto sdidi = TRY_UNWRAP(sd->inventory_data[i], continue); + if (itemdb_isequip2(sdidi)) continue; Packet_Repeat<0x01ee> info; info.ioff2 = i.shift(); info.name_id = sd->status.inventory[i].nameid; - info.item_type = sd->inventory_data[i]->type; + info.item_type = sdidi->type; info.identify = 1; info.amount = sd->status.inventory[i].amount; - if (sd->inventory_data[i]->equip == EPOS::ARROW) + if (sdidi->equip == EPOS::ARROW) { info.epos = EPOS::ARROW; if (bool(sd->status.inventory[i].equip)) @@ -1479,17 +1416,18 @@ void clif_equiplist(dumb_ptr<map_session_data> sd) std::vector<Packet_Repeat<0x00a4>> repeat_a4; for (IOff0 i : IOff0::iter()) { - if (!sd->status.inventory[i].nameid - || sd->inventory_data[i] == nullptr - || !itemdb_isequip2(sd->inventory_data[i])) + if (!sd->status.inventory[i].nameid) + continue; + P<struct item_data> sdidi = TRY_UNWRAP(sd->inventory_data[i], continue); + if (!itemdb_isequip2(sdidi)) continue; Packet_Repeat<0x00a4> info; info.ioff2 = i.shift(); info.name_id = sd->status.inventory[i].nameid; info.item_type = ( - sd->inventory_data[i]->type == ItemType::_7 + sdidi->type == ItemType::_7 ? ItemType::WEAPON - : sd->inventory_data[i]->type); + : sdidi->type); info.identify = 0; info.epos_pc = pc_equippoint(sd, i); info.epos_inv = sd->status.inventory[i].equip; @@ -1513,10 +1451,9 @@ void clif_equiplist(dumb_ptr<map_session_data> sd) * カプラさんに預けてある消耗品&収集品リスト *------------------------------------------ */ -int clif_storageitemlist(dumb_ptr<map_session_data> sd, Storage *stor) +int clif_storageitemlist(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor) { nullpo_retz(sd); - nullpo_retz(stor); Session *s = sd->sess; std::vector<Packet_Repeat<0x01f0>> repeat_1f0; @@ -1525,9 +1462,7 @@ int clif_storageitemlist(dumb_ptr<map_session_data> sd, Storage *stor) if (!stor->storage_[i].nameid) continue; - struct item_data *id; - id = itemdb_search(stor->storage_[i].nameid); - nullpo_retz(id); + P<struct item_data> id = itemdb_search(stor->storage_[i].nameid); if (itemdb_isequip2(id)) continue; @@ -1555,10 +1490,9 @@ int clif_storageitemlist(dumb_ptr<map_session_data> sd, Storage *stor) * カプラさんに預けてある装備リスト *------------------------------------------ */ -int clif_storageequiplist(dumb_ptr<map_session_data> sd, Storage *stor) +int clif_storageequiplist(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor) { nullpo_retz(sd); - nullpo_retz(stor); Session *s = sd->sess; std::vector<Packet_Repeat<0x00a6>> repeat_a6; @@ -1567,9 +1501,7 @@ int clif_storageequiplist(dumb_ptr<map_session_data> sd, Storage *stor) if (!stor->storage_[i].nameid) continue; - struct item_data *id; - id = itemdb_search(stor->storage_[i].nameid); - nullpo_retz(id); + P<struct item_data> id = itemdb_search(stor->storage_[i].nameid); if (!itemdb_isequip2(id)) continue; Packet_Repeat<0x00a6> info; @@ -1831,7 +1763,7 @@ int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val, if (bl->bl_type == BL::PC) sd = bl->is_player(); - if (sd && bool(sd->status.option & Option::INVISIBILITY)) + if (sd && bool(sd->status.option & Opt0::INVISIBILITY)) return 0; if (sd @@ -1845,7 +1777,7 @@ int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val, fixed_1d7.look_type = type; IOff0 idx = sd->equip_index_maybe[equip_point]; - if (idx.ok() && sd->inventory_data[idx]) + if (idx.ok() && sd->inventory_data[idx].is_some()) { fixed_1d7.weapon_or_name_id_or_value = unwrap<ItemNameId>(sd->status.inventory[idx].nameid); } @@ -1862,14 +1794,14 @@ int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val, fixed_1d7.weapon_or_name_id_or_value = unwrap<ItemNameId>(sd->attack_spell_look_override); else { - if (widx.ok() && sd->inventory_data[widx]) + if (widx.ok() && sd->inventory_data[widx].is_some()) { fixed_1d7.weapon_or_name_id_or_value = unwrap<ItemNameId>(sd->status.inventory[widx].nameid); } else fixed_1d7.weapon_or_name_id_or_value = unwrap<ItemNameId>(ItemNameId()); } - if (sidx.ok() && sidx != widx && sd->inventory_data[sidx]) + if (sidx.ok() && sidx != widx && sd->inventory_data[sidx].is_some()) { fixed_1d7.shield = sd->status.inventory[sidx].nameid; } @@ -2074,7 +2006,7 @@ int clif_changeoption(dumb_ptr<block_list> bl) nullpo_retz(bl); - Option option = *battle_get_option(bl); + Opt0 option = *battle_get_option(bl); sc_data = battle_get_sc_data(bl); Packet_Fixed<0x0119> fixed_119; @@ -2263,10 +2195,9 @@ int clif_tradecompleted(dumb_ptr<map_session_data> sd, int fail) *------------------------------------------ */ int clif_updatestorageamount(dumb_ptr<map_session_data> sd, - Storage *stor) + Borrowed<Storage> stor) { nullpo_retz(sd); - nullpo_retz(stor); Session *s = sd->sess; Packet_Fixed<0x00f2> fixed_f2; @@ -2281,11 +2212,10 @@ int clif_updatestorageamount(dumb_ptr<map_session_data> sd, * カプラ倉庫にアイテムを追加する *------------------------------------------ */ -int clif_storageitemadded(dumb_ptr<map_session_data> sd, Storage *stor, +int clif_storageitemadded(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor, SOff0 index, int amount) { nullpo_retz(sd); - nullpo_retz(stor); Session *s = sd->sess; Packet_Fixed<0x00f4> fixed_f4; @@ -2357,7 +2287,7 @@ static void clif_getareachar_pc(dumb_ptr<map_session_data> sd, dumb_ptr<map_session_data> dstsd) { - if (bool(dstsd->status.option & Option::INVISIBILITY)) + if (bool(dstsd->status.option & Opt0::INVISIBILITY)) return; nullpo_retv(sd); @@ -2392,7 +2322,7 @@ void clif_getareachar_npc(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data> nd) nullpo_retv(sd); nullpo_retv(nd); - if (nd->npc_class == NEGATIVE_SPECIES || nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS) + if (nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS) return; Buffer buf; @@ -2470,7 +2400,7 @@ int clif_fixpcpos(dumb_ptr<map_session_data> sd) */ int clif_damage(dumb_ptr<block_list> src, dumb_ptr<block_list> dst, tick_t tick, interval_t sdelay, interval_t ddelay, int damage, - int div, DamageType type, int damage2) + int div, DamageType type) { eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data; @@ -2488,7 +2418,7 @@ int clif_damage(dumb_ptr<block_list> src, dumb_ptr<block_list> dst, fixed_8a.damage = (damage > 0x7fff) ? 0x7fff : damage; fixed_8a.div = div; fixed_8a.damage_type = type; - fixed_8a.damage2 = damage2; + fixed_8a.damage2 = 0; Buffer buf = create_fpacket<0x008a, 29>(fixed_8a); clif_send(buf, src, SendWho::AREA); @@ -2686,43 +2616,6 @@ void clif_mobinsight(dumb_ptr<block_list> bl, dumb_ptr<mob_data> md) } /*========================================== - * - *------------------------------------------ - */ -int clif_skillinfo(dumb_ptr<map_session_data> sd, SkillID skillid, int type, - int range) -{ - nullpo_retz(sd); - - Session *s = sd->sess; - if (!sd->status.skill[skillid].lv) - return 0; - Packet_Fixed<0x0147> fixed_147; - fixed_147.info.skill_id = skillid; - if (type < 0) - fixed_147.info.type_or_inf = skill_get_inf(skillid); - else - fixed_147.info.type_or_inf = type; - fixed_147.info.flags = SkillFlags::ZERO; - fixed_147.info.level = sd->status.skill[skillid].lv; - fixed_147.info.sp = skill_get_sp(skillid, sd->status.skill[skillid].lv); - if (range < 0) - { - range = skill_get_range(skillid, sd->status.skill[skillid].lv); - if (range < 0) - range = battle_get_range(sd) - (range + 1); - fixed_147.info.range = range; - } - else - fixed_147.info.range = range; - fixed_147.info.unused = ""_s; - fixed_147.info.can_raise = sd->status.skill[skillid].lv < skill_get_max_raise(skillid); - send_fpacket<0x0147, 39>(s, fixed_147); - - return 0; -} - -/*========================================== * スキルリストを送信する *------------------------------------------ */ @@ -2971,8 +2864,6 @@ int clif_party_info(PartyPair p, Session *s) int i; dumb_ptr<map_session_data> sd = nullptr; - nullpo_retz(p); - Packet_Head<0x00fb> head_fb; std::vector<Packet_Repeat<0x00fb>> repeat_fb; head_fb.party_name = p->name; @@ -3018,15 +2909,12 @@ int clif_party_info(PartyPair p, Session *s) void clif_party_invite(dumb_ptr<map_session_data> sd, dumb_ptr<map_session_data> tsd) { - PartyPair p; - nullpo_retv(sd); nullpo_retv(tsd); Session *s = tsd->sess; - if (!(p = party_search(sd->status.party_id))) - return; + PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return); Packet_Fixed<0x00fe> fixed_fe; fixed_fe.account_id = sd->status_key.account_id; @@ -3068,8 +2956,6 @@ void clif_party_inviteack(dumb_ptr<map_session_data> sd, CharName nick, int flag */ void clif_party_option(PartyPair p, dumb_ptr<map_session_data> sd, int flag) { - nullpo_retv(p); - if (sd == nullptr && flag == 0) { int i; @@ -3102,8 +2988,6 @@ void clif_party_leaved(PartyPair p, dumb_ptr<map_session_data> sd, { int i; - nullpo_retv(p); - Packet_Fixed<0x0105> fixed_105; fixed_105.account_id = account_id; fixed_105.char_name = name; @@ -3140,8 +3024,6 @@ void clif_party_message(PartyPair p, AccountId account_id, XString mes) dumb_ptr<map_session_data> sd = nullptr; int i; - nullpo_retv(p); - for (i = 0; i < MAX_PARTY; i++) { sd = dumb_ptr<map_session_data>(p->member[i].sd); @@ -3214,21 +3096,6 @@ int clif_movetoattack(dumb_ptr<map_session_data> sd, dumb_ptr<block_list> bl) } /*========================================== - * MVPエフェクト - *------------------------------------------ - */ -int clif_mvp_effect(dumb_ptr<map_session_data> sd) -{ - nullpo_retz(sd); - - Packet_Fixed<0x010c> fixed_10c; - fixed_10c.block_id = sd->bl_id; - Buffer buf = create_fpacket<0x010c, 6>(fixed_10c); - clif_send(buf, sd, SendWho::AREA); - return 0; -} - -/*========================================== * エモーション *------------------------------------------ */ @@ -3243,7 +3110,6 @@ void clif_emotion(dumb_ptr<block_list> bl, int type) clif_send(buf, bl, SendWho::AREA); } -static void clif_emotion_towards(dumb_ptr<block_list> bl, dumb_ptr<block_list> target, int type) { @@ -3442,9 +3308,9 @@ RecvResult clif_parse_LoadEndAck(Session *s, dumb_ptr<map_session_data> sd) // 119 // 78 - if (battle_config.player_invincible_time > 0) + if (battle_config.player_invincible_time > interval_t::zero()) { - pc_setinvincibletimer(sd, static_cast<interval_t>(battle_config.player_invincible_time)); + pc_setinvincibletimer(sd, battle_config.player_invincible_time); } map_addblock(sd); // ブロック登録 @@ -3467,7 +3333,6 @@ RecvResult clif_parse_LoadEndAck(Session *s, dumb_ptr<map_session_data> sd) std::bind(pc_calc_pvprank_timer, ph::_1, ph::_2, sd->bl_id)); sd->pvp_rank = 0; - sd->pvp_lastusers = 0; sd->pvp_point = 5; } } @@ -3642,16 +3507,20 @@ RecvResult clif_parse_GetCharNameRequest(Session *s, dumb_ptr<map_session_data> fixed_95.char_name = ssd->status_key.name; send_fpacket<0x0095, 30>(s, fixed_95); - PartyPair p; - PartyName party_name; int send = 0; - if (ssd->status.party_id && (p = party_search(ssd->status.party_id))) + if (ssd->status.party_id) { - party_name = p->name; - send = 1; + Option<PartyPair> p_ = party_search(ssd->status.party_id); + + OMATCH_BEGIN_SOME (p, p_) + { + party_name = p->name; + send = 1; + } + OMATCH_END (); } if (send) @@ -3665,7 +3534,7 @@ RecvResult clif_parse_GetCharNameRequest(Session *s, dumb_ptr<map_session_data> send_fpacket<0x0195, 102>(s, fixed_195); } - if (pc_isGM(sd).satisfies(GmLevel::from(static_cast<uint32_t>(battle_config.hack_info_GM_level)))) + if (pc_isGM(sd).satisfies(battle_config.hack_info_GM_level)) { IP4Address ip = ssd->get_ip(); Packet_Fixed<0x020c> fixed_20c; @@ -3853,25 +3722,6 @@ RecvResult clif_parse_Emotion(Session *s, dumb_ptr<map_session_data> sd) *------------------------------------------ */ static -RecvResult clif_parse_HowManyConnections(Session *s, dumb_ptr<map_session_data>) -{ - Packet_Fixed<0x00c1> fixed; - RecvResult rv = recv_fpacket<0x00c1, 2>(s, fixed); - if (rv != RecvResult::Complete) - return rv; - - Packet_Fixed<0x00c2> fixed_c2; - fixed_c2.users = map_getusers(); - send_fpacket<0x00c2, 6>(s, fixed_c2); - - return rv; -} - -/*========================================== - * - *------------------------------------------ - */ -static RecvResult clif_parse_ActionRequest(Session *s, dumb_ptr<map_session_data> sd) { Packet_Fixed<0x0089> fixed; @@ -3904,7 +3754,7 @@ RecvResult clif_parse_ActionRequest(Session *s, dumb_ptr<map_session_data> sd) { case DamageType::NORMAL: case DamageType::CONTINUOUS: - if (bool(sd->status.option & Option::HIDE)) + if (bool(sd->status.option & Opt0::HIDE)) return rv; if (!battle_config.skill_delay_attack_enable) { @@ -4046,7 +3896,7 @@ RecvResult clif_parse_Wis(Session *s, dumb_ptr<map_session_data> sd) if (dstsd->sess == s) { ZString mes = "You cannot page yourself."_s; - clif_wis_message(s, wisp_server_name, mes); + clif_wis_message(s, WISP_SERVER_NAME, mes); } else { @@ -4199,15 +4049,16 @@ RecvResult clif_parse_EquipItem(Session *s, dumb_ptr<map_session_data> sd) if (sd->npc_id) return rv; - if (sd->inventory_data[index]) + OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[index]) { EPOS epos = fixed.epos_ignored; - if (sd->inventory_data[index]->type == ItemType::ARROW) + if (sdidi->type == ItemType::ARROW) epos = EPOS::ARROW; // Note: the EPOS argument to pc_equipitem is actually ignored pc_equipitem(sd, index, epos); } + OMATCH_END (); return rv; } @@ -4816,6 +4667,60 @@ RecvResult clif_parse_PartyMessage(Session *s, dumb_ptr<map_session_data> sd) return rv; } +void clif_sendallquest(dumb_ptr<map_session_data> sd) +{ + int i; + QuestId questid; + if (!sd) + return; + + if (!sd->sess) + return; + + Session *s = sd->sess; + Packet_Head<0x0215> head_215; + std::vector<Packet_Repeat<0x0215>> repeat_215; + + assert (sd->status.global_reg_num < GLOBAL_REG_NUM); + for (QuestId q = wrap<QuestId>(0); q < wrap<QuestId>(-1); q = next(q)) + { + P<struct quest_data> quest_data_ = TRY_UNWRAP(questdb_exists(q), continue); + for (i = 0; i < sd->status.global_reg_num; i++) + { + if (sd->status.global_reg[i].str == quest_data_->quest_vr) + { + int val = ((sd->status.global_reg[i].value & (((1 << quest_data_->quest_mask) - 1) << (quest_data_->quest_shift * quest_data_->quest_mask))) >> (quest_data_->quest_shift * quest_data_->quest_mask)); + Packet_Repeat<0x0215> info; + info.variable = unwrap<QuestId>(quest_data_->questid); + info.value = val; + repeat_215.push_back(info); + break; + } + } + } + + send_vpacket<0x0215, 4, 6>(s, head_215, repeat_215); + return; +} + +void clif_sendquest(dumb_ptr<map_session_data> sd, QuestId questid, int value) +{ + if (!sd) + return; + + if (!sd->sess) + return; + + Session *s = sd->sess; + + Packet_Fixed<0x0214> fixed; + fixed.variable = unwrap<QuestId>(questid); + fixed.value = value; + send_fpacket<0x0214, 8>(s, fixed); + return; +} + + func_table clif_parse_func_table[0x0220] = { {0, 10, nullptr, }, // 0x0000 @@ -5011,7 +4916,7 @@ func_table clif_parse_func_table[0x0220] = {0, 5, nullptr, }, // 0x00be {1000, 3, clif_parse_Emotion, }, // 0x00bf {0, 7, nullptr, }, // 0x00c0 - {0, 2, clif_parse_HowManyConnections, }, // 0x00c1 + {0, 2, nullptr, }, // 0x00c1 {0, 6, nullptr, }, // 0x00c2 {0, 8, nullptr, }, // 0x00c3 {0, 6, nullptr, }, // 0x00c4 @@ -5431,12 +5336,10 @@ uint16_t clif_check_packet_flood(Session *s, int cmd) // They are flooding if (tick < sd->flood_rates[cmd] + rate) { - TimeT now = TimeT::now(); - // If it's a nasty flood we log and possibly kick - if (now > sd->packet_flood_reset_due) + if (tick > sd->packet_flood_reset_due) { - sd->packet_flood_reset_due = static_cast<time_t>(now) + battle_config.packet_spam_threshold; + sd->packet_flood_reset_due = tick + battle_config.packet_spam_threshold; sd->packet_flood_in = 0; } @@ -5638,7 +5541,6 @@ unknown_packet: PRINTF("\nclif_parse: session #%d, packet 0x%x, lenght %zu\n"_fmt, s, packet_id, packet_avail(s)); { - ZString packet_txt = "save/packet.txt"_s; if (sd && sd->state.auth) { PRINTF("Unknown packet: Account ID %d, character ID %d, player name %s.\n"_fmt, @@ -5650,35 +5552,27 @@ unknown_packet: else PRINTF("Unknown packet (unknown)\n"_fmt); - io::AppendFile fp(packet_txt); - if (!fp.is_open()) - { - PRINTF("clif.c: cant write [%s] !!! data is lost !!!\n"_fmt, - packet_txt); - return; - } - else { timestamp_seconds_buffer now; stamp_time(now); if (sd && sd->state.auth) { - FPRINTF(fp, + FPRINTF(stderr, "%s\nPlayer with account ID %d (character ID %d, player name %s) sent wrong packet:\n"_fmt, now, sd->status_key.account_id, sd->status_key.char_id, sd->status_key.name); } else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified) - FPRINTF(fp, + FPRINTF(stderr, "%s\nUnauthenticated player with account ID %d sent wrong packet:\n"_fmt, now, sd->bl_id); else - FPRINTF(fp, + FPRINTF(stderr, "%s\nUnknown connection sent wrong packet:\n"_fmt, now); - packet_dump(fp, s); + packet_dump(s); } } } @@ -5687,6 +5581,7 @@ unknown_packet: void do_init_clif(void) { - make_listen_port(map_port, SessionParsers{.func_parse= clif_parse, .func_delete= clif_delete}); + make_listen_port(map_conf.map_port, SessionParsers{.func_parse= clif_parse, .func_delete= clif_delete}); } +} // namespace map } // namespace tmwa diff --git a/src/map/clif.hpp b/src/map/clif.hpp index adb4889..5117ff3 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -20,34 +20,26 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include "fwd.hpp" +#include "../mmo/clif.t.hpp" -#include "clif.t.hpp" +#include "fwd.hpp" #include <functional> -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" +#include "../high/mmo.hpp" #include "../net/timer.t.hpp" -#include "../mmo/fwd.hpp" -#include "../mmo/mmo.hpp" - #include "battle.t.hpp" #include "map.t.hpp" #include "pc.t.hpp" -#include "skill.t.hpp" +#include "../mmo/skill.t.hpp" namespace tmwa { -void clif_setip(IP4Address); -void clif_setport(int); - -IP4Address clif_getip(void); -int clif_getport(void); +namespace map +{ int clif_countusers(void); void clif_setwaitclose(Session *); @@ -57,7 +49,6 @@ int clif_charselectok(BlockId); int clif_dropflooritem(dumb_ptr<flooritem_data>); int clif_clearflooritem(dumb_ptr<flooritem_data>, Session *); int clif_clearchar(dumb_ptr<block_list>, BeingRemoveWhy); // area or fd -int clif_clearchar_delay(tick_t, dumb_ptr<block_list>, BeingRemoveWhy); void clif_clearchar_id(BlockId, BeingRemoveWhy, Session *); int clif_spawnpc(dumb_ptr<map_session_data>); //area int clif_spawnnpc(dumb_ptr<npc_data>); // area @@ -87,11 +78,11 @@ void clif_delitem(dumb_ptr<map_session_data>, IOff0, int); //self int clif_updatestatus(dumb_ptr<map_session_data>, SP); //self int clif_damage(dumb_ptr<block_list>, dumb_ptr<block_list>, tick_t, interval_t, interval_t, - int, int, DamageType, int); // area + int, int, DamageType); // area inline int clif_takeitem(dumb_ptr<block_list> src, dumb_ptr<block_list> dst) { - return clif_damage(src, dst, tick_t(), interval_t::zero(), interval_t::zero(), 0, 0, DamageType::TAKEITEM, 0); + return clif_damage(src, dst, tick_t(), interval_t::zero(), interval_t::zero(), 0, 0, DamageType::TAKEITEM); } int clif_changelook(dumb_ptr<block_list>, LOOK, int); // area void clif_changelook_accessories(dumb_ptr<block_list> bl, dumb_ptr<map_session_data> dst); // area or target; list gloves, boots etc. @@ -105,6 +96,7 @@ int clif_changeoption(dumb_ptr<block_list>); // area int clif_useitemack(dumb_ptr<map_session_data>, IOff0, int, int); // self void clif_emotion(dumb_ptr<block_list> bl, int type); +void clif_emotion_towards(dumb_ptr<block_list> bl, dumb_ptr<block_list> target, int type); void clif_sitting(Session *, dumb_ptr<map_session_data> sd); // trade @@ -119,12 +111,12 @@ int clif_tradecancelled(dumb_ptr<map_session_data> sd); int clif_tradecompleted(dumb_ptr<map_session_data> sd, int fail); // storage -int clif_storageitemlist(dumb_ptr<map_session_data> sd, Storage *stor); +int clif_storageitemlist(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor); int clif_storageequiplist(dumb_ptr<map_session_data> sd, - Storage *stor); + Borrowed<Storage> stor); int clif_updatestorageamount(dumb_ptr<map_session_data> sd, - Storage *stor); -int clif_storageitemadded(dumb_ptr<map_session_data> sd, Storage *stor, + Borrowed<Storage> stor); +int clif_storageitemadded(dumb_ptr<map_session_data> sd, Borrowed<Storage> stor, SOff0 index, int amount); int clif_storageitemremoved(dumb_ptr<map_session_data> sd, SOff0 index, int amount); @@ -136,8 +128,6 @@ void clif_pcoutsight(dumb_ptr<block_list>, dumb_ptr<map_session_data>); void clif_mobinsight(dumb_ptr<block_list>, dumb_ptr<mob_data>); void clif_moboutsight(dumb_ptr<block_list>, dumb_ptr<mob_data>); -int clif_skillinfo(dumb_ptr<map_session_data> sd, SkillID skillid, int type, - int range); void clif_skillinfoblock(dumb_ptr<map_session_data> sd); int clif_skillup(dumb_ptr<map_session_data> sd, SkillID skill_num); @@ -157,8 +147,6 @@ void clif_wis_end(Session *s, int flag); void clif_itemlist(dumb_ptr<map_session_data> sd); void clif_equiplist(dumb_ptr<map_session_data> sd); -int clif_mvp_effect(dumb_ptr<map_session_data> sd); - int clif_movetoattack(dumb_ptr<map_session_data> sd, dumb_ptr<block_list> bl); // party @@ -187,6 +175,10 @@ int clif_GM_kick(dumb_ptr<map_session_data> sd, dumb_ptr<map_session_data> tsd, int type); int clif_foreachclient(std::function<void(dumb_ptr<map_session_data>)>); +// quest +void clif_sendallquest(dumb_ptr<map_session_data> sd); +void clif_sendquest(dumb_ptr<map_session_data> sd, QuestId questid, int value); void do_init_clif(void); +} // namespace map } // namespace tmwa diff --git a/src/map/clif.t.hpp b/src/map/clif.t.hpp deleted file mode 100644 index 1789ee8..0000000 --- a/src/map/clif.t.hpp +++ /dev/null @@ -1,717 +0,0 @@ -#pragma once -// clif.t.hpp - Network interface to the client. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> -// -// 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 <http://www.gnu.org/licenses/>. - -#include "fwd.hpp" - -#include <cstdint> - -#include "../ints/little.hpp" - -#include "../compat/iter.hpp" - -#include "../generic/enum.hpp" - -#include "../mmo/consts.hpp" -#include "../mmo/enums.hpp" - - -namespace tmwa -{ -namespace e -{ -// [Fate] status.option properties. These are persistent status changes. -// IDs that are not listed are not used in the code (to the best of my knowledge) -enum class Option : uint16_t -{ - ZERO = 0x0000, - - // [Fate] This is the GM `@hide' flag - HIDE = 0x0040, - // [Fate] Complete invisibility to other clients - INVISIBILITY = 0x1000, - - // ? - REAL_ANY_HIDE = HIDE, -}; -enum class Opt1 : uint16_t -{ - ZERO = 0, - _stone1 = 1, - _freeze = 2, - _stan = 3, - _sleep = 4, - _stone6 = 6, -}; -enum class Opt2 : uint16_t -{ - ZERO = 0x0000, - _poison = 0x0001, - _curse = 0x0002, - _silence = 0x0004, - BLIND = 0x0010, - _speedpotion0 = 0x0020, - _signumcrucis = 0x0040, - _atkpot = 0x0080, - _heal = 0x0100, - _slowpoison = 0x0200, -}; -enum class Opt3 : uint16_t -{ - ZERO = 0x0000, - _concentration = 0x0001, - _overthrust = 0x0002, - _energycoat = 0x0004, - _explosionspirits = 0x0008, - _steelbody = 0x0010, - _berserk = 0x0080, - - _marionette = 0x0400, - _assumptio = 0x0800, -}; - -ENUM_BITWISE_OPERATORS(Option) -ENUM_BITWISE_OPERATORS(Opt2) -ENUM_BITWISE_OPERATORS(Opt3) -} -using e::Option; -using e::Opt1; -using e::Opt2; -using e::Opt3; - - -enum class ItemType : uint8_t -{ - USE = 0, // in eA, healing only - _1 = 1, // unused - _2 = 2, // in eA, other usable items - JUNK = 3, // "useless" items (e.g. quests) - WEAPON = 4, // all weapons - ARMOR = 5, // all other equipment - _6 = 6, // in eA, card - _7 = 7, // in eA, pet egg - _8 = 8, // in eA, pet equipment - _9 = 9, // unused - ARROW = 10, // ammo - _11 = 11, // in eA, delayed use (special script) -}; - -enum class BeingRemoveWhy : uint8_t -{ - // general disappearance - GONE = 0, - // only case handled specially in client - DEAD = 1, - QUIT = 2, - WARPED = 3, - // handled specially in clif_clearchar - sent as 0 over network - DISGUISE = 9, - - // handled speciall in mob_warp - not actually sent over network - NEGATIVE1 = 0xff, -}; - -enum class PickupFail : uint8_t -{ - OKAY = 0, - BAD_ITEM = 1, - TOO_HEAVY = 2, - TOO_FAR = 3, - INV_FULL = 4, - STACK_FULL = 5, - DROP_STEAL = 6, -}; - -// this is used for both input and output -// different values are valid in 0x0089 vs 0x008a -enum class DamageType : uint8_t -{ - NORMAL = 0x00, - TAKEITEM = 0x01, - SIT = 0x02, - STAND = 0x03, - RETURNED = 0x04, - CONTINUOUS = 0x07, - DOUBLED = 0x08, - CRITICAL = 0x0a, - FLEE2 = 0x0b, -}; - -enum class LOOK : uint8_t -{ - BASE = 0, - HAIR = 1, - WEAPON = 2, - HEAD_BOTTOM = 3, - HEAD_TOP = 4, - HEAD_MID = 5, - HAIR_COLOR = 6, - CLOTHES_COLOR = 7, - SHIELD = 8, - SHOES = 9, - GLOVES = 10, - CAPE = 11, - MISC1 = 12, - MISC2 = 13, - - COUNT, -}; - -// Note: there is also a typedef by this name in <dirent.h> -// but we should be fine since we never include it. -// (in the long term we should still rename this though) -enum class DIR : uint8_t -{ - S = 0, - SW = 1, - W = 2, - NW = 3, - N = 4, - NE = 5, - E = 6, - SE = 7, - - COUNT, -}; - -constexpr -earray<int, DIR, DIR::COUNT> dirx //= -{{ - 0, -1, -1, -1, 0, 1, 1, 1, -}}, diry //= -{{ - 1, 1, 0, -1, -1, -1, 0, 1, -}}; - -constexpr -bool dir_is_diagonal(DIR d) -{ - return static_cast<uint8_t>(d) & 1; -} - - -enum class SP : uint16_t -{ - // sent to client - SPEED = 0, - - // when used as "no stat" - ZERO = 0, - - // sent to client - BASEEXP = 1, - // sent to client - JOBEXP = 2, -#if 0 - KARMA = 3, -#endif - - // sent to client - HP = 5, - // sent to client - MAXHP = 6, - // sent to client - SP = 7, - // sent to client - MAXSP = 8, - // sent to client - STATUSPOINT = 9, - - // sent to client - BASELEVEL = 11, - // sent to client - SKILLPOINT = 12, - // sent to client - STR = 13, - // sent to client - AGI = 14, - // sent to client - VIT = 15, - // sent to client - INT = 16, - // sent to client - DEX = 17, - // sent to client - LUK = 18, - CLASS = 19, - // sent to client - ZENY = 20, - SEX = 21, - // sent to client - NEXTBASEEXP = 22, - // sent to client - NEXTJOBEXP = 23, - // sent to client - WEIGHT = 24, - // sent to client - MAXWEIGHT = 25, - - // sent to client - USTR = 32, - // sent to client - UAGI = 33, - // sent to client - UVIT = 34, - // sent to client - UINT = 35, - // sent to client - UDEX = 36, - // sent to client - ULUK = 37, - - // sent to client - ATK1 = 41, - // sent to client - ATK2 = 42, - // sent to client - MATK1 = 43, - // sent to client - MATK2 = 44, - // sent to client - DEF1 = 45, - // sent to client - DEF2 = 46, - // sent to client - MDEF1 = 47, - // sent to client - MDEF2 = 48, - // sent to client - HIT = 49, - // sent to client - FLEE1 = 50, - // sent to client - FLEE2 = 51, - // sent to client - CRITICAL = 52, - // sent to client - ASPD = 53, - - // sent to client - JOBLEVEL = 55, - -#if 0 - PARTNER = 57, - CART = 58, - FAME = 59, - UNBREAKABLE = 60, -#endif - - DEAF = 70, - - // sent to client - GM = 500, - - // sent to client - ATTACKRANGE = 1000, -#if 0 - ATKELE = 1001, -#endif -#if 0 - DEFELE = 1002, -#endif -#if 0 - CASTRATE = 1003, -#endif - MAXHPRATE = 1004, -#if 0 - MAXSPRATE = 1005, -#endif -#if 0 - SPRATE = 1006, -#endif - -#if 0 - ADDEFF = 1012, -#endif -#if 0 - RESEFF = 1013, -#endif - BASE_ATK = 1014, - ASPD_RATE = 1015, - HP_RECOV_RATE = 1016, -#if 0 - SP_RECOV_RATE = 1017, -#endif -#if 0 - SPEED_RATE = 1018, -#endif - CRITICAL_DEF = 1019, -#if 0 - NEAR_ATK_DEF = 1020, -#endif -#if 0 - LONG_ATK_DEF = 1021, -#endif -#if 0 - DOUBLE_RATE = 1022, -#endif - DOUBLE_ADD_RATE = 1023, -#if 0 - MATK = 1024, -#endif -#if 0 - MATK_RATE = 1025, -#endif -#if 0 - IGNORE_DEF_ELE = 1026, -#endif -#if 0 - IGNORE_DEF_RACE = 1027, -#endif -#if 0 - ATK_RATE = 1028, -#endif - SPEED_ADDRATE = 1029, -#if 0 - ASPD_ADDRATE = 1030, -#endif -#if 0 - MAGIC_ATK_DEF = 1031, -#endif -#if 0 - MISC_ATK_DEF = 1032, -#endif -#if 0 - IGNORE_MDEF_ELE = 1033, -#endif -#if 0 - IGNORE_MDEF_RACE = 1034, -#endif - -#if 0 - PERFECT_HIT_RATE = 1038, -#endif -#if 0 - PERFECT_HIT_ADD_RATE = 1039, -#endif -#if 0 - CRITICAL_RATE = 1040, -#endif -#if 0 - GET_ZENY_NUM = 1041, -#endif -#if 0 - ADD_GET_ZENY_NUM = 1042, -#endif - -#if 0 - ADD_MONSTER_DROP_ITEM = 1047, -#endif -#if 0 - DEF_RATIO_ATK_ELE = 1048, -#endif -#if 0 - DEF_RATIO_ATK_RACE = 1049, -#endif -#if 0 - ADD_SPEED = 1050, -#endif -#if 0 - HIT_RATE = 1051, -#endif -#if 0 - FLEE_RATE = 1052, -#endif -#if 0 - FLEE2_RATE = 1053, -#endif - DEF_RATE = 1054, - DEF2_RATE = 1055, -#if 0 - MDEF_RATE = 1056, -#endif -#if 0 - MDEF2_RATE = 1057, -#endif -#if 0 - SPLASH_RANGE = 1058, -#endif -#if 0 - SPLASH_ADD_RANGE = 1059, -#endif - - HP_DRAIN_RATE = 1061, -#if 0 - SP_DRAIN_RATE = 1062, -#endif -#if 0 - SHORT_WEAPON_DAMAGE_RETURN = 1063, -#endif -#if 0 - LONG_WEAPON_DAMAGE_RETURN = 1064, -#endif - -#if 0 - ADDEFF2 = 1067, -#endif - BREAK_WEAPON_RATE = 1068, - BREAK_ARMOR_RATE = 1069, - ADD_STEAL_RATE = 1070, - MAGIC_DAMAGE_RETURN = 1071, -#if 0 - RANDOM_ATTACK_INCREASE = 1072, -#endif -}; - -constexpr -SP attr_to_sp(ATTR attr) -{ - return static_cast<SP>(static_cast<uint16_t>(attr) + static_cast<uint16_t>(SP::STR)); -} - -constexpr -ATTR sp_to_attr(SP sp) -{ - return static_cast<ATTR>(static_cast<uint16_t>(sp) - static_cast<uint16_t>(SP::STR)); -} - -constexpr -SP attr_to_usp(ATTR attr) -{ - return static_cast<SP>(static_cast<uint16_t>(attr) + static_cast<uint16_t>(SP::USTR)); -} - -constexpr -ATTR usp_to_attr(SP sp) -{ - return static_cast<ATTR>(static_cast<uint16_t>(sp) - static_cast<uint16_t>(SP::USTR)); -} - -constexpr -SP sp_to_usp(SP sp) -{ - return attr_to_usp(sp_to_attr(sp)); -} - -constexpr -SP usp_to_sp(SP sp) -{ - return attr_to_sp(usp_to_attr(sp)); -} - - -// xxxx xxxx xxyy yyyy yyyy dddd -struct NetPosition1 -{ - Byte data[3]; -}; - -struct Position1 -{ - uint16_t x, y; - DIR dir; -}; - -inline -bool native_to_network(NetPosition1 *network, Position1 native) -{ - uint16_t x = native.x; - uint16_t y = native.y; - uint8_t d = static_cast<uint8_t>(native.dir); - - uint8_t *p = reinterpret_cast<uint8_t *>(network); - p[0] = x >> 2; - p[1] = (x << 6) | ((y >> 4) & 0x3f); - p[2] = y << 4 | d; - - return x < 1024 && y < 1024 && d < 16; -} - -inline -bool network_to_native(Position1 *native, NetPosition1 network) -{ - const uint8_t *p = reinterpret_cast<const uint8_t *>(&network); - native->x = (p[0] & (0x3ff >> 2)) << 2 | p[1] >> (8 - 2); - native->y = (p[1] & (0x3ff >> 4)) << 4 | p[2] >> (8 - 4); - uint8_t d = p[2] & 0x0f; - native->dir = static_cast<DIR>(d); - return d < 8; -} - -// x0xx xxxx xxy0 yyyy yyyy x1xx xxxx xxy1 yyyy yyyy -struct NetPosition2 -{ - Byte data[5]; -}; - -struct Position2 -{ - uint16_t x0, y0; - uint16_t x1, y1; -}; - -inline -bool native_to_network(NetPosition2 *network, Position2 native) -{ - uint16_t x0 = native.x0; - uint16_t y0 = native.y0; - uint16_t x1 = native.x1; - uint16_t y1 = native.y1; - - uint8_t *p = reinterpret_cast<uint8_t *>(network); - p[0] = x0 >> 2; - p[1] = (x0 << 6) | ((y0 >> 4) & 0x3f); - p[2] = (y0 << 4) | ((x1 >> 6) & 0x0f); - p[3] = (x1 << 2) | ((y1 >> 8) & 0x03); - p[4] = y1; - - return x0 < 1024 && y0 < 1024 && x1 < 1024 && y1 < 1024; -} - -inline -bool network_to_native(Position2 *native, NetPosition2 network) -{ - const uint8_t *p = reinterpret_cast<const uint8_t *>(&network); - native->x0 = (p[0] & (0x3ff >> 2)) << 2 | p[1] >> (8 - 2); - native->y0 = (p[1] & (0x3ff >> 4)) << 4 | p[2] >> (8 - 4); - native->x1 = (p[2] & (0x3ff >> 6)) << 6 | p[3] >> (8 - 6); - native->y1 = (p[3] & (0x3ff >> 8)) << 8 | p[4] >> (8 - 8); - return true; -} - -struct IOff2; -struct SOff1; - -struct IOff0 -{ - uint16_t index; - - bool ok() const - { return get0() < MAX_INVENTORY; } - uint16_t get0() const - { return index; } - static IOff0 from(uint16_t i) - { return IOff0{i}; } - static IteratorPair<ValueIterator<IOff0>> iter() - { return {IOff0::from(0), IOff0::from(MAX_INVENTORY)}; } - friend uint16_t convert_for_printf(IOff0 i0) { return i0.index; } - - IOff0& operator ++() { ++index; return *this; } - friend bool operator == (IOff0 l, IOff0 r) { return l.index == r.index; } - friend bool operator != (IOff0 l, IOff0 r) { return !(l == r); } - IOff2 shift() const; - - IOff0() : index(0) {} -private: - explicit IOff0(uint16_t i) : index(i) {} -}; - -struct SOff0 -{ - uint16_t index; - - bool ok() const - { return get0() < MAX_STORAGE; } - uint16_t get0() const - { return index; } - static SOff0 from(uint16_t i) - { return SOff0{i}; } - static IteratorPair<ValueIterator<SOff0>> iter() - { return {SOff0::from(0), SOff0::from(MAX_STORAGE)}; } - friend uint16_t convert_for_printf(SOff0 s0) { return s0.index; } - - SOff0& operator ++() { ++index; return *this; } - friend bool operator == (SOff0 l, SOff0 r) { return l.index == r.index; } - friend bool operator != (SOff0 l, SOff0 r) { return !(l == r); } - SOff1 shift() const; - - SOff0() : index(0) {} -private: - explicit SOff0(uint16_t i) : index(i) {} -}; - -struct IOff2 -{ - uint16_t index; - - bool ok() const - { return get2() < MAX_INVENTORY; } - uint16_t get2() const - { return index - 2; } - static IOff2 from(uint16_t i) - { return IOff2{static_cast<uint16_t>(i + 2)}; } - static IteratorPair<ValueIterator<IOff2>> iter() - { return {IOff2::from(0), IOff2::from(MAX_INVENTORY)}; } - - IOff2& operator ++() { ++index; return *this; } - friend bool operator == (IOff2 l, IOff2 r) { return l.index == r.index; } - friend bool operator != (IOff2 l, IOff2 r) { return !(l == r); } - IOff0 unshift() const - { return IOff0::from(get2()); } - - IOff2() : index(0) {} -private: - explicit IOff2(uint16_t i) : index(i) {} -}; - -struct SOff1 -{ - uint16_t index; - - bool ok() const - { return get1() < MAX_STORAGE; } - uint16_t get1() const - { return index - 1; } - static SOff1 from(uint16_t i) - { return SOff1{static_cast<uint16_t>(i + 1)}; } - static IteratorPair<ValueIterator<SOff1>> iter() - { return {SOff1::from(0), SOff1::from(MAX_STORAGE)}; } - - SOff1& operator ++() { ++index; return *this; } - friend bool operator == (SOff1 l, SOff1 r) { return l.index == r.index; } - friend bool operator != (SOff1 l, SOff1 r) { return !(l == r); } - SOff0 unshift() const - { return SOff0::from(get1()); } - - SOff1() : index(0) {} -private: - explicit SOff1(uint16_t i) : index(i) {} -}; - -inline IOff2 IOff0::shift() const -{ return IOff2::from(get0()); } -inline SOff1 SOff0::shift() const -{ return SOff1::from(get0()); } - -inline -bool native_to_network(Little16 *network, IOff2 native) -{ - return native_to_network(network, native.index); -} - -inline -bool network_to_native(IOff2 *native, Little16 network) -{ - return network_to_native(&native->index, network); -} - -inline -bool native_to_network(Little16 *network, SOff1 native) -{ - return native_to_network(network, native.index); -} - -inline -bool network_to_native(SOff1 *native, Little16 network) -{ - return network_to_native(&native->index, network); -} -} // namespace tmwa diff --git a/src/map/magic-expr-eval.cpp b/src/map/consts.hpp index 9903600..a68d8e3 100644 --- a/src/map/magic-expr-eval.cpp +++ b/src/map/consts.hpp @@ -1,5 +1,5 @@ -#include "magic-expr-eval.hpp" -// magic-expr-eval.cpp - Utilities for evaluating magic. +#pragma once +// consts.hpp - Constants for tmwa-map. // // Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> // @@ -18,12 +18,17 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include "../poison.hpp" +#include "fwd.hpp" + +#include "../ints/udl.hpp" + +#include "../mmo/ids.hpp" namespace tmwa { -namespace magic +namespace map { -} // namespace magic +constexpr BlockId MAX_FLOORITEM = wrap<BlockId>(500000_u32); +} // namespace map } // namespace tmwa diff --git a/src/map/fwd.hpp b/src/map/fwd.hpp index 79bbbcd..911d566 100644 --- a/src/map/fwd.hpp +++ b/src/map/fwd.hpp @@ -20,11 +20,38 @@ #include "../sanity.hpp" +#include <cstdint> + +#include "../ints/fwd.hpp" // rank 1 +#include "../range/fwd.hpp" // rank 1 +#include "../strings/fwd.hpp" // rank 1 +#include "../compat/fwd.hpp" // rank 2 +#include "../generic/fwd.hpp" // rank 3 +#include "../io/fwd.hpp" // rank 4 +#include "../net/fwd.hpp" // rank 5 +#include "../sexpr/fwd.hpp" // rank 5 +#include "../mmo/fwd.hpp" // rank 6 +#include "../proto2/fwd.hpp" // rank 8 +#include "../high/fwd.hpp" // rank 9 +#include "../wire/fwd.hpp" // rank 9 +#include "../ast/fwd.hpp" // rank 10 +// map/fwd.hpp is rank ∞ because it is an executable + namespace tmwa { +namespace map +{ // meh, add more when I feel like it -class BlockId; +struct BattleConf; +struct MapConf; + +struct charid2nick; +struct map_abstract; +struct mob_db_; +struct skill_db_; +struct event_data; + struct block_list; struct map_session_data; struct npc_data; @@ -36,9 +63,13 @@ class npc_data_script; class npc_data_shop; class npc_data_warp; class npc_data_message; -struct NpcEvent; struct item_data; +struct quest_data; + +struct ScriptState; +struct str_data_t; +class SIR; namespace magic { @@ -55,5 +86,7 @@ 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 new file mode 100644 index 0000000..dce3906 --- /dev/null +++ b/src/map/globals.cpp @@ -0,0 +1,149 @@ +#include "globals.hpp" +// globals.cpp - Evil global variables for tmwa-map. +// +// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "../generic/intern-pool.hpp" + +#include "../io/write.hpp" + +#include "../proto2/net-Storage.hpp" + +#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" +#include "script-parse-internal.hpp" +#include "skill.hpp" + +#include "../poison.hpp" + + +namespace tmwa +{ + namespace map + { + BattleConf battle_config; + MapConf map_conf; + + // only used by intif.cpp + // and clif.cpp for the new on_delete stuff ... + Session *char_session; + int chrif_state; + std::map<MapName, RString> resnametable; + Map<ItemNameId, item_data> item_db; + Map<QuestId, quest_data> 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<RString, proc_t> procs; + std::map<RString, val_t> const_defm; + } // namespace magic_v2 + } // namespace magic + + DMap<BlockId, dumb_ptr<block_list>> id_db; + UPMap<MapName, map_abstract> maps_db; + DMap<CharName, dumb_ptr<map_session_data>> nick_db; + Map<CharId, charid2nick> charid_db; + int world_user_count = 0; + Array<dumb_ptr<block_list>, unwrap<BlockId>(MAX_FLOORITEM)> object; + BlockId first_free_object_id = BlockId(); + int save_settings = 0xFFFF; + int block_free_lock = 0; + std::vector<dumb_ptr<block_list>> block_free; + /// This is a dummy entry that is shared by all the linked lists, + /// so that any entry can unlink itself without worrying about + /// whether it was the the head of the list. + block_list bl_head; + std::unique_ptr<io::AppendFile> map_logfile; + long map_logfile_index; + mob_db_ mob_db[2001]; + std::list<AString> npc_srcs; + int npc_warp, npc_shop, npc_script, npc_mob; + BlockId npc_id = START_NPC_NUM; + Map<NpcEvent, struct event_data> ev_db; + DMap<NpcName, dumb_ptr<npc_data>> npcs_by_name; + // used for clock-based event triggers + // only tm_min, tm_hour, and tm_mday are used + tm ev_tm_b = + { + .tm_sec= 0, + .tm_min= -1, + .tm_hour= -1, + .tm_mday= -1, + .tm_mon= 0, + .tm_year= 0, + .tm_wday= 0, + .tm_yday= 0, + .tm_isdst= 0, + }; + Map<PartyId, PartyMost> party_db; + std::map<AccountId, GmLevel> gm_accountm; + tick_t natural_heal_tick, natural_heal_prev_tick; + interval_t natural_heal_diff_tick; + int last_save_fd; + bool save_flag; + Map<AccountId, Storage> storage_db; + + Map<RString, str_data_t> str_datam; + str_data_t LABEL_NEXTLINE_; + Map<ScriptLabel, int> scriptlabel_db; + std::set<ScriptLabel> probable_labels; + UPMap<RString, const ScriptBuffer> userfunc_db; + int parse_cmd_if = 0; + Option<Borrowed<str_data_t>> parse_cmdp = None; + InternPool variable_names; + // TODO: replace this whole mess with some sort of input stream that works + // a line at a time. + ZString startptr; + int startline; + int script_errors = 0; + DMap<SIR, int> mapreg_db; + Map<SIR, RString> mapregstr_db; + int mapreg_dirty = -1; + + std::vector<SkillID> skill_pool_skills; + earray<skill_db_, SkillID, SkillID::MAX_SKILL_DB> skill_db; + // these variables are set in the 'else' branches, + // and used in the (recursive) 'if' branch + // TODO kill it, kill it with fire. + BlockId skill_area_temp_id; + int skill_area_temp_hp; + + // Some other globals are not moved here, because they are + // large and initialized in-place and then *mostly* unmodified. + // + // src/map/atcommand.cpp: + // Map<XString, AtCommandInfo> atcommand_info; + // src/map/script-fun.cpp: + // BuiltinFunction builtin_functions[]; + // src/map/clif.cpp: + // func_table clif_parse_func_table[0x0220]; + // src/map/magic-expr.cpp: + // std::map<ZString, fun_t> functions; + // src/map/magic-stmt.cpp: + // std::map<ZString, op_t> operations; + } // namespace map +} // namespace tmwa diff --git a/src/map/globals.hpp b/src/map/globals.hpp new file mode 100644 index 0000000..b457b4e --- /dev/null +++ b/src/map/globals.hpp @@ -0,0 +1,109 @@ +#pragma once +// globals.hpp - Evil global variables for tmwa-map. +// +// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include <ctime> + +#include <list> +#include <map> +#include <memory> +#include <set> +#include <vector> + +#include "../ints/wrap.hpp" + +#include "../net/timer.t.hpp" + +#include "../mmo/skill.t.hpp" + +#include "consts.hpp" +#include "script-buffer.hpp" + + +namespace tmwa +{ + namespace map + { + extern BattleConf battle_config; + extern MapConf map_conf; + extern Session *char_session; + extern int chrif_state; + extern std::map<MapName, RString> resnametable; + extern Map<ItemNameId, item_data> item_db; + extern Map<QuestId, quest_data> 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<RString, proc_t> procs; + extern std::map<RString, val_t> const_defm; + } // namespace magic_v2 + } // namespace magic + extern DMap<BlockId, dumb_ptr<block_list>> id_db; + extern UPMap<MapName, map_abstract> maps_db; + extern DMap<CharName, dumb_ptr<map_session_data>> nick_db; + extern Map<CharId, charid2nick> charid_db; + extern int world_user_count; + extern Array<dumb_ptr<block_list>, unwrap<BlockId>(MAX_FLOORITEM)> object; + extern BlockId first_free_object_id; + extern int save_settings; + extern int block_free_lock; + extern std::vector<dumb_ptr<block_list>> block_free; + extern block_list bl_head; + extern std::unique_ptr<io::AppendFile> map_logfile; + extern long map_logfile_index; + extern mob_db_ mob_db[2001]; + extern std::list<AString> npc_srcs; + extern int npc_warp, npc_shop, npc_script, npc_mob; + extern BlockId npc_id; + extern Map<NpcEvent, event_data> ev_db; + extern DMap<NpcName, dumb_ptr<npc_data>> npcs_by_name; + extern tm ev_tm_b; + extern Map<PartyId, PartyMost> party_db; + extern std::map<AccountId, GmLevel> gm_accountm; + extern tick_t natural_heal_tick, natural_heal_prev_tick; + extern interval_t natural_heal_diff_tick; + extern int last_save_fd; + extern bool save_flag; + extern Map<AccountId, Storage> storage_db; + extern Map<RString, str_data_t> str_datam; + extern str_data_t LABEL_NEXTLINE_; + extern Map<ScriptLabel, int> scriptlabel_db; + extern std::set<ScriptLabel> probable_labels; + extern UPMap<RString, const ScriptBuffer> userfunc_db; + extern int parse_cmd_if; + extern Option<Borrowed<str_data_t>> parse_cmdp; + extern InternPool variable_names; + extern ZString startptr; + extern int startline; + extern int script_errors; + extern DMap<SIR, int> mapreg_db; + extern Map<SIR, RString> mapregstr_db; + extern int mapreg_dirty; + extern std::vector<SkillID> skill_pool_skills; + extern earray<skill_db_, SkillID, SkillID::MAX_SKILL_DB> skill_db; + extern BlockId skill_area_temp_id; + extern int skill_area_temp_hp; + } // namespace map +} // namespace tmwa diff --git a/src/map/grfio.cpp b/src/map/grfio.cpp index 4a1656b..3475108 100644 --- a/src/map/grfio.cpp +++ b/src/map/grfio.cpp @@ -33,19 +33,21 @@ #include "../strings/zstring.hpp" #include "../io/cxxstdio.hpp" +#include "../io/extract.hpp" #include "../io/read.hpp" -#include "../mmo/extract.hpp" -#include "../mmo/mmo.hpp" +#include "../high/extract_mmo.hpp" +#include "../high/mmo.hpp" + +#include "globals.hpp" #include "../poison.hpp" namespace tmwa { -static -std::map<MapName, RString> resnametable; - +namespace map +{ bool load_resnametable(ZString filename) { io::ReadFile in(filename); @@ -106,4 +108,5 @@ std::vector<uint8_t> grfio_reads(MapName rname) close(fd); return buffer; } +} // namespace map } // namespace tmwa diff --git a/src/map/grfio.hpp b/src/map/grfio.hpp index d2ab058..25e27ef 100644 --- a/src/map/grfio.hpp +++ b/src/map/grfio.hpp @@ -26,17 +26,16 @@ #include <vector> -#include "../strings/fwd.hpp" - -#include "../mmo/fwd.hpp" - namespace tmwa { +namespace map +{ bool load_resnametable(ZString filename); /// Load a resource into memory, subject to data/resnametable.txt. /// Normally, resourcename is xxx-y.gat and the file is xxx-y.wlk. /// Currently there is exactly one .wlk per .gat, but multiples are fine. std::vector<uint8_t> grfio_reads(MapName resourcename); +} // namespace map } // namespace tmwa diff --git a/src/map/intif.cpp b/src/map/intif.cpp index 314db24..a5709ef 100644 --- a/src/map/intif.cpp +++ b/src/map/intif.cpp @@ -29,16 +29,19 @@ #include "../io/cxxstdio.hpp" -#include "../net/packets.hpp" #include "../net/socket.hpp" -#include "../mmo/mmo.hpp" +#include "../high/mmo.hpp" #include "../proto2/char-map.hpp" +#include "../wire/packets.hpp" + #include "battle.hpp" +#include "battle_conf.hpp" #include "chrif.hpp" #include "clif.hpp" +#include "globals.hpp" #include "map.hpp" #include "party.hpp" #include "pc.hpp" @@ -49,6 +52,8 @@ namespace tmwa { +namespace map +{ //----------------------------------------------------------------- // inter serverへの送信 @@ -156,9 +161,8 @@ void intif_request_storage(AccountId account_id) } // 倉庫データ送信 -void intif_send_storage(Storage *stor) +void intif_send_storage(Borrowed<Storage> stor) { - nullpo_retv(stor); if (!char_session) return; @@ -386,7 +390,6 @@ int intif_parse_AccountReg(Session *, const Packet_Head<0x3804>& head, const std static int intif_parse_LoadStorage(Session *, const Packet_Payload<0x3810>& payload) { - Storage *stor; dumb_ptr<map_session_data> sd; sd = map_id2sd(account_to_block(payload.account_id)); @@ -397,7 +400,7 @@ int intif_parse_LoadStorage(Session *, const Packet_Payload<0x3810>& payload) payload.account_id); return 1; } - stor = account2storage(payload.account_id); + P<Storage> stor = account2storage(payload.account_id); if (stor->storage_status == 1) { // Already open.. lets ignore this update if (battle_config.error_log) @@ -463,9 +466,7 @@ void intif_parse_PartyInfo(Session *, const Packet_Head<0x3821>& head, bool has_ PartyId pi = head.party_id; PartyMost pm = option.party_most; - PartyPair pp; - pp.party_id = pi; - pp.party_most = ± + PartyPair pp{pi, borrow(pm)}; party_recv_info(pp); } @@ -697,4 +698,5 @@ RecvResult intif_parse(Session *s, uint16_t packet_id) } return rv; } +} // namespace map } // namespace tmwa diff --git a/src/map/intif.hpp b/src/map/intif.hpp index 5be61a9..ac68040 100644 --- a/src/map/intif.hpp +++ b/src/map/intif.hpp @@ -22,17 +22,11 @@ #include "fwd.hpp" -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" - -#include "../net/fwd.hpp" - -#include "../mmo/fwd.hpp" - namespace tmwa { +namespace map +{ RecvResult intif_parse(Session *, uint16_t packet_id); void intif_GMmessage(XString mes); @@ -44,7 +38,7 @@ void intif_saveaccountreg(dumb_ptr<map_session_data> sd); void intif_request_accountreg(dumb_ptr<map_session_data> sd); void intif_request_storage(AccountId account_id); -void intif_send_storage(Storage *stor); +void intif_send_storage(Borrowed<Storage> stor); void intif_create_party(dumb_ptr<map_session_data> sd, PartyName name); void intif_request_partyinfo(PartyId party_id); @@ -55,4 +49,5 @@ void intif_party_leave(PartyId party_id, AccountId accound_id); void intif_party_changemap(dumb_ptr<map_session_data> sd, int online); void intif_party_message(PartyId party_id, AccountId account_id, XString mes); void intif_party_checkconflict(PartyId party_id, AccountId account_id, CharName nick); +} // namespace map } // namespace tmwa diff --git a/src/map/itemdb.cpp b/src/map/itemdb.cpp index 50cc5c4..7dd725e 100644 --- a/src/map/itemdb.cpp +++ b/src/map/itemdb.cpp @@ -29,20 +29,24 @@ #include "../generic/db.hpp" #include "../io/cxxstdio.hpp" -#include "../io/read.hpp" +#include "../io/extract.hpp" +#include "../io/line.hpp" #include "../mmo/config_parse.hpp" -#include "../mmo/extract.hpp" #include "../mmo/extract_enums.hpp" +#include "../ast/item.hpp" + +#include "globals.hpp" +#include "script-parse.hpp" + #include "../poison.hpp" namespace tmwa { -static -Map<ItemNameId, struct item_data> item_db; - +namespace map +{ // Function declarations /*========================================== @@ -51,24 +55,24 @@ Map<ItemNameId, struct item_data> item_db; */ // name = item alias, so we should find items aliases first. if not found then look for "jname" (full name) static -void itemdb_searchname_sub(struct item_data *item, ItemName str, struct item_data **dst) +void itemdb_searchname_sub(Borrowed<struct item_data> item, ItemName str, Borrowed<Option<Borrowed<struct item_data>>> dst) { if (item->name == str) - *dst = item; + *dst = Some(item); } /*========================================== * 名前で検索 *------------------------------------------ */ -struct item_data *itemdb_searchname(XString str_) +Option<Borrowed<struct item_data>> itemdb_searchname(XString str_) { ItemName str = stringish<ItemName>(str_); if (XString(str) != str_) - return nullptr; - struct item_data *item = nullptr; + return None; + Option<P<struct item_data>> item = None; for (auto& pair : item_db) - itemdb_searchname_sub(&pair.second, str, &item); + itemdb_searchname_sub(borrow(pair.second), str, borrow(item)); return item; } @@ -76,7 +80,7 @@ struct item_data *itemdb_searchname(XString str_) * DBの存在確認 *------------------------------------------ */ -struct item_data *itemdb_exists(ItemNameId nameid) +Option<Borrowed<struct item_data>> itemdb_exists(ItemNameId nameid) { return item_db.search(nameid); } @@ -85,13 +89,16 @@ struct item_data *itemdb_exists(ItemNameId nameid) * DBの検索 *------------------------------------------ */ -struct item_data *itemdb_search(ItemNameId nameid) +Borrowed<struct item_data> itemdb_search(ItemNameId nameid) { - struct item_data *id = item_db.search(nameid); - if (id) + Option<P<struct item_data>> id_ = item_db.search(nameid); + OMATCH_BEGIN_SOME (id, id_) + { return id; + } + OMATCH_END (); - id = item_db.init(nameid); + P<struct item_data> id = item_db.init(nameid); id->nameid = nameid; id->value_buy = 10; @@ -123,10 +130,8 @@ int itemdb_isequip(ItemNameId nameid) * *------------------------------------------ */ -int itemdb_isequip2(struct item_data *data) +bool itemdb_isequip2(Borrowed<struct item_data> data) { - if (!data) - return false; ItemType type = data->type; return !(type == ItemType::USE || type == ItemType::_2 @@ -149,99 +154,64 @@ int itemdb_isequip3(ItemNameId nameid) bool itemdb_readdb(ZString filename) { - bool rv = true; - - int ln = 0, lines = 0; + io::LineCharReader in(filename); + if (!in.is_open()) { - io::ReadFile in(filename); - - if (!in.is_open()) - { - PRINTF("can't read %s\n"_fmt, filename); - return false; - } + PRINTF("can't read %s\n"_fmt, filename); + return false; + } - lines = 0; + int ln = 0; - AString line; - while (in.getline(line)) + while (true) + { + auto res = TRY_UNWRAP(ast::item::parse_item(in), + { + PRINTF("read %s done (count=%d)\n"_fmt, filename, ln); + return true; + }); + if (res.get_failure()) + PRINTF("%s\n"_fmt, res.get_failure()); + ast::item::ItemOrComment ioc = TRY_UNWRAP(std::move(res.get_success()), return false); + + MATCH_BEGIN (ioc) { - lines++; - if (is_comment(line)) - continue; - // a line is 17 normal fields followed by 2 {} fields - // the fields are separated by ", *", but there may be , - // in the {}. - - auto it = std::find(line.begin(), line.end(), '{'); - XString main_part = line.xislice_h(it).rstrip(); - // According to the code, tail_part may be empty. See later. - ZString tail_part = line.xislice_t(it); - - XString unused_slot_count; - item_data idv {}; - if (!extract( - main_part, record<','>( - &idv.nameid, - lstripping(&idv.name), - lstripping(&idv.jname), - lstripping(&idv.type), - lstripping(&idv.value_buy), - lstripping(&idv.value_sell), - lstripping(&idv.weight), - lstripping(&idv.atk), - lstripping(&idv.def), - lstripping(&idv.range), - lstripping(&idv.magic_bonus), - lstripping(&unused_slot_count), - lstripping(&idv.sex), - lstripping(&idv.equip), - lstripping(&idv.wlv), - lstripping(&idv.elv), - lstripping(&idv.look) - ) - ) - ) - { - PRINTF("%s:%d: error: bad item line: %s\n"_fmt, - filename, lines, line); - rv = false; - continue; - } - - ln++; - - struct item_data *id = itemdb_search(idv.nameid); - *id = std::move(idv); - if (id->value_buy == 0 && id->value_sell == 0) + MATCH_CASE (const ast::item::Comment&, c) { + (void)c; } - else if (id->value_buy == 0) + MATCH_CASE (const ast::item::Item&, item) { - id->value_buy = id->value_sell * 2; + ln++; + + item_data idv {}; + idv.nameid = item.id.data; + idv.name = item.name.data; + idv.jname = item.jname.data; + idv.type = item.type.data; + idv.value_buy = item.buy_price.data ?: item.sell_price.data * 2; + idv.value_sell = item.sell_price.data ?: item.buy_price.data / 2; + idv.weight = item.weight.data; + idv.atk = item.atk.data; + idv.def = item.def.data; + idv.range = item.range.data; + idv.magic_bonus = item.magic_bonus.data; + idv.sex = item.gender.data; + idv.equip = item.loc.data; + idv.wlv = item.wlv.data; + idv.elv = item.elv.data; + idv.look = item.view.data; + + idv.use_script = compile_script(STRPRINTF("use script %d"_fmt, idv.nameid), item.use_script, true); + idv.equip_script = compile_script(STRPRINTF("equip script %d"_fmt, idv.nameid), item.equip_script, true); + + Borrowed<struct item_data> id = itemdb_search(idv.nameid); + *id = std::move(idv); } - else if (id->value_sell == 0) - { - id->value_sell = id->value_buy / 2; - } - - id->use_script = nullptr; - id->equip_script = nullptr; - - if (!tail_part) - continue; - id->use_script = parse_script(tail_part, lines, true); - - tail_part = tail_part.xislice_t(std::find(tail_part.begin() + 1, tail_part.end(), '{')); - if (!tail_part) - continue; - id->equip_script = parse_script(tail_part, lines, true); } - PRINTF("read %s done (count=%d)\n"_fmt, filename, ln); + MATCH_END (); } - - return rv; } /*========================================== @@ -265,4 +235,5 @@ void do_final_itemdb(void) itemdb_final(&pair.second); item_db.clear(); } +} // namespace map } // namespace tmwa diff --git a/src/map/itemdb.hpp b/src/map/itemdb.hpp index 25b4dad..5e19c0b 100644 --- a/src/map/itemdb.hpp +++ b/src/map/itemdb.hpp @@ -23,14 +23,16 @@ #include "fwd.hpp" #include "../mmo/ids.hpp" -#include "../mmo/mmo.hpp" +#include "../high/mmo.hpp" #include "map.t.hpp" -#include "script.hpp" +#include "script-buffer.hpp" namespace tmwa { +namespace map +{ struct item_data { ItemNameId nameid; @@ -59,11 +61,11 @@ struct random_item_data }; inline -struct item_data *itemdb_searchname(ItemName) = delete; -struct item_data *itemdb_searchname(XString name); +Option<Borrowed<struct item_data>> itemdb_searchname(ItemName) = delete; +Option<Borrowed<struct item_data>> itemdb_searchname(XString name); // TODO this function should die -struct item_data *itemdb_search(ItemNameId nameid); -struct item_data *itemdb_exists(ItemNameId nameid); +Borrowed<struct item_data> itemdb_search(ItemNameId nameid); +Option<Borrowed<struct item_data>> itemdb_exists(ItemNameId nameid); inline ItemType itemdb_type(ItemNameId n) @@ -97,11 +99,12 @@ int itemdb_value_sell(ItemNameId n) } int itemdb_isequip(ItemNameId); -int itemdb_isequip2(struct item_data *); +bool itemdb_isequip2(Borrowed<struct item_data>); int itemdb_isequip3(ItemNameId); void itemdb_reload(void); void do_final_itemdb(void); bool itemdb_readdb(ZString filename); +} // namespace map } // namespace tmwa diff --git a/src/map/magic-expr-eval.hpp b/src/map/magic-expr-eval.hpp index 4529c04..e8ed4aa 100644 --- a/src/map/magic-expr-eval.hpp +++ b/src/map/magic-expr-eval.hpp @@ -28,6 +28,8 @@ namespace tmwa { +namespace map +{ namespace magic { // TODO soon kill this unlike I killed VAR @@ -48,4 +50,5 @@ namespace magic #define ARG_MAY_BE_AREA(x) (args[x].is<ValArea>() || args[x].is<ValArea>()) } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/magic-expr.cpp b/src/map/magic-expr.cpp index 674c850..197727e 100644 --- a/src/map/magic-expr.cpp +++ b/src/map/magic-expr.cpp @@ -33,7 +33,8 @@ #include "../generic/random.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" + +#include "../mmo/cxxstdio_enums.hpp" #include "battle.hpp" #include "itemdb.hpp" @@ -42,12 +43,15 @@ #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 @@ -56,14 +60,15 @@ void free_area(dumb_ptr<area_t> area) if (!area) return; - MATCH (*area) + MATCH_BEGIN (*area) { - CASE (const AreaUnion&, a) + MATCH_CASE (const AreaUnion&, a) { free_area(a.a_union[0]); free_area(a.a_union[1]); } } + MATCH_END (); area.delete_(); } @@ -71,109 +76,111 @@ void free_area(dumb_ptr<area_t> area) static dumb_ptr<area_t> dup_area(dumb_ptr<area_t> area) { - MATCH (*area) + MATCH_BEGIN (*area) { - CASE (const location_t&, loc) + MATCH_CASE (const location_t&, loc) { return dumb_ptr<area_t>::make(loc); } - CASE (const AreaUnion&, a) + 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<area_t>::make(u); } - CASE (const AreaRect&, rect) + MATCH_CASE (const AreaRect&, rect) { return dumb_ptr<area_t>::make(rect); } - CASE (const AreaBar&, bar) + MATCH_CASE (const AreaBar&, bar) { return dumb_ptr<area_t>::make(bar); } } + MATCH_END (); abort(); } void magic_copy_var(val_t *dest, const val_t *src) { - MATCH (*src) + MATCH_BEGIN (*src) { - // mumble mumble not a public API ... - default: + MATCH_DEFAULT () { abort(); } - CASE (const ValUndef&, s) + MATCH_CASE (const ValUndef&, s) { *dest = s; } - CASE (const ValInt&, s) + MATCH_CASE (const ValInt&, s) { *dest = s; } - CASE (const ValDir&, s) + MATCH_CASE (const ValDir&, s) { *dest = s; } - CASE (const ValString&, s) + MATCH_CASE (const ValString&, s) { *dest = ValString{s.v_string}; } - CASE (const ValEntityInt&, s) + MATCH_CASE (const ValEntityInt&, s) { *dest = s; } - CASE (const ValEntityPtr&, s) + MATCH_CASE (const ValEntityPtr&, s) { *dest = s; } - CASE (const ValLocation&, s) + MATCH_CASE (const ValLocation&, s) { *dest = s; } - CASE (const ValArea&, s) + MATCH_CASE (const ValArea&, s) { *dest = ValArea{dup_area(s.v_area)}; } - CASE (const ValSpell&, s) + MATCH_CASE (const ValSpell&, s) { *dest = s; } - CASE (const ValInvocationInt&, s) + MATCH_CASE (const ValInvocationInt&, s) { *dest = s; } - CASE (const ValInvocationPtr&, s) + MATCH_CASE (const ValInvocationPtr&, s) { *dest = s; } - CASE (const ValFail&, s) + MATCH_CASE (const ValFail&, s) { *dest = s; } - CASE (const ValNegative1&, s) + MATCH_CASE (const ValNegative1&, s) { *dest = s; } } + MATCH_END (); } void magic_clear_var(val_t *v) { - MATCH (*v) + MATCH_BEGIN (*v) { - CASE (ValString&, s) + MATCH_CASE (ValString&, s) { (void)s; } - CASE (const ValArea&, a) + MATCH_CASE (const ValArea&, a) { free_area(a.v_area); } } + MATCH_END (); } static @@ -212,63 +219,64 @@ void stringify(val_t *v) }}; AString buf; - MATCH (*v) + MATCH_BEGIN (*v) { - default: + MATCH_DEFAULT () { abort(); } - CASE (const ValUndef&, x) + MATCH_CASE (const ValUndef&, x) { (void)x; buf = "UNDEF"_s; } - CASE (const ValInt&, x) + MATCH_CASE (const ValInt&, x) { buf = STRPRINTF("%i"_fmt, x.v_int); } - CASE (const ValString&, x) + MATCH_CASE (const ValString&, x) { (void)x; return; } - CASE (const ValDir&, x) + MATCH_CASE (const ValDir&, x) { buf = dirs[x.v_dir]; } - CASE (const ValEntityPtr&, x) + MATCH_CASE (const ValEntityPtr&, x) { buf = show_entity(x.v_entity); } - CASE (const ValLocation&, x) + MATCH_CASE (const ValLocation&, x) { buf = STRPRINTF("<\"%s\", %d, %d>"_fmt, x.v_location.m->name_, x.v_location.x, x.v_location.y); } - CASE (const ValArea&, x) + MATCH_CASE (const ValArea&, x) { buf = "%area"_s; free_area(x.v_area); } - CASE (const ValSpell&, x) + MATCH_CASE (const ValSpell&, x) { buf = x.v_spell->name; } - CASE (const ValInvocationInt&, x) + MATCH_CASE (const ValInvocationInt&, x) { dumb_ptr<invocation> invocation_ = map_id2bl(x.v_iid)->is_spell(); buf = invocation_->spell->name; } - CASE (const ValInvocationPtr&, x) + MATCH_CASE (const ValInvocationPtr&, x) { dumb_ptr<invocation> invocation_ = x.v_invocation; buf = invocation_->spell->name; } } + MATCH_END (); *v = ValString{buf}; } @@ -310,14 +318,15 @@ void make_location(val_t *v) { if (ValArea *a = v->get_if<ValArea>()) { - MATCH (*a->v_area) + MATCH_BEGIN (*a->v_area) { - CASE (const location_t&, location) + MATCH_CASE (const location_t&, location) { free_area(a->v_area); *v = ValLocation{location}; } } + MATCH_END (); } } @@ -582,39 +591,41 @@ int fun_if_then_else(dumb_ptr<env_t>, val_t *result, Slice<val_t> args) return 0; } -void magic_area_rect(map_local **m, int *x, int *y, int *width, int *height, +Borrowed<map_local> magic_area_rect(int *x, int *y, int *width, int *height, area_t& area_) { - MATCH (area_) + MATCH_BEGIN (area_) { - CASE (const AreaUnion&, a) + MATCH_CASE (const AreaUnion&, a) { (void)a; abort(); } - CASE (const location_t&, a_loc) + MATCH_CASE (const location_t&, a_loc) { - *m = a_loc.m; + P<map_local> m = a_loc.m; *x = a_loc.x; *y = a_loc.y; *width = 1; *height = 1; + return m; } - CASE (const AreaRect&, a_rect) + MATCH_CASE (const AreaRect&, a_rect) { - *m = a_rect.loc.m; + P<map_local> 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; } - CASE (const AreaBar&, a_bar) + 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; - *m = a_bar.loc.m; + P<map_local> m = a_bar.loc.m; switch (a_bar.dir) { @@ -653,53 +664,54 @@ void magic_area_rect(map_local **m, int *x, int *y, int *width, int *height, *y = ty; *width = *height = 1; } + return m; } } + MATCH_END (); + abort(); } -int magic_location_in_area(map_local *m, int x, int y, dumb_ptr<area_t> area) +int magic_location_in_area(Borrowed<map_local> m, int x, int y, dumb_ptr<area_t> area) { - MATCH (*area) + MATCH_BEGIN (*area) { - CASE (const AreaUnion&, a) + 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]); } - CASE (const location_t&, a_loc) + MATCH_CASE (const location_t&, a_loc) { (void)a_loc; // TODO this can be simplified - map_local *am; int ax, ay, awidth, aheight; - magic_area_rect(&am, &ax, &ay, &awidth, &aheight, *area); + P<map_local> am = magic_area_rect(&ax, &ay, &awidth, &aheight, *area); return (am == m && (x >= ax) && (y >= ay) && (x < ax + awidth) && (y < ay + aheight)); } - CASE (const AreaRect&, a_rect) + MATCH_CASE (const AreaRect&, a_rect) { (void)a_rect; // TODO this is too complicated - map_local *am; int ax, ay, awidth, aheight; - magic_area_rect(&am, &ax, &ay, &awidth, &aheight, *area); + P<map_local> am = magic_area_rect(&ax, &ay, &awidth, &aheight, *area); return (am == m && (x >= ax) && (y >= ay) && (x < ax + awidth) && (y < ay + aheight)); } - CASE (const AreaBar&, a_bar) + MATCH_CASE (const AreaBar&, a_bar) { (void)a_bar; // TODO this is wrong - map_local *am; int ax, ay, awidth, aheight; - magic_area_rect(&am, &ax, &ay, &awidth, &aheight, *area); + P<map_local> 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(); } @@ -872,18 +884,17 @@ int fun_hash_entity(dumb_ptr<env_t>, val_t *result, Slice<val_t> args) // ret -1: not a string, ret 1: no such item, ret 0: OK int magic_find_item(Slice<val_t> args, int index, Item *item_, int *stackable) { - struct item_data *item_data; + Option<P<struct item_data>> item_data_ = None; int must_add_sequentially; if (args[index].is<ValInt>()) - item_data = itemdb_exists(wrap<ItemNameId>(static_cast<uint16_t>(ARGINT(index)))); + item_data_ = itemdb_exists(wrap<ItemNameId>(static_cast<uint16_t>(ARGINT(index)))); else if (args[index].is<ValString>()) - item_data = itemdb_searchname(ARGSTR(index)); + item_data_ = itemdb_searchname(ARGSTR(index)); else return -1; - if (!item_data) - return 1; + P<struct item_data> item_data = TRY_UNWRAP(item_data_, return 1); // Very elegant. must_add_sequentially = ( @@ -1082,22 +1093,21 @@ int fun_line_of_sight(dumb_ptr<env_t>, val_t *result, Slice<val_t> args) void magic_random_location(location_t *dest, dumb_ptr<area_t> area) { - MATCH (*area) + MATCH_BEGIN (*area) { - CASE (const AreaUnion&, a) + 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]); } - CASE (const location_t&, a_loc) + MATCH_CASE (const location_t&, a_loc) { (void)a_loc; // TODO this can be simplified - map_local *m; int x, y, w, h; - magic_area_rect(&m, &x, &y, &w, &h, *area); + P<map_local> m = magic_area_rect(&x, &y, &w, &h, *area); if (w <= 1) w = 1; @@ -1113,13 +1123,12 @@ void magic_random_location(location_t *dest, dumb_ptr<area_t> area) dest->x = pair.first; dest->y = pair.second; } - CASE (const AreaRect&, a_rect) + MATCH_CASE (const AreaRect&, a_rect) { (void)a_rect; // TODO this can be simplified - map_local *m; int x, y, w, h; - magic_area_rect(&m, &x, &y, &w, &h, *area); + P<map_local> m = magic_area_rect(&x, &y, &w, &h, *area); if (w <= 1) w = 1; @@ -1135,13 +1144,12 @@ void magic_random_location(location_t *dest, dumb_ptr<area_t> area) dest->x = pair.first; dest->y = pair.second; } - CASE (const AreaBar&, a_bar) + MATCH_CASE (const AreaBar&, a_bar) { (void)a_bar; // TODO this is wrong - map_local *m; int x, y, w, h; - magic_area_rect(&m, &x, &y, &w, &h, *area); + P<map_local> m = magic_area_rect(&x, &y, &w, &h, *area); if (w <= 1) w = 1; @@ -1158,6 +1166,7 @@ void magic_random_location(location_t *dest, dumb_ptr<area_t> area) dest->y = pair.second; } } + MATCH_END (); } static @@ -1228,7 +1237,7 @@ int fun_running_status_update(dumb_ptr<env_t>, val_t *result, Slice<val_t> args) static int fun_status_option(dumb_ptr<env_t>, val_t *result, Slice<val_t> args) { - *result = ValInt{(bool((ARGPC(0))->status.option & static_cast<Option>(ARGINT(1))))}; + *result = ValInt{(bool((ARGPC(0))->status.option & static_cast<Opt0>(ARGINT(1))))}; return 0; } @@ -1511,10 +1520,8 @@ int eval_location(dumb_ptr<env_t> env, location_t *dest, const e_location_t *exp && x.is<ValInt>() && y.is<ValInt>()) { MapName name = VString<15>(ZString(m.get_if<ValString>()->v_string)); - map_local *map_id = map_mapname2mapid(name); magic_clear_var(&m); - if (!map_id) - return 1; + P<map_local> map_id = TRY_UNWRAP(map_mapname2mapid(name), return 1); dest->m = map_id; dest->x = x.get_if<ValInt>()->v_int; dest->y = y.get_if<ValInt>()->v_int; @@ -1532,9 +1539,9 @@ int eval_location(dumb_ptr<env_t> env, location_t *dest, const e_location_t *exp static dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_) { - MATCH (expr_) + MATCH_BEGIN (expr_) { - CASE (const e_location_t&, a_loc) + MATCH_CASE (const e_location_t&, a_loc) { location_t loc; if (eval_location(env, &loc, &a_loc)) @@ -1546,7 +1553,7 @@ dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_) return dumb_ptr<area_t>::make(loc); } } - CASE (const ExprAreaUnion&, a) + MATCH_CASE (const ExprAreaUnion&, a) { AreaUnion u; bool fail = false; @@ -1568,7 +1575,7 @@ dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_) } return dumb_ptr<area_t>::make(u); } - CASE (const ExprAreaRect&, a_rect) + MATCH_CASE (const ExprAreaRect&, a_rect) { val_t width, height; magic_eval(env, &width, a_rect.width); @@ -1594,7 +1601,7 @@ dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_) return nullptr; } } - CASE (const ExprAreaBar&, a_bar) + MATCH_CASE (const ExprAreaBar&, a_bar) { val_t width, depth, dir; magic_eval(env, &width, a_bar.width); @@ -1626,6 +1633,7 @@ dumb_ptr<area_t> eval_area(dumb_ptr<env_t> env, const e_area_t& expr_) } } } + MATCH_END (); abort(); } @@ -1744,14 +1752,14 @@ int magic_signature_check(ZString opname, ZString funname, ZString signature, void magic_eval(dumb_ptr<env_t> env, val_t *dest, dumb_ptr<expr_t> expr) { - MATCH (*expr) + MATCH_BEGIN (*expr) { - CASE (const val_t&, e_val) + MATCH_CASE (const val_t&, e_val) { magic_copy_var(dest, &e_val); } - CASE (const e_location_t&, e_location) + MATCH_CASE (const e_location_t&, e_location) { location_t loc; if (eval_location(env, &loc, &e_location)) @@ -1759,14 +1767,14 @@ void magic_eval(dumb_ptr<env_t> env, val_t *dest, dumb_ptr<expr_t> expr) else *dest = ValLocation{loc}; } - CASE (const e_area_t&, e_area) + MATCH_CASE (const e_area_t&, e_area) { if (dumb_ptr<area_t> area = eval_area(env, e_area)) *dest = ValArea{area}; else *dest = ValFail(); } - CASE (const ExprFunApp&, e_funapp) + MATCH_CASE (const ExprFunApp&, e_funapp) { val_t arguments[MAX_ARGS]; int args_nr = e_funapp.args_nr; @@ -1800,12 +1808,12 @@ void magic_eval(dumb_ptr<env_t> env, val_t *dest, dumb_ptr<expr_t> expr) for (i = 0; i < args_nr; ++i) magic_clear_var(&arguments[i]); } - CASE (const ExprId&, e) + MATCH_CASE (const ExprId&, e) { val_t& v = env->VAR(e.e_id); magic_copy_var(dest, &v); } - CASE (const ExprField&, e_field) + MATCH_CASE (const ExprField&, e_field) { val_t v; int id = e_field.id; @@ -1833,6 +1841,7 @@ void magic_eval(dumb_ptr<env_t> env, val_t *dest, dumb_ptr<expr_t> expr) } } } + MATCH_END (); } int magic_eval_int(dumb_ptr<env_t> env, dumb_ptr<expr_t> expr) @@ -1861,4 +1870,5 @@ AString magic_eval_str(dumb_ptr<env_t> env, dumb_ptr<expr_t> expr) return result.get_if<ValString>()->v_string; } } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/magic-expr.hpp b/src/map/magic-expr.hpp index 294e665..055f37b 100644 --- a/src/map/magic-expr.hpp +++ b/src/map/magic-expr.hpp @@ -21,20 +21,16 @@ #include "fwd.hpp" -#include "../generic/fwd.hpp" - -#include "../range/fwd.hpp" - #include "../strings/zstring.hpp" #include "../strings/literal.hpp" -#include "../mmo/fwd.hpp" - #include "magic-interpreter.t.hpp" namespace tmwa { +namespace map +{ namespace magic { /* @@ -97,14 +93,15 @@ int magic_find_item(Slice<val_t> args, int index, Item *item, int *stackable); default: break; \ } -int magic_location_in_area(map_local *m, int x, int y, dumb_ptr<area_t> area); +int magic_location_in_area(Borrowed<map_local> m, int x, int y, dumb_ptr<area_t> area); /* Helper definitions for dealing with functions and operations */ int magic_signature_check(ZString opname, ZString funname, ZString signature, Slice<val_t> args, int line, int column); -void magic_area_rect(map_local **m, int *x, int *y, int *width, int *height, +Borrowed<map_local> 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 index 0d9db55..f53ddc8 100644 --- a/src/map/magic-expr.py +++ b/src/map/magic-expr.py @@ -1,7 +1,7 @@ class fun_t(object): __slots__ = ('_value') - name = 'tmwa::magic::fun_t' + name = 'tmwa::map::magic::fun_t' depth = 1 enabled = True @@ -31,8 +31,8 @@ class fun_t(object): ''' tests = [ - ('static_cast<tmwa::magic::fun_t *>(nullptr)', + ('static_cast<tmwa::map::magic::fun_t *>(nullptr)', '(fun_t *) nullptr'), - ('new tmwa::magic::fun_t{"name"_s, "sig"_s, \'\\0\', nullptr}', - 'regex:\(fun_t \*\) = \{->name = "name", ->signature = "sig", ->ret_ty = 0 \'\\\\000\', ->fun = (0x)?0}'), + ('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 index be9a61a..c2be363 100644 --- a/src/map/magic-interpreter-base.cpp +++ b/src/map/magic-interpreter-base.cpp @@ -25,10 +25,12 @@ #include "../strings/xstring.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" + +#include "../mmo/cxxstdio_enums.hpp" #include "../net/timer.hpp" +#include "globals.hpp" #include "magic.hpp" #include "magic-expr.hpp" #include "magic-interpreter.hpp" @@ -39,6 +41,8 @@ namespace tmwa { +namespace map +{ namespace magic { static @@ -78,9 +82,6 @@ void set_spell(val_t *v, dumb_ptr<spell_t> x) *v = ValSpell{x}; } -magic_conf_t magic_conf; /* Global magic conf */ -env_t magic_default_env = { &magic_conf, nullptr }; - AString magic_find_invocation(XString spellname) { auto it = magic_conf.spells_by_name.find(spellname); @@ -313,22 +314,22 @@ const effect_set_t *spellguard_check_sub(spellguard_check_t *check, if (guard == nullptr) return nullptr; - MATCH (*guard) + MATCH_BEGIN (*guard) { - CASE (const GuardCondition&, s) + MATCH_CASE (const GuardCondition&, s) { if (!magic_eval_int(env, s.s_condition)) return nullptr; } - CASE (const GuardComponents&, s) + MATCH_CASE (const GuardComponents&, s) { copy_components(&check->components, s.s_components); } - CASE (const GuardCatalysts&, s) + MATCH_CASE (const GuardCatalysts&, s) { copy_components(&check->catalysts, s.s_catalysts); } - CASE (const GuardChoice&, s) + MATCH_CASE (const GuardChoice&, s) { spellguard_check_t altcheck = *check; const effect_set_t *retval; @@ -350,15 +351,15 @@ const effect_set_t *spellguard_check_sub(spellguard_check_t *check, return spellguard_check_sub(check, s.s_alt, caster, env, near_miss); } - CASE (const GuardMana&, s) + MATCH_CASE (const GuardMana&, s) { check->mana += magic_eval_int(env, s.s_mana); } - CASE (const GuardCastTime&, s) + MATCH_CASE (const GuardCastTime&, s) { check->casttime += static_cast<interval_t>(magic_eval_int(env, s.s_casttime)); } - CASE (const effect_set_t&, s_effect) + MATCH_CASE (const effect_set_t&, s_effect) { if (spellguard_can_satisfy(check, caster, env, near_miss)) return &s_effect; @@ -366,6 +367,7 @@ const effect_set_t *spellguard_check_sub(spellguard_check_t *check, return nullptr; } } + MATCH_END (); return spellguard_check_sub(check, guard->next, caster, env, near_miss); } @@ -547,4 +549,5 @@ int spell_unbind(dumb_ptr<map_session_data> subject, dumb_ptr<invocation> invoca return 1; } } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/magic-interpreter-base.hpp b/src/map/magic-interpreter-base.hpp index 4bb41a0..7c00db0 100644 --- a/src/map/magic-interpreter-base.hpp +++ b/src/map/magic-interpreter-base.hpp @@ -21,20 +21,13 @@ #include "fwd.hpp" -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" - -#include "../mmo/fwd.hpp" - namespace tmwa { +namespace map +{ namespace magic { -extern magic_conf_t magic_conf; /* Global magic conf */ -extern env_t magic_default_env; /* Fake default environment */ - /** * Adds a component selection to a component holder (which may initially be nullptr) */ @@ -87,4 +80,5 @@ dumb_ptr<spell_t> magic_find_spell(XString invocation); void spell_update_location(dumb_ptr<invocation> invocation); } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/magic-interpreter.hpp b/src/map/magic-interpreter.hpp index 3bb600c..cbd92a9 100644 --- a/src/map/magic-interpreter.hpp +++ b/src/map/magic-interpreter.hpp @@ -19,39 +19,43 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include "fwd.hpp" - #include "magic-interpreter.t.hpp" +#include "fwd.hpp" + #include <cassert> #include <memory> -#include "../strings/fwd.hpp" #include "../strings/rstring.hpp" -#include "../generic/fwd.hpp" - #include "../sexpr/variant.hpp" #include "../net/timer.t.hpp" #include "../mmo/ids.hpp" -#include "../mmo/utils.hpp" #include "map.hpp" -#include "script.hpp" -#include "skill.t.hpp" +#include "script-buffer.hpp" +#include "../mmo/skill.t.hpp" namespace tmwa { +namespace map +{ namespace magic { struct location_t { - map_local *m; + Borrowed<map_local> 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<map_local> m_, int x_, int y_) : m(m_), x(x_), y(y_) {} }; struct AreaUnion @@ -622,4 +626,5 @@ struct proc_t {} }; } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/magic-interpreter.py b/src/map/magic-interpreter.py index cf17b1c..520ab37 100644 --- a/src/map/magic-interpreter.py +++ b/src/map/magic-interpreter.py @@ -1,6 +1,6 @@ class AreaUnion(object): __slots__ = ('_value') - name = 'tmwa::magic::AreaUnion' + name = 'tmwa::map::magic::AreaUnion' enabled = True def __init__(self, value): @@ -27,23 +27,23 @@ class area_t(object): using tmwa::operator "" _s; inline - tmwa::map_local *fake_map_local_x_dup_for_area_t(tmwa::ZString name) + tmwa::Borrowed<tmwa::map::map_local> fake_map_local_x_dup_for_area_t(tmwa::ZString name) { - auto *p = new tmwa::map_local{}; + auto *p = new tmwa::map::map_local{}; p->name_ = tmwa::stringish<tmwa::MapName>(name); - return p; + return tmwa::borrow(*p); } ''' tests = [ - ('tmwa::magic::area_t(tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456})', - '{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}}, size = 1}'), - ('tmwa::magic::area_t(tmwa::magic::AreaUnion{{tmwa::dumb_ptr<tmwa::magic::area_t>::make(tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456}), tmwa::dumb_ptr<tmwa::magic::area_t>::make(tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 321, 654})}})', - '{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::AreaUnion) = {{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}}, size = 1}, {<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 321, y = 654}}, size = 1}}}, size = 2}'), - ('tmwa::magic::area_t(tmwa::magic::AreaRect{tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456}, 789, 102})', - '{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::magic::AreaRect) = {loc = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}, width = 789, height = 102}}, size = 80478}'), - ('tmwa::magic::area_t(tmwa::magic::AreaBar{tmwa::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 42, 43}, 123, 456, tmwa::DIR::NW})', - '{<tmwa::sexpr::Variant<tmwa::magic::location_t, tmwa::magic::AreaUnion, tmwa::magic::AreaRect, tmwa::magic::AreaBar>> = {(tmwa::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}'), + ('tmwa::map::magic::area_t(tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(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<tmwa::map::magic::area_t>::make(tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 123, 456}), tmwa::dumb_ptr<tmwa::map::magic::area_t>::make(tmwa::map::magic::location_t{fake_map_local_x_dup_for_area_t("map"_s), 321, 654})}})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(tmwa::map::magic::AreaUnion) = {{<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(tmwa::map::magic::location_t) = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 123, y = 456}}, size = 1}, {<tmwa::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(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::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(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::sexpr::Variant<tmwa::map::magic::location_t, tmwa::map::magic::AreaUnion, tmwa::map::magic::AreaRect, tmwa::map::magic::AreaBar>> = {(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}'), ] @@ -55,47 +55,47 @@ class val_t(object): using tmwa::operator "" _s; inline - tmwa::map_local *fake_map_local_x_dup_for_val_t(tmwa::ZString name) + tmwa::Borrowed<tmwa::map::map_local> fake_map_local_x_dup_for_val_t(tmwa::ZString name) { - auto *p = new tmwa::map_local{}; + auto *p = new tmwa::map::map_local{}; p->name_ = tmwa::stringish<tmwa::MapName>(name); - return p; + return tmwa::borrow(*p); } ''' tests = [ - ('tmwa::magic::val_t(tmwa::magic::ValUndef{})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValUndef) = {<No data fields>}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValInt{42})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValInt) = {v_int = 42}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValDir{tmwa::DIR::NW})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValDir) = {v_dir = tmwa::DIR::NW}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValString{"Hello"_s})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValString) = {v_string = "Hello"}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValEntityInt{tmwa::wrap<tmwa::BlockId>(123)})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValEntityInt) = {v_eid = 123}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValEntityPtr{tmwa::dumb_ptr<tmwa::block_list>()})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValEntityPtr) = {v_entity = 0x0}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValLocation{tmwa::magic::location_t{fake_map_local_x_dup_for_val_t("map"_s), 42, 123}})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValLocation) = {v_location = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 42, y = 123}}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValArea{tmwa::dumb_ptr<tmwa::magic::area_t>()})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValArea) = {v_area = 0x0}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValSpell{tmwa::dumb_ptr<tmwa::magic::spell_t>()})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValSpell) = {v_spell = 0x0}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValInvocationInt{tmwa::wrap<tmwa::BlockId>(123)})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValInvocationInt) = {v_iid = 123}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValInvocationPtr{})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValInvocationPtr) = {v_invocation = 0x0}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValFail{})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValFail) = {<No data fields>}}, <No data fields>}'), - ('tmwa::magic::val_t(tmwa::magic::ValNegative1{})', - '{<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValNegative1) = {<No data fields>}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValUndef{})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValUndef) = {<No data fields>}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValInt{42})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValInt) = {v_int = 42}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValDir{tmwa::DIR::NW})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValDir) = {v_dir = tmwa::DIR::NW}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValString{"Hello"_s})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValString) = {v_string = "Hello"}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValEntityInt{tmwa::wrap<tmwa::BlockId>(123)})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValEntityInt) = {v_eid = 123}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValEntityPtr{tmwa::dumb_ptr<tmwa::map::block_list>()})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValEntityPtr) = {v_entity = 0x0}}, <No data fields>}'), + ('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::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValLocation) = {v_location = {m = (map_local *) = {->name = "map", ->xs = 0, ->ys = 0}, x = 42, y = 123}}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValArea{tmwa::dumb_ptr<tmwa::map::magic::area_t>()})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValArea) = {v_area = 0x0}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValSpell{tmwa::dumb_ptr<tmwa::map::magic::spell_t>()})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValSpell) = {v_spell = 0x0}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValInvocationInt{tmwa::wrap<tmwa::BlockId>(123)})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValInvocationInt) = {v_iid = 123}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValInvocationPtr{})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValInvocationPtr) = {v_invocation = 0x0}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValFail{})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValFail) = {<No data fields>}}, <No data fields>}'), + ('tmwa::map::magic::val_t(tmwa::map::magic::ValNegative1{})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValNegative1) = {<No data fields>}}, <No data fields>}'), ] class ExprAreaUnion(object): __slots__ = ('_value') - name = 'tmwa::magic::ExprAreaUnion' + name = 'tmwa::map::magic::ExprAreaUnion' enabled = True def __init__(self, value): @@ -119,14 +119,14 @@ class e_area_t(object): enabled = True tests = [ - ('tmwa::magic::e_area_t(tmwa::magic::e_location_t())', - '{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}'), - ('tmwa::magic::e_area_t(tmwa::magic::ExprAreaUnion{{tmwa::dumb_ptr<tmwa::magic::e_area_t>::make(tmwa::magic::e_location_t()), tmwa::dumb_ptr<tmwa::magic::e_area_t>::make(tmwa::magic::e_location_t())}})', - '{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::ExprAreaUnion) = {{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}, {<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}}}, <No data fields>}'), - ('tmwa::magic::e_area_t(tmwa::magic::ExprAreaRect{tmwa::magic::e_location_t(), tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::expr_t>()})', - '{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::ExprAreaRect) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, height = 0x0}}, <No data fields>}'), - ('tmwa::magic::e_area_t(tmwa::magic::ExprAreaBar{tmwa::magic::e_location_t(), tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::expr_t>()})', - '{<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::ExprAreaBar) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, depth = 0x0, dir = 0x0}}, <No data fields>}'), + ('tmwa::map::magic::e_area_t(tmwa::map::magic::e_location_t())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}'), + ('tmwa::map::magic::e_area_t(tmwa::map::magic::ExprAreaUnion{{tmwa::dumb_ptr<tmwa::map::magic::e_area_t>::make(tmwa::map::magic::e_location_t()), tmwa::dumb_ptr<tmwa::map::magic::e_area_t>::make(tmwa::map::magic::e_location_t())}})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::ExprAreaUnion) = {{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}, {<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}}}, <No data fields>}'), + ('tmwa::map::magic::e_area_t(tmwa::map::magic::ExprAreaRect{tmwa::map::magic::e_location_t(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>()})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::ExprAreaRect) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, height = 0x0}}, <No data fields>}'), + ('tmwa::map::magic::e_area_t(tmwa::map::magic::ExprAreaBar{tmwa::map::magic::e_location_t(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>()})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::ExprAreaBar) = {loc = {m = 0x0, x = 0x0, y = 0x0}, width = 0x0, depth = 0x0, dir = 0x0}}, <No data fields>}'), ] @@ -135,18 +135,18 @@ class expr_t(object): enabled = True tests = [ - ('tmwa::magic::expr_t(tmwa::magic::val_t(tmwa::magic::ValUndef()))', - '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::val_t) = {<tmwa::sexpr::Variant<tmwa::magic::ValUndef, tmwa::magic::ValInt, tmwa::magic::ValDir, tmwa::magic::ValString, tmwa::magic::ValEntityInt, tmwa::magic::ValEntityPtr, tmwa::magic::ValLocation, tmwa::magic::ValArea, tmwa::magic::ValSpell, tmwa::magic::ValInvocationInt, tmwa::magic::ValInvocationPtr, tmwa::magic::ValFail, tmwa::magic::ValNegative1>> = {(tmwa::magic::ValUndef) = {<No data fields>}}, <No data fields>}}, <No data fields>}'), - ('tmwa::magic::expr_t(tmwa::magic::e_location_t())', - '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}'), - ('tmwa::magic::expr_t(tmwa::magic::e_area_t(tmwa::magic::e_location_t()))', - '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::e_area_t) = {<tmwa::sexpr::Variant<tmwa::magic::e_location_t, tmwa::magic::ExprAreaUnion, tmwa::magic::ExprAreaRect, tmwa::magic::ExprAreaBar>> = {(tmwa::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}}, <No data fields>}'), - ('tmwa::magic::expr_t(tmwa::magic::ExprFunApp())', - '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::ExprFunApp) = {funp = (fun_t *) nullptr, line_nr = 0, column = 0, args_nr = 0, args = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, <No data fields>}'), - ('tmwa::magic::expr_t(tmwa::magic::ExprId{123})', - '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::ExprId) = {e_id = 123}}, <No data fields>}'), - ('tmwa::magic::expr_t(tmwa::magic::ExprField{tmwa::dumb_ptr<tmwa::magic::expr_t>(), 42})', - '{<tmwa::sexpr::Variant<tmwa::magic::val_t, tmwa::magic::e_location_t, tmwa::magic::e_area_t, tmwa::magic::ExprFunApp, tmwa::magic::ExprId, tmwa::magic::ExprField>> = {(tmwa::magic::ExprField) = {expr = 0x0, id = 42}}, <No data fields>}'), + ('tmwa::map::magic::expr_t(tmwa::map::magic::val_t(tmwa::map::magic::ValUndef()))', + '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::val_t) = {<tmwa::sexpr::Variant<tmwa::map::magic::ValUndef, tmwa::map::magic::ValInt, tmwa::map::magic::ValDir, tmwa::map::magic::ValString, tmwa::map::magic::ValEntityInt, tmwa::map::magic::ValEntityPtr, tmwa::map::magic::ValLocation, tmwa::map::magic::ValArea, tmwa::map::magic::ValSpell, tmwa::map::magic::ValInvocationInt, tmwa::map::magic::ValInvocationPtr, tmwa::map::magic::ValFail, tmwa::map::magic::ValNegative1>> = {(tmwa::map::magic::ValUndef) = {<No data fields>}}, <No data fields>}}, <No data fields>}'), + ('tmwa::map::magic::expr_t(tmwa::map::magic::e_location_t())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}'), + ('tmwa::map::magic::expr_t(tmwa::map::magic::e_area_t(tmwa::map::magic::e_location_t()))', + '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::e_area_t) = {<tmwa::sexpr::Variant<tmwa::map::magic::e_location_t, tmwa::map::magic::ExprAreaUnion, tmwa::map::magic::ExprAreaRect, tmwa::map::magic::ExprAreaBar>> = {(tmwa::map::magic::e_location_t) = {m = 0x0, x = 0x0, y = 0x0}}, <No data fields>}}, <No data fields>}'), + ('tmwa::map::magic::expr_t(tmwa::map::magic::ExprFunApp())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::ExprFunApp) = {funp = (fun_t *) nullptr, line_nr = 0, column = 0, args_nr = 0, args = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, <No data fields>}'), + ('tmwa::map::magic::expr_t(tmwa::map::magic::ExprId{123})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::ExprId) = {e_id = 123}}, <No data fields>}'), + ('tmwa::map::magic::expr_t(tmwa::map::magic::ExprField{tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), 42})', + '{<tmwa::sexpr::Variant<tmwa::map::magic::val_t, tmwa::map::magic::e_location_t, tmwa::map::magic::e_area_t, tmwa::map::magic::ExprFunApp, tmwa::map::magic::ExprId, tmwa::map::magic::ExprField>> = {(tmwa::map::magic::ExprField) = {expr = 0x0, id = 42}}, <No data fields>}'), ] @@ -154,30 +154,30 @@ class effect_t(object): enabled = True tests = [ - ('tmwa::magic::effect_t(tmwa::magic::EffectSkip{}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectSkip) = {<No data fields>}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectAbort{}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectAbort) = {<No data fields>}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectAssign{42, tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectAssign) = {id = 42, expr = 0x0}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectForEach{123, tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::magic::FOREACH_FILTER::PC}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectForEach) = {id = 123, area = 0x0, body = 0x0, filter = tmwa::magic::FOREACH_FILTER::PC}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectFor{42, tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectFor) = {id = 42, start = 0x0, stop = 0x0, body = 0x0}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectIf{tmwa::dumb_ptr<tmwa::magic::expr_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectIf) = {cond = 0x0, true_branch = 0x0, false_branch = 0x0}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectSleep{tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectSleep) = {e_sleep = 0x0}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectScript{tmwa::dumb_ptr<const tmwa::ScriptBuffer>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectScript) = {e_script = 0x0}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectBreak{}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectBreak) = {<No data fields>}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectOp(), tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::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::magic::effect_t(tmwa::magic::EffectEnd{}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectEnd) = {<No data fields>}}, next = 0x0}'), - ('tmwa::magic::effect_t(tmwa::magic::EffectCall{nullptr, tmwa::dumb_ptr<std::vector<tmwa::dumb_ptr<tmwa::magic::expr_t>>>(), tmwa::dumb_ptr<tmwa::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::EffectSkip, tmwa::magic::EffectAbort, tmwa::magic::EffectAssign, tmwa::magic::EffectForEach, tmwa::magic::EffectFor, tmwa::magic::EffectIf, tmwa::magic::EffectSleep, tmwa::magic::EffectScript, tmwa::magic::EffectBreak, tmwa::magic::EffectOp, tmwa::magic::EffectEnd, tmwa::magic::EffectCall>> = {(tmwa::magic::EffectCall) = {formalv = 0x0, actualvp = 0x0, body = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectSkip{}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectSkip) = {<No data fields>}}, next = 0x0}'), + ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectAbort{}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectAbort) = {<No data fields>}}, next = 0x0}'), + ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectAssign{42, tmwa::dumb_ptr<tmwa::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectAssign) = {id = 42, expr = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectForEach{123, tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::map::magic::FOREACH_FILTER::PC}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(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::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(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::map::magic::expr_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(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::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectSleep) = {e_sleep = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectScript{tmwa::dumb_ptr<const tmwa::map::ScriptBuffer>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectScript) = {e_script = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectBreak{}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectBreak) = {<No data fields>}}, next = 0x0}'), + ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectOp(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(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::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectEnd) = {<No data fields>}}, next = 0x0}'), + ('tmwa::map::magic::effect_t(tmwa::map::magic::EffectCall{nullptr, tmwa::dumb_ptr<std::vector<tmwa::dumb_ptr<tmwa::map::magic::expr_t>>>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::EffectSkip, tmwa::map::magic::EffectAbort, tmwa::map::magic::EffectAssign, tmwa::map::magic::EffectForEach, tmwa::map::magic::EffectFor, tmwa::map::magic::EffectIf, tmwa::map::magic::EffectSleep, tmwa::map::magic::EffectScript, tmwa::map::magic::EffectBreak, tmwa::map::magic::EffectOp, tmwa::map::magic::EffectEnd, tmwa::map::magic::EffectCall>> = {(tmwa::map::magic::EffectCall) = {formalv = nullptr, actualvp = 0x0, body = 0x0}}, next = 0x0}'), ] @@ -185,20 +185,20 @@ class spellguard_t(object): enabled = True tests = [ - ('tmwa::magic::spellguard_t(tmwa::magic::GuardCondition{tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardCondition) = {s_condition = 0x0}}, next = 0x0}'), - ('tmwa::magic::spellguard_t(tmwa::magic::GuardMana{tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardMana) = {s_mana = 0x0}}, next = 0x0}'), - ('tmwa::magic::spellguard_t(tmwa::magic::GuardCastTime{tmwa::dumb_ptr<tmwa::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardCastTime) = {s_casttime = 0x0}}, next = 0x0}'), - ('tmwa::magic::spellguard_t(tmwa::magic::GuardComponents{tmwa::dumb_ptr<tmwa::magic::component_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardComponents) = {s_components = 0x0}}, next = 0x0}'), - ('tmwa::magic::spellguard_t(tmwa::magic::GuardCatalysts{tmwa::dumb_ptr<tmwa::magic::component_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardCatalysts) = {s_catalysts = 0x0}}, next = 0x0}'), - ('tmwa::magic::spellguard_t(tmwa::magic::GuardChoice{tmwa::dumb_ptr<tmwa::magic::spellguard_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::GuardChoice) = {s_alt = 0x0}}, next = 0x0}'), - ('tmwa::magic::spellguard_t(tmwa::magic::effect_set_t{tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::dumb_ptr<tmwa::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::magic::spellguard_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::GuardCondition, tmwa::magic::GuardMana, tmwa::magic::GuardCastTime, tmwa::magic::GuardComponents, tmwa::magic::GuardCatalysts, tmwa::magic::GuardChoice, tmwa::magic::effect_set_t>> = {(tmwa::magic::effect_set_t) = {effect = 0x0, at_trigger = 0x0, at_end = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardCondition{tmwa::dumb_ptr<tmwa::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardCondition) = {s_condition = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardMana{tmwa::dumb_ptr<tmwa::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardMana) = {s_mana = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardCastTime{tmwa::dumb_ptr<tmwa::map::magic::expr_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardCastTime) = {s_casttime = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardComponents{tmwa::dumb_ptr<tmwa::map::magic::component_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardComponents) = {s_components = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardCatalysts{tmwa::dumb_ptr<tmwa::map::magic::component_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardCatalysts) = {s_catalysts = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::spellguard_t(tmwa::map::magic::GuardChoice{tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::GuardChoice) = {s_alt = 0x0}}, next = 0x0}'), + ('tmwa::map::magic::spellguard_t(tmwa::map::magic::effect_set_t{tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::dumb_ptr<tmwa::map::magic::effect_t>()}, tmwa::dumb_ptr<tmwa::map::magic::spellguard_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::GuardCondition, tmwa::map::magic::GuardMana, tmwa::map::magic::GuardCastTime, tmwa::map::magic::GuardComponents, tmwa::map::magic::GuardCatalysts, tmwa::map::magic::GuardChoice, tmwa::map::magic::effect_set_t>> = {(tmwa::map::magic::effect_set_t) = {effect = 0x0, at_trigger = 0x0, at_end = 0x0}}, next = 0x0}'), ] @@ -206,10 +206,10 @@ class cont_activation_record_t(object): enabled = True tests = [ - ('tmwa::magic::cont_activation_record_t(tmwa::magic::CarForEach{42, true, tmwa::dumb_ptr<tmwa::magic::effect_t>(), tmwa::dumb_ptr<std::vector<tmwa::BlockId>>(), 123}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::CarForEach, tmwa::magic::CarFor, tmwa::magic::CarProc>> = {(tmwa::magic::CarForEach) = {id = 42, ty_is_spell_not_entity = true, body = 0x0, entities_vp = 0x0, index = 123}}, return_location = 0x0}'), - ('tmwa::magic::cont_activation_record_t(tmwa::magic::CarFor{42, tmwa::dumb_ptr<tmwa::magic::effect_t>(), 123, 456}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::CarForEach, tmwa::magic::CarFor, tmwa::magic::CarProc>> = {(tmwa::magic::CarFor) = {id = 42, body = 0x0, current = 123, stop = 456}}, return_location = 0x0}'), - ('tmwa::magic::cont_activation_record_t(tmwa::magic::CarProc{123, nullptr, tmwa::dumb_ptr<tmwa::magic::val_t[]>()}, tmwa::dumb_ptr<tmwa::magic::effect_t>())', - '{<tmwa::sexpr::Variant<tmwa::magic::CarForEach, tmwa::magic::CarFor, tmwa::magic::CarProc>> = {(tmwa::magic::CarProc) = {args_nr = 123, formalap = 0x0, old_actualpa = 0x0 = {sz = 0}}}, return_location = 0x0}'), + ('tmwa::map::magic::cont_activation_record_t(tmwa::map::magic::CarForEach{42, true, tmwa::dumb_ptr<tmwa::map::magic::effect_t>(), tmwa::dumb_ptr<std::vector<tmwa::BlockId>>(), 123}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::CarForEach, tmwa::map::magic::CarFor, tmwa::map::magic::CarProc>> = {(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<tmwa::map::magic::effect_t>(), 123, 456}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::CarForEach, tmwa::map::magic::CarFor, tmwa::map::magic::CarProc>> = {(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::map::magic::val_t[]>()}, tmwa::dumb_ptr<tmwa::map::magic::effect_t>())', + '{<tmwa::sexpr::Variant<tmwa::map::magic::CarForEach, tmwa::map::magic::CarFor, tmwa::map::magic::CarProc>> = {(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 index ab151fc..e302354 100644 --- a/src/map/magic-interpreter.t.hpp +++ b/src/map/magic-interpreter.t.hpp @@ -26,6 +26,8 @@ namespace tmwa { +namespace map +{ namespace magic { enum class SPELLARG : uint8_t @@ -79,4 +81,5 @@ 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 index 2a657fa..4d8330a 100644 --- a/src/map/magic-stmt.cpp +++ b/src/map/magic-stmt.cpp @@ -29,7 +29,8 @@ #include "../generic/random2.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" + +#include "../mmo/cxxstdio_enums.hpp" #include "../net/timer.hpp" @@ -42,7 +43,9 @@ #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" @@ -50,6 +53,8 @@ namespace tmwa { +namespace map +{ namespace magic { /* used for local spell effects */ @@ -58,17 +63,18 @@ constexpr Species INVISIBLE_NPC = wrap<Species>(127); static void clear_activation_record(cont_activation_record_t *ar) { - MATCH (*ar) + MATCH_BEGIN (*ar) { - CASE (CarForEach&, c_foreach) + MATCH_CASE (CarForEach&, c_foreach) { c_foreach.entities_vp.delete_(); } - CASE (CarProc&, c_proc) + MATCH_CASE (CarProc&, c_proc) { c_proc.old_actualpa.delete_(); } } + MATCH_END (); } static @@ -218,7 +224,7 @@ BlockId trigger_spell(BlockId subject, BlockId spell) } static -void entity_warp(dumb_ptr<block_list> target, map_local *destm, int destx, int desty); +void entity_warp(dumb_ptr<block_list> target, Borrowed<map_local> destm, int destx, int desty); static void char_update(dumb_ptr<map_session_data> character) @@ -261,7 +267,7 @@ void timer_callback_effect_npc_delete(TimerData *, tick_t, BlockId npc_id) } static -dumb_ptr<npc_data> local_spell_effect(map_local *m, int x, int y, int effect, +dumb_ptr<npc_data> local_spell_effect(Borrowed<map_local> m, int x, int y, int effect, interval_t tdelay) { /* 1 minute should be enough for all interesting spell effects, I hope */ @@ -421,7 +427,7 @@ int op_messenger_npc(dumb_ptr<env_t>, Slice<val_t> args) } static -void entity_warp(dumb_ptr<block_list> target, map_local *destm, int destx, int desty) +void entity_warp(dumb_ptr<block_list> target, Borrowed<map_local> destm, int destx, int desty) { if (target->bl_type == BL::PC || target->bl_type == BL::MOB) { @@ -767,8 +773,8 @@ int op_injure(dumb_ptr<env_t> env, Slice<val_t> args) if (target->bl_type == BL::PC && !target->bl_m->flag.get(MapFlag::PVP) - && !target->is_player()->special_state.killable - && (caster->bl_type != BL::PC || !caster->is_player()->special_state.killer)) + && (caster->bl_type == BL::PC) + && ((caster->is_player()->state.pvpchannel > 1) && (target->is_player()->state.pvpchannel != caster->is_player()->state.pvpchannel))) return 0; /* Cannot damage other players outside of pvp */ if (target != caster) @@ -786,7 +792,7 @@ int op_injure(dumb_ptr<env_t> env, Slice<val_t> args) // 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, 0); + damage_caused, 0, DamageType::NORMAL); if (caster->bl_type == BL::PC) { @@ -995,9 +1001,9 @@ dumb_ptr<effect_t> return_to_stack(dumb_ptr<invocation> invocation_) { cont_activation_record_t *ar = &invocation_->stack.back(); - MATCH (*ar) + MATCH_BEGIN (*ar) { - CASE (const CarProc&, c_proc) + MATCH_CASE (const CarProc&, c_proc) { dumb_ptr<effect_t> ret = ar->return_location; for (int i = 0; i < c_proc.args_nr; i++) @@ -1014,7 +1020,7 @@ dumb_ptr<effect_t> return_to_stack(dumb_ptr<invocation> invocation_) return ret; } - CASE (CarForEach&, c_foreach) + MATCH_CASE (CarForEach&, c_foreach) { BlockId entity_id; val_t *var = &invocation_->env->varu[c_foreach.id]; @@ -1045,7 +1051,7 @@ dumb_ptr<effect_t> return_to_stack(dumb_ptr<invocation> invocation_) return c_foreach.body; } - CASE (CarFor&, c_for) + MATCH_CASE (CarFor&, c_for) { if (c_for.current > c_for.stop) { @@ -1062,6 +1068,7 @@ dumb_ptr<effect_t> return_to_stack(dumb_ptr<invocation> invocation_) return c_for.body; } } + MATCH_END (); abort(); } } @@ -1133,46 +1140,43 @@ void find_entities_in_area(area_t& area_, std::vector<BlockId> *entities_vp, FOREACH_FILTER filter) { - MATCH (area_) + MATCH_BEGIN (area_) { - CASE (const AreaUnion&, a) + 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); } - CASE (const location_t&, a_loc) + MATCH_CASE (const location_t&, a_loc) { (void)a_loc; // TODO this can be simplified - map_local *m; int x, y, width, height; - magic_area_rect(&m, &x, &y, &width, &height, area_); + Borrowed<map_local> 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 */); } - CASE (const AreaRect&, a_rect) + MATCH_CASE (const AreaRect&, a_rect) { (void)a_rect; // TODO this can be simplified - map_local *m; int x, y, width, height; - magic_area_rect(&m, &x, &y, &width, &height, area_); + Borrowed<map_local> 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 */); } - CASE (const AreaBar&, a_bar) + MATCH_CASE (const AreaBar&, a_bar) { (void)a_bar; // TODO this is wrong - map_local *m; int x, y, width, height; - magic_area_rect(&m, &x, &y, &width, &height, area_); + Borrowed<map_local> 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, @@ -1180,6 +1184,7 @@ void find_entities_in_area(area_t& area_, BL::NUL /* filter elsewhere */); } } + MATCH_END (); } static @@ -1312,13 +1317,13 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete) dumb_ptr<effect_t> next = e->next; int i; - MATCH (*e) + MATCH_BEGIN (*e) { - CASE (const EffectSkip&, e_) + MATCH_CASE (const EffectSkip&, e_) { (void)e_; } - CASE (const EffectAbort&, e_) + MATCH_CASE (const EffectAbort&, e_) { (void)e_; invocation_->flags |= INVOCATION_FLAG::ABORTED; @@ -1326,34 +1331,34 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete) clear_stack(invocation_); next = nullptr; } - CASE (const EffectEnd&, e_) + MATCH_CASE (const EffectEnd&, e_) { (void)e_; clear_stack(invocation_); next = nullptr; } - CASE (const EffectAssign&, e_assign) + MATCH_CASE (const EffectAssign&, e_assign) { magic_eval(invocation_->env, &invocation_->env->varu[e_assign.id], e_assign.expr); } - CASE (const EffectForEach&, e_foreach) + MATCH_CASE (const EffectForEach&, e_foreach) { next = run_foreach(invocation_, &e_foreach, next); } - CASE (const EffectFor&, e_for) + MATCH_CASE (const EffectFor&, e_for) { next = run_for (invocation_, &e_for, next); } - CASE (const EffectIf&, e_if) + 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; } - CASE (const EffectSleep&, e_) + MATCH_CASE (const EffectSleep&, e_) { interval_t sleeptime = static_cast<interval_t>( magic_eval_int(invocation_->env, e_.e_sleep)); @@ -1361,7 +1366,7 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete) if (sleeptime > interval_t::zero()) return sleeptime; } - CASE (const EffectScript&, e_) + MATCH_CASE (const EffectScript&, e_) { dumb_ptr<map_session_data> caster = map_id_is_player(invocation_->caster); if (caster) @@ -1391,7 +1396,7 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete) // dealing with an NPC int newpos = run_script_l( - ScriptPointer(&*e_.e_script, invocation_->script_pos), + 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 */ @@ -1408,12 +1413,12 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete) } REFRESH_INVOCATION; // Script may have killed the caster } - CASE (const EffectBreak&, e_) + MATCH_CASE (const EffectBreak&, e_) { (void)e_; next = return_to_stack(invocation_); } - CASE (const EffectOp&, e_op) + MATCH_CASE (const EffectOp&, e_op) { op_t *op = e_op.opp; val_t args[MAX_ARGS]; @@ -1432,11 +1437,12 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete) REFRESH_INVOCATION; // Effect may have killed the caster } - CASE (const EffectCall&, e_call) + MATCH_CASE (const EffectCall&, e_call) { next = run_call(invocation_, &e_call, next); } } + MATCH_END (); break_match: if (!next) @@ -1535,4 +1541,5 @@ int spell_attack(BlockId caster_id, BlockId target_id) return 1; } } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/magic-stmt.hpp b/src/map/magic-stmt.hpp index 0385858..3b04fe3 100644 --- a/src/map/magic-stmt.hpp +++ b/src/map/magic-stmt.hpp @@ -21,17 +21,15 @@ #include "fwd.hpp" -#include "../range/fwd.hpp" - #include "../strings/zstring.hpp" -#include "../generic/fwd.hpp" - -#include "skill.t.hpp" +#include "../mmo/skill.t.hpp" namespace tmwa { +namespace map +{ namespace magic { struct op_t @@ -91,4 +89,5 @@ int spell_attack(BlockId caster, BlockId target); void spell_free_invocation(dumb_ptr<invocation> invocation); } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/magic-stmt.py b/src/map/magic-stmt.py index 14289ef..7cc43d0 100644 --- a/src/map/magic-stmt.py +++ b/src/map/magic-stmt.py @@ -1,7 +1,7 @@ class op_t(object): __slots__ = ('_value') - name = 'tmwa::magic::op_t' + name = 'tmwa::map::magic::op_t' depth = 1 enabled = True @@ -30,8 +30,8 @@ class op_t(object): ''' tests = [ - ('static_cast<tmwa::magic::op_t *>(nullptr)', + ('static_cast<tmwa::map::magic::op_t *>(nullptr)', '(op_t *) nullptr'), - ('new tmwa::magic::op_t{"name"_s, "sig"_s, nullptr}', - 'regex:\(op_t \*\) = \{->name = "name", ->signature = "sig", ->op = (0x)?0}'), + ('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 index 5b375b2..52b1b8f 100644 --- a/src/map/magic-v2.cpp +++ b/src/map/magic-v2.cpp @@ -24,6 +24,8 @@ #include <map> #include <set> +#include "../range/slice.hpp" + #include "../strings/rstring.hpp" #include "../strings/literal.hpp" @@ -34,27 +36,28 @@ #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 - std::map<RString, proc_t> procs; - static - std::map<RString, val_t> const_defm; - - static size_t intern_id(ZString id_name) { // TODO use InternPool @@ -134,14 +137,15 @@ namespace magic_v2 /* 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 (*src) + MATCH_BEGIN (*src) { - CASE (EffectIf&, e_if) + 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); @@ -172,13 +176,14 @@ namespace magic_v2 } /* If the premise is a disjunction, b is the continuation of _all_ branches */ - MATCH (*a) + MATCH_BEGIN (*a) { - CASE(const GuardChoice&, s) + MATCH_CASE (const GuardChoice&, s) { spellguard_implication(s.s_alt, b); } } + MATCH_END (); if (a->next) spellguard_implication(a->next, b); @@ -575,9 +580,9 @@ namespace magic_v2 { count = 1; - item_data *item = itemdb_searchname(s._str); - if (!item) - return fail(s, "no such item"_s); + Borrowed<item_data> item = TRY_UNWRAP(itemdb_searchname(s._str), + return fail(s, "no such item"_s) + ); id = item->nameid; return true; } @@ -591,9 +596,9 @@ namespace magic_v2 if (s._list[1]._type != sexpr::STRING) return fail(s._list[1], "item pair second not name"_s); - item_data *item = itemdb_searchname(s._list[1]._str); - if (!item) - return fail(s, "no such item"_s); + Borrowed<item_data> item = TRY_UNWRAP(itemdb_searchname(s._list[1]._str), + return fail(s, "no such item"_s) + ); id = item->nameid; return true; } @@ -782,7 +787,20 @@ namespace magic_v2 if (s._list[1]._type != sexpr::STRING) return fail(s._list[1], "not string"_s); ZString body = s._list[1]._str; - std::unique_ptr<const ScriptBuffer> script = parse_script(body, s._list[1]._span.begin.line, true); + 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<const ScriptBuffer> 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; @@ -1273,4 +1291,5 @@ bool load_magic_file_v2(ZString filename) return rv; } } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/magic-v2.hpp b/src/map/magic-v2.hpp index 9ad44a9..fac2773 100644 --- a/src/map/magic-v2.hpp +++ b/src/map/magic-v2.hpp @@ -25,10 +25,13 @@ 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 index a0238b5..418312a 100644 --- a/src/map/magic.cpp +++ b/src/map/magic.cpp @@ -28,6 +28,7 @@ #include "../io/cxxstdio.hpp" +#include "globals.hpp" #include "magic-expr.hpp" #include "magic-interpreter.hpp" #include "magic-interpreter-base.hpp" @@ -40,6 +41,8 @@ namespace tmwa { +namespace map +{ namespace magic { /// Return a pair of strings, {spellname, parameter} @@ -71,7 +74,7 @@ int magic_message(dumb_ptr<map_session_data> caster, XString source_invocation) { if (pc_isdead(caster)) return 0; - if (bool(caster->status.option & Option::HIDE)) + if (bool(caster->status.option & Opt0::HIDE)) return 0; // No spellcasting while hidden int power = caster->matk1; @@ -123,4 +126,5 @@ int magic_message(dumb_ptr<map_session_data> caster, XString source_invocation) return 0; /* Not a spell */ } } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/magic.hpp b/src/map/magic.hpp index a420872..70d40dc 100644 --- a/src/map/magic.hpp +++ b/src/map/magic.hpp @@ -21,16 +21,14 @@ #include "fwd.hpp" -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" - #include "map.t.hpp" -#include "skill.t.hpp" +#include "../mmo/skill.t.hpp" namespace tmwa { +namespace map +{ namespace magic { /** @@ -46,4 +44,5 @@ namespace magic */ int magic_message(dumb_ptr<map_session_data> caster, XString source_invocation); } // namespace magic +} // namespace map } // namespace tmwa diff --git a/src/map/main.cpp b/src/map/main.cpp index c16f642..f19272d 100644 --- a/src/map/main.cpp +++ b/src/map/main.cpp @@ -17,7 +17,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include "../mmo/core.hpp" +#include "../high/core.hpp" #include "map.hpp" @@ -26,6 +26,9 @@ namespace tmwa { +namespace map +{ +} // namespace map } // namespace tmwa int main(int argc, char **argv) diff --git a/src/map/map.cpp b/src/map/map.cpp index 4a25029..c1d760a 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -45,34 +45,41 @@ #include "../generic/random2.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" +#include "../io/extract.hpp" #include "../io/read.hpp" +#include "../io/span.hpp" #include "../io/tty.hpp" #include "../io/write.hpp" #include "../net/socket.hpp" #include "../net/timer.hpp" +#include "../net/timestamp-utils.hpp" #include "../mmo/config_parse.hpp" -#include "../mmo/core.hpp" -#include "../mmo/extract.hpp" -#include "../mmo/utils.hpp" +#include "../mmo/cxxstdio_enums.hpp" #include "../mmo/version.hpp" +#include "../high/core.hpp" + #include "atcommand.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "chrif.hpp" #include "clif.hpp" +#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" #include "npc.hpp" +#include "npc-parse.hpp" #include "party.hpp" #include "pc.hpp" -#include "script.hpp" +#include "script-startup.hpp" #include "skill.hpp" #include "storage.hpp" #include "trade.hpp" @@ -82,53 +89,16 @@ namespace tmwa { -DMap<BlockId, dumb_ptr<block_list>> id_db; - -UPMap<MapName, map_abstract> maps_db; - -static -DMap<CharName, dumb_ptr<map_session_data>> nick_db; - -struct charid2nick -{ - CharName nick; - int req_id; -}; - -static -Map<CharId, struct charid2nick> charid_db; - -static -int users = 0; -static -Array<dumb_ptr<block_list>, unwrap<BlockId>(MAX_FLOORITEM)> object; -static -BlockId first_free_object_id = BlockId(); - -interval_t autosave_time = DEFAULT_AUTOSAVE_INTERVAL; -int save_settings = 0xFFFF; - -AString motd_txt = "conf/motd.txt"_s; - -CharName wisp_server_name = stringish<CharName>("Server"_s); // can be modified in char-server configuration file - -static -void map_delmap(MapName mapname); - void SessionDeleter::operator()(SessionData *sd) { - really_delete1 static_cast<map_session_data *>(sd); + really_delete1 static_cast<map::map_session_data *>(sd); } -VString<49> convert_for_printf(NpcEvent ev) +namespace map { - return STRNPRINTF(50, "%s::%s"_fmt, ev.npc, ev.label); -} -bool extract(XString str, NpcEvent *ev) -{ - XString mid; - return extract(str, record<':'>(&ev->npc, &mid, &ev->label)) && !mid; -} +const CharName WISP_SERVER_NAME = stringish<CharName>("Server"_s); + +map_local undefined_gat = [](){ map_local rv {}; rv.name_ = stringish<MapName>("undefined.gat"_s); return rv; }(); /*========================================== * 全map鯖総計での接続数設定 @@ -137,7 +107,7 @@ bool extract(XString str, NpcEvent *ev) */ void map_setusers(int n) { - users = n; + world_user_count = n; } /*========================================== @@ -146,14 +116,9 @@ void map_setusers(int n) */ int map_getusers(void) { - return users; + return world_user_count; } -static -int block_free_lock = 0; -static -std::vector<dumb_ptr<block_list>> block_free; - void MapBlockLock::freeblock(dumb_ptr<block_list> bl) { if (block_free_lock == 0) @@ -178,12 +143,6 @@ MapBlockLock::~MapBlockLock() } } -/// This is a dummy entry that is shared by all the linked lists, -/// so that any entry can unlink itself without worrying about -/// whether it was the the head of the list. -static -struct block_list bl_head; - /*========================================== * map[]のblock_listに追加 * mobは数が多いので別リスト @@ -202,10 +161,10 @@ int map_addblock(dumb_ptr<block_list> bl) return 0; } - map_local *m = bl->bl_m; + P<map_local> m = bl->bl_m; int x = bl->bl_x; int y = bl->bl_y; - if (!m || + if (m == borrow(undefined_gat) || x < 0 || x >= m->xs || y < 0 || y >= m->ys) return 1; @@ -283,7 +242,7 @@ int map_delblock(dumb_ptr<block_list> bl) * セル上のPCとMOBの数を数える (グランドクロス用) *------------------------------------------ */ -int map_count_oncell(map_local *m, int x, int y) +int map_count_oncell(Borrowed<map_local> m, int x, int y) { int bx, by; dumb_ptr<block_list> bl = nullptr; @@ -318,14 +277,17 @@ int map_count_oncell(map_local *m, int x, int y) *------------------------------------------ */ void map_foreachinarea(std::function<void(dumb_ptr<block_list>)> func, - map_local *m, + Borrowed<map_local> m, int x0, int y0, int x1, int y1, BL type) { std::vector<dumb_ptr<block_list>> bl_list; - if (!m) - return; + // there are some broadcasts during startup + // disable then + if (m == borrow(undefined_gat)) + abort(); + if (x0 < 0) x0 = 0; if (y0 < 0) @@ -381,7 +343,7 @@ void map_foreachinarea(std::function<void(dumb_ptr<block_list>)> func, *------------------------------------------ */ void map_foreachinmovearea(std::function<void(dumb_ptr<block_list>)> func, - map_local *m, + Borrowed<map_local> m, int x0, int y0, int x1, int y1, int dx, int dy, BL type) @@ -501,7 +463,7 @@ void map_foreachinmovearea(std::function<void(dumb_ptr<block_list>)> func, // area radius - may be more useful in some instances) // void map_foreachincell(std::function<void(dumb_ptr<block_list>)> func, - map_local *m, + Borrowed<map_local> m, int x, int y, BL type) { @@ -674,7 +636,7 @@ void map_clearflooritem_timer(TimerData *tid, tick_t, BlockId id) map_delobject(fitem->bl_id, BL::ITEM); } -std::pair<uint16_t, uint16_t> map_randfreecell(map_local *m, +std::pair<uint16_t, uint16_t> map_randfreecell(Borrowed<map_local> m, uint16_t x, uint16_t y, uint16_t w, uint16_t h) { for (int itr : random_::iterator(w * h)) @@ -689,7 +651,7 @@ std::pair<uint16_t, uint16_t> map_randfreecell(map_local *m, /// Return a randomly selected passable cell within a given range. static -std::pair<uint16_t, uint16_t> map_searchrandfreecell(map_local *m, int x, int y, int range) +std::pair<uint16_t, uint16_t> map_searchrandfreecell(Borrowed<map_local> m, int x, int y, int range) { int whole_range = 2 * range + 1; return map_randfreecell(m, x - range, y - range, whole_range, whole_range); @@ -702,7 +664,7 @@ std::pair<uint16_t, uint16_t> map_searchrandfreecell(map_local *m, int x, int y, *------------------------------------------ */ BlockId map_addflooritem_any(Item *item_data, int amount, - map_local *m, int x, int y, + Borrowed<map_local> m, int x, int y, dumb_ptr<map_session_data> *owners, interval_t *owner_protection, interval_t lifetime, int dispersal) { @@ -767,7 +729,7 @@ BlockId map_addflooritem_any(Item *item_data, int amount, } BlockId map_addflooritem(Item *item_data, int amount, - map_local *m, int x, int y, + Borrowed<map_local> m, int x, int y, dumb_ptr<map_session_data> first_sd, dumb_ptr<map_session_data> second_sd, dumb_ptr<map_session_data> third_sd) @@ -776,14 +738,14 @@ BlockId map_addflooritem(Item *item_data, int amount, interval_t owner_protection[3]; { - owner_protection[0] = static_cast<interval_t>(battle_config.item_first_get_time); - owner_protection[1] = owner_protection[0] + static_cast<interval_t>(battle_config.item_second_get_time); - owner_protection[2] = owner_protection[1] + static_cast<interval_t>(battle_config.item_third_get_time); + owner_protection[0] = battle_config.item_first_get_time; + owner_protection[1] = owner_protection[0] + battle_config.item_second_get_time; + owner_protection[2] = owner_protection[1] + battle_config.item_third_get_time; } return map_addflooritem_any(item_data, amount, m, x, y, owners, owner_protection, - static_cast<interval_t>(battle_config.flooritem_lifetime), 1); + battle_config.flooritem_lifetime, 1); } /*========================================== @@ -792,9 +754,7 @@ BlockId map_addflooritem(Item *item_data, int amount, */ void map_addchariddb(CharId charid, CharName name) { - struct charid2nick *p = charid_db.search(charid); - if (p == nullptr) - p = charid_db.init(charid); + P<struct charid2nick> p = charid_db.init(charid); p->nick = name; p->req_id = 0; @@ -929,13 +889,17 @@ dumb_ptr<map_session_data> map_id2sd(BlockId id) */ CharName map_charid2nick(CharId id) { - struct charid2nick *p = charid_db.search(id); + Option<P<struct charid2nick>> p_ = charid_db.search(id); - if (p == nullptr) - return CharName(); - if (p->req_id != 0) - return CharName(); - return p->nick; + return p_.cmap( + [](P<struct charid2nick> p) + { + return p->req_id == 0; + }, + [](P<struct charid2nick> p) + { + return p->nick; + }).move_or(CharName()); } /*========================================*/ @@ -1047,11 +1011,9 @@ dumb_ptr<block_list> map_id2bl(BlockId id) * map.npcへ追加 (warp等の領域持ちのみ) *------------------------------------------ */ -int map_addnpc(map_local *m, dumb_ptr<npc_data> nd) +int map_addnpc(Borrowed<map_local> m, dumb_ptr<npc_data> nd) { int i; - if (!m) - return -1; for (i = 0; i < m->npc_num && i < MAX_NPC_PER_MAP; i++) if (m->npc[i] == nullptr) break; @@ -1109,27 +1071,39 @@ void map_removenpc(void) * map名からmap番号へ変換 *------------------------------------------ */ -map_local *map_mapname2mapid(MapName name) +Option<Borrowed<map_local>> map_mapname2mapid(MapName name) { - map_abstract *md = maps_db.get(name); - if (md == nullptr || md->gat == nullptr) - return nullptr; - return static_cast<map_local *>(md); + Option<P<map_abstract>> md_ = maps_db.get(name); + return md_.cmap( + [](P<map_abstract> md) + { + return bool(md->gat); + }, + [](P<map_abstract> md) + { + return md.downcast_to<map_local>(); + }); } /*========================================== * 他鯖map名からip,port変換 *------------------------------------------ */ -int map_mapname2ipport(MapName name, IP4Address *ip, int *port) +int map_mapname2ipport(MapName name, Borrowed<IP4Address> ip, Borrowed<int> port) { - map_abstract *md = maps_db.get(name); - if (md == nullptr || md->gat) - return -1; - map_remote *mdos = static_cast<map_remote *>(md); - *ip = mdos->ip; - *port = mdos->port; - return 0; + Option<P<map_abstract>> md_ = maps_db.get(name); + return md_.cmap( + [](P<map_abstract> md) + { + return !md->gat; + }, + [ip, port](P<map_abstract> md) + { + auto mdos = md.downcast_to<map_remote>(); + *ip = mdos->ip; + *port = mdos->port; + return 0; + }).copy_or(-1); } /// Check compatibility of directions. @@ -1208,7 +1182,7 @@ DIR map_calc_dir(dumb_ptr<block_list> src, int x, int y) * (m,x,y)の状態を調べる *------------------------------------------ */ -MapCell map_getcell(map_local *m, int x, int y) +MapCell map_getcell(Borrowed<map_local> m, int x, int y) { if (x < 0 || x >= m->xs - 1 || y < 0 || y >= m->ys - 1) return MapCell::UNWALKABLE; @@ -1219,7 +1193,7 @@ MapCell map_getcell(map_local *m, int x, int y) * (m,x,y)の状態をtにする *------------------------------------------ */ -void map_setcell(map_local *m, int x, int y, MapCell t) +void map_setcell(Borrowed<map_local> m, int x, int y, MapCell t) { if (x < 0 || x >= m->xs || y < 0 || y >= m->ys) return; @@ -1232,37 +1206,41 @@ void map_setcell(map_local *m, int x, int y, MapCell t) */ int map_setipport(MapName name, IP4Address ip, int port) { - map_abstract *md = maps_db.get(name); - if (md == nullptr) + Option<P<map_abstract>> md_ = maps_db.get(name); + OMATCH_BEGIN (md_) { - // not exist -> add new data - auto mdos = make_unique<map_remote>(); - mdos->name_ = name; - mdos->gat = nullptr; - mdos->ip = ip; - mdos->port = port; - maps_db.put(mdos->name_, std::move(mdos)); - } - else - { - if (md->gat) + OMATCH_CASE_SOME (md) { - // local -> check data - if (ip != clif_getip() || port != clif_getport()) + if (md->gat) + { + // local -> check data + if (ip != map_conf.map_ip || port != map_conf.map_port) + { + PRINTF("from char server : %s -> %s:%d\n"_fmt, + name, ip, port); + return 1; + } + } + else { - PRINTF("from char server : %s -> %s:%d\n"_fmt, - name, ip, port); - return 1; + // update + P<map_remote> mdos = md.downcast_to<map_remote>(); + mdos->ip = ip; + mdos->port = port; } } - else + OMATCH_CASE_NONE () { - // update - map_remote *mdos = static_cast<map_remote *>(md); + // not exist -> add new data + auto mdos = make_unique<map_remote>(); + mdos->name_ = name; + mdos->gat = nullptr; mdos->ip = ip; mdos->port = port; + maps_db.put(mdos->name_, std::move(mdos)); } } + OMATCH_END (); return 0; } @@ -1281,7 +1259,7 @@ bool map_readmap(map_local *m, size_t num, MapName fn) int xs = m->xs = gat_v[0] | gat_v[1] << 8; int ys = m->ys = gat_v[2] | gat_v[3] << 8; - PRINTF("\rLoading Maps [%zu/%zu]: %-30s (%i, %i)"_fmt, + PRINTF("Loading Maps [%zu/%zu]: %-30s (%i, %i)\r"_fmt, num, maps_db.size(), fn, xs, ys); fflush(stdout); @@ -1334,7 +1312,7 @@ bool map_readallmap(void) } } - PRINTF("\rMaps Loaded: %-65zu\n"_fmt, maps_db.size()); + PRINTF("Maps Loaded: %-65zu\n"_fmt, maps_db.size()); if (maps_removed) { PRINTF("Cowardly refusing to keep going after removing %d maps.\n"_fmt, @@ -1349,7 +1327,6 @@ bool map_readallmap(void) * 読み込むmapを追加する *------------------------------------------ */ -static void map_addmap(MapName mapname) { if (mapname == "clear"_s) @@ -1384,18 +1361,11 @@ void map_delmap(MapName mapname) constexpr int LOGFILE_SECONDS_PER_CHUNK_SHIFT = 10; static -std::unique_ptr<io::AppendFile> map_logfile; -static -AString map_logfile_name; -static -long map_logfile_index; - -static void map_close_logfile(void) { if (map_logfile) { - AString filename = STRPRINTF("%s.%ld"_fmt, map_logfile_name, map_logfile_index); + AString filename = STRPRINTF("%s.%ld"_fmt, map_conf.log_file, map_logfile_index); const char *args[] = { "gzip", @@ -1423,22 +1393,23 @@ void map_start_logfile(long index) AString filename_buf = STRPRINTF( "%s.%ld"_fmt, - map_logfile_name, + map_conf.log_file, map_logfile_index); map_logfile = make_unique<io::AppendFile>(filename_buf); if (!map_logfile->is_open()) { map_logfile.reset(); - perror(map_logfile_name.c_str()); + perror(map_conf.log_file.c_str()); } } static -void map_set_logfile(AString filename) +void map_set_logfile() { - struct timeval tv; + if (!map_conf.log_file) + return; - map_logfile_name = std::move(filename); + struct timeval tv; gettimeofday(&tv, nullptr); map_start_logfile(tv.tv_sec >> LOGFILE_SECONDS_PER_CHUNK_SHIFT); @@ -1463,147 +1434,6 @@ void map_log(XString line) log_with_timestamp(*map_logfile, line); } -/*========================================== - * 設定ファイルを読み込む - *------------------------------------------ - */ -static -bool map_config_read(ZString cfgName) -{ - struct hostent *h = nullptr; - - io::ReadFile in(cfgName); - if (!in.is_open()) - { - PRINTF("Map configuration file not found at: %s\n"_fmt, cfgName); - return false; - } - - bool rv = true; - AString line; - while (in.getline(line)) - { - if (is_comment(line)) - continue; - XString w1; - ZString w2; - if (!config_split(line, &w1, &w2)) - { - PRINTF("Bad config line: %s\n"_fmt, line); - rv = false; - continue; - } - if (w1 == "userid"_s) - { - AccountName name = stringish<AccountName>(w2); - chrif_setuserid(name); - } - else if (w1 == "passwd"_s) - { - AccountPass pass = stringish<AccountPass>(w2); - chrif_setpasswd(pass); - } - else if (w1 == "char_ip"_s) - { - h = gethostbyname(w2.c_str()); - IP4Address w2ip; - if (h != nullptr) - { - w2ip = IP4Address({ - static_cast<uint8_t>(h->h_addr[0]), - static_cast<uint8_t>(h->h_addr[1]), - static_cast<uint8_t>(h->h_addr[2]), - static_cast<uint8_t>(h->h_addr[3]), - }); - PRINTF("Character server IP address : %s -> %s\n"_fmt, - w2, w2ip); - } - else - { - PRINTF("Bad IP value: %s\n"_fmt, line); - return false; - } - chrif_setip(w2ip); - } - else if (w1 == "char_port"_s) - { - chrif_setport(atoi(w2.c_str())); - } - else if (w1 == "map_ip"_s) - { - h = gethostbyname(w2.c_str()); - IP4Address w2ip; - if (h != nullptr) - { - w2ip = IP4Address({ - static_cast<uint8_t>(h->h_addr[0]), - static_cast<uint8_t>(h->h_addr[1]), - static_cast<uint8_t>(h->h_addr[2]), - static_cast<uint8_t>(h->h_addr[3]), - }); - PRINTF("Map server IP address : %s -> %s\n"_fmt, - w2, w2ip); - } - else - { - PRINTF("Bad IP value: %s\n"_fmt, line); - return false; - } - clif_setip(w2ip); - } - else if (w1 == "map_port"_s) - { - clif_setport(atoi(w2.c_str())); - } - else if (w1 == "map"_s) - { - MapName name = VString<15>(w2); - map_addmap(name); - } - else if (w1 == "delmap"_s) - { - MapName name = VString<15>(w2); - map_delmap(name); - } - else if (w1 == "npc"_s) - { - npc_addsrcfile(w2); - } - else if (w1 == "delnpc"_s) - { - npc_delsrcfile(w2); - } - else if (w1 == "autosave_time"_s) - { - autosave_time = std::chrono::seconds(atoi(w2.c_str())); - if (autosave_time <= interval_t::zero()) - autosave_time = DEFAULT_AUTOSAVE_INTERVAL; - } - else if (w1 == "motd_txt"_s) - { - motd_txt = w2; - } - else if (w1 == "mapreg_txt"_s) - { - mapreg_txt = w2; - } - else if (w1 == "gm_log"_s) - { - gm_log = std::move(w2); - } - else if (w1 == "log_file"_s) - { - map_set_logfile(w2); - } - else if (w1 == "import"_s) - { - rv &= map_config_read(w2); - } - } - - return rv; -} - static void cleanup_sub(dumb_ptr<block_list> bl) { @@ -1629,17 +1459,86 @@ void cleanup_sub(dumb_ptr<block_list> bl) } } +int compare_item(Item *a, Item *b) +{ + return (a->nameid == b->nameid); +} + +static +bool map_config(io::Spanned<XString> key, io::Spanned<ZString> value) +{ + return parse_map_conf(map_conf, key, value); +} + +static +bool battle_config_(io::Spanned<XString> key, io::Spanned<ZString> value) +{ + return parse_battle_conf(battle_config, key, value); +} + +static +bool map_confs(io::Spanned<XString> key, io::Spanned<ZString> value) +{ + if (key.data == "map_conf"_s) + return load_config_file(value.data, map_config); + if (key.data == "battle_conf"_s) + return load_config_file(value.data, battle_config_); + if (key.data == "atcommand_conf"_s) + return atcommand_config_read(value.data); + + if (key.data == "item_db"_s) + return itemdb_readdb(value.data); + if (key.data == "mob_db"_s) + return mob_readdb(value.data); + if (key.data == "quest_db"_s) + return quest_readdb(value.data); + if (key.data == "mob_skill_db"_s) + 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); + if (key.data == "const_db"_s) + return read_constdb(value.data); + key.span.error("Unknown meta-key for map server"_s); + return false; +} + +int map_scriptcont(dumb_ptr<map_session_data> sd, BlockId id) +{ + dumb_ptr<block_list> bl = map_id2bl(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; +} +} // namespace map + /*========================================== * map鯖終了時処理 *------------------------------------------ */ void term_func(void) { + using namespace tmwa::map; for (auto& mit : maps_db) { if (!mit.second->gat) continue; - map_local *map_id = static_cast<map_local *>(mit.second.get()); + P<map_local> map_id = borrow(*mit.second).downcast_to<map_local>(); map_foreachinarea(cleanup_sub, map_id, @@ -1662,46 +1561,14 @@ void term_func(void) map_close_logfile(); } -int compare_item(Item *a, Item *b) -{ - return (a->nameid == b->nameid); -} - -static -bool map_confs(XString key, ZString value) -{ - if (key == "map_conf"_s) - return map_config_read(value); - if (key == "battle_conf"_s) - return battle_config_read(value); - if (key == "atcommand_conf"_s) - return atcommand_config_read(value); - - if (key == "item_db"_s) - return itemdb_readdb(value); - if (key == "mob_db"_s) - return mob_readdb(value); - if (key == "mob_skill_db"_s) - return mob_readskilldb(value); - if (key == "skill_db"_s) - return skill_readdb(value); - if (key == "magic_conf"_s) - return magic::load_magic_file_v2(value); - - if (key == "resnametable"_s) - return load_resnametable(value); - if (key == "const_db"_s) - return read_constdb(value); - PRINTF("unknown map conf key: %s\n"_fmt, AString(key)); - return false; -} - /*====================================================== * Map-Server Init and Command-line Arguments [Valaris] *------------------------------------------------------ */ int do_init(Slice<ZString> argv) { + using namespace tmwa::map; + ZString argv0 = argv.pop_front(); runflag &= magic::magic_init0(); @@ -1749,7 +1616,8 @@ int do_init(Slice<ZString> argv) if (!loaded_config_yet) runflag &= load_config_file("conf/tmwa-map.conf"_s, map_confs); - battle_config_check(); + map_set_logfile(); + runflag &= map_readallmap(); do_init_chrif(); @@ -1767,26 +1635,7 @@ int do_init(Slice<ZString> argv) PRINTF("The server is running in " SGR_BOLD SGR_RED "PK Mode" SGR_RESET "\n"_fmt); PRINTF("The map-server is " SGR_BOLD SGR_GREEN "ready" SGR_RESET " (Server is listening on the port %d).\n\n"_fmt, - clif_getport()); - - return 0; -} - -int map_scriptcont(dumb_ptr<map_session_data> sd, BlockId id) -{ - dumb_ptr<block_list> bl = map_id2bl(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; - } + map_conf.map_port); return 0; } diff --git a/src/map/map.hpp b/src/map/map.hpp index d88ff54..f57dcee 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -20,17 +20,16 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include "fwd.hpp" - #include "map.t.hpp" +#include "fwd.hpp" + #include <chrono> #include <functional> #include <list> #include "../ints/udl.hpp" -#include "../strings/fwd.hpp" #include "../strings/rstring.hpp" #include "../strings/astring.hpp" #include "../strings/vstring.hpp" @@ -42,18 +41,19 @@ #include "../net/socket.hpp" #include "../net/timer.t.hpp" -#include "../mmo/utils.hpp" - #include "battle.t.hpp" -#include "clif.t.hpp" +#include "../mmo/clif.t.hpp" #include "mapflag.hpp" #include "mob.t.hpp" -#include "script.hpp" // change to script.t.hpp -#include "skill.t.hpp" +#include "script-buffer.hpp" +#include "script-persist.hpp" +#include "../mmo/skill.t.hpp" namespace tmwa { +namespace map +{ constexpr int MAX_NPC_PER_MAP = 512; constexpr int BLOCK_SIZE = 8; #define AREA_SIZE battle_config.area_size @@ -61,47 +61,19 @@ constexpr std::chrono::seconds LIFETIME_FLOORITEM = 1_min; constexpr int MAX_SKILL_LEVEL = 100; constexpr int MAX_EVENTTIMER = 32; constexpr interval_t NATURAL_HEAL_INTERVAL = 500_ms; -constexpr BlockId MAX_FLOORITEM = wrap<BlockId>(500000_u32); constexpr int MAX_LEVEL = 255; constexpr int MAX_WALKPATH = 48; constexpr int MAX_DROP_PER_MAP = 48; -constexpr interval_t DEFAULT_AUTOSAVE_INTERVAL = 1_min; - -// formerly VString<49>, as name::label -struct NpcEvent -{ - NpcName npc; - ScriptLabel label; +constexpr std::chrono::seconds DEFAULT_AUTOSAVE_INTERVAL = 1_min; - explicit operator bool() - { - return npc || label; - } - bool operator !() - { - return !bool(*this); - } - - friend bool operator == (const NpcEvent& l, const NpcEvent& r) - { - return l.npc == r.npc && l.label == r.label; - } - - friend bool operator < (const NpcEvent& l, const NpcEvent& r) - { - return l.npc < r.npc || (l.npc == r.npc && l.label < r.label); - } - - friend VString<49> convert_for_printf(NpcEvent ev); -}; -bool extract(XString str, NpcEvent *ev); +extern map_local undefined_gat; struct block_list { dumb_ptr<block_list> bl_next, bl_prev; BlockId bl_id; - map_local *bl_m; + Borrowed<map_local> bl_m = borrow(undefined_gat); short bl_x, bl_y; BL bl_type; @@ -156,7 +128,7 @@ struct map_session_data : block_list, SessionData unsigned dead_sit:2; unsigned skillcastcancel:1; unsigned waitingdisconnect:1; - unsigned lr_flag:2; + unsigned lr_flag_is_arrow_2:1; unsigned connect_new:1; unsigned arrow_atk:1; BF attack_type;//:3; @@ -170,11 +142,10 @@ struct map_session_data : block_list, SessionData unsigned shroud_disappears_on_pickup:1; unsigned shroud_disappears_on_talk:1; unsigned seen_motd:1; + unsigned pvpchannel; } state; struct { - unsigned killer:1; - unsigned killable:1; unsigned unbreakable_weapon:1; unsigned unbreakable_armor:1; unsigned deaf:1; @@ -185,7 +156,19 @@ struct map_session_data : block_list, SessionData unsigned char tmw_version; // tmw client version CharKey status_key; CharData status; - GenericArray<struct item_data *, InventoryIndexing<IOff0, MAX_INVENTORY>> inventory_data; + GenericArray<Option<Borrowed<struct item_data>>, InventoryIndexing<IOff0, MAX_INVENTORY>> inventory_data = + {{ + None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, + }}; // explicit is better than implicit earray<IOff0, EQUIP, EQUIP::COUNT> equip_index_maybe; int weight, max_weight; MapName mapname_; @@ -205,13 +188,12 @@ struct map_session_data : block_list, SessionData int npc_amount; // I have no idea exactly what these are doing ... // but one should probably be replaced with a ScriptPointer ??? - const ScriptBuffer *npc_script, *npc_scriptroot; + Option<Borrowed<const ScriptBuffer>> npc_script = None, npc_scriptroot = None; std::vector<struct script_data> npc_stackbuf; RString npc_str; struct { unsigned storage:1; - unsigned divorce:1; } npc_flags; Timer attacktimer; @@ -235,7 +217,7 @@ struct map_session_data : block_list, SessionData short spellpower_bonus_target, spellpower_bonus_current; // [Fate] Spellpower boni. _current is the active one. //_current slowly approximates _target, and _target is determined by equipment. - short attackrange, attackrange_; + short attackrange; // [Fate] Used for gradual healing; amount of enqueued regeneration struct quick_regeneration quick_regeneration_hp, quick_regeneration_sp; @@ -249,18 +231,17 @@ struct map_session_data : block_list, SessionData interval_t hp_sub, sp_sub; interval_t inchealhptick, inchealsptick; - ItemLook weapontype1, weapontype2; + ItemLook weapontype1; earray<int, ATTR, ATTR::COUNT> paramb, paramc, parame, paramcard; int hit, flee, flee2; interval_t aspd, amotion, dmotion; int watk, watk2; int def, def2, mdef, mdef2, critical, matk1, matk2; int hprate, sprate, dsprate; - int watk_, watk_2; int base_atk, atk_rate; int arrow_atk; int arrow_cri, arrow_hit, arrow_range; - int nhealhp, nhealsp, nshealhp, nshealsp, nsshealhp, nsshealsp; + int nhealhp, nhealsp; int aspd_rate, speed_rate, hprecov_rate, sprecov_rate, critical_def, double_rate; int matk_rate; @@ -269,9 +250,6 @@ struct map_session_data : block_list, SessionData mdef_rate, mdef2_rate; int double_add_rate, speed_add_rate, aspd_add_rate, perfect_hit_add; short hp_drain_rate, hp_drain_per, sp_drain_rate, sp_drain_per; - short hp_drain_rate_, hp_drain_per_, sp_drain_rate_, sp_drain_per_; - short break_weapon_rate, break_armor_rate; - short add_steal_rate; int die_counter; @@ -283,7 +261,6 @@ struct map_session_data : block_list, SessionData Map<SIR, RString> regstrm; earray<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data; - short sc_count; AccountId trade_partner; Array<IOff2, TRADE_MAX> deal_item_index; @@ -298,11 +275,8 @@ struct map_session_data : block_list, SessionData PartyId partyspy; // [Syrus22] - int catch_target_class; - int pvp_point, pvp_rank; Timer pvp_timer; - int pvp_lastusers; std::list<NpcEvent> eventqueuel; Array<Timer, MAX_EVENTTIMER> eventtimer; @@ -312,14 +286,14 @@ struct map_session_data : block_list, SessionData unsigned in_progress:1; } auto_ban_info; - TimeT chat_reset_due; - TimeT chat_repeat_reset_due; + tick_t chat_reset_due; + tick_t chat_repeat_reset_due; int chat_lines_in; int chat_total_repeats; RString chat_lastmsg; tick_t flood_rates[0x220]; - TimeT packet_flood_reset_due; + tick_t packet_flood_reset_due; int packet_flood_in; IP4Address get_ip() @@ -355,7 +329,7 @@ struct npc_data : block_list Opt1 opt1; Opt2 opt2; Opt3 opt3; - Option option; + Opt0 option; short flag; std::list<RString> eventqueuel; @@ -383,6 +357,7 @@ public: std::unique_ptr<const ScriptBuffer> script; // Diameter. short xs, ys; + bool event_needs_map; // Whether the timer advances if not beyond end. bool timer_active; @@ -439,7 +414,7 @@ struct mob_data : block_list MobMode mode; struct { - map_local *m; + Borrowed<map_local> m = borrow(undefined_gat); short x0, y0, xs, ys; interval_t delay1, delay2; } spawn; @@ -478,11 +453,10 @@ struct mob_data : block_list std::vector<Item> lootitemv; earray<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data; - short sc_count; Opt1 opt1; Opt2 opt2; Opt3 opt3; - Option option; + Opt0 option; short min_chase; Timer deletetimer; @@ -514,10 +488,10 @@ struct map_abstract // gat is nullptr for map_remote and non-nullptr for map_local std::unique_ptr<MapCell[]> gat; + map_abstract() = default; + map_abstract(map_abstract&&) = default; virtual ~map_abstract() {} }; -extern -UPMap<MapName, map_abstract> maps_db; struct map_local : map_abstract { @@ -538,7 +512,7 @@ struct map_remote : map_abstract }; inline -MapCell read_gatp(map_local *m, int x, int y) +MapCell read_gatp(Borrowed<map_local> m, int x, int y) { assert (0 <= x && x < m->xs); assert (0 <= y && y < m->ys); @@ -554,12 +528,7 @@ struct flooritem_data : block_list Item item_data; }; -extern interval_t autosave_time; -extern int save_settings; - -extern AString motd_txt; - -extern CharName wisp_server_name; +extern const CharName WISP_SERVER_NAME; // 鯖全体情報 void map_setusers(int); @@ -580,21 +549,21 @@ public: int map_addblock(dumb_ptr<block_list>); int map_delblock(dumb_ptr<block_list>); void map_foreachinarea(std::function<void(dumb_ptr<block_list>)>, - map_local *, + Borrowed<map_local>, int, int, int, int, BL); // -- moonsoul (added map_foreachincell) void map_foreachincell(std::function<void(dumb_ptr<block_list>)>, - map_local *, + Borrowed<map_local>, int, int, BL); void map_foreachinmovearea(std::function<void(dumb_ptr<block_list>)>, - map_local *, + Borrowed<map_local>, int, int, int, int, int, int, BL); //block関連に追加 -int map_count_oncell(map_local *m, int x, int y); +int map_count_oncell(Borrowed<map_local> m, int x, int y); // 一時的object関連 BlockId map_addobject(dumb_ptr<block_list>); void map_delobject(BlockId, BL type); @@ -604,7 +573,7 @@ void map_foreachobject(std::function<void(dumb_ptr<block_list>)>, // void map_quit(dumb_ptr<map_session_data>); // npc -int map_addnpc(map_local *, dumb_ptr<npc_data>); +int map_addnpc(Borrowed<map_local>, dumb_ptr<npc_data>); void map_log(XString line); #define MAP_LOG(format, ...) \ @@ -612,7 +581,7 @@ void map_log(XString line); #define MAP_LOG_PC(sd, fmt, ...) \ MAP_LOG("PC%d %s:%d,%d " fmt, \ - sd->status_key.char_id, (sd->bl_m ? sd->bl_m->name_ : stringish<MapName>("undefined.gat"_s)), sd->bl_x, sd->bl_y, ## __VA_ARGS__) + sd->status_key.char_id, (sd->bl_m->name_), sd->bl_x, sd->bl_y, ## __VA_ARGS__) // 床アイテム関連 void map_clearflooritem_timer(TimerData *, tick_t, BlockId); @@ -622,17 +591,15 @@ void map_clearflooritem(BlockId id) map_clearflooritem_timer(nullptr, tick_t(), id); } BlockId map_addflooritem_any(Item *, int amount, - map_local *m, int x, int y, + Borrowed<map_local> m, int x, int y, dumb_ptr<map_session_data> *owners, interval_t *owner_protection, interval_t lifetime, int dispersal); BlockId map_addflooritem(Item *, int, - map_local *, int, int, + Borrowed<map_local>, int, int, dumb_ptr<map_session_data>, dumb_ptr<map_session_data>, dumb_ptr<map_session_data>); // キャラid=>キャラ名 変換関連 -extern -DMap<BlockId, dumb_ptr<block_list>> id_db; void map_addchariddb(CharId charid, CharName name); CharName map_charid2nick(CharId); @@ -671,8 +638,8 @@ dumb_ptr<magic::invocation> map_id_is_spell(BlockId id) } -map_local *map_mapname2mapid(MapName); -int map_mapname2ipport(MapName, IP4Address *, int *); +Option<Borrowed<map_local>> map_mapname2mapid(MapName); +int map_mapname2ipport(MapName, Borrowed<IP4Address>, Borrowed<int>); int map_setipport(MapName name, IP4Address ip, int port); void map_addiddb(dumb_ptr<block_list>); void map_deliddb(dumb_ptr<block_list> bl); @@ -689,14 +656,14 @@ dumb_ptr<map_session_data> map_get_prev_session( dumb_ptr<map_session_data> current); // gat関連 -MapCell map_getcell(map_local *, int, int); -void map_setcell(map_local *, int, int, MapCell); +MapCell map_getcell(Borrowed<map_local>, int, int); +void map_setcell(Borrowed<map_local>, int, int, MapCell); // その他 bool map_check_dir(DIR s_dir, DIR t_dir); DIR map_calc_dir(dumb_ptr<block_list> src, int x, int y); -std::pair<uint16_t, uint16_t> map_randfreecell(map_local *m, +std::pair<uint16_t, uint16_t> map_randfreecell(Borrowed<map_local> m, uint16_t x, uint16_t y, uint16_t w, uint16_t h); inline dumb_ptr<map_session_data> block_list::as_player() { return dumb_ptr<map_session_data>(static_cast<map_session_data *>(this)) ; } @@ -722,4 +689,14 @@ inline dumb_ptr<npc_data_script> npc_data::is_script() { return npc_subtype == N inline dumb_ptr<npc_data_shop> npc_data::is_shop() { return npc_subtype == NpcSubtype::SHOP ? as_shop() : nullptr ; } inline dumb_ptr<npc_data_warp> npc_data::is_warp() { return npc_subtype == NpcSubtype::WARP ? as_warp() : nullptr ; } inline dumb_ptr<npc_data_message> npc_data::is_message() { return npc_subtype == NpcSubtype::MESSAGE ? as_message() : nullptr ; } + +void map_addmap(MapName mapname); +void map_delmap(MapName mapname); + +struct charid2nick +{ + CharName nick; + int req_id; +}; +} // namespace map } // namespace tmwa diff --git a/src/map/map.py b/src/map/map.py index cb29d53..c7adf56 100644 --- a/src/map/map.py +++ b/src/map/map.py @@ -1,7 +1,7 @@ class map_local(object): __slots__ = ('_value') - name = 'tmwa::map_local' + name = 'tmwa::map::map_local' depth = 1 enabled = True @@ -26,14 +26,14 @@ class map_local(object): yield '->ys', value['ys'] tests = [ - ('static_cast<tmwa::map_local *>(nullptr)', '(map_local *) nullptr'), + ('static_cast<tmwa::map::map_local *>(nullptr)', '(map_local *) nullptr'), ('fake_map_local("map"_s, 42, 404)', '(map_local *) = {->name = "map", ->xs = 42, ->ys = 404}'), ] class map_remote(object): __slots__ = ('_value') - name = 'tmwa::map_remote' + name = 'tmwa::map::map_remote' depth = 1 enabled = True @@ -58,14 +58,14 @@ class map_remote(object): yield '->port', value['port'] tests = [ - ('static_cast<tmwa::map_remote *>(nullptr)', '(map_remote *) nullptr'), + ('static_cast<tmwa::map::map_remote *>(nullptr)', '(map_remote *) nullptr'), ('fake_map_remote("map"_s, tmwa::IP4Address({8, 8, 8, 8}), 6667)', '(map_remote *) = {->name = "map", ->ip = 8.8.8.8, ->port = 6667}'), ] class map_abstract(object): __slots__ = ('_value') - name = 'tmwa::map_abstract' + name = 'tmwa::map::map_abstract' depth = 1 enabled = True @@ -79,19 +79,19 @@ class map_abstract(object): if value is None: return '(map_abstract *) nullptr' gat = value.dereference()['gat'] - gat = gat.address.cast(gdb.lookup_type('tmwa::map_abstract').pointer().pointer()).dereference() + gat = gat.address.cast(gdb.lookup_type('tmwa::map::map_abstract').pointer().pointer()).dereference() if gat: - return value.cast(gdb.lookup_type('tmwa::map_local').pointer()) + return value.cast(gdb.lookup_type('tmwa::map::map_local').pointer()) else: - return value.cast(gdb.lookup_type('tmwa::map_remote').pointer()) + return value.cast(gdb.lookup_type('tmwa::map::map_remote').pointer()) tests = [ - ('static_cast<tmwa::map_abstract *>(nullptr)', '(map_abstract *) nullptr'), + ('static_cast<tmwa::map::map_abstract *>(nullptr)', '(map_abstract *) nullptr'), ] + [ - ('static_cast<tmwa::map_abstract *>(%s); value->gat.reset(new tmwa::MapCell[1])' % expr, expected) + ('static_cast<tmwa::map::map_abstract *>(%s); value->gat.reset(new tmwa::map::MapCell[1])' % expr, expected) for (expr, expected) in map_local.tests[1:] ] + [ - ('static_cast<tmwa::map_abstract *>(%s)' % expr, expected) + ('static_cast<tmwa::map::map_abstract *>(%s)' % expr, expected) for (expr, expected) in map_remote.tests[1:] ] @@ -99,9 +99,9 @@ class map_abstract(object): using tmwa::operator "" _s; inline - tmwa::map_local *fake_map_local(tmwa::ZString name, int xs, int ys) + tmwa::map::map_local *fake_map_local(tmwa::ZString name, int xs, int ys) { - auto *p = new tmwa::map_local{}; + auto *p = new tmwa::map::map_local{}; p->name_ = tmwa::stringish<tmwa::MapName>(name); p->xs = xs; p->ys = ys; @@ -109,17 +109,17 @@ class map_abstract(object): } inline - tmwa::map_remote *fake_map_remote(tmwa::ZString name, tmwa::IP4Address ip, uint16_t port) + tmwa::map::map_remote *fake_map_remote(tmwa::ZString name, tmwa::IP4Address ip, uint16_t port) { - auto *p = new tmwa::map_remote{}; + auto *p = new tmwa::map::map_remote{}; p->name_ = tmwa::stringish<tmwa::MapName>(name); p->ip = ip; p->port = port; return p; } - void fake_delete(tmwa::map_abstract *); - void fake_delete(tmwa::map_abstract *map) + void fake_delete(tmwa::map::map_abstract *); + void fake_delete(tmwa::map::map_abstract *map) { delete map; } diff --git a/src/map/map.t.hpp b/src/map/map.t.hpp index b475f9b..267c049 100644 --- a/src/map/map.t.hpp +++ b/src/map/map.t.hpp @@ -29,11 +29,13 @@ #include "../generic/enum.hpp" #include "../mmo/ids.hpp" -#include "../mmo/mmo.hpp" +#include "../high/mmo.hpp" namespace tmwa { +namespace map +{ enum class BL : uint8_t { NUL, @@ -191,13 +193,9 @@ ENUM_BITWISE_OPERATORS(MapCell) } using e::MapCell; -struct MobName : VString<23> {}; -struct NpcName : VString<23> {}; -struct ScriptLabel : VString<23> {}; -struct ItemName : VString<23> {}; - inline BlockId account_to_block(AccountId a) { return wrap<BlockId>(unwrap<AccountId>(a)); } inline AccountId block_to_account(BlockId b) { return wrap<AccountId>(unwrap<BlockId>(b)); } +} // namespace map } // namespace tmwa diff --git a/src/map/mapflag.cpp b/src/map/mapflag.cpp index be2ae67..9f3c9ab 100644 --- a/src/map/mapflag.cpp +++ b/src/map/mapflag.cpp @@ -18,11 +18,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. +#include "../strings/xstring.hpp" + #include "../poison.hpp" namespace tmwa { +namespace map +{ // because bitfields, that's why bool MapFlags::get(MapFlag mf) const @@ -38,7 +42,7 @@ void MapFlags::set(MapFlag mf, bool val) flags &=~ static_cast<unsigned>(mf); } -bool extract(XString str, MapFlag *mf) +bool impl_extract(XString str, MapFlag *mf) { const struct { @@ -91,4 +95,5 @@ MapFlag map_flag_from_int(int shift) { return static_cast<MapFlag>(1 << shift); } +} // namespace map } // namespace tmwa diff --git a/src/map/mapflag.hpp b/src/map/mapflag.hpp index 6d046fa..3538c56 100644 --- a/src/map/mapflag.hpp +++ b/src/map/mapflag.hpp @@ -22,11 +22,11 @@ #include <cstdint> -#include "../mmo/extract.hpp" // TODO remove this (requires specializing the *other* half) - namespace tmwa { +namespace map +{ // originally from script.cpp // These are part of the script API, so they can't change ever, // even though they are silly. @@ -77,7 +77,8 @@ public: void set(MapFlag, bool); }; -bool extract(XString str, MapFlag *mf); +bool impl_extract(XString str, MapFlag *mf); MapFlag map_flag_from_int(int shift); +} // namespace map } // namespace tmwa diff --git a/src/map/mapflag.py b/src/map/mapflag.py index fe5b016..b0a2f24 100644 --- a/src/map/mapflag.py +++ b/src/map/mapflag.py @@ -1,6 +1,6 @@ class MapFlags(object): __slots__ = ('_value') - name = 'tmwa::MapFlags' + name = 'tmwa::map::MapFlags' enabled = True def __init__(self, value): @@ -52,12 +52,12 @@ class MapFlags(object): ('RESAVE', 30), ] tests = [ - ('reinterpret_cast<const tmwa::MapFlags&>(static_cast<const unsigned int&>(0x80000000))', 'MapFlags(0x80000000)'), - ('reinterpret_cast<const tmwa::MapFlags&>(static_cast<const unsigned int&>(0xf0000000))', 'MapFlags(TOWN | OUTSIDE | RESAVE | 0x80000000)'), + ('reinterpret_cast<const tmwa::map::MapFlags&>(static_cast<const unsigned int&>(0x80000000))', 'MapFlags(0x80000000)'), + ('reinterpret_cast<const tmwa::map::MapFlags&>(static_cast<const unsigned int&>(0xf0000000))', 'MapFlags(TOWN | OUTSIDE | RESAVE | 0x80000000)'), ] + [ - ('tmwa::MapFlags(); value.set(tmwa::MapFlag::%s, true)' % n, 'MapFlags(%s)' % n) + ('tmwa::map::MapFlags(); value.set(tmwa::map::MapFlag::%s, true)' % n, 'MapFlags(%s)' % n) for (n, _) in junk ] + [ - ('reinterpret_cast<const tmwa::MapFlags&>(static_cast<const unsigned int&>(1 << %d))' % i, 'MapFlags(%s)' % n) + ('reinterpret_cast<const tmwa::map::MapFlags&>(static_cast<const unsigned int&>(1 << %d))' % i, 'MapFlags(%s)' % n) for (n, i) in junk ] diff --git a/src/map/mob.cpp b/src/map/mob.cpp index dd061d0..539b547 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -36,18 +36,20 @@ #include "../generic/random.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" +#include "../io/extract.hpp" #include "../io/read.hpp" #include "../net/socket.hpp" #include "../net/timer.hpp" #include "../mmo/config_parse.hpp" -#include "../mmo/extract.hpp" +#include "../mmo/cxxstdio_enums.hpp" #include "../mmo/extract_enums.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "clif.hpp" +#include "globals.hpp" #include "itemdb.hpp" #include "map.hpp" #include "npc.hpp" @@ -61,6 +63,8 @@ namespace tmwa { +namespace map +{ constexpr interval_t MIN_MOBTHINKTIME = 100_ms; // Move probability in the negligent mode MOB (rate of 1000 minute) @@ -68,8 +72,6 @@ constexpr random_::Fraction MOB_LAZYMOVEPERC {50, 1000}; // Warp probability in the negligent mode MOB (rate of 1000 minute) constexpr random_::Fraction MOB_LAZYWARPPERC {20, 1000}; -static -struct mob_db_ mob_db[2001]; struct mob_db_& get_mob_db(Species s) { return mob_db[unwrap<Species>(s)]; @@ -318,12 +320,12 @@ int mob_gen_exp(mob_db_ *mob) (2 * mob->attrs[ATTR::LUK] * mob->max_hp / mod_def); double attack_factor = (mob->atk1 + mob->atk2 + mob->attrs[ATTR::STR] / 3.0 + mob->attrs[ATTR::DEX] / 2.0 + - mob->attrs[ATTR::LUK]) * (1872.0 / mob->adelay) / 4; + mob->attrs[ATTR::LUK]) * (1872.0 / mob->adelay.count()) / 4; double dodge_factor = pow(mob->lv + mob->attrs[ATTR::AGI] + mob->attrs[ATTR::LUK] / 2.0, 4.0 / 3.0); // TODO s/persuit/pursuit/g sometime when I'm not worried about diffs double persuit_factor = - (3 + mob->range) * bool(mob->mode & MobMode::CAN_MOVE) * 1000 / mob->speed; + (3 + mob->range) * bool(mob->mode & MobMode::CAN_MOVE) * 1000 / mob->speed.count(); double aggression_factor = bool(mob->mode & MobMode::AGGRESSIVE) ? 10.0 / 9.0 @@ -331,8 +333,7 @@ int mob_gen_exp(mob_db_ *mob) int xp = floor(effective_hp * pow(sqrt(attack_factor) + sqrt(dodge_factor) + sqrt(persuit_factor) + 55, 3) - * aggression_factor / 2000000.0 - * static_cast<double>(battle_config.base_exp_rate) / 100.); + * aggression_factor / 2000000.0); if (xp < 1) xp = 1; PRINTF("Exp for mob '%s' generated: %d\n"_fmt, mob->name, xp); @@ -357,10 +358,10 @@ void mob_init(dumb_ptr<mob_data> md) md->stats[mob_stat::LUK] = get_mob_db(mob_class).attrs[ATTR::LUK]; md->stats[mob_stat::ATK1] = get_mob_db(mob_class).atk1; md->stats[mob_stat::ATK2] = get_mob_db(mob_class).atk2; - md->stats[mob_stat::ADELAY] = get_mob_db(mob_class).adelay; + md->stats[mob_stat::ADELAY] = get_mob_db(mob_class).adelay.count(); md->stats[mob_stat::DEF] = get_mob_db(mob_class).def; md->stats[mob_stat::MDEF] = get_mob_db(mob_class).mdef; - md->stats[mob_stat::SPEED] = get_mob_db(mob_class).speed; + md->stats[mob_stat::SPEED] = get_mob_db(mob_class).speed.count(); md->stats[mob_stat::XP_BONUS] = MOB_XP_BONUS_BASE; for (i = 0; i < mutations_nr; i++) @@ -410,15 +411,15 @@ BlockId mob_once_spawn(dumb_ptr<map_session_data> sd, NpcEvent event) { dumb_ptr<mob_data> md = nullptr; - map_local *m; int count; - if (sd && mapname == MOB_THIS_MAP) - m = sd->bl_m; - else - m = map_mapname2mapid(mapname); + P<map_local> m = ( + (sd && mapname == MOB_THIS_MAP) + ? sd->bl_m + : TRY_UNWRAP(map_mapname2mapid(mapname), return BlockId()) + ); - if (m == nullptr || amount <= 0 || mobdb_checkid(mob_class) == Species()) + if (amount <= 0 || mobdb_checkid(mob_class) == Species()) return BlockId(); if (sd) @@ -470,18 +471,18 @@ BlockId mob_once_spawn_area(dumb_ptr<map_session_data> sd, { int x, y, i, max, lx = -1, ly = -1; BlockId id; - map_local *m; - if (mapname == MOB_THIS_MAP) - m = sd->bl_m; - else - m = map_mapname2mapid(mapname); + P<map_local> m = ( + (mapname == MOB_THIS_MAP) + ? sd->bl_m + : TRY_UNWRAP(map_mapname2mapid(mapname), return BlockId()) + ); max = (y1 - y0 + 1) * (x1 - x0 + 1) * 3; if (max > 1000) max = 1000; - if (m == nullptr || amount <= 0 || (mobdb_checkid(mob_class) == Species())) // A summon is stopped if a value is unusual + if (amount <= 0 || (mobdb_checkid(mob_class) == Species())) // A summon is stopped if a value is unusual return BlockId(); for (i = 0; i < amount; i++) @@ -1148,7 +1149,7 @@ int mob_spawn(BlockId id) mob_init(md); if (!md->stats[mob_stat::SPEED]) - md->stats[mob_stat::SPEED] = get_mob_db(md->mob_class).speed; + md->stats[mob_stat::SPEED] = get_mob_db(md->mob_class).speed.count(); md->def_ele = get_mob_db(md->mob_class).element; md->master_id = BlockId(); md->master_dist = 0; @@ -1178,11 +1179,10 @@ int mob_spawn(BlockId id) assert (!md->sc_data[i].timer); md->sc_data[i].val1 = 0; } - md->sc_count = 0; md->opt1 = Opt1::ZERO; md->opt2 = Opt2::ZERO; md->opt3 = Opt3::ZERO; - md->option = Option::ZERO; + md->option = Opt0::ZERO; md->hp = battle_get_max_hp(md); if (md->hp <= 0) @@ -1342,7 +1342,7 @@ int mob_target(dumb_ptr<mob_data> md, dumb_ptr<block_list> bl, int dist) nullpo_retz(bl); sc_data = battle_get_sc_data(bl); - Option *option = battle_get_option(bl); + Opt0 *option = battle_get_option(bl); Race race = get_mob_db(md->mob_class).race; if (md->mode == MobMode::ZERO) @@ -1572,7 +1572,7 @@ int mob_ai_sub_hard_slavemob(dumb_ptr<mob_data> md, tick_t tick) // Since it is in the map on which the master is not, teleport is carried out and it pursues. if (mmd->bl_m != md->bl_m) { - mob_warp(md, mmd->bl_m, mmd->bl_x, mmd->bl_y, BeingRemoveWhy::WARPED); + mob_warp(md, Some(mmd->bl_m), mmd->bl_x, mmd->bl_y, BeingRemoveWhy::WARPED); md->state.master_check = 1; return 0; } @@ -1584,7 +1584,7 @@ int mob_ai_sub_hard_slavemob(dumb_ptr<mob_data> md, tick_t tick) // Since the master was in near immediately before, teleport is carried out and it pursues. if (old_dist < 10 && md->master_dist > 18) { - mob_warp(md, nullptr, mmd->bl_x, mmd->bl_y, BeingRemoveWhy::WARPED); + mob_warp(md, None, mmd->bl_x, mmd->bl_y, BeingRemoveWhy::WARPED); md->state.master_check = 1; return 0; } @@ -2177,7 +2177,7 @@ void mob_ai_lazy(TimerData *, tick_t tick) */ struct delay_item_drop { - map_local *m; + Borrowed<map_local> m = borrow(undefined_gat); int x, y; ItemNameId nameid; int amount; @@ -2186,7 +2186,7 @@ struct delay_item_drop struct delay_item_drop2 { - map_local *m; + Borrowed<map_local> m = borrow(undefined_gat); int x, y; Item item_data; dumb_ptr<map_session_data> first_sd, second_sd, third_sd; @@ -2541,7 +2541,6 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage, int base_exp, job_exp, flag = 1; double per; - PartyPair p; // [Fate] The above is the old formula. We do a more involved computation below. // [o11c] Look in git history for old code, you idiot! @@ -2592,16 +2591,17 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage, ); if (it == ptv.end()) { - p = party_search(pid); - if (p && p->exp != 0) + Option<PartyPair> p_ = party_search(pid); + OMATCH_BEGIN_SOME (p, p_) { - DmgLogParty pn {}; - pn.p = p; - pn.base_exp = base_exp; - pn.job_exp = job_exp; - ptv.push_back(pn); - flag = 0; + if (p->exp != 0) + { + DmgLogParty pn{p, base_exp, job_exp}; + ptv.push_back(pn); + flag = 0; + } } + OMATCH_END (); } else { @@ -2763,7 +2763,7 @@ int mob_warpslave(dumb_ptr<mob_data> md, int x, int y) * mobワープ *------------------------------------------ */ -int mob_warp(dumb_ptr<mob_data> md, map_local *m, int x, int y, BeingRemoveWhy type) +int mob_warp(dumb_ptr<mob_data> md, Option<Borrowed<map_local>> m_, int x, int y, BeingRemoveWhy type) { int i = 0, xs = 0, ys = 0, bx = x, by = y; @@ -2772,8 +2772,7 @@ int mob_warp(dumb_ptr<mob_data> md, map_local *m, int x, int y, BeingRemoveWhy t if (md->bl_prev == nullptr) return 0; - if (m == nullptr) - m = md->bl_m; + P<map_local> m = m_.copy_or(md->bl_m); if (type != BeingRemoveWhy::NEGATIVE1) { @@ -2892,10 +2891,10 @@ int mob_summonslave(dumb_ptr<mob_data> md2, int *value_, int amount, int flag) bx = md2->bl_x; by = md2->bl_y; - map_local *m = md2->bl_m; + P<map_local> m = md2->bl_m; Species values[5]; - for (count = 0; count < 5 && values[count] != Species(); ++count) + for (count = 0; count < 5 && value_[count]; ++count) values[count] = wrap<Species>(value_[count]); if (count < 1) return 0; @@ -3417,10 +3416,10 @@ int mob_makedummymobdb(Species mob_class) get_mob_db(mob_class).race = Race::formless; get_mob_db(mob_class).element = LevelElement{0, Element::neutral}; get_mob_db(mob_class).mode = MobMode::ZERO; - get_mob_db(mob_class).speed = 300; - get_mob_db(mob_class).adelay = 1000; - get_mob_db(mob_class).amotion = 500; - get_mob_db(mob_class).dmotion = 500; + get_mob_db(mob_class).speed = 300_ms; + get_mob_db(mob_class).adelay = 1000_ms; + get_mob_db(mob_class).amotion = 500_ms; + get_mob_db(mob_class).dmotion = 500_ms; for (i = 0; i < 8; i++) { get_mob_db(mob_class).dropitem[i].nameid = ItemNameId(); @@ -3430,7 +3429,7 @@ int mob_makedummymobdb(Species mob_class) } static -bool extract(XString str, LevelElement *le) +bool impl_extract(XString str, LevelElement *le) { int tmp; if (extract(str, &tmp)) @@ -3531,30 +3530,33 @@ bool mob_readdb(ZString filename) continue; } - // TODO move this lower - get_mob_db(mob_class) = std::move(mdbv); - if (get_mob_db(mob_class).base_exp < 0) - get_mob_db(mob_class).base_exp = 0; - else if (get_mob_db(mob_class).base_exp > 0 - && (get_mob_db(mob_class).base_exp * - battle_config.base_exp_rate / 100 > 1000000000 - || get_mob_db(mob_class).base_exp * - battle_config.base_exp_rate / 100 < 0)) - get_mob_db(mob_class).base_exp = 1000000000; - else - get_mob_db(mob_class).base_exp = get_mob_db(mob_class).base_exp * battle_config.base_exp_rate / 100; - + { + PRINTF("bad mob line: Xp needs to be greater than 0. %s\n"_fmt, line); + rv = false; + continue; + } + if (get_mob_db(mob_class).base_exp > 1000000000) + { + PRINTF("bad mob line: Xp needs to be less than 1000000000. %s\n"_fmt, line); + rv = false; + continue; + } if (get_mob_db(mob_class).job_exp < 0) - get_mob_db(mob_class).job_exp = 0; - else if (get_mob_db(mob_class).job_exp > 0 - && (get_mob_db(mob_class).job_exp * battle_config.job_exp_rate / - 100 > 1000000000 - || get_mob_db(mob_class).job_exp * - battle_config.job_exp_rate / 100 < 0)) - get_mob_db(mob_class).job_exp = 1000000000; - else - get_mob_db(mob_class).job_exp = get_mob_db(mob_class).job_exp * battle_config.job_exp_rate / 100; + { + PRINTF("bad mob line: Job Xp needs to be greater than 0. %s\n"_fmt, line); + rv = false; + continue; + } + if (get_mob_db(mob_class).job_exp > 1000000000) + { + PRINTF("bad mob line: Job Xp needs to be less than 1000000000. %s\n"_fmt, line); + rv = false; + continue; + } + + // TODO move this lower + get_mob_db(mob_class) = std::move(mdbv); for (int i = 0; i < 8; i++) { @@ -3585,7 +3587,7 @@ bool mob_readdb(ZString filename) } static -bool extract(XString str, MobSkillCondition *msc) +bool impl_extract(XString str, MobSkillCondition *msc) { const struct { @@ -3609,7 +3611,7 @@ bool extract(XString str, MobSkillCondition *msc) } static -bool extract(XString str, MobSkillState *mss) +bool impl_extract(XString str, MobSkillState *mss) { const struct { @@ -3632,7 +3634,7 @@ bool extract(XString str, MobSkillState *mss) } static -bool extract(XString str, MobSkillTarget *mst) +bool impl_extract(XString str, MobSkillTarget *mst) { const struct { @@ -3745,4 +3747,5 @@ void do_init_mob2(void) MIN_MOBTHINKTIME * 10 ).detach(); } +} // namespace map } // namespace tmwa diff --git a/src/map/mob.hpp b/src/map/mob.hpp index d0cc07a..6d87228 100644 --- a/src/map/mob.hpp +++ b/src/map/mob.hpp @@ -20,24 +20,25 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include "fwd.hpp" - #include "mob.t.hpp" -#include "../generic/fwd.hpp" +#include "fwd.hpp" + #include "../generic/enum.hpp" #include "../generic/random.t.hpp" #include "../net/timer.t.hpp" #include "battle.t.hpp" -#include "clif.t.hpp" +#include "../mmo/clif.t.hpp" #include "map.hpp" -#include "skill.t.hpp" +#include "../mmo/skill.t.hpp" namespace tmwa { +namespace map +{ #define ENGLISH_NAME stringish<MobName>("--en--"_s) #define JAPANESE_NAME stringish<MobName>("--ja--"_s) #define MOB_THIS_MAP stringish<MapName>("this"_s) @@ -72,7 +73,7 @@ struct mob_db_ Race race; LevelElement element; MobMode mode; - int speed, adelay, amotion, dmotion; + interval_t speed, adelay, amotion, dmotion; int mutations_nr, mutation_power; struct { @@ -127,7 +128,7 @@ int mob_deleteslave(dumb_ptr<mob_data> md); int mob_counttargeted(dumb_ptr<mob_data> md, dumb_ptr<block_list> src, ATK target_lv); -int mob_warp(dumb_ptr<mob_data> md, map_local *m, int x, int y, BeingRemoveWhy type); +int mob_warp(dumb_ptr<mob_data> md, Option<Borrowed<map_local>> m, int x, int y, BeingRemoveWhy type); int mobskill_use(dumb_ptr<mob_data> md, tick_t tick, MobSkillCondition event); int mobskill_event(dumb_ptr<mob_data> md, BF flag); @@ -136,4 +137,5 @@ void mobskill_castend_pos(TimerData *tid, tick_t tick, BlockId id); int mob_summonslave(dumb_ptr<mob_data> md2, int *value, int amount, int flag); void mob_reload(void); +} // namespace map } // namespace tmwa diff --git a/src/map/mob.t.hpp b/src/map/mob.t.hpp index 160a8a3..54e7ebe 100644 --- a/src/map/mob.t.hpp +++ b/src/map/mob.t.hpp @@ -27,6 +27,8 @@ namespace tmwa { +namespace map +{ enum class MobSkillTarget { MST_TARGET = 0, @@ -61,4 +63,5 @@ enum class MobSkillState : uint8_t MSS_LOOT, MSS_CHASE, }; +} // namespace map } // namespace tmwa diff --git a/src/map/magic-interpreter.cpp b/src/map/npc-internal.hpp index 389a821..993263f 100644 --- a/src/map/magic-interpreter.cpp +++ b/src/map/npc-internal.hpp @@ -1,7 +1,9 @@ -#include "magic-interpreter.hpp" -// magic-interpreter.cpp - Old magic. +#pragma once +// npc-internal.hpp - Noncombatants. // -// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> // // This file is part of The Mana World (Athena server) // @@ -18,12 +20,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include "../poison.hpp" +#include "npc.hpp" +#include "fwd.hpp" namespace tmwa { -namespace magic +namespace map { -} // namespace magic +struct event_data +{ + dumb_ptr<npc_data_script> nd; + int pos; +}; +} // namespace map } // namespace tmwa diff --git a/src/map/npc-parse.cpp b/src/map/npc-parse.cpp new file mode 100644 index 0000000..4d9fcbd --- /dev/null +++ b/src/map/npc-parse.cpp @@ -0,0 +1,807 @@ +#include "npc-parse.hpp" +// npc-parse.cpp - Noncombatants. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include <list> + +#include "../compat/nullpo.hpp" + +#include "../strings/astring.hpp" +#include "../strings/xstring.hpp" +#include "../strings/literal.hpp" + +#include "../generic/enum.hpp" + +#include "../io/cxxstdio.hpp" +#include "../io/extract.hpp" +#include "../io/line.hpp" + +#include "../mmo/config_parse.hpp" + +#include "../high/extract_mmo.hpp" + +#include "../ast/npc.hpp" + +#include "battle.hpp" +#include "battle_conf.hpp" +#include "clif.hpp" +#include "globals.hpp" +#include "itemdb.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "npc-internal.hpp" +#include "script-parse.hpp" + +#include "../poison.hpp" + + +namespace tmwa +{ +namespace map +{ +static +void npc_clearsrcfile(void) +{ + npc_srcs.clear(); +} + +void npc_addsrcfile(AString name) +{ + if (name == "clear"_s) + { + npc_clearsrcfile(); + return; + } + + npc_srcs.push_back(name); +} + +void npc_delsrcfile(XString name) +{ + if (name == "all"_s) + { + npc_clearsrcfile(); + return; + } + + for (auto it = npc_srcs.begin(); it != npc_srcs.end(); ++it) + { + if (*it == name) + { + npc_srcs.erase(it); + return; + } + } +} + +static +void register_npc_name(dumb_ptr<npc_data> nd) +{ + earray<LString, NpcSubtype, NpcSubtype::COUNT> types //= + {{ + "WARP"_s, + "SHOP"_s, + "SCRIPT"_s, + "MESSAGE"_s, + }}; + if (!nd->name) + { + if (nd->npc_subtype == NpcSubtype::MESSAGE) + return; + PRINTF("WARNING: npc with no name:\n%s @ %s,%d,%d\n"_fmt, + types[nd->npc_subtype], + nd->bl_m->name_, nd->bl_x, nd->bl_y); + return; + } + if (dumb_ptr<npc_data> nd_old = npcs_by_name.get(nd->name)) + { + if (nd->npc_subtype != NpcSubtype::WARP + || nd_old->npc_subtype != NpcSubtype::WARP) + { + PRINTF("WARNING: replacing npc with name: %s\n"_fmt, nd->name); + PRINTF("old: %s @ %s,%d,%d\n"_fmt, + types[nd_old->npc_subtype], + nd_old->bl_m->name_, nd_old->bl_x, nd_old->bl_y); + PRINTF("new: %s @ %s,%d,%d\n"_fmt, + types[nd->npc_subtype], + nd->bl_m->name_, nd->bl_x, nd->bl_y); + } + } + // TODO also check #s ? + npcs_by_name.put(nd->name, nd); +} + +// extern for atcommand @addwarp +bool npc_load_warp(ast::npc::Warp& warp) +{ + MapName mapname = warp.m.data; + int x = warp.x.data, y = warp.y.data; + + int xs = warp.xs.data, ys = warp.ys.data; + MapName to_mapname = warp.to_m.data; + int to_x = warp.to_x.data, to_y = warp.to_y.data; + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), abort()); + + dumb_ptr<npc_data_warp> nd; + nd.new_(); + nd->bl_id = npc_get_new_npc_id(); + nd->n = map_addnpc(m, nd); + + nd->bl_prev = nd->bl_next = nullptr; + nd->bl_m = m; + nd->bl_x = x; + nd->bl_y = y; + nd->dir = DIR::S; + nd->flag = 0; + nd->name = warp.name.data; + + nd->npc_class = WARP_CLASS; + nd->speed = 200_ms; + nd->option = Opt0::ZERO; + nd->opt1 = Opt1::ZERO; + nd->opt2 = Opt2::ZERO; + nd->opt3 = Opt3::ZERO; + nd->warp.name = to_mapname; + nd->warp.x = to_x; + nd->warp.y = to_y; + nd->warp.xs = xs; + nd->warp.ys = ys; + + for (int i = 0; i < ys; i++) + { + for (int j = 0; j < xs; j++) + { + int x_lo = x - xs / 2; + int y_lo = y - ys / 2; + int xc = x_lo + j; + int yc = y_lo + i; + MapCell t = map_getcell(m, xc, yc); + if (bool(t & MapCell::UNWALKABLE)) + continue; + map_setcell(m, xc, yc, t | MapCell::NPC_NEAR); + } + } + + npc_warp++; + nd->bl_type = BL::NPC; + nd->npc_subtype = NpcSubtype::WARP; + map_addblock(nd); + clif_spawnnpc(nd); + register_npc_name(nd); + + return true; +} + +static +bool npc_load_shop(ast::npc::Shop& shop) +{ + MapName mapname = shop.m.data; + int x = shop.x.data, y = shop.y.data; + DIR dir = shop.d.data; + dumb_ptr<npc_data_shop> nd; + Species npc_class = shop.npc_class.data; + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), abort()); + + nd.new_(); + nd->shop_items.reserve(shop.items.data.size()); + for (auto& it : shop.items.data) + { + nd->shop_items.emplace_back(); + auto& back = nd->shop_items.back(); + P<item_data> id = ((extract(it.data.name.data, &back.nameid) && back.nameid) + ? ({ + P<item_data> id_ = TRY_UNWRAP(itemdb_exists(back.nameid), { it.data.name.span.error("No item with this numerical id"_s); return false; }); + id_; + }) + : ({ + P<item_data> id_ = TRY_UNWRAP(itemdb_searchname(XString(it.data.name.data)), { it.data.name.span.error("No item with this name"_s); return false; }); + back.nameid = id_->nameid; + id_; + })); + + back.value = it.data.value.data; + if (it.data.value_multiply) + { + back.value = id->value_buy * back.value; + } + } + + nd->bl_prev = nd->bl_next = nullptr; + nd->bl_m = m; + nd->bl_x = x; + nd->bl_y = y; + nd->bl_id = npc_get_new_npc_id(); + nd->dir = dir; + nd->flag = 0; + nd->name = shop.name.data; + nd->npc_class = npc_class; + nd->speed = 200_ms; + nd->option = Opt0::ZERO; + nd->opt1 = Opt1::ZERO; + nd->opt2 = Opt2::ZERO; + nd->opt3 = Opt3::ZERO; + + npc_shop++; + nd->bl_type = BL::NPC; + nd->npc_subtype = NpcSubtype::SHOP; + nd->n = map_addnpc(m, nd); + map_addblock(nd); + clif_spawnnpc(nd); + register_npc_name(nd); + + return true; +} + +static +bool npc_load_monster(ast::npc::Monster& monster) +{ + MapName mapname = monster.m.data; + int x = monster.x.data, y = monster.y.data; + int xs = monster.xs.data, ys = monster.ys.data; + + Species mob_class = monster.mob_class.data; + int num = monster.num.data; + interval_t delay1 = monster.delay1.data; + interval_t delay2 = monster.delay2.data; + NpcEvent eventname = monster.event.data; + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), abort()); + + if (num > 1 && battle_config.mob_count_rate != 100) + { + num = num * battle_config.mob_count_rate / 100; + if (num < 1) + num = 1; + } + + for (int i = 0; i < num; i++) + { + dumb_ptr<mob_data> md; + md.new_(); + + md->bl_prev = nullptr; + md->bl_next = nullptr; + md->bl_m = m; + md->bl_x = x; + md->bl_y = y; + MobName expected = get_mob_db(mob_class).jname; + if (monster.name.data != expected) + { + monster.name.span.warning(STRPRINTF("Visible label/jname should match: %s"_fmt, expected)); + } + if (monster.name.data == ENGLISH_NAME) + md->name = get_mob_db(mob_class).name; + else if (monster.name.data == JAPANESE_NAME) + md->name = get_mob_db(mob_class).jname; + else + md->name = monster.name.data; + + md->n = i; + md->mob_class = mob_class; + md->bl_id = npc_get_new_npc_id(); + md->spawn.m = m; + md->spawn.x0 = x; + md->spawn.y0 = y; + md->spawn.xs = xs; + md->spawn.ys = ys; + md->spawn.delay1 = delay1; + md->spawn.delay2 = delay2; + + really_memzero_this(&md->state); + // md->timer = nullptr; + md->target_id = BlockId(); + md->attacked_id = BlockId(); + + md->lootitemv.clear(); + + md->npc_event = eventname; + + md->bl_type = BL::MOB; + map_addiddb(md); + mob_spawn(md->bl_id); + + npc_mob++; + } + + return true; +} + +static +bool npc_load_mapflag(ast::npc::MapFlag& mapflag) +{ + MapName mapname = mapflag.m.data; + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), abort()); + + MapFlag mf; + if (!extract(mapflag.name.data, &mf)) + { + mapflag.name.span.error("No such mapflag"_s); + return false; + } + + if (mf == MapFlag::NOPVP) + { + if (mapflag.vec_extra.data.size()) + { + mapflag.vec_extra.span.error("No extra argument expected for mapflag 'nopvp'"_s); + return false; + } + m->flag.set(MapFlag::NOPVP, 1); + m->flag.set(MapFlag::PVP, 0); + return true; + } + + MapName savemap; + int savex, savey; + + if (mf == MapFlag::NOSAVE) + { + if (mapflag.vec_extra.data.size() == 3 + && extract(mapflag.vec_extra.data[0].data, &savemap) + && extract(mapflag.vec_extra.data[1].data, &savex) + && extract(mapflag.vec_extra.data[2].data, &savey) + && map_mapname2mapid(savemap).is_some()) + { + m->save.map_ = savemap; + m->save.x = savex; + m->save.y = savey; + } + else + { + mapflag.vec_extra.span.error("Unable to extract nosave savepoint"_s); + return false; + } + } + else if (mf == MapFlag::RESAVE) + { + if (mapflag.vec_extra.data.size() == 3 + && extract(mapflag.vec_extra.data[0].data, &savemap) + && extract(mapflag.vec_extra.data[1].data, &savex) + && extract(mapflag.vec_extra.data[2].data, &savey) + && map_mapname2mapid(savemap).is_some()) + { + m->resave.map_ = savemap; + m->resave.x = savex; + m->resave.y = savey; + } + else + { + mapflag.vec_extra.span.error("Unable to extract resave savepoint"_s); + return false; + } + } + else + { + if (mapflag.vec_extra.data.size()) + { + mapflag.vec_extra.span.error("No extra argument expected for mapflag"_s); + return false; + } + } + m->flag.set(mf, true); + + return true; +} + +static +void npc_convertlabel_db(ScriptLabel lname, int pos, dumb_ptr<npc_data_script> nd) +{ + nullpo_retv(nd); + + struct npc_label_list eln {}; + eln.name = lname; + eln.pos = pos; + nd->scr.label_listv.push_back(std::move(eln)); +} + +static +bool npc_load_script_function(ast::script::ScriptBody& body, ast::npc::ScriptFunction& script_function) +{ + std::unique_ptr<const ScriptBuffer> script = compile_script(STRPRINTF("script function \"%s\""_fmt, script_function.name.data), body, false); + if (script == nullptr) + return false; + + userfunc_db.put(script_function.name.data, std::move(script)); + + return true; +} + +static +bool npc_load_script_none(ast::script::ScriptBody& body, ast::npc::ScriptNone& script_none) +{ + std::unique_ptr<const ScriptBuffer> script = compile_script(STRPRINTF("script npc \"%s\""_fmt, script_none.name.data), body, false); + if (script == nullptr) + return false; + + dumb_ptr<npc_data_script> nd; + nd.new_(); + nd->scr.event_needs_map = false; + + nd->name = script_none.name.data; + + nd->bl_prev = nd->bl_next = nullptr; + nd->bl_m = borrow(undefined_gat); + nd->bl_x = 0; + nd->bl_y = 0; + nd->bl_id = npc_get_new_npc_id(); + nd->dir = DIR::S; + nd->flag = 0; + nd->npc_class = INVISIBLE_CLASS; + nd->speed = 200_ms; + nd->scr.script = std::move(script); + nd->option = Opt0::ZERO; + nd->opt1 = Opt1::ZERO; + nd->opt2 = Opt2::ZERO; + nd->opt3 = Opt3::ZERO; + + npc_script++; + nd->bl_type = BL::NPC; + nd->npc_subtype = NpcSubtype::SCRIPT; + + register_npc_name(nd); + + for (auto& pair : scriptlabel_db) + npc_convertlabel_db(pair.first, pair.second, nd); + + for (npc_label_list& el : nd->scr.label_listv) + { + ScriptLabel lname = el.name; + int pos = el.pos; + + if (lname.startswith("On"_s)) + { + struct event_data ev {}; + ev.nd = nd; + ev.pos = pos; + NpcEvent buf; + buf.npc = nd->name; + buf.label = lname; + ev_db.insert(buf, ev); + } + } + + for (npc_label_list& el : nd->scr.label_listv) + { + int t_ = 0; + ScriptLabel lname = el.name; + int pos = el.pos; + if (lname.startswith("OnTimer"_s) && extract(lname.xslice_t(7), &t_) && t_ > 0) + { + interval_t t = static_cast<interval_t>(t_); + + npc_timerevent_list tel {}; + tel.timer = t; + tel.pos = pos; + + auto it = std::lower_bound(nd->scr.timer_eventv.begin(), nd->scr.timer_eventv.end(), tel, + [](const npc_timerevent_list& l, const npc_timerevent_list& r) + { + return l.timer < r.timer; + } + ); + assert (it == nd->scr.timer_eventv.end() || it->timer != tel.timer); + + nd->scr.timer_eventv.insert(it, std::move(tel)); + } + } + // The counter starts stopped with 0 ticks, which is the first event, + // unless there is none, in which case begin == end. + nd->scr.timer = interval_t::zero(); + nd->scr.next_event = nd->scr.timer_eventv.begin(); + // nd->scr.timerid = nullptr; + + return true; +} + +static +bool npc_load_script_map(ast::script::ScriptBody& body, ast::npc::ScriptMap& script_map) +{ + MapName mapname = script_map.m.data; + int x = script_map.x.data, y = script_map.y.data; + DIR dir = script_map.d.data; + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), + { + script_map.m.span.error("No such map"_s); + return false; + }); + + std::unique_ptr<const ScriptBuffer> script = compile_script(STRPRINTF("script npc \"%s\""_fmt, script_map.name.data), body, false); + if (script == nullptr) + return false; + + + dumb_ptr<npc_data_script> nd; + nd.new_(); + + Species npc_class = script_map.npc_class.data; + int xs = script_map.xs.data, ys = script_map.ys.data; + + { + for (int i = 0; i < ys; i++) + { + for (int j = 0; j < xs; j++) + { + int x_lo = x - xs / 2; + int y_lo = y - ys / 2; + int xc = x_lo + j; + int yc = y_lo + i; + MapCell t = map_getcell(m, xc, yc); + if (bool(t & MapCell::UNWALKABLE)) + continue; + map_setcell(m, xc, yc, t | MapCell::NPC_NEAR); + } + } + + nd->scr.xs = xs; + nd->scr.ys = ys; + nd->scr.event_needs_map = true; + } + + nd->name = script_map.name.data; + + nd->bl_prev = nd->bl_next = nullptr; + nd->bl_m = m; + nd->bl_x = x; + nd->bl_y = y; + nd->bl_id = npc_get_new_npc_id(); + nd->dir = dir; + nd->flag = 0; + nd->npc_class = npc_class; + nd->speed = 200_ms; + nd->scr.script = std::move(script); + nd->option = Opt0::ZERO; + nd->opt1 = Opt1::ZERO; + nd->opt2 = Opt2::ZERO; + nd->opt3 = Opt3::ZERO; + + npc_script++; + nd->bl_type = BL::NPC; + nd->npc_subtype = NpcSubtype::SCRIPT; + + nd->n = map_addnpc(m, nd); + map_addblock(nd); + + clif_spawnnpc(nd); + + register_npc_name(nd); + + for (auto& pair : scriptlabel_db) + npc_convertlabel_db(pair.first, pair.second, nd); + + for (npc_label_list& el : nd->scr.label_listv) + { + ScriptLabel lname = el.name; + int pos = el.pos; + + if (lname.startswith("On"_s)) + { + struct event_data ev {}; + ev.nd = nd; + ev.pos = pos; + NpcEvent buf; + buf.npc = nd->name; + buf.label = lname; + ev_db.insert(buf, ev); + } + } + + for (npc_label_list& el : nd->scr.label_listv) + { + int t_ = 0; + ScriptLabel lname = el.name; + int pos = el.pos; + if (lname.startswith("OnTimer"_s) && extract(lname.xslice_t(7), &t_) && t_ > 0) + { + interval_t t = static_cast<interval_t>(t_); + + npc_timerevent_list tel {}; + tel.timer = t; + tel.pos = pos; + + auto it = std::lower_bound(nd->scr.timer_eventv.begin(), nd->scr.timer_eventv.end(), tel, + [](const npc_timerevent_list& l, const npc_timerevent_list& r) + { + return l.timer < r.timer; + } + ); + assert (it == nd->scr.timer_eventv.end() || it->timer != tel.timer); + + nd->scr.timer_eventv.insert(it, std::move(tel)); + } + } + // The counter starts stopped with 0 ticks, which is the first event, + // unless there is none, in which case begin == end. + nd->scr.timer = interval_t::zero(); + nd->scr.next_event = nd->scr.timer_eventv.begin(); + // nd->scr.timerid = nullptr; + + return true; +} + +static +bool npc_load_script_any(ast::npc::Script *script) +{ + MATCH_BEGIN (*script) + { + MATCH_CASE (ast::npc::ScriptFunction&, script_function) + { + return npc_load_script_function(script->body, script_function); + } + MATCH_CASE (ast::npc::ScriptNone&, script_none) + { + return npc_load_script_none(script->body, script_none); + } + MATCH_CASE (ast::npc::ScriptMap&, script_map) + { + auto& mapname = script_map.m; + Option<P<map_local>> m = map_mapname2mapid(mapname.data); + if (m.is_none()) + { + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; + } + return npc_load_script_map(script->body, script_map); + } + } + MATCH_END (); + abort(); +} + +dumb_ptr<npc_data> npc_spawn_text(Borrowed<map_local> m, int x, int y, + Species npc_class, NpcName name, AString message) +{ + dumb_ptr<npc_data_message> retval; + retval.new_(); + retval->bl_id = npc_get_new_npc_id(); + retval->bl_x = x; + retval->bl_y = y; + retval->bl_m = m; + retval->bl_type = BL::NPC; + retval->npc_subtype = NpcSubtype::MESSAGE; + + retval->name = name; + if (message) + retval->message = message; + + retval->npc_class = npc_class; + retval->speed = 200_ms; + + clif_spawnnpc(retval); + map_addblock(retval); + map_addiddb(retval); + register_npc_name(retval); + + return retval; +} + +static +bool load_one_npc(io::LineCharReader& fp, bool& done) +{ + auto res = TRY_UNWRAP(ast::npc::parse_top(fp), { done = true; return true; }); + if (res.get_failure()) + PRINTF("%s\n"_fmt, res.get_failure()); + ast::npc::TopLevel tl = TRY_UNWRAP(std::move(res.get_success()), return false); + + MATCH_BEGIN (tl) + { + MATCH_CASE (ast::npc::Comment&, c) + { + (void)c; + return true; + } + MATCH_CASE (ast::npc::Warp&, warp) + { + auto& mapname = warp.m; + Option<P<map_local>> m = map_mapname2mapid(mapname.data); + if (m.is_none()) + { + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; + } + return npc_load_warp(warp); + } + MATCH_CASE (ast::npc::Shop&, shop) + { + auto& mapname = shop.m; + Option<P<map_local>> m = map_mapname2mapid(mapname.data); + if (m.is_none()) + { + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; + } + return npc_load_shop(shop); + } + MATCH_CASE (ast::npc::Monster&, monster) + { + auto& mapname = monster.m; + Option<P<map_local>> m = map_mapname2mapid(mapname.data); + if (m.is_none()) + { + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; + } + return npc_load_monster(monster); + } + MATCH_CASE (ast::npc::MapFlag&, mapflag) + { + auto& mapname = mapflag.m; + Option<P<map_local>> m = map_mapname2mapid(mapname.data); + if (m.is_none()) + { + mapname.span.error(STRPRINTF("Map not found: %s"_fmt, mapname.data)); + return false; + } + return npc_load_mapflag(mapflag); + } + MATCH_CASE (ast::npc::Script&, script) + { + return npc_load_script_any(&script); + } + } + MATCH_END (); + abort(); +} + +static +bool load_npc_file(ZString nsl) +{ + io::LineCharReader fp(nsl); + if (!fp.is_open()) + { + PRINTF("file not found : %s\n"_fmt, nsl); + return false; + } + PRINTF("Loading NPCs [%d]: %-54s\r"_fmt, unwrap<BlockId>(npc_id) - unwrap<BlockId>(START_NPC_NUM), + nsl); + + bool done = false; + while (!done) + { + if (!load_one_npc(fp, done)) + return false; + } + return true; +} + +bool do_init_npc(void) +{ + bool rv = true; + + for (; !npc_srcs.empty(); npc_srcs.pop_front()) + { + AString nsl = npc_srcs.front(); + rv &= load_npc_file(nsl); + } + PRINTF("NPCs Loaded: %d [Warps:%d Shops:%d Scripts:%d Mobs:%d] %20s\n"_fmt, + unwrap<BlockId>(npc_id) - unwrap<BlockId>(START_NPC_NUM), npc_warp, npc_shop, npc_script, npc_mob, ""_s); + + if (script_errors) + { + PRINTF("Cowardly refusing to continue after %d errors\n"_fmt, script_errors); + rv = false; + } + return rv; +} +} // namespace map +} // namespace tmwa diff --git a/src/map/npc-parse.hpp b/src/map/npc-parse.hpp new file mode 100644 index 0000000..9bc3448 --- /dev/null +++ b/src/map/npc-parse.hpp @@ -0,0 +1,44 @@ +#pragma once +// npc-parse.hpp - Noncombatants. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + + +namespace tmwa +{ +namespace map +{ +bool npc_load_warp(ast::npc::Warp& warp); + +/** + * Spawns and installs a talk-only NPC + * + * \param message The message to speak. If message is nullptr, the NPC will not do anything at all. + */ +dumb_ptr<npc_data> npc_spawn_text(Borrowed<map_local> m, int x, int y, + Species class_, NpcName name, AString message); + +void npc_addsrcfile(AString name); +void npc_delsrcfile(XString name); +bool do_init_npc(void); +} // namespace map +} // namespace tmwa diff --git a/src/map/npc.cpp b/src/map/npc.cpp index a6427d6..4296432 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -1,4 +1,4 @@ -#include "npc.hpp" +#include "npc-internal.hpp" // npc.cpp - Noncombatants. // // Copyright © ????-2004 Athena Dev Teams @@ -38,23 +38,20 @@ #include "../generic/db.hpp" #include "../io/cxxstdio.hpp" -#include "../io/read.hpp" +#include "../io/extract.hpp" #include "../net/timer.hpp" -#include "../mmo/config_parse.hpp" -#include "../mmo/extract.hpp" -#include "../mmo/utils.hpp" - #include "../proto2/map-user.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "clif.hpp" +#include "globals.hpp" #include "itemdb.hpp" #include "map.hpp" -#include "mob.hpp" #include "pc.hpp" -#include "script.hpp" +#include "script-call.hpp" #include "skill.hpp" #include "../poison.hpp" @@ -62,14 +59,8 @@ namespace tmwa { -static -std::list<AString> npc_srcs; - -static -BlockId npc_id = START_NPC_NUM; -static -int npc_warp, npc_shop, npc_script, npc_mob; - +namespace map +{ BlockId npc_get_new_npc_id(void) { BlockId rv = npc_id; @@ -77,21 +68,6 @@ BlockId npc_get_new_npc_id(void) return rv; } -struct event_data -{ - dumb_ptr<npc_data_script> nd; - int pos; -}; -static -Map<NpcEvent, struct event_data> ev_db; -static -DMap<NpcName, dumb_ptr<npc_data>> npcs_by_name; - -// used for clock-based event triggers -// only tm_min, tm_hour, and tm_mday are used -static -struct tm ev_tm_b; - /*========================================== * NPCの無効化/有効化 * npc_enable @@ -206,22 +182,6 @@ int npc_delete(dumb_ptr<npc_data> nd) return 0; } -void npc_timer_event(NpcEvent eventname) -{ - struct event_data *ev = ev_db.search(eventname); - dumb_ptr<npc_data_script> nd; -// int xs,ys; - - if ((ev == nullptr || (nd = ev->nd) == nullptr)) - { - PRINTF("npc_event: event not found [%s]\n"_fmt, - eventname); - return; - } - - run_script(ScriptPointer(nd->scr.script.get(), ev->pos), nd->bl_id, nd->bl_id); -} - /*========================================== * 全てのNPCのOn*イベント実行 *------------------------------------------ @@ -236,7 +196,7 @@ void npc_event_doall_sub(NpcEvent key, struct event_data *ev, if (name == p) { - run_script_l(ScriptPointer(ev->nd->scr.script.get(), ev->pos), rid, ev->nd->bl_id, + run_script_l(ScriptPointer(borrow(*ev->nd->scr.script), ev->pos), rid, ev->nd->bl_id, argv); (*c)++; } @@ -259,7 +219,7 @@ void npc_event_do_sub(NpcEvent key, struct event_data *ev, if (name == key) { - run_script_l(ScriptPointer(ev->nd->scr.script.get(), ev->pos), rid, ev->nd->bl_id, + run_script_l(ScriptPointer(borrow(*ev->nd->scr.script), ev->pos), rid, ev->nd->bl_id, argv); (*c)++; } @@ -353,7 +313,7 @@ void npc_timerevent(TimerData *, tick_t tick, BlockId id, interval_t data) id, next)); } - run_script(ScriptPointer(nd->scr.script.get(), te->pos), BlockId(), nd->bl_id); + run_script(ScriptPointer(borrow(*nd->scr.script), te->pos), BlockId(), nd->bl_id); } /// Start (or resume) counting ticks to the next npc_timerevent. @@ -465,39 +425,36 @@ void npc_settimerevent_tick(dumb_ptr<npc_data_script> nd, interval_t newtimer) int npc_event(dumb_ptr<map_session_data> sd, NpcEvent eventname, int mob_kill) { - struct event_data *ev = ev_db.search(eventname); + Option<P<struct event_data>> ev_ = ev_db.search(eventname); dumb_ptr<npc_data_script> nd; - int xs, ys; if (sd == nullptr) { PRINTF("npc_event nullpo?\n"_fmt); } - if (ev == nullptr && eventname.label == stringish<ScriptLabel>("OnTouch"_s)) + if (ev_.is_none() && eventname.label == stringish<ScriptLabel>("OnTouch"_s)) return 1; - if (ev == nullptr || (nd = ev->nd) == nullptr) + P<struct event_data> ev = TRY_UNWRAP(ev_, { - if (mob_kill) - { - { - return 0; - } - } - else - { - if (battle_config.error_log) - PRINTF("npc_event: event not found [%s]\n"_fmt, - eventname); - return 0; - } + if (!mob_kill && battle_config.error_log) + PRINTF("npc_event: event not found [%s]\n"_fmt, + eventname); + return 0; + }); + if ((nd = ev->nd) == nullptr) + { + if (!mob_kill && battle_config.error_log) + PRINTF("npc_event: event not found [%s]\n"_fmt, + eventname); + return 0; } - xs = nd->scr.xs; - ys = nd->scr.ys; - if (xs >= 0 && ys >= 0) + if (nd->scr.event_needs_map) { + int xs = nd->scr.xs; + int ys = nd->scr.ys; if (nd->bl_m != sd->bl_m) return 1; if (xs > 0 @@ -521,28 +478,7 @@ int npc_event(dumb_ptr<map_session_data> sd, NpcEvent eventname, sd->npc_id = nd->bl_id; sd->npc_pos = - run_script(ScriptPointer(nd->scr.script.get(), ev->pos), sd->bl_id, nd->bl_id); - return 0; -} - -static -void npc_command_sub(NpcEvent key, struct event_data *ev, NpcName npcname, XString command) -{ - if (ev->nd->name == npcname - && key.label.startswith("OnCommand"_s)) - { - XString temp = key.label.xslice_t(9); - - if (command == temp) - run_script(ScriptPointer(ev->nd->scr.script.get(), ev->pos), BlockId(), ev->nd->bl_id); - } -} - -int npc_command(dumb_ptr<map_session_data>, NpcName npcname, XString command) -{ - for (auto& pair : ev_db) - npc_command_sub(pair.first, &pair.second, npcname, command); - + run_script(ScriptPointer(borrow(*nd->scr.script), ev->pos), sd->bl_id, nd->bl_id); return 0; } @@ -550,7 +486,7 @@ int npc_command(dumb_ptr<map_session_data>, NpcName npcname, XString command) * 接触型のNPC処理 *------------------------------------------ */ -int npc_touch_areanpc(dumb_ptr<map_session_data> sd, map_local *m, int x, int y) +int npc_touch_areanpc(dumb_ptr<map_session_data> sd, Borrowed<map_local> m, int x, int y) { int i, f = 1; int xs, ys; @@ -647,7 +583,7 @@ int npc_checknear(dumb_ptr<map_session_data> sd, BlockId id) if (nd->bl_type != BL::NPC) return 1; - if (nd->npc_class == NEGATIVE_SPECIES) + if (nd->npc_class == INVISIBLE_CLASS) return 0; // エリア判定 @@ -696,7 +632,7 @@ int npc_click(dumb_ptr<map_session_data> sd, BlockId id) npc_event_dequeue(sd); break; case NpcSubtype::SCRIPT: - sd->npc_pos = run_script(ScriptPointer(nd->is_script()->scr.script.get(), 0), sd->bl_id, id); + sd->npc_pos = run_script(ScriptPointer(borrow(*nd->is_script()->scr.script), 0), sd->bl_id, id); break; case NpcSubtype::MESSAGE: if (nd->is_message()->message) @@ -737,7 +673,7 @@ int npc_scriptcont(dumb_ptr<map_session_data> sd, BlockId id) return 0; } - sd->npc_pos = run_script(ScriptPointer(nd->is_script()->scr.script.get(), sd->npc_pos), sd->bl_id, id); + sd->npc_pos = run_script(ScriptPointer(borrow(*nd->is_script()->scr.script), sd->npc_pos), sd->bl_id, id); return 0; } @@ -848,8 +784,7 @@ int npc_buylist(dumb_ptr<map_session_data> sd, const uint16_t& item_l_count = item_list[i].count; const ItemNameId& item_l_id = item_list[i].name_id; - struct item_data *item_data; - if ((item_data = itemdb_exists(item_l_id)) != nullptr) + P<struct item_data> item_data = TRY_UNWRAP(itemdb_exists(item_l_id), continue); { int amount = item_l_count; Item item_tmp {}; @@ -918,718 +853,6 @@ int npc_selllist(dumb_ptr<map_session_data> sd, } -// -// 初期化関係 -// - -/*========================================== - * 読み込むnpcファイルのクリア - *------------------------------------------ - */ -static -void npc_clearsrcfile(void) -{ - npc_srcs.clear(); -} - -/*========================================== - * 読み込むnpcファイルの追加 - *------------------------------------------ - */ -void npc_addsrcfile(AString name) -{ - if (name == "clear"_s) - { - npc_clearsrcfile(); - return; - } - - npc_srcs.push_back(name); -} - -/*========================================== - * 読み込むnpcファイルの削除 - *------------------------------------------ - */ -void npc_delsrcfile(XString name) -{ - if (name == "all"_s) - { - npc_clearsrcfile(); - return; - } - - for (auto it = npc_srcs.begin(); it != npc_srcs.end(); ++it) - { - if (*it == name) - { - npc_srcs.erase(it); - return; - } - } -} - -static -void register_npc_name(dumb_ptr<npc_data> nd) -{ - earray<LString, NpcSubtype, NpcSubtype::COUNT> types //= - {{ - "WARP"_s, - "SHOP"_s, - "SCRIPT"_s, - "MESSAGE"_s, - }}; - if (!nd->name) - { - if (nd->npc_subtype == NpcSubtype::MESSAGE) - return; - PRINTF("WARNING: npc with no name:\n%s @ %s,%d,%d\n"_fmt, - types[nd->npc_subtype], - nd->bl_m->name_, nd->bl_x, nd->bl_y); - return; - } - if (dumb_ptr<npc_data> nd_old = npcs_by_name.get(nd->name)) - { - if (nd->npc_subtype != NpcSubtype::WARP - || nd_old->npc_subtype != NpcSubtype::WARP) - { - PRINTF("WARNING: replacing npc with name: %s\n"_fmt, nd->name); - PRINTF("old: %s @ %s,%d,%d\n"_fmt, - types[nd_old->npc_subtype], - nd_old->bl_m->name_, nd_old->bl_x, nd_old->bl_y); - PRINTF("new: %s @ %s,%d,%d\n"_fmt, - types[nd->npc_subtype], - nd->bl_m->name_, nd->bl_x, nd->bl_y); - } - } - // TODO also check #s ? - npcs_by_name.put(nd->name, nd); -} - -/*========================================== - * warp行解析 - *------------------------------------------ - */ -int npc_parse_warp(XString w1, XString, NpcName w3, XString w4) -{ - int x, y, xs, ys, to_x, to_y; - int i, j; - MapName mapname, to_mapname; - dumb_ptr<npc_data_warp> nd; - - if (!extract(w1, record<','>(&mapname, &x, &y)) || - !extract(w4, record<','>(&xs, &ys, &to_mapname, &to_x, &to_y))) - { - PRINTF("bad warp line : %s\n"_fmt, w3); - return 1; - } - - map_local *m = map_mapname2mapid(mapname); - - nd.new_(); - nd->bl_id = npc_get_new_npc_id(); - nd->n = map_addnpc(m, nd); - - nd->bl_prev = nd->bl_next = nullptr; - nd->bl_m = m; - nd->bl_x = x; - nd->bl_y = y; - nd->dir = DIR::S; - nd->flag = 0; - nd->name = w3; - - if (!battle_config.warp_point_debug) - nd->npc_class = WARP_CLASS; - else - nd->npc_class = WARP_DEBUG_CLASS; - nd->speed = 200_ms; - nd->option = Option::ZERO; - nd->opt1 = Opt1::ZERO; - nd->opt2 = Opt2::ZERO; - nd->opt3 = Opt3::ZERO; - nd->warp.name = to_mapname; - xs += 2; - ys += 2; - nd->warp.x = to_x; - nd->warp.y = to_y; - nd->warp.xs = xs; - nd->warp.ys = ys; - - for (i = 0; i < ys; i++) - { - for (j = 0; j < xs; j++) - { - int x_lo = x - xs / 2; - int y_lo = y - ys / 2; - int xc = x_lo + j; - int yc = y_lo + i; - MapCell t = map_getcell(m, xc, yc); - if (bool(t & MapCell::UNWALKABLE)) - continue; - map_setcell(m, xc, yc, t | MapCell::NPC_NEAR); - } - } - - npc_warp++; - nd->bl_type = BL::NPC; - nd->npc_subtype = NpcSubtype::WARP; - map_addblock(nd); - clif_spawnnpc(nd); - register_npc_name(nd); - - return 0; -} - -static -bool extract(XString xs, npc_item_list *itv) -{ - XString name_or_id; - if (!extract(xs, record<':'>(&name_or_id, &itv->value))) - return false; - struct item_data *id = nullptr; - if (extract(name_or_id, &itv->nameid) && itv->nameid) - goto return_true; - - id = itemdb_searchname(name_or_id.rstrip()); - if (id == nullptr) - return false; - itv->nameid = id->nameid; - goto return_true; - -return_true: - if (itv->value < 0) - { - if (id == nullptr) - id = itemdb_search(itv->nameid); - itv->value = id->value_buy * abs(itv->value); - } - return true; -} - -/*========================================== - * shop行解析 - *------------------------------------------ - */ -static -int npc_parse_shop(XString w1, XString, NpcName w3, ZString w4a) -{ - int x, y; - DIR dir; - MapName mapname; - dumb_ptr<npc_data_shop> nd; - ZString::iterator w4comma; - Species npc_class; - - int dir_; // TODO use enum directly in extract - if (!extract(w1, record<','>(&mapname, &x, &y, &dir_)) - || dir_ < 0 || dir_ >= 8 - || (w4comma = std::find(w4a.begin(), w4a.end(), ',')) == w4a.end() - || !extract(w4a.xislice_h(w4comma), &npc_class)) - { - PRINTF("bad shop line : %s\n"_fmt, w3); - return 1; - } - dir = static_cast<DIR>(dir_); - map_local *m = map_mapname2mapid(mapname); - - nd.new_(); - ZString w4b = w4a.xislice_t(w4comma + 1); - - if (!extract(w4b, vrec<','>(&nd->shop_items))) - { - PRINTF("bad shop items : %s\n"_fmt, w3); - PRINTF(" somewhere --> %s\n"_fmt, w4b); - nd->shop_items.clear(); - } - - if (nd->shop_items.empty()) - { - nd.delete_(); - return 1; - } - - nd->bl_prev = nd->bl_next = nullptr; - nd->bl_m = m; - nd->bl_x = x; - nd->bl_y = y; - nd->bl_id = npc_get_new_npc_id(); - nd->dir = dir; - nd->flag = 0; - nd->name = w3; - nd->npc_class = npc_class; - nd->speed = 200_ms; - nd->option = Option::ZERO; - nd->opt1 = Opt1::ZERO; - nd->opt2 = Opt2::ZERO; - nd->opt3 = Opt3::ZERO; - - npc_shop++; - nd->bl_type = BL::NPC; - nd->npc_subtype = NpcSubtype::SHOP; - nd->n = map_addnpc(m, nd); - map_addblock(nd); - clif_spawnnpc(nd); - register_npc_name(nd); - - return 0; -} - -/*========================================== - * NPCのラベルデータコンバート - *------------------------------------------ - */ -static -void npc_convertlabel_db(ScriptLabel lname, int pos, dumb_ptr<npc_data_script> nd) -{ - nullpo_retv(nd); - - struct npc_label_list eln {}; - eln.name = lname; - eln.pos = pos; - nd->scr.label_listv.push_back(std::move(eln)); -} - -/*========================================== - * script行解析 - *------------------------------------------ - */ -static -int npc_parse_script(XString w1, XString w2, NpcName w3, ZString w4, - XString first_line, io::ReadFile& fp, int *lines) -{ - int x, y; - DIR dir = DIR::S; - map_local *m; - int xs = 0, ys = 0; // [Valaris] thanks to fov - Species npc_class; - MapName mapname; - std::unique_ptr<const ScriptBuffer> script = nullptr; - dumb_ptr<npc_data_script> nd; - int evflag = 0; - - if (w1 == "-"_s) - { - x = 0; - y = 0; - m = nullptr; - } - else - { - int dir_; // TODO use enum directly in extract - if (!extract(w1, record<','>(&mapname, &x, &y, &dir_)) - || dir_ < 0 || dir_ >= 8 - || (w2 == "script"_s && !w4.contains(','))) - { - PRINTF("bad script line : %s\n"_fmt, w3); - return 1; - } - dir = static_cast<DIR>(dir_); - m = map_mapname2mapid(mapname); - } - - if (w2 == "script"_s) - { - // may be empty - MString srcbuf; - srcbuf += first_line.xislice_t(std::find(first_line.begin(), first_line.end(), '{')); - // Note: it was a bug that this was missing. I think. - int startline = *lines; - - // while (!srcbuf.rstrip().endswith('}')) - while (true) - { - auto it = std::find_if_not(srcbuf.rbegin(), srcbuf.rend(), [](char c){ return c == ' ' || c == '\n'; }); - if (it != srcbuf.rend() && *it == '}') - break; - - AString line; - if (!fp.getline(line)) - // eof - break; - (*lines)++; - if (!srcbuf) - { - // may be a no-op - srcbuf += line.xislice_t(std::find(line.begin(), line.end(), '{')); - // safe to execute more than once - // But will usually only happen once - startline = *lines; - } - else - srcbuf += line; - srcbuf += '\n'; - } - script = parse_script(AString(srcbuf), startline, false); - if (script == nullptr) - // script parse error? - return 1; - } - else - { - assert(0 && "duplicate() is no longer supported!\n"_s); - return 0; - } - - nd.new_(); - - if (m == nullptr) - { - } - else if (extract(w4, record<','>(&npc_class, &xs, &ys))) - { - if (xs >= 0) - xs = xs * 2 + 1; - if (ys >= 0) - ys = ys * 2 + 1; - - if (npc_class != NEGATIVE_SPECIES) - { - - for (int i = 0; i < ys; i++) - { - for (int j = 0; j < xs; j++) - { - int x_lo = x - xs / 2; - int y_lo = y - ys / 2; - int xc = x_lo + j; - int yc = y_lo + i; - MapCell t = map_getcell(m, xc, yc); - if (bool(t & MapCell::UNWALKABLE)) - continue; - map_setcell(m, xc, yc, t | MapCell::NPC_NEAR); - } - } - } - - nd->scr.xs = xs; - nd->scr.ys = ys; - } - else - { - XString w4x = w4; - if (w4x.endswith(',')) - w4x = w4x.xrslice_h(1); - if (!extract(w4x, &npc_class)) - abort(); - nd->scr.xs = 0; - nd->scr.ys = 0; - } - - if (npc_class == NEGATIVE_SPECIES && m != nullptr) - { - evflag = 1; - } - - if (w3.contains(':')) - { - assert(false && "feature removed"_s); - abort(); - } - - { - nd->name = w3; - } - - nd->bl_prev = nd->bl_next = nullptr; - nd->bl_m = m; - nd->bl_x = x; - nd->bl_y = y; - nd->bl_id = npc_get_new_npc_id(); - nd->dir = dir; - nd->flag = 0; - nd->npc_class = npc_class; - nd->speed = 200_ms; - nd->scr.script = std::move(script); - nd->option = Option::ZERO; - nd->opt1 = Opt1::ZERO; - nd->opt2 = Opt2::ZERO; - nd->opt3 = Opt3::ZERO; - - npc_script++; - nd->bl_type = BL::NPC; - nd->npc_subtype = NpcSubtype::SCRIPT; - if (m != nullptr) - { - nd->n = map_addnpc(m, nd); - map_addblock(nd); - - if (evflag) - { - struct event_data ev {}; - ev.nd = nd; - ev.pos = 0; - NpcEvent npcev; - npcev.npc = nd->name; - npcev.label = ScriptLabel(); - ev_db.insert(npcev, ev); - } - else - clif_spawnnpc(nd); - } - register_npc_name(nd); - - for (auto& pair : scriptlabel_db) - npc_convertlabel_db(pair.first, pair.second, nd); - - for (npc_label_list& el : nd->scr.label_listv) - { - ScriptLabel lname = el.name; - int pos = el.pos; - - if (lname.startswith("On"_s)) - { - struct event_data ev {}; - ev.nd = nd; - ev.pos = pos; - NpcEvent buf; - buf.npc = nd->name; - buf.label = lname; - ev_db.insert(buf, ev); - } - } - - //----------------------------------------- - // ラベルデータからタイマーイベント取り込み - for (npc_label_list& el : nd->scr.label_listv) - { - int t_ = 0; - ScriptLabel lname = el.name; - int pos = el.pos; - if (lname.startswith("OnTimer"_s) && extract(lname.xslice_t(7), &t_) && t_ > 0) - { - interval_t t = static_cast<interval_t>(t_); - - npc_timerevent_list tel {}; - tel.timer = t; - tel.pos = pos; - - auto it = std::lower_bound(nd->scr.timer_eventv.begin(), nd->scr.timer_eventv.end(), tel, - [](const npc_timerevent_list& l, const npc_timerevent_list& r) - { - return l.timer < r.timer; - } - ); - assert (it == nd->scr.timer_eventv.end() || it->timer != tel.timer); - - nd->scr.timer_eventv.insert(it, std::move(tel)); - } - } - // The counter starts stopped with 0 ticks, which is the first event, - // unless there is none, in which case begin == end. - nd->scr.timer = interval_t::zero(); - nd->scr.next_event = nd->scr.timer_eventv.begin(); - // nd->scr.timerid = nullptr; - - return 0; -} - -/*========================================== - * function行解析 - *------------------------------------------ - */ -static -int npc_parse_function(XString, XString, XString w3, ZString, - XString first_line, io::ReadFile& fp, int *lines) -{ - MString srcbuf; - srcbuf += first_line.xislice_t(std::find(first_line.begin(), first_line.end(), '{')); - int startline = *lines; - - while (true) - { - auto it = std::find_if_not(srcbuf.rbegin(), srcbuf.rend(), [](char c){ return c == ' ' || c == '\n'; }); - if (it != srcbuf.rend() && *it == '}') - break; - - AString line; - if (!fp.getline(line)) - break; - (*lines)++; - if (!srcbuf) - { - srcbuf += line.xislice_t(std::find(line.begin(), line.end(), '{')); - startline = *lines; - } - else - srcbuf += line; - srcbuf += '\n'; - } - std::unique_ptr<const ScriptBuffer> script = parse_script(AString(srcbuf), startline, false); - if (script == nullptr) - { - // script parse error? - return 1; - } - - userfunc_db.put(w3, std::move(script)); - - return 0; -} - -/*========================================== - * mob行解析 - *------------------------------------------ - */ -static -int npc_parse_mob(XString w1, XString, MobName w3, ZString w4) -{ - int x, y, xs, ys, num; - Species mob_class; - int i; - MapName mapname; - NpcEvent eventname; - dumb_ptr<mob_data> md; - - xs = ys = 0; - int delay1_ = 0, delay2_ = 0; - if (!extract(w1, record<',', 3>(&mapname, &x, &y, &xs, &ys)) || - !extract(w4, record<',', 2>(&mob_class, &num, &delay1_, &delay2_, &eventname))) - { - PRINTF("bad monster line : %s\n"_fmt, w3); - return 1; - } - interval_t delay1 = std::chrono::milliseconds(delay1_); - interval_t delay2 = std::chrono::milliseconds(delay2_); - - map_local *m = map_mapname2mapid(mapname); - - if (num > 1 && battle_config.mob_count_rate != 100) - { - if ((num = num * battle_config.mob_count_rate / 100) < 1) - num = 1; - } - - for (i = 0; i < num; i++) - { - md.new_(); - - md->bl_prev = nullptr; - md->bl_next = nullptr; - md->bl_m = m; - md->bl_x = x; - md->bl_y = y; - if (w3 == ENGLISH_NAME) - md->name = get_mob_db(mob_class).name; - else if (w3 == JAPANESE_NAME) - md->name = get_mob_db(mob_class).jname; - else - md->name = w3; - - md->n = i; - md->mob_class = mob_class; - md->bl_id = npc_get_new_npc_id(); - md->spawn.m = m; - md->spawn.x0 = x; - md->spawn.y0 = y; - md->spawn.xs = xs; - md->spawn.ys = ys; - md->spawn.delay1 = delay1; - md->spawn.delay2 = delay2; - - really_memzero_this(&md->state); - // md->timer = nullptr; - md->target_id = BlockId(); - md->attacked_id = BlockId(); - - md->lootitemv.clear(); - - md->npc_event = eventname; - - md->bl_type = BL::MOB; - map_addiddb(md); - mob_spawn(md->bl_id); - - npc_mob++; - } - - return 0; -} - -/*========================================== - * マップフラグ行の解析 - *------------------------------------------ - */ -static -int npc_parse_mapflag(XString w1, XString, XString w3, ZString w4) -{ - MapName mapname, savemap; - int savex, savey; - - mapname = stringish<MapName>(w1); - if (!mapname) - return 1; - - map_local *m = map_mapname2mapid(mapname); - if (m == nullptr) - return 1; - - MapFlag mf; - if (!extract(w3, &mf)) - return 1; - - if (battle_config.pk_mode && mf == MapFlag::NOPVP) - { - m->flag.set(MapFlag::NOPVP, 1); - m->flag.set(MapFlag::PVP, 0); - return 0; - } - - if (mf == MapFlag::NOSAVE) - { - if (w4 == "SavePoint"_s) - { - m->save.map_ = stringish<MapName>("SavePoint"_s); - m->save.x = -1; - m->save.y = -1; - } - else if (extract(w4, record<','>(&savemap, &savex, &savey))) - { - m->save.map_ = savemap; - m->save.x = savex; - m->save.y = savey; - } - } - if (mf == MapFlag::RESAVE) - { - if (extract(w4, record<','>(&savemap, &savex, &savey))) - { - m->resave.map_ = savemap; - m->resave.x = savex; - m->resave.y = savey; - } - } - m->flag.set(mf, true); - - return 0; -} - -dumb_ptr<npc_data> npc_spawn_text(map_local *m, int x, int y, - Species npc_class, NpcName name, AString message) -{ - dumb_ptr<npc_data_message> retval; - retval.new_(); - retval->bl_id = npc_get_new_npc_id(); - retval->bl_x = x; - retval->bl_y = y; - retval->bl_m = m; - retval->bl_type = BL::NPC; - retval->npc_subtype = NpcSubtype::MESSAGE; - - retval->name = name; - if (message) - retval->message = message; - - retval->npc_class = npc_class; - retval->speed = 200_ms; - - clif_spawnnpc(retval); - map_addblock(retval); - map_addiddb(retval); - register_npc_name(retval); - - return retval; -} - static void npc_free_internal(dumb_ptr<npc_data> nd_) { @@ -1675,116 +898,5 @@ void npc_free(dumb_ptr<npc_data> nd) map_delblock(nd); npc_free_internal(nd); } - -/*========================================== - * npc初期化 - *------------------------------------------ - */ -bool do_init_npc(void) -{ - bool rv = true; - // other fields unused - ev_tm_b.tm_min = -1; - ev_tm_b.tm_hour = -1; - ev_tm_b.tm_mday = -1; - - for (; !npc_srcs.empty(); npc_srcs.pop_front()) - { - AString nsl = npc_srcs.front(); - io::ReadFile fp(nsl); - if (!fp.is_open()) - { - PRINTF("file not found : %s\n"_fmt, nsl); - rv = false; - continue; - } - PRINTF("\rLoading NPCs [%d]: %-54s"_fmt, unwrap<BlockId>(npc_id) - unwrap<BlockId>(START_NPC_NUM), - nsl); - int lines = 0; - AString zline; - while (fp.getline(zline)) - { - XString w1, w2, w3, w4x; - ZString w4z; - lines++; - - if (is_comment(zline)) - continue; - - if (!extract(zline, record<'|', 3>(&w1, &w2, &w3, &w4x)) || !w1 || !w2 || !w3) - { - FPRINTF(stderr, "%s:%d: Broken script line: %s\n"_fmt, nsl, lines, zline); - rv = false; - continue; - } - if (&*w4x.end() == &*zline.end()) - { - w4z = zline.xrslice_t(w4x.size()); - } - assert(bool(w4x) == bool(w4z)); - - if (w1 != "-"_s && w1 != "function"_s) - { - auto comma = std::find(w1.begin(), w1.end(), ','); - MapName mapname = stringish<MapName>(w1.xislice_h(comma)); - map_local *m = map_mapname2mapid(mapname); - if (m == nullptr) - { - // "mapname" is not assigned to this server - FPRINTF(stderr, "%s:%d: Map not found: %s\n"_fmt, nsl, lines, mapname); - rv = false; - continue; - } - } - if (w2 == "warp"_s) - { - NpcName npcname = stringish<NpcName>(w3); - npc_parse_warp(w1, w2, npcname, w4z); - } - else if (w2 == "shop"_s) - { - NpcName npcname = stringish<NpcName>(w3); - npc_parse_shop(w1, w2, npcname, w4z); - } - else if (w2 == "script"_s) - { - if (w1 == "function"_s) - { - npc_parse_function(w1, w2, w3, w4z, - w4x, fp, &lines); - } - else - { - NpcName npcname = stringish<NpcName>(w3); - npc_parse_script(w1, w2, npcname, w4z, - w4x, fp, &lines); - } - } - else if (w2 == "monster"_s) - { - MobName mobname = stringish<MobName>(w3); - npc_parse_mob(w1, w2, mobname, w4z); - } - else if (w2 == "mapflag"_s) - { - npc_parse_mapflag(w1, w2, w3, w4z); - } - else - { - PRINTF("odd script line: %s\n"_fmt, zline); - script_errors++; - } - } - fflush(stdout); - } - PRINTF("\rNPCs Loaded: %d [Warps:%d Shops:%d Scripts:%d Mobs:%d] %20s\n"_fmt, - unwrap<BlockId>(npc_id) - unwrap<BlockId>(START_NPC_NUM), npc_warp, npc_shop, npc_script, npc_mob, ""_s); - - if (script_errors) - { - PRINTF("Cowardly refusing to continue after %d errors\n"_fmt, script_errors); - rv = false; - } - return rv; -} +} // namespace map } // namespace tmwa diff --git a/src/map/npc.hpp b/src/map/npc.hpp index 33dd378..b587f5f 100644 --- a/src/map/npc.hpp +++ b/src/map/npc.hpp @@ -24,38 +24,33 @@ #include <cstdint> -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" +#include "../range/slice.hpp" #include "../net/timer.t.hpp" -#include "../proto2/fwd.hpp" - #include "map.hpp" +#include "script-call.t.hpp" namespace tmwa { +namespace map +{ constexpr BlockId START_NPC_NUM = wrap<BlockId>(110000000); // TODO make these species, see npc_class in npc_data constexpr Species WARP_CLASS = wrap<Species>(45); constexpr Species FAKE_NPC_CLASS = wrap<Species>(127); -constexpr Species WARP_DEBUG_CLASS = wrap<Species>(722); constexpr Species INVISIBLE_CLASS = wrap<Species>(32767); int npc_event_dequeue(dumb_ptr<map_session_data> sd); int npc_event(dumb_ptr<map_session_data> sd, NpcEvent npcname, int); -void npc_timer_event(NpcEvent eventname); // Added by RoVeRT -int npc_command(dumb_ptr<map_session_data> sd, NpcName npcname, XString command); -int npc_touch_areanpc(dumb_ptr<map_session_data>, map_local *, int, int); +int npc_touch_areanpc(dumb_ptr<map_session_data>, Borrowed<map_local>, int, int); int npc_click(dumb_ptr<map_session_data>, BlockId); int npc_scriptcont(dumb_ptr<map_session_data>, BlockId); int npc_buysellsel(dumb_ptr<map_session_data>, BlockId, int); int npc_buylist(dumb_ptr<map_session_data>, const std::vector<Packet_Repeat<0x00c8>>&); int npc_selllist(dumb_ptr<map_session_data>, const std::vector<Packet_Repeat<0x00c9>>&); -int npc_parse_warp(XString w1, XString, NpcName w3, XString w4); int npc_enable(NpcName name, bool flag); dumb_ptr<npc_data> npc_name2id(NpcName name); @@ -63,21 +58,10 @@ dumb_ptr<npc_data> npc_name2id(NpcName name); BlockId npc_get_new_npc_id(void); /** - * Spawns and installs a talk-only NPC - * - * \param message The message to speak. If message is nullptr, the NPC will not do anything at all. - */ -dumb_ptr<npc_data> npc_spawn_text(map_local *m, int x, int y, - Species class_, NpcName name, AString message); - -/** * Uninstalls and frees an NPC */ void npc_free(dumb_ptr<npc_data> npc); -void npc_addsrcfile(AString); -void npc_delsrcfile(XString); -bool do_init_npc(void); int npc_event_do_oninit(void); int npc_event_doall_l(ScriptLabel name, BlockId rid, Slice<argrec_t> argv); @@ -98,4 +82,5 @@ void npc_timerevent_stop(dumb_ptr<npc_data_script> nd); interval_t npc_gettimerevent_tick(dumb_ptr<npc_data_script> nd); void npc_settimerevent_tick(dumb_ptr<npc_data_script> nd, interval_t newtimer); int npc_delete(dumb_ptr<npc_data> nd); +} // namespace map } // namespace tmwa diff --git a/src/map/party.cpp b/src/map/party.cpp index 8713c60..ccbfd75 100644 --- a/src/map/party.cpp +++ b/src/map/party.cpp @@ -32,10 +32,12 @@ #include "../net/timer.hpp" #include "../mmo/ids.hpp" -#include "../mmo/mmo.hpp" +#include "../high/mmo.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "clif.hpp" +#include "globals.hpp" #include "intif.hpp" #include "map.hpp" #include "pc.hpp" @@ -45,13 +47,12 @@ namespace tmwa { +namespace map +{ // 座標やHP送信の間隔 constexpr interval_t PARTY_SEND_XYHP_INVERVAL = 1_s; static -Map<PartyId, PartyMost> party_db; - -static void party_check_conflict(dumb_ptr<map_session_data> sd); static void party_send_xyhp_timer(TimerData *tid, tick_t tick); @@ -66,32 +67,30 @@ void do_init_party(void) } // 検索 -PartyPair party_search(PartyId party_id) +Option<PartyPair> party_search(PartyId party_id) { - PartyPair p; - p.party_most = party_db.search(party_id); - if (p) - p.party_id = party_id; - return p; + Option<P<PartyMost>> party_most_ = party_db.search(party_id); + return party_most_.map([party_id](P<PartyMost> party_most) + { + return PartyPair{party_id, party_most}; + }); } static -void party_searchname_sub(PartyPair p, PartyName str, PartyPair *dst) +void party_searchname_sub(PartyPair p, PartyName str, Borrowed<Option<PartyPair>> dst) { if (p->name == str) - *dst = p; + *dst = Some(p); } // パーティ名検索 -PartyPair party_searchname(PartyName str) +Option<PartyPair> party_searchname(PartyName str) { - PartyPair p; + Option<PartyPair> p = None; for (auto& pair : party_db) { - PartyPair tmp; - tmp.party_id = pair.first; - tmp.party_most = &pair.second; - party_searchname_sub(tmp, str, &p); + PartyPair tmp{pair.first, borrow(pair.second)}; + party_searchname_sub(tmp, str, borrow(p)); } return p; } @@ -129,15 +128,13 @@ void party_created(AccountId account_id, int fail, PartyId party_id, PartyName n { sd->status.party_id = party_id; - PartyPair p = party_search(party_id); - if (p) + if (party_search(party_id).is_some()) { PRINTF("party_created(): ID already exists!\n"_fmt); exit(1); } - p.party_most = party_db.init(party_id); - p.party_id = party_id; + Borrowed<PartyMost> p = party_db.init(party_id); p->name = name; /* The party was created successfully. */ @@ -158,8 +155,6 @@ void party_request_info(PartyId party_id) static int party_check_member(PartyPair p) { - nullpo_retz(p); - for (io::FD i : iter_fds()) { Session *s = get_session(i); @@ -215,24 +210,32 @@ int party_recv_noinfo(PartyId party_id) return 0; } -// 情報所得 -int party_recv_info(const PartyPair sp) +static +PartyPair handle_info(const PartyPair sp) { - int i; - - nullpo_retz(sp); - - PartyPair p = party_search(sp.party_id); - if (!p) + Option<PartyPair> p_ = party_search(sp.party_id); + OMATCH_BEGIN_SOME (p, p_) + { + *p.party_most = *sp.party_most; + return p; + } + OMATCH_END (); { - p.party_most = party_db.init(sp.party_id); + PartyPair p{sp.party_id, party_db.init(sp.party_id)}; // 最初のロードなのでユーザーのチェックを行う *p.party_most = *sp.party_most; party_check_member(p); + return p; } - else - *p.party_most = *sp.party_most; +} + +// 情報所得 +int party_recv_info(const PartyPair sp) +{ + int i; + + PartyPair p = handle_info(sp); for (i = 0; i < MAX_PARTY; i++) { // sdの設定 @@ -261,13 +264,13 @@ int party_recv_info(const PartyPair sp) int party_invite(dumb_ptr<map_session_data> sd, AccountId account_id) { dumb_ptr<map_session_data> tsd = map_id2sd(account_to_block(account_id)); - PartyPair p = party_search(sd->status.party_id); + PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0); int i; int full = 1; /* Indicates whether or not there's room for one more. */ nullpo_retz(sd); - if (!tsd || !p || !tsd->sess) + if (!tsd || !tsd->sess) return 0; if (!battle_config.invite_request_check) @@ -359,7 +362,6 @@ int party_reply_invite(dumb_ptr<map_session_data> sd, AccountId account_id, int int party_member_added(PartyId party_id, AccountId account_id, int flag) { dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(account_id)), sd2; - PartyPair p = party_search(party_id); if (sd == nullptr) { @@ -376,12 +378,12 @@ int party_member_added(PartyId party_id, AccountId account_id, int flag) sd->party_invite = PartyId(); sd->party_invite_account = AccountId(); - if (!p) + PartyPair p = TRY_UNWRAP(party_search(party_id), { PRINTF("party_member_added: party %d not found.\n"_fmt, party_id); intif_party_leave(party_id, account_id); return 0; - } + }); if (flag == 1) { // 失敗 @@ -408,13 +410,11 @@ int party_member_added(PartyId party_id, AccountId account_id, int flag) // パーティ除名要求 int party_removemember(dumb_ptr<map_session_data> sd, AccountId account_id) { - PartyPair p; int i; nullpo_retz(sd); - if (!(p = party_search(sd->status.party_id))) - return 0; + PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0); for (i = 0; i < MAX_PARTY; i++) { // リーダーかどうかチェック @@ -439,13 +439,11 @@ int party_removemember(dumb_ptr<map_session_data> sd, AccountId account_id) // パーティ脱退要求 int party_leave(dumb_ptr<map_session_data> sd) { - PartyPair p; int i; nullpo_retz(sd); - if (!(p = party_search(sd->status.party_id))) - return 0; + PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0); for (i = 0; i < MAX_PARTY; i++) { // 所属しているか @@ -462,8 +460,8 @@ int party_leave(dumb_ptr<map_session_data> sd) int party_member_leaved(PartyId party_id, AccountId account_id, CharName name) { dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(account_id)); - PartyPair p = party_search(party_id); - if (p) + Option<PartyPair> p_ = party_search(party_id); + OMATCH_BEGIN_SOME (p, p_) { int i; for (i = 0; i < MAX_PARTY; i++) @@ -474,6 +472,7 @@ int party_member_leaved(PartyId party_id, AccountId account_id, CharName name) p->member[i].sd = nullptr; } } + OMATCH_END (); if (sd != nullptr && sd->status.party_id == party_id) { sd->status.party_id = PartyId(); @@ -485,10 +484,8 @@ int party_member_leaved(PartyId party_id, AccountId account_id, CharName name) // パーティ解散通知 int party_broken(PartyId party_id) { - PartyPair p; int i; - if (!(p = party_search(party_id))) - return 0; + PartyPair p = TRY_UNWRAP(party_search(party_id), return 0); for (i = 0; i < MAX_PARTY; i++) { @@ -508,12 +505,11 @@ int party_broken(PartyId party_id) // パーティの設定変更要求 int party_changeoption(dumb_ptr<map_session_data> sd, int exp, int item) { - PartyPair p; - nullpo_retz(sd); - if (!sd->status.party_id - || !(p = party_search(sd->status.party_id))) + if (!sd->status.party_id) + return 0; + if (party_search(sd->status.party_id).is_none()) return 0; intif_party_changeoption(sd->status.party_id, sd->status_key.account_id, exp, item); @@ -524,10 +520,8 @@ int party_changeoption(dumb_ptr<map_session_data> sd, int exp, int item) int party_optionchanged(PartyId party_id, AccountId account_id, int exp, int item, int flag) { - PartyPair p; dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(account_id)); - if (!(p = party_search(party_id))) - return 0; + PartyPair p = TRY_UNWRAP(party_search(party_id), return 0); if (!(flag & 0x01)) p->exp = exp; @@ -541,10 +535,8 @@ int party_optionchanged(PartyId party_id, AccountId account_id, int exp, int ite void party_recv_movemap(PartyId party_id, AccountId account_id, MapName mapname, int online, int lv) { - PartyPair p; int i; - if (!(p = party_search(party_id))) - return; + PartyPair p = TRY_UNWRAP(party_search(party_id), return); for (i = 0; i < MAX_PARTY; i++) { PartyMember *m = &p->member[i]; @@ -584,8 +576,6 @@ void party_recv_movemap(PartyId party_id, AccountId account_id, MapName mapname, // パーティメンバの移動 int party_send_movemap(dumb_ptr<map_session_data> sd) { - PartyPair p; - nullpo_retz(sd); if (!sd->status.party_id) @@ -599,7 +589,7 @@ int party_send_movemap(dumb_ptr<map_session_data> sd) party_check_conflict(sd); // あるならパーティ情報送信 - if ((p = party_search(sd->status.party_id))) + PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0); { party_check_member(p); // 所属を確認する if (sd->status.party_id == p.party_id) @@ -616,15 +606,13 @@ int party_send_movemap(dumb_ptr<map_session_data> sd) // パーティメンバのログアウト int party_send_logout(dumb_ptr<map_session_data> sd) { - PartyPair p; - nullpo_retz(sd); if (sd->status.party_id) intif_party_changemap(sd, 0); // sdが無効になるのでパーティ情報から削除 - if ((p = party_search(sd->status.party_id))) + PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return 0); { int i; for (i = 0; i < MAX_PARTY; i++) @@ -646,9 +634,7 @@ void party_send_message(dumb_ptr<map_session_data> sd, XString mes) // パーティメッセージ受信 void party_recv_message(PartyId party_id, AccountId account_id, XString mes) { - PartyPair p; - if (!(p = party_search(party_id))) - return; + PartyPair p = TRY_UNWRAP(party_search(party_id), return); clif_party_message(p, account_id, mes); } @@ -667,8 +653,6 @@ void party_send_xyhp_timer_sub(PartyPair p) { int i; - nullpo_retv(p); - for (i = 0; i < MAX_PARTY; i++) { dumb_ptr<map_session_data> sd = dumb_ptr<map_session_data>(p->member[i].sd); @@ -697,9 +681,7 @@ void party_send_xyhp_timer(TimerData *, tick_t) { for (auto& pair : party_db) { - PartyPair tmp; - tmp.party_id = pair.first; - tmp.party_most = &pair.second; + PartyPair tmp{pair.first, borrow(pair.second)}; party_send_xyhp_timer_sub(tmp); } } @@ -709,8 +691,6 @@ void party_send_xy_clear(PartyPair p) { int i; - nullpo_retv(p); - for (i = 0; i < MAX_PARTY; i++) { dumb_ptr<map_session_data> sd = dumb_ptr<map_session_data>(p->member[i].sd); @@ -739,13 +719,11 @@ void party_send_hp_check(dumb_ptr<block_list> bl, PartyId party_id, int *flag) } // 経験値公平分配 -int party_exp_share(PartyPair p, map_local *mapid, int base_exp, int job_exp) +int party_exp_share(PartyPair p, Borrowed<map_local> mapid, int base_exp, int job_exp) { dumb_ptr<map_session_data> sd; int i, c; - nullpo_retz(p); - for (i = c = 0; i < MAX_PARTY; i++) { sd = dumb_ptr<map_session_data>(p->member[i].sd); @@ -770,7 +748,6 @@ int party_exp_share(PartyPair p, map_local *mapid, int base_exp, int job_exp) void party_foreachsamemap(std::function<void(dumb_ptr<block_list>)> func, dumb_ptr<map_session_data> sd, int type) { - PartyPair p; int i; int x0, y0, x1, y1; dumb_ptr<map_session_data> list[MAX_PARTY]; @@ -778,8 +755,7 @@ void party_foreachsamemap(std::function<void(dumb_ptr<block_list>)> func, nullpo_retv(sd); - if (!(p = party_search(sd->status.party_id))) - return; + PartyPair p = TRY_UNWRAP(party_search(sd->status.party_id), return); x0 = sd->bl_x - AREA_SIZE; y0 = sd->bl_y - AREA_SIZE; @@ -807,4 +783,5 @@ void party_foreachsamemap(std::function<void(dumb_ptr<block_list>)> func, if (list[i]->bl_prev) // 有効かどうかチェック func(list[i]); } +} // namespace map } // namespace tmwa diff --git a/src/map/party.hpp b/src/map/party.hpp index 01a8125..669857e 100644 --- a/src/map/party.hpp +++ b/src/map/party.hpp @@ -24,18 +24,14 @@ #include <functional> -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" - -#include "../mmo/fwd.hpp" - namespace tmwa { +namespace map +{ void do_init_party(void); -PartyPair party_search(PartyId party_id); -PartyPair party_searchname(PartyName str); +Option<PartyPair> party_search(PartyId party_id); +Option<PartyPair> party_searchname(PartyName str); int party_create(dumb_ptr<map_session_data> sd, PartyName name); void party_created(AccountId account_id, int fail, PartyId party_id, PartyName name); @@ -65,8 +61,9 @@ void party_recv_message(PartyId party_id, AccountId account_id, XString mes); void party_send_xy_clear(PartyPair p); void party_send_hp_check(dumb_ptr<block_list> bl, PartyId party_id, int *flag); -int party_exp_share(PartyPair p, map_local *map, int base_exp, int job_exp); +int party_exp_share(PartyPair p, Borrowed<map_local> map, int base_exp, int job_exp); void party_foreachsamemap(std::function<void(dumb_ptr<block_list>)> func, dumb_ptr<map_session_data> sd, int type); +} // namespace map } // namespace tmwa diff --git a/src/map/path.cpp b/src/map/path.cpp index 6950797..52d20ad 100644 --- a/src/map/path.cpp +++ b/src/map/path.cpp @@ -31,7 +31,7 @@ #include "../io/cxxstdio.hpp" -#include "clif.t.hpp" +#include "../mmo/clif.t.hpp" #include "map.hpp" #include "../poison.hpp" @@ -39,6 +39,8 @@ namespace tmwa { +namespace map +{ constexpr int MAX_HEAP = 150; struct tmp_path { @@ -212,10 +214,8 @@ int add_path(int *heap, struct tmp_path *tp, int x, int y, int dist, *------------------------------------------ */ static -bool can_place(struct map_local *m, int x, int y) +bool can_place(Borrowed<struct map_local> m, int x, int y) { - nullpo_retz(m); - return !bool(read_gatp(m, x, y) & MapCell::UNWALKABLE); } @@ -224,10 +224,8 @@ bool can_place(struct map_local *m, int x, int y) *------------------------------------------ */ static -int can_move(struct map_local *m, int x0, int y0, int x1, int y1) +int can_move(Borrowed<struct map_local> m, int x0, int y0, int x1, int y1) { - nullpo_retz(m); - if (x0 - x1 < -1 || x0 - x1 > 1 || y0 - y1 < -1 || y0 - y1 > 1) return 0; if (x1 < 0 || y1 < 0 || x1 >= m->xs || y1 >= m->ys) @@ -247,7 +245,7 @@ int can_move(struct map_local *m, int x0, int y0, int x1, int y1) * path探索 (x0,y0)->(x1,y1) *------------------------------------------ */ -int path_search(struct walkpath_data *wpd, map_local *m, int x0, int y0, int x1, int y1, int flag) +int path_search(struct walkpath_data *wpd, Borrowed<map_local> m, int x0, int y0, int x1, int y1, int flag) { int heap[MAX_HEAP + 1]; int i, rp, x, y; @@ -256,7 +254,7 @@ int path_search(struct walkpath_data *wpd, map_local *m, int x0, int y0, int x1, nullpo_retz(wpd); assert (m->gat); - map_local *md = m; + P<map_local> md = m; if (x1 < 0 || x1 >= md->xs || y1 < 0 || y1 >= md->ys || bool(read_gatp(md, x1, y1) & MapCell::UNWALKABLE)) return -1; @@ -361,4 +359,5 @@ int path_search(struct walkpath_data *wpd, map_local *m, int x0, int y0, int x1, return -1; } } +} // namespace map } // namespace tmwa diff --git a/src/map/path.hpp b/src/map/path.hpp index 3619e2e..f16baaa 100644 --- a/src/map/path.hpp +++ b/src/map/path.hpp @@ -25,5 +25,8 @@ namespace tmwa { -int path_search(struct walkpath_data *, map_local *, int, int, int, int, int); +namespace map +{ +int path_search(struct walkpath_data *, Borrowed<map_local>, int, int, int, int, int); +} // namespace map } // namespace tmwa diff --git a/src/map/pc.cpp b/src/map/pc.cpp index ada5b9f..6fa35b0 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -37,36 +37,42 @@ #include "../generic/random.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" #include "../io/read.hpp" -#include "../net/timer.hpp" +#include "../mmo/cxxstdio_enums.hpp" -#include "../mmo/utils.hpp" +#include "../net/timer.hpp" +#include "../net/timestamp-utils.hpp" #include "../proto2/char-map.hpp" #include "atcommand.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "chrif.hpp" #include "clif.hpp" +#include "globals.hpp" #include "intif.hpp" #include "itemdb.hpp" #include "magic-stmt.hpp" #include "map.hpp" +#include "map_conf.hpp" #include "npc.hpp" #include "party.hpp" #include "path.hpp" -#include "script.hpp" +#include "script-call.hpp" #include "skill.hpp" #include "storage.hpp" #include "trade.hpp" +#include "quest.hpp" #include "../poison.hpp" namespace tmwa { +namespace map +{ // PVP順位計算の間隔 constexpr std::chrono::milliseconds PVP_CALCRANK_INTERVAL = 1_s; @@ -120,7 +126,7 @@ int sp_coefficient_0 = 100; // coefficients for each weapon type // (not all used) static //const -earray<interval_t, ItemLook, ItemLook::SINGLE_HANDED_COUNT> aspd_base_0 //= +earray<interval_t, ItemLook, ItemLook::COUNT> aspd_base_0 //= {{ 650_ms, 700_ms, @@ -254,10 +260,6 @@ earray<EPOS, EQUIP, EQUIP::COUNT> equip_pos //= EPOS::ARROW, }}; -// TODO use DMap<> -static -std::map<AccountId, GmLevel> gm_accountm; - static int pc_checkoverhp(dumb_ptr<map_session_data> sd); static @@ -287,16 +289,12 @@ int pc_iskiller(dumb_ptr<map_session_data> src, { nullpo_retz(src); - if (src->bl_type != BL::PC) + if (src->bl_type != BL::PC || target->bl_type != BL::PC) return 0; - if (src->special_state.killer) + if ((src->state.pvpchannel == 1) && (target->state.pvpchannel == 1) && !src->bl_m->flag.get(MapFlag::NOPVP)) return 1; - - if (target->bl_type != BL::PC) - return 0; - if (target->special_state.killable) + if ((src->state.pvpchannel > 1) && (target->state.pvpchannel == src->state.pvpchannel)) // this one does not respect NOPVP return 1; - return 0; } @@ -319,6 +317,33 @@ int distance(int x0, int y0, int x1, int y1) } static +void pc_pvp_timer(TimerData *, tick_t, BlockId id) +{ + dumb_ptr<map_session_data> sd = map_id2sd(id); + + assert (sd != nullptr); + assert (sd->bl_type == BL::PC); +} + +int pc_setpvptimer(dumb_ptr<map_session_data> sd, interval_t val) +{ + nullpo_retz(sd); + + sd->pvp_timer = Timer(gettick() + val, + std::bind(pc_pvp_timer, ph::_1, ph::_2, + sd->bl_id)); + return 0; +} + +int pc_delpvptimer(dumb_ptr<map_session_data> sd) +{ + nullpo_retz(sd); + + sd->pvp_timer.cancel(); + return 0; +} + +static void pc_invincible_timer(TimerData *, tick_t, BlockId id) { dumb_ptr<map_session_data> sd = map_id2sd(id); @@ -377,7 +402,6 @@ int pc_setrestartvalue(dumb_ptr<map_session_data> sd, int type) clif_updatestatus(sd, SP::SP); sd->heal_xp = 0; // [Fate] Set gainable xp for healing this player to 0 - return 0; } @@ -464,7 +488,7 @@ void pc_makesavestatus(dumb_ptr<map_session_data> sd) // セーブ禁止マップだったので指定位置に移動 if (sd->bl_m->flag.get(MapFlag::NOSAVE)) { - map_local *m = sd->bl_m; + P<map_local> m = sd->bl_m; if (m->save.map_ == "SavePoint"_s) sd->status.last_point = sd->status.save_point; else @@ -505,12 +529,7 @@ EPOS pc_equippoint(dumb_ptr<map_session_data> sd, IOff0 n) { nullpo_retr(EPOS::ZERO, sd); - if (!sd->inventory_data[n]) - return EPOS::ZERO; - - EPOS ep = sd->inventory_data[n]->equip; - - return ep; + return sd->inventory_data[n].pmd_pget(&item_data::equip).copy_or(EPOS::ZERO); } static @@ -521,7 +540,11 @@ int pc_setinventorydata(dumb_ptr<map_session_data> sd) for (IOff0 i : IOff0::iter()) { ItemNameId id = sd->status.inventory[i].nameid; - sd->inventory_data[i] = itemdb_search(id); + // If you think you understand this line, you're wrong. + // It does not do what you think it does. Rather, you need to + // understand it in the context in which it is used. Despite this, + // it is quite common for elements to be None. + sd->inventory_data[i] = Some(itemdb_search(id)); } return 0; } @@ -531,40 +554,8 @@ int pc_calcweapontype(dumb_ptr<map_session_data> sd) { nullpo_retz(sd); - if (sd->weapontype1 != ItemLook::NONE - && sd->weapontype2 == ItemLook::NONE) - sd->status.weapon = sd->weapontype1; - if (sd->weapontype1 == ItemLook::NONE - && sd->weapontype2 != ItemLook::NONE) - sd->status.weapon = sd->weapontype2; - else if (sd->weapontype1 == ItemLook::BLADE - && sd->weapontype2 == ItemLook::BLADE) - sd->status.weapon = ItemLook::DUAL_BLADE; - else if (sd->weapontype1 == ItemLook::_2 - && sd->weapontype2 == ItemLook::_2) - sd->status.weapon = ItemLook::DUAL_2; - else if (sd->weapontype1 == ItemLook::_6 - && sd->weapontype2 == ItemLook::_6) - sd->status.weapon = ItemLook::DUAL_6; - else if ((sd->weapontype1 == ItemLook::BLADE - && sd->weapontype2 == ItemLook::_2) - || (sd->weapontype1 == ItemLook::_2 - && sd->weapontype2 == ItemLook::BLADE)) - sd->status.weapon = ItemLook::DUAL_12; - else if ( - (sd->weapontype1 == ItemLook::BLADE - && sd->weapontype2 == ItemLook::_6) - || (sd->weapontype1 == ItemLook::_6 - && sd->weapontype2 == ItemLook::BLADE)) - sd->status.weapon = ItemLook::DUAL_16; - else if ( - (sd->weapontype1 == ItemLook::_2 - && sd->weapontype2 == ItemLook::_6) - || (sd->weapontype1 == ItemLook::_6 - && sd->weapontype2 == ItemLook::_2)) - sd->status.weapon = ItemLook::DUAL_26; - else - sd->status.weapon = sd->weapontype1; + // TODO now that there is no calculation here, store only once + sd->status.weapon = sd->weapontype1; return 0; } @@ -588,27 +579,30 @@ int pc_setequipindex(dumb_ptr<map_session_data> sd) sd->equip_index_maybe[j] = i; if (bool(sd->status.inventory[i].equip & EPOS::WEAPON)) { - if (sd->inventory_data[i]) - sd->weapontype1 = sd->inventory_data[i]->look; - else - sd->weapontype1 = ItemLook::NONE; + OMATCH_BEGIN (sd->inventory_data[i]) + { + OMATCH_CASE_SOME (sdidi) + { + sd->weapontype1 = sdidi->look; + } + OMATCH_CASE_NONE () + { + sd->weapontype1 = ItemLook::NONE; + } + } + OMATCH_END (); } if (bool(sd->status.inventory[i].equip & EPOS::SHIELD)) { - if (sd->inventory_data[i]) + OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[i]) { - if (sd->inventory_data[i]->type == ItemType::WEAPON) + if (sdidi->type == ItemType::WEAPON) { if (sd->status.inventory[i].equip == EPOS::SHIELD) - sd->weapontype2 = sd->inventory_data[i]->look; - else - sd->weapontype2 = ItemLook::NONE; + assert(0 && "unreachable - offhand weapons are not supported"); } - else - sd->weapontype2 = ItemLook::NONE; } - else - sd->weapontype2 = ItemLook::NONE; + OMATCH_END (); } } } @@ -620,21 +614,18 @@ int pc_setequipindex(dumb_ptr<map_session_data> sd) static int pc_isequip(dumb_ptr<map_session_data> sd, IOff0 n) { - struct item_data *item; eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data; //転生や養子の場合の元の職業を算出する nullpo_retz(sd); - item = sd->inventory_data[n]; sc_data = battle_get_sc_data(sd); - GmLevel gm_all_equipment = GmLevel::from(static_cast<uint32_t>(battle_config.gm_all_equipment)); + GmLevel gm_all_equipment = battle_config.gm_all_equipment; if (gm_all_equipment && pc_isGM(sd).satisfies(gm_all_equipment)) return 1; - if (item == nullptr) - return 0; + P<struct item_data> item = TRY_UNWRAP(sd->inventory_data[n], return 0); if (item->sex != SEX::NEUTRAL && sd->status.sex != item->sex) return 0; if (item->elv > 0 && sd->status.base_level < item->elv) @@ -648,12 +639,11 @@ int pc_isequip(dumb_ptr<map_session_data> sd, IOff0 n) * char鯖から送られてきたステータスを設定 *------------------------------------------ */ -int pc_authok(AccountId id, int login_id2, TimeT connect_until_time, +int pc_authok(AccountId id, int login_id2, short tmw_version, const CharKey *st_key, const CharData *st_data) { dumb_ptr<map_session_data> sd = nullptr; - PartyPair p; tick_t tick = gettick(); sd = map_id2sd(account_to_block(id)); @@ -681,7 +671,7 @@ int pc_authok(AccountId id, int login_id2, TimeT connect_until_time, sd->state.connect_new = 1; sd->bl_prev = sd->bl_next = nullptr; - sd->weapontype1 = sd->weapontype2 = ItemLook::NONE; + sd->weapontype1 = ItemLook::NONE; sd->speed = DEFAULT_WALK_SPEED; sd->state.dead_sit = 0; sd->dir = DIR::S; @@ -727,21 +717,6 @@ int pc_authok(AccountId id, int login_id2, TimeT connect_until_time, // sd->sc_data[i].timer = nullptr; sd->sc_data[i].val1 = 0; } - sd->sc_count = 0; - { - Option old_option = sd->status.option; - sd->status.option = Option::ZERO; - - // This would leak information. - // It's better to make it obvious that players can see you. - if (false && bool(old_option & Option::INVISIBILITY)) - is_atcommand(sd->sess, sd, "@invisible"_s, GmLevel()); - - if (bool(old_option & Option::HIDE)) - is_atcommand(sd->sess, sd, "@hide"_s, GmLevel()); - // atcommand_hide might already send it, but also might not - clif_changeoption(sd); - } // パーティー関係の初期化 sd->party_sended = 0; @@ -757,9 +732,24 @@ int pc_authok(AccountId id, int login_id2, TimeT connect_until_time, pc_setpos(sd, sd->status.last_point.map_, sd->status.last_point.x, sd->status.last_point.y, BeingRemoveWhy::GONE); + { + Opt0 old_option = sd->status.option; + sd->status.option = Opt0::ZERO; + + // This would leak information. + // It's better to make it obvious that players can see you. + if (false && bool(old_option & Opt0::INVISIBILITY)) + is_atcommand(sd->sess, sd, "@invisible"_s, GmLevel()); + + if (bool(old_option & Opt0::HIDE)) + is_atcommand(sd->sess, sd, "@hide"_s, GmLevel()); + // atcommand_hide might already send it, but also might not + clif_changeoption(sd); + } + // パーティ、ギルドデータの要求 if (sd->status.party_id - && !(p = party_search(sd->status.party_id))) + && party_search(sd->status.party_id).is_none()) party_request_info(sd->status.party_id); // pvpの設定 @@ -793,27 +783,21 @@ int pc_authok(AccountId id, int login_id2, TimeT connect_until_time, sd->auto_ban_info.in_progress = 0; // Initialize antispam vars - sd->chat_reset_due = TimeT(); + sd->chat_reset_due = tick_t(); sd->chat_lines_in = sd->chat_total_repeats = 0; - sd->chat_repeat_reset_due = TimeT(); + sd->chat_repeat_reset_due = tick_t(); sd->chat_lastmsg = RString(); for (tick_t& t : sd->flood_rates) t = tick_t(); - sd->packet_flood_reset_due = TimeT(); + sd->packet_flood_reset_due = tick_t(); sd->packet_flood_in = 0; - // message of the limited time of the account - if (connect_until_time) - { - timestamp_seconds_buffer buffer; - stamp_time(buffer, &connect_until_time); - AString tmpstr = STRPRINTF("Your account time limit is: %s"_fmt, buffer); - - clif_wis_message(sd->sess, wisp_server_name, tmpstr); - } pc_calcstatus(sd, 1); + npc_event_doall_l(stringish<ScriptLabel>("OnPCLoginEvent"_s), sd->bl_id, nullptr); + // Init Quest Log + clif_sendallquest(sd); return 0; } @@ -830,7 +814,7 @@ void pc_show_motd(dumb_ptr<map_session_data> sd) clif_displaymessage(sd->sess, "This server is Free Software, for details type @source in chat or use the tmwa-source tool"_s); sd->state.seen_motd = true; - io::ReadFile in(motd_txt); + io::ReadFile in(map_conf.motd_txt); if (in.is_open()) { AString buf; @@ -865,7 +849,8 @@ int pc_calc_skillpoint(dumb_ptr<map_session_data> sd) nullpo_retz(sd); - for (i = 0; i < skill_pool_skills_size; i++) { + for (i = 0; i < skill_pool_skills.size(); i++) + { int lv = sd->status.skill[skill_pool_skills[i]].lv; if (lv) skill_points += ((lv * (lv - 1)) >> 1) - 1; @@ -940,6 +925,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) int bl; int aspd_rate, refinedef = 0; int str, dstr, dex; + int b_pvpchannel = 0; nullpo_retz(sd); @@ -968,6 +954,8 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) b_mdef = sd->mdef; b_mdef2 = sd->mdef2; b_base_atk = sd->base_atk; + if (!pc_isdead(sd) && sd->state.pvpchannel == 1) + b_pvpchannel = sd->state.pvpchannel; sd->max_weight = max_weight_base_0 + sd->status.attrs[ATTR::STR] * 300; @@ -976,11 +964,11 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) sd->weight = 0; for (IOff0 i : IOff0::iter()) { - if (!sd->status.inventory[i].nameid - || sd->inventory_data[i] == nullptr) + if (!sd->status.inventory[i].nameid) continue; + P<struct item_data> sdidi = TRY_UNWRAP(sd->inventory_data[i], continue); sd->weight += - sd->inventory_data[i]->weight * + sdidi->weight * sd->status.inventory[i].amount; } // used to fill cart @@ -1005,7 +993,6 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) sd->status.max_hp = 0; sd->status.max_sp = 0; sd->attackrange = 0; - sd->attackrange_ = 0; sd->matk1 = 0; sd->matk2 = 0; sd->speed = DEFAULT_WALK_SPEED; @@ -1016,13 +1003,9 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) sd->arrow_atk = 0; sd->arrow_hit = 0; sd->arrow_range = 0; - sd->nhealhp = sd->nhealsp = sd->nshealhp = sd->nshealsp = sd->nsshealhp = - sd->nsshealsp = 0; + sd->nhealhp = sd->nhealsp = 0; really_memzero_this(&sd->special_state); - sd->watk_ = 0; //二刀流用(仮) - sd->watk_2 = 0; - sd->aspd_rate = 100; sd->speed_rate = 100; sd->hprecov_rate = 100; @@ -1038,8 +1021,6 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) sd->double_add_rate = sd->perfect_hit_add = 0; sd->hp_drain_rate = sd->hp_drain_per = sd->sp_drain_rate = sd->sp_drain_per = 0; - sd->hp_drain_rate_ = sd->hp_drain_per_ = sd->sp_drain_rate_ = - sd->sp_drain_per_ = 0; sd->spellpower_bonus_target = 0; @@ -1057,13 +1038,14 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) || sd->equip_index_maybe[EQUIP::LEGS] == index)) continue; - if (sd->inventory_data[index]) + OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[index]) { sd->spellpower_bonus_target += - sd->inventory_data[index]->magic_bonus; + sdidi->magic_bonus; // used to apply cards } + OMATCH_END (); } #ifdef USE_ASTRAL_SOUL_SKILL @@ -1092,31 +1074,15 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) && (sd->equip_index_maybe[EQUIP::TORSO] == index || sd->equip_index_maybe[EQUIP::LEGS] == index)) continue; - if (sd->inventory_data[index]) + OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[index]) { - sd->def += sd->inventory_data[index]->def; - if (sd->inventory_data[index]->type == ItemType::WEAPON) + sd->def += sdidi->def; + if (sdidi->type == ItemType::WEAPON) { if (i == EQUIP::SHIELD && sd->status.inventory[index].equip == EPOS::SHIELD) { - //二刀流用データ入力 - sd->watk_ += sd->inventory_data[index]->atk; - sd->watk_2 = 0; - - sd->attackrange_ += sd->inventory_data[index]->range; - sd->state.lr_flag = 1; - { - argrec_t arg[2] = - { - {"@slotId"_s, static_cast<int>(i)}, - {"@itemId"_s, unwrap<ItemNameId>(sd->inventory_data[index]->nameid)}, - }; - run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0), - sd->bl_id, BlockId(), - arg); - } - sd->state.lr_flag = 0; + assert(0 && "unreachable - offhand weapons are not supported"); } else { @@ -1124,66 +1090,62 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) argrec_t arg[2] = { {"@slotId"_s, static_cast<int>(i)}, - {"@itemId"_s, unwrap<ItemNameId>(sd->inventory_data[index]->nameid)}, + {"@itemId"_s, unwrap<ItemNameId>(sdidi->nameid)}, }; - sd->watk += sd->inventory_data[index]->atk; + sd->watk += sdidi->atk; - sd->attackrange += sd->inventory_data[index]->range; - run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0), + sd->attackrange += sdidi->range; + run_script_l(ScriptPointer(borrow(*sdidi->equip_script), 0), sd->bl_id, BlockId(), arg); } } - else if (sd->inventory_data[index]->type == ItemType::ARMOR) + else if (sdidi->type == ItemType::ARMOR) { argrec_t arg[2] = { {"@slotId"_s, static_cast<int>(i)}, - {"@itemId"_s, unwrap<ItemNameId>(sd->inventory_data[index]->nameid)}, + {"@itemId"_s, unwrap<ItemNameId>(sdidi->nameid)}, }; - sd->watk += sd->inventory_data[index]->atk; - run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0), + sd->watk += sdidi->atk; + run_script_l(ScriptPointer(borrow(*sdidi->equip_script), 0), sd->bl_id, BlockId(), arg); } } + OMATCH_END (); } if (battle_is_unarmed(sd)) { sd->watk += skill_power(sd, SkillID::TMW_BRAWLING) / 3; // +66 for 200 sd->watk2 += skill_power(sd, SkillID::TMW_BRAWLING) >> 3; // +25 for 200 - sd->watk_ += skill_power(sd, SkillID::TMW_BRAWLING) / 3; // +66 for 200 - sd->watk_2 += skill_power(sd, SkillID::TMW_BRAWLING) >> 3; // +25 for 200 } IOff0 aidx = sd->equip_index_maybe[EQUIP::ARROW]; if (aidx.ok()) { IOff0 index = aidx; - if (sd->inventory_data[index]) + OMATCH_BEGIN_SOME (sdidi, sd->inventory_data[index]) { //まだ属性が入っていない argrec_t arg[2] = { {"@slotId"_s, static_cast<int>(EQUIP::ARROW)}, - {"@itemId"_s, unwrap<ItemNameId>(sd->inventory_data[index]->nameid)}, + {"@itemId"_s, unwrap<ItemNameId>(sdidi->nameid)}, }; - sd->state.lr_flag = 2; - run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0), + sd->state.lr_flag_is_arrow_2 = 1; + run_script_l(ScriptPointer(borrow(*sdidi->equip_script), 0), sd->bl_id, BlockId(), arg); - sd->state.lr_flag = 0; - sd->arrow_atk += sd->inventory_data[index]->atk; + sd->state.lr_flag_is_arrow_2 = 0; + sd->arrow_atk += sdidi->atk; } + OMATCH_END (); } sd->def += (refinedef + 50) / 100; if (sd->attackrange < 1) sd->attackrange = 1; - if (sd->attackrange_ < 1) - sd->attackrange_ = 1; - if (sd->attackrange < sd->attackrange_) - sd->attackrange = sd->attackrange_; if (sd->status.weapon == ItemLook::BOW) sd->attackrange += sd->arrow_range; sd->double_rate += sd->double_add_rate; @@ -1201,9 +1163,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) for (ATTR attr : ATTRs) sd->paramc[attr] = std::max(0, sd->status.attrs[attr] + sd->paramb[attr] + sd->parame[attr]); - if (sd->status.weapon == ItemLook::BOW - || sd->status.weapon == ItemLook::_13 - || sd->status.weapon == ItemLook::_14) + if (sd->status.weapon == ItemLook::BOW) { str = sd->paramc[ATTR::DEX]; dex = sd->paramc[ATTR::STR]; @@ -1291,20 +1251,11 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) sd->mdef2 = 1; // 二刀流 ASPD 修正 - if (sd->status.weapon < ItemLook::SINGLE_HANDED_COUNT) + { sd->aspd += aspd_base_0[sd->status.weapon] - (sd->paramc[ATTR::AGI] * 4 + sd->paramc[ATTR::DEX]) * aspd_base_0[sd->status.weapon] / 1000; - else - sd->aspd += ( - (aspd_base_0[sd->weapontype1] - - (sd->paramc[ATTR::AGI] * 4 + sd->paramc[ATTR::DEX]) - * aspd_base_0[sd->weapontype1] / 1000) - + (aspd_base_0[sd->weapontype2] - - (sd->paramc[ATTR::AGI] * 4 + sd->paramc[ATTR::DEX]) - * aspd_base_0[sd->weapontype2] / 1000) - ) - * 140 / 200; + } aspd_rate = sd->aspd_rate; @@ -1366,7 +1317,6 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) } // スキルやステータス異常による残りのパラメータ補正 - if (sd->sc_count) { // ATK/DEF変化形 if (sd->sc_data[StatusChange::SC_POISON].timer) // 毒状態 @@ -1401,7 +1351,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) if (sd->attack_spell_override) sd->aspd = sd->attack_spell_delay; - sd->aspd = std::max(sd->aspd, static_cast<interval_t>(battle_config.max_aspd)); + sd->aspd = std::max(sd->aspd, battle_config.max_aspd); sd->amotion = sd->aspd; sd->dmotion = std::chrono::milliseconds(800 - sd->paramc[ATTR::AGI] * 4); sd->dmotion = std::max(sd->dmotion, 400_ms); @@ -1478,6 +1428,8 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) clif_updatestatus(sd, SP::HP); if (b_sp != sd->status.sp) clif_updatestatus(sd, SP::SP); + if (b_pvpchannel != sd->state.pvpchannel) + sd->state.pvpchannel = b_pvpchannel; return 0; } @@ -1498,122 +1450,116 @@ int pc_bonus(dumb_ptr<map_session_data> sd, SP type, int val) case SP::INT: case SP::DEX: case SP::LUK: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->parame[sp_to_attr(type)] += val; break; #if 0 case SP::ATK1: - if (!sd->state.lr_flag) + if (!sd->state.lr_flag_is_arrow_2) sd->watk += val; - else if (sd->state.lr_flag == 1) - sd->watk_ += val; break; #endif #if 0 case SP::ATK2: - if (!sd->state.lr_flag) + if (!sd->state.lr_flag_is_arrow_2) sd->watk2 += val; - else if (sd->state.lr_flag == 1) - sd->watk_2 += val; break; #endif #if 0 case SP::BASE_ATK: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->base_atk += val; break; #endif #if 0 case SP::MATK1: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->matk1 += val; break; #endif #if 0 case SP::MATK2: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->matk2 += val; break; #endif #if 0 case SP::DEF1: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->def += val; break; #endif case SP::MDEF1: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->mdef += val; break; #if 0 case SP::MDEF2: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->mdef += val; break; #endif case SP::HIT: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->hit += val; else sd->arrow_hit += val; break; case SP::FLEE1: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->flee += val; break; #if 0 case SP::FLEE2: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->flee2 += val * 10; break; #endif case SP::CRITICAL: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->critical += val * 10; else sd->arrow_cri += val * 10; break; case SP::MAXHP: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->status.max_hp += val; break; case SP::MAXSP: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->status.max_sp += val; break; case SP::MAXHPRATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->hprate += val; break; #if 0 case SP::MAXSPRATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->sprate += val; break; #endif #if 0 case SP::SPRATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->dsprate += val; break; #endif case SP::ATTACKRANGE: - if (!sd->state.lr_flag) + if (!sd->state.lr_flag_is_arrow_2) sd->attackrange += val; - else if (sd->state.lr_flag == 1) - sd->attackrange_ += val; - else if (sd->state.lr_flag == 2) + else sd->arrow_range += val; break; #if 0 case SP::ADD_SPEED: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->speed -= val; break; #endif #if 0 case SP::SPEED_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) { if (sd->speed_rate > 100 - val) sd->speed_rate = 100 - val; @@ -1621,17 +1567,17 @@ int pc_bonus(dumb_ptr<map_session_data> sd, SP type, int val) break; #endif case SP::SPEED_ADDRATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->speed_add_rate = sd->speed_add_rate * (100 - val) / 100; break; #if 0 case SP::ASPD: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->aspd -= val * 10; break; #endif case SP::ASPD_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) { if (sd->aspd_rate > 100 - val) sd->aspd_rate = 100 - val; @@ -1639,99 +1585,99 @@ int pc_bonus(dumb_ptr<map_session_data> sd, SP type, int val) break; #if 0 case SP::ASPD_ADDRATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->aspd_add_rate = sd->aspd_add_rate * (100 - val) / 100; break; #endif case SP::HP_RECOV_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->hprecov_rate += val; break; #if 0 case SP::SP_RECOV_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->sprecov_rate += val; break; #endif case SP::CRITICAL_DEF: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->critical_def += val; break; #if 0 case SP::DOUBLE_RATE: - if (sd->state.lr_flag == 0 && sd->double_rate < val) + if (!sd->state.lr_flag_is_arrow_2 && sd->double_rate < val) sd->double_rate = val; break; #endif case SP::DOUBLE_ADD_RATE: - if (sd->state.lr_flag == 0) + if (!sd->state.lr_flag_is_arrow_2) sd->double_add_rate += val; break; #if 0 case SP::MATK_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->matk_rate += val; break; #endif #if 0 case SP::ATK_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->atk_rate += val; break; #endif #if 0 case SP::PERFECT_HIT_RATE: - if (sd->state.lr_flag != 2 && sd->perfect_hit < val) + if (!sd->state.lr_flag_is_arrow_2 && sd->perfect_hit < val) sd->perfect_hit = val; break; #endif #if 0 case SP::PERFECT_HIT_ADD_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->perfect_hit_add += val; break; #endif #if 0 case SP::CRITICAL_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->critical_rate += val; break; #endif #if 0 case SP::HIT_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->hit_rate += val; break; #endif #if 0 case SP::FLEE_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->flee_rate += val; break; #endif #if 0 case SP::FLEE2_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->flee2_rate += val; break; #endif case SP::DEF_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->def_rate += val; break; case SP::DEF2_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->def2_rate += val; break; #if 0 case SP::MDEF_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->mdef_rate += val; break; #endif #if 0 case SP::MDEF2_RATE: - if (sd->state.lr_flag != 2) + if (!sd->state.lr_flag_is_arrow_2) sd->mdef2_rate += val; break; #endif @@ -1758,29 +1704,19 @@ int pc_bonus2(dumb_ptr<map_session_data> sd, SP type, int type2, int val) switch (type) { case SP::HP_DRAIN_RATE: - if (!sd->state.lr_flag) + if (!sd->state.lr_flag_is_arrow_2) { sd->hp_drain_rate += type2; sd->hp_drain_per += val; } - else if (sd->state.lr_flag == 1) - { - sd->hp_drain_rate_ += type2; - sd->hp_drain_per_ += val; - } break; #if 0 case SP::SP_DRAIN_RATE: - if (!sd->state.lr_flag) + if (!sd->state.lr_flag_is_arrow_2) { sd->sp_drain_rate += type2; sd->sp_drain_per += val; } - else if (sd->state.lr_flag == 1) - { - sd->sp_drain_rate_ += type2; - sd->sp_drain_per_ += val; - } break; #endif default: @@ -1971,7 +1907,6 @@ int pc_remove_items(dumb_ptr<map_session_data> player, ItemNameId item_id, int c PickupFail pc_additem(dumb_ptr<map_session_data> sd, Item *item_data, int amount) { - struct item_data *data; int w; MAP_LOG_PC(sd, "PICKUP %d %d"_fmt, item_data->nameid, amount); @@ -1981,7 +1916,7 @@ PickupFail pc_additem(dumb_ptr<map_session_data> sd, Item *item_data, if (!item_data->nameid || amount <= 0) return PickupFail::BAD_ITEM; - data = itemdb_search(item_data->nameid); + P<struct item_data> data = itemdb_search(item_data->nameid); if ((w = data->weight * amount) + sd->weight > sd->max_weight) return PickupFail::TOO_HEAVY; @@ -2013,7 +1948,7 @@ PickupFail pc_additem(dumb_ptr<map_session_data> sd, Item *item_data, sd->status.inventory[i].equip = EPOS::ZERO; sd->status.inventory[i].amount = amount; - sd->inventory_data[i] = data; + sd->inventory_data[i] = Some(data); clif_additem(sd, i, amount, PickupFail::OKAY); } else @@ -2037,18 +1972,18 @@ int pc_delitem(dumb_ptr<map_session_data> sd, IOff0 n, int amount, int type) trade_tradecancel(sd); if (!sd->status.inventory[n].nameid || amount <= 0 - || sd->status.inventory[n].amount < amount - || sd->inventory_data[n] == nullptr) + || sd->status.inventory[n].amount < amount) return 1; + P<struct item_data> sdidn = TRY_UNWRAP(sd->inventory_data[n], return 1); sd->status.inventory[n].amount -= amount; - sd->weight -= sd->inventory_data[n]->weight * amount; + sd->weight -= sdidn->weight * amount; if (sd->status.inventory[n].amount <= 0) { if (bool(sd->status.inventory[n].equip)) pc_unequipitem(sd, n, CalcStatus::NOW); sd->status.inventory[n] = Item{}; - sd->inventory_data[n] = nullptr; + sd->inventory_data[n] = None; } if (!(type & 1)) clif_delitem(sd, n, amount); @@ -2097,8 +2032,6 @@ int pc_dropitem(dumb_ptr<map_session_data> sd, IOff0 n, int amount) static int can_pick_item_up_from(dumb_ptr<map_session_data> self, BlockId other_id) { - PartyPair p = party_search(self->status.party_id); - /* From ourselves or from no-one? */ if (!self || self->bl_id == other_id || !other_id) return 1; @@ -2114,9 +2047,10 @@ int can_pick_item_up_from(dumb_ptr<map_session_data> self, BlockId other_id) return 1; /* From a party member? */ + Option<PartyPair> p = party_search(self->status.party_id); if (self->status.party_id && self->status.party_id == other->status.party_id - && p && p->item != 0) + && p.pmd_pget(&PartyMost::item).copy_or(0) != 0) return 1; /* From someone who is far away? */ @@ -2192,16 +2126,13 @@ int pc_takeitem(dumb_ptr<map_session_data> sd, dumb_ptr<flooritem_data> fitem) static int pc_isUseitem(dumb_ptr<map_session_data> sd, IOff0 n) { - struct item_data *item; ItemNameId nameid; nullpo_retz(sd); - item = sd->inventory_data[n]; + P<struct item_data> item = TRY_UNWRAP(sd->inventory_data[n], return 0); nameid = sd->status.inventory[n].nameid; - if (item == nullptr) - return 0; if (itemdb_type(nameid) != ItemType::USE) return 0; @@ -2223,7 +2154,9 @@ int pc_useitem(dumb_ptr<map_session_data> sd, IOff0 n) nullpo_retr(1, sd); - if (n.ok() && sd->inventory_data[n]) + if (!n.ok()) + return 0; + OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[n]) { amount = sd->status.inventory[n].amount; if (!sd->status.inventory[n].nameid @@ -2234,12 +2167,13 @@ int pc_useitem(dumb_ptr<map_session_data> sd, IOff0 n) return 1; } - const ScriptBuffer *script = sd->inventory_data[n]->use_script.get(); + P<const ScriptBuffer> script = borrow(*sdidn->use_script); clif_useitemack(sd, n, amount - 1, 1); pc_delitem(sd, n, 1, 1); run_script(ScriptPointer(script, 0), sd->bl_id, BlockId()); } + OMATCH_END (); return 0; } @@ -2277,18 +2211,19 @@ int pc_setpos(dumb_ptr<map_session_data> sd, mapname_ = mapname_org; - map_local *m = map_mapname2mapid(mapname_); - if (!m) + Option<P<map_local>> m_ = map_mapname2mapid(mapname_); + if (m_.is_none()) { if (sd->mapname_) { IP4Address ip; int port; - if (map_mapname2ipport(mapname_, &ip, &port) == 0) + if (map_mapname2ipport(mapname_, borrow(ip), borrow(port)) == 0) { skill_stop_dancing(sd, 1); clif_clearchar(sd, clrtype); map_delblock(sd); + // *cringe* sd->mapname_ = mapname_; sd->bl_x = x; sd->bl_y = y; @@ -2310,6 +2245,7 @@ int pc_setpos(dumb_ptr<map_session_data> sd, #endif return 1; } + P<map_local> m = TRY_UNWRAP(m_, abort()); if (x < 0 || x >= m->xs || y < 0 || y >= m->ys) x = y = 0; @@ -2353,35 +2289,6 @@ int pc_setpos(dumb_ptr<map_session_data> sd, } /*========================================== - * PCのランダムワープ - *------------------------------------------ - */ -int pc_randomwarp(dumb_ptr<map_session_data> sd, BeingRemoveWhy type) -{ - int x, y, i = 0; - - nullpo_retz(sd); - - map_local *m = sd->bl_m; - - if (sd->bl_m->flag.get(MapFlag::NOTELEPORT)) // テレポート禁止 - return 0; - - do - { - x = random_::in(1, m->xs - 2); - y = random_::in(1, m->ys - 2); - } - while (bool(read_gatp(m, x, y) & MapCell::UNWALKABLE) - && (i++) < 1000); - - if (i < 1000) - pc_setpos(sd, m->name_, x, y, type); - - return 0; -} - -/*========================================== * *------------------------------------------ */ @@ -2504,8 +2411,8 @@ void pc_walk(TimerData *, tick_t tick, BlockId id, unsigned char data) if (sd->status.party_id) { // パーティのHP情報通知検査 - PartyPair p = party_search(sd->status.party_id); - if (p) + Option<PartyPair> p = party_search(sd->status.party_id); + if (p.is_some()) { int p_flag = 0; map_foreachinmovearea(std::bind(party_send_hp_check, ph::_1, sd->status.party_id, &p_flag), @@ -2631,72 +2538,6 @@ void pc_touch_all_relevant_npcs(dumb_ptr<map_session_data> sd) sd->areanpc_id = BlockId(); } -/*========================================== - * - *------------------------------------------ - */ -int pc_movepos(dumb_ptr<map_session_data> sd, int dst_x, int dst_y) -{ - int moveblock; - int dx, dy; - - struct walkpath_data wpd; - - nullpo_retz(sd); - - if (path_search(&wpd, sd->bl_m, sd->bl_x, sd->bl_y, dst_x, dst_y, 0)) - return 1; - - sd->dir = sd->head_dir = map_calc_dir(sd, dst_x, dst_y); - - dx = dst_x - sd->bl_x; - dy = dst_y - sd->bl_y; - - moveblock = (sd->bl_x / BLOCK_SIZE != dst_x / BLOCK_SIZE - || sd->bl_y / BLOCK_SIZE != dst_y / BLOCK_SIZE); - - map_foreachinmovearea(std::bind(clif_pcoutsight, ph::_1, sd), - sd->bl_m, - sd->bl_x - AREA_SIZE, sd->bl_y - AREA_SIZE, - sd->bl_x + AREA_SIZE, sd->bl_y + AREA_SIZE, - dx, dy, - BL::NUL); - - if (moveblock) - map_delblock(sd); - sd->bl_x = dst_x; - sd->bl_y = dst_y; - if (moveblock) - map_addblock(sd); - - map_foreachinmovearea(std::bind(clif_pcinsight, ph::_1, sd), - sd->bl_m, - sd->bl_x - AREA_SIZE, sd->bl_y - AREA_SIZE, - sd->bl_x + AREA_SIZE, sd->bl_y + AREA_SIZE, - -dx, -dy, - BL::NUL); - - if (sd->status.party_id) - { // パーティのHP情報通知検査 - PartyPair p = party_search(sd->status.party_id); - if (p) - { - int flag = 0; - map_foreachinmovearea(std::bind(party_send_hp_check, ph::_1, sd->status.party_id, &flag), - sd->bl_m, - sd->bl_x - AREA_SIZE, sd->bl_y - AREA_SIZE, - sd->bl_x + AREA_SIZE, sd->bl_y + AREA_SIZE, - -dx, -dy, - BL::PC); - if (flag) - sd->party_hp = -1; - } - } - - pc_touch_all_relevant_npcs(sd); - return 0; -} - // // 武器戦闘 // @@ -2764,8 +2605,8 @@ void pc_attack_timer(TimerData *, tick_t tick, BlockId id) if (sd->opt1 != Opt1::ZERO) return; - Option *opt = battle_get_option(bl); - if (opt != nullptr && bool(*opt & Option::REAL_ANY_HIDE)) + Opt0 *opt = battle_get_option(bl); + if (opt != nullptr && bool(*opt & Opt0::REAL_ANY_HIDE)) return; if (!battle_config.skill_delay_attack_enable) @@ -2824,7 +2665,7 @@ void pc_attack_timer(TimerData *, tick_t tick, BlockId id) sd->attackabletime = tick + (sd->aspd * 2); } if (sd->attackabletime <= tick) - sd->attackabletime = tick + static_cast<interval_t>(battle_config.max_aspd) * 2; + sd->attackabletime = tick + battle_config.max_aspd * 2; } } @@ -3017,6 +2858,12 @@ int pc_gainexp_reason(dumb_ptr<map_session_data> sd, int base_exp, int job_exp, } } + // Double Xp Weekends + base_exp = (base_exp * static_cast<double>(battle_config.base_exp_rate) / 100.); + if (base_exp <= 0) + base_exp = 0; + else if (base_exp > 1000000000) + base_exp = 1000000000; sd->status.base_exp += base_exp; // [Fate] Adjust experience points that healers can extract from this character @@ -3024,7 +2871,6 @@ int pc_gainexp_reason(dumb_ptr<map_session_data> sd, int base_exp, int job_exp, { const int max_heal_xp = 20 + (sd->status.base_level * sd->status.base_level); - sd->heal_xp += base_exp; if (sd->heal_xp > max_heal_xp) sd->heal_xp = max_heal_xp; @@ -3047,6 +2893,12 @@ int pc_gainexp_reason(dumb_ptr<map_session_data> sd, int base_exp, int job_exp, } } + // Double Xp Weekends + job_exp = (job_exp * static_cast<double>(battle_config.job_exp_rate) / 100.); + if (job_exp <= 0) + job_exp = 0; + else if (job_exp > 1000000000) + job_exp = 1000000000; sd->status.job_exp += job_exp; if (sd->status.job_exp < 0) sd->status.job_exp = 0; @@ -3247,94 +3099,6 @@ int pc_skillup(dumb_ptr<map_session_data> sd, SkillID skill_num) } /*========================================== - * /resetlvl - *------------------------------------------ - */ -int pc_resetlvl(dumb_ptr<map_session_data> sd, int type) -{ - nullpo_retz(sd); - - for (SkillID i : erange(SkillID(1), MAX_SKILL)) - { - sd->status.skill[i].lv = 0; - } - - if (type == 1) - { - sd->status.skill_point = 0; - sd->status.base_level = 1; - sd->status.job_level = 1; - sd->status.base_exp = 0; - sd->status.job_exp = 0; - sd->status.option = Option::ZERO; - - for (ATTR attr : ATTRs) - sd->status.attrs[attr] = 1; - } - - if (type == 2) - { - sd->status.skill_point = 0; - sd->status.base_level = 1; - sd->status.job_level = 1; - sd->status.base_exp = 0; - sd->status.job_exp = 0; - } - if (type == 3) - { - sd->status.base_level = 1; - sd->status.base_exp = 0; - } - if (type == 4) - { - sd->status.job_level = 1; - sd->status.job_exp = 0; - } - - clif_updatestatus(sd, SP::STATUSPOINT); - clif_updatestatus(sd, SP::STR); - clif_updatestatus(sd, SP::AGI); - clif_updatestatus(sd, SP::VIT); - clif_updatestatus(sd, SP::INT); - clif_updatestatus(sd, SP::DEX); - clif_updatestatus(sd, SP::LUK); - clif_updatestatus(sd, SP::BASELEVEL); - clif_updatestatus(sd, SP::JOBLEVEL); - clif_updatestatus(sd, SP::STATUSPOINT); - clif_updatestatus(sd, SP::NEXTBASEEXP); - clif_updatestatus(sd, SP::NEXTJOBEXP); - clif_updatestatus(sd, SP::SKILLPOINT); - - clif_updatestatus(sd, SP::USTR); // Updates needed stat points - Valaris - clif_updatestatus(sd, SP::UAGI); - clif_updatestatus(sd, SP::UVIT); - clif_updatestatus(sd, SP::UINT); - clif_updatestatus(sd, SP::UDEX); - clif_updatestatus(sd, SP::ULUK); // End Addition - - for (EQUIP i : EQUIPs) - { - // unequip items that can't be equipped by base 1 [Valaris] - IOff0 *idx = &sd->equip_index_maybe[i]; - if ((*idx).ok()) - { - if (!pc_isequip(sd, *idx)) - { - pc_unequipitem(sd, *idx, CalcStatus::LATER); - *idx = IOff0::from(-1); - } - } - } - - clif_skillinfoblock(sd); - pc_calcstatus(sd, 0); - - MAP_LOG_STATS(sd, "STATRESET"_fmt); - - return 0; -} - -/*========================================== * /resetstate *------------------------------------------ */ @@ -3435,9 +3199,12 @@ int pc_damage(dumb_ptr<block_list> src, dumb_ptr<map_session_data> sd, if (sd->status.party_id) { // on-the-fly party hp updates [Valaris] - PartyPair p = party_search(sd->status.party_id); - if (p) + Option<PartyPair> p_ = party_search(sd->status.party_id); + OMATCH_BEGIN_SOME (p, p_) + { clif_party_hp(p, sd); + } + OMATCH_END (); } // end addition [Valaris] return 0; @@ -3728,8 +3495,7 @@ int pc_setparam(dumb_ptr<map_session_data> sd, SP type, int val) } break; case SP::SEX: - // this is a really bad idea - sd->sex = static_cast<SEX>(val); + chrif_char_ask_name(AccountId(), sd->status_key.name, 5, HumanTimeDiff()); break; case SP::WEIGHT: sd->weight = val; @@ -3755,7 +3521,7 @@ int pc_setparam(dumb_ptr<map_session_data> sd, SP type, int val) case SP::INT: case SP::DEX: case SP::LUK: - sd->status.attrs[sp_to_attr(type)] = val; + pc_statusup2(sd, type, (val - sd->status.attrs[sp_to_attr(type)])); break; } clif_updatestatus(sd, type); @@ -3803,9 +3569,12 @@ int pc_heal(dumb_ptr<map_session_data> sd, int hp, int sp) if (sd->status.party_id) { // on-the-fly party hp updates [Valaris] - PartyPair p = party_search(sd->status.party_id); - if (p) + Option<PartyPair> p_ = party_search(sd->status.party_id); + OMATCH_BEGIN_SOME (p, p_) + { clif_party_hp(p, sd); + } + OMATCH_END (); } // end addition [Valaris] return hp + sp; @@ -3933,75 +3702,6 @@ int pc_itemheal_effect(dumb_ptr<map_session_data> sd, int hp, int sp) } /*========================================== - * HP/SP回復 - *------------------------------------------ - */ -int pc_percentheal(dumb_ptr<map_session_data> sd, int hp, int sp) -{ - nullpo_retz(sd); - - if (pc_checkoverhp(sd)) - { - if (hp > 0) - hp = 0; - } - if (pc_checkoversp(sd)) - { - if (sp > 0) - sp = 0; - } - if (hp) - { - if (hp >= 100) - { - sd->status.hp = sd->status.max_hp; - } - else if (hp <= -100) - { - sd->status.hp = 0; - pc_damage(nullptr, sd, 1); - } - else - { - sd->status.hp += sd->status.max_hp * hp / 100; - if (sd->status.hp > sd->status.max_hp) - sd->status.hp = sd->status.max_hp; - if (sd->status.hp <= 0) - { - sd->status.hp = 0; - pc_damage(nullptr, sd, 1); - hp = 0; - } - } - } - if (sp) - { - if (sp >= 100) - { - sd->status.sp = sd->status.max_sp; - } - else if (sp <= -100) - { - sd->status.sp = 0; - } - else - { - sd->status.sp += sd->status.max_sp * sp / 100; - if (sd->status.sp > sd->status.max_sp) - sd->status.sp = sd->status.max_sp; - if (sd->status.sp < 0) - sd->status.sp = 0; - } - } - if (hp) - clif_updatestatus(sd, SP::HP); - if (sp) - clif_updatestatus(sd, SP::SP); - - return 0; -} - -/*========================================== * 見た目変更 *------------------------------------------ */ @@ -4044,21 +3744,6 @@ int pc_changelook(dumb_ptr<map_session_data> sd, LOOK type, int val) } /*========================================== - * 付属品(鷹,ペコ,カート)設定 - *------------------------------------------ - */ -int pc_setoption(dumb_ptr<map_session_data> sd, Option type) -{ - nullpo_retz(sd); - - sd->status.option = type; - clif_changeoption(sd); - pc_calcstatus(sd, 0); - - return 0; -} - -/*========================================== * script用変数の値を読む *------------------------------------------ */ @@ -4088,11 +3773,8 @@ ZString pc_readregstr(dumb_ptr<map_session_data> sd, SIR reg) { nullpo_retr(ZString(), sd); - RString *s = sd->regstrm.search(reg); - if (s) - return *s; - - return ZString(); + Option<P<RString>> s = sd->regstrm.search(reg); + return s.map([](P<RString> s_) -> ZString { return *s_; }).copy_or(""_s); } /*========================================== @@ -4119,14 +3801,37 @@ void pc_setregstr(dumb_ptr<map_session_data> sd, SIR reg, RString str) int pc_readglobalreg(dumb_ptr<map_session_data> sd, VarName reg) { int i; - + int quest_shift = 0; + int quest_mask = 0; nullpo_retz(sd); + QuestId questid; + XString var = reg; + VarName vr; assert (sd->status.global_reg_num < GLOBAL_REG_NUM); + Option<P<struct quest_data>> quest_data_ = questdb_searchname(var); + OMATCH_BEGIN_SOME(quest_data, quest_data_) + { + questid = quest_data->questid; + reg = quest_data->quest_vr; + vr = quest_data->quest_var; + quest_shift = quest_data->quest_shift; + quest_mask = quest_data->quest_mask; + } + OMATCH_END (); for (i = 0; i < sd->status.global_reg_num; i++) { if (sd->status.global_reg[i].str == reg) - return sd->status.global_reg[i].value; + { + if (questid) + { + return ((sd->status.global_reg[i].value & (((1 << quest_mask) - 1) << (quest_shift * quest_mask))) >> (quest_shift * quest_mask)); + } + else + { + return sd->status.global_reg[i].value; + } + } } return 0; @@ -4139,8 +3844,13 @@ int pc_readglobalreg(dumb_ptr<map_session_data> sd, VarName reg) int pc_setglobalreg(dumb_ptr<map_session_data> sd, VarName reg, int val) { int i; - + int quest_shift = 0; + int quest_mask = 0; + int bitval = val; nullpo_retz(sd); + QuestId questid; + XString var = reg; + VarName vr; //PC_DIE_COUNTERがスクリプトなどで変更された時の処理 if (reg == stringish<VarName>("PC_DIE_COUNTER"_s) && sd->die_counter != val) @@ -4148,6 +3858,17 @@ int pc_setglobalreg(dumb_ptr<map_session_data> sd, VarName reg, int val) sd->die_counter = val; pc_calcstatus(sd, 0); } + Option<P<struct quest_data>> quest_data_ = questdb_searchname(var); + OMATCH_BEGIN_SOME(quest_data, quest_data_) + { + questid = quest_data->questid; + reg = quest_data->quest_vr; + vr = quest_data->quest_var; + quest_shift = quest_data->quest_shift; + quest_mask = quest_data->quest_mask; + assert (((1 << quest_mask) - 1) >= val); + } + OMATCH_END (); assert (sd->status.global_reg_num < GLOBAL_REG_NUM); if (val == 0) { @@ -4155,9 +3876,18 @@ int pc_setglobalreg(dumb_ptr<map_session_data> sd, VarName reg, int val) { if (sd->status.global_reg[i].str == reg) { - sd->status.global_reg[i] = - sd->status.global_reg[sd->status.global_reg_num - 1]; - sd->status.global_reg_num--; + if (questid) + { + bitval = ((sd->status.global_reg[i].value & ~(((1 << quest_mask) - 1) << (quest_shift * quest_mask))) | (val << (quest_shift * quest_mask))); + clif_sendquest(sd, questid, val); + } + sd->status.global_reg[i].value = bitval; + if (sd->status.global_reg[i].value == 0) + { + sd->status.global_reg[i] = + sd->status.global_reg[sd->status.global_reg_num - 1]; + sd->status.global_reg_num--; + } break; } } @@ -4167,14 +3897,24 @@ int pc_setglobalreg(dumb_ptr<map_session_data> sd, VarName reg, int val) { if (sd->status.global_reg[i].str == reg) { - sd->status.global_reg[i].value = val; + if (questid) + { + bitval = ((sd->status.global_reg[i].value & ~(((1 << quest_mask) - 1) << (quest_shift * quest_mask))) | (val << (quest_shift * quest_mask))); + clif_sendquest(sd, questid, val); + } + sd->status.global_reg[i].value = bitval; return 0; } } if (sd->status.global_reg_num < GLOBAL_REG_NUM) { sd->status.global_reg[i].str = reg; - sd->status.global_reg[i].value = val; + if (questid) + { + bitval = ((sd->status.global_reg[i].value & ~(((1 << quest_mask) - 1) << (quest_shift * quest_mask))) | (val << (quest_shift * quest_mask))); + clif_sendquest(sd, questid, val); + } + sd->status.global_reg[i].value = bitval; sd->status.global_reg_num++; return 0; } @@ -4400,7 +4140,6 @@ int pc_signal_advanced_equipment_change(dumb_ptr<map_session_data> sd, IOff0 n) int pc_equipitem(dumb_ptr<map_session_data> sd, IOff0 n, EPOS) { ItemNameId nameid; - struct item_data *id; //ソス]ソスソスソスソスソス{ソスqソスフ場合ソスフ鯉ソスソスフ職ソスニゑソスソスZソスoソスソスソスソス nullpo_retz(sd); @@ -4412,9 +4151,8 @@ int pc_equipitem(dumb_ptr<map_session_data> sd, IOff0 n, EPOS) } nameid = sd->status.inventory[n].nameid; - id = sd->inventory_data[n]; - if (!id) // can't actually happen - the only caller checks this. - return 0; + // can't actually happen - the only caller checks this. + P<struct item_data> id = TRY_UNWRAP(sd->inventory_data[n], return 0); EPOS pos = pc_equippoint(sd, n); if (battle_config.battle_log) @@ -4475,17 +4213,18 @@ int pc_equipitem(dumb_ptr<map_session_data> sd, IOff0 n, EPOS) ItemNameId view_i; ItemLook view_l = ItemLook::NONE; // TODO: This is ugly. - if (sd->inventory_data[n]) + OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[n]) { - bool look_not_weapon = sd->inventory_data[n]->look == ItemLook::NONE; + bool look_not_weapon = sdidn->look == ItemLook::NONE; bool equip_is_weapon = bool(sd->status.inventory[n].equip & EPOS::WEAPON); assert (look_not_weapon != equip_is_weapon); if (look_not_weapon) - view_i = sd->inventory_data[n]->nameid; + view_i = sdidn->nameid; else - view_l = sd->inventory_data[n]->look; + view_l = sdidn->look; } + OMATCH_END (); if (bool(sd->status.inventory[n].equip & EPOS::WEAPON)) { @@ -4495,25 +4234,27 @@ int pc_equipitem(dumb_ptr<map_session_data> sd, IOff0 n, EPOS) } if (bool(sd->status.inventory[n].equip & EPOS::SHIELD)) { - if (sd->inventory_data[n]) + OMATCH_BEGIN (sd->inventory_data[n]) { - if (sd->inventory_data[n]->type == ItemType::WEAPON) + OMATCH_CASE_SOME (sdidn) { - sd->status.shield = ItemNameId(); - if (sd->status.inventory[n].equip == EPOS::SHIELD) - sd->weapontype2 = view_l; + if (sdidn->type == ItemType::WEAPON) + { + sd->status.shield = ItemNameId(); + if (sd->status.inventory[n].equip == EPOS::SHIELD) + assert(0 && "unreachable - offhand weapons are not supported"); + } + else if (sdidn->type == ItemType::ARMOR) + { + sd->status.shield = view_i; + } } - else if (sd->inventory_data[n]->type == ItemType::ARMOR) + OMATCH_CASE_NONE () { - sd->status.shield = view_i; - sd->weapontype2 = ItemLook::NONE; + sd->status.shield = ItemNameId(); } } - else - { - sd->status.shield = ItemNameId(); - sd->weapontype2 = ItemLook::NONE; - } + OMATCH_END (); pc_calcweapontype(sd); clif_changelook(sd, LOOK::SHIELD, unwrap<ItemNameId>(sd->status.shield)); } @@ -4563,14 +4304,14 @@ int pc_unequipitem(dumb_ptr<map_session_data> sd, IOff0 n, CalcStatus type) if (bool(sd->status.inventory[n].equip & EPOS::WEAPON)) { sd->weapontype1 = ItemLook::NONE; - sd->status.weapon = sd->weapontype2; + // when reading the diff, think twice about this + sd->status.weapon = ItemLook::NONE; pc_calcweapontype(sd); pc_set_weapon_look(sd); } if (bool(sd->status.inventory[n].equip & EPOS::SHIELD)) { sd->status.shield = ItemNameId(); - sd->weapontype2 = ItemLook::NONE; pc_calcweapontype(sd); clif_changelook(sd, LOOK::SHIELD, unwrap<ItemNameId>(sd->status.shield)); } @@ -4728,8 +4469,7 @@ void pc_calc_pvprank_sub(dumb_ptr<block_list> bl, dumb_ptr<map_session_data> sd2 int pc_calc_pvprank(dumb_ptr<map_session_data> sd) { nullpo_retz(sd); - map_local *m = sd->bl_m; - nullpo_retz(m); + P<map_local> m = sd->bl_m; if (!(m->flag.get(MapFlag::PVP))) return 0; @@ -4816,15 +4556,12 @@ int pc_divorce(dumb_ptr<map_session_data> sd) } p_sd->status.partner_id = CharId(); sd->status.partner_id = CharId(); - - if (sd->npc_flags.divorce) - { - sd->npc_flags.divorce = 0; - map_scriptcont(sd, sd->npc_id); - } } else + { + sd->status.partner_id = CharId(); chrif_send_divorce(sd->status_key.char_id); + } return 0; } @@ -4857,10 +4594,6 @@ dumb_ptr<map_session_data> pc_get_partner(dumb_ptr<map_session_data> sd) * SP回復量計算 *------------------------------------------ */ -static -tick_t natural_heal_tick, natural_heal_prev_tick; -static -interval_t natural_heal_diff_tick; static interval_t pc_spheal(dumb_ptr<map_session_data> sd) @@ -4918,12 +4651,12 @@ int pc_natural_heal_hp(dumb_ptr<map_session_data> sd) return 0; } - if (sd->hp_sub >= static_cast<interval_t>(battle_config.natural_healhp_interval)) + if (sd->hp_sub >= battle_config.natural_healhp_interval) { bonus = sd->nhealhp; - while (sd->hp_sub >= static_cast<interval_t>(battle_config.natural_healhp_interval)) + while (sd->hp_sub >= battle_config.natural_healhp_interval) { - sd->hp_sub -= static_cast<interval_t>(battle_config.natural_healhp_interval); + sd->hp_sub -= battle_config.natural_healhp_interval; if (sd->status.hp + bonus <= sd->status.max_hp) sd->status.hp += bonus; else @@ -4936,28 +4669,7 @@ int pc_natural_heal_hp(dumb_ptr<map_session_data> sd) if (bhp != sd->status.hp) clif_updatestatus(sd, SP::HP); - if (sd->nshealhp > 0) - { - if (sd->inchealhptick >= static_cast<interval_t>(battle_config.natural_heal_skill_interval) - && sd->status.hp < sd->status.max_hp) - { - bonus = sd->nshealhp; - while (sd->inchealhptick >= static_cast<interval_t>(battle_config.natural_heal_skill_interval)) - { - sd->inchealhptick -= static_cast<interval_t>(battle_config.natural_heal_skill_interval); - if (sd->status.hp + bonus <= sd->status.max_hp) - sd->status.hp += bonus; - else - { - bonus = sd->status.max_hp - sd->status.hp; - sd->status.hp = sd->status.max_hp; - sd->hp_sub = sd->inchealhptick = interval_t::zero(); - } - } - } - } - else - sd->inchealhptick = interval_t::zero(); + sd->inchealhptick = interval_t::zero(); return 0; } @@ -4985,12 +4697,12 @@ int pc_natural_heal_sp(dumb_ptr<map_session_data> sd) else sd->inchealsptick = interval_t::zero(); - if (sd->sp_sub >= static_cast<interval_t>(battle_config.natural_healsp_interval)) + if (sd->sp_sub >= battle_config.natural_healsp_interval) { bonus = sd->nhealsp; - while (sd->sp_sub >= static_cast<interval_t>(battle_config.natural_healsp_interval)) + while (sd->sp_sub >= battle_config.natural_healsp_interval) { - sd->sp_sub -= static_cast<interval_t>(battle_config.natural_healsp_interval); + sd->sp_sub -= battle_config.natural_healsp_interval; if (sd->status.sp + bonus <= sd->status.max_sp) sd->status.sp += bonus; else @@ -5004,28 +4716,7 @@ int pc_natural_heal_sp(dumb_ptr<map_session_data> sd) if (bsp != sd->status.sp) clif_updatestatus(sd, SP::SP); - if (sd->nshealsp > 0) - { - if (sd->inchealsptick >= static_cast<interval_t>(battle_config.natural_heal_skill_interval) - && sd->status.sp < sd->status.max_sp) - { - bonus = sd->nshealsp; - while (sd->inchealsptick >= static_cast<interval_t>(battle_config.natural_heal_skill_interval)) - { - sd->inchealsptick -= static_cast<interval_t>(battle_config.natural_heal_skill_interval); - if (sd->status.sp + bonus <= sd->status.max_sp) - sd->status.sp += bonus; - else - { - bonus = sd->status.max_sp - sd->status.sp; - sd->status.sp = sd->status.max_sp; - sd->sp_sub = sd->inchealsptick = interval_t::zero(); - } - } - } - } - else - sd->inchealsptick = interval_t::zero(); + sd->inchealsptick = interval_t::zero(); return 0; } @@ -5147,8 +4838,6 @@ void pc_setsavepoint(dumb_ptr<map_session_data> sd, MapName mapname, int x, int *------------------------------------------ */ static -int last_save_fd, save_flag; -static void pc_autosave_sub(dumb_ptr<map_session_data> sd) { nullpo_retv(sd); @@ -5175,7 +4864,7 @@ void pc_autosave(TimerData *, tick_t) if (save_flag == 0) last_save_fd = -1; - interval_t interval = autosave_time / (clif_countusers() + 1); + interval_t interval = map_conf.autosave_time / (clif_countusers() + 1); if (interval <= interval_t::zero()) interval = 1_ms; Timer(gettick() + interval, @@ -5228,7 +4917,7 @@ void do_init_pc(void) pc_natural_heal, NATURAL_HEAL_INTERVAL ).detach(); - Timer(gettick() + autosave_time, + Timer(gettick() + map_conf.autosave_time, pc_autosave ).detach(); } @@ -5240,15 +4929,15 @@ void pc_cleanup(dumb_ptr<map_session_data> sd) void pc_invisibility(dumb_ptr<map_session_data> sd, int enabled) { - if (enabled && !bool(sd->status.option & Option::INVISIBILITY)) + if (enabled && !bool(sd->status.option & Opt0::INVISIBILITY)) { clif_clearchar(sd, BeingRemoveWhy::WARPED); - sd->status.option |= Option::INVISIBILITY; + sd->status.option |= Opt0::INVISIBILITY; clif_status_change(sd, StatusChange::CLIF_OPTION_SC_INVISIBILITY, 1); } else if (!enabled) { - sd->status.option &= ~Option::INVISIBILITY; + sd->status.option &= ~Opt0::INVISIBILITY; clif_status_change(sd, StatusChange::CLIF_OPTION_SC_INVISIBILITY, 0); pc_setpos(sd, sd->bl_m->name_, sd->bl_x, sd->bl_y, BeingRemoveWhy::WARPED); } @@ -5280,4 +4969,5 @@ int pc_logout(dumb_ptr<map_session_data> sd) // [fate] Player logs out MAP_LOG_STATS(sd, "LOGOUT"_fmt); return 0; } +} // namespace map } // namespace tmwa diff --git a/src/map/pc.hpp b/src/map/pc.hpp index 3187cd9..d100938 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -20,24 +20,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include "fwd.hpp" - #include "pc.t.hpp" -#include "../strings/fwd.hpp" +#include "fwd.hpp" #include "../generic/dumb_ptr.hpp" -#include "../mmo/utils.hpp" - -#include "../proto2/fwd.hpp" - -#include "clif.t.hpp" +#include "../mmo/clif.t.hpp" #include "map.hpp" +#include "quest.hpp" namespace tmwa { +namespace map +{ inline void pc_setsit(dumb_ptr<map_session_data> sd) { @@ -62,7 +59,7 @@ void pc_setdir(dumb_ptr<map_session_data> sd, DIR b) inline bool pc_isinvisible(dumb_ptr<map_session_data> sd) { - return bool(sd->status.option & Option::HIDE); + return bool(sd->status.option & Opt0::HIDE); } inline bool pc_is90overweight(dumb_ptr<map_session_data> sd) @@ -83,7 +80,7 @@ int pc_counttargeted(dumb_ptr<map_session_data> sd, dumb_ptr<block_list> src, int pc_setrestartvalue(dumb_ptr<map_session_data> sd, int type); void pc_makesavestatus(dumb_ptr<map_session_data>); int pc_setnewpc(dumb_ptr<map_session_data>, AccountId, CharId, int, uint32_t /*tick_t*/, SEX); -int pc_authok(AccountId, int, TimeT, short tmw_version, const CharKey *, const CharData *); +int pc_authok(AccountId, int, short tmw_version, const CharKey *, const CharData *); int pc_authfail(AccountId accid); EPOS pc_equippoint(dumb_ptr<map_session_data> sd, IOff0 n); @@ -93,10 +90,8 @@ IOff0 pc_checkequip(dumb_ptr<map_session_data> sd, EPOS pos); int pc_walktoxy(dumb_ptr<map_session_data>, int, int); int pc_stop_walking(dumb_ptr<map_session_data>, int); -int pc_movepos(dumb_ptr<map_session_data>, int, int); int pc_setpos(dumb_ptr<map_session_data>, MapName, int, int, BeingRemoveWhy); void pc_setsavepoint(dumb_ptr<map_session_data>, MapName, int, int); -int pc_randomwarp(dumb_ptr<map_session_data> sd, BeingRemoveWhy type); ADDITEM pc_checkadditem(dumb_ptr<map_session_data>, ItemNameId, int); int pc_inventoryblank(dumb_ptr<map_session_data>); @@ -133,7 +128,6 @@ int pc_need_status_point(dumb_ptr<map_session_data>, SP); int pc_statusup(dumb_ptr<map_session_data>, SP); int pc_statusup2(dumb_ptr<map_session_data>, SP, int); int pc_skillup(dumb_ptr<map_session_data>, SkillID); -int pc_resetlvl(dumb_ptr<map_session_data>, int type); int pc_resetstate(dumb_ptr<map_session_data>); int pc_resetskill(dumb_ptr<map_session_data>); int pc_equipitem(dumb_ptr<map_session_data>, IOff0, EPOS); @@ -144,8 +138,6 @@ int pc_useitem(dumb_ptr<map_session_data>, IOff0); int pc_damage(dumb_ptr<block_list>, dumb_ptr<map_session_data>, int); int pc_heal(dumb_ptr<map_session_data>, int, int); int pc_itemheal(dumb_ptr<map_session_data> sd, int hp, int sp); -int pc_percentheal(dumb_ptr<map_session_data> sd, int, int); -int pc_setoption(dumb_ptr<map_session_data>, Option); int pc_changelook(dumb_ptr<map_session_data>, LOOK, int); int pc_readparam(dumb_ptr<map_session_data>, SP); @@ -154,6 +146,8 @@ int pc_readreg(dumb_ptr<map_session_data>, SIR); void pc_setreg(dumb_ptr<map_session_data>, SIR, int); ZString pc_readregstr(dumb_ptr<map_session_data> sd, SIR reg); void pc_setregstr(dumb_ptr<map_session_data> sd, SIR reg, RString str); +void update_quest(dumb_ptr<map_session_data> sd, VarName quest_var, int value); +void update_allquest(dumb_ptr<map_session_data> sd); int pc_readglobalreg(dumb_ptr<map_session_data>, VarName ); int pc_setglobalreg(dumb_ptr<map_session_data>, VarName , int); int pc_readaccountreg(dumb_ptr<map_session_data>, VarName ); @@ -177,6 +171,8 @@ void pc_setstand(dumb_ptr<map_session_data> sd); void pc_cleanup(dumb_ptr<map_session_data> sd); // [Fate] Clean up after a logged-out PC int pc_read_gm_account(Session *, const std::vector<Packet_Repeat<0x2b15>>&); +int pc_setpvptimer(dumb_ptr<map_session_data> sd, interval_t); +int pc_delpvptimer(dumb_ptr<map_session_data> sd); int pc_setinvincibletimer(dumb_ptr<map_session_data> sd, interval_t); int pc_delinvincibletimer(dumb_ptr<map_session_data> sd); int pc_logout(dumb_ptr<map_session_data> sd); // [fate] Player logs out @@ -184,4 +180,5 @@ int pc_logout(dumb_ptr<map_session_data> sd); // [fate] Player logs out void pc_show_motd(dumb_ptr<map_session_data> sd); void do_init_pc(void); +} // namespace map } // namespace tmwa diff --git a/src/map/pc.t.hpp b/src/map/pc.t.hpp index 427e8c3..c9235fa 100644 --- a/src/map/pc.t.hpp +++ b/src/map/pc.t.hpp @@ -28,6 +28,8 @@ namespace tmwa { +namespace map +{ enum class PC_GAINEXP_REASON { KILLING = 0, @@ -54,4 +56,5 @@ enum class CalcStatus NOW, LATER, }; +} // namespace map } // namespace tmwa diff --git a/src/map/quest.cpp b/src/map/quest.cpp new file mode 100644 index 0000000..dfe19ff --- /dev/null +++ b/src/map/quest.cpp @@ -0,0 +1,135 @@ +#include "quest.hpp" +// quest.cpp - Quest Log. +// +// Copyright © 2015 Ed Pasek <pasekei@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include <algorithm> + +#include "../strings/astring.hpp" +#include "../strings/zstring.hpp" +#include "../strings/xstring.hpp" + +#include "../generic/db.hpp" + +#include "../io/cxxstdio.hpp" +#include "../io/extract.hpp" +#include "../io/line.hpp" + +#include "../mmo/config_parse.hpp" +#include "../mmo/extract_enums.hpp" + +#include "../ast/quest.hpp" + +#include "../poison.hpp" +#include "globals.hpp" +#include "script-parse.hpp" + +namespace tmwa +{ +namespace map +{ +// Function declarations + +static +void questdb_searchname_sub(Borrowed<struct quest_data> quest, VarName str, Borrowed<Option<Borrowed<struct quest_data>>> dst) +{ + if (quest->quest_var == str) + *dst = Some(quest); +} + +Option<Borrowed<struct quest_data>> questdb_searchname(XString str_) +{ + VarName str = stringish<VarName>(str_); + if (XString(str) != str_) + return None; + Option<P<struct quest_data>> quest = None; + for (auto& pair : quest_db) + questdb_searchname_sub(borrow(pair.second), str, borrow(quest)); + return quest; +} + +Borrowed<struct quest_data> questdb_search(QuestId questid) +{ + Option<P<struct quest_data>> id_ = quest_db.search(questid); + OMATCH_BEGIN_SOME (id, id_) + { + return id; + } + OMATCH_END (); + + P<struct quest_data> id = quest_db.init(questid); + + id->questid = questid; + + return id; +} + +Option<Borrowed<struct quest_data>> questdb_exists(QuestId questid) +{ + return quest_db.search(questid); +} + +bool quest_readdb(ZString filename) +{ + io::LineCharReader in(filename); + + if (!in.is_open()) + { + PRINTF("can't read %s\n"_fmt, filename); + return false; + } + + int ln = 0; + + while (true) + { + auto res = TRY_UNWRAP(ast::quest::parse_quest(in), + { + PRINTF("read %s done (count=%d)\n"_fmt, filename, ln); + return true; + }); + if (res.get_failure()) + PRINTF("%s\n"_fmt, res.get_failure()); + ast::quest::QuestOrComment ioc = TRY_UNWRAP(std::move(res.get_success()), return false); + + MATCH_BEGIN (ioc) + { + MATCH_CASE (const ast::quest::Comment&, c) + { + (void)c; + } + MATCH_CASE (const ast::quest::Quest&, quest) + { + ln++; + + quest_data qdv {}; + qdv.questid = quest.questid.data; + qdv.quest_var = quest.quest_var.data; + qdv.quest_vr = quest.quest_vr.data; + qdv.quest_shift = quest.quest_shift.data; + qdv.quest_mask = quest.quest_mask.data; + + Borrowed<struct quest_data> id = questdb_search(qdv.questid); + *id = std::move(qdv); + } + } + MATCH_END (); + } +} +} // namespace map +} // namespace tmwa diff --git a/src/map/quest.hpp b/src/map/quest.hpp new file mode 100644 index 0000000..65e6f4e --- /dev/null +++ b/src/map/quest.hpp @@ -0,0 +1,51 @@ +#pragma once +// quest.hpp - Quest Log. +// +// Copyright © 2015 Ed Pasek <pasekei@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include "../mmo/ids.hpp" +#include "../high/mmo.hpp" + +#include "map.hpp" +#include "script-buffer.hpp" + +namespace tmwa +{ +namespace map +{ +constexpr int MAX_QUEST_DB (60355+1); +struct quest_data +{ + QuestId questid; + VarName quest_var; + VarName quest_vr; + int quest_shift; + int quest_mask; +}; +inline +Option<Borrowed<struct quest_data>> questdb_searchname(VarName) = delete; +Option<Borrowed<struct quest_data>> questdb_searchname(XString quest_var); +Borrowed<struct quest_data> questdb_search(QuestId questid); +Option<Borrowed<struct quest_data>> questdb_exists(QuestId questid); + +// get quest var by quest name / mask / bit +bool quest_readdb(ZString filename); +} // namespace map +} // namespace tmwa diff --git a/src/map/script-buffer.hpp b/src/map/script-buffer.hpp new file mode 100644 index 0000000..e720044 --- /dev/null +++ b/src/map/script-buffer.hpp @@ -0,0 +1,45 @@ +#pragma once +// script-buffer.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include <memory> + + +namespace tmwa +{ +namespace map +{ +class ScriptBuffer; +} // namespace map +} // namespace tmwa + +namespace std +{ +template<> +struct default_delete<const tmwa::map::ScriptBuffer> +{ + default_delete() {} + default_delete(default_delete<tmwa::map::ScriptBuffer>) {} + void operator()(const tmwa::map::ScriptBuffer *sd); +}; +} // namespace std diff --git a/src/map/script-call-internal.hpp b/src/map/script-call-internal.hpp new file mode 100644 index 0000000..b9b3a9f --- /dev/null +++ b/src/map/script-call-internal.hpp @@ -0,0 +1,100 @@ +#pragma once +// script-call-internal.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "script-call.hpp" +#include "fwd.hpp" + +#include "../mmo/ids.hpp" + +#include "script-persist.hpp" + + +namespace tmwa +{ +namespace map +{ +enum class VariableCode : uint8_t +{ + PARAM, + VARIABLE, +}; + +struct script_stack +{ + std::vector<struct script_data> stack_datav; +}; + +enum class ScriptEndState; +// future improvements coming! +class ScriptState +{ +public: + struct script_stack *stack; + int start, end; + ScriptEndState state; + BlockId rid, oid; + ScriptPointer scriptp, new_scriptp; + int defsp, new_defsp, freeloop; +}; + +void run_func(ScriptState *st); + +enum class ScriptEndState +{ + ZERO, + STOP, + END, + RERUNLINE, + GOTO, + RETFUNC, +}; + +dumb_ptr<map_session_data> script_rid2sd(ScriptState *st); +void get_val(dumb_ptr<map_session_data> sd, struct script_data *data); +__attribute__((deprecated)) +void get_val(ScriptState *st, struct script_data *data); +struct script_data get_val2(ScriptState *st, SIR reg); +void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, struct script_data vd); +void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, int id); +void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, RString zd); +__attribute__((warn_unused_result)) +RString conv_str(ScriptState *st, struct script_data *data); +__attribute__((warn_unused_result)) +int conv_num(ScriptState *st, struct script_data *data); +__attribute__((warn_unused_result)) +Borrowed<const ScriptBuffer> conv_script(ScriptState *st, struct script_data *data); + +template<class T> +void push_int(struct script_stack *stack, int val); +template<class T> +void push_reg(struct script_stack *stack, SIR reg); +template<class T> +void push_script(struct script_stack *stack, Borrowed<const ScriptBuffer> code); +template<class T> +void push_str(struct script_stack *stack, RString str); + +void push_copy(struct script_stack *stack, int pos_); +void pop_stack(struct script_stack *stack, int start, int end); +} // namespace map +} // namespace tmwa + +#include "script-call-internal.tcc" diff --git a/src/map/script-call-internal.tcc b/src/map/script-call-internal.tcc new file mode 100644 index 0000000..e10b69c --- /dev/null +++ b/src/map/script-call-internal.tcc @@ -0,0 +1,79 @@ +// script-call-internal.tcc - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "script-persist.hpp" + + +namespace tmwa +{ +namespace map +{ +template<class D> +bool first_type_is_any() +{ + return false; +} + +template<class D, class F, class... R> +constexpr +bool first_type_is_any() +{ + return std::is_same<D, F>::value || first_type_is_any<D, R...>(); +} + + +template<class T> +void push_int(struct script_stack *stack, int val) +{ + static_assert(first_type_is_any<T, ScriptDataPos, ScriptDataInt, ScriptDataArg, ScriptDataFuncRef>(), "not int type"); + + script_data nsd = T{.numi= val}; + stack->stack_datav.push_back(nsd); +} + +template<class T> +void push_reg(struct script_stack *stack, SIR reg) +{ + static_assert(first_type_is_any<T, ScriptDataParam, ScriptDataVariable>(), "not reg type"); + + script_data nsd = T{.reg= reg}; + stack->stack_datav.push_back(nsd); +} + +template<class T> +void push_script(struct script_stack *stack, Borrowed<const ScriptBuffer> code) +{ + static_assert(first_type_is_any<T, ScriptDataRetInfo>(), "not scriptbuf type"); + + script_data nsd = T{.script= code}; + stack->stack_datav.push_back(nsd); +} + +template<class T> +void push_str(struct script_stack *stack, RString str) +{ + static_assert(first_type_is_any<T, ScriptDataStr>(), "not str type"); + + script_data nsd = T{.str= str}; + stack->stack_datav.push_back(nsd); +} +} // namespace map +} // namespace tmwa diff --git a/src/map/script-call.cpp b/src/map/script-call.cpp new file mode 100644 index 0000000..c3c6aa1 --- /dev/null +++ b/src/map/script-call.cpp @@ -0,0 +1,926 @@ +#include "script-call-internal.hpp" +// script-call.cpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011 Chuck Miller +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// Copyright © 2013 wushin +// +// 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 <http://www.gnu.org/licenses/>. + +#include "../generic/intern-pool.hpp" + +#include "../io/cxxstdio.hpp" + +#include "../mmo/cxxstdio_enums.hpp" + +#include "battle.hpp" +#include "battle_conf.hpp" +#include "globals.hpp" +#include "map.hpp" +#include "npc.hpp" +#include "pc.hpp" +#include "script-fun.hpp" +#include "script-parse-internal.hpp" +#include "script-persist.hpp" +#include "script-startup-internal.hpp" + +#include "../poison.hpp" + + +namespace tmwa +{ +namespace map +{ +constexpr bool DEBUG_RUN = false; + +static +struct ScriptConfigRun +{ + static const + int check_cmdcount = 8192; + static const + int check_gotocount = 512; +} script_config; + + +/*========================================== + * ridからsdへの解決 + *------------------------------------------ + */ +dumb_ptr<map_session_data> script_rid2sd(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = map_id2sd(st->rid); + if (!sd) + { + PRINTF("script_rid2sd: fatal error ! player not attached!\n"_fmt); + } + return sd; +} + +/*========================================== + * 変数の読み取り + *------------------------------------------ + */ +void get_val(dumb_ptr<map_session_data> sd, struct script_data *data) +{ + MATCH_BEGIN (*data) + { + MATCH_CASE (const ScriptDataParam&, u) + { + if (sd == nullptr) + PRINTF("get_val error param SP::%d\n"_fmt, u.reg.sp()); + int numi = 0; + if (sd) + numi = pc_readparam(sd, u.reg.sp()); + *data = ScriptDataInt{numi}; + } + MATCH_CASE (const ScriptDataVariable&, u) + { + ZString name_ = variable_names.outtern(u.reg.base()); + VarName name = stringish<VarName>(name_); + char prefix = name.front(); + char postfix = name.back(); + + if (prefix != '$') + { + if (sd == nullptr) + PRINTF("get_val error name?:%s\n"_fmt, name); + } + if (postfix == '$') + { + RString str; + if (prefix == '@') + { + if (sd) + str = pc_readregstr(sd, u.reg); + } + else if (prefix == '$') + { + Option<P<RString>> s_ = mapregstr_db.search(u.reg); + OMATCH_BEGIN_SOME (s, s_) + { + str = *s; + } + OMATCH_END (); + } + else + { + PRINTF("script: get_val: illegal scope string variable.\n"_fmt); + str = "!!ERROR!!"_s; + } + *data = ScriptDataStr{str}; + } + else + { + int numi = 0; + if (prefix == '@') + { + if (sd) + numi = pc_readreg(sd, u.reg); + } + else if (prefix == '$') + { + numi = mapreg_db.get(u.reg); + } + else if (prefix == '#') + { + if (name[1] == '#') + { + if (sd) + numi = pc_readaccountreg2(sd, name); + } + else + { + if (sd) + numi = pc_readaccountreg(sd, name); + } + } + else + { + if (sd) + numi = pc_readglobalreg(sd, name); + } + *data = ScriptDataInt{numi}; + } + } + } + MATCH_END (); +} + +void get_val(ScriptState *st, struct script_data *data) +{ + dumb_ptr<map_session_data> sd = st->rid ? map_id2sd(st->rid) : nullptr; + get_val(sd, data); +} + +/*========================================== + * 変数の読み取り2 + *------------------------------------------ + */ +struct script_data get_val2(ScriptState *st, SIR reg) +{ + struct script_data dat = ScriptDataVariable{reg}; + get_val(st, &dat); + return dat; +} + +/*========================================== + * 変数設定用 + *------------------------------------------ + */ +void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, struct script_data vd) +{ + if (type == VariableCode::PARAM) + { + int val = vd.get_if<ScriptDataInt>()->numi; + pc_setparam(sd, reg.sp(), val); + return; + } + assert (type == VariableCode::VARIABLE); + + ZString name_ = variable_names.outtern(reg.base()); + VarName name = stringish<VarName>(name_); + char prefix = name.front(); + char postfix = name.back(); + + if (postfix == '$') + { + RString str = vd.get_if<ScriptDataStr>()->str; + if (prefix == '@') + { + pc_setregstr(sd, reg, str); + } + else if (prefix == '$') + { + mapreg_setregstr(reg, str); + } + else + { + PRINTF("script: set_reg: illegal scope string variable !"_fmt); + } + } + else + { + int val = vd.get_if<ScriptDataInt>()->numi; + if (prefix == '@') + { + pc_setreg(sd, reg, val); + } + else if (prefix == '$') + { + mapreg_setreg(reg, val); + } + else if (prefix == '#') + { + if (name[1] == '#') + pc_setaccountreg2(sd, name, val); + else + pc_setaccountreg(sd, name, val); + } + else + { + pc_setglobalreg(sd, name, val); + } + } +} + +void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, int id) +{ + struct script_data vd = ScriptDataInt{id}; + set_reg(sd, type, reg, vd); +} + +void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, RString zd) +{ + struct script_data vd = ScriptDataStr{zd}; + set_reg(sd, type, reg, vd); +} + +/*========================================== + * 文字列への変換 + *------------------------------------------ + */ +RString conv_str(ScriptState *st, struct script_data *data) +{ + get_val(st, data); + assert (!data->is<ScriptDataRetInfo>()); + if (auto *u = data->get_if<ScriptDataInt>()) + { + AString buf = STRPRINTF("%d"_fmt, u->numi); + *data = ScriptDataStr{buf}; + } + return data->get_if<ScriptDataStr>()->str; +} + +/*========================================== + * 数値へ変換 + *------------------------------------------ + */ +int conv_num(ScriptState *st, struct script_data *data) +{ + int rv = 0; + get_val(st, data); + assert (!data->is<ScriptDataRetInfo>()); + MATCH_BEGIN (*data) + { + MATCH_DEFAULT () + { + abort(); + } + MATCH_CASE (const ScriptDataStr&, u) + { + RString p = u.str; + rv = atoi(p.c_str()); + } + MATCH_CASE (const ScriptDataInt&, u) + { + return u.numi; + } + MATCH_CASE (const ScriptDataPos&, u) + { + return u.numi; + } + } + MATCH_END () + *data = ScriptDataInt{rv}; + return rv; +} + +Borrowed<const ScriptBuffer> conv_script(ScriptState *st, struct script_data *data) +{ + get_val(st, data); + return data->get_if<ScriptDataRetInfo>()->script; +} + +void push_copy(struct script_stack *stack, int pos_) +{ + script_data csd = stack->stack_datav[pos_]; + stack->stack_datav.push_back(csd); +} + +void pop_stack(struct script_stack *stack, int start, int end) +{ + auto it = stack->stack_datav.begin(); + stack->stack_datav.erase(it + start, it + end); +} + +// +// 実行部main +// +/*========================================== + * コマンドの読み取り + *------------------------------------------ + */ +static +ByteCode get_com(ScriptPointer *script) +{ + if (static_cast<uint8_t>(script->peek()) >= 0x80) + { + // synthetic! Does not advance pos yet. + return ByteCode::INT; + } + return script->pop(); +} + +/*========================================== + * 数値の所得 + *------------------------------------------ + */ +static +int get_num(ScriptPointer *scr) +{ + int i = 0; + int j = 0; + uint8_t val; + do + { + val = static_cast<uint8_t>(scr->pop()); + i += (val & 0x7f) << j; + j += 6; + } + while (val >= 0xc0); + return i; +} + +/*========================================== + * スタックから値を取り出す + *------------------------------------------ + */ +static +int pop_val(ScriptState *st) +{ + if (st->stack->stack_datav.empty()) + return 0; + script_data& back = st->stack->stack_datav.back(); + get_val(st, &back); + int rv = 0; + if (auto *u = back.get_if<ScriptDataInt>()) + rv = u->numi; + st->stack->stack_datav.pop_back(); + return rv; +} + +static +bool isstr(struct script_data& c) +{ + return c.is<ScriptDataStr>(); +} + +/*========================================== + * 加算演算子 + *------------------------------------------ + */ +static +void op_add(ScriptState *st) +{ + get_val(st, &st->stack->stack_datav.back()); + script_data back = st->stack->stack_datav.back(); + st->stack->stack_datav.pop_back(); + + script_data& back1 = st->stack->stack_datav.back(); + get_val(st, &back1); + + if (!(isstr(back) || isstr(back1))) + { + back1.get_if<ScriptDataInt>()->numi += back.get_if<ScriptDataInt>()->numi; + } + else + { + RString sb = conv_str(st, &back); + RString sb1 = conv_str(st, &back1); + MString buf; + buf += sb1; + buf += sb; + back1 = ScriptDataStr{.str= AString(buf)}; + } +} + +/*========================================== + * 二項演算子(文字列) + *------------------------------------------ + */ +static +void op_2str(ScriptState *st, ByteCode op, ZString s1, ZString s2) +{ + int a = 0; + + switch (op) + { + case ByteCode::EQ: + a = s1 == s2; + break; + case ByteCode::NE: + a = s1 != s2; + break; + case ByteCode::GT: + a = s1 > s2; + break; + case ByteCode::GE: + a = s1 >= s2; + break; + case ByteCode::LT: + a = s1 < s2; + break; + case ByteCode::LE: + a = s1 <= s2; + break; + default: + PRINTF("illegal string operater\n"_fmt); + break; + } + + push_int<ScriptDataInt>(st->stack, a); +} + +/*========================================== + * 二項演算子(数値) + *------------------------------------------ + */ +static +void op_2num(ScriptState *st, ByteCode op, int i1, int i2) +{ + switch (op) + { + case ByteCode::SUB: + i1 -= i2; + break; + case ByteCode::MUL: + i1 *= i2; + break; + case ByteCode::DIV: + i1 /= i2; + break; + case ByteCode::MOD: + i1 %= i2; + break; + case ByteCode::AND: + i1 &= i2; + break; + case ByteCode::OR: + i1 |= i2; + break; + case ByteCode::XOR: + i1 ^= i2; + break; + case ByteCode::LAND: + i1 = i1 && i2; + break; + case ByteCode::LOR: + i1 = i1 || i2; + break; + case ByteCode::EQ: + i1 = i1 == i2; + break; + case ByteCode::NE: + i1 = i1 != i2; + break; + case ByteCode::GT: + i1 = i1 > i2; + break; + case ByteCode::GE: + i1 = i1 >= i2; + break; + case ByteCode::LT: + i1 = i1 < i2; + break; + case ByteCode::LE: + i1 = i1 <= i2; + break; + case ByteCode::R_SHIFT: + i1 = i1 >> i2; + break; + case ByteCode::L_SHIFT: + i1 = i1 << i2; + break; + } + push_int<ScriptDataInt>(st->stack, i1); +} + +/*========================================== + * 二項演算子 + *------------------------------------------ + */ +static +void op_2(ScriptState *st, ByteCode op) +{ + // pop_val has unfortunate implications here + script_data d2 = st->stack->stack_datav.back(); + st->stack->stack_datav.pop_back(); + get_val(st, &d2); + script_data d1 = st->stack->stack_datav.back(); + st->stack->stack_datav.pop_back(); + get_val(st, &d1); + + if (isstr(d1) && isstr(d2)) + { + // ss => op_2str + op_2str(st, op, d1.get_if<ScriptDataStr>()->str, d2.get_if<ScriptDataStr>()->str); + } + else if (!(isstr(d1) || isstr(d2))) + { + // ii => op_2num + op_2num(st, op, d1.get_if<ScriptDataInt>()->numi, d2.get_if<ScriptDataInt>()->numi); + } + else + { + // si,is => error + PRINTF("script: op_2: int&str, str&int not allow.\n"_fmt); + push_int<ScriptDataInt>(st->stack, 0); + } +} + +/*========================================== + * 単項演算子 + *------------------------------------------ + */ +static +void op_1num(ScriptState *st, ByteCode op) +{ + int i1; + i1 = pop_val(st); + switch (op) + { + case ByteCode::NEG: + i1 = -i1; + break; + case ByteCode::NOT: + i1 = ~i1; + break; + case ByteCode::LNOT: + i1 = !i1; + break; + } + push_int<ScriptDataInt>(st->stack, i1); +} + +/*========================================== + * 関数の実行 + *------------------------------------------ + */ +void run_func(ScriptState *st) +{ + size_t end_sp = st->stack->stack_datav.size(); + size_t start_sp = end_sp - 1; + while (!st->stack->stack_datav[start_sp].is<ScriptDataArg>()) + { + start_sp--; + if (start_sp == 0) + { + if (battle_config.error_log) + PRINTF("function not found\n"_fmt); + st->state = ScriptEndState::END; + return; + } + } + // the func is before the arg + start_sp--; + st->start = start_sp; + st->end = end_sp; + + if (!st->stack->stack_datav[st->start].is<ScriptDataFuncRef>()) + { + PRINTF("run_func: not function and command! \n"_fmt); + st->state = ScriptEndState::END; + return; + } + size_t func = st->stack->stack_datav[st->start].get_if<ScriptDataFuncRef>()->numi; + + if (DEBUG_RUN && battle_config.etc_log) + { + PRINTF("run_func : %s\n"_fmt, + builtin_functions[func].name); + PRINTF("stack dump :"_fmt); + for (script_data& d : st->stack->stack_datav) + { + MATCH_BEGIN (d) + { + MATCH_CASE (const ScriptDataInt&, u) + { + PRINTF(" int(%d)"_fmt, u.numi); + } + MATCH_CASE (const ScriptDataRetInfo&, u) + { + PRINTF(" retinfo(%p)"_fmt, static_cast<const void *>(&*u.script)); + } + MATCH_CASE (const ScriptDataParam&, u) + { + PRINTF(" param(%d)"_fmt, u.reg.sp()); + } + MATCH_CASE (const ScriptDataVariable&, u) + { + PRINTF(" name(%s)[%d]"_fmt, variable_names.outtern(u.reg.base()), u.reg.index()); + } + MATCH_CASE (const ScriptDataArg&, u) + { + (void)u; + PRINTF(" arg"_fmt); + } + MATCH_CASE (const ScriptDataPos&, u) + { + (void)u; + PRINTF(" pos(%d)"_fmt, u.numi); + } + MATCH_CASE (const ScriptDataStr&, u) + { + (void)u; + PRINTF(" str(%s)"_fmt, u.str); + } + MATCH_CASE (const ScriptDataFuncRef&, u) + { + (void)u; + PRINTF(" func(%s)"_fmt, builtin_functions[u.numi].name); + } + } + MATCH_END (); + } + PRINTF("\n"_fmt); + } + builtin_functions[func].func(st); + + pop_stack(st->stack, start_sp, end_sp); + + if (st->state == ScriptEndState::RETFUNC) + { + // ユーザー定義関数からの復帰 + int olddefsp = st->defsp; + + pop_stack(st->stack, st->defsp, start_sp); // 復帰に邪魔なスタック削除 + if (st->defsp < 4 + || !st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>()) + { + PRINTF("script:run_func (return) return without callfunc or callsub!\n"_fmt); + st->state = ScriptEndState::END; + return; + } + assert (olddefsp == st->defsp); // pretty sure it hasn't changed yet + st->scriptp.code = Some(conv_script(st, &st->stack->stack_datav[olddefsp - 1])); // スクリプトを復元 + st->scriptp.pos = conv_num(st, &st->stack->stack_datav[olddefsp - 2]); // スクリプト位置の復元 + st->defsp = conv_num(st, &st->stack->stack_datav[olddefsp - 3]); // 基準スタックポインタを復元 + // Number of arguments. + int i = conv_num(st, &st->stack->stack_datav[olddefsp - 4]); // 引数の数所得 + assert (i == 0); + + pop_stack(st->stack, olddefsp - 4 - i, olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除 + + st->state = ScriptEndState::GOTO; + } +} + +/*========================================== + * スクリプトの実行メイン部分 + *------------------------------------------ + */ +static +void run_script_main(ScriptState *st, Borrowed<const ScriptBuffer> rootscript) +{ + int cmdcount = script_config.check_cmdcount; + int gotocount = script_config.check_gotocount; + struct script_stack *stack = st->stack; + + st->defsp = stack->stack_datav.size(); + + int rerun_pos = st->scriptp.pos; + st->state = ScriptEndState::ZERO; + while (st->state == ScriptEndState::ZERO) + { + switch (ByteCode c = get_com(&st->scriptp)) + { + case ByteCode::EOL: + if (stack->stack_datav.size() != st->defsp) + { + if (true) + PRINTF("stack.sp (%zu) != default (%d)\n"_fmt, + stack->stack_datav.size(), + st->defsp); + abort(); + } + rerun_pos = st->scriptp.pos; + break; + case ByteCode::INT: + // synthesized! + push_int<ScriptDataInt>(stack, get_num(&st->scriptp)); + break; + + case ByteCode::POS: + case ByteCode::VARIABLE: + case ByteCode::FUNC_REF: + case ByteCode::PARAM: + // Note that these 3 have *very* different meanings, + // despite being encoded similarly. + { + int arg = 0; + arg |= static_cast<uint8_t>(st->scriptp.pop()) << 0; + arg |= static_cast<uint8_t>(st->scriptp.pop()) << 8; + arg |= static_cast<uint8_t>(st->scriptp.pop()) << 16; + switch(c) + { + case ByteCode::POS: + push_int<ScriptDataPos>(stack, arg); + break; + case ByteCode::VARIABLE: + push_reg<ScriptDataVariable>(stack, SIR::from(arg)); + break; + case ByteCode::FUNC_REF: + push_int<ScriptDataFuncRef>(stack, arg); + break; + case ByteCode::PARAM: + SP arg_sp = static_cast<SP>(arg); + push_reg<ScriptDataParam>(stack, SIR::from(arg_sp)); + break; + } + } + break; + case ByteCode::ARG: + push_int<ScriptDataArg>(stack, 0); + break; + case ByteCode::STR: + push_str<ScriptDataStr>(stack, st->scriptp.pops()); + break; + case ByteCode::FUNC: + run_func(st); + if (st->state == ScriptEndState::GOTO) + { + rerun_pos = st->scriptp.pos; + st->state = ScriptEndState::ZERO; + if (st->freeloop != 1 && gotocount > 0 && (--gotocount) <= 0) + { + PRINTF("run_script: infinity loop !\n"_fmt); + st->state = ScriptEndState::END; + } + } + break; + + case ByteCode::ADD: + op_add(st); + break; + + case ByteCode::SUB: + case ByteCode::MUL: + case ByteCode::DIV: + case ByteCode::MOD: + case ByteCode::EQ: + case ByteCode::NE: + case ByteCode::GT: + case ByteCode::GE: + case ByteCode::LT: + case ByteCode::LE: + case ByteCode::AND: + case ByteCode::OR: + case ByteCode::XOR: + case ByteCode::LAND: + case ByteCode::LOR: + case ByteCode::R_SHIFT: + case ByteCode::L_SHIFT: + op_2(st, c); + break; + + case ByteCode::NEG: + case ByteCode::NOT: + case ByteCode::LNOT: + op_1num(st, c); + break; + + case ByteCode::NOP: + st->state = ScriptEndState::END; + break; + + default: + if (battle_config.error_log) + PRINTF("unknown command : %d @ %zu\n"_fmt, + c, st->scriptp.pos); + st->state = ScriptEndState::END; + break; + } + if (st->freeloop != 1 && cmdcount > 0 && (--cmdcount) <= 0) + { + PRINTF("run_script: infinity loop !\n"_fmt); + st->state = ScriptEndState::END; + } + } + switch (st->state) + { + case ScriptEndState::STOP: + break; + case ScriptEndState::END: + { + dumb_ptr<map_session_data> sd = map_id2sd(st->rid); + st->scriptp.code = None; + st->scriptp.pos = -1; + if (sd && sd->npc_id == st->oid) + npc_event_dequeue(sd); + } + break; + case ScriptEndState::RERUNLINE: + st->scriptp.pos = rerun_pos; + break; + } + + if (st->state != ScriptEndState::END) + { + // 再開するためにスタック情報を保存 + dumb_ptr<map_session_data> sd = map_id2sd(st->rid); + if (sd) + { + sd->npc_stackbuf = stack->stack_datav; + sd->npc_script = st->scriptp.code; + // sd->npc_pos is set later ... ??? + sd->npc_scriptroot = Some(rootscript); + } + } +} + +/*========================================== + * スクリプトの実行 + *------------------------------------------ + */ +int run_script(ScriptPointer sp, BlockId rid, BlockId oid) +{ + return run_script_l(sp, rid, oid, nullptr); +} + +int run_script_l(ScriptPointer sp, BlockId rid, BlockId oid, + Slice<argrec_t> args) +{ + struct script_stack stack; + ScriptState st; + dumb_ptr<map_session_data> sd = map_id2sd(rid); + P<const ScriptBuffer> rootscript = TRY_UNWRAP(sp.code, return -1); + int i; + if (sp.pos >> 24) + return -1; + + if (sd && !sd->npc_stackbuf.empty() && sd->npc_scriptroot == Some(rootscript)) + { + // 前回のスタックを復帰 + sp.code = sd->npc_script; + stack.stack_datav = std::move(sd->npc_stackbuf); + } + st.stack = &stack; + st.scriptp = sp; + st.rid = rid; + st.oid = oid; + for (i = 0; i < args.size(); i++) + { + if (args[i].name.back() == '$') + pc_setregstr(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.s); + else + pc_setreg(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.i); + } + run_script_main(&st, rootscript); + + stack.stack_datav.clear(); + return st.scriptp.pos; +} + +void set_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e, int val) +{ + size_t k = variable_names.intern(var); + SIR reg = SIR::from(k, e); + set_reg(sd, VariableCode::VARIABLE, reg, val); +} +void set_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e, XString val) +{ + size_t k = variable_names.intern(var); + SIR reg = SIR::from(k, e); + set_reg(sd, VariableCode::VARIABLE, reg, val); +} +int get_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e) +{ + size_t k = variable_names.intern(var); + SIR reg = SIR::from(k, e); + struct script_data dat = ScriptDataVariable{.reg= reg}; + get_val(sd, &dat); + if (auto *u = dat.get_if<ScriptDataInt>()) + return u->numi; + PRINTF("Warning: you lied about the type and I'm too lazy to fix it!"_fmt); + return 0; +} +ZString get_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e) +{ + size_t k = variable_names.intern(var); + SIR reg = SIR::from(k, e); + struct script_data dat = ScriptDataVariable{.reg= reg}; + get_val(sd, &dat); + if (auto *u = dat.get_if<ScriptDataStr>()) + // this is almost certainly a memory leak after CONSTSTR removal + return u->str; + PRINTF("Warning: you lied about the type and I can't fix it!"_fmt); + return ZString(); +} +} // namespace map +} // namespace tmwa diff --git a/src/map/script-call.hpp b/src/map/script-call.hpp new file mode 100644 index 0000000..d494326 --- /dev/null +++ b/src/map/script-call.hpp @@ -0,0 +1,67 @@ +#pragma once +// script-call.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "script-call.t.hpp" + +#include "fwd.hpp" + +#include "../compat/borrow.hpp" + +#include "script-buffer.hpp" + + +namespace tmwa +{ +namespace map +{ +enum class ByteCode : uint8_t; + +// implemented in script-parse.cpp because reasons +struct ScriptPointer +{ + Option<Borrowed<const ScriptBuffer>> code; + size_t pos; + + ScriptPointer() + : code(None) + , pos() + {} + ScriptPointer(Borrowed<const ScriptBuffer> c, size_t p) + : code(Some(c)) + , pos(p) + {} + + ByteCode peek() const; + ByteCode pop(); + ZString pops(); +}; + +int run_script_l(ScriptPointer, BlockId, BlockId, Slice<argrec_t> args); +int run_script(ScriptPointer, BlockId, BlockId); + +void set_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e, int val); +void set_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e, XString val); + +int get_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e); +ZString get_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e); +} // namespace map +} // namespace tmwa diff --git a/src/map/script-call.t.hpp b/src/map/script-call.t.hpp new file mode 100644 index 0000000..5ef7de6 --- /dev/null +++ b/src/map/script-call.t.hpp @@ -0,0 +1,48 @@ +#pragma once +// script-call.t.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include "../strings/zstring.hpp" + + +namespace tmwa +{ +namespace map +{ +struct argrec_t +{ + ZString name; + union _aru + { + int i; + ZString s; + + _aru(int n) : i(n) {} + _aru(ZString z) : s(z) {} + } v; + + argrec_t(ZString n, int i) : name(n), v(i) {} + argrec_t(ZString n, ZString z) : name(n), v(z) {} +}; +} // namespace map +} // namespace tmwa diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp new file mode 100644 index 0000000..3291cfc --- /dev/null +++ b/src/map/script-fun.cpp @@ -0,0 +1,3164 @@ +#include "script-fun.hpp" +// script-fun.cpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011 Chuck Miller +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// Copyright © 2013 wushin +// +// 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 <http://www.gnu.org/licenses/>. + +#include "../compat/fun.hpp" + +#include "../generic/db.hpp" +#include "../generic/dumb_ptr.hpp" +#include "../generic/intern-pool.hpp" +#include "../generic/random.hpp" + +#include "../io/cxxstdio.hpp" +#include "../io/extract.hpp" + +#include "../net/timer.hpp" + +#include "../proto2/net-HumanTimeDiff.hpp" + +#include "../high/core.hpp" +#include "../high/extract_mmo.hpp" + +#include "atcommand.hpp" +#include "battle.hpp" +#include "battle_conf.hpp" +#include "chrif.hpp" +#include "clif.hpp" +#include "globals.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "magic-interpreter-base.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "npc.hpp" +#include "party.hpp" +#include "pc.hpp" +#include "script-call-internal.hpp" +#include "script-parse-internal.hpp" +#include "script-persist.hpp" +#include "skill.hpp" +#include "storage.hpp" + +#include "../poison.hpp" + + +namespace tmwa +{ +namespace map +{ +static +Array<LString, 11> pos_str //= +{{ + "Head"_s, + "Body"_s, + "Left hand"_s, + "Right hand"_s, + "Robe"_s, + "Shoes"_s, + "Accessory 1"_s, + "Accessory 2"_s, + "Head 2"_s, + "Head 3"_s, + "Not Equipped"_s, +}}; + +#define AARG(n) (st->stack->stack_datav[st->start + 2 + (n)]) +#define HARG(n) (st->end > st->start + 2 + (n)) + +// +// 埋め込み関数 +// +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_mes(ScriptState *st) +{ + RString mes = conv_str(st, &AARG(0)); + clif_scriptmes(script_rid2sd(st), st->oid, mes); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_goto(ScriptState *st) +{ + if (!AARG(0).is<ScriptDataPos>()) + { + PRINTF("script: goto: that's not a label!\n"_fmt); + st->state = ScriptEndState::END; + return; + } + + st->scriptp.pos = conv_num(st, &AARG(0)); + st->state = ScriptEndState::GOTO; +} + +/*========================================== + * ユーザー定義関数の呼び出し + *------------------------------------------ + */ +static +void builtin_callfunc(ScriptState *st) +{ + RString str = conv_str(st, &AARG(0)); + Option<P<const ScriptBuffer>> scr_ = userfunc_db.get(str); + + OMATCH_BEGIN (scr_) + { + OMATCH_CASE_SOME (scr) + { + int j = 0; + assert (st->start + 3 == st->end); +#if 0 + for (int i = st->start + 3; i < st->end; i++, j++) + push_copy(st->stack, i); +#endif + + push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ + push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ + push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ + push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort())); // 現在のスクリプトをプッシュ + + st->scriptp = ScriptPointer(scr, 0); + st->defsp = st->start + 4 + j; + st->state = ScriptEndState::GOTO; + } + OMATCH_CASE_NONE () + { + PRINTF("script: callfunc: function not found! [%s]\n"_fmt, str); + st->state = ScriptEndState::END; + } + } + OMATCH_END (); +} + +/*========================================== + * サブルーティンの呼び出し + *------------------------------------------ + */ +static +void builtin_callsub(ScriptState *st) +{ + int pos_ = conv_num(st, &AARG(0)); + int j = 0; + assert (st->start + 3 == st->end); +#if 0 + for (int i = st->start + 3; i < st->end; i++, j++) + push_copy(st->stack, i); +#endif + + push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ + push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ + push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ + push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort())); // 現在のスクリプトをプッシュ + + st->scriptp.pos = pos_; + st->defsp = st->start + 4 + j; + st->state = ScriptEndState::GOTO; +} + +/*========================================== + * サブルーチン/ユーザー定義関数の終了 + *------------------------------------------ + */ +static +void builtin_return(ScriptState *st) +{ +#if 0 + if (HARG(0)) + { // 戻り値有り + push_copy(st->stack, st->start + 2); + } +#endif + st->state = ScriptEndState::RETFUNC; +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_next(ScriptState *st) +{ + st->state = ScriptEndState::STOP; + clif_scriptnext(script_rid2sd(st), st->oid); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_close(ScriptState *st) +{ + st->state = ScriptEndState::END; + clif_scriptclose(script_rid2sd(st), st->oid); +} + +static +void builtin_close2(ScriptState *st) +{ + st->state = ScriptEndState::STOP; + clif_scriptclose(script_rid2sd(st), st->oid); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_menu(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + if (sd->state.menu_or_input == 0) + { + // First half: show menu. + st->state = ScriptEndState::RERUNLINE; + sd->state.menu_or_input = 1; + + MString buf; + for (int i = 0; i < (st->end - (st->start + 2)) / 2; i++) + { + RString choice_str = conv_str(st, &AARG(i * 2 + 0)); + if (!choice_str) + break; + buf += choice_str; + buf += ':'; + } + + clif_scriptmenu(script_rid2sd(st), st->oid, AString(buf)); + } + else + { + // Rerun: item is chosen from menu. + if (sd->npc_menu == 0xff) + { + // cancel + sd->state.menu_or_input = 0; + st->state = ScriptEndState::END; + return; + } + + // Actually jump to the label. + // Logic change: menu_choices is the *total* number of labels, + // not just the displayed number that ends with the "". + // (Would it be better to pop the stack before rerunning?) + int menu_choices = (st->end - (st->start + 2)) / 2; + pc_setreg(sd, SIR::from(variable_names.intern("@menu"_s)), sd->npc_menu); + sd->state.menu_or_input = 0; + if (sd->npc_menu > 0 && sd->npc_menu <= menu_choices) + { + int arg_index = (sd->npc_menu - 1) * 2 + 1; + if (!AARG(arg_index).is<ScriptDataPos>()) + { + st->state = ScriptEndState::END; + return; + } + st->scriptp.pos = AARG(arg_index).get_if<ScriptDataPos>()->numi; + st->state = ScriptEndState::GOTO; + } + } +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_rand(ScriptState *st) +{ + if (HARG(1)) + { + int min = conv_num(st, &AARG(0)); + int max = conv_num(st, &AARG(1)); + if (min > max) + std::swap(max, min); + push_int<ScriptDataInt>(st->stack, random_::in(min, max)); + } + else + { + int range = conv_num(st, &AARG(0)); + push_int<ScriptDataInt>(st->stack, range <= 0 ? 0 : random_::to(range)); + } +} + +/*========================================== + * Check whether the PC is at the specified location + *------------------------------------------ + */ +static +void builtin_isat(ScriptState *st) +{ + int x, y; + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x = conv_num(st, &AARG(1)); + y = conv_num(st, &AARG(2)); + + if (!sd) + return; + + push_int<ScriptDataInt>(st->stack, + (x == sd->bl_x) && (y == sd->bl_y) + && (str == sd->bl_m->name_)); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_warp(ScriptState *st) +{ + int x, y; + dumb_ptr<map_session_data> sd = script_rid2sd(st); + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x = conv_num(st, &AARG(1)); + y = conv_num(st, &AARG(2)); + pc_setpos(sd, str, x, y, BeingRemoveWhy::GONE); +} + +/*========================================== + * エリア指定ワープ + *------------------------------------------ + */ +static +void builtin_areawarp_sub(dumb_ptr<block_list> bl, MapName mapname, int x, int y) +{ + dumb_ptr<map_session_data> sd = bl->is_player(); + pc_setpos(sd, mapname, x, y, BeingRemoveWhy::GONE); +} + +static +void builtin_areawarp(ScriptState *st) +{ + int x, y; + int x0, y0, x1, y1; + + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x0 = conv_num(st, &AARG(1)); + y0 = conv_num(st, &AARG(2)); + x1 = conv_num(st, &AARG(3)); + y1 = conv_num(st, &AARG(4)); + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(5)))); + x = conv_num(st, &AARG(6)); + y = conv_num(st, &AARG(7)); + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + + map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y), + m, + x0, y0, + x1, y1, + BL::PC); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_heal(ScriptState *st) +{ + int hp, sp; + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + hp = conv_num(st, &AARG(0)); + sp = conv_num(st, &AARG(1)); + + if(sd != nullptr && (sd->status.hp < 1 && hp > 0)){ + pc_setstand(sd); + if (battle_config.player_invincible_time > interval_t::zero()) + pc_setinvincibletimer(sd, battle_config.player_invincible_time); + clif_resurrection(sd, 1); + } + + if(HARG(2) && bool(conv_num(st, &AARG(2))) && hp > 0) + pc_itemheal(sd, hp, sp); + else + pc_heal(sd, hp, sp); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_input(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = nullptr; + script_data& scrd = AARG(0); + assert (scrd.is<ScriptDataVariable>()); + + SIR reg = scrd.get_if<ScriptDataVariable>()->reg; + ZString name = variable_names.outtern(reg.base()); +// char prefix = name.front(); + char postfix = name.back(); + + sd = script_rid2sd(st); + if (sd->state.menu_or_input) + { + // Second time (rerun) + sd->state.menu_or_input = 0; + if (postfix == '$') + { + set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_str); + } + else + { + //commented by Lupus (check Value Number Input fix in clif.c) + //** Fix by fritz :X keeps people from abusing old input bugs + // wtf? + if (sd->npc_amount < 0) //** If input amount is less then 0 + { + clif_tradecancelled(sd); // added "Deal has been cancelled" message by Valaris + builtin_close(st); //** close + } + + set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_amount); + } + } + else + { + // First time - send prompt to client, then wait + st->state = ScriptEndState::RERUNLINE; + if (postfix == '$') + clif_scriptinputstr(sd, st->oid); + else + clif_scriptinput(sd, st->oid); + sd->state.menu_or_input = 1; + } +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_if (ScriptState *st) +{ + int sel, i; + + sel = conv_num(st, &AARG(0)); + if (!sel) + return; + + // 関数名をコピー + push_copy(st->stack, st->start + 3); + // 間に引数マーカを入れて + push_int<ScriptDataArg>(st->stack, 0); + // 残りの引数をコピー + for (i = st->start + 4; i < st->end; i++) + { + push_copy(st->stack, i); + } + run_func(st); +} + +/*========================================== + * 変数設定 + *------------------------------------------ + */ +static +void builtin_set(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = nullptr; + if (auto *u = AARG(0).get_if<ScriptDataParam>()) + { + SIR reg = u->reg; + sd = script_rid2sd(st); + + int val = conv_num(st, &AARG(1)); + set_reg(sd, VariableCode::PARAM, reg, val); + return; + } + + SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg; + + ZString name = variable_names.outtern(reg.base()); + char prefix = name.front(); + char postfix = name.back(); + + if (prefix != '$') + sd = script_rid2sd(st); + + if (postfix == '$') + { + // 文字列 + RString str = conv_str(st, &AARG(1)); + set_reg(sd, VariableCode::VARIABLE, reg, str); + } + else + { + // 数値 + int val = conv_num(st, &AARG(1)); + set_reg(sd, VariableCode::VARIABLE, reg, val); + } + +} + +/*========================================== + * 配列変数設定 + *------------------------------------------ + */ +static +void builtin_setarray(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = nullptr; + SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg; + ZString name = variable_names.outtern(reg.base()); + char prefix = name.front(); + char postfix = name.back(); + + if (prefix != '$' && prefix != '@') + { + PRINTF("builtin_setarray: illegal scope!\n"_fmt); + return; + } + if (prefix != '$') + sd = script_rid2sd(st); + + for (int j = 0, i = 1; i < st->end - st->start - 2 && j < 256; i++, j++) + { + if (postfix == '$') + set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_str(st, &AARG(i))); + else + set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_num(st, &AARG(i))); + } +} + +/*========================================== + * 配列変数クリア + *------------------------------------------ + */ +static +void builtin_cleararray(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = nullptr; + SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg; + ZString name = variable_names.outtern(reg.base()); + char prefix = name.front(); + char postfix = name.back(); + int sz = conv_num(st, &AARG(2)); + + if (prefix != '$' && prefix != '@') + { + PRINTF("builtin_cleararray: illegal scope!\n"_fmt); + return; + } + if (prefix != '$') + sd = script_rid2sd(st); + + for (int i = 0; i < sz; i++) + { + if (postfix == '$') + set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_str(st, &AARG(1))); + else + set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_num(st, &AARG(1))); + } + +} + +/*========================================== + * 配列変数のサイズ所得 + *------------------------------------------ + */ +static +int getarraysize(ScriptState *st, SIR reg) +{ + int i = reg.index(), c = i; + for (; i < 256; i++) + { + struct script_data vd = get_val2(st, reg.iplus(i)); + MATCH_BEGIN (vd) + { + MATCH_CASE (const ScriptDataStr&, u) + { + if (u.str[0]) + c = i; + continue; + } + MATCH_CASE (const ScriptDataInt&, u) + { + if (u.numi) + c = i; + continue; + } + } + MATCH_END (); + abort(); + } + return c + 1; +} + +static +void builtin_getarraysize(ScriptState *st) +{ + SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg; + ZString name = variable_names.outtern(reg.base()); + char prefix = name.front(); + + if (prefix != '$' && prefix != '@') + { + PRINTF("builtin_copyarray: illegal scope!\n"_fmt); + return; + } + + push_int<ScriptDataInt>(st->stack, getarraysize(st, reg)); +} + +/*========================================== + * 指定要素を表す値(キー)を所得する + *------------------------------------------ + */ +static +void builtin_getelementofarray(ScriptState *st) +{ + if (auto *u = AARG(0).get_if<ScriptDataVariable>()) + { + int i = conv_num(st, &AARG(1)); + if (i > 255 || i < 0) + { + PRINTF("script: getelementofarray (operator[]): param2 illegal number: %d\n"_fmt, + i); + push_int<ScriptDataInt>(st->stack, 0); + } + else + { + push_reg<ScriptDataVariable>(st->stack, + u->reg.iplus(i)); + } + } + else + { + PRINTF("script: getelementofarray (operator[]): param1 not named!\n"_fmt); + push_int<ScriptDataInt>(st->stack, 0); + } +} + +static +void builtin_wgm(ScriptState *st) +{ + ZString message = ZString(conv_str(st, &AARG(0))); + + intif_wis_message_to_gm(WISP_SERVER_NAME, + battle_config.hack_info_GM_level, + STRPRINTF("[GM] %s"_fmt, message)); +} + +static +void builtin_gmlog(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + ZString message = ZString(conv_str(st, &AARG(0))); + log_atcommand(sd, STRPRINTF("{SCRIPT} %s"_fmt, message)); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_setlook(ScriptState *st) +{ + LOOK type = LOOK(conv_num(st, &AARG(0))); + int val = conv_num(st, &AARG(1)); + + pc_changelook(script_rid2sd(st), type, val); + +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_countitem(ScriptState *st) +{ + ItemNameId nameid; + int count = 0; + dumb_ptr<map_session_data> sd; + + struct script_data *data; + + sd = script_rid2sd(st); + + data = &AARG(0); + get_val(st, data); + if (data->is<ScriptDataStr>()) + { + ZString name = ZString(conv_str(st, data)); + Option<P<struct item_data>> item_data_ = itemdb_searchname(name); + OMATCH_BEGIN_SOME (item_data, item_data_) + { + nameid = item_data->nameid; + } + OMATCH_END (); + } + else + nameid = wrap<ItemNameId>(conv_num(st, data)); + + if (nameid) + { + for (IOff0 i : IOff0::iter()) + { + if (sd->status.inventory[i].nameid == nameid) + count += sd->status.inventory[i].amount; + } + } + else + { + if (battle_config.error_log) + PRINTF("wrong item ID: countitem (%i)\n"_fmt, nameid); + } + push_int<ScriptDataInt>(st->stack, count); + +} + +/*========================================== + * 重量チェック + *------------------------------------------ + */ +static +void builtin_checkweight(ScriptState *st) +{ + ItemNameId nameid; + int amount; + dumb_ptr<map_session_data> sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data = &AARG(0); + get_val(st, data); + if (data->is<ScriptDataStr>()) + { + ZString name = ZString(conv_str(st, data)); + Option<P<struct item_data>> item_data_ = itemdb_searchname(name); + OMATCH_BEGIN_SOME (item_data, item_data_) + { + nameid = item_data->nameid; + } + OMATCH_END (); + } + else + nameid = wrap<ItemNameId>(conv_num(st, data)); + + amount = conv_num(st, &AARG(1)); + if (amount <= 0 || !nameid) + { + //If it gets the wrong item ID or the amount<=0, don't count its weight (assume it's a non-existent item) + push_int<ScriptDataInt>(st->stack, 0); + return; + } + + if (itemdb_weight(nameid) * amount + sd->weight > sd->max_weight) + { + push_int<ScriptDataInt>(st->stack, 0); + } + else + { + push_int<ScriptDataInt>(st->stack, 1); + } + +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_getitem(ScriptState *st) +{ + ItemNameId nameid; + int amount; + dumb_ptr<map_session_data> sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data = &AARG(0); + get_val(st, data); + if (data->is<ScriptDataStr>()) + { + ZString name = ZString(conv_str(st, data)); + Option<P<struct item_data>> item_data_ = itemdb_searchname(name); + OMATCH_BEGIN_SOME (item_data, item_data_) + { + nameid = item_data->nameid; + } + OMATCH_END (); + } + else + nameid = wrap<ItemNameId>(conv_num(st, data)); + + if ((amount = + conv_num(st, &AARG(1))) <= 0) + { + return; //return if amount <=0, skip the useles iteration + } + + if (nameid) + { + Item item_tmp {}; + item_tmp.nameid = nameid; + if (HARG(3)) //アイテムを指定したIDに渡す + sd = map_id2sd(wrap<BlockId>(conv_num(st, &AARG(3)))); + if (sd == nullptr) //アイテムを渡す相手がいなかったらお帰り + return; + PickupFail flag; + if ((flag = pc_additem(sd, &item_tmp, amount)) != PickupFail::OKAY) + { + clif_additem(sd, IOff0::from(0), 0, flag); + map_addflooritem(&item_tmp, amount, + sd->bl_m, sd->bl_x, sd->bl_y, + nullptr, nullptr, nullptr); + } + } + +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_makeitem(ScriptState *st) +{ + ItemNameId nameid; + int amount; + int x, y; + dumb_ptr<map_session_data> sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data = &AARG(0); + get_val(st, data); + if (data->is<ScriptDataStr>()) + { + ZString name = ZString(conv_str(st, data)); + Option<P<struct item_data>> item_data_ = itemdb_searchname(name); + OMATCH_BEGIN_SOME (item_data, item_data_) + { + nameid = item_data->nameid; + } + OMATCH_END (); + } + else + nameid = wrap<ItemNameId>(conv_num(st, data)); + + amount = conv_num(st, &AARG(1)); + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(2)))); + x = conv_num(st, &AARG(3)); + y = conv_num(st, &AARG(4)); + + P<map_local> m = ((sd && mapname == MOB_THIS_MAP) + ? sd->bl_m + : TRY_UNWRAP(map_mapname2mapid(mapname), return)); + + if (nameid) + { + Item item_tmp {}; + item_tmp.nameid = nameid; + + map_addflooritem(&item_tmp, amount, m, x, y, nullptr, nullptr, nullptr); + } +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_delitem(ScriptState *st) +{ + ItemNameId nameid; + int amount; + dumb_ptr<map_session_data> sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data = &AARG(0); + get_val(st, data); + if (data->is<ScriptDataStr>()) + { + ZString name = ZString(conv_str(st, data)); + Option<P<struct item_data>> item_data_ = itemdb_searchname(name); + OMATCH_BEGIN_SOME (item_data, item_data_) + { + nameid = item_data->nameid; + } + OMATCH_END (); + } + else + nameid = wrap<ItemNameId>(conv_num(st, data)); + + amount = conv_num(st, &AARG(1)); + + if (!nameid || amount <= 0) + { + //By Lupus. Don't run FOR if you've got the wrong item ID or amount<=0 + return; + } + + for (IOff0 i : IOff0::iter()) + { + if (sd->status.inventory[i].nameid == nameid) + { + if (sd->status.inventory[i].amount >= amount) + { + pc_delitem(sd, i, amount, 0); + break; + } + else + { + amount -= sd->status.inventory[i].amount; + if (amount == 0) + amount = sd->status.inventory[i].amount; + pc_delitem(sd, i, amount, 0); + break; + } + } + } + +} + +/*========================================== + *キャラ関係のID取得 + *------------------------------------------ + */ +static +void builtin_getcharid(ScriptState *st) +{ + int num; + dumb_ptr<map_session_data> sd; + + num = conv_num(st, &AARG(0)); + if (HARG(1)) + sd = map_nick2sd(stringish<CharName>(ZString(conv_str(st, &AARG(1))))); + else + sd = script_rid2sd(st); + if (sd == nullptr) + { + push_int<ScriptDataInt>(st->stack, -1); + return; + } + if (num == 0) + push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status_key.char_id)); + if (num == 1) + push_int<ScriptDataInt>(st->stack, unwrap<PartyId>(sd->status.party_id)); + if (num == 2) + push_int<ScriptDataInt>(st->stack, 0/*guild_id*/); + if (num == 3) + push_int<ScriptDataInt>(st->stack, unwrap<AccountId>(sd->status_key.account_id)); +} + +/*========================================== + *指定IDのPT名取得 + *------------------------------------------ + */ +static +RString builtin_getpartyname_sub(PartyId party_id) +{ + Option<PartyPair> p = party_search(party_id); + + return p.pmd_pget(&PartyMost::name).copy_or(PartyName()); +} + +/*========================================== + * キャラクタの名前 + *------------------------------------------ + */ +static +void builtin_strcharinfo(ScriptState *st) +{ + dumb_ptr<map_session_data> sd; + int num; + + sd = script_rid2sd(st); + num = conv_num(st, &AARG(0)); + if (num == 0) + { + RString buf = sd->status_key.name.to__actual(); + push_str<ScriptDataStr>(st->stack, buf); + } + if (num == 1) + { + RString buf = builtin_getpartyname_sub(sd->status.party_id); + if (buf) + push_str<ScriptDataStr>(st->stack, buf); + else + push_str<ScriptDataStr>(st->stack, ""_s); + } + if (num == 2) + { + // was: guild name + push_str<ScriptDataStr>(st->stack, ""_s); + } + +} + +// indexed by the equip_* in db/const.txt +// TODO change to use EQUIP +static +Array<EPOS, 11> equip //= +{{ + EPOS::HAT, + EPOS::MISC1, + EPOS::SHIELD, + EPOS::WEAPON, + EPOS::GLOVES, + EPOS::SHOES, + EPOS::CAPE, + EPOS::MISC2, + EPOS::TORSO, + EPOS::LEGS, + EPOS::ARROW, +}}; + +/*========================================== + * GetEquipID(Pos); Pos: 1-10 + *------------------------------------------ + */ +static +void builtin_getequipid(ScriptState *st) +{ + int num; + dumb_ptr<map_session_data> sd; + + sd = script_rid2sd(st); + if (sd == nullptr) + { + PRINTF("getequipid: sd == nullptr\n"_fmt); + return; + } + num = conv_num(st, &AARG(0)); + IOff0 i = pc_checkequip(sd, equip[num - 1]); + if (i.ok()) + { + Option<P<struct item_data>> item_ = sd->inventory_data[i]; + OMATCH_BEGIN (item_) + { + OMATCH_CASE_SOME (item) + { + push_int<ScriptDataInt>(st->stack, unwrap<ItemNameId>(item->nameid)); + } + OMATCH_CASE_NONE () + { + push_int<ScriptDataInt>(st->stack, 0); + } + } + OMATCH_END (); + } + else + { + push_int<ScriptDataInt>(st->stack, -1); + } +} + +/*========================================== + * freeloop + *------------------------------------------ + */ +static +void builtin_freeloop(ScriptState *st) +{ + int num; + num = conv_num(st, &AARG(0)); + if(num == 1) + { + st->freeloop = 1; + } + else + { + st->freeloop = 0; + } +} + +/*========================================== + * 装備名文字列(精錬メニュー用) + *------------------------------------------ + */ +static +void builtin_getequipname(ScriptState *st) +{ + int num; + dumb_ptr<map_session_data> sd; + + AString buf; + + sd = script_rid2sd(st); + num = conv_num(st, &AARG(0)); + IOff0 i = pc_checkequip(sd, equip[num - 1]); + if (i.ok()) + { + Option<P<struct item_data>> item_ = sd->inventory_data[i]; + OMATCH_BEGIN (item_) + { + OMATCH_CASE_SOME (item) + { + buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], item->jname); + } + OMATCH_CASE_NONE () + { + buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], pos_str[10]); + } + } + OMATCH_END (); + } + else + { + buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], pos_str[10]); + } + push_str<ScriptDataStr>(st->stack, buf); + +} + +/*========================================== + * 装備品による能力値ボーナス + *------------------------------------------ + */ +static +void builtin_bonus(ScriptState *st) +{ + SP type = SP(conv_num(st, &AARG(0))); + int val = conv_num(st, &AARG(1)); + dumb_ptr<map_session_data> sd = script_rid2sd(st); + pc_bonus(sd, type, val); + +} + +/*========================================== + * 装備品による能力値ボーナス + *------------------------------------------ + */ +static +void builtin_bonus2(ScriptState *st) +{ + SP type = SP(conv_num(st, &AARG(0))); + int type2 = conv_num(st, &AARG(1)); + int val = conv_num(st, &AARG(2)); + dumb_ptr<map_session_data> sd = script_rid2sd(st); + pc_bonus2(sd, type, type2, val); + +} + +/*========================================== + * スキル所得 + *------------------------------------------ + */ +static +void builtin_skill(ScriptState *st) +{ + int level, flag = 1; + dumb_ptr<map_session_data> sd; + + SkillID id = SkillID(conv_num(st, &AARG(0))); + level = conv_num(st, &AARG(1)); + if (HARG(2)) + flag = conv_num(st, &AARG(2)); + sd = script_rid2sd(st); + pc_skill(sd, id, level, flag); + clif_skillinfoblock(sd); + +} + +/*========================================== + * [Fate] Sets the skill level permanently + *------------------------------------------ + */ +static +void builtin_setskill(ScriptState *st) +{ + int level; + dumb_ptr<map_session_data> sd; + + SkillID id = static_cast<SkillID>(conv_num(st, &AARG(0))); + level = conv_num(st, &AARG(1)); + sd = script_rid2sd(st); + + level = std::min(level, MAX_SKILL_LEVEL); + level = std::max(level, 0); + sd->status.skill[id].lv = level; + clif_skillinfoblock(sd); +} + +/*========================================== + * スキルレベル所得 + *------------------------------------------ + */ +static +void builtin_getskilllv(ScriptState *st) +{ + SkillID id = SkillID(conv_num(st, &AARG(0))); + push_int<ScriptDataInt>(st->stack, pc_checkskill(script_rid2sd(st), id)); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_getgmlevel(ScriptState *st) +{ + push_int<ScriptDataInt>(st->stack, pc_isGM(script_rid2sd(st)).get_all_bits()); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_end(ScriptState *st) +{ + st->state = ScriptEndState::END; +} + +/*========================================== + * [Freeyorp] Return the current opt2 + *------------------------------------------ + */ + +static +void builtin_getopt2(ScriptState *st) +{ + dumb_ptr<map_session_data> sd; + + sd = script_rid2sd(st); + + push_int<ScriptDataInt>(st->stack, static_cast<uint16_t>(sd->opt2)); + +} + +/*========================================== + * [Freeyorp] Sets opt2 + *------------------------------------------ + */ + +static +void builtin_setopt2(ScriptState *st) +{ + dumb_ptr<map_session_data> sd; + + Opt2 new_opt2 = Opt2(conv_num(st, &AARG(0))); + sd = script_rid2sd(st); + if (new_opt2 == sd->opt2) + return; + sd->opt2 = new_opt2; + clif_changeoption(sd); + pc_calcstatus(sd, 0); + +} + +/*========================================== + * セーブポイントの保存 + *------------------------------------------ + */ +static +void builtin_savepoint(ScriptState *st) +{ + int x, y; + + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x = conv_num(st, &AARG(1)); + y = conv_num(st, &AARG(2)); + pc_setsavepoint(script_rid2sd(st), str, x, y); +} + +/*========================================== + * gettimetick(type) + * + * type The type of time measurement. + * Specify 0 for the system tick, 1 for + * seconds elapsed today, or 2 for seconds + * since Unix epoch. Defaults to 0 for any + * other value. + *------------------------------------------ + */ +static +void builtin_gettimetick(ScriptState *st) /* Asgard Version */ +{ + int type; + type = conv_num(st, &AARG(0)); + + switch (type) + { + /* Number of seconds elapsed today(0-86399, 00:00:00-23:59:59). */ + case 1: + { + struct tm t = TimeT::now(); + push_int<ScriptDataInt>(st->stack, + t.tm_hour * 3600 + t.tm_min * 60 + t.tm_sec); + break; + } + /* Seconds since Unix epoch. */ + case 2: + push_int<ScriptDataInt>(st->stack, static_cast<time_t>(TimeT::now())); + break; + /* System tick(unsigned int, and yes, it will wrap). */ + case 0: + default: + push_int<ScriptDataInt>(st->stack, gettick().time_since_epoch().count()); + break; + } +} + +/*========================================== + * GetTime(Type); + * 1: Sec 2: Min 3: Hour + * 4: WeekDay 5: MonthDay 6: Month + * 7: Year + *------------------------------------------ + */ +static +void builtin_gettime(ScriptState *st) /* Asgard Version */ +{ + int type = conv_num(st, &AARG(0)); + + struct tm t = TimeT::now(); + + switch (type) + { + case 1: //Sec(0~59) + push_int<ScriptDataInt>(st->stack, t.tm_sec); + break; + case 2: //Min(0~59) + push_int<ScriptDataInt>(st->stack, t.tm_min); + break; + case 3: //Hour(0~23) + push_int<ScriptDataInt>(st->stack, t.tm_hour); + break; + case 4: //WeekDay(0~6) + push_int<ScriptDataInt>(st->stack, t.tm_wday); + break; + case 5: //MonthDay(01~31) + push_int<ScriptDataInt>(st->stack, t.tm_mday); + break; + case 6: //Month(01~12) + push_int<ScriptDataInt>(st->stack, t.tm_mon + 1); + break; + case 7: //Year(20xx) + push_int<ScriptDataInt>(st->stack, t.tm_year + 1900); + break; + default: //(format error) + push_int<ScriptDataInt>(st->stack, -1); + break; + } +} + +/*========================================== + * カプラ倉庫を開く + *------------------------------------------ + */ +static +void builtin_openstorage(ScriptState *st) +{ +// int sync = 0; +// if (st->end >= 3) sync = conv_num(st,& (st->stack->stack_data[st->start+2])); + dumb_ptr<map_session_data> sd = script_rid2sd(st); + +// if (sync) { + st->state = ScriptEndState::STOP; + sd->npc_flags.storage = 1; +// } else st->state = ScriptEndState::END; + + storage_storageopen(sd); +} + +/*========================================== + * NPCで経験値上げる + *------------------------------------------ + */ +static +void builtin_getexp(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + int base = 0, job = 0; + + base = conv_num(st, &AARG(0)); + job = conv_num(st, &AARG(1)); + if (base < 0 || job < 0) + return; + if (sd) + pc_gainexp_reason(sd, base, job, PC_GAINEXP_REASON::SCRIPT); + +} + +/*========================================== + * モンスター発生 + *------------------------------------------ + */ +static +void builtin_monster(ScriptState *st) +{ + Species mob_class; + int amount, x, y; + NpcEvent event; + + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x = conv_num(st, &AARG(1)); + y = conv_num(st, &AARG(2)); + MobName str = stringish<MobName>(ZString(conv_str(st, &AARG(3)))); + mob_class = wrap<Species>(conv_num(st, &AARG(4))); + amount = conv_num(st, &AARG(5)); + if (HARG(6)) + extract(ZString(conv_str(st, &AARG(6))), &event); + + mob_once_spawn(map_id2sd(st->rid), mapname, x, y, str, mob_class, amount, + event); +} + +/*========================================== + * モンスター発生 + *------------------------------------------ + */ +static +void builtin_areamonster(ScriptState *st) +{ + Species mob_class; + int amount, x0, y0, x1, y1; + NpcEvent event; + + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x0 = conv_num(st, &AARG(1)); + y0 = conv_num(st, &AARG(2)); + x1 = conv_num(st, &AARG(3)); + y1 = conv_num(st, &AARG(4)); + MobName str = stringish<MobName>(ZString(conv_str(st, &AARG(5)))); + mob_class = wrap<Species>(conv_num(st, &AARG(6))); + amount = conv_num(st, &AARG(7)); + if (HARG(8)) + extract(ZString(conv_str(st, &AARG(8))), &event); + + mob_once_spawn_area(map_id2sd(st->rid), mapname, x0, y0, x1, y1, str, mob_class, + amount, event); +} + +/*========================================== + * モンスター削除 + *------------------------------------------ + */ +static +void builtin_killmonster_sub(dumb_ptr<block_list> bl, NpcEvent event) +{ + dumb_ptr<mob_data> md = bl->is_mob(); + if (event) + { + if (event == md->npc_event) + mob_delete(md); + return; + } + else if (!event) + { + if (md->spawn.delay1 == static_cast<interval_t>(-1) + && md->spawn.delay2 == static_cast<interval_t>(-1)) + mob_delete(md); + return; + } +} + +static +void builtin_killmonster(ScriptState *st) +{ + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + ZString event_ = ZString(conv_str(st, &AARG(1))); + NpcEvent event; + if (event_ != "All"_s) + extract(event_, &event); + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + map_foreachinarea(std::bind(builtin_killmonster_sub, ph::_1, event), + m, + 0, 0, + m->xs, m->ys, + BL::MOB); +} + +/*========================================== + * NPC主体イベント実行 + *------------------------------------------ + */ +static +void builtin_donpcevent(ScriptState *st) +{ + ZString event_ = ZString(conv_str(st, &AARG(0))); + NpcEvent event; + extract(event_, &event); + npc_event_do(event); +} + +/*========================================== + * イベントタイマー追加 + *------------------------------------------ + */ +static +void builtin_addtimer(ScriptState *st) +{ + interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(0))); + ZString event_ = ZString(conv_str(st, &AARG(1))); + NpcEvent event; + extract(event_, &event); + pc_addeventtimer(script_rid2sd(st), tick, event); +} + +/*========================================== + * NPCタイマー初期化 + *------------------------------------------ + */ +static +void builtin_initnpctimer(ScriptState *st) +{ + dumb_ptr<npc_data> nd_; + if (HARG(0)) + nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0))))); + else + nd_ = map_id_is_npc(st->oid); + assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); + dumb_ptr<npc_data_script> nd = nd_->is_script(); + + npc_settimerevent_tick(nd, interval_t::zero()); + npc_timerevent_start(nd); +} + +/*========================================== + * NPCタイマー開始 + *------------------------------------------ + */ +static +void builtin_startnpctimer(ScriptState *st) +{ + dumb_ptr<npc_data> nd_; + if (HARG(0)) + nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0))))); + else + nd_ = map_id_is_npc(st->oid); + assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); + dumb_ptr<npc_data_script> nd = nd_->is_script(); + + npc_timerevent_start(nd); +} + +/*========================================== + * NPCタイマー停止 + *------------------------------------------ + */ +static +void builtin_stopnpctimer(ScriptState *st) +{ + dumb_ptr<npc_data> nd_; + if (HARG(0)) + nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0))))); + else + nd_ = map_id_is_npc(st->oid); + assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); + dumb_ptr<npc_data_script> nd = nd_->is_script(); + + npc_timerevent_stop(nd); +} + +/*========================================== + * NPCタイマー情報所得 + *------------------------------------------ + */ +static +void builtin_getnpctimer(ScriptState *st) +{ + dumb_ptr<npc_data> nd_; + int type = conv_num(st, &AARG(0)); + int val = 0; + if (HARG(1)) + nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(1))))); + else + nd_ = map_id_is_npc(st->oid); + assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); + dumb_ptr<npc_data_script> nd = nd_->is_script(); + + switch (type) + { + case 0: + val = npc_gettimerevent_tick(nd).count(); + break; + case 1: + val = nd->scr.timer_active; + break; + case 2: + val = nd->scr.timer_eventv.size(); + break; + } + push_int<ScriptDataInt>(st->stack, val); +} + +/*========================================== + * NPCタイマー値設定 + *------------------------------------------ + */ +static +void builtin_setnpctimer(ScriptState *st) +{ + dumb_ptr<npc_data> nd_; + interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(0))); + if (HARG(1)) + nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(1))))); + else + nd_ = map_id_is_npc(st->oid); + assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); + dumb_ptr<npc_data_script> nd = nd_->is_script(); + + npc_settimerevent_tick(nd, tick); +} + +/*========================================== + * 天の声アナウンス + *------------------------------------------ + */ +static +void builtin_announce(ScriptState *st) +{ + int flag; + ZString str = ZString(conv_str(st, &AARG(0))); + flag = conv_num(st, &AARG(1)); + + if (flag & 0x0f) + { + dumb_ptr<block_list> bl; + if (flag & 0x08) + bl = map_id2bl(st->oid); + else + bl = script_rid2sd(st); + clif_GMmessage(bl, str, flag); + } + else + intif_GMmessage(str); +} + +/*========================================== + * 天の声アナウンス(特定マップ) + *------------------------------------------ + */ +static +void builtin_mapannounce_sub(dumb_ptr<block_list> bl, XString str, int flag) +{ + clif_GMmessage(bl, str, flag | 3); +} + +static +void builtin_mapannounce(ScriptState *st) +{ + int flag; + + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + ZString str = ZString(conv_str(st, &AARG(1))); + flag = conv_num(st, &AARG(2)); + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + map_foreachinarea(std::bind(builtin_mapannounce_sub, ph::_1, str, flag & 0x10), + m, + 0, 0, + m->xs, m->ys, + BL::PC); +} + +/*========================================== + * ユーザー数所得 + *------------------------------------------ + */ +static +void builtin_getusers(ScriptState *st) +{ + int flag = conv_num(st, &AARG(0)); + dumb_ptr<block_list> bl = map_id2bl((flag & 0x08) ? st->oid : st->rid); + int val = 0; + switch (flag & 0x07) + { + case 0: + val = bl->bl_m->users; + break; + case 1: + val = map_getusers(); + break; + } + push_int<ScriptDataInt>(st->stack, val); +} + +/*========================================== + * マップ指定ユーザー数所得 + *------------------------------------------ + */ +static +void builtin_getmapusers(ScriptState *st) +{ + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str), + { + push_int<ScriptDataInt>(st->stack, -1); + return; + }); + push_int<ScriptDataInt>(st->stack, m->users); +} + +/*========================================== + * エリア指定ユーザー数所得 + *------------------------------------------ + */ +static +void builtin_getareausers_sub(dumb_ptr<block_list> bl, int *users) +{ + if (bool(bl->is_player()->status.option & Opt0::HIDE)) + return; + (*users)++; +} + +static +void builtin_getareausers_living_sub(dumb_ptr<block_list> bl, int *users) +{ + if (bool(bl->is_player()->status.option & Opt0::HIDE)) + return; + if (!pc_isdead(bl->is_player())) + (*users)++; +} + +static +void builtin_getareausers(ScriptState *st) +{ + int x0, y0, x1, y1, users = 0; + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x0 = conv_num(st, &AARG(1)); + y0 = conv_num(st, &AARG(2)); + x1 = conv_num(st, &AARG(3)); + y1 = conv_num(st, &AARG(4)); + + int living = 0; + if (HARG(5)) + { + living = conv_num(st, &AARG(5)); + } + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str), + { + push_int<ScriptDataInt>(st->stack, -1); + return; + }); + map_foreachinarea(std::bind(living ? builtin_getareausers_living_sub: builtin_getareausers_sub, ph::_1, &users), + m, + x0, y0, + x1, y1, + BL::PC); + push_int<ScriptDataInt>(st->stack, users); +} + +/*========================================== + * エリア指定ドロップアイテム数所得 + *------------------------------------------ + */ +static +void builtin_getareadropitem_sub(dumb_ptr<block_list> bl, ItemNameId item, int *amount) +{ + dumb_ptr<flooritem_data> drop = bl->is_item(); + + if (drop->item_data.nameid == item) + (*amount) += drop->item_data.amount; + +} + +static +void builtin_getareadropitem_sub_anddelete(dumb_ptr<block_list> bl, ItemNameId item, int *amount) +{ + dumb_ptr<flooritem_data> drop = bl->is_item(); + + if (drop->item_data.nameid == item) + { + (*amount) += drop->item_data.amount; + clif_clearflooritem(drop, nullptr); + map_delobject(drop->bl_id, drop->bl_type); + } +} + +static +void builtin_getareadropitem(ScriptState *st) +{ + ItemNameId item; + int x0, y0, x1, y1, amount = 0, delitems = 0; + struct script_data *data; + + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x0 = conv_num(st, &AARG(1)); + y0 = conv_num(st, &AARG(2)); + x1 = conv_num(st, &AARG(3)); + y1 = conv_num(st, &AARG(4)); + + data = &AARG(5); + get_val(st, data); + if (data->is<ScriptDataStr>()) + { + ZString name = ZString(conv_str(st, data)); + Option<P<struct item_data>> item_data_ = itemdb_searchname(name); + OMATCH_BEGIN_SOME (item_data, item_data_) + { + item = item_data->nameid; + } + OMATCH_END (); + } + else + item = wrap<ItemNameId>(conv_num(st, data)); + + if (HARG(6)) + delitems = conv_num(st, &AARG(6)); + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str), + { + push_int<ScriptDataInt>(st->stack, -1); + return; + }); + if (delitems) + map_foreachinarea(std::bind(builtin_getareadropitem_sub_anddelete, ph::_1, item, &amount), + m, + x0, y0, + x1, y1, + BL::ITEM); + else + map_foreachinarea(std::bind(builtin_getareadropitem_sub, ph::_1, item, &amount), + m, + x0, y0, + x1, y1, + BL::ITEM); + + push_int<ScriptDataInt>(st->stack, amount); +} + +/*========================================== + * NPCの有効化 + *------------------------------------------ + */ +static +void builtin_enablenpc(ScriptState *st) +{ + NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARG(0)))); + npc_enable(str, 1); +} + +/*========================================== + * NPCの無効化 + *------------------------------------------ + */ +static +void builtin_disablenpc(ScriptState *st) +{ + NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARG(0)))); + npc_enable(str, 0); +} + +/*========================================== + * 状態異常にかかる + *------------------------------------------ + */ +static +void builtin_sc_start(ScriptState *st) +{ + dumb_ptr<block_list> bl; + int val1; + StatusChange type = static_cast<StatusChange>(conv_num(st, &AARG(0))); + interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(1))); + if (tick < 1_s) + // work around old behaviour of: + // speed potion + // atk potion + // matk potion + // + // which used to use seconds + // all others used milliseconds + tick *= 1000; + val1 = conv_num(st, &AARG(2)); + if (HARG(3)) //指定したキャラを状態異常にする + bl = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(3)))); + else + bl = map_id2bl(st->rid); + skill_status_change_start(bl, type, val1, tick); +} + +/*========================================== + * 状態異常が直る + *------------------------------------------ + */ +static +void builtin_sc_end(ScriptState *st) +{ + dumb_ptr<block_list> bl; + StatusChange type = StatusChange(conv_num(st, &AARG(0))); + bl = map_id2bl(st->rid); + skill_status_change_end(bl, type, nullptr); +} + +static +void builtin_sc_check(ScriptState *st) +{ + dumb_ptr<block_list> bl; + StatusChange type = StatusChange(conv_num(st, &AARG(0))); + bl = map_id2bl(st->rid); + + push_int<ScriptDataInt>(st->stack, skill_status_change_active(bl, type)); + +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_debugmes(ScriptState *st) +{ + RString mes = conv_str(st, &AARG(0)); + PRINTF("script debug: %d %d: '%s'\n"_fmt, + st->rid, st->oid, mes); +} + +/*========================================== + * ステータスリセット + *------------------------------------------ + */ +static +void builtin_resetstatus(ScriptState *st) +{ + dumb_ptr<map_session_data> sd; + sd = script_rid2sd(st); + pc_resetstate(sd); +} + +/*========================================== + * RIDのアタッチ + *------------------------------------------ + */ +static +void builtin_attachrid(ScriptState *st) +{ + st->rid = wrap<BlockId>(conv_num(st, &AARG(0))); + push_int<ScriptDataInt>(st->stack, (map_id2sd(st->rid) != nullptr)); +} + +/*========================================== + * RIDのデタッチ + *------------------------------------------ + */ +static +void builtin_detachrid(ScriptState *st) +{ + st->rid = BlockId(); +} + +/*========================================== + * 存在チェック + *------------------------------------------ + */ +static +void builtin_isloggedin(ScriptState *st) +{ + push_int<ScriptDataInt>(st->stack, + map_id2sd(wrap<BlockId>(conv_num(st, &AARG(0)))) != nullptr); +} + +static +void builtin_setmapflag(ScriptState *st) +{ + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + int i = conv_num(st, &AARG(1)); + MapFlag mf = map_flag_from_int(i); + Option<P<map_local>> m_ = map_mapname2mapid(str); + OMATCH_BEGIN_SOME (m, m_) + { + m->flag.set(mf, 1); + } + OMATCH_END (); +} + +static +void builtin_removemapflag(ScriptState *st) +{ + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + int i = conv_num(st, &AARG(1)); + MapFlag mf = map_flag_from_int(i); + Option<P<map_local>> m_ = map_mapname2mapid(str); + OMATCH_BEGIN_SOME (m, m_) + { + m->flag.set(mf, 0); + } + OMATCH_END (); +} + +static +void builtin_getmapflag(ScriptState *st) +{ + int r = -1; + + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + int i = conv_num(st, &AARG(1)); + MapFlag mf = map_flag_from_int(i); + Option<P<map_local>> m_ = map_mapname2mapid(str); + OMATCH_BEGIN_SOME (m, m_) + { + r = m->flag.get(mf); + } + OMATCH_END (); + + push_int<ScriptDataInt>(st->stack, r); +} + +static +void builtin_pvpon(ScriptState *st) +{ + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str), return); + if (!m->flag.get(MapFlag::PVP) && !m->flag.get(MapFlag::NOPVP)) + { + m->flag.set(MapFlag::PVP, 1); + + if (battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris] + return; + + for (io::FD i : iter_fds()) + { + Session *s = get_session(i); + if (!s) + continue; + map_session_data *pl_sd = static_cast<map_session_data *>(s->session_data.get()); + if (pl_sd && pl_sd->state.auth) + { + if (m == pl_sd->bl_m && !pl_sd->pvp_timer) + { + pl_sd->pvp_timer = Timer(gettick() + 200_ms, + std::bind(pc_calc_pvprank_timer, ph::_1, ph::_2, + pl_sd->bl_id)); + pl_sd->pvp_rank = 0; + pl_sd->pvp_point = 5; + } + } + } + } +} + +static +void builtin_pvpoff(ScriptState *st) +{ + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str), return); + if (m->flag.get(MapFlag::PVP) && m->flag.get(MapFlag::NOPVP)) + { + m->flag.set(MapFlag::PVP, 0); + + if (battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris] + return; + + for (io::FD i : iter_fds()) + { + Session *s = get_session(i); + if (!s) + continue; + map_session_data *pl_sd = static_cast<map_session_data *>(s->session_data.get()); + if (pl_sd && pl_sd->state.auth) + { + if (m == pl_sd->bl_m) + { + pl_sd->pvp_timer.cancel(); + } + } + } + } +} + +static +void builtin_setpvpchannel(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + int flag; + flag = conv_num(st, &AARG(0)); + if (flag < 1) + flag = 0; + + sd->state.pvpchannel = flag; +} + +static +void builtin_getpvpflag(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + int num = conv_num(st, &AARG(0)); + int flag = 0; + + switch (num){ + case 0: + flag = sd->state.pvpchannel; + break; + case 1: + flag = bool(sd->status.option & Opt0::HIDE); + break; + } + + push_int<ScriptDataInt>(st->stack, flag); +} + +/*========================================== + * NPCエモーション + *------------------------------------------ + */ + +static +void builtin_emotion(ScriptState *st) +{ + ZString str; + dumb_ptr<map_session_data> pl_sd = nullptr; + int type = conv_num(st, &AARG(0)); + if (HARG(1)) { + str = ZString(conv_str(st, &AARG(1))); + CharName player = stringish<CharName>(str); + pl_sd = map_nick2sd(player); + } + if (type < 0 || type > 200) + return; + if (pl_sd != nullptr) + clif_emotion_towards(map_id2bl(st->oid), pl_sd, type); + else if (st->rid && str == "self"_s) + clif_emotion(map_id2sd(st->rid), type); + else + clif_emotion(map_id2bl(st->oid), type); +} + +static +void builtin_mapwarp(ScriptState *st) // Added by RoVeRT +{ + int x, y; + int x0, y0, x1, y1; + + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x0 = 0; + y0 = 0; + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + x1 = m->xs; + y1 = m->ys; + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(1)))); + x = conv_num(st, &AARG(2)); + y = conv_num(st, &AARG(3)); + + map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y), + m, + x0, y0, + x1, y1, + BL::PC); +} + +static +void builtin_mobcount_sub(dumb_ptr<block_list> bl, NpcEvent event, int *c) +{ + if (event == bl->is_mob()->npc_event) + (*c)++; +} + +static +void builtin_mobcount(ScriptState *st) // Added by RoVeRT +{ + int c = 0; + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + ZString event_ = ZString(conv_str(st, &AARG(1))); + NpcEvent event; + extract(event_, &event); + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), + { + push_int<ScriptDataInt>(st->stack, -1); + return; + }); + map_foreachinarea(std::bind(builtin_mobcount_sub, ph::_1, event, &c), + m, + 0, 0, + m->xs, m->ys, + BL::MOB); + + push_int<ScriptDataInt>(st->stack, (c - 1)); + +} + +static +void builtin_marriage(ScriptState *st) +{ + CharName partner = stringish<CharName>(ZString(conv_str(st, &AARG(0)))); + dumb_ptr<map_session_data> sd = script_rid2sd(st); + dumb_ptr<map_session_data> p_sd = map_nick2sd(partner); + + if (sd == nullptr || p_sd == nullptr || pc_marriage(sd, p_sd) < 0) + { + push_int<ScriptDataInt>(st->stack, 0); + return; + } + push_int<ScriptDataInt>(st->stack, 1); +} + +static +void builtin_divorce(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + if (sd == nullptr || pc_divorce(sd) < 0) + { + push_int<ScriptDataInt>(st->stack, 0); + return; + } + + push_int<ScriptDataInt>(st->stack, 1); +} + +/*========================================== + * IDからItem名 + *------------------------------------------ + */ +static +void builtin_getitemname(ScriptState *st) +{ + Option<P<struct item_data>> i_data = None; + struct script_data *data; + + data = &AARG(0); + get_val(st, data); + if (data->is<ScriptDataStr>()) + { + ZString name = ZString(conv_str(st, data)); + i_data = itemdb_searchname(name); + } + else + { + ItemNameId item_id = wrap<ItemNameId>(conv_num(st, data)); + i_data = Some(itemdb_search(item_id)); + } + + RString item_name = i_data.pmd_pget(&item_data::jname).copy_or(stringish<ItemName>("Unknown Item"_s)); + + push_str<ScriptDataStr>(st->stack, item_name); +} + +static +void builtin_getitemlink(ScriptState *st) +{ + struct script_data *data; + AString buf; + data = &AARG(0); + ZString name = conv_str(st, data); + + ItemNameId item_id; + Option<P<struct item_data>> item_data_ = itemdb_searchname(name); + OMATCH_BEGIN (item_data_) + { + OMATCH_CASE_SOME (item_data) + { + buf = STRPRINTF("@@%d|@@"_fmt, item_data->nameid); + } + OMATCH_CASE_NONE () + { + buf = "Unknown Item"_s; + } + } + OMATCH_END (); + push_str<ScriptDataStr>(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<ScriptDataStr>(st->stack, invocation); +} + +static +void builtin_getpartnerid2(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status.partner_id)); +} + +/*========================================== + * PCの所持品情報読み取り + *------------------------------------------ + */ +static +void builtin_getinventorylist(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + int j = 0; + if (!sd) + return; + for (IOff0 i : IOff0::iter()) + { + if (sd->status.inventory[i].nameid + && sd->status.inventory[i].amount > 0) + { + pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_id"_s), j), + unwrap<ItemNameId>(sd->status.inventory[i].nameid)); + pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_amount"_s), j), + sd->status.inventory[i].amount); + pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_equip"_s), j), + static_cast<uint16_t>(sd->status.inventory[i].equip)); + j++; + } + } + pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_count"_s)), j); +} + +static +void builtin_getactivatedpoolskilllist(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + SkillID pool_skills[MAX_SKILL_POOL]; + int skill_pool_size = skill_pool(sd, pool_skills); + int i, count = 0; + + if (!sd) + return; + + for (i = 0; i < skill_pool_size; i++) + { + SkillID skill_id = pool_skills[i]; + + if (sd->status.skill[skill_id].lv) + { + pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count), + static_cast<uint16_t>(skill_id)); + pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count), + sd->status.skill[skill_id].lv); + pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count), + static_cast<uint16_t>(sd->status.skill[skill_id].flags)); + pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count), + skill_name(skill_id)); + ++count; + } + } + pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count); + +} + +static +void builtin_getunactivatedpoolskilllist(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + int i, count = 0; + + if (!sd) + return; + + for (i = 0; i < skill_pool_skills.size(); i++) + { + SkillID skill_id = skill_pool_skills[i]; + + if (sd->status.skill[skill_id].lv + && !bool(sd->status.skill[skill_id].flags & SkillFlags::POOL_ACTIVATED)) + { + pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count), + static_cast<uint16_t>(skill_id)); + pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count), + sd->status.skill[skill_id].lv); + pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count), + static_cast<uint16_t>(sd->status.skill[skill_id].flags)); + pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count), + skill_name(skill_id)); + ++count; + } + } + pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count); +} + +static +void builtin_poolskill(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + SkillID skill_id = SkillID(conv_num(st, &AARG(0))); + + skill_pool_activate(sd, skill_id); + clif_skillinfoblock(sd); + +} + +static +void builtin_unpoolskill(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + SkillID skill_id = SkillID(conv_num(st, &AARG(0))); + + skill_pool_deactivate(sd, skill_id); + clif_skillinfoblock(sd); + +} + +/*========================================== + * NPCから発生するエフェクト + * misceffect(effect, [target]) + * + * effect The effect type/ID. + * target The player name or being ID on + * which to display the effect. If not + * specified, it attempts to default to + * the current NPC or invoking PC. + *------------------------------------------ + */ +static +void builtin_misceffect(ScriptState *st) +{ + int type; + BlockId id; + CharName name; + dumb_ptr<block_list> bl = nullptr; + + type = conv_num(st, &AARG(0)); + + if (HARG(1)) + { + struct script_data *sdata = &AARG(1); + + get_val(st, sdata); + + if (sdata->is<ScriptDataStr>()) + name = stringish<CharName>(ZString(conv_str(st, sdata))); + else + id = wrap<BlockId>(conv_num(st, sdata)); + } + + if (name.to__actual()) + { + dumb_ptr<map_session_data> sd = map_nick2sd(name); + if (sd) + bl = sd; + } + else if (id) + bl = map_id2bl(id); + else if (st->oid) + bl = map_id2bl(st->oid); + else + { + dumb_ptr<map_session_data> sd = script_rid2sd(st); + if (sd) + bl = sd; + } + + if (bl) + clif_misceffect(bl, type); + +} + +/*========================================== + * Special effects [Valaris] + *------------------------------------------ + */ +static +void builtin_specialeffect(ScriptState *st) +{ + dumb_ptr<block_list> bl = map_id2bl(st->oid); + + if (bl == nullptr) + return; + + clif_specialeffect(bl, + conv_num(st, + &AARG(0)), + 0); + +} + +static +void builtin_specialeffect2(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + if (sd == nullptr) + return; + + clif_specialeffect(sd, + conv_num(st, + &AARG(0)), + 0); + +} + +/*========================================== + * Nude [Valaris] + *------------------------------------------ + */ + +static +void builtin_nude(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + if (sd == nullptr) + return; + + for (EQUIP i : EQUIPs) + { + IOff0 idx = sd->equip_index_maybe[i]; + if (idx.ok()) + pc_unequipitem(sd, idx, CalcStatus::LATER); + } + pc_calcstatus(sd, 0); + +} + +/*========================================== + * UnequipById [Freeyorp] + *------------------------------------------ + */ + +static +void builtin_unequipbyid(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + if (sd == nullptr) + return; + + EQUIP slot_id = EQUIP(conv_num(st, &AARG(0))); + + if (slot_id >= EQUIP() && slot_id < EQUIP::COUNT) + { + IOff0 idx = sd->equip_index_maybe[slot_id]; + if (idx.ok()) + pc_unequipitem(sd, idx, CalcStatus::LATER); + } + + pc_calcstatus(sd, 0); + +} + +/*========================================== + * npcwarp [remoitnane] + * Move NPC to a new position on the same map. + *------------------------------------------ + */ +static +void builtin_npcwarp(ScriptState *st) +{ + int x, y; + dumb_ptr<npc_data> nd = nullptr; + + x = conv_num(st, &AARG(0)); + y = conv_num(st, &AARG(1)); + NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(2)))); + nd = npc_name2id(npc); + + if (!nd) + { + PRINTF("builtin_npcwarp: no such npc: '%s'\n"_fmt, npc); + return; + } + + P<map_local> m = nd->bl_m; + + /* Crude sanity checks. */ + if (!nd->bl_prev + || x < 0 || x > m->xs -1 + || y < 0 || y > m->ys - 1) + return; + + npc_enable(npc, 0); + map_delblock(nd); /* [Freeyorp] */ + nd->bl_x = x; + nd->bl_y = y; + map_addblock(nd); + npc_enable(npc, 1); + +} + +/*========================================== + * npcareawarp [remoitnane] [wushin] + * Move NPC to a new area on the same map. + *------------------------------------------ + */ +static +void builtin_npcareawarp(ScriptState *st) +{ + int x0, y0, x1, y1, x, y, max, cb, lx = -1, ly = -1, j = 0; + dumb_ptr<npc_data> nd = nullptr; + + NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(5)))); + nd = npc_name2id(npc); + + x0 = conv_num(st, &AARG(0)); + y0 = conv_num(st, &AARG(1)); + x1 = conv_num(st, &AARG(2)); + y1 = conv_num(st, &AARG(3)); + cb = conv_num(st, &AARG(4)); + + if (!nd) + { + PRINTF("builtin_npcareawarp: no such npc: '%s'\n"_fmt, npc); + return; + } + + max = (y1 - y0 + 1) * (x1 - x0 + 1) * 3; + if (max > 1000) + max = 1000; + + P<map_local> m = nd->bl_m; + if (cb) { + do + { + x = random_::in(x0, x1); + y = random_::in(y0, y1); + } + while (bool(map_getcell(m, x, y) & MapCell::UNWALKABLE) + && (++j) < max); + if (j >= max) + { + if (lx >= 0) + { // Since reference went wrong, the place which boiled before is used. + x = lx; + y = ly; + } + else + return; // Since reference of the place which boils first went wrong, it stops. + } + } + else + x = random_::in(x0, x1); + y = random_::in(y0, y1); + + npc_enable(npc, 0); + map_delblock(nd); /* [Freeyorp] */ + nd->bl_x = x; + nd->bl_y = y; + map_addblock(nd); + npc_enable(npc, 1); + +} + +/*========================================== + * message [MouseJstr] + *------------------------------------------ + */ + +static +void builtin_message(ScriptState *st) +{ + CharName player = stringish<CharName>(ZString(conv_str(st, &AARG(0)))); + ZString msg = ZString(conv_str(st, &AARG(1))); + + dumb_ptr<map_session_data> pl_sd = map_nick2sd(player); + if (pl_sd == nullptr) + return; + clif_displaymessage(pl_sd->sess, msg); + +} + +/*========================================== + * npctalk (sends message to surrounding + * area) [Valaris] + *------------------------------------------ + */ + +static +void builtin_npctalk(ScriptState *st) +{ + dumb_ptr<npc_data> nd = map_id_is_npc(st->oid); + RString str = conv_str(st, &AARG(0)); + + if (nd) + { + clif_message(nd, str); + } +} + +/*========================================== + * getlook char info. getlook(arg) + *------------------------------------------ + */ +static +void builtin_getlook(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + LOOK type = LOOK(conv_num(st, &AARG(0))); + int val = -1; + switch (type) + { + case LOOK::HAIR: //1 + val = sd->status.hair; + break; + case LOOK::WEAPON: //2 + val = static_cast<uint16_t>(sd->status.weapon); + break; + case LOOK::HEAD_BOTTOM: //3 + val = unwrap<ItemNameId>(sd->status.head_bottom); + break; + case LOOK::HEAD_TOP: //4 + val = unwrap<ItemNameId>(sd->status.head_top); + break; + case LOOK::HEAD_MID: //5 + val = unwrap<ItemNameId>(sd->status.head_mid); + break; + case LOOK::HAIR_COLOR: //6 + val = sd->status.hair_color; + break; + case LOOK::CLOTHES_COLOR: //7 + val = sd->status.clothes_color; + break; + case LOOK::SHIELD: //8 + val = unwrap<ItemNameId>(sd->status.shield); + break; + case LOOK::SHOES: //9 + break; + } + + push_int<ScriptDataInt>(st->stack, val); +} + +/*========================================== + * get char save point. argument: 0- map name, 1- x, 2- y + *------------------------------------------ +*/ +static +void builtin_getsavepoint(ScriptState *st) +{ + int x, y, type; + dumb_ptr<map_session_data> sd; + + sd = script_rid2sd(st); + + type = conv_num(st, &AARG(0)); + + x = sd->status.save_point.x; + y = sd->status.save_point.y; + switch (type) + { + case 0: + { + RString mapname = sd->status.save_point.map_; + push_str<ScriptDataStr>(st->stack, mapname); + } + break; + case 1: + push_int<ScriptDataInt>(st->stack, x); + break; + case 2: + push_int<ScriptDataInt>(st->stack, y); + break; + } +} + +/*========================================== + * areatimer + *------------------------------------------ + */ +static +void builtin_areatimer_sub(dumb_ptr<block_list> bl, interval_t tick, NpcEvent event) +{ + pc_addeventtimer(bl->is_player(), tick, event); +} + +static +void builtin_areatimer(ScriptState *st) +{ + int x0, y0, x1, y1; + + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x0 = conv_num(st, &AARG(1)); + y0 = conv_num(st, &AARG(2)); + x1 = conv_num(st, &AARG(3)); + y1 = conv_num(st, &AARG(4)); + interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(5))); + ZString event_ = ZString(conv_str(st, &AARG(6))); + NpcEvent event; + extract(event_, &event); + + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + + map_foreachinarea(std::bind(builtin_areatimer_sub, ph::_1, tick, event), + m, + x0, y0, + x1, y1, + BL::PC); +} + +/*========================================== + * Check whether the PC is in the specified rectangle + *------------------------------------------ + */ +static +void builtin_isin(ScriptState *st) +{ + int x1, y1, x2, y2; + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + x1 = conv_num(st, &AARG(1)); + y1 = conv_num(st, &AARG(2)); + x2 = conv_num(st, &AARG(3)); + y2 = conv_num(st, &AARG(4)); + + if (!sd) + return; + + push_int<ScriptDataInt>(st->stack, + (sd->bl_x >= x1 && sd->bl_x <= x2) + && (sd->bl_y >= y1 && sd->bl_y <= y2) + && (str == sd->bl_m->name_)); +} + +/*========================================== + * Check whether the coords are collision + *------------------------------------------ + */ +static +void builtin_iscollision(ScriptState *st) +{ + int x, y; + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + + x = conv_num(st, &AARG(1)); + y = conv_num(st, &AARG(2)); + + push_int<ScriptDataInt>(st->stack, + bool(map_getcell(m, x, y) & MapCell::UNWALKABLE)); +} + +// Trigger the shop on a (hopefully) nearby shop NPC +static +void builtin_shop(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + dumb_ptr<npc_data> nd; + + if (!sd) + return; + + NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0)))); + nd = npc_name2id(name); + if (!nd) + { + PRINTF("builtin_shop: no such npc: '%s'\n"_fmt, name); + return; + } + + builtin_close(st); + clif_npcbuysell(sd, nd->bl_id); +} + +/*========================================== + * Check whether the PC is dead + *------------------------------------------ + */ +static +void builtin_isdead(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + push_int<ScriptDataInt>(st->stack, pc_isdead(sd)); +} + +/*======================================== + * Changes a NPC name, and sprite + *---------------------------------------- + */ +static +void builtin_fakenpcname(ScriptState *st) +{ + NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0)))); + NpcName newname = stringish<NpcName>(ZString(conv_str(st, &AARG(1)))); + Species newsprite = wrap<Species>(static_cast<uint16_t>(conv_num(st, &AARG(2)))); + dumb_ptr<npc_data> nd = npc_name2id(name); + if (!nd) + { + PRINTF("builtin_fakenpcname: no such npc: '%s'\n"_fmt, name); + return; + } + nd->name = newname; + nd->npc_class = newsprite; + + // Refresh this npc + npc_enable(name, 0); + npc_enable(name, 1); + +} + +/*============================ + * Gets the PC's x pos + *---------------------------- + */ +static +void builtin_getx(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + push_int<ScriptDataInt>(st->stack, sd->bl_x); +} + +/*============================ + * Gets the PC's y pos + *---------------------------- + */ +static +void builtin_gety(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + push_int<ScriptDataInt>(st->stack, sd->bl_y); +} + +/* + * Get the PC's current map's name + */ +static +void builtin_getmap(ScriptState *st) +{ + dumb_ptr<map_session_data> sd = script_rid2sd(st); + + push_str<ScriptDataStr>(st->stack, sd->bl_m->name_); +} + +/* + * Get the NPC's info + */ +static +void builtin_strnpcinfo(ScriptState *st) +{ + int num = conv_num(st, &AARG(0)); + RString name; + dumb_ptr<npc_data> nd; + + if(HARG(1)){ + NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(1)))); + nd = npc_name2id(npc); + if (!nd) + { + PRINTF("builtin_strnpcinfo: no such npc: '%s'\n"_fmt, npc); + return; + } + } else { + nd = map_id_is_npc(st->oid); + } + + switch(num) + { + case 0: + name = nd->name; + break; + case 1: + name = nd->name.xislice_h(std::find(nd->name.begin(), nd->name.end(), '#')); + break; + case 2: + name = nd->name.xislice_t(std::find(nd->name.begin(), nd->name.end(), '#')); + break; + case 3: + name = nd->bl_m->name_; + break; + } + + push_str<ScriptDataStr>(st->stack, name); +} + +/*============================ + * Gets the NPC's x pos + *---------------------------- + */ +static +void builtin_getnpcx(ScriptState *st) +{ + dumb_ptr<npc_data> nd; + + if(HARG(0)){ + NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0)))); + nd = npc_name2id(name); + if (!nd) + { + PRINTF("builtin_getnpcx: no such npc: '%s'\n"_fmt, name); + return; + } + } else { + nd = map_id_is_npc(st->oid); + } + + push_int<ScriptDataInt>(st->stack, nd->bl_x); +} + +/*============================ + * Gets the NPC's y pos + *---------------------------- + */ +static +void builtin_getnpcy(ScriptState *st) +{ + dumb_ptr<npc_data> nd; + + if(HARG(0)){ + NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0)))); + nd = npc_name2id(name); + if (!nd) + { + PRINTF("builtin_getnpcy: no such npc: '%s'\n"_fmt, name); + return; + } + } else { + nd = map_id_is_npc(st->oid); + } + + push_int<ScriptDataInt>(st->stack, nd->bl_y); +} + +static +void builtin_mapexit(ScriptState *) +{ + runflag = 0; +} + + +#define BUILTIN(func, args, ret) \ +{builtin_##func, #func ## _s, args, ret} + +BuiltinFunction builtin_functions[] = +{ + BUILTIN(mes, "s"_s, '\0'), + BUILTIN(goto, "L"_s, '\0'), + BUILTIN(callfunc, "F"_s, '\0'), + BUILTIN(callsub, "L"_s, '\0'), + BUILTIN(return, ""_s, '\0'), + BUILTIN(next, ""_s, '\0'), + BUILTIN(close, ""_s, '\0'), + BUILTIN(close2, ""_s, '\0'), + BUILTIN(menu, "sL**"_s, '\0'), + BUILTIN(rand, "i?"_s, 'i'), + BUILTIN(isat, "Mxy"_s, 'i'), + BUILTIN(warp, "Mxy"_s, '\0'), + BUILTIN(areawarp, "MxyxyMxy"_s, '\0'), + BUILTIN(heal, "ii?"_s, '\0'), + BUILTIN(input, "N"_s, '\0'), + BUILTIN(if, "iF*"_s, '\0'), + BUILTIN(set, "Ne"_s, '\0'), + BUILTIN(setarray, "Ne*"_s, '\0'), + BUILTIN(cleararray, "Nei"_s, '\0'), + BUILTIN(getarraysize, "N"_s, 'i'), + BUILTIN(getelementofarray, "Ni"_s, '.'), + BUILTIN(setlook, "ii"_s, '\0'), + BUILTIN(countitem, "I"_s, 'i'), + BUILTIN(checkweight, "Ii"_s, 'i'), + BUILTIN(getitem, "Ii??"_s, '\0'), + BUILTIN(makeitem, "IiMxy"_s, '\0'), + BUILTIN(delitem, "Ii"_s, '\0'), + BUILTIN(getcharid, "i?"_s, 'i'), + BUILTIN(strcharinfo, "i"_s, 's'), + BUILTIN(getequipid, "i"_s, 'i'), + BUILTIN(getequipname, "i"_s, 's'), + BUILTIN(bonus, "ii"_s, '\0'), + BUILTIN(bonus2, "iii"_s, '\0'), + BUILTIN(skill, "ii?"_s, '\0'), + BUILTIN(setskill, "ii"_s, '\0'), + BUILTIN(getskilllv, "i"_s, 'i'), + BUILTIN(getgmlevel, ""_s, 'i'), + BUILTIN(end, ""_s, '\0'), + BUILTIN(getopt2, ""_s, 'i'), + BUILTIN(setopt2, "i"_s, '\0'), + BUILTIN(savepoint, "Mxy"_s, '\0'), + BUILTIN(gettimetick, "i"_s, 'i'), + BUILTIN(gettime, "i"_s, 'i'), + BUILTIN(openstorage, ""_s, '\0'), + BUILTIN(getexp, "ii"_s, '\0'), + BUILTIN(monster, "Mxysmi?"_s, '\0'), + BUILTIN(areamonster, "Mxyxysmi?"_s, '\0'), + BUILTIN(killmonster, "ME"_s, '\0'), + BUILTIN(donpcevent, "E"_s, '\0'), + BUILTIN(addtimer, "tE"_s, '\0'), + BUILTIN(initnpctimer, "?"_s, '\0'), + BUILTIN(startnpctimer, "?"_s, '\0'), + BUILTIN(stopnpctimer, "?"_s, '\0'), + BUILTIN(getnpctimer, "i?"_s, 'i'), + BUILTIN(setnpctimer, "i?"_s, '\0'), + BUILTIN(announce, "si"_s, '\0'), + BUILTIN(mapannounce, "Msi"_s, '\0'), + BUILTIN(getusers, "i"_s, 'i'), + BUILTIN(getmapusers, "M"_s, 'i'), + BUILTIN(getareausers, "Mxyxy?"_s, 'i'), + BUILTIN(getareadropitem, "Mxyxyi?"_s, 'i'), + BUILTIN(enablenpc, "s"_s, '\0'), + BUILTIN(disablenpc, "s"_s, '\0'), + BUILTIN(sc_start, "iTi?"_s, '\0'), + BUILTIN(sc_end, "i"_s, '\0'), + BUILTIN(sc_check, "i"_s, 'i'), + BUILTIN(debugmes, "s"_s, '\0'), + BUILTIN(wgm, "s"_s, '\0'), + BUILTIN(gmlog, "s"_s, '\0'), + BUILTIN(resetstatus, ""_s, '\0'), + BUILTIN(attachrid, "i"_s, 'i'), + BUILTIN(detachrid, ""_s, '\0'), + BUILTIN(isloggedin, "i"_s, 'i'), + BUILTIN(setmapflag, "Mi"_s, '\0'), + BUILTIN(removemapflag, "Mi"_s, '\0'), + BUILTIN(getmapflag, "Mi"_s, 'i'), + BUILTIN(pvpon, "M"_s, '\0'), + BUILTIN(pvpoff, "M"_s, '\0'), + BUILTIN(setpvpchannel, "i"_s, '\0'), + BUILTIN(getpvpflag, "i"_s, 'i'), + BUILTIN(emotion, "i?"_s, '\0'), + BUILTIN(mapwarp, "MMxy"_s, '\0'), + BUILTIN(mobcount, "ME"_s, 'i'), + BUILTIN(marriage, "P"_s, 'i'), + BUILTIN(divorce, ""_s, 'i'), + BUILTIN(getitemname, "I"_s, 's'), + BUILTIN(getitemlink, "I"_s, 's'), + BUILTIN(getspellinvocation, "s"_s, 's'), + BUILTIN(getpartnerid2, ""_s, 'i'), + BUILTIN(getinventorylist, ""_s, '\0'), + BUILTIN(getactivatedpoolskilllist, ""_s, '\0'), + BUILTIN(getunactivatedpoolskilllist, ""_s, '\0'), + BUILTIN(poolskill, "i"_s, '\0'), + BUILTIN(unpoolskill, "i"_s, '\0'), + BUILTIN(misceffect, "i?"_s, '\0'), + BUILTIN(specialeffect, "i"_s, '\0'), + BUILTIN(specialeffect2, "i"_s, '\0'), + BUILTIN(nude, ""_s, '\0'), + BUILTIN(unequipbyid, "i"_s, '\0'), + BUILTIN(npcwarp, "xys"_s, '\0'), + BUILTIN(npcareawarp, "xyxyis"_s, '\0'), + BUILTIN(message, "Ps"_s, '\0'), + BUILTIN(npctalk, "s"_s, '\0'), + BUILTIN(getlook, "i"_s, 'i'), + BUILTIN(getsavepoint, "i"_s, '.'), + BUILTIN(areatimer, "MxyxytE"_s, '\0'), + BUILTIN(isin, "Mxyxy"_s, 'i'), + BUILTIN(iscollision, "Mxy"_s, 'i'), + BUILTIN(shop, "s"_s, '\0'), + BUILTIN(isdead, ""_s, 'i'), + BUILTIN(fakenpcname, "ssi"_s, '\0'), + BUILTIN(getx, ""_s, 'i'), + BUILTIN(gety, ""_s, 'i'), + BUILTIN(getnpcx, "?"_s, 'i'), + BUILTIN(getnpcy, "?"_s, 'i'), + BUILTIN(strnpcinfo, "i?"_s, 's'), + BUILTIN(getmap, ""_s, 's'), + BUILTIN(mapexit, ""_s, '\0'), + BUILTIN(freeloop, "i"_s, '\0'), + {nullptr, ""_s, ""_s, '\0'}, +}; +} // namespace map +} // namespace tmwa diff --git a/src/map/script-fun.hpp b/src/map/script-fun.hpp new file mode 100644 index 0000000..81d68fe --- /dev/null +++ b/src/map/script-fun.hpp @@ -0,0 +1,41 @@ +#pragma once +// script-fun.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include "../strings/literal.hpp" + +namespace tmwa +{ +namespace map +{ +struct BuiltinFunction +{ + void (*func)(ScriptState *); + LString name; + LString arg; + char ret; +}; + +extern BuiltinFunction builtin_functions[]; +} // namespace map +} // namespace tmwa diff --git a/src/map/script-parse-internal.hpp b/src/map/script-parse-internal.hpp new file mode 100644 index 0000000..ddaeef0 --- /dev/null +++ b/src/map/script-parse-internal.hpp @@ -0,0 +1,67 @@ +#pragma once +// script-parse-internal.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "script-parse.hpp" +#include "fwd.hpp" + +#include "../strings/rstring.hpp" + + +namespace tmwa +{ +namespace map +{ +enum class StringCode : uint8_t +{ + NOP, POS, INT, PARAM, FUNC, + VARIABLE, +}; +enum class ByteCode : uint8_t +{ + // types and specials + // Note that 'INT' is synthetic, and does not occur in the data stream + NOP, POS, INT, PARAM, FUNC, STR, ARG, + VARIABLE, EOL, + + // unary and binary operators + LOR, LAND, LE, LT, GE, GT, EQ, NE, + XOR, OR, AND, ADD, SUB, MUL, DIV, MOD, + NEG, LNOT, NOT, R_SHIFT, L_SHIFT, + + // additions + // needed because FUNC is used for the actual call + FUNC_REF, +}; + +struct str_data_t +{ + StringCode type; + RString strs; + int backpatch; + int label_; + int val; +}; + +Option<Borrowed<str_data_t>> search_strp(XString p); +Borrowed<str_data_t> add_strp(XString p); +} // namespace map +} // namespace tmwa diff --git a/src/map/script-parse.cpp b/src/map/script-parse.cpp new file mode 100644 index 0000000..fb306c5 --- /dev/null +++ b/src/map/script-parse.cpp @@ -0,0 +1,851 @@ +#include "script-parse-internal.hpp" +// script-parse.cpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011 Chuck Miller +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// Copyright © 2013 wushin +// +// 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 <http://www.gnu.org/licenses/>. + +#include <set> + +#include "../generic/array.hpp" +#include "../generic/db.hpp" +#include "../generic/intern-pool.hpp" + +#include "../strings/rstring.hpp" + +#include "../io/cxxstdio.hpp" + +#include "../mmo/cxxstdio_enums.hpp" + +#include "../ast/script.hpp" + +#include "globals.hpp" +#include "map.t.hpp" +#include "script-buffer.hpp" +#include "script-call.hpp" +#include "script-fun.hpp" + +#include "../poison.hpp" + + +namespace tmwa +{ +namespace map +{ +constexpr bool DEBUG_DISP = false; + +class ScriptBuffer +{ + typedef ZString::iterator ZSit; + + std::vector<ByteCode> script_buf; + RString debug_name; + std::vector<std::pair<ScriptLabel, size_t>> debug_labels; +public: + ScriptBuffer(RString name) : debug_name(std::move(name)) {} + + // construction methods + void add_scriptc(ByteCode a); + void add_scriptb(uint8_t a); + void add_scripti(uint32_t a); + void add_scriptl(Borrowed<str_data_t> a); + void set_label(Borrowed<str_data_t> ld, int pos_); + ZSit parse_simpleexpr(ZSit p); + ZSit parse_subexpr(ZSit p, int limit); + ZSit parse_expr(ZSit p); + ZSit parse_line(ZSit p, bool *canstep); + void parse_script(ZString src, int line, bool implicit_end); + + // consumption methods + ByteCode operator[](size_t i) const { return script_buf[i]; } + ZString get_str(size_t i) const + { + return ZString(strings::really_construct_from_a_pointer, reinterpret_cast<const char *>(&script_buf[i]), nullptr); + } +}; +} // namespace map +} // namespace tmwa + +void std::default_delete<const tmwa::map::ScriptBuffer>::operator()(const tmwa::map::ScriptBuffer *sd) +{ + really_delete1 sd; +} + +namespace tmwa +{ +namespace map +{ +// implemented for script-call.hpp because reasons +ByteCode ScriptPointer::peek() const { return (*TRY_UNWRAP(code, abort()))[pos]; } +ByteCode ScriptPointer::pop() { return (*TRY_UNWRAP(code, abort()))[pos++]; } +ZString ScriptPointer::pops() +{ + ZString rv = TRY_UNWRAP(code, abort())->get_str(pos); + pos += rv.size(); + ++pos; + return rv; +} + +static +struct ScriptConfigParse +{ + static const + int warn_func_no_comma = 1; + static const + int warn_cmd_no_comma = 1; + static const + int warn_func_mismatch_paramnum = 1; + static const + int warn_cmd_mismatch_paramnum = 1; +} script_config; + + +Option<Borrowed<str_data_t>> search_strp(XString p) +{ + return str_datam.search(p); +} + +Borrowed<str_data_t> add_strp(XString p) +{ + Option<P<str_data_t>> rv_ = search_strp(p); + OMATCH_BEGIN_SOME (rv, rv_) + { + return rv; + } + OMATCH_END (); + + RString p2 = p; + P<str_data_t> datum = str_datam.init(p2); + datum->type = StringCode::NOP; + datum->strs = p2; + datum->backpatch = -1; + datum->label_ = -1; + return datum; +} + +/*========================================== + * スクリプトバッファに1バイト書き込む + *------------------------------------------ + */ +void ScriptBuffer::add_scriptc(ByteCode a) +{ + script_buf.push_back(a); +} + +/*========================================== + * スクリプトバッファにデータタイプを書き込む + *------------------------------------------ + */ +void ScriptBuffer::add_scriptb(uint8_t a) +{ + add_scriptc(static_cast<ByteCode>(a)); +} + +/*========================================== + * スクリプトバッファに整数を書き込む + *------------------------------------------ + */ +void ScriptBuffer::add_scripti(uint32_t a) +{ + while (a >= 0x40) + { + add_scriptb(a | 0xc0); + a = (a - 0x40) >> 6; + } + add_scriptb(a | 0x80); +} + +/*========================================== + * スクリプトバッファにラベル/変数/関数を書き込む + *------------------------------------------ + */ +// 最大16Mまで +void ScriptBuffer::add_scriptl(P<str_data_t> ld) +{ + int backpatch = ld->backpatch; + + switch (ld->type) + { + case StringCode::POS: + add_scriptc(ByteCode::POS); + add_scriptb(static_cast<uint8_t>(ld->label_)); + add_scriptb(static_cast<uint8_t>(ld->label_ >> 8)); + add_scriptb(static_cast<uint8_t>(ld->label_ >> 16)); + break; + case StringCode::NOP: + // need to set backpatch, because it might become a label later + add_scriptc(ByteCode::VARIABLE); + ld->backpatch = script_buf.size(); + add_scriptb(static_cast<uint8_t>(backpatch)); + add_scriptb(static_cast<uint8_t>(backpatch >> 8)); + add_scriptb(static_cast<uint8_t>(backpatch >> 16)); + break; + case StringCode::INT: + add_scripti(ld->val); + break; + case StringCode::FUNC: + add_scriptc(ByteCode::FUNC_REF); + add_scriptb(static_cast<uint8_t>(ld->val)); + add_scriptb(static_cast<uint8_t>(ld->val >> 8)); + add_scriptb(static_cast<uint8_t>(ld->val >> 16)); + break; + case StringCode::PARAM: + add_scriptc(ByteCode::PARAM); + add_scriptb(static_cast<uint8_t>(ld->val)); + add_scriptb(static_cast<uint8_t>(ld->val >> 8)); + add_scriptb(static_cast<uint8_t>(ld->val >> 16)); + break; + default: + abort(); + } +} + +/*========================================== + * ラベルを解決する + *------------------------------------------ + */ +void ScriptBuffer::set_label(Borrowed<str_data_t> ld, int pos_) +{ + int next; + + ld->type = StringCode::POS; + ld->label_ = pos_; + for (int i = ld->backpatch; i >= 0 && i != 0x00ffffff; i = next) + { + next = 0; + // woot! no longer endian-dependent! + next |= static_cast<uint8_t>(script_buf[i + 0]) << 0; + next |= static_cast<uint8_t>(script_buf[i + 1]) << 8; + next |= static_cast<uint8_t>(script_buf[i + 2]) << 16; + script_buf[i - 1] = ByteCode::POS; + script_buf[i] = static_cast<ByteCode>(pos_); + script_buf[i + 1] = static_cast<ByteCode>(pos_ >> 8); + script_buf[i + 2] = static_cast<ByteCode>(pos_ >> 16); + } +} + +/*========================================== + * スペース/コメント読み飛ばし + *------------------------------------------ + */ +static +ZString::iterator skip_space(ZString::iterator p) +{ + while (1) + { + while (isspace(*p)) + p++; + if (p[0] == '/' && p[1] == '/') + { + while (*p && *p != '\n') + p++; + } + else if (p[0] == '/' && p[1] == '*') + { + p++; + while (*p && (p[-1] != '*' || p[0] != '/')) + p++; + if (*p) + p++; + } + else + break; + } + return p; +} + +/*========================================== + * 1単語スキップ + *------------------------------------------ + */ +static +ZString::iterator skip_word(ZString::iterator p) +{ + // prefix + if (*p == '$') + p++; // MAP鯖内共有変数用 + if (*p == '@') + p++; // 一時的変数用(like weiss) + if (*p == '#') + p++; // account変数用 + if (*p == '#') + p++; // ワールドaccount変数用 + + while (isalnum(*p) || *p == '_') + p++; + + // postfix + if (*p == '$') + p++; // 文字列変数 + + return p; +} + +/*========================================== + * エラーメッセージ出力 + *------------------------------------------ + */ +static +void disp_error_message(ZString mes, ZString::iterator pos_) +{ + script_errors++; + + assert (startptr.begin() <= pos_ && pos_ <= startptr.end()); + + int line; + ZString::iterator p; + + for (line = startline, p = startptr.begin(); p != startptr.end(); line++) + { + ZString::iterator linestart = p; + ZString::iterator lineend = std::find(p, startptr.end(), '\n'); + if (pos_ < lineend) + { + PRINTF("\n%s\nline %d : "_fmt, mes, line); + for (int i = 0; linestart + i != lineend; i++) + { + if (linestart + i != pos_) + PRINTF("%c"_fmt, linestart[i]); + else + PRINTF("\'%c\'"_fmt, linestart[i]); + } + PRINTF("\a\n"_fmt); + return; + } + p = lineend + 1; + } +} + +/*========================================== + * 項の解析 + *------------------------------------------ + */ +ZString::iterator ScriptBuffer::parse_simpleexpr(ZString::iterator p) +{ + p = skip_space(p); + + if (*p == ';' || *p == ',') + { + disp_error_message("unexpected expr end"_s, p); + exit(1); + } + if (*p == '(') + { + + p = parse_subexpr(p + 1, -1); + p = skip_space(p); + if ((*p++) != ')') + { + disp_error_message("unmatch ')'"_s, p); + exit(1); + } + } + else if (isdigit(*p) || ((*p == '-' || *p == '+') && isdigit(p[1]))) + { + char *np; + int i = strtoul(&*p, &np, 0); + add_scripti(i); + p += np - &*p; + } + else if (*p == '"') + { + add_scriptc(ByteCode::STR); + p++; + while (*p && *p != '"') + { + if (*p == '\\') + p++; + else if (*p == '\n') + { + disp_error_message("unexpected newline @ string"_s, p); + exit(1); + } + add_scriptb(*p++); + } + if (!*p) + { + disp_error_message("unexpected eof @ string"_s, p); + exit(1); + } + add_scriptb(0); + p++; //'"' + } + else + { + // label , register , function etc + ZString::iterator p2 = skip_word(p); + if (p2 == p) + { + disp_error_message("unexpected character"_s, p); + exit(1); + } + XString word(&*p, &*p2, nullptr); + if (word.startswith("On"_s) || word.startswith("L_"_s) || word.startswith("S_"_s)) + probable_labels.insert(stringish<ScriptLabel>(word)); + if (parse_cmd_if && (word == "callsub"_s || word == "callfunc"_s || word == "return"_s)) + { + disp_error_message("Sorry, callsub/callfunc/return have never worked properly in an if statement."_s, p); + } + P<str_data_t> ld = add_strp(word); + + parse_cmdp = Some(ld); // warn_*_mismatch_paramnumのために必要 + // why not just check l->str == "if"_s or std::string(p, p2) == "if"_s? + if (Some(ld) == search_strp("if"_s)) // warn_cmd_no_commaのために必要 + parse_cmd_if++; + p = p2; + + if (ld->type != StringCode::FUNC && *p == '[') + { + // array(name[i] => getelementofarray(name,i) ) + add_scriptl(TRY_UNWRAP(search_strp("getelementofarray"_s), abort())); + add_scriptc(ByteCode::ARG); + add_scriptl(ld); + p = parse_subexpr(p + 1, -1); + p = skip_space(p); + if (*p != ']') + { + disp_error_message("unmatch ']'"_s, p); + exit(1); + } + p++; + add_scriptc(ByteCode::FUNC); + } + else + add_scriptl(ld); + + } + + return p; +} + +/*========================================== + * 式の解析 + *------------------------------------------ + */ +ZString::iterator ScriptBuffer::parse_subexpr(ZString::iterator p, int limit) +{ + ByteCode op; + int opl, len; + + p = skip_space(p); + + if (*p == '-') + { + ZString::iterator tmpp = skip_space(p + 1); + if (*tmpp == ';' || *tmpp == ',') + { + --script_errors; disp_error_message("deprecated: implicit 'next statement' label"_s, p); + add_scriptl(borrow(LABEL_NEXTLINE_)); + p++; + return p; + } + } + ZString::iterator tmpp = p; + if ((op = ByteCode::NEG, *p == '-') || (op = ByteCode::LNOT, *p == '!') + || (op = ByteCode::NOT, *p == '~')) + { + p = parse_subexpr(p + 1, 100); + add_scriptc(op); + } + else + p = parse_simpleexpr(p); + p = skip_space(p); + while (((op = ByteCode::ADD, opl = 6, len = 1, *p == '+') || + (op = ByteCode::SUB, opl = 6, len = 1, *p == '-') || + (op = ByteCode::MUL, opl = 7, len = 1, *p == '*') || + (op = ByteCode::DIV, opl = 7, len = 1, *p == '/') || + (op = ByteCode::MOD, opl = 7, len = 1, *p == '%') || + (op = ByteCode::FUNC, opl = 8, len = 1, *p == '(') || + (op = ByteCode::LAND, opl = 1, len = 2, *p == '&' && p[1] == '&') || + (op = ByteCode::AND, opl = 5, len = 1, *p == '&') || + (op = ByteCode::LOR, opl = 0, len = 2, *p == '|' && p[1] == '|') || + (op = ByteCode::OR, opl = 4, len = 1, *p == '|') || + (op = ByteCode::XOR, opl = 3, len = 1, *p == '^') || + (op = ByteCode::EQ, opl = 2, len = 2, *p == '=' && p[1] == '=') || + (op = ByteCode::NE, opl = 2, len = 2, *p == '!' && p[1] == '=') || + (op = ByteCode::R_SHIFT, opl = 5, len = 2, *p == '>' && p[1] == '>') || + (op = ByteCode::GE, opl = 2, len = 2, *p == '>' && p[1] == '=') || + (op = ByteCode::GT, opl = 2, len = 1, *p == '>') || + (op = ByteCode::L_SHIFT, opl = 5, len = 2, *p == '<' && p[1] == '<') || + (op = ByteCode::LE, opl = 2, len = 2, *p == '<' && p[1] == '=') || + (op = ByteCode::LT, opl = 2, len = 1, *p == '<')) && opl > limit) + { + p += len; + if (op == ByteCode::FUNC) + { + int i = 0; + P<str_data_t> funcp = TRY_UNWRAP(parse_cmdp, abort()); + Array<ZString::iterator, 128> plist; + + if (funcp->type != StringCode::FUNC) + { + disp_error_message("expect function"_s, tmpp); + exit(0); + } + + add_scriptc(ByteCode::ARG); + while (*p && *p != ')' && i < 128) + { + plist[i] = p; + p = parse_subexpr(p, -1); + p = skip_space(p); + if (*p == ',') + p++; + else if (*p != ')' && script_config.warn_func_no_comma) + { + disp_error_message("expect ',' or ')' at func params"_s, + p); + } + p = skip_space(p); + i++; + } + if (i == 128) + { + disp_error_message("PANIC: unrecoverable error in function argument list"_s, p); + abort(); + } + plist[i] = p; + if (*p != ')') + { + disp_error_message("func request '(' ')'"_s, p); + exit(1); + } + p++; + + if (funcp->type == StringCode::FUNC + && script_config.warn_func_mismatch_paramnum) + { + ZString arg = builtin_functions[funcp->val].arg; + int j = 0; + // TODO handle ? and multiple * correctly + for (j = 0; arg[j]; j++) + if (arg[j] == '*' || arg[j] == '?') + break; + if ((arg[j] == 0 && i != j) || ((arg[j] == '*' || arg[j] == '?') && i < j)) + { + disp_error_message("illegal number of parameters"_s, + plist[std::min(i, j)]); + } + if (!builtin_functions[funcp->val].ret) + { + disp_error_message("statement in function context"_s, tmpp); + } + } + } + else // not op == ByteCode::FUNC + { + p = parse_subexpr(p, opl); + } + add_scriptc(op); + p = skip_space(p); + } + return p; /* return first untreated operator */ +} + +/*========================================== + * 式の評価 + *------------------------------------------ + */ +ZString::iterator ScriptBuffer::parse_expr(ZString::iterator p) +{ + switch (*p) + { + case ')': + case ';': + case ':': + case '[': + case ']': + case '}': + disp_error_message("unexpected char"_s, p); + exit(1); + } + p = parse_subexpr(p, -1); + return p; +} + +/*========================================== + * 行の解析 + *------------------------------------------ + */ +ZString::iterator ScriptBuffer::parse_line(ZString::iterator p, bool *can_step) +{ + int i = 0; + Array<ZString::iterator, 128> plist; + + p = skip_space(p); + if (*p == ';') + { + disp_error_message("Double semi-colon"_s, p); + ++p; + return p; + } + + parse_cmd_if = 0; // warn_cmd_no_commaのために必要 + + // 最初は関数名 + ZString::iterator p2 = p; + p = parse_simpleexpr(p); + p = skip_space(p); + + P<str_data_t> cmd = TRY_UNWRAP(parse_cmdp, abort()); + if (cmd->type != StringCode::FUNC) + { + disp_error_message("expect command"_s, p2); + } + + { + // TODO should be LString, but no heterogenous lookup yet + static + std::set<ZString> terminators = + { + "goto"_s, + "return"_s, + "close"_s, + "menu"_s, + "end"_s, + "mapexit"_s, + "shop"_s, + }; + *can_step = terminators.count(cmd->strs) == 0; + } + + add_scriptc(ByteCode::ARG); + while (*p && *p != ';' && i < 128) + { + plist[i] = p; + + p = parse_expr(p); + p = skip_space(p); + // 引数区切りの,処理 + if (*p == ',') + p++; + else if (*p != ';' && script_config.warn_cmd_no_comma + && parse_cmd_if * 2 <= i) + { + disp_error_message("expect ',' or ';' at cmd params"_s, p); + } + p = skip_space(p); + i++; + } + if (i == 128) + { + disp_error_message("PANIC: unknown error in command argument list"_s, p); + abort(); + } + plist[i] = p; + if (*(p++) != ';') + { + disp_error_message("need ';'"_s, p); + exit(1); + } + add_scriptc(ByteCode::FUNC); + + if (cmd->type == StringCode::FUNC + && script_config.warn_cmd_mismatch_paramnum) + { + ZString arg = builtin_functions[cmd->val].arg; + int j = 0; + // TODO see above + for (j = 0; arg[j]; j++) + if (arg[j] == '*' || arg[j] == '?') + break; + if ((arg[j] == 0 && i != j) || ((arg[j] == '*' || arg[j] == '?') && i < j)) + { + disp_error_message("illegal number of parameters"_s, + plist[std::min(i, j)]); + } + if (builtin_functions[cmd->val].ret) + { + disp_error_message("function in statement context"_s, p2); + } + } + + return p; +} + +/*========================================== + * 組み込み関数の追加 + *------------------------------------------ + */ +static +void add_builtin_functions(void) +{ + for (int i = 0; builtin_functions[i].func; i++) + { + P<str_data_t> n = add_strp(builtin_functions[i].name); + n->type = StringCode::FUNC; + n->val = i; + } +} + +std::unique_ptr<const ScriptBuffer> compile_script(RString debug_name, const ast::script::ScriptBody& body, bool implicit_end) +{ + auto script_buf = make_unique<ScriptBuffer>(std::move(debug_name)); + script_buf->parse_script(body.braced_body, body.span.begin.line, implicit_end); + return std::move(script_buf); +} + +/*========================================== + * スクリプトの解析 + *------------------------------------------ + */ +void ScriptBuffer::parse_script(ZString src, int line, bool implicit_end) +{ + static int first = 1; + + if (first) + { + add_builtin_functions(); + } + first = 0; + LABEL_NEXTLINE_.type = StringCode::NOP; + LABEL_NEXTLINE_.backpatch = -1; + LABEL_NEXTLINE_.label_ = -1; + for (auto& pair : str_datam) + { + str_data_t& dit = pair.second; + if (dit.type == StringCode::POS || dit.type == StringCode::VARIABLE) + { + dit.type = StringCode::NOP; + dit.backpatch = -1; + dit.label_ = -1; + } + } + + // 外部用label dbの初期化 + scriptlabel_db.clear(); + + // for error message + startptr = src; + startline = line; + + bool can_step = true; + + ZString::iterator p = src.begin(); + p = skip_space(p); + if (*p != '{') + { + disp_error_message("not found '{'"_s, p); + abort(); + } + for (p++; *p && *p != '}';) + { + p = skip_space(p); + if (*skip_space(skip_word(p)) == ':') + { + if (can_step) + { + --script_errors; disp_error_message("deprecated: implicit fallthrough"_s, p); + } + can_step = true; + + ZString::iterator tmpp = skip_word(p); + XString str(&*p, &*tmpp, nullptr); + P<str_data_t> ld = add_strp(str); + bool e1 = ld->type != StringCode::NOP; + bool e2 = ld->type == StringCode::POS; + bool e3 = ld->label_ != -1; + assert (e1 == e2 && e2 == e3); + if (e3) + { + disp_error_message("dup label "_s, p); + exit(1); + } + set_label(ld, script_buf.size()); + scriptlabel_db.insert(stringish<ScriptLabel>(str), script_buf.size()); + debug_labels.push_back(std::make_pair(stringish<ScriptLabel>(str), script_buf.size())); + p = tmpp + 1; + continue; + } + + if (!can_step) + { + --script_errors; disp_error_message("deprecated: unreachable statement"_s, p); + } + // 他は全部一緒くた + p = parse_line(p, &can_step); + p = skip_space(p); + add_scriptc(ByteCode::EOL); + + set_label(borrow(LABEL_NEXTLINE_), script_buf.size()); + LABEL_NEXTLINE_.type = StringCode::NOP; + LABEL_NEXTLINE_.backpatch = -1; + LABEL_NEXTLINE_.label_ = -1; + } + + if (can_step && !implicit_end) + { + --script_errors; disp_error_message("deprecated: implicit end"_s, p); + } + add_scriptc(ByteCode::NOP); + + // resolve the unknown labels + for (auto& pair : str_datam) + { + str_data_t& sit = pair.second; + if (sit.type == StringCode::NOP) + { + sit.type = StringCode::VARIABLE; + sit.label_ = 0; // anything but -1. Shouldn't matter, but helps asserts. + size_t pool_index = variable_names.intern(sit.strs); + for (int next, j = sit.backpatch; j >= 0 && j != 0x00ffffff; j = next) + { + next = 0; + next |= static_cast<uint8_t>(script_buf[j + 0]) << 0; + next |= static_cast<uint8_t>(script_buf[j + 1]) << 8; + next |= static_cast<uint8_t>(script_buf[j + 2]) << 16; + script_buf[j] = static_cast<ByteCode>(pool_index); + script_buf[j + 1] = static_cast<ByteCode>(pool_index >> 8); + script_buf[j + 2] = static_cast<ByteCode>(pool_index >> 16); + } + } + } + + for (const auto& pair : scriptlabel_db) + { + ScriptLabel key = pair.first; + if (key.startswith("On"_s)) + continue; + if (!(key.startswith("L_"_s) || key.startswith("S_"_s))) + PRINTF("Warning: ugly label: %s\n"_fmt, key); + else if (!probable_labels.count(key)) + PRINTF("Warning: unused label: %s\n"_fmt, key); + } + for (ScriptLabel used : probable_labels) + { + if (scriptlabel_db.search(used).is_none()) + PRINTF("Warning: no such label: %s\n"_fmt, used); + } + probable_labels.clear(); + + if (!DEBUG_DISP) + return; + for (size_t i = 0; i < script_buf.size(); i++) + { + if ((i & 15) == 0) + PRINTF("%04zx : "_fmt, i); + PRINTF("%02x "_fmt, script_buf[i]); + if ((i & 15) == 15) + PRINTF("\n"_fmt); + } + PRINTF("\n"_fmt); +} +} // namespace map +} // namespace tmwa diff --git a/src/map/script-parse.hpp b/src/map/script-parse.hpp new file mode 100644 index 0000000..e08c470 --- /dev/null +++ b/src/map/script-parse.hpp @@ -0,0 +1,36 @@ +#pragma once +// script-parse.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include <memory> + +#include "script-buffer.hpp" + + +namespace tmwa +{ +namespace map +{ +std::unique_ptr<const ScriptBuffer> compile_script(RString debug_name, const ast::script::ScriptBody& body, bool implicit_end); +} // namespace map +} // namespace tmwa diff --git a/src/map/script-parse.py b/src/map/script-parse.py new file mode 100644 index 0000000..0309f54 --- /dev/null +++ b/src/map/script-parse.py @@ -0,0 +1,553 @@ +class ScriptBuffer(object): + __slots__ = ('_value') + name = 'tmwa::map::ScriptBuffer' + enabled = True + + def __init__(self, value): + self._value = value + + def to_string(self): + return self._value['debug_name'] + + def get_com(self, b, i, r, labels_dict): + # see script-parse-internal.hpp:ByteCode and script-call.cpp:get_com + ops = ''' + NOP, POS, INT, PARAM, FUNC, STR, ARG, + VARIABLE, EOL, + LOR, LAND, LE, LT, GE, GT, EQ, NE, + XOR, OR, AND, ADD, SUB, MUL, DIV, MOD, + NEG, LNOT, NOT, R_SHIFT, L_SHIFT, + FUNC_REF, + '''.replace(',', '').split() + ci = int(b[i]) + if ci >= 0x80: + rv = 0 + sh = 0 + # Because of how the python iterator up a frame consumed the i already, + # have to manually unroll the first iteration of the C loop. + # TODO itertools.chain([i], r) or something? + if True: + if True: + rv += (ci & 0x7f) << sh + sh += 6 + if ci >= 0xc0: + while True: + i = next(r) + ci = int(b[i]) + + rv += (ci & 0x7f) << sh + sh += 6 + if not (ci >= 0xc0): + break + return 'INT %d' % rv + + cs = ops[ci] + if cs in {'POS', 'VARIABLE', 'FUNC_REF', 'PARAM'}: + ai = 0 + ai |= (int(b[next(r)]) << 0) + ai |= (int(b[next(r)]) << 8) + ai |= (int(b[next(r)]) << 16) + if cs == 'POS': + ln = labels_dict.get(ai) + if ln is not None: + return 'POS %d %s' % (ai, ln[0]) + elif cs == 'VARIABLE': + global rstring_disable_children + rstring_disable_children = True + try: + rv = 'VARIABLE %s' % gdb.parse_and_eval('tmwa::map::variable_names.names._M_impl._M_start[{ai}]'.format(ai=ai)) + finally: + rstring_disable_children = False + return rv + elif cs == 'FUNC_REF': + return 'FUNC_REF %s' % gdb.parse_and_eval('tmwa::map::builtin_functions[{ai}].name'.format(ai=ai)) + elif cs == 'PARAM': + # https://sourceware.org/bugzilla/show_bug.cgi?id=17568 + try: + # this should work + SP = gdb.lookup_type('tmwa::SP') + except gdb.error: + # this should not work + SP = gdb.parse_and_eval('tmwa::SP').type + return 'PARAM %s' % gdb.Value(ai).cast(SP) + else: + assert False + return '%s %d' % (cs, ai) + elif cs == 'STR': + buf = bytearray() + while True: + i = next(r) + ci = int(b[i]) + if ci == 0: + break + buf.append(ci) + return 'STR "%s"' % str(buf).replace('\\', '\\\\').replace('"', '\\"') + elif cs == 'EOL': + return cs + '\n' + return cs + + def children(self): + labels = self._value['debug_labels'] + labels_begin = labels['_M_impl']['_M_start'] + labels_end = labels['_M_impl']['_M_finish'] + labels_size = int(labels_end - labels_begin) + labels_list = [labels_begin[i] for i in range(labels_size)] + labels_dict = {} + char_ptr = gdb.lookup_type('char').pointer() + for e in labels_list: + offset = int(e['second']) + label_name = str(e['first'].address.cast(char_ptr)) + labels_dict.setdefault(offset, []).append(label_name) + code = self._value['script_buf'] + code_begin = code['_M_impl']['_M_start'] + code_end = code['_M_impl']['_M_finish'] + code_size = int(code_end - code_begin) + r = iter(range(code_size)) + for i in r: + buf = [] + for label in labels_dict.get(i, []): + if label.startswith('On'): + yield 'blah', 'event %s:' % label + else: + yield 'blah', 'label %s:' % label + c = self.get_com(code_begin, i, r, labels_dict) + yield 'blah', '%6d: %s' % (i, c) + + + def display_hint(self): + return 'array' + + src = ''' + { + end; + S_Sub: + return; + OnFoo: + callsub S_Sub; + setarray @a, + -1, 0, 1, + 0x0, 0x1, 0x2, + 0x1, 0x2, 0x3, + 0x3, 0x4, 0x5, + 0x7, 0x8, 0x9, + 0xf, 0x10, 0x11, + 0x1f, 0x20, 0x21, + 0x3f, 0x40, 0x41, + 0x7f, 0x80, 0x81, + 0xff, 0x100, 0x101, + 0x1ff, 0x200, 0x201, + 0x3ff, 0x400, 0x401, + 0x7ff, 0x800, 0x801, + 0xfff, 0x1000, 0x1001, + 0x1fff, 0x2000, 0x2001, + 0x3fff, 0x4000, 0x4001, + 0x7fff, 0x8000, 0x8001, + 0xffff, 0x10000, 0x10001, + 0x1ffff, 0x20000, 0x20001, + 0x3ffff, 0x40000, 0x40001, + 0x7ffff, 0x80000, 0x80001, + 0xfffff, 0x100000, 0x100001, + 0x1fffff, 0x200000, 0x200001, + 0x3fffff, 0x400000, 0x400001, + 0x7fffff, 0x800000, 0x800001, + 0xffffff, 0x1000000, 0x1000001, + 0x1ffffff, 0x2000000, 0x2000001, + 0x3ffffff, 0x4000000, 0x4000001, + 0x7ffffff, 0x8000000, 0x8000001, + 0xfffffff, 0x10000000, 0x10000001, + 0x1fffffff, 0x20000000, 0x20000001, + 0x3fffffff, 0x40000000, 0x40000001, + 0x7fffffff, 0x80000000, 0x80000001, + 0xffffffff; + set TEST_FAKE_PARAM_BASELEVEL, TEST_FAKE_CONSTANT; + set @s$, "hello"; + set @i, a || b; + set @i, a && b; + set @i, a <= b; + set @i, a < b; + set @i, a >= b; + set @i, a > b; + set @i, a == b; + set @i, a != b; + set @i, a ^ b; + set @i, a | b; + set @i, a & b; + set @i, a + b; + set @i, a - b; + set @i, a * b; + set @i, a / b; + set @i, a % b; + set @i, - b; + set @i, ! b; + set @i, ~ b; + set @i, a >> b; + set @i, a << b; + } + '''.replace('\n', ' ') + + asm = ''\ + +''' 0: FUNC_REF "end", + 4: ARG, + 5: FUNC, + 6: EOL + , + label "S_Sub":, + 7: FUNC_REF "return", + 11: ARG, + 12: FUNC, + 13: EOL + , + label "OnFoo":, + 14: FUNC_REF "callsub", + 18: ARG, + 19: POS 7 "S_Sub", + 23: FUNC, + 24: EOL + , + 25: FUNC_REF "setarray", + 29: ARG, + 30: VARIABLE "@a", + 34: INT 1, + 35: NEG, + 36: INT 0, + 37: INT 1, + 38: INT 0, + 39: INT 1, + 40: INT 2, + 41: INT 1, + 42: INT 2, + 43: INT 3, + 44: INT 3, + 45: INT 4, + 46: INT 5, + 47: INT 7, + 48: INT 8, + 49: INT 9, + 50: INT 15, + 51: INT 16, + 52: INT 17, + 53: INT 31, + 54: INT 32, + 55: INT 33, + 56: INT 63, + 57: INT 64, + 59: INT 65, + 61: INT 127, + 63: INT 128, + 65: INT 129, + 67: INT 255, + 69: INT 256, + 71: INT 257, + 73: INT 511, + 75: INT 512, + 77: INT 513, + 79: INT 1023, + 81: INT 1024, + 83: INT 1025, + 85: INT 2047, + 87: INT 2048, + 89: INT 2049, + 91: INT 4095, + 93: INT 4096, + 95: INT 4097, + 97: INT 8191, + 100: INT 8192, + 103: INT 8193, + 106: INT 16383, + 109: INT 16384, + 112: INT 16385, + 115: INT 32767, + 118: INT 32768, + 121: INT 32769, + 124: INT 65535, + 127: INT 65536, + 130: INT 65537, + 133: INT 131071, + 136: INT 131072, + 139: INT 131073, + 142: INT 262143, + 145: INT 262144, + 148: INT 262145, + 151: INT 524287, + 155: INT 524288, + 159: INT 524289, + 163: INT 1048575, + 167: INT 1048576, + 171: INT 1048577, + 175: INT 2097151, + 179: INT 2097152, + 183: INT 2097153, + 187: INT 4194303, + 191: INT 4194304, + 195: INT 4194305, + 199: INT 8388607, + 203: INT 8388608, + 207: INT 8388609, + 211: INT 16777215, + 215: INT 16777216, + 219: INT 16777217, + 223: INT 33554431, + 228: INT 33554432, + 233: INT 33554433, + 238: INT 67108863, + 243: INT 67108864, + 248: INT 67108865, + 253: INT 134217727, + 258: INT 134217728, + 263: INT 134217729, + 268: INT 268435455, + 273: INT 268435456, + 278: INT 268435457, + 283: INT 536870911, + 288: INT 536870912, + 293: INT 536870913, + 298: INT 1073741823, + 303: INT 1073741824, + 308: INT 1073741825, + 313: INT 2147483647, + 319: INT 2147483648, + 325: INT 2147483649, + 331: INT 4294967295, + 337: FUNC, + 338: EOL + , + 339: FUNC_REF "set", + 343: ARG, + 344: PARAM tmwa::SP::BASELEVEL, + 348: INT 42, + 349: FUNC, + 350: EOL + , + 351: FUNC_REF "set", + 355: ARG, + 356: VARIABLE "@s$", + 360: STR "hello", + 367: FUNC, + 368: EOL + , + 369: FUNC_REF "set", + 373: ARG, + 374: VARIABLE "@i", + 378: VARIABLE "a", + 382: VARIABLE "b", + 386: LOR, + 387: FUNC, + 388: EOL + , + 389: FUNC_REF "set", + 393: ARG, + 394: VARIABLE "@i", + 398: VARIABLE "a", + 402: VARIABLE "b", + 406: LAND, + 407: FUNC, + 408: EOL + , + 409: FUNC_REF "set", + 413: ARG, + 414: VARIABLE "@i", + 418: VARIABLE "a", + 422: VARIABLE "b", + 426: LE, + 427: FUNC, + 428: EOL + , + 429: FUNC_REF "set", + 433: ARG, + 434: VARIABLE "@i", + 438: VARIABLE "a", + 442: VARIABLE "b", + 446: LT, + 447: FUNC, + 448: EOL + , + 449: FUNC_REF "set", + 453: ARG, + 454: VARIABLE "@i", + 458: VARIABLE "a", + 462: VARIABLE "b", + 466: GE, + 467: FUNC, + 468: EOL + , + 469: FUNC_REF "set", + 473: ARG, + 474: VARIABLE "@i", + 478: VARIABLE "a", + 482: VARIABLE "b", + 486: GT, + 487: FUNC, + 488: EOL + , + 489: FUNC_REF "set", + 493: ARG, + 494: VARIABLE "@i", + 498: VARIABLE "a", + 502: VARIABLE "b", + 506: EQ, + 507: FUNC, + 508: EOL + , + 509: FUNC_REF "set", + 513: ARG, + 514: VARIABLE "@i", + 518: VARIABLE "a", + 522: VARIABLE "b", + 526: NE, + 527: FUNC, + 528: EOL + , + 529: FUNC_REF "set", + 533: ARG, + 534: VARIABLE "@i", + 538: VARIABLE "a", + 542: VARIABLE "b", + 546: XOR, + 547: FUNC, + 548: EOL + , + 549: FUNC_REF "set", + 553: ARG, + 554: VARIABLE "@i", + 558: VARIABLE "a", + 562: VARIABLE "b", + 566: OR, + 567: FUNC, + 568: EOL + , + 569: FUNC_REF "set", + 573: ARG, + 574: VARIABLE "@i", + 578: VARIABLE "a", + 582: VARIABLE "b", + 586: AND, + 587: FUNC, + 588: EOL + , + 589: FUNC_REF "set", + 593: ARG, + 594: VARIABLE "@i", + 598: VARIABLE "a", + 602: VARIABLE "b", + 606: ADD, + 607: FUNC, + 608: EOL + , + 609: FUNC_REF "set", + 613: ARG, + 614: VARIABLE "@i", + 618: VARIABLE "a", + 622: VARIABLE "b", + 626: SUB, + 627: FUNC, + 628: EOL + , + 629: FUNC_REF "set", + 633: ARG, + 634: VARIABLE "@i", + 638: VARIABLE "a", + 642: VARIABLE "b", + 646: MUL, + 647: FUNC, + 648: EOL + , + 649: FUNC_REF "set", + 653: ARG, + 654: VARIABLE "@i", + 658: VARIABLE "a", + 662: VARIABLE "b", + 666: DIV, + 667: FUNC, + 668: EOL + , + 669: FUNC_REF "set", + 673: ARG, + 674: VARIABLE "@i", + 678: VARIABLE "a", + 682: VARIABLE "b", + 686: MOD, + 687: FUNC, + 688: EOL + , + 689: FUNC_REF "set", + 693: ARG, + 694: VARIABLE "@i", + 698: VARIABLE "b", + 702: NEG, + 703: FUNC, + 704: EOL + , + 705: FUNC_REF "set", + 709: ARG, + 710: VARIABLE "@i", + 714: VARIABLE "b", + 718: LNOT, + 719: FUNC, + 720: EOL + , + 721: FUNC_REF "set", + 725: ARG, + 726: VARIABLE "@i", + 730: VARIABLE "b", + 734: NOT, + 735: FUNC, + 736: EOL + , + 737: FUNC_REF "set", + 741: ARG, + 742: VARIABLE "@i", + 746: VARIABLE "a", + 750: VARIABLE "b", + 754: R_SHIFT, + 755: FUNC, + 756: EOL + , + 757: FUNC_REF "set", + 761: ARG, + 762: VARIABLE "@i", + 766: VARIABLE "a", + 770: VARIABLE "b", + 774: L_SHIFT, + 775: FUNC, + 776: EOL + , + 777: NOP'''.replace(''' + ''', '\n') + + test_extra = (''' + #include "../compat/borrow.hpp" + #include "../io/line.hpp" + #include "../mmo/clif.t.hpp" + #include "../ast/script.hpp" + #include "../map/script-parse-internal.hpp" + + using tmwa::operator "" _s; + + static + const tmwa::map::ScriptBuffer& test_script_buffer(tmwa::LString source) + { + auto p = tmwa::map::add_strp("TEST_FAKE_PARAM_BASELEVEL"_s); + p->type = tmwa::map::StringCode::PARAM; + p->val = static_cast<uint16_t>(tmwa::SP::BASELEVEL); + p = tmwa::map::add_strp("TEST_FAKE_CONSTANT"_s); + p->type = tmwa::map::StringCode::INT; + p->val = 42; + + tmwa::io::LineCharReader lr(tmwa::io::from_string, "<script debug print test>"_s, source); + tmwa::ast::script::ScriptOptions opt; + opt.implicit_start = true; + opt.implicit_end = true; + auto code_res = tmwa::ast::script::parse_script_body(lr, opt); + auto code = TRY_UNWRAP(code_res.get_success(), abort()); + auto ups = tmwa::map::compile_script("script debug print test"_s, code, opt.implicit_end); + assert(ups); + return *ups.release(); + } + ''') + + # the pretty-printer is designed to be run with 'set print array on' + # but the testsuite runs with all settings default, in this case off. + tests = [ + ('test_script_buffer("' + src.replace('\\', '\\\\').replace('"', '\\"') + '"_s)', + '"script debug print test" = {' + asm.replace('\n', ' ').replace('EOL ,', 'EOL\n,') + '}'), + ] diff --git a/src/map/script-persist.hpp b/src/map/script-persist.hpp new file mode 100644 index 0000000..45c2761 --- /dev/null +++ b/src/map/script-persist.hpp @@ -0,0 +1,128 @@ +#pragma once +// script-persist.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include "../compat/borrow.hpp" + +#include "../strings/rstring.hpp" + +#include "../sexpr/variant.hpp" + +#include "../mmo/clif.t.hpp" + +#include "script-buffer.hpp" + + +namespace tmwa +{ +namespace map +{ +class SIR +{ + uint32_t impl; + SIR(SP v) + : impl(static_cast<uint32_t>(v)) + {} + SIR(unsigned v, uint8_t i) + : impl((i << 24) | v) + {} +public: + SIR() : impl() {} + + unsigned base() const { return impl & 0x00ffffff; } + uint8_t index() const { return impl >> 24; } + SIR iplus(uint8_t i) const { return SIR(base(), index() + i); } + static SIR from(unsigned v, uint8_t i=0) { return SIR(v, i); } + + SP sp() const { return static_cast<SP>(impl); } + static SIR from(SP v) { return SIR(v); } + + friend bool operator == (SIR l, SIR r) { return l.impl == r.impl; } + friend bool operator < (SIR l, SIR r) { return l.impl < r.impl; } +}; + +struct ScriptDataPos +{ + int numi; +}; +struct ScriptDataInt +{ + int numi; +}; +struct ScriptDataParam +{ + SIR reg; +}; +struct ScriptDataStr +{ + RString str; +}; +struct ScriptDataArg +{ + int numi; +}; +struct ScriptDataVariable +{ + SIR reg; +}; +struct ScriptDataRetInfo +{ + // Not a ScriptPointer - pos is stored in a separate slot, + // to avoid exploding the struct for everyone. + Borrowed<const ScriptBuffer> script; +}; +struct ScriptDataFuncRef +{ + int numi; +}; + +using ScriptDataVariantBase = Variant< + ScriptDataPos, + ScriptDataInt, + ScriptDataParam, + ScriptDataStr, + ScriptDataArg, + ScriptDataVariable, + ScriptDataRetInfo, + ScriptDataFuncRef +>; +struct script_data : ScriptDataVariantBase +{ + script_data() = delete; + // TODO see if I can delete the copy ctor/assign instead of defaulting + script_data(script_data&&) = default; + script_data(const script_data&) = default /*delete*/; + script_data& operator = (script_data&&) = default; + script_data& operator = (const script_data&) = default /*delete*/; + + script_data(ScriptDataPos v) : ScriptDataVariantBase(std::move(v)) {} + script_data(ScriptDataInt v) : ScriptDataVariantBase(std::move(v)) {} + script_data(ScriptDataParam v) : ScriptDataVariantBase(std::move(v)) {} + script_data(ScriptDataStr v) : ScriptDataVariantBase(std::move(v)) {} + script_data(ScriptDataArg v) : ScriptDataVariantBase(std::move(v)) {} + script_data(ScriptDataVariable v) : ScriptDataVariantBase(std::move(v)) {} + script_data(ScriptDataRetInfo v) : ScriptDataVariantBase(std::move(v)) {} + script_data(ScriptDataFuncRef v) : ScriptDataVariantBase(std::move(v)) {} +}; +} // namespace map +} // namespace tmwa diff --git a/src/map/script-persist.py b/src/map/script-persist.py new file mode 100644 index 0000000..3181344 --- /dev/null +++ b/src/map/script-persist.py @@ -0,0 +1,37 @@ +class script_data(object): + enabled = True + + test_extra = ''' + #include "../strings/literal.hpp" + using tmwa::operator "" _s; + + #include "../map/script-parse-internal.hpp" + + static + tmwa::Borrowed<const tmwa::map::ScriptBuffer> fake_script() + { + static + const std::vector<tmwa::map::ByteCode> buffer; + return tmwa::borrow(reinterpret_cast<const tmwa::map::ScriptBuffer&>(buffer)); + } + + ''' + + tests = [ + ('tmwa::map::script_data(tmwa::map::ScriptDataPos{42})', + '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataPos) = {numi = 42}}, <No data fields>}'), + ('tmwa::map::script_data(tmwa::map::ScriptDataInt{123})', + '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataInt) = {numi = 123}}, <No data fields>}'), + ('tmwa::map::script_data(tmwa::map::ScriptDataParam{tmwa::map::SIR()})', + '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataParam) = {reg = {impl = 0}}}, <No data fields>}'), + ('tmwa::map::script_data(tmwa::map::ScriptDataStr{"Hello"_s})', + '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataStr) = {str = "Hello"}}, <No data fields>}'), + ('tmwa::map::script_data(tmwa::map::ScriptDataArg{0})', + '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataArg) = {numi = 0}}, <No data fields>}'), + ('tmwa::map::script_data(tmwa::map::ScriptDataVariable{tmwa::map::SIR()})', + '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataVariable) = {reg = {impl = 0}}}, <No data fields>}'), + ('tmwa::map::script_data(tmwa::map::ScriptDataRetInfo{fake_script()})', + '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataRetInfo) = {script = <fake_script()::buffer>}}, <No data fields>}'), + ('tmwa::map::script_data(tmwa::map::ScriptDataFuncRef{404})', + '{<tmwa::sexpr::Variant<tmwa::map::ScriptDataPos, tmwa::map::ScriptDataInt, tmwa::map::ScriptDataParam, tmwa::map::ScriptDataStr, tmwa::map::ScriptDataArg, tmwa::map::ScriptDataVariable, tmwa::map::ScriptDataRetInfo, tmwa::map::ScriptDataFuncRef>> = {(tmwa::map::ScriptDataFuncRef) = {numi = 404}}, <No data fields>}'), + ] diff --git a/src/map/script-startup-internal.hpp b/src/map/script-startup-internal.hpp new file mode 100644 index 0000000..91ce09b --- /dev/null +++ b/src/map/script-startup-internal.hpp @@ -0,0 +1,36 @@ +#pragma once +// script-startup-internal.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "script-startup.hpp" +#include "fwd.hpp" + +#include "script-persist.hpp" + + +namespace tmwa +{ +namespace map +{ +void mapreg_setreg(SIR reg, int val); +void mapreg_setregstr(SIR reg, XString str); +} // namespace map +} // namespace tmwa diff --git a/src/map/script-startup.cpp b/src/map/script-startup.cpp new file mode 100644 index 0000000..ad809db --- /dev/null +++ b/src/map/script-startup.cpp @@ -0,0 +1,265 @@ +#include "script-startup-internal.hpp" +// script-startup.cpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011 Chuck Miller +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// Copyright © 2013 wushin +// +// 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 <http://www.gnu.org/licenses/>. + +#include "../strings/zstring.hpp" + +#include "../generic/db.hpp" +#include "../generic/intern-pool.hpp" + +#include "../io/cxxstdio.hpp" +#include "../io/extract.hpp" +#include "../io/read.hpp" +#include "../io/lock.hpp" + +#include "../net/timer.hpp" + +#include "globals.hpp" +#include "map.hpp" +#include "map_conf.hpp" +#include "script-parse-internal.hpp" +#include "script-persist.hpp" + +#include "../poison.hpp" + + +namespace tmwa +{ +namespace map +{ +constexpr std::chrono::milliseconds MAPREG_AUTOSAVE_INTERVAL = 10_s; + +bool read_constdb(ZString filename) +{ + io::ReadFile in(filename); + if (!in.is_open()) + { + PRINTF("can't read %s\n"_fmt, filename); + return false; + } + + bool rv = true; + AString line_; + while (in.getline(line_)) + { + // is_comment only works for whole-line comments + // that could change once the Z dependency is dropped ... + LString comment = "//"_s; + XString line = line_.xislice_h(std::search(line_.begin(), line_.end(), comment.begin(), comment.end())).rstrip(); + if (!line) + continue; + // "%m[A-Za-z0-9_] %i %i" + + // TODO promote either qsplit() or asplit() + auto _it = std::find(line.begin(), line.end(), ' '); + auto name = line.xislice_h(_it); + auto _rest = line.xislice_t(_it); + while (_rest.startswith(' ')) + _rest = _rest.xslice_t(1); + auto _it2 = std::find(_rest.begin(), _rest.end(), ' '); + auto val_ = _rest.xislice_h(_it2); + auto type_ = _rest.xislice_t(_it2); + while (type_.startswith(' ')) + type_ = type_.xslice_t(1); + // yes, the above actually DTRT even for underlength input + + int val; + int type = 0; + // Note for future archeaologists: this code is indented correctly + if (std::find_if_not(name.begin(), name.end(), + [](char c) + { + return ('0' <= c && c <= '9') + || ('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z') + || (c == '_'); + }) != name.end() + || !extract(val_, &val) + || (!extract(type_, &type) && type_)) + { + PRINTF("Bad const line: %s\n"_fmt, line_); + rv = false; + continue; + } + P<str_data_t> n = add_strp(name); + n->type = type ? StringCode::PARAM : StringCode::INT; + n->val = val; + } + return rv; +} + +/*========================================== + * マップ変数の変更 + *------------------------------------------ + */ +void mapreg_setreg(SIR reg, int val) +{ + mapreg_db.put(reg, val); + + mapreg_dirty = 1; +} + +/*========================================== + * 文字列型マップ変数の変更 + *------------------------------------------ + */ +void mapreg_setregstr(SIR reg, XString str) +{ + if (!str) + mapregstr_db.erase(reg); + else + mapregstr_db.insert(reg, str); + + mapreg_dirty = 1; +} + +/*========================================== + * 永続的マップ変数の読み込み + *------------------------------------------ + */ +static +void script_load_mapreg(void) +{ + io::ReadFile in(map_conf.mapreg_txt); + + if (!in.is_open()) + return; + + AString line; + while (in.getline(line)) + { + XString buf1, buf2; + int index = 0; + if (extract(line, + record<'\t'>( + record<','>(&buf1, &index), + &buf2)) + || extract(line, + record<'\t'>( + record<','>(&buf1), + &buf2))) + { + int s = variable_names.intern(buf1); + SIR key = SIR::from(s, index); + if (buf1.back() == '$') + { + mapregstr_db.insert(key, buf2); + } + else + { + int v; + if (!extract(buf2, &v)) + goto borken; + mapreg_db.put(key, v); + } + } + else + { + borken: + PRINTF("%s: %s broken data !\n"_fmt, map_conf.mapreg_txt, AString(buf1)); + continue; + } + } + mapreg_dirty = 0; +} + +/*========================================== + * 永続的マップ変数の書き込み + *------------------------------------------ + */ +static +void script_save_mapreg_intsub(SIR key, int data, io::WriteFile& fp) +{ + int num = key.base(), i = key.index(); + ZString name = variable_names.outtern(num); + if (name[1] != '@') + { + if (i == 0) + FPRINTF(fp, "%s\t%d\n"_fmt, name, data); + else + FPRINTF(fp, "%s,%d\t%d\n"_fmt, name, i, data); + } +} + +static +void script_save_mapreg_strsub(SIR key, ZString data, io::WriteFile& fp) +{ + int num = key.base(), i = key.index(); + ZString name = variable_names.outtern(num); + if (name[1] != '@') + { + if (i == 0) + FPRINTF(fp, "%s\t%s\n"_fmt, name, data); + else + FPRINTF(fp, "%s,%d\t%s\n"_fmt, name, i, data); + } +} + +static +void script_save_mapreg(void) +{ + io::WriteLock fp(map_conf.mapreg_txt); + if (!fp.is_open()) + return; + for (auto& pair : mapreg_db) + script_save_mapreg_intsub(pair.first, pair.second, fp); + for (auto& pair : mapregstr_db) + script_save_mapreg_strsub(pair.first, pair.second, fp); + mapreg_dirty = 0; +} + +static +void script_autosave_mapreg(TimerData *, tick_t) +{ + if (mapreg_dirty) + script_save_mapreg(); +} + +void do_final_script(void) +{ + if (mapreg_dirty >= 0) + script_save_mapreg(); + + mapreg_db.clear(); + mapregstr_db.clear(); + scriptlabel_db.clear(); + userfunc_db.clear(); + + str_datam.clear(); +} + +/*========================================== + * 初期化 + *------------------------------------------ + */ +void do_init_script(void) +{ + script_load_mapreg(); + + Timer(gettick() + MAPREG_AUTOSAVE_INTERVAL, + script_autosave_mapreg, + MAPREG_AUTOSAVE_INTERVAL + ).detach(); +} +} // namespace map +} // namespace tmwa diff --git a/src/map/script-startup.hpp b/src/map/script-startup.hpp new file mode 100644 index 0000000..1691c94 --- /dev/null +++ b/src/map/script-startup.hpp @@ -0,0 +1,34 @@ +#pragma once +// script-startup.hpp - EAthena script frontend, engine, and library. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +namespace tmwa +{ +namespace map +{ +void do_init_script(void); +void do_final_script(void); + +bool read_constdb(ZString filename); +} // namespace map +} // namespace tmwa diff --git a/src/map/script.cpp b/src/map/script.cpp deleted file mode 100644 index 40dfc0e..0000000 --- a/src/map/script.cpp +++ /dev/null @@ -1,5100 +0,0 @@ -#include "script.hpp" -// script.cpp - EAthena script frontend, engine, and library. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011 Chuck Miller -// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> -// Copyright © 2013 wushin -// -// 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 <http://www.gnu.org/licenses/>. - -#include <cassert> -#include <cmath> -#include <cstdlib> -#include <ctime> - -#include <algorithm> -#include <set> - -#include "../compat/fun.hpp" - -#include "../strings/mstring.hpp" -#include "../strings/rstring.hpp" -#include "../strings/astring.hpp" -#include "../strings/zstring.hpp" -#include "../strings/xstring.hpp" -#include "../strings/literal.hpp" - -#include "../generic/db.hpp" -#include "../generic/intern-pool.hpp" -#include "../generic/random.hpp" - -#include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" -#include "../io/lock.hpp" -#include "../io/read.hpp" -#include "../io/write.hpp" - -#include "../net/socket.hpp" -#include "../net/timer.hpp" - -#include "../mmo/core.hpp" -#include "../mmo/extract.hpp" -#include "../mmo/human_time_diff.hpp" -#include "../mmo/utils.hpp" - -#include "atcommand.hpp" -#include "battle.hpp" -#include "chrif.hpp" -#include "clif.hpp" -#include "intif.hpp" -#include "itemdb.hpp" -#include "magic-interpreter-base.hpp" -#include "map.hpp" -#include "mob.hpp" -#include "npc.hpp" -#include "party.hpp" -#include "pc.hpp" -#include "skill.hpp" -#include "storage.hpp" - -#include "../poison.hpp" - - -namespace tmwa -{ -constexpr bool DEBUG_DISP = false; -constexpr bool DEBUG_RUN = false; - -struct str_data_t -{ - StringCode type; - RString strs; - int backpatch; - int label_; - int val; -}; -static -Map<RString, str_data_t> str_datam; -static -str_data_t LABEL_NEXTLINE_; - -static -DMap<SIR, int> mapreg_db; -static -Map<SIR, RString> mapregstr_db; -static -int mapreg_dirty = -1; -AString mapreg_txt = "save/mapreg.txt"_s; -constexpr std::chrono::milliseconds MAPREG_AUTOSAVE_INTERVAL = 10_s; - -Map<ScriptLabel, int> scriptlabel_db; -static -std::set<ScriptLabel> probable_labels; -UPMap<RString, const ScriptBuffer> userfunc_db; - -static -Array<LString, 11> pos_str //= -{{ - "Head"_s, - "Body"_s, - "Left hand"_s, - "Right hand"_s, - "Robe"_s, - "Shoes"_s, - "Accessory 1"_s, - "Accessory 2"_s, - "Head 2"_s, - "Head 3"_s, - "Not Equipped"_s, -}}; - -static -struct Script_Config -{ - static const - int warn_func_no_comma = 1; - static const - int warn_cmd_no_comma = 1; - static const - int warn_func_mismatch_paramnum = 1; - static const - int warn_cmd_mismatch_paramnum = 1; - static const - int check_cmdcount = 8192; - static const - int check_gotocount = 512; -} script_config; - -static -int parse_cmd_if = 0; -static -str_data_t *parse_cmdp; - -static -void run_func(ScriptState *st); - -static -void mapreg_setreg(SIR num, int val); -static -void mapreg_setregstr(SIR num, XString str); - -struct BuiltinFunction -{ - void (*func)(ScriptState *); - LString name; - LString arg; - char ret; -}; -// defined later -extern BuiltinFunction builtin_functions[]; - -static -InternPool variable_names; - - -template<class D> -bool first_type_is_any() -{ - return false; -} - -template<class D, class F, class... R> -constexpr -bool first_type_is_any() -{ - return std::is_same<D, F>::value || first_type_is_any<D, R...>(); -} - - -static -str_data_t *search_strp(XString p) -{ - return str_datam.search(p); -} - -static -str_data_t *add_strp(XString p) -{ - if (str_data_t *rv = search_strp(p)) - return rv; - - RString p2 = p; - str_data_t *datum = str_datam.init(p2); - datum->type = StringCode::NOP; - datum->strs = p2; - datum->backpatch = -1; - datum->label_ = -1; - return datum; -} - -/*========================================== - * スクリプトバッファに1バイト書き込む - *------------------------------------------ - */ -void ScriptBuffer::add_scriptc(ByteCode a) -{ - script_buf.push_back(a); -} - -/*========================================== - * スクリプトバッファにデータタイプを書き込む - *------------------------------------------ - */ -void ScriptBuffer::add_scriptb(uint8_t a) -{ - add_scriptc(static_cast<ByteCode>(a)); -} - -/*========================================== - * スクリプトバッファに整数を書き込む - *------------------------------------------ - */ -void ScriptBuffer::add_scripti(uint32_t a) -{ - while (a >= 0x40) - { - add_scriptb(a | 0xc0); - a = (a - 0x40) >> 6; - } - add_scriptb(a | 0x80); -} - -/*========================================== - * スクリプトバッファにラベル/変数/関数を書き込む - *------------------------------------------ - */ -// 最大16Mまで -void ScriptBuffer::add_scriptl(str_data_t *ld) -{ - int backpatch = ld->backpatch; - - switch (ld->type) - { - case StringCode::POS: - add_scriptc(ByteCode::POS); - add_scriptb(static_cast<uint8_t>(ld->label_)); - add_scriptb(static_cast<uint8_t>(ld->label_ >> 8)); - add_scriptb(static_cast<uint8_t>(ld->label_ >> 16)); - break; - case StringCode::NOP: - // need to set backpatch, because it might become a label later - add_scriptc(ByteCode::VARIABLE); - ld->backpatch = script_buf.size(); - add_scriptb(static_cast<uint8_t>(backpatch)); - add_scriptb(static_cast<uint8_t>(backpatch >> 8)); - add_scriptb(static_cast<uint8_t>(backpatch >> 16)); - break; - case StringCode::INT: - add_scripti(ld->val); - break; - case StringCode::FUNC: - add_scriptc(ByteCode::FUNC_REF); - add_scriptb(static_cast<uint8_t>(ld->val)); - add_scriptb(static_cast<uint8_t>(ld->val >> 8)); - add_scriptb(static_cast<uint8_t>(ld->val >> 16)); - break; - case StringCode::PARAM: - add_scriptc(ByteCode::PARAM); - add_scriptb(static_cast<uint8_t>(ld->val)); - add_scriptb(static_cast<uint8_t>(ld->val >> 8)); - add_scriptb(static_cast<uint8_t>(ld->val >> 16)); - break; - default: - abort(); - } -} - -/*========================================== - * ラベルを解決する - *------------------------------------------ - */ -void ScriptBuffer::set_label(str_data_t *ld, int pos_) -{ - int next; - - ld->type = StringCode::POS; - ld->label_ = pos_; - for (int i = ld->backpatch; i >= 0 && i != 0x00ffffff; i = next) - { - next = 0; - // woot! no longer endian-dependent! - next |= static_cast<uint8_t>(script_buf[i + 0]) << 0; - next |= static_cast<uint8_t>(script_buf[i + 1]) << 8; - next |= static_cast<uint8_t>(script_buf[i + 2]) << 16; - script_buf[i - 1] = ByteCode::POS; - script_buf[i] = static_cast<ByteCode>(pos_); - script_buf[i + 1] = static_cast<ByteCode>(pos_ >> 8); - script_buf[i + 2] = static_cast<ByteCode>(pos_ >> 16); - } -} - -/*========================================== - * スペース/コメント読み飛ばし - *------------------------------------------ - */ -static -ZString::iterator skip_space(ZString::iterator p) -{ - while (1) - { - while (isspace(*p)) - p++; - if (p[0] == '/' && p[1] == '/') - { - while (*p && *p != '\n') - p++; - } - else if (p[0] == '/' && p[1] == '*') - { - p++; - while (*p && (p[-1] != '*' || p[0] != '/')) - p++; - if (*p) - p++; - } - else - break; - } - return p; -} - -/*========================================== - * 1単語スキップ - *------------------------------------------ - */ -static -ZString::iterator skip_word(ZString::iterator p) -{ - // prefix - if (*p == '$') - p++; // MAP鯖内共有変数用 - if (*p == '@') - p++; // 一時的変数用(like weiss) - if (*p == '#') - p++; // account変数用 - if (*p == '#') - p++; // ワールドaccount変数用 - - while (isalnum(*p) || *p == '_') - p++; - - // postfix - if (*p == '$') - p++; // 文字列変数 - - return p; -} - -// TODO: replace this whole mess with some sort of input stream that works -// a line at a time. -static -ZString startptr; -static -int startline; - -int script_errors = 0; -/*========================================== - * エラーメッセージ出力 - *------------------------------------------ - */ -static -void disp_error_message(ZString mes, ZString::iterator pos_) -{ - script_errors++; - - assert (startptr.begin() <= pos_ && pos_ <= startptr.end()); - - int line; - ZString::iterator p; - - for (line = startline, p = startptr.begin(); p != startptr.end(); line++) - { - ZString::iterator linestart = p; - ZString::iterator lineend = std::find(p, startptr.end(), '\n'); - if (pos_ < lineend) - { - PRINTF("\n%s\nline %d : "_fmt, mes, line); - for (int i = 0; linestart + i != lineend; i++) - { - if (linestart + i != pos_) - PRINTF("%c"_fmt, linestart[i]); - else - PRINTF("\'%c\'"_fmt, linestart[i]); - } - PRINTF("\a\n"_fmt); - return; - } - p = lineend + 1; - } -} - -/*========================================== - * 項の解析 - *------------------------------------------ - */ -ZString::iterator ScriptBuffer::parse_simpleexpr(ZString::iterator p) -{ - p = skip_space(p); - - if (*p == ';' || *p == ',') - { - disp_error_message("unexpected expr end"_s, p); - exit(1); - } - if (*p == '(') - { - - p = parse_subexpr(p + 1, -1); - p = skip_space(p); - if ((*p++) != ')') - { - disp_error_message("unmatch ')'"_s, p); - exit(1); - } - } - else if (isdigit(*p) || ((*p == '-' || *p == '+') && isdigit(p[1]))) - { - char *np; - int i = strtoul(&*p, &np, 0); - add_scripti(i); - p += np - &*p; - } - else if (*p == '"') - { - add_scriptc(ByteCode::STR); - p++; - while (*p && *p != '"') - { - if (*p == '\\') - p++; - else if (*p == '\n') - { - disp_error_message("unexpected newline @ string"_s, p); - exit(1); - } - add_scriptb(*p++); - } - if (!*p) - { - disp_error_message("unexpected eof @ string"_s, p); - exit(1); - } - add_scriptb(0); - p++; //'"' - } - else - { - // label , register , function etc - ZString::iterator p2 = skip_word(p); - if (p2 == p) - { - disp_error_message("unexpected character"_s, p); - exit(1); - } - XString word(&*p, &*p2, nullptr); - if (word.startswith("On"_s) || word.startswith("L_"_s) || word.startswith("S_"_s)) - probable_labels.insert(stringish<ScriptLabel>(word)); - if (parse_cmd_if && (word == "callsub"_s || word == "callfunc"_s || word == "return"_s)) - { - disp_error_message("Sorry, callsub/callfunc/return have never worked properly in an if statement."_s, p); - } - str_data_t *ld = add_strp(word); - - parse_cmdp = ld; // warn_*_mismatch_paramnumのために必要 - // why not just check l->str == "if"_s or std::string(p, p2) == "if"_s? - if (ld == search_strp("if"_s)) // warn_cmd_no_commaのために必要 - parse_cmd_if++; - p = p2; - - if (ld->type != StringCode::FUNC && *p == '[') - { - // array(name[i] => getelementofarray(name,i) ) - add_scriptl(search_strp("getelementofarray"_s)); - add_scriptc(ByteCode::ARG); - add_scriptl(ld); - p = parse_subexpr(p + 1, -1); - p = skip_space(p); - if (*p != ']') - { - disp_error_message("unmatch ']'"_s, p); - exit(1); - } - p++; - add_scriptc(ByteCode::FUNC); - } - else - add_scriptl(ld); - - } - - return p; -} - -/*========================================== - * 式の解析 - *------------------------------------------ - */ -ZString::iterator ScriptBuffer::parse_subexpr(ZString::iterator p, int limit) -{ - ByteCode op; - int opl, len; - - p = skip_space(p); - - if (*p == '-') - { - ZString::iterator tmpp = skip_space(p + 1); - if (*tmpp == ';' || *tmpp == ',') - { - --script_errors; disp_error_message("deprecated: implicit 'next statement' label"_s, p); - add_scriptl(&LABEL_NEXTLINE_); - p++; - return p; - } - } - ZString::iterator tmpp = p; - if ((op = ByteCode::NEG, *p == '-') || (op = ByteCode::LNOT, *p == '!') - || (op = ByteCode::NOT, *p == '~')) - { - p = parse_subexpr(p + 1, 100); - add_scriptc(op); - } - else - p = parse_simpleexpr(p); - p = skip_space(p); - while (((op = ByteCode::ADD, opl = 6, len = 1, *p == '+') || - (op = ByteCode::SUB, opl = 6, len = 1, *p == '-') || - (op = ByteCode::MUL, opl = 7, len = 1, *p == '*') || - (op = ByteCode::DIV, opl = 7, len = 1, *p == '/') || - (op = ByteCode::MOD, opl = 7, len = 1, *p == '%') || - (op = ByteCode::FUNC, opl = 8, len = 1, *p == '(') || - (op = ByteCode::LAND, opl = 1, len = 2, *p == '&' && p[1] == '&') || - (op = ByteCode::AND, opl = 5, len = 1, *p == '&') || - (op = ByteCode::LOR, opl = 0, len = 2, *p == '|' && p[1] == '|') || - (op = ByteCode::OR, opl = 4, len = 1, *p == '|') || - (op = ByteCode::XOR, opl = 3, len = 1, *p == '^') || - (op = ByteCode::EQ, opl = 2, len = 2, *p == '=' && p[1] == '=') || - (op = ByteCode::NE, opl = 2, len = 2, *p == '!' && p[1] == '=') || - (op = ByteCode::R_SHIFT, opl = 5, len = 2, *p == '>' && p[1] == '>') || - (op = ByteCode::GE, opl = 2, len = 2, *p == '>' && p[1] == '=') || - (op = ByteCode::GT, opl = 2, len = 1, *p == '>') || - (op = ByteCode::L_SHIFT, opl = 5, len = 2, *p == '<' && p[1] == '<') || - (op = ByteCode::LE, opl = 2, len = 2, *p == '<' && p[1] == '=') || - (op = ByteCode::LT, opl = 2, len = 1, *p == '<')) && opl > limit) - { - p += len; - if (op == ByteCode::FUNC) - { - int i = 0; - str_data_t *funcp = parse_cmdp; - ZString::iterator plist[128]; - - if (funcp->type != StringCode::FUNC) - { - disp_error_message("expect function"_s, tmpp); - exit(0); - } - - add_scriptc(ByteCode::ARG); - while (*p && *p != ')' && i < 128) - { - plist[i] = p; - p = parse_subexpr(p, -1); - p = skip_space(p); - if (*p == ',') - p++; - else if (*p != ')' && script_config.warn_func_no_comma) - { - disp_error_message("expect ',' or ')' at func params"_s, - p); - } - p = skip_space(p); - i++; - } - plist[i] = p; - if (*p != ')') - { - disp_error_message("func request '(' ')'"_s, p); - exit(1); - } - p++; - - if (funcp->type == StringCode::FUNC - && script_config.warn_func_mismatch_paramnum) - { - ZString arg = builtin_functions[funcp->val].arg; - int j = 0; - // TODO handle ? and multiple * correctly - for (j = 0; arg[j]; j++) - if (arg[j] == '*' || arg[j] == '?') - break; - if ((arg[j] == 0 && i != j) || ((arg[j] == '*' || arg[j] == '?') && i < j)) - { - disp_error_message("illegal number of parameters"_s, - plist[std::min(i, j)]); - } - if (!builtin_functions[funcp->val].ret) - { - disp_error_message("statement in function context"_s, tmpp); - } - } - } - else // not op == ByteCode::FUNC - { - p = parse_subexpr(p, opl); - } - add_scriptc(op); - p = skip_space(p); - } - return p; /* return first untreated operator */ -} - -/*========================================== - * 式の評価 - *------------------------------------------ - */ -ZString::iterator ScriptBuffer::parse_expr(ZString::iterator p) -{ - switch (*p) - { - case ')': - case ';': - case ':': - case '[': - case ']': - case '}': - disp_error_message("unexpected char"_s, p); - exit(1); - } - p = parse_subexpr(p, -1); - return p; -} - -/*========================================== - * 行の解析 - *------------------------------------------ - */ -ZString::iterator ScriptBuffer::parse_line(ZString::iterator p, bool *can_step) -{ - int i = 0; - ZString::iterator plist[128]; - - p = skip_space(p); - if (*p == ';') - return p; - - parse_cmd_if = 0; // warn_cmd_no_commaのために必要 - - // 最初は関数名 - ZString::iterator p2 = p; - p = parse_simpleexpr(p); - p = skip_space(p); - - str_data_t *cmd = parse_cmdp; - if (cmd->type != StringCode::FUNC) - { - disp_error_message("expect command"_s, p2); - } - - { - // TODO should be LString, but no heterogenous lookup yet - static - std::set<ZString> terminators = - { - "goto"_s, - "return"_s, - "close"_s, - "menu"_s, - "end"_s, - "mapexit"_s, - "shop"_s, - }; - *can_step = terminators.count(cmd->strs) == 0; - } - - add_scriptc(ByteCode::ARG); - while (*p && *p != ';' && i < 128) - { - plist[i] = p; - - p = parse_expr(p); - p = skip_space(p); - // 引数区切りの,処理 - if (*p == ',') - p++; - else if (*p != ';' && script_config.warn_cmd_no_comma - && parse_cmd_if * 2 <= i) - { - disp_error_message("expect ',' or ';' at cmd params"_s, p); - } - p = skip_space(p); - i++; - } - plist[i] = p; - if (*(p++) != ';') - { - disp_error_message("need ';'"_s, p); - exit(1); - } - add_scriptc(ByteCode::FUNC); - - if (cmd->type == StringCode::FUNC - && script_config.warn_cmd_mismatch_paramnum) - { - ZString arg = builtin_functions[cmd->val].arg; - int j = 0; - // TODO see above - for (j = 0; arg[j]; j++) - if (arg[j] == '*' || arg[j] == '?') - break; - if ((arg[j] == 0 && i != j) || ((arg[j] == '*' || arg[j] == '?') && i < j)) - { - disp_error_message("illegal number of parameters"_s, - plist[std::min(i, j)]); - } - if (builtin_functions[cmd->val].ret) - { - disp_error_message("function in statement context"_s, p2); - } - } - - return p; -} - -/*========================================== - * 組み込み関数の追加 - *------------------------------------------ - */ -static -void add_builtin_functions(void) -{ - for (int i = 0; builtin_functions[i].func; i++) - { - str_data_t *n = add_strp(builtin_functions[i].name); - n->type = StringCode::FUNC; - n->val = i; - } -} - -bool read_constdb(ZString filename) -{ - io::ReadFile in(filename); - if (!in.is_open()) - { - PRINTF("can't read %s\n"_fmt, filename); - return false; - } - - bool rv = true; - AString line_; - while (in.getline(line_)) - { - // is_comment only works for whole-line comments - // that could change once the Z dependency is dropped ... - LString comment = "//"_s; - XString line = line_.xislice_h(std::search(line_.begin(), line_.end(), comment.begin(), comment.end())).rstrip(); - if (!line) - continue; - // "%m[A-Za-z0-9_] %i %i" - - // TODO promote either qsplit() or asplit() - auto _it = std::find(line.begin(), line.end(), ' '); - auto name = line.xislice_h(_it); - auto _rest = line.xislice_t(_it); - while (_rest.startswith(' ')) - _rest = _rest.xslice_t(1); - auto _it2 = std::find(_rest.begin(), _rest.end(), ' '); - auto val_ = _rest.xislice_h(_it2); - auto type_ = _rest.xislice_t(_it2); - while (type_.startswith(' ')) - type_ = type_.xslice_t(1); - // yes, the above actually DTRT even for underlength input - - int val; - int type = 0; - // Note for future archeaologists: this code is indented correctly - if (std::find_if_not(name.begin(), name.end(), - [](char c) - { - return ('0' <= c && c <= '9') - || ('A' <= c && c <= 'Z') - || ('a' <= c && c <= 'z') - || (c == '_'); - }) != name.end() - || !extract(val_, &val) - || (!extract(type_, &type) && type_)) - { - PRINTF("Bad const line: %s\n"_fmt, line_); - rv = false; - continue; - } - str_data_t *n = add_strp(name); - n->type = type ? StringCode::PARAM : StringCode::INT; - n->val = val; - } - return rv; -} - -std::unique_ptr<const ScriptBuffer> parse_script(ZString src, int line, bool implicit_end) -{ - auto script_buf = make_unique<ScriptBuffer>(); - script_buf->parse_script(src, line, implicit_end); - return std::move(script_buf); -} - -/*========================================== - * スクリプトの解析 - *------------------------------------------ - */ -void ScriptBuffer::parse_script(ZString src, int line, bool implicit_end) -{ - static int first = 1; - - if (first) - { - add_builtin_functions(); - } - first = 0; - LABEL_NEXTLINE_.type = StringCode::NOP; - LABEL_NEXTLINE_.backpatch = -1; - LABEL_NEXTLINE_.label_ = -1; - for (auto& pair : str_datam) - { - str_data_t& dit = pair.second; - if (dit.type == StringCode::POS || dit.type == StringCode::VARIABLE) - { - dit.type = StringCode::NOP; - dit.backpatch = -1; - dit.label_ = -1; - } - } - - // 外部用label dbの初期化 - scriptlabel_db.clear(); - - // for error message - startptr = src; - startline = line; - - bool can_step = true; - - ZString::iterator p = src.begin(); - p = skip_space(p); - if (*p != '{') - { - disp_error_message("not found '{'"_s, p); - abort(); - } - for (p++; *p && *p != '}';) - { - p = skip_space(p); - if (*skip_space(skip_word(p)) == ':') - { - if (can_step) - { - --script_errors; disp_error_message("deprecated: implicit fallthrough"_s, p); - } - can_step = true; - - ZString::iterator tmpp = skip_word(p); - XString str(&*p, &*tmpp, nullptr); - str_data_t *ld = add_strp(str); - bool e1 = ld->type != StringCode::NOP; - bool e2 = ld->type == StringCode::POS; - bool e3 = ld->label_ != -1; - assert (e1 == e2 && e2 == e3); - if (e3) - { - disp_error_message("dup label "_s, p); - exit(1); - } - set_label(ld, script_buf.size()); - scriptlabel_db.insert(stringish<ScriptLabel>(str), script_buf.size()); - p = tmpp + 1; - continue; - } - - if (!can_step) - { - --script_errors; disp_error_message("deprecated: unreachable statement"_s, p); - } - // 他は全部一緒くた - p = parse_line(p, &can_step); - p = skip_space(p); - add_scriptc(ByteCode::EOL); - - set_label(&LABEL_NEXTLINE_, script_buf.size()); - LABEL_NEXTLINE_.type = StringCode::NOP; - LABEL_NEXTLINE_.backpatch = -1; - LABEL_NEXTLINE_.label_ = -1; - } - - if (can_step && !implicit_end) - { - --script_errors; disp_error_message("deprecated: implicit end"_s, p); - } - add_scriptc(ByteCode::NOP); - - // resolve the unknown labels - for (auto& pair : str_datam) - { - str_data_t& sit = pair.second; - if (sit.type == StringCode::NOP) - { - sit.type = StringCode::VARIABLE; - sit.label_ = 0; // anything but -1. Shouldn't matter, but helps asserts. - size_t pool_index = variable_names.intern(sit.strs); - for (int next, j = sit.backpatch; j >= 0 && j != 0x00ffffff; j = next) - { - next = 0; - next |= static_cast<uint8_t>(script_buf[j + 0]) << 0; - next |= static_cast<uint8_t>(script_buf[j + 1]) << 8; - next |= static_cast<uint8_t>(script_buf[j + 2]) << 16; - script_buf[j] = static_cast<ByteCode>(pool_index); - script_buf[j + 1] = static_cast<ByteCode>(pool_index >> 8); - script_buf[j + 2] = static_cast<ByteCode>(pool_index >> 16); - } - } - } - - for (const auto& pair : scriptlabel_db) - { - ScriptLabel key = pair.first; - if (key.startswith("On"_s)) - continue; - if (!(key.startswith("L_"_s) || key.startswith("S_"_s))) - PRINTF("Warning: ugly label: %s\n"_fmt, key); - else if (!probable_labels.count(key)) - PRINTF("Warning: unused label: %s\n"_fmt, key); - } - for (ScriptLabel used : probable_labels) - { - if (!scriptlabel_db.search(used)) - PRINTF("Warning: no such label: %s\n"_fmt, used); - } - probable_labels.clear(); - - if (!DEBUG_DISP) - return; - for (size_t i = 0; i < script_buf.size(); i++) - { - if ((i & 15) == 0) - PRINTF("%04zx : "_fmt, i); - PRINTF("%02x "_fmt, script_buf[i]); - if ((i & 15) == 15) - PRINTF("\n"_fmt); - } - PRINTF("\n"_fmt); -} - -// -// 実行系 -// -enum class ScriptEndState -{ - ZERO, - STOP, - END, - RERUNLINE, - GOTO, - RETFUNC, -}; - -/*========================================== - * ridからsdへの解決 - *------------------------------------------ - */ -static -dumb_ptr<map_session_data> script_rid2sd(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = map_id2sd(st->rid); - if (!sd) - { - PRINTF("script_rid2sd: fatal error ! player not attached!\n"_fmt); - } - return sd; -} - -/*========================================== - * 変数の読み取り - *------------------------------------------ - */ -static -void get_val(dumb_ptr<map_session_data> sd, struct script_data *data) -{ - MATCH (*data) - { - CASE (const ScriptDataParam&, u) - { - if (sd == nullptr) - PRINTF("get_val error param SP::%d\n"_fmt, u.reg.sp()); - int numi = 0; - if (sd) - numi = pc_readparam(sd, u.reg.sp()); - *data = ScriptDataInt{numi}; - } - CASE (const ScriptDataVariable&, u) - { - ZString name_ = variable_names.outtern(u.reg.base()); - VarName name = stringish<VarName>(name_); - char prefix = name.front(); - char postfix = name.back(); - - if (prefix != '$') - { - if (sd == nullptr) - PRINTF("get_val error name?:%s\n"_fmt, name); - } - if (postfix == '$') - { - RString str; - if (prefix == '@') - { - if (sd) - str = pc_readregstr(sd, u.reg); - } - else if (prefix == '$') - { - RString *s = mapregstr_db.search(u.reg); - if (s) - str = *s; - } - else - { - PRINTF("script: get_val: illegal scope string variable.\n"_fmt); - str = "!!ERROR!!"_s; - } - *data = ScriptDataStr{str}; - } - else - { - int numi = 0; - if (prefix == '@') - { - if (sd) - numi = pc_readreg(sd, u.reg); - } - else if (prefix == '$') - { - numi = mapreg_db.get(u.reg); - } - else if (prefix == '#') - { - if (name[1] == '#') - { - if (sd) - numi = pc_readaccountreg2(sd, name); - } - else - { - if (sd) - numi = pc_readaccountreg(sd, name); - } - } - else - { - if (sd) - numi = pc_readglobalreg(sd, name); - } - *data = ScriptDataInt{numi}; - } - } - } -} - -static __attribute__((deprecated)) -void get_val(ScriptState *st, struct script_data *data) -{ - dumb_ptr<map_session_data> sd = st->rid ? map_id2sd(st->rid) : nullptr; - get_val(sd, data); -} - -/*========================================== - * 変数の読み取り2 - *------------------------------------------ - */ -static -struct script_data get_val2(ScriptState *st, SIR reg) -{ - struct script_data dat = ScriptDataVariable{reg}; - get_val(st, &dat); - return dat; -} - -/*========================================== - * 変数設定用 - *------------------------------------------ - */ -static -void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, struct script_data vd) -{ - if (type == VariableCode::PARAM) - { - int val = vd.get_if<ScriptDataInt>()->numi; - pc_setparam(sd, reg.sp(), val); - return; - } - assert (type == VariableCode::VARIABLE); - - ZString name_ = variable_names.outtern(reg.base()); - VarName name = stringish<VarName>(name_); - char prefix = name.front(); - char postfix = name.back(); - - if (postfix == '$') - { - RString str = vd.get_if<ScriptDataStr>()->str; - if (prefix == '@') - { - pc_setregstr(sd, reg, str); - } - else if (prefix == '$') - { - mapreg_setregstr(reg, str); - } - else - { - PRINTF("script: set_reg: illegal scope string variable !"_fmt); - } - } - else - { - int val = vd.get_if<ScriptDataInt>()->numi; - if (prefix == '@') - { - pc_setreg(sd, reg, val); - } - else if (prefix == '$') - { - mapreg_setreg(reg, val); - } - else if (prefix == '#') - { - if (name[1] == '#') - pc_setaccountreg2(sd, name, val); - else - pc_setaccountreg(sd, name, val); - } - else - { - pc_setglobalreg(sd, name, val); - } - } -} - -static -void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, int id) -{ - struct script_data vd = ScriptDataInt{id}; - set_reg(sd, type, reg, vd); -} - -static -void set_reg(dumb_ptr<map_session_data> sd, VariableCode type, SIR reg, RString zd) -{ - struct script_data vd = ScriptDataStr{zd}; - set_reg(sd, type, reg, vd); -} - -/*========================================== - * 文字列への変換 - *------------------------------------------ - */ -static __attribute__((warn_unused_result)) -RString conv_str(ScriptState *st, struct script_data *data) -{ - get_val(st, data); - assert (!data->is<ScriptDataRetInfo>()); - if (auto *u = data->get_if<ScriptDataInt>()) - { - AString buf = STRPRINTF("%d"_fmt, u->numi); - *data = ScriptDataStr{buf}; - } - return data->get_if<ScriptDataStr>()->str; -} - -/*========================================== - * 数値へ変換 - *------------------------------------------ - */ -static __attribute__((warn_unused_result)) -int conv_num(ScriptState *st, struct script_data *data) -{ - int rv = 0; - get_val(st, data); - assert (!data->is<ScriptDataRetInfo>()); - MATCH (*data) - { - default: - abort(); - CASE (const ScriptDataStr&, u) - { - RString p = u.str; - rv = atoi(p.c_str()); - } - CASE (const ScriptDataInt&, u) - { - return u.numi; - } - CASE (const ScriptDataPos&, u) - { - return u.numi; - } - } - *data = ScriptDataInt{rv}; - return rv; -} - -static __attribute__((warn_unused_result)) -const ScriptBuffer *conv_script(ScriptState *st, struct script_data *data) -{ - get_val(st, data); - return data->get_if<ScriptDataRetInfo>()->script; -} - - -template<class T> -static -void push_int(struct script_stack *stack, int val) -{ - static_assert(first_type_is_any<T, ScriptDataPos, ScriptDataInt, ScriptDataArg, ScriptDataFuncRef>(), "not int type"); - - script_data nsd = T{.numi= val}; - stack->stack_datav.push_back(nsd); -} - -template<class T> -static -void push_reg(struct script_stack *stack, SIR reg) -{ - static_assert(first_type_is_any<T, ScriptDataParam, ScriptDataVariable>(), "not reg type"); - - script_data nsd = T{.reg= reg}; - stack->stack_datav.push_back(nsd); -} - -template<class T> -static -void push_script(struct script_stack *stack, const ScriptBuffer *code) -{ - static_assert(first_type_is_any<T, ScriptDataRetInfo>(), "not scriptbuf type"); - - script_data nsd = T{.script= code}; - stack->stack_datav.push_back(nsd); -} - -template<class T> -static -void push_str(struct script_stack *stack, RString str) -{ - static_assert(first_type_is_any<T, ScriptDataStr>(), "not str type"); - - script_data nsd = T{.str= str}; - stack->stack_datav.push_back(nsd); -} - -static -void push_copy(struct script_stack *stack, int pos_) -{ - script_data csd = stack->stack_datav[pos_]; - stack->stack_datav.push_back(csd); -} - -static -void pop_stack(struct script_stack *stack, int start, int end) -{ - auto it = stack->stack_datav.begin(); - stack->stack_datav.erase(it + start, it + end); -} - - -#define AARGO2(n) (st->stack->stack_datav[st->start + (n)]) -#define HARGO2(n) (st->end > st->start + (n)) - -// -// 埋め込み関数 -// -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_mes(ScriptState *st) -{ - RString mes = conv_str(st, &AARGO2(2)); - clif_scriptmes(script_rid2sd(st), st->oid, mes); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_goto(ScriptState *st) -{ - if (!AARGO2(2).is<ScriptDataPos>()) - { - PRINTF("script: goto: not label !\n"_fmt); - st->state = ScriptEndState::END; - return; - } - - st->scriptp.pos = conv_num(st, &AARGO2(2)); - st->state = ScriptEndState::GOTO; -} - -/*========================================== - * ユーザー定義関数の呼び出し - *------------------------------------------ - */ -static -void builtin_callfunc(ScriptState *st) -{ - RString str = conv_str(st, &AARGO2(2)); - const ScriptBuffer *scr = userfunc_db.get(str); - - if (scr) - { - int j = 0; - assert (st->start + 3 == st->end); -#if 0 - for (int i = st->start + 3; i < st->end; i++, j++) - push_copy(st->stack, i); -#endif - - push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ - push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ - push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ - push_script<ScriptDataRetInfo>(st->stack, st->scriptp.code); // 現在のスクリプトをプッシュ - - st->scriptp = ScriptPointer(scr, 0); - st->defsp = st->start + 4 + j; - st->state = ScriptEndState::GOTO; - } - else - { - PRINTF("script:callfunc: function not found! [%s]\n"_fmt, str); - st->state = ScriptEndState::END; - } -} - -/*========================================== - * サブルーティンの呼び出し - *------------------------------------------ - */ -static -void builtin_callsub(ScriptState *st) -{ - int pos_ = conv_num(st, &AARGO2(2)); - int j = 0; - assert (st->start + 3 == st->end); -#if 0 - for (int i = st->start + 3; i < st->end; i++, j++) - push_copy(st->stack, i); -#endif - - push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ - push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ - push_int<ScriptDataInt>(st->stack, st->scriptp.pos); // 現在のスクリプト位置をプッシュ - push_script<ScriptDataRetInfo>(st->stack, st->scriptp.code); // 現在のスクリプトをプッシュ - - st->scriptp.pos = pos_; - st->defsp = st->start + 4 + j; - st->state = ScriptEndState::GOTO; -} - -/*========================================== - * サブルーチン/ユーザー定義関数の終了 - *------------------------------------------ - */ -static -void builtin_return(ScriptState *st) -{ -#if 0 - if (HARGO2(2)) - { // 戻り値有り - push_copy(st->stack, st->start + 2); - } -#endif - st->state = ScriptEndState::RETFUNC; -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_next(ScriptState *st) -{ - st->state = ScriptEndState::STOP; - clif_scriptnext(script_rid2sd(st), st->oid); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_close(ScriptState *st) -{ - st->state = ScriptEndState::END; - clif_scriptclose(script_rid2sd(st), st->oid); -} - -static -void builtin_close2(ScriptState *st) -{ - st->state = ScriptEndState::STOP; - clif_scriptclose(script_rid2sd(st), st->oid); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_menu(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - if (sd->state.menu_or_input == 0) - { - // First half: show menu. - st->state = ScriptEndState::RERUNLINE; - sd->state.menu_or_input = 1; - - MString buf; - for (int i = st->start + 2; i < st->end; i += 2) - { - RString choice_str = conv_str(st, &AARGO2(i - st->start)); - if (!choice_str) - break; - buf += choice_str; - buf += ':'; - } - - clif_scriptmenu(script_rid2sd(st), st->oid, AString(buf)); - } - else - { - // Rerun: item is chosen from menu. - if (sd->npc_menu == 0xff) - { - // cancel - sd->state.menu_or_input = 0; - st->state = ScriptEndState::END; - return; - } - - // Actually jump to the label. - // Logic change: menu_choices is the *total* number of labels, - // not just the displayed number that ends with the "". - // (Would it be better to pop the stack before rerunning?) - int menu_choices = (st->end - (st->start + 2)) / 2; - pc_setreg(sd, SIR::from(variable_names.intern("@menu"_s)), sd->npc_menu); - sd->state.menu_or_input = 0; - if (sd->npc_menu > 0 && sd->npc_menu <= menu_choices) - { - int arg_index = (sd->npc_menu - 1) * 2 + 1; - if (!AARGO2(arg_index + 2).is<ScriptDataPos>()) - { - st->state = ScriptEndState::END; - return; - } - st->scriptp.pos = AARGO2(arg_index + 2).get_if<ScriptDataPos>()->numi; - st->state = ScriptEndState::GOTO; - } - } -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_rand(ScriptState *st) -{ - if (HARGO2(3)) - { - int min = conv_num(st, &AARGO2(2)); - int max = conv_num(st, &AARGO2(3)); - if (min > max) - std::swap(max, min); - push_int<ScriptDataInt>(st->stack, random_::in(min, max)); - } - else - { - int range = conv_num(st, &AARGO2(2)); - push_int<ScriptDataInt>(st->stack, range <= 0 ? 0 : random_::to(range)); - } -} - -/*========================================== - * Check whether the PC is at the specified location - *------------------------------------------ - */ -static -void builtin_isat(ScriptState *st) -{ - int x, y; - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x = conv_num(st, &AARGO2(3)); - y = conv_num(st, &AARGO2(4)); - - if (!sd) - return; - - push_int<ScriptDataInt>(st->stack, - (x == sd->bl_x) && (y == sd->bl_y) - && (str == sd->bl_m->name_)); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_warp(ScriptState *st) -{ - int x, y; - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x = conv_num(st, &AARGO2(3)); - y = conv_num(st, &AARGO2(4)); - if (str == "Random"_s) - pc_randomwarp(sd, BeingRemoveWhy::WARPED); - else if (str == "SavePoint"_s or str == "Save"_s) - { - if (sd->bl_m->flag.get(MapFlag::NORETURN)) - return; - - pc_setpos(sd, sd->status.save_point.map_, sd->status.save_point.x, sd->status.save_point.y, - BeingRemoveWhy::WARPED); - } - else - pc_setpos(sd, str, x, y, BeingRemoveWhy::GONE); -} - -/*========================================== - * エリア指定ワープ - *------------------------------------------ - */ -static -void builtin_areawarp_sub(dumb_ptr<block_list> bl, MapName mapname, int x, int y) -{ - dumb_ptr<map_session_data> sd = bl->is_player(); - if (mapname == "Random"_s) - pc_randomwarp(sd, BeingRemoveWhy::WARPED); - else - pc_setpos(sd, mapname, x, y, BeingRemoveWhy::GONE); -} - -static -void builtin_areawarp(ScriptState *st) -{ - int x, y; - int x0, y0, x1, y1; - - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x0 = conv_num(st, &AARGO2(3)); - y0 = conv_num(st, &AARGO2(4)); - x1 = conv_num(st, &AARGO2(5)); - y1 = conv_num(st, &AARGO2(6)); - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(7)))); - x = conv_num(st, &AARGO2(8)); - y = conv_num(st, &AARGO2(9)); - - map_local *m = map_mapname2mapid(mapname); - if (m == nullptr) - return; - - map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y), - m, - x0, y0, - x1, y1, - BL::PC); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_heal(ScriptState *st) -{ - int hp, sp; - - hp = conv_num(st, &AARGO2(2)); - sp = conv_num(st, &AARGO2(3)); - pc_heal(script_rid2sd(st), hp, sp); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_itemheal(ScriptState *st) -{ - int hp, sp; - - hp = conv_num(st, &AARGO2(2)); - sp = conv_num(st, &AARGO2(3)); - pc_itemheal(script_rid2sd(st), hp, sp); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_percentheal(ScriptState *st) -{ - int hp, sp; - - hp = conv_num(st, &AARGO2(2)); - sp = conv_num(st, &AARGO2(3)); - pc_percentheal(script_rid2sd(st), hp, sp); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_input(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = nullptr; - script_data& scrd = AARGO2(2); - assert (scrd.is<ScriptDataVariable>()); - - SIR reg = scrd.get_if<ScriptDataVariable>()->reg; - ZString name = variable_names.outtern(reg.base()); -// char prefix = name.front(); - char postfix = name.back(); - - sd = script_rid2sd(st); - if (sd->state.menu_or_input) - { - // Second time (rerun) - sd->state.menu_or_input = 0; - if (postfix == '$') - { - set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_str); - } - else - { - //commented by Lupus (check Value Number Input fix in clif.c) - //** Fix by fritz :X keeps people from abusing old input bugs - // wtf? - if (sd->npc_amount < 0) //** If input amount is less then 0 - { - clif_tradecancelled(sd); // added "Deal has been cancelled" message by Valaris - builtin_close(st); //** close - } - - set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_amount); - } - } - else - { - // First time - send prompt to client, then wait - st->state = ScriptEndState::RERUNLINE; - if (postfix == '$') - clif_scriptinputstr(sd, st->oid); - else - clif_scriptinput(sd, st->oid); - sd->state.menu_or_input = 1; - } -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_if (ScriptState *st) -{ - int sel, i; - - sel = conv_num(st, &AARGO2(2)); - if (!sel) - return; - - // 関数名をコピー - push_copy(st->stack, st->start + 3); - // 間に引数マーカを入れて - push_int<ScriptDataArg>(st->stack, 0); - // 残りの引数をコピー - for (i = st->start + 4; i < st->end; i++) - { - push_copy(st->stack, i); - } - run_func(st); -} - -/*========================================== - * 変数設定 - *------------------------------------------ - */ -static -void builtin_set(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = nullptr; - if (auto *u = AARGO2(2).get_if<ScriptDataParam>()) - { - SIR reg = u->reg; - sd = script_rid2sd(st); - - int val = conv_num(st, &AARGO2(3)); - set_reg(sd, VariableCode::PARAM, reg, val); - return; - } - - SIR reg = AARGO2(2).get_if<ScriptDataVariable>()->reg; - - ZString name = variable_names.outtern(reg.base()); - char prefix = name.front(); - char postfix = name.back(); - - if (prefix != '$') - sd = script_rid2sd(st); - - if (postfix == '$') - { - // 文字列 - RString str = conv_str(st, &AARGO2(3)); - set_reg(sd, VariableCode::VARIABLE, reg, str); - } - else - { - // 数値 - int val = conv_num(st, &AARGO2(3)); - set_reg(sd, VariableCode::VARIABLE, reg, val); - } - -} - -/*========================================== - * 配列変数設定 - *------------------------------------------ - */ -static -void builtin_setarray(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = nullptr; - SIR reg = AARGO2(2).get_if<ScriptDataVariable>()->reg; - ZString name = variable_names.outtern(reg.base()); - char prefix = name.front(); - char postfix = name.back(); - - if (prefix != '$' && prefix != '@') - { - PRINTF("builtin_setarray: illegal scope !\n"_fmt); - return; - } - if (prefix != '$') - sd = script_rid2sd(st); - - for (int j = 0, i = st->start + 3; i < st->end && j < 256; i++, j++) - { - if (postfix == '$') - set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_str(st, &AARGO2(i - st->start))); - else - set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), conv_num(st, &AARGO2(i - st->start))); - } -} - -/*========================================== - * 配列変数クリア - *------------------------------------------ - */ -static -void builtin_cleararray(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = nullptr; - SIR reg = AARGO2(2).get_if<ScriptDataVariable>()->reg; - ZString name = variable_names.outtern(reg.base()); - char prefix = name.front(); - char postfix = name.back(); - int sz = conv_num(st, &AARGO2(4)); - - if (prefix != '$' && prefix != '@') - { - PRINTF("builtin_cleararray: illegal scope !\n"_fmt); - return; - } - if (prefix != '$') - sd = script_rid2sd(st); - - for (int i = 0; i < sz; i++) - { - if (postfix == '$') - set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_str(st, &AARGO2(3))); - else - set_reg(sd, VariableCode::VARIABLE, reg.iplus(i), conv_num(st, &AARGO2(3))); - } - -} - -/*========================================== - * 配列変数のサイズ所得 - *------------------------------------------ - */ -static -int getarraysize(ScriptState *st, SIR reg) -{ - int i = reg.index(), c = i; - for (; i < 256; i++) - { - struct script_data vd = get_val2(st, reg.iplus(i)); - MATCH (vd) - { - CASE (const ScriptDataStr&, u) - { - if (u.str[0]) - c = i; - goto continue_outer; - } - CASE (const ScriptDataInt&, u) - { - if (u.numi) - c = i; - goto continue_outer; - } - } - abort(); - continue_outer: - ; - } - return c + 1; -} - -static -void builtin_getarraysize(ScriptState *st) -{ - SIR reg = AARGO2(2).get_if<ScriptDataVariable>()->reg; - ZString name = variable_names.outtern(reg.base()); - char prefix = name.front(); - - if (prefix != '$' && prefix != '@') - { - PRINTF("builtin_copyarray: illegal scope !\n"_fmt); - return; - } - - push_int<ScriptDataInt>(st->stack, getarraysize(st, reg)); -} - -/*========================================== - * 指定要素を表す値(キー)を所得する - *------------------------------------------ - */ -static -void builtin_getelementofarray(ScriptState *st) -{ - if (auto *u = AARGO2(2).get_if<ScriptDataVariable>()) - { - int i = conv_num(st, &AARGO2(3)); - if (i > 255 || i < 0) - { - PRINTF("script: getelementofarray (operator[]): param2 illegal number %d\n"_fmt, - i); - push_int<ScriptDataInt>(st->stack, 0); - } - else - { - push_reg<ScriptDataVariable>(st->stack, - u->reg.iplus(i)); - } - } - else - { - PRINTF("script: getelementofarray (operator[]): param1 not name !\n"_fmt); - push_int<ScriptDataInt>(st->stack, 0); - } -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_setlook(ScriptState *st) -{ - LOOK type = LOOK(conv_num(st, &AARGO2(2))); - int val = conv_num(st, &AARGO2(3)); - - pc_changelook(script_rid2sd(st), type, val); - -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_countitem(ScriptState *st) -{ - ItemNameId nameid; - int count = 0; - dumb_ptr<map_session_data> sd; - - struct script_data *data; - - sd = script_rid2sd(st); - - data = &AARGO2(2); - get_val(st, data); - if (data->is<ScriptDataStr>()) - { - ZString name = ZString(conv_str(st, data)); - struct item_data *item_data = itemdb_searchname(name); - if (item_data != nullptr) - nameid = item_data->nameid; - } - else - nameid = wrap<ItemNameId>(conv_num(st, data)); - - if (nameid) - { - for (IOff0 i : IOff0::iter()) - { - if (sd->status.inventory[i].nameid == nameid) - count += sd->status.inventory[i].amount; - } - } - else - { - if (battle_config.error_log) - PRINTF("wrong item ID : countitem (%i)\n"_fmt, nameid); - } - push_int<ScriptDataInt>(st->stack, count); - -} - -/*========================================== - * 重量チェック - *------------------------------------------ - */ -static -void builtin_checkweight(ScriptState *st) -{ - ItemNameId nameid; - int amount; - dumb_ptr<map_session_data> sd; - struct script_data *data; - - sd = script_rid2sd(st); - - data = &AARGO2(2); - get_val(st, data); - if (data->is<ScriptDataStr>()) - { - ZString name = ZString(conv_str(st, data)); - struct item_data *item_data = itemdb_searchname(name); - if (item_data) - nameid = item_data->nameid; - } - else - nameid = wrap<ItemNameId>(conv_num(st, data)); - - amount = conv_num(st, &AARGO2(3)); - if (amount <= 0 || !nameid) - { - //if get wrong item ID or amount<=0, don't count weight of non existing items - push_int<ScriptDataInt>(st->stack, 0); - return; - } - - if (itemdb_weight(nameid) * amount + sd->weight > sd->max_weight) - { - push_int<ScriptDataInt>(st->stack, 0); - } - else - { - push_int<ScriptDataInt>(st->stack, 1); - } - -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_getitem(ScriptState *st) -{ - ItemNameId nameid; - int amount; - dumb_ptr<map_session_data> sd; - struct script_data *data; - - sd = script_rid2sd(st); - - data = &AARGO2(2); - get_val(st, data); - if (data->is<ScriptDataStr>()) - { - ZString name = ZString(conv_str(st, data)); - struct item_data *item_data = itemdb_searchname(name); - if (item_data != nullptr) - nameid = item_data->nameid; - } - else - nameid = wrap<ItemNameId>(conv_num(st, data)); - - if ((amount = - conv_num(st, &AARGO2(3))) <= 0) - { - return; //return if amount <=0, skip the useles iteration - } - - if (nameid) - { - Item item_tmp {}; - item_tmp.nameid = nameid; - if (HARGO2(5)) //アイテムを指定したIDに渡す - sd = map_id2sd(wrap<BlockId>(conv_num(st, &AARGO2(5)))); - if (sd == nullptr) //アイテムを渡す相手がいなかったらお帰り - return; - PickupFail flag; - if ((flag = pc_additem(sd, &item_tmp, amount)) != PickupFail::OKAY) - { - clif_additem(sd, IOff0::from(0), 0, flag); - map_addflooritem(&item_tmp, amount, - sd->bl_m, sd->bl_x, sd->bl_y, - nullptr, nullptr, nullptr); - } - } - -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_makeitem(ScriptState *st) -{ - ItemNameId nameid; - int amount; - int x, y; - dumb_ptr<map_session_data> sd; - struct script_data *data; - - sd = script_rid2sd(st); - - data = &AARGO2(2); - get_val(st, data); - if (data->is<ScriptDataStr>()) - { - ZString name = ZString(conv_str(st, data)); - struct item_data *item_data = itemdb_searchname(name); - if (item_data) - nameid = item_data->nameid; - } - else - nameid = wrap<ItemNameId>(conv_num(st, data)); - - amount = conv_num(st, &AARGO2(3)); - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(4)))); - x = conv_num(st, &AARGO2(5)); - y = conv_num(st, &AARGO2(6)); - - map_local *m; - if (sd && mapname == MOB_THIS_MAP) - m = sd->bl_m; - else - m = map_mapname2mapid(mapname); - - if (nameid) - { - Item item_tmp {}; - item_tmp.nameid = nameid; - - map_addflooritem(&item_tmp, amount, m, x, y, nullptr, nullptr, nullptr); - } -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_delitem(ScriptState *st) -{ - ItemNameId nameid; - int amount; - dumb_ptr<map_session_data> sd; - struct script_data *data; - - sd = script_rid2sd(st); - - data = &AARGO2(2); - get_val(st, data); - if (data->is<ScriptDataStr>()) - { - ZString name = ZString(conv_str(st, data)); - struct item_data *item_data = itemdb_searchname(name); - if (item_data) - nameid = item_data->nameid; - } - else - nameid = wrap<ItemNameId>(conv_num(st, data)); - - amount = conv_num(st, &AARGO2(3)); - - if (!nameid || amount <= 0) - { - //by Lupus. Don't run FOR if u got wrong item ID or amount<=0 - return; - } - - for (IOff0 i : IOff0::iter()) - { - if (sd->status.inventory[i].nameid == nameid) - { - if (sd->status.inventory[i].amount >= amount) - { - pc_delitem(sd, i, amount, 0); - break; - } - else - { - amount -= sd->status.inventory[i].amount; - if (amount == 0) - amount = sd->status.inventory[i].amount; - pc_delitem(sd, i, amount, 0); - break; - } - } - } - -} - -/*========================================== - *キャラ関係のパラメータ取得 - *------------------------------------------ - */ -static -void builtin_readparam(ScriptState *st) -{ - dumb_ptr<map_session_data> sd; - - SP type = SP(conv_num(st, &AARGO2(2))); - if (HARGO2(3)) - sd = map_nick2sd(stringish<CharName>(ZString(conv_str(st, &AARGO2(3))))); - else - sd = script_rid2sd(st); - - if (sd == nullptr) - { - push_int<ScriptDataInt>(st->stack, -1); - return; - } - - push_int<ScriptDataInt>(st->stack, pc_readparam(sd, type)); - -} - -/*========================================== - *キャラ関係のID取得 - *------------------------------------------ - */ -static -void builtin_getcharid(ScriptState *st) -{ - int num; - dumb_ptr<map_session_data> sd; - - num = conv_num(st, &AARGO2(2)); - if (HARGO2(3)) - sd = map_nick2sd(stringish<CharName>(ZString(conv_str(st, &AARGO2(3))))); - else - sd = script_rid2sd(st); - if (sd == nullptr) - { - push_int<ScriptDataInt>(st->stack, -1); - return; - } - if (num == 0) - push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status_key.char_id)); - if (num == 1) - push_int<ScriptDataInt>(st->stack, unwrap<PartyId>(sd->status.party_id)); - if (num == 2) - push_int<ScriptDataInt>(st->stack, 0/*guild_id*/); - if (num == 3) - push_int<ScriptDataInt>(st->stack, unwrap<AccountId>(sd->status_key.account_id)); -} - -/*========================================== - *指定IDのPT名取得 - *------------------------------------------ - */ -static -RString builtin_getpartyname_sub(PartyId party_id) -{ - PartyPair p = party_search(party_id); - - if (p) - return p->name; - - return RString(); -} - -/*========================================== - * キャラクタの名前 - *------------------------------------------ - */ -static -void builtin_strcharinfo(ScriptState *st) -{ - dumb_ptr<map_session_data> sd; - int num; - - sd = script_rid2sd(st); - num = conv_num(st, &AARGO2(2)); - if (num == 0) - { - RString buf = sd->status_key.name.to__actual(); - push_str<ScriptDataStr>(st->stack, buf); - } - if (num == 1) - { - RString buf = builtin_getpartyname_sub(sd->status.party_id); - if (buf) - push_str<ScriptDataStr>(st->stack, buf); - else - push_str<ScriptDataStr>(st->stack, ""_s); - } - if (num == 2) - { - // was: guild name - push_str<ScriptDataStr>(st->stack, ""_s); - } - -} - -// indexed by the equip_* in db/const.txt -// TODO change to use EQUIP -static -Array<EPOS, 11> equip //= -{{ - EPOS::HAT, - EPOS::MISC1, - EPOS::SHIELD, - EPOS::WEAPON, - EPOS::GLOVES, - EPOS::SHOES, - EPOS::CAPE, - EPOS::MISC2, - EPOS::TORSO, - EPOS::LEGS, - EPOS::ARROW, -}}; - -/*========================================== - * GetEquipID(Pos); Pos: 1-10 - *------------------------------------------ - */ -static -void builtin_getequipid(ScriptState *st) -{ - int num; - dumb_ptr<map_session_data> sd; - struct item_data *item; - - sd = script_rid2sd(st); - if (sd == nullptr) - { - PRINTF("getequipid: sd == nullptr\n"_fmt); - return; - } - num = conv_num(st, &AARGO2(2)); - IOff0 i = pc_checkequip(sd, equip[num - 1]); - if (i.ok()) - { - item = sd->inventory_data[i]; - if (item) - push_int<ScriptDataInt>(st->stack, unwrap<ItemNameId>(item->nameid)); - else - push_int<ScriptDataInt>(st->stack, 0); - } - else - { - push_int<ScriptDataInt>(st->stack, -1); - } -} - -/*========================================== - * 装備名文字列(精錬メニュー用) - *------------------------------------------ - */ -static -void builtin_getequipname(ScriptState *st) -{ - int num; - dumb_ptr<map_session_data> sd; - struct item_data *item; - - AString buf; - - sd = script_rid2sd(st); - num = conv_num(st, &AARGO2(2)); - IOff0 i = pc_checkequip(sd, equip[num - 1]); - if (i.ok()) - { - item = sd->inventory_data[i]; - if (item) - buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], item->jname); - else - buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], pos_str[10]); - } - else - { - buf = STRPRINTF("%s-[%s]"_fmt, pos_str[num - 1], pos_str[10]); - } - push_str<ScriptDataStr>(st->stack, buf); - -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_statusup2(ScriptState *st) -{ - SP type = SP(conv_num(st, &AARGO2(2))); - int val = conv_num(st, &AARGO2(3)); - dumb_ptr<map_session_data> sd = script_rid2sd(st); - pc_statusup2(sd, type, val); - -} - -/*========================================== - * 装備品による能力値ボーナス - *------------------------------------------ - */ -static -void builtin_bonus(ScriptState *st) -{ - SP type = SP(conv_num(st, &AARGO2(2))); - int val = conv_num(st, &AARGO2(3)); - dumb_ptr<map_session_data> sd = script_rid2sd(st); - pc_bonus(sd, type, val); - -} - -/*========================================== - * 装備品による能力値ボーナス - *------------------------------------------ - */ -static -void builtin_bonus2(ScriptState *st) -{ - SP type = SP(conv_num(st, &AARGO2(2))); - int type2 = conv_num(st, &AARGO2(3)); - int val = conv_num(st, &AARGO2(4)); - dumb_ptr<map_session_data> sd = script_rid2sd(st); - pc_bonus2(sd, type, type2, val); - -} - -/*========================================== - * スキル所得 - *------------------------------------------ - */ -static -void builtin_skill(ScriptState *st) -{ - int level, flag = 1; - dumb_ptr<map_session_data> sd; - - SkillID id = SkillID(conv_num(st, &AARGO2(2))); - level = conv_num(st, &AARGO2(3)); - if (HARGO2(4)) - flag = conv_num(st, &AARGO2(4)); - sd = script_rid2sd(st); - pc_skill(sd, id, level, flag); - clif_skillinfoblock(sd); - -} - -/*========================================== - * [Fate] Sets the skill level permanently - *------------------------------------------ - */ -static -void builtin_setskill(ScriptState *st) -{ - int level; - dumb_ptr<map_session_data> sd; - - SkillID id = static_cast<SkillID>(conv_num(st, &AARGO2(2))); - level = conv_num(st, &AARGO2(3)); - sd = script_rid2sd(st); - - level = std::min(level, MAX_SKILL_LEVEL); - level = std::max(level, 0); - sd->status.skill[id].lv = level; - clif_skillinfoblock(sd); -} - -/*========================================== - * スキルレベル所得 - *------------------------------------------ - */ -static -void builtin_getskilllv(ScriptState *st) -{ - SkillID id = SkillID(conv_num(st, &AARGO2(2))); - push_int<ScriptDataInt>(st->stack, pc_checkskill(script_rid2sd(st), id)); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_getgmlevel(ScriptState *st) -{ - push_int<ScriptDataInt>(st->stack, pc_isGM(script_rid2sd(st)).get_all_bits()); -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_end(ScriptState *st) -{ - st->state = ScriptEndState::END; -} - -/*========================================== - * [Freeyorp] Return the current opt2 - *------------------------------------------ - */ - -static -void builtin_getopt2(ScriptState *st) -{ - dumb_ptr<map_session_data> sd; - - sd = script_rid2sd(st); - - push_int<ScriptDataInt>(st->stack, static_cast<uint16_t>(sd->opt2)); - -} - -/*========================================== - * [Freeyorp] Sets opt2 - *------------------------------------------ - */ - -static -void builtin_setopt2(ScriptState *st) -{ - dumb_ptr<map_session_data> sd; - - Opt2 new_opt2 = Opt2(conv_num(st, &AARGO2(2))); - sd = script_rid2sd(st); - if (new_opt2 == sd->opt2) - return; - sd->opt2 = new_opt2; - clif_changeoption(sd); - pc_calcstatus(sd, 0); - -} - -/*========================================== - * セーブポイントの保存 - *------------------------------------------ - */ -static -void builtin_savepoint(ScriptState *st) -{ - int x, y; - - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x = conv_num(st, &AARGO2(3)); - y = conv_num(st, &AARGO2(4)); - pc_setsavepoint(script_rid2sd(st), str, x, y); -} - -/*========================================== - * gettimetick(type) - * - * type The type of time measurement. - * Specify 0 for the system tick, 1 for - * seconds elapsed today, or 2 for seconds - * since Unix epoch. Defaults to 0 for any - * other value. - *------------------------------------------ - */ -static -void builtin_gettimetick(ScriptState *st) /* Asgard Version */ -{ - int type; - type = conv_num(st, &AARGO2(2)); - - switch (type) - { - /* Number of seconds elapsed today(0-86399, 00:00:00-23:59:59). */ - case 1: - { - struct tm t = TimeT::now(); - push_int<ScriptDataInt>(st->stack, - t.tm_hour * 3600 + t.tm_min * 60 + t.tm_sec); - break; - } - /* Seconds since Unix epoch. */ - case 2: - push_int<ScriptDataInt>(st->stack, static_cast<time_t>(TimeT::now())); - break; - /* System tick(unsigned int, and yes, it will wrap). */ - case 0: - default: - push_int<ScriptDataInt>(st->stack, gettick().time_since_epoch().count()); - break; - } -} - -/*========================================== - * GetTime(Type); - * 1: Sec 2: Min 3: Hour - * 4: WeekDay 5: MonthDay 6: Month - * 7: Year - *------------------------------------------ - */ -static -void builtin_gettime(ScriptState *st) /* Asgard Version */ -{ - int type = conv_num(st, &AARGO2(2)); - - struct tm t = TimeT::now(); - - switch (type) - { - case 1: //Sec(0~59) - push_int<ScriptDataInt>(st->stack, t.tm_sec); - break; - case 2: //Min(0~59) - push_int<ScriptDataInt>(st->stack, t.tm_min); - break; - case 3: //Hour(0~23) - push_int<ScriptDataInt>(st->stack, t.tm_hour); - break; - case 4: //WeekDay(0~6) - push_int<ScriptDataInt>(st->stack, t.tm_wday); - break; - case 5: //MonthDay(01~31) - push_int<ScriptDataInt>(st->stack, t.tm_mday); - break; - case 6: //Month(01~12) - push_int<ScriptDataInt>(st->stack, t.tm_mon + 1); - break; - case 7: //Year(20xx) - push_int<ScriptDataInt>(st->stack, t.tm_year + 1900); - break; - default: //(format error) - push_int<ScriptDataInt>(st->stack, -1); - break; - } -} - -/*========================================== - * カプラ倉庫を開く - *------------------------------------------ - */ -static -void builtin_openstorage(ScriptState *st) -{ -// int sync = 0; -// if (st->end >= 3) sync = conv_num(st,& (st->stack->stack_data[st->start+2])); - dumb_ptr<map_session_data> sd = script_rid2sd(st); - -// if (sync) { - st->state = ScriptEndState::STOP; - sd->npc_flags.storage = 1; -// } else st->state = ScriptEndState::END; - - storage_storageopen(sd); -} - -/*========================================== - * NPCで経験値上げる - *------------------------------------------ - */ -static -void builtin_getexp(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - int base = 0, job = 0; - - base = conv_num(st, &AARGO2(2)); - job = conv_num(st, &AARGO2(3)); - if (base < 0 || job < 0) - return; - if (sd) - pc_gainexp_reason(sd, base, job, PC_GAINEXP_REASON::SCRIPT); - -} - -/*========================================== - * モンスター発生 - *------------------------------------------ - */ -static -void builtin_monster(ScriptState *st) -{ - Species mob_class; - int amount, x, y; - NpcEvent event; - - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x = conv_num(st, &AARGO2(3)); - y = conv_num(st, &AARGO2(4)); - MobName str = stringish<MobName>(ZString(conv_str(st, &AARGO2(5)))); - mob_class = wrap<Species>(conv_num(st, &AARGO2(6))); - amount = conv_num(st, &AARGO2(7)); - if (HARGO2(8)) - extract(ZString(conv_str(st, &AARGO2(8))), &event); - - mob_once_spawn(map_id2sd(st->rid), mapname, x, y, str, mob_class, amount, - event); -} - -/*========================================== - * モンスター発生 - *------------------------------------------ - */ -static -void builtin_areamonster(ScriptState *st) -{ - Species mob_class; - int amount, x0, y0, x1, y1; - NpcEvent event; - - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x0 = conv_num(st, &AARGO2(3)); - y0 = conv_num(st, &AARGO2(4)); - x1 = conv_num(st, &AARGO2(5)); - y1 = conv_num(st, &AARGO2(6)); - MobName str = stringish<MobName>(ZString(conv_str(st, &AARGO2(7)))); - mob_class = wrap<Species>(conv_num(st, &AARGO2(8))); - amount = conv_num(st, &AARGO2(9)); - if (HARGO2(10)) - extract(ZString(conv_str(st, &AARGO2(10))), &event); - - mob_once_spawn_area(map_id2sd(st->rid), mapname, x0, y0, x1, y1, str, mob_class, - amount, event); -} - -/*========================================== - * モンスター削除 - *------------------------------------------ - */ -static -void builtin_killmonster_sub(dumb_ptr<block_list> bl, NpcEvent event) -{ - dumb_ptr<mob_data> md = bl->is_mob(); - if (event) - { - if (event == md->npc_event) - mob_delete(md); - return; - } - else if (!event) - { - if (md->spawn.delay1 == static_cast<interval_t>(-1) - && md->spawn.delay2 == static_cast<interval_t>(-1)) - mob_delete(md); - return; - } -} - -static -void builtin_killmonster(ScriptState *st) -{ - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - ZString event_ = ZString(conv_str(st, &AARGO2(3))); - NpcEvent event; - if (event_ != "All"_s) - extract(event_, &event); - - map_local *m = map_mapname2mapid(mapname); - if (m == nullptr) - return; - map_foreachinarea(std::bind(builtin_killmonster_sub, ph::_1, event), - m, - 0, 0, - m->xs, m->ys, - BL::MOB); -} - -static -void builtin_killmonsterall_sub(dumb_ptr<block_list> bl) -{ - mob_delete(bl->is_mob()); -} - -static -void builtin_killmonsterall(ScriptState *st) -{ - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - - map_local *m = map_mapname2mapid(mapname); - if (m == nullptr) - return; - map_foreachinarea(builtin_killmonsterall_sub, - m, - 0, 0, - m->xs, m->ys, - BL::MOB); -} - -/*========================================== - * NPC主体イベント実行 - *------------------------------------------ - */ -static -void builtin_donpcevent(ScriptState *st) -{ - ZString event_ = ZString(conv_str(st, &AARGO2(2))); - NpcEvent event; - extract(event_, &event); - npc_event_do(event); -} - -/*========================================== - * イベントタイマー追加 - *------------------------------------------ - */ -static -void builtin_addtimer(ScriptState *st) -{ - interval_t tick = static_cast<interval_t>(conv_num(st, &AARGO2(2))); - ZString event_ = ZString(conv_str(st, &AARGO2(3))); - NpcEvent event; - extract(event_, &event); - pc_addeventtimer(script_rid2sd(st), tick, event); -} - -/*========================================== - * NPCタイマー初期化 - *------------------------------------------ - */ -static -void builtin_initnpctimer(ScriptState *st) -{ - dumb_ptr<npc_data> nd_; - if (HARGO2(2)) - nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(2))))); - else - nd_ = map_id_is_npc(st->oid); - assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); - dumb_ptr<npc_data_script> nd = nd_->is_script(); - - npc_settimerevent_tick(nd, interval_t::zero()); - npc_timerevent_start(nd); -} - -/*========================================== - * NPCタイマー開始 - *------------------------------------------ - */ -static -void builtin_startnpctimer(ScriptState *st) -{ - dumb_ptr<npc_data> nd_; - if (HARGO2(2)) - nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(2))))); - else - nd_ = map_id_is_npc(st->oid); - assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); - dumb_ptr<npc_data_script> nd = nd_->is_script(); - - npc_timerevent_start(nd); -} - -/*========================================== - * NPCタイマー停止 - *------------------------------------------ - */ -static -void builtin_stopnpctimer(ScriptState *st) -{ - dumb_ptr<npc_data> nd_; - if (HARGO2(2)) - nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(2))))); - else - nd_ = map_id_is_npc(st->oid); - assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); - dumb_ptr<npc_data_script> nd = nd_->is_script(); - - npc_timerevent_stop(nd); -} - -/*========================================== - * NPCタイマー情報所得 - *------------------------------------------ - */ -static -void builtin_getnpctimer(ScriptState *st) -{ - dumb_ptr<npc_data> nd_; - int type = conv_num(st, &AARGO2(2)); - int val = 0; - if (HARGO2(3)) - nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(3))))); - else - nd_ = map_id_is_npc(st->oid); - assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); - dumb_ptr<npc_data_script> nd = nd_->is_script(); - - switch (type) - { - case 0: - val = npc_gettimerevent_tick(nd).count(); - break; - case 1: - val = nd->scr.timer_active; - break; - case 2: - val = nd->scr.timer_eventv.size(); - break; - } - push_int<ScriptDataInt>(st->stack, val); -} - -/*========================================== - * NPCタイマー値設定 - *------------------------------------------ - */ -static -void builtin_setnpctimer(ScriptState *st) -{ - dumb_ptr<npc_data> nd_; - interval_t tick = static_cast<interval_t>(conv_num(st, &AARGO2(2))); - if (HARGO2(3)) - nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARGO2(3))))); - else - nd_ = map_id_is_npc(st->oid); - assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); - dumb_ptr<npc_data_script> nd = nd_->is_script(); - - npc_settimerevent_tick(nd, tick); -} - -/*========================================== - * 天の声アナウンス - *------------------------------------------ - */ -static -void builtin_announce(ScriptState *st) -{ - int flag; - ZString str = ZString(conv_str(st, &AARGO2(2))); - flag = conv_num(st, &AARGO2(3)); - - if (flag & 0x0f) - { - dumb_ptr<block_list> bl; - if (flag & 0x08) - bl = map_id2bl(st->oid); - else - bl = script_rid2sd(st); - clif_GMmessage(bl, str, flag); - } - else - intif_GMmessage(str); -} - -/*========================================== - * 天の声アナウンス(特定マップ) - *------------------------------------------ - */ -static -void builtin_mapannounce_sub(dumb_ptr<block_list> bl, XString str, int flag) -{ - clif_GMmessage(bl, str, flag | 3); -} - -static -void builtin_mapannounce(ScriptState *st) -{ - int flag; - - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - ZString str = ZString(conv_str(st, &AARGO2(3))); - flag = conv_num(st, &AARGO2(4)); - - map_local *m = map_mapname2mapid(mapname); - if (m == nullptr) - return; - map_foreachinarea(std::bind(builtin_mapannounce_sub, ph::_1, str, flag & 0x10), - m, - 0, 0, - m->xs, m->ys, - BL::PC); -} - -/*========================================== - * ユーザー数所得 - *------------------------------------------ - */ -static -void builtin_getusers(ScriptState *st) -{ - int flag = conv_num(st, &AARGO2(2)); - dumb_ptr<block_list> bl = map_id2bl((flag & 0x08) ? st->oid : st->rid); - int val = 0; - switch (flag & 0x07) - { - case 0: - val = bl->bl_m->users; - break; - case 1: - val = map_getusers(); - break; - } - push_int<ScriptDataInt>(st->stack, val); -} - -/*========================================== - * マップ指定ユーザー数所得 - *------------------------------------------ - */ -static -void builtin_getmapusers(ScriptState *st) -{ - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - map_local *m = map_mapname2mapid(str); - if (m == nullptr) - { - push_int<ScriptDataInt>(st->stack, -1); - return; - } - push_int<ScriptDataInt>(st->stack, m->users); -} - -/*========================================== - * エリア指定ユーザー数所得 - *------------------------------------------ - */ -static -void builtin_getareausers_sub(dumb_ptr<block_list> bl, int *users) -{ - if (bool(bl->is_player()->status.option & Option::HIDE)) - return; - (*users)++; -} - -static -void builtin_getareausers_living_sub(dumb_ptr<block_list> bl, int *users) -{ - if (bool(bl->is_player()->status.option & Option::HIDE)) - return; - if (!pc_isdead(bl->is_player())) - (*users)++; -} - -static -void builtin_getareausers(ScriptState *st) -{ - int x0, y0, x1, y1, users = 0; - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x0 = conv_num(st, &AARGO2(3)); - y0 = conv_num(st, &AARGO2(4)); - x1 = conv_num(st, &AARGO2(5)); - y1 = conv_num(st, &AARGO2(6)); - - int living = 0; - if (HARGO2(7)) - { - living = conv_num(st, &AARGO2(7)); - } - map_local *m = map_mapname2mapid(str); - if (m == nullptr) - { - push_int<ScriptDataInt>(st->stack, -1); - return; - } - map_foreachinarea(std::bind(living ? builtin_getareausers_living_sub: builtin_getareausers_sub, ph::_1, &users), - m, - x0, y0, - x1, y1, - BL::PC); - push_int<ScriptDataInt>(st->stack, users); -} - -/*========================================== - * エリア指定ドロップアイテム数所得 - *------------------------------------------ - */ -static -void builtin_getareadropitem_sub(dumb_ptr<block_list> bl, ItemNameId item, int *amount) -{ - dumb_ptr<flooritem_data> drop = bl->is_item(); - - if (drop->item_data.nameid == item) - (*amount) += drop->item_data.amount; - -} - -static -void builtin_getareadropitem_sub_anddelete(dumb_ptr<block_list> bl, ItemNameId item, int *amount) -{ - dumb_ptr<flooritem_data> drop = bl->is_item(); - - if (drop->item_data.nameid == item) - { - (*amount) += drop->item_data.amount; - clif_clearflooritem(drop, nullptr); - map_delobject(drop->bl_id, drop->bl_type); - } -} - -static -void builtin_getareadropitem(ScriptState *st) -{ - ItemNameId item; - int x0, y0, x1, y1, amount = 0, delitems = 0; - struct script_data *data; - - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x0 = conv_num(st, &AARGO2(3)); - y0 = conv_num(st, &AARGO2(4)); - x1 = conv_num(st, &AARGO2(5)); - y1 = conv_num(st, &AARGO2(6)); - - data = &AARGO2(7); - get_val(st, data); - if (data->is<ScriptDataStr>()) - { - ZString name = ZString(conv_str(st, data)); - struct item_data *item_data = itemdb_searchname(name); - if (item_data) - item = item_data->nameid; - } - else - item = wrap<ItemNameId>(conv_num(st, data)); - - if (HARGO2(8)) - delitems = conv_num(st, &AARGO2(8)); - - map_local *m = map_mapname2mapid(str); - if (m == nullptr) - { - push_int<ScriptDataInt>(st->stack, -1); - return; - } - if (delitems) - map_foreachinarea(std::bind(builtin_getareadropitem_sub_anddelete, ph::_1, item, &amount), - m, - x0, y0, - x1, y1, - BL::ITEM); - else - map_foreachinarea(std::bind(builtin_getareadropitem_sub, ph::_1, item, &amount), - m, - x0, y0, - x1, y1, - BL::ITEM); - - push_int<ScriptDataInt>(st->stack, amount); -} - -/*========================================== - * NPCの有効化 - *------------------------------------------ - */ -static -void builtin_enablenpc(ScriptState *st) -{ - NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2)))); - npc_enable(str, 1); -} - -/*========================================== - * NPCの無効化 - *------------------------------------------ - */ -static -void builtin_disablenpc(ScriptState *st) -{ - NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2)))); - npc_enable(str, 0); -} - -/*========================================== - * 状態異常にかかる - *------------------------------------------ - */ -static -void builtin_sc_start(ScriptState *st) -{ - dumb_ptr<block_list> bl; - int val1; - StatusChange type = static_cast<StatusChange>(conv_num(st, &AARGO2(2))); - interval_t tick = static_cast<interval_t>(conv_num(st, &AARGO2(3))); - if (tick < 1_s) - // work around old behaviour of: - // speed potion - // atk potion - // matk potion - // - // which used to use seconds - // all others used milliseconds - tick *= 1000; - val1 = conv_num(st, &AARGO2(4)); - if (HARGO2(5)) //指定したキャラを状態異常にする - bl = map_id2bl(wrap<BlockId>(conv_num(st, &AARGO2(5)))); - else - bl = map_id2bl(st->rid); - skill_status_change_start(bl, type, val1, tick); -} - -/*========================================== - * 状態異常が直る - *------------------------------------------ - */ -static -void builtin_sc_end(ScriptState *st) -{ - dumb_ptr<block_list> bl; - StatusChange type = StatusChange(conv_num(st, &AARGO2(2))); - bl = map_id2bl(st->rid); - skill_status_change_end(bl, type, nullptr); -} - -static -void builtin_sc_check(ScriptState *st) -{ - dumb_ptr<block_list> bl; - StatusChange type = StatusChange(conv_num(st, &AARGO2(2))); - bl = map_id2bl(st->rid); - - push_int<ScriptDataInt>(st->stack, skill_status_change_active(bl, type)); - -} - -/*========================================== - * - *------------------------------------------ - */ -static -void builtin_debugmes(ScriptState *st) -{ - RString mes = conv_str(st, &AARGO2(2)); - PRINTF("script debug : %d %d : %s\n"_fmt, - st->rid, st->oid, mes); -} - -/*========================================== - * ステータスリセット - *------------------------------------------ - */ -static -void builtin_resetstatus(ScriptState *st) -{ - dumb_ptr<map_session_data> sd; - sd = script_rid2sd(st); - pc_resetstate(sd); -} - -/*========================================== - * 性別変換 - *------------------------------------------ - */ -static -void builtin_changesex(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = nullptr; - sd = script_rid2sd(st); - - chrif_char_ask_name(AccountId(), sd->status_key.name, 5, HumanTimeDiff()); // type: 5 - changesex - chrif_save(sd); -} - -/*========================================== - * RIDのアタッチ - *------------------------------------------ - */ -static -void builtin_attachrid(ScriptState *st) -{ - st->rid = wrap<BlockId>(conv_num(st, &AARGO2(2))); - push_int<ScriptDataInt>(st->stack, (map_id2sd(st->rid) != nullptr)); -} - -/*========================================== - * RIDのデタッチ - *------------------------------------------ - */ -static -void builtin_detachrid(ScriptState *st) -{ - st->rid = BlockId(); -} - -/*========================================== - * 存在チェック - *------------------------------------------ - */ -static -void builtin_isloggedin(ScriptState *st) -{ - push_int<ScriptDataInt>(st->stack, - map_id2sd(wrap<BlockId>(conv_num(st, &AARGO2(2)))) != nullptr); -} - -static -void builtin_setmapflag(ScriptState *st) -{ - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - int i = conv_num(st, &AARGO2(3)); - MapFlag mf = map_flag_from_int(i); - map_local *m = map_mapname2mapid(str); - if (m != nullptr) - { - m->flag.set(mf, 1); - } -} - -static -void builtin_removemapflag(ScriptState *st) -{ - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - int i = conv_num(st, &AARGO2(3)); - MapFlag mf = map_flag_from_int(i); - map_local *m = map_mapname2mapid(str); - if (m != nullptr) - { - m->flag.set(mf, 0); - } -} - -static -void builtin_getmapflag(ScriptState *st) -{ - int r = -1; - - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - int i = conv_num(st, &AARGO2(3)); - MapFlag mf = map_flag_from_int(i); - map_local *m = map_mapname2mapid(str); - if (m != nullptr) - { - r = m->flag.get(mf); - } - - push_int<ScriptDataInt>(st->stack, r); -} - -static -void builtin_pvpon(ScriptState *st) -{ - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - map_local *m = map_mapname2mapid(str); - if (m != nullptr && !m->flag.get(MapFlag::PVP) && !m->flag.get(MapFlag::NOPVP)) - { - m->flag.set(MapFlag::PVP, 1); - - if (battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris] - return; - - for (io::FD i : iter_fds()) - { - Session *s = get_session(i); - if (!s) - continue; - map_session_data *pl_sd = static_cast<map_session_data *>(s->session_data.get()); - if (pl_sd && pl_sd->state.auth) - { - if (m == pl_sd->bl_m && !pl_sd->pvp_timer) - { - pl_sd->pvp_timer = Timer(gettick() + 200_ms, - std::bind(pc_calc_pvprank_timer, ph::_1, ph::_2, - pl_sd->bl_id)); - pl_sd->pvp_rank = 0; - pl_sd->pvp_lastusers = 0; - pl_sd->pvp_point = 5; - } - } - } - } - -} - -static -void builtin_pvpoff(ScriptState *st) -{ - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - map_local *m = map_mapname2mapid(str); - if (m != nullptr && m->flag.get(MapFlag::PVP) && m->flag.get(MapFlag::NOPVP)) - { - m->flag.set(MapFlag::PVP, 0); - - if (battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris] - return; - - for (io::FD i : iter_fds()) - { - Session *s = get_session(i); - if (!s) - continue; - map_session_data *pl_sd = static_cast<map_session_data *>(s->session_data.get()); - if (pl_sd && pl_sd->state.auth) - { - if (m == pl_sd->bl_m) - { - pl_sd->pvp_timer.cancel(); - } - } - } - } - -} - -/*========================================== - * NPCエモーション - *------------------------------------------ - */ - -static -void builtin_emotion(ScriptState *st) -{ - int type; - type = conv_num(st, &AARGO2(2)); - if (type < 0 || type > 100) - return; - clif_emotion(map_id2bl(st->oid), type); -} - -static -void builtin_mapwarp(ScriptState *st) // Added by RoVeRT -{ - int x, y; - int x0, y0, x1, y1; - - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x0 = 0; - y0 = 0; - map_local *m = map_mapname2mapid(mapname); - x1 = m->xs; - y1 = m->ys; - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(3)))); - x = conv_num(st, &AARGO2(4)); - y = conv_num(st, &AARGO2(5)); - - if (m == nullptr) - return; - - map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y), - m, - x0, y0, - x1, y1, - BL::PC); -} - -static -void builtin_cmdothernpc(ScriptState *st) // Added by RoVeRT -{ - NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2)))); - ZString command = ZString(conv_str(st, &AARGO2(3))); - - npc_command(map_id2sd(st->rid), npc, command); -} - -static -void builtin_mobcount_sub(dumb_ptr<block_list> bl, NpcEvent event, int *c) -{ - if (event == bl->is_mob()->npc_event) - (*c)++; -} - -static -void builtin_mobcount(ScriptState *st) // Added by RoVeRT -{ - int c = 0; - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - ZString event_ = ZString(conv_str(st, &AARGO2(3))); - NpcEvent event; - extract(event_, &event); - - map_local *m = map_mapname2mapid(mapname); - if (m == nullptr) - { - push_int<ScriptDataInt>(st->stack, -1); - return; - } - map_foreachinarea(std::bind(builtin_mobcount_sub, ph::_1, event, &c), - m, - 0, 0, - m->xs, m->ys, - BL::MOB); - - push_int<ScriptDataInt>(st->stack, (c - 1)); - -} - -static -void builtin_marriage(ScriptState *st) -{ - CharName partner = stringish<CharName>(ZString(conv_str(st, &AARGO2(2)))); - dumb_ptr<map_session_data> sd = script_rid2sd(st); - dumb_ptr<map_session_data> p_sd = map_nick2sd(partner); - - if (sd == nullptr || p_sd == nullptr || pc_marriage(sd, p_sd) < 0) - { - push_int<ScriptDataInt>(st->stack, 0); - return; - } - push_int<ScriptDataInt>(st->stack, 1); -} - -static -void builtin_divorce(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - st->state = ScriptEndState::STOP; // rely on pc_divorce to restart - - sd->npc_flags.divorce = 1; - - if (sd == nullptr || pc_divorce(sd) < 0) - { - push_int<ScriptDataInt>(st->stack, 0); - return; - } - - push_int<ScriptDataInt>(st->stack, 1); -} - -/*========================================== - * IDからItem名 - *------------------------------------------ - */ -static -void builtin_getitemname(ScriptState *st) -{ - struct item_data *i_data; - struct script_data *data; - - data = &AARGO2(2); - get_val(st, data); - if (data->is<ScriptDataStr>()) - { - ZString name = ZString(conv_str(st, data)); - i_data = itemdb_searchname(name); - } - else - { - ItemNameId item_id = wrap<ItemNameId>(conv_num(st, data)); - i_data = itemdb_search(item_id); - } - - RString item_name; - if (i_data) - item_name = i_data->jname; - else - item_name = "Unknown Item"_s; - - push_str<ScriptDataStr>(st->stack, item_name); -} - -static -void builtin_getspellinvocation(ScriptState *st) -{ - RString name = conv_str(st, &AARGO2(2)); - - AString invocation = magic::magic_find_invocation(name); - if (!invocation) - invocation = "..."_s; - - push_str<ScriptDataStr>(st->stack, invocation); -} - -static -void builtin_getpartnerid2(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status.partner_id)); -} - -/*========================================== - * PCの所持品情報読み取り - *------------------------------------------ - */ -static -void builtin_getinventorylist(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - int j = 0; - if (!sd) - return; - for (IOff0 i : IOff0::iter()) - { - if (sd->status.inventory[i].nameid - && sd->status.inventory[i].amount > 0) - { - pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_id"_s), j), - unwrap<ItemNameId>(sd->status.inventory[i].nameid)); - pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_amount"_s), j), - sd->status.inventory[i].amount); - pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_equip"_s), j), - static_cast<uint16_t>(sd->status.inventory[i].equip)); - j++; - } - } - pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_count"_s)), j); -} - -static -void builtin_getactivatedpoolskilllist(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - SkillID pool_skills[MAX_SKILL_POOL]; - int skill_pool_size = skill_pool(sd, pool_skills); - int i, count = 0; - - if (!sd) - return; - - for (i = 0; i < skill_pool_size; i++) - { - SkillID skill_id = pool_skills[i]; - - if (sd->status.skill[skill_id].lv) - { - pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count), - static_cast<uint16_t>(skill_id)); - pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count), - sd->status.skill[skill_id].lv); - pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count), - static_cast<uint16_t>(sd->status.skill[skill_id].flags)); - pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count), - skill_name(skill_id)); - ++count; - } - } - pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count); - -} - -static -void builtin_getunactivatedpoolskilllist(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - int i, count = 0; - - if (!sd) - return; - - for (i = 0; i < skill_pool_skills_size; i++) - { - SkillID skill_id = skill_pool_skills[i]; - - if (sd->status.skill[skill_id].lv - && !bool(sd->status.skill[skill_id].flags & SkillFlags::POOL_ACTIVATED)) - { - pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count), - static_cast<uint16_t>(skill_id)); - pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count), - sd->status.skill[skill_id].lv); - pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count), - static_cast<uint16_t>(sd->status.skill[skill_id].flags)); - pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count), - skill_name(skill_id)); - ++count; - } - } - pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count); -} - -static -void builtin_poolskill(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - SkillID skill_id = SkillID(conv_num(st, &AARGO2(2))); - - skill_pool_activate(sd, skill_id); - clif_skillinfoblock(sd); - -} - -static -void builtin_unpoolskill(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - SkillID skill_id = SkillID(conv_num(st, &AARGO2(2))); - - skill_pool_deactivate(sd, skill_id); - clif_skillinfoblock(sd); - -} - -/*========================================== - * NPCから発生するエフェクト - * misceffect(effect, [target]) - * - * effect The effect type/ID. - * target The player name or being ID on - * which to display the effect. If not - * specified, it attempts to default to - * the current NPC or invoking PC. - *------------------------------------------ - */ -static -void builtin_misceffect(ScriptState *st) -{ - int type; - BlockId id; - CharName name; - dumb_ptr<block_list> bl = nullptr; - - type = conv_num(st, &AARGO2(2)); - - if (HARGO2(3)) - { - struct script_data *sdata = &AARGO2(3); - - get_val(st, sdata); - - if (sdata->is<ScriptDataStr>()) - name = stringish<CharName>(ZString(conv_str(st, sdata))); - else - id = wrap<BlockId>(conv_num(st, sdata)); - } - - if (name.to__actual()) - { - dumb_ptr<map_session_data> sd = map_nick2sd(name); - if (sd) - bl = sd; - } - else if (id) - bl = map_id2bl(id); - else if (st->oid) - bl = map_id2bl(st->oid); - else - { - dumb_ptr<map_session_data> sd = script_rid2sd(st); - if (sd) - bl = sd; - } - - if (bl) - clif_misceffect(bl, type); - -} - -/*========================================== - * Special effects [Valaris] - *------------------------------------------ - */ -static -void builtin_specialeffect(ScriptState *st) -{ - dumb_ptr<block_list> bl = map_id2bl(st->oid); - - if (bl == nullptr) - return; - - clif_specialeffect(bl, - conv_num(st, - &AARGO2(2)), - 0); - -} - -static -void builtin_specialeffect2(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - if (sd == nullptr) - return; - - clif_specialeffect(sd, - conv_num(st, - &AARGO2(2)), - 0); - -} - -/*========================================== - * Nude [Valaris] - *------------------------------------------ - */ - -static -void builtin_nude(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - if (sd == nullptr) - return; - - for (EQUIP i : EQUIPs) - { - IOff0 idx = sd->equip_index_maybe[i]; - if (idx.ok()) - pc_unequipitem(sd, idx, CalcStatus::LATER); - } - pc_calcstatus(sd, 0); - -} - -/*========================================== - * UnequipById [Freeyorp] - *------------------------------------------ - */ - -static -void builtin_unequipbyid(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - if (sd == nullptr) - return; - - EQUIP slot_id = EQUIP(conv_num(st, &AARGO2(2))); - - if (slot_id >= EQUIP() && slot_id < EQUIP::COUNT) - { - IOff0 idx = sd->equip_index_maybe[slot_id]; - if (idx.ok()) - pc_unequipitem(sd, idx, CalcStatus::LATER); - } - - pc_calcstatus(sd, 0); - -} - -/*========================================== - * gmcommand [MouseJstr] - * - * suggested on the forums... - *------------------------------------------ - */ - -static -void builtin_gmcommand(ScriptState *st) -{ - dumb_ptr<map_session_data> sd; - - sd = script_rid2sd(st); - RString cmd = conv_str(st, &AARGO2(2)); - - is_atcommand(sd->sess, sd, cmd, GmLevel::from(-1U)); - -} - -/*========================================== - * npcwarp [remoitnane] - * Move NPC to a new position on the same map. - *------------------------------------------ - */ -static -void builtin_npcwarp(ScriptState *st) -{ - int x, y; - dumb_ptr<npc_data> nd = nullptr; - - x = conv_num(st, &AARGO2(2)); - y = conv_num(st, &AARGO2(3)); - NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARGO2(4)))); - nd = npc_name2id(npc); - - if (!nd) - { - PRINTF("builtin_npcwarp: no such npc: %s\n"_fmt, npc); - return; - } - - map_local *m = nd->bl_m; - - /* Crude sanity checks. */ - if (m == nullptr || !nd->bl_prev - || x < 0 || x > m->xs -1 - || y < 0 || y > m->ys - 1) - return; - - npc_enable(npc, 0); - map_delblock(nd); /* [Freeyorp] */ - nd->bl_x = x; - nd->bl_y = y; - map_addblock(nd); - npc_enable(npc, 1); - -} - -/*========================================== - * message [MouseJstr] - *------------------------------------------ - */ - -static -void builtin_message(ScriptState *st) -{ - CharName player = stringish<CharName>(ZString(conv_str(st, &AARGO2(2)))); - ZString msg = ZString(conv_str(st, &AARGO2(3))); - - dumb_ptr<map_session_data> pl_sd = map_nick2sd(player); - if (pl_sd == nullptr) - return; - clif_displaymessage(pl_sd->sess, msg); - -} - -/*========================================== - * npctalk (sends message to surrounding - * area) [Valaris] - *------------------------------------------ - */ - -static -void builtin_npctalk(ScriptState *st) -{ - dumb_ptr<npc_data> nd = map_id_is_npc(st->oid); - RString str = conv_str(st, &AARGO2(2)); - - if (nd) - { - clif_message(nd, str); - } -} - -/*========================================== - * getlook char info. getlook(arg) - *------------------------------------------ - */ -static -void builtin_getlook(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - LOOK type = LOOK(conv_num(st, &AARGO2(2))); - int val = -1; - switch (type) - { - case LOOK::HAIR: //1 - val = sd->status.hair; - break; - case LOOK::WEAPON: //2 - val = static_cast<uint16_t>(sd->status.weapon); - break; - case LOOK::HEAD_BOTTOM: //3 - val = unwrap<ItemNameId>(sd->status.head_bottom); - break; - case LOOK::HEAD_TOP: //4 - val = unwrap<ItemNameId>(sd->status.head_top); - break; - case LOOK::HEAD_MID: //5 - val = unwrap<ItemNameId>(sd->status.head_mid); - break; - case LOOK::HAIR_COLOR: //6 - val = sd->status.hair_color; - break; - case LOOK::CLOTHES_COLOR: //7 - val = sd->status.clothes_color; - break; - case LOOK::SHIELD: //8 - val = unwrap<ItemNameId>(sd->status.shield); - break; - case LOOK::SHOES: //9 - break; - } - - push_int<ScriptDataInt>(st->stack, val); -} - -/*========================================== - * get char save point. argument: 0- map name, 1- x, 2- y - *------------------------------------------ -*/ -static -void builtin_getsavepoint(ScriptState *st) -{ - int x, y, type; - dumb_ptr<map_session_data> sd; - - sd = script_rid2sd(st); - - type = conv_num(st, &AARGO2(2)); - - x = sd->status.save_point.x; - y = sd->status.save_point.y; - switch (type) - { - case 0: - { - RString mapname = sd->status.save_point.map_; - push_str<ScriptDataStr>(st->stack, mapname); - } - break; - case 1: - push_int<ScriptDataInt>(st->stack, x); - break; - case 2: - push_int<ScriptDataInt>(st->stack, y); - break; - } -} - -/*========================================== - * areatimer - *------------------------------------------ - */ -static -void builtin_areatimer_sub(dumb_ptr<block_list> bl, interval_t tick, NpcEvent event) -{ - pc_addeventtimer(bl->is_player(), tick, event); -} - -static -void builtin_areatimer(ScriptState *st) -{ - int x0, y0, x1, y1; - - MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x0 = conv_num(st, &AARGO2(3)); - y0 = conv_num(st, &AARGO2(4)); - x1 = conv_num(st, &AARGO2(5)); - y1 = conv_num(st, &AARGO2(6)); - interval_t tick = static_cast<interval_t>(conv_num(st, &AARGO2(7))); - ZString event_ = ZString(conv_str(st, &AARGO2(8))); - NpcEvent event; - extract(event_, &event); - - map_local *m = map_mapname2mapid(mapname); - if (m == nullptr) - return; - - map_foreachinarea(std::bind(builtin_areatimer_sub, ph::_1, tick, event), - m, - x0, y0, - x1, y1, - BL::PC); -} - -/*========================================== - * Check whether the PC is in the specified rectangle - *------------------------------------------ - */ -static -void builtin_isin(ScriptState *st) -{ - int x1, y1, x2, y2; - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - MapName str = stringish<MapName>(ZString(conv_str(st, &AARGO2(2)))); - x1 = conv_num(st, &AARGO2(3)); - y1 = conv_num(st, &AARGO2(4)); - x2 = conv_num(st, &AARGO2(5)); - y2 = conv_num(st, &AARGO2(6)); - - if (!sd) - return; - - push_int<ScriptDataInt>(st->stack, - (sd->bl_x >= x1 && sd->bl_x <= x2) - && (sd->bl_y >= y1 && sd->bl_y <= y2) - && (str == sd->bl_m->name_)); -} - -// Trigger the shop on a (hopefully) nearby shop NPC -static -void builtin_shop(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - dumb_ptr<npc_data> nd; - - if (!sd) - return; - - NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2)))); - nd = npc_name2id(name); - if (!nd) - { - PRINTF("builtin_shop: no such npc: %s\n"_fmt, name); - return; - } - - builtin_close(st); - clif_npcbuysell(sd, nd->bl_id); -} - -/*========================================== - * Check whether the PC is dead - *------------------------------------------ - */ -static -void builtin_isdead(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - push_int<ScriptDataInt>(st->stack, pc_isdead(sd)); -} - -/*======================================== - * Changes a NPC name, and sprite - *---------------------------------------- - */ -static -void builtin_fakenpcname(ScriptState *st) -{ - NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARGO2(2)))); - NpcName newname = stringish<NpcName>(ZString(conv_str(st, &AARGO2(3)))); - Species newsprite = wrap<Species>(static_cast<uint16_t>(conv_num(st, &AARGO2(4)))); - dumb_ptr<npc_data> nd = npc_name2id(name); - if (!nd) - { - PRINTF("builtin_fakenpcname: no such npc: %s\n"_fmt, name); - return; - } - nd->name = newname; - nd->npc_class = newsprite; - - // Refresh this npc - npc_enable(name, 0); - npc_enable(name, 1); - -} - -/*============================ - * Gets the PC's x pos - *---------------------------- - */ -static -void builtin_getx(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - push_int<ScriptDataInt>(st->stack, sd->bl_x); -} - -/*============================ - * Gets the PC's y pos - *---------------------------- - */ -static -void builtin_gety(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - push_int<ScriptDataInt>(st->stack, sd->bl_y); -} - -/* - * Get the PC's current map's name - */ -static -void builtin_getmap(ScriptState *st) -{ - dumb_ptr<map_session_data> sd = script_rid2sd(st); - - push_str<ScriptDataStr>(st->stack, sd->bl_m->name_); -} - -static -void builtin_mapexit(ScriptState *) -{ - runflag = 0; -} - - -// -// 実行部main -// -/*========================================== - * コマンドの読み取り - *------------------------------------------ - */ -static -ByteCode get_com(ScriptPointer *script) -{ - if (static_cast<uint8_t>(script->peek()) >= 0x80) - { - // synthetic! Does not advance pos yet. - return ByteCode::INT; - } - return script->pop(); -} - -/*========================================== - * 数値の所得 - *------------------------------------------ - */ -static -int get_num(ScriptPointer *scr) -{ - int i = 0; - int j = 0; - uint8_t val; - do - { - val = static_cast<uint8_t>(scr->pop()); - i += (val & 0x7f) << j; - j += 6; - } - while (val >= 0xc0); - return i; -} - -/*========================================== - * スタックから値を取り出す - *------------------------------------------ - */ -static -int pop_val(ScriptState *st) -{ - if (st->stack->stack_datav.empty()) - return 0; - script_data& back = st->stack->stack_datav.back(); - get_val(st, &back); - int rv = 0; - if (auto *u = back.get_if<ScriptDataInt>()) - rv = u->numi; - st->stack->stack_datav.pop_back(); - return rv; -} - -static -bool isstr(struct script_data& c) -{ - return c.is<ScriptDataStr>(); -} - -/*========================================== - * 加算演算子 - *------------------------------------------ - */ -static -void op_add(ScriptState *st) -{ - get_val(st, &st->stack->stack_datav.back()); - script_data back = st->stack->stack_datav.back(); - st->stack->stack_datav.pop_back(); - - script_data& back1 = st->stack->stack_datav.back(); - get_val(st, &back1); - - if (!(isstr(back) || isstr(back1))) - { - back1.get_if<ScriptDataInt>()->numi += back.get_if<ScriptDataInt>()->numi; - } - else - { - RString sb = conv_str(st, &back); - RString sb1 = conv_str(st, &back1); - MString buf; - buf += sb1; - buf += sb; - back1 = ScriptDataStr{.str= AString(buf)}; - } -} - -/*========================================== - * 二項演算子(文字列) - *------------------------------------------ - */ -static -void op_2str(ScriptState *st, ByteCode op, ZString s1, ZString s2) -{ - int a = 0; - - switch (op) - { - case ByteCode::EQ: - a = s1 == s2; - break; - case ByteCode::NE: - a = s1 != s2; - break; - case ByteCode::GT: - a = s1 > s2; - break; - case ByteCode::GE: - a = s1 >= s2; - break; - case ByteCode::LT: - a = s1 < s2; - break; - case ByteCode::LE: - a = s1 <= s2; - break; - default: - PRINTF("illegal string operater\n"_fmt); - break; - } - - push_int<ScriptDataInt>(st->stack, a); -} - -/*========================================== - * 二項演算子(数値) - *------------------------------------------ - */ -static -void op_2num(ScriptState *st, ByteCode op, int i1, int i2) -{ - switch (op) - { - case ByteCode::SUB: - i1 -= i2; - break; - case ByteCode::MUL: - i1 *= i2; - break; - case ByteCode::DIV: - i1 /= i2; - break; - case ByteCode::MOD: - i1 %= i2; - break; - case ByteCode::AND: - i1 &= i2; - break; - case ByteCode::OR: - i1 |= i2; - break; - case ByteCode::XOR: - i1 ^= i2; - break; - case ByteCode::LAND: - i1 = i1 && i2; - break; - case ByteCode::LOR: - i1 = i1 || i2; - break; - case ByteCode::EQ: - i1 = i1 == i2; - break; - case ByteCode::NE: - i1 = i1 != i2; - break; - case ByteCode::GT: - i1 = i1 > i2; - break; - case ByteCode::GE: - i1 = i1 >= i2; - break; - case ByteCode::LT: - i1 = i1 < i2; - break; - case ByteCode::LE: - i1 = i1 <= i2; - break; - case ByteCode::R_SHIFT: - i1 = i1 >> i2; - break; - case ByteCode::L_SHIFT: - i1 = i1 << i2; - break; - } - push_int<ScriptDataInt>(st->stack, i1); -} - -/*========================================== - * 二項演算子 - *------------------------------------------ - */ -static -void op_2(ScriptState *st, ByteCode op) -{ - // pop_val has unfortunate implications here - script_data d2 = st->stack->stack_datav.back(); - st->stack->stack_datav.pop_back(); - get_val(st, &d2); - script_data d1 = st->stack->stack_datav.back(); - st->stack->stack_datav.pop_back(); - get_val(st, &d1); - - if (isstr(d1) && isstr(d2)) - { - // ss => op_2str - op_2str(st, op, d1.get_if<ScriptDataStr>()->str, d2.get_if<ScriptDataStr>()->str); - } - else if (!(isstr(d1) || isstr(d2))) - { - // ii => op_2num - op_2num(st, op, d1.get_if<ScriptDataInt>()->numi, d2.get_if<ScriptDataInt>()->numi); - } - else - { - // si,is => error - PRINTF("script: op_2: int&str, str&int not allow.\n"_fmt); - push_int<ScriptDataInt>(st->stack, 0); - } -} - -/*========================================== - * 単項演算子 - *------------------------------------------ - */ -static -void op_1num(ScriptState *st, ByteCode op) -{ - int i1; - i1 = pop_val(st); - switch (op) - { - case ByteCode::NEG: - i1 = -i1; - break; - case ByteCode::NOT: - i1 = ~i1; - break; - case ByteCode::LNOT: - i1 = !i1; - break; - } - push_int<ScriptDataInt>(st->stack, i1); -} - -/*========================================== - * 関数の実行 - *------------------------------------------ - */ -void run_func(ScriptState *st) -{ - size_t end_sp = st->stack->stack_datav.size(); - size_t start_sp = end_sp - 1; - while (!st->stack->stack_datav[start_sp].is<ScriptDataArg>()) - { - start_sp--; - if (start_sp == 0) - { - if (battle_config.error_log) - PRINTF("function not found\n"_fmt); - st->state = ScriptEndState::END; - return; - } - } - // the func is before the arg - start_sp--; - st->start = start_sp; - st->end = end_sp; - - if (!st->stack->stack_datav[st->start].is<ScriptDataFuncRef>()) - { - PRINTF("run_func: not function and command! \n"_fmt); - st->state = ScriptEndState::END; - return; - } - size_t func = st->stack->stack_datav[st->start].get_if<ScriptDataFuncRef>()->numi; - - if (DEBUG_RUN && battle_config.etc_log) - { - PRINTF("run_func : %s\n"_fmt, - builtin_functions[func].name); - PRINTF("stack dump :"_fmt); - for (script_data& d : st->stack->stack_datav) - { - MATCH (d) - { - CASE (const ScriptDataInt&, u) - { - PRINTF(" int(%d)"_fmt, u.numi); - } - CASE (const ScriptDataRetInfo&, u) - { - PRINTF(" retinfo(%p)"_fmt, static_cast<const void *>(u.script)); - } - CASE (const ScriptDataParam&, u) - { - PRINTF(" param(%d)"_fmt, u.reg.sp()); - } - CASE (const ScriptDataVariable&, u) - { - PRINTF(" name(%s)[%d]"_fmt, variable_names.outtern(u.reg.base()), u.reg.index()); - } - CASE (const ScriptDataArg&, u) - { - (void)u; - PRINTF(" arg"_fmt); - } - CASE (const ScriptDataPos&, u) - { - (void)u; - PRINTF(" pos(%d)"_fmt, u.numi); - } - CASE (const ScriptDataStr&, u) - { - (void)u; - PRINTF(" str(%s)"_fmt, u.str); - } - CASE (const ScriptDataFuncRef&, u) - { - (void)u; - PRINTF(" func(%s)"_fmt, builtin_functions[u.numi].name); - } - } - } - PRINTF("\n"_fmt); - } - builtin_functions[func].func(st); - - pop_stack(st->stack, start_sp, end_sp); - - if (st->state == ScriptEndState::RETFUNC) - { - // ユーザー定義関数からの復帰 - int olddefsp = st->defsp; - - pop_stack(st->stack, st->defsp, start_sp); // 復帰に邪魔なスタック削除 - if (st->defsp < 4 - || !st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>()) - { - PRINTF("script:run_func (return) return without callfunc or callsub!\n"_fmt); - st->state = ScriptEndState::END; - return; - } - assert (olddefsp == st->defsp); // pretty sure it hasn't changed yet - st->scriptp.code = conv_script(st, &st->stack->stack_datav[olddefsp - 1]); // スクリプトを復元 - st->scriptp.pos = conv_num(st, &st->stack->stack_datav[olddefsp - 2]); // スクリプト位置の復元 - st->defsp = conv_num(st, &st->stack->stack_datav[olddefsp - 3]); // 基準スタックポインタを復元 - // Number of arguments. - int i = conv_num(st, &st->stack->stack_datav[olddefsp - 4]); // 引数の数所得 - assert (i == 0); - - pop_stack(st->stack, olddefsp - 4 - i, olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除 - - st->state = ScriptEndState::GOTO; - } -} - -// pretend it's external so this can be called in the debugger -void dump_script(const ScriptBuffer *script); -void dump_script(const ScriptBuffer *script) -{ - ScriptPointer scriptp(script, 0); - while (scriptp.pos < reinterpret_cast<const std::vector<ByteCode> *>(script)->size()) - { - PRINTF("%6zu: "_fmt, scriptp.pos); - switch (ByteCode c = get_com(&scriptp)) - { - case ByteCode::EOL: - PRINTF("EOL\n"_fmt); // extra newline between functions - break; - case ByteCode::INT: - // synthesized! - PRINTF("INT %d"_fmt, get_num(&scriptp)); - break; - - case ByteCode::POS: - case ByteCode::VARIABLE: - case ByteCode::FUNC_REF: - case ByteCode::PARAM: - { - int arg = 0; - arg |= static_cast<uint8_t>(scriptp.pop()) << 0; - arg |= static_cast<uint8_t>(scriptp.pop()) << 8; - arg |= static_cast<uint8_t>(scriptp.pop()) << 16; - switch(c) - { - case ByteCode::POS: - PRINTF("POS %d"_fmt, arg); - break; - case ByteCode::VARIABLE: - PRINTF("VARIABLE %s"_fmt, variable_names.outtern(arg)); - break; - case ByteCode::FUNC_REF: - PRINTF("FUNC_REF %s"_fmt, builtin_functions[arg].name); - break; - case ByteCode::PARAM: - PRINTF("PARAM SP::#%d (sorry)"_fmt, arg); - break; - } - } - break; - case ByteCode::ARG: - PRINTF("ARG"_fmt); - break; - case ByteCode::STR: - PRINTF("STR \"%s\""_fmt, scriptp.pops()); - break; - case ByteCode::FUNC: - PRINTF("FUNC"_fmt); - break; - - case ByteCode::ADD: - PRINTF("ADD"_fmt); - break; - case ByteCode::SUB: - PRINTF("SUB"_fmt); - break; - case ByteCode::MUL: - PRINTF("MUL"_fmt); - break; - case ByteCode::DIV: - PRINTF("DIV"_fmt); - break; - case ByteCode::MOD: - PRINTF("MOD"_fmt); - break; - case ByteCode::EQ: - PRINTF("EQ"_fmt); - break; - case ByteCode::NE: - PRINTF("NE"_fmt); - break; - case ByteCode::GT: - PRINTF("GT"_fmt); - break; - case ByteCode::GE: - PRINTF("GE"_fmt); - break; - case ByteCode::LT: - PRINTF("LT"_fmt); - break; - case ByteCode::LE: - PRINTF("LE"_fmt); - break; - case ByteCode::AND: - PRINTF("AND"_fmt); - break; - case ByteCode::OR: - PRINTF("OR"_fmt); - break; - case ByteCode::XOR: - PRINTF("XOR"_fmt); - break; - case ByteCode::LAND: - PRINTF("LAND"_fmt); - break; - case ByteCode::LOR: - PRINTF("LOR"_fmt); - break; - case ByteCode::R_SHIFT: - PRINTF("R_SHIFT"_fmt); - break; - case ByteCode::L_SHIFT: - PRINTF("L_SHIFT"_fmt); - break; - case ByteCode::NEG: - PRINTF("NEG"_fmt); - break; - case ByteCode::NOT: - PRINTF("NOT"_fmt); - break; - case ByteCode::LNOT: - PRINTF("LNOT"_fmt); - break; - - case ByteCode::NOP: - PRINTF("NOP"_fmt); - break; - - default: - PRINTF("??? %d"_fmt, c); - break; - } - PRINTF("\n"_fmt); - } -} - -/*========================================== - * スクリプトの実行メイン部分 - *------------------------------------------ - */ -static -void run_script_main(ScriptState *st, const ScriptBuffer *rootscript) -{ - int cmdcount = script_config.check_cmdcount; - int gotocount = script_config.check_gotocount; - struct script_stack *stack = st->stack; - - st->defsp = stack->stack_datav.size(); - - int rerun_pos = st->scriptp.pos; - st->state = ScriptEndState::ZERO; - while (st->state == ScriptEndState::ZERO) - { - switch (ByteCode c = get_com(&st->scriptp)) - { - case ByteCode::EOL: - if (stack->stack_datav.size() != st->defsp) - { - if (true) - PRINTF("stack.sp (%zu) != default (%d)\n"_fmt, - stack->stack_datav.size(), - st->defsp); - abort(); - } - rerun_pos = st->scriptp.pos; - break; - case ByteCode::INT: - // synthesized! - push_int<ScriptDataInt>(stack, get_num(&st->scriptp)); - break; - - case ByteCode::POS: - case ByteCode::VARIABLE: - case ByteCode::FUNC_REF: - case ByteCode::PARAM: - // Note that these 3 have *very* different meanings, - // despite being encoded similarly. - { - int arg = 0; - arg |= static_cast<uint8_t>(st->scriptp.pop()) << 0; - arg |= static_cast<uint8_t>(st->scriptp.pop()) << 8; - arg |= static_cast<uint8_t>(st->scriptp.pop()) << 16; - switch(c) - { - case ByteCode::POS: - push_int<ScriptDataPos>(stack, arg); - break; - case ByteCode::VARIABLE: - push_reg<ScriptDataVariable>(stack, SIR::from(arg)); - break; - case ByteCode::FUNC_REF: - push_int<ScriptDataFuncRef>(stack, arg); - break; - case ByteCode::PARAM: - SP arg_sp = static_cast<SP>(arg); - push_reg<ScriptDataParam>(stack, SIR::from(arg_sp)); - break; - } - } - break; - case ByteCode::ARG: - push_int<ScriptDataArg>(stack, 0); - break; - case ByteCode::STR: - push_str<ScriptDataStr>(stack, st->scriptp.pops()); - break; - case ByteCode::FUNC: - run_func(st); - if (st->state == ScriptEndState::GOTO) - { - rerun_pos = st->scriptp.pos; - st->state = ScriptEndState::ZERO; - if (gotocount > 0 && (--gotocount) <= 0) - { - PRINTF("run_script: infinity loop !\n"_fmt); - st->state = ScriptEndState::END; - } - } - break; - - case ByteCode::ADD: - op_add(st); - break; - - case ByteCode::SUB: - case ByteCode::MUL: - case ByteCode::DIV: - case ByteCode::MOD: - case ByteCode::EQ: - case ByteCode::NE: - case ByteCode::GT: - case ByteCode::GE: - case ByteCode::LT: - case ByteCode::LE: - case ByteCode::AND: - case ByteCode::OR: - case ByteCode::XOR: - case ByteCode::LAND: - case ByteCode::LOR: - case ByteCode::R_SHIFT: - case ByteCode::L_SHIFT: - op_2(st, c); - break; - - case ByteCode::NEG: - case ByteCode::NOT: - case ByteCode::LNOT: - op_1num(st, c); - break; - - case ByteCode::NOP: - st->state = ScriptEndState::END; - break; - - default: - if (battle_config.error_log) - PRINTF("unknown command : %d @ %zu\n"_fmt, - c, st->scriptp.pos); - st->state = ScriptEndState::END; - break; - } - if (cmdcount > 0 && (--cmdcount) <= 0) - { - PRINTF("run_script: infinity loop !\n"_fmt); - st->state = ScriptEndState::END; - } - } - switch (st->state) - { - case ScriptEndState::STOP: - break; - case ScriptEndState::END: - { - dumb_ptr<map_session_data> sd = map_id2sd(st->rid); - st->scriptp.code = nullptr; - st->scriptp.pos = -1; - if (sd && sd->npc_id == st->oid) - npc_event_dequeue(sd); - } - break; - case ScriptEndState::RERUNLINE: - st->scriptp.pos = rerun_pos; - break; - } - - if (st->state != ScriptEndState::END) - { - // 再開するためにスタック情報を保存 - dumb_ptr<map_session_data> sd = map_id2sd(st->rid); - if (sd) - { - sd->npc_stackbuf = stack->stack_datav; - sd->npc_script = st->scriptp.code; - // sd->npc_pos is set later ... ??? - sd->npc_scriptroot = rootscript; - } - } -} - -/*========================================== - * スクリプトの実行 - *------------------------------------------ - */ -int run_script(ScriptPointer sp, BlockId rid, BlockId oid) -{ - return run_script_l(sp, rid, oid, nullptr); -} - -int run_script_l(ScriptPointer sp, BlockId rid, BlockId oid, - Slice<argrec_t> args) -{ - struct script_stack stack; - ScriptState st; - dumb_ptr<map_session_data> sd = map_id2sd(rid); - const ScriptBuffer *rootscript = sp.code; - int i; - if (sp.code == nullptr || sp.pos >> 24) - return -1; - - if (sd && !sd->npc_stackbuf.empty() && sd->npc_scriptroot == rootscript) - { - // 前回のスタックを復帰 - sp.code = sd->npc_script; - stack.stack_datav = std::move(sd->npc_stackbuf); - } - st.stack = &stack; - st.scriptp = sp; - st.rid = rid; - st.oid = oid; - for (i = 0; i < args.size(); i++) - { - if (args[i].name.back() == '$') - pc_setregstr(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.s); - else - pc_setreg(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.i); - } - run_script_main(&st, rootscript); - - stack.stack_datav.clear(); - return st.scriptp.pos; -} - -/*========================================== - * マップ変数の変更 - *------------------------------------------ - */ -void mapreg_setreg(SIR reg, int val) -{ - mapreg_db.put(reg, val); - - mapreg_dirty = 1; -} - -/*========================================== - * 文字列型マップ変数の変更 - *------------------------------------------ - */ -void mapreg_setregstr(SIR reg, XString str) -{ - if (!str) - mapregstr_db.erase(reg); - else - mapregstr_db.insert(reg, str); - - mapreg_dirty = 1; -} - -/*========================================== - * 永続的マップ変数の読み込み - *------------------------------------------ - */ -static -void script_load_mapreg(void) -{ - io::ReadFile in(mapreg_txt); - - if (!in.is_open()) - return; - - AString line; - while (in.getline(line)) - { - XString buf1, buf2; - int index = 0; - if (extract(line, - record<'\t'>( - record<','>(&buf1, &index), - &buf2)) - || extract(line, - record<'\t'>( - record<','>(&buf1), - &buf2))) - { - int s = variable_names.intern(buf1); - SIR key = SIR::from(s, index); - if (buf1.back() == '$') - { - mapregstr_db.insert(key, buf2); - } - else - { - int v; - if (!extract(buf2, &v)) - goto borken; - mapreg_db.put(key, v); - } - } - else - { - borken: - PRINTF("%s: %s broken data !\n"_fmt, mapreg_txt, AString(buf1)); - continue; - } - } - mapreg_dirty = 0; -} - -/*========================================== - * 永続的マップ変数の書き込み - *------------------------------------------ - */ -static -void script_save_mapreg_intsub(SIR key, int data, io::WriteFile& fp) -{ - int num = key.base(), i = key.index(); - ZString name = variable_names.outtern(num); - if (name[1] != '@') - { - if (i == 0) - FPRINTF(fp, "%s\t%d\n"_fmt, name, data); - else - FPRINTF(fp, "%s,%d\t%d\n"_fmt, name, i, data); - } -} - -static -void script_save_mapreg_strsub(SIR key, ZString data, io::WriteFile& fp) -{ - int num = key.base(), i = key.index(); - ZString name = variable_names.outtern(num); - if (name[1] != '@') - { - if (i == 0) - FPRINTF(fp, "%s\t%s\n"_fmt, name, data); - else - FPRINTF(fp, "%s,%d\t%s\n"_fmt, name, i, data); - } -} - -static -void script_save_mapreg(void) -{ - io::WriteLock fp(mapreg_txt); - if (!fp.is_open()) - return; - for (auto& pair : mapreg_db) - script_save_mapreg_intsub(pair.first, pair.second, fp); - for (auto& pair : mapregstr_db) - script_save_mapreg_strsub(pair.first, pair.second, fp); - mapreg_dirty = 0; -} - -static -void script_autosave_mapreg(TimerData *, tick_t) -{ - if (mapreg_dirty) - script_save_mapreg(); -} - -void do_final_script(void) -{ - if (mapreg_dirty >= 0) - script_save_mapreg(); - - mapreg_db.clear(); - mapregstr_db.clear(); - scriptlabel_db.clear(); - userfunc_db.clear(); - - str_datam.clear(); -} - -/*========================================== - * 初期化 - *------------------------------------------ - */ -void do_init_script(void) -{ - script_load_mapreg(); - - Timer(gettick() + MAPREG_AUTOSAVE_INTERVAL, - script_autosave_mapreg, - MAPREG_AUTOSAVE_INTERVAL - ).detach(); -} - -#define BUILTIN(func, args, ret) \ -{builtin_##func, #func ## _s, args, ret} - -BuiltinFunction builtin_functions[] = -{ - BUILTIN(mes, "s"_s, '\0'), - BUILTIN(goto, "L"_s, '\0'), - BUILTIN(callfunc, "F"_s, '\0'), - BUILTIN(callsub, "L"_s, '\0'), - BUILTIN(return, ""_s, '\0'), - BUILTIN(next, ""_s, '\0'), - BUILTIN(close, ""_s, '\0'), - BUILTIN(close2, ""_s, '\0'), - BUILTIN(menu, "sL**"_s, '\0'), - BUILTIN(rand, "i?"_s, 'i'), - BUILTIN(isat, "Mxy"_s, 'i'), - BUILTIN(warp, "Mxy"_s, '\0'), - BUILTIN(areawarp, "MxyxyMxy"_s, '\0'), - BUILTIN(heal, "ii"_s, '\0'), - BUILTIN(itemheal, "ii"_s, '\0'), - BUILTIN(percentheal, "ii"_s, '\0'), - BUILTIN(input, "N"_s, '\0'), - BUILTIN(if, "iF*"_s, '\0'), - BUILTIN(set, "Ne"_s, '\0'), - BUILTIN(setarray, "Ne*"_s, '\0'), - BUILTIN(cleararray, "Nei"_s, '\0'), - BUILTIN(getarraysize, "N"_s, 'i'), - BUILTIN(getelementofarray, "Ni"_s, '.'), - BUILTIN(setlook, "ii"_s, '\0'), - BUILTIN(countitem, "I"_s, 'i'), - BUILTIN(checkweight, "Ii"_s, 'i'), - BUILTIN(getitem, "Ii??"_s, '\0'), - BUILTIN(makeitem, "IiMxy"_s, '\0'), - BUILTIN(delitem, "Ii"_s, '\0'), - BUILTIN(readparam, "i?"_s, 'i'), - BUILTIN(getcharid, "i?"_s, 'i'), - BUILTIN(strcharinfo, "i"_s, 's'), - BUILTIN(getequipid, "i"_s, 'i'), - BUILTIN(getequipname, "i"_s, 's'), - BUILTIN(statusup2, "ii"_s, '\0'), - BUILTIN(bonus, "ii"_s, '\0'), - BUILTIN(bonus2, "iii"_s, '\0'), - BUILTIN(skill, "ii?"_s, '\0'), - BUILTIN(setskill, "ii"_s, '\0'), - BUILTIN(getskilllv, "i"_s, 'i'), - BUILTIN(getgmlevel, ""_s, 'i'), - BUILTIN(end, ""_s, '\0'), - BUILTIN(getopt2, ""_s, 'i'), - BUILTIN(setopt2, "i"_s, '\0'), - BUILTIN(savepoint, "Mxy"_s, '\0'), - BUILTIN(gettimetick, "i"_s, 'i'), - BUILTIN(gettime, "i"_s, 'i'), - BUILTIN(openstorage, ""_s, '\0'), - BUILTIN(getexp, "ii"_s, '\0'), - BUILTIN(monster, "Mxysmi?"_s, '\0'), - BUILTIN(areamonster, "Mxyxysmi?"_s, '\0'), - BUILTIN(killmonster, "ME"_s, '\0'), - BUILTIN(killmonsterall, "M"_s, '\0'), - BUILTIN(donpcevent, "E"_s, '\0'), - BUILTIN(addtimer, "tE"_s, '\0'), - BUILTIN(initnpctimer, ""_s, '\0'), - BUILTIN(startnpctimer, "?"_s, '\0'), - BUILTIN(stopnpctimer, ""_s, '\0'), - BUILTIN(getnpctimer, "i"_s, 'i'), - BUILTIN(setnpctimer, "i"_s, '\0'), - BUILTIN(announce, "si"_s, '\0'), - BUILTIN(mapannounce, "Msi"_s, '\0'), - BUILTIN(getusers, "i"_s, 'i'), - BUILTIN(getmapusers, "M"_s, 'i'), - BUILTIN(getareausers, "Mxyxy?"_s, 'i'), - BUILTIN(getareadropitem, "Mxyxyi?"_s, 'i'), - BUILTIN(enablenpc, "s"_s, '\0'), - BUILTIN(disablenpc, "s"_s, '\0'), - BUILTIN(sc_start, "iTi?"_s, '\0'), - BUILTIN(sc_end, "i"_s, '\0'), - BUILTIN(sc_check, "i"_s, 'i'), - BUILTIN(debugmes, "s"_s, '\0'), - BUILTIN(resetstatus, ""_s, '\0'), - BUILTIN(changesex, ""_s, '\0'), - BUILTIN(attachrid, "i"_s, 'i'), - BUILTIN(detachrid, ""_s, '\0'), - BUILTIN(isloggedin, "i"_s, 'i'), - BUILTIN(setmapflag, "Mi"_s, '\0'), - BUILTIN(removemapflag, "Mi"_s, '\0'), - BUILTIN(getmapflag, "Mi"_s, 'i'), - BUILTIN(pvpon, "M"_s, '\0'), - BUILTIN(pvpoff, "M"_s, '\0'), - BUILTIN(emotion, "i"_s, '\0'), - BUILTIN(mapwarp, "MMxy"_s, '\0'), - BUILTIN(cmdothernpc, "ss"_s, '\0'), - BUILTIN(mobcount, "ME"_s, 'i'), - BUILTIN(marriage, "P"_s, 'i'), - BUILTIN(divorce, ""_s, 'i'), - BUILTIN(getitemname, "I"_s, 's'), - BUILTIN(getspellinvocation, "s"_s, 's'), - BUILTIN(getpartnerid2, ""_s, 'i'), - BUILTIN(getinventorylist, ""_s, '\0'), - BUILTIN(getactivatedpoolskilllist, ""_s, '\0'), - BUILTIN(getunactivatedpoolskilllist, ""_s, '\0'), - BUILTIN(poolskill, "i"_s, '\0'), - BUILTIN(unpoolskill, "i"_s, '\0'), - BUILTIN(misceffect, "i?"_s, '\0'), - BUILTIN(specialeffect, "i"_s, '\0'), - BUILTIN(specialeffect2, "i"_s, '\0'), - BUILTIN(nude, ""_s, '\0'), - BUILTIN(unequipbyid, "i"_s, '\0'), - BUILTIN(gmcommand, "s"_s, '\0'), - BUILTIN(npcwarp, "xys"_s, '\0'), - BUILTIN(message, "Ps"_s, '\0'), - BUILTIN(npctalk, "s"_s, '\0'), - BUILTIN(getlook, "i"_s, 'i'), - BUILTIN(getsavepoint, "i"_s, '.'), - BUILTIN(areatimer, "MxyxytE"_s, '\0'), - BUILTIN(isin, "Mxyxy"_s, 'i'), - BUILTIN(shop, "s"_s, '\0'), - BUILTIN(isdead, ""_s, 'i'), - BUILTIN(fakenpcname, "ssi"_s, '\0'), - BUILTIN(getx, ""_s, 'i'), - BUILTIN(gety, ""_s, 'i'), - BUILTIN(getmap, ""_s, 's'), - BUILTIN(mapexit, ""_s, '\0'), - {nullptr, ""_s, ""_s, '\0'}, -}; - -void set_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e, int val) -{ - size_t k = variable_names.intern(var); - SIR reg = SIR::from(k, e); - set_reg(sd, VariableCode::VARIABLE, reg, val); -} -void set_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e, XString val) -{ - size_t k = variable_names.intern(var); - SIR reg = SIR::from(k, e); - set_reg(sd, VariableCode::VARIABLE, reg, val); -} -int get_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e) -{ - size_t k = variable_names.intern(var); - SIR reg = SIR::from(k, e); - struct script_data dat = ScriptDataVariable{.reg= reg}; - get_val(sd, &dat); - if (auto *u = dat.get_if<ScriptDataInt>()) - return u->numi; - PRINTF("Warning: you lied about the type and I'm too lazy to fix it!"_fmt); - return 0; -} -ZString get_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e) -{ - size_t k = variable_names.intern(var); - SIR reg = SIR::from(k, e); - struct script_data dat = ScriptDataVariable{.reg= reg}; - get_val(sd, &dat); - if (auto *u = dat.get_if<ScriptDataStr>()) - // this is almost certainly a memory leak after CONSTSTR removal - return u->str; - PRINTF("Warning: you lied about the type and I can't fix it!"_fmt); - return ZString(); -} -} // namespace tmwa diff --git a/src/map/script.hpp b/src/map/script.hpp deleted file mode 100644 index d5200a6..0000000 --- a/src/map/script.hpp +++ /dev/null @@ -1,277 +0,0 @@ -#pragma once -// script.hpp - EAthena script frontend, engine, and library. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> -// -// 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 <http://www.gnu.org/licenses/>. - -#include "fwd.hpp" - -#include <cstdint> - -#include <vector> - -#include "../range/slice.hpp" - -#include "../strings/zstring.hpp" - -#include "../generic/db.hpp" -#include "../generic/dumb_ptr.hpp" - -#include "../sexpr/variant.hpp" - -#include "../mmo/ids.hpp" - -#include "clif.t.hpp" -#include "map.t.hpp" - - -namespace tmwa -{ -enum class VariableCode : uint8_t -{ - PARAM, - VARIABLE, -}; - -enum class StringCode : uint8_t -{ - NOP, POS, INT, PARAM, FUNC, - VARIABLE, -}; - -enum class ByteCode : uint8_t -{ - // types and specials - // Note that 'INT' is synthetic, and does not occur in the data stream - NOP, POS, INT, PARAM, FUNC, STR, ARG, - VARIABLE, EOL, - - // unary and binary operators - LOR, LAND, LE, LT, GE, GT, EQ, NE, - XOR, OR, AND, ADD, SUB, MUL, DIV, MOD, - NEG, LNOT, NOT, R_SHIFT, L_SHIFT, - - // additions - // needed because FUNC is used for the actual call - FUNC_REF, -}; - -struct str_data_t; - -class ScriptBuffer -{ - typedef ZString::iterator ZSit; - - std::vector<ByteCode> script_buf; -public: - // construction methods used only by script.cpp - void add_scriptc(ByteCode a); - void add_scriptb(uint8_t a); - void add_scripti(uint32_t a); - void add_scriptl(str_data_t *a); - void set_label(str_data_t *ld, int pos_); - ZSit parse_simpleexpr(ZSit p); - ZSit parse_subexpr(ZSit p, int limit); - ZSit parse_expr(ZSit p); - ZSit parse_line(ZSit p, bool *canstep); - void parse_script(ZString src, int line, bool implicit_end); - - // consumption methods used only by script.cpp - ByteCode operator[](size_t i) const { return script_buf[i]; } - ZString get_str(size_t i) const - { - return ZString(strings::really_construct_from_a_pointer, reinterpret_cast<const char *>(&script_buf[i]), nullptr); - } - - // method used elsewhere -}; - -struct ScriptPointer -{ - const ScriptBuffer *code; - size_t pos; - - ScriptPointer() - : code() - , pos() - {} - - ScriptPointer(const ScriptBuffer *c, size_t p) - : code(c) - , pos(p) - {} - - ByteCode peek() const { return (*code)[pos]; } - ByteCode pop() { return (*code)[pos++]; } - ZString pops() - { - ZString rv = code->get_str(pos); - pos += rv.size(); - ++pos; - return rv; - } -}; - -// internal -class SIR -{ - uint32_t impl; - SIR(SP v) - : impl(static_cast<uint32_t>(v)) - {} - SIR(unsigned v, uint8_t i) - : impl((i << 24) | v) - {} -public: - SIR() : impl() {} - - unsigned base() const { return impl & 0x00ffffff; } - uint8_t index() const { return impl >> 24; } - SIR iplus(uint8_t i) const { return SIR(base(), index() + i); } - static SIR from(unsigned v, uint8_t i=0) { return SIR(v, i); } - - SP sp() const { return static_cast<SP>(impl); } - static SIR from(SP v) { return SIR(v); } - - friend bool operator == (SIR l, SIR r) { return l.impl == r.impl; } - friend bool operator < (SIR l, SIR r) { return l.impl < r.impl; } -}; - -struct ScriptDataPos -{ - int numi; -}; -struct ScriptDataInt -{ - int numi; -}; -struct ScriptDataParam -{ - SIR reg; -}; -struct ScriptDataStr -{ - RString str; -}; -struct ScriptDataArg -{ - int numi; -}; -struct ScriptDataVariable -{ - SIR reg; -}; -struct ScriptDataRetInfo -{ - // Not a ScriptPointer - pos is stored in a separate slot, - // to avoid exploding the struct for everyone. - const ScriptBuffer *script; -}; -struct ScriptDataFuncRef -{ - int numi; -}; - -using ScriptDataVariantBase = Variant< - ScriptDataPos, - ScriptDataInt, - ScriptDataParam, - ScriptDataStr, - ScriptDataArg, - ScriptDataVariable, - ScriptDataRetInfo, - ScriptDataFuncRef ->; -struct script_data : ScriptDataVariantBase -{ - script_data() = delete; - // TODO see if I can delete the copy ctor/assign instead of defaulting - script_data(script_data&&) = default; - script_data(const script_data&) = default /*delete*/; - script_data& operator = (script_data&&) = default; - script_data& operator = (const script_data&) = default /*delete*/; - - script_data(ScriptDataPos v) : ScriptDataVariantBase(std::move(v)) {} - script_data(ScriptDataInt v) : ScriptDataVariantBase(std::move(v)) {} - script_data(ScriptDataParam v) : ScriptDataVariantBase(std::move(v)) {} - script_data(ScriptDataStr v) : ScriptDataVariantBase(std::move(v)) {} - script_data(ScriptDataArg v) : ScriptDataVariantBase(std::move(v)) {} - script_data(ScriptDataVariable v) : ScriptDataVariantBase(std::move(v)) {} - script_data(ScriptDataRetInfo v) : ScriptDataVariantBase(std::move(v)) {} - script_data(ScriptDataFuncRef v) : ScriptDataVariantBase(std::move(v)) {} -}; - -struct script_stack -{ - std::vector<struct script_data> stack_datav; -}; - -enum class ScriptEndState; -// future improvements coming! -class ScriptState -{ -public: - struct script_stack *stack; - int start, end; - ScriptEndState state; - BlockId rid, oid; - ScriptPointer scriptp, new_scriptp; - int defsp, new_defsp; -}; - -std::unique_ptr<const ScriptBuffer> parse_script(ZString, int, bool implicit_end); - -struct argrec_t -{ - ZString name; - union _aru - { - int i; - ZString s; - - _aru(int n) : i(n) {} - _aru(ZString z) : s(z) {} - } v; - - argrec_t(ZString n, int i) : name(n), v(i) {} - argrec_t(ZString n, ZString z) : name(n), v(z) {} -}; -int run_script_l(ScriptPointer, BlockId, BlockId, Slice<argrec_t> args); -int run_script(ScriptPointer, BlockId, BlockId); - -extern -Map<ScriptLabel, int> scriptlabel_db; -extern -UPMap<RString, const ScriptBuffer> userfunc_db; - -void do_init_script(void); -void do_final_script(void); - -extern AString mapreg_txt; - -extern int script_errors; - -bool read_constdb(ZString filename); - -void set_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e, int val); -void set_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e, XString val); - -int get_script_var_i(dumb_ptr<map_session_data> sd, VarName var, int e); -ZString get_script_var_s(dumb_ptr<map_session_data> sd, VarName var, int e); -} // namespace tmwa diff --git a/src/map/script.py b/src/map/script.py deleted file mode 100644 index a5010cd..0000000 --- a/src/map/script.py +++ /dev/null @@ -1,25 +0,0 @@ -class script_data(object): - enabled = True - - test_extra = ''' - using tmwa::operator "" _s; - ''' - - tests = [ - ('tmwa::script_data(tmwa::ScriptDataPos{42})', - '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataPos) = {numi = 42}}, <No data fields>}'), - ('tmwa::script_data(tmwa::ScriptDataInt{123})', - '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataInt) = {numi = 123}}, <No data fields>}'), - ('tmwa::script_data(tmwa::ScriptDataParam{tmwa::SIR()})', - '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataParam) = {reg = {impl = 0}}}, <No data fields>}'), - ('tmwa::script_data(tmwa::ScriptDataStr{"Hello"_s})', - '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataStr) = {str = "Hello"}}, <No data fields>}'), - ('tmwa::script_data(tmwa::ScriptDataArg{0})', - '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataArg) = {numi = 0}}, <No data fields>}'), - ('tmwa::script_data(tmwa::ScriptDataVariable{tmwa::SIR()})', - '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataVariable) = {reg = {impl = 0}}}, <No data fields>}'), - ('tmwa::script_data(tmwa::ScriptDataRetInfo{static_cast<const tmwa::ScriptBuffer *>(nullptr)})', - '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataRetInfo) = {script = 0x0}}, <No data fields>}'), - ('tmwa::script_data(tmwa::ScriptDataFuncRef{404})', - '{<tmwa::sexpr::Variant<tmwa::ScriptDataPos, tmwa::ScriptDataInt, tmwa::ScriptDataParam, tmwa::ScriptDataStr, tmwa::ScriptDataArg, tmwa::ScriptDataVariable, tmwa::ScriptDataRetInfo, tmwa::ScriptDataFuncRef>> = {(tmwa::ScriptDataFuncRef) = {numi = 404}}, <No data fields>}'), - ] diff --git a/src/map/skill-pools.cpp b/src/map/skill-pools.cpp index 89bf426..dfc70b0 100644 --- a/src/map/skill-pools.cpp +++ b/src/map/skill-pools.cpp @@ -21,9 +21,12 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" + +#include "../mmo/cxxstdio_enums.hpp" #include "battle.hpp" +#include "consts.hpp" +#include "globals.hpp" #include "pc.hpp" #include "../poison.hpp" @@ -31,26 +34,18 @@ namespace tmwa { -Array<SkillID, MAX_POOL_SKILLS> skill_pool_skills; -int skill_pool_skills_size = 0; - +namespace map +{ void skill_pool_register(SkillID id) { - if (skill_pool_skills_size + 1 >= MAX_POOL_SKILLS) - { - FPRINTF(stderr, - "Too many pool skills! Increase MAX_POOL_SKILLS and recompile."_fmt); - return; - } - - skill_pool_skills[skill_pool_skills_size++] = id; + skill_pool_skills.push_back(id); } int skill_pool(dumb_ptr<map_session_data> sd, SkillID *skills) { int i, count = 0; - for (i = 0; count < MAX_SKILL_POOL && i < skill_pool_skills_size; i++) + for (i = 0; count < MAX_SKILL_POOL && i < skill_pool_skills.size(); i++) { SkillID skill_id = skill_pool_skills[i]; if (bool(sd->status.skill[skill_id].flags & SkillFlags::POOL_ACTIVATED)) @@ -147,4 +142,5 @@ int skill_power_bl(dumb_ptr<block_list> bl, SkillID skill) else return 0; } +} // namespace map } // namespace tmwa diff --git a/src/map/skill-pools.hpp b/src/map/skill-pools.hpp index c8890e8..0f75e8e 100644 --- a/src/map/skill-pools.hpp +++ b/src/map/skill-pools.hpp @@ -23,4 +23,7 @@ namespace tmwa { +namespace map +{ +} // namespace map } // namespace tmwa diff --git a/src/map/skill.cpp b/src/map/skill.cpp index add6a42..8a397a3 100644 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -39,16 +39,18 @@ #include "../generic/random.hpp" #include "../io/cxxstdio.hpp" -#include "../io/cxxstdio_enums.hpp" +#include "../io/extract.hpp" #include "../io/read.hpp" #include "../net/timer.hpp" -#include "../mmo/extract.hpp" +#include "../mmo/cxxstdio_enums.hpp" #include "../mmo/extract_enums.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "clif.hpp" +#include "globals.hpp" #include "magic-stmt.hpp" #include "mob.hpp" #include "pc.hpp" @@ -58,7 +60,10 @@ namespace tmwa { -struct skill_name_db skill_names[] = +namespace map +{ +static +skill_name_db skill_names[] = { {SkillID::AC_OWL, "OWL"_s, "Owl's_Eye"_s}, @@ -90,9 +95,6 @@ struct skill_name_db skill_names[] = {SkillID::ZERO, ""_s, ""_s} }; -earray<skill_db_, SkillID, SkillID::MAX_SKILL_DB> skill_db; - - static int skill_attack(BF attack_type, dumb_ptr<block_list> src, dumb_ptr<block_list> dsrc, dumb_ptr<block_list> bl, @@ -286,7 +288,7 @@ int skill_attack(BF attack_type, dumb_ptr<block_list> src, lv = flag.level; dmg = battle_calc_attack(attack_type, src, bl, skillid, skilllv, flag.lo); //ダメージ計算 - damage = dmg.damage + dmg.damage2; + damage = dmg.damage; if (lv == 15) lv = -1; @@ -347,26 +349,16 @@ int skill_attack(BF attack_type, dumb_ptr<block_list> src, { hp += (dmg.damage * sd->hp_drain_per) / 100; } - if (sd->hp_drain_rate_ && dmg.damage2 > 0 - && random_::chance({sd->hp_drain_rate_, 100})) - { - hp += (dmg.damage2 * sd->hp_drain_per_) / 100; - } if (sd->sp_drain_rate > 0 && dmg.damage > 0 && random_::chance({sd->sp_drain_rate, 100})) { sp += (dmg.damage * sd->sp_drain_per) / 100; } - if (sd->sp_drain_rate_ > 0 && dmg.damage2 > 0 - && random_::chance({sd->sp_drain_rate_, 100})) - { - sp += (dmg.damage2 * sd->sp_drain_per_) / 100; - } if (hp || sp) pc_heal(sd, hp, sp); } - return (dmg.damage + dmg.damage2); /* 与ダメを返す */ + return (dmg.damage); /* 与ダメを返す */ } typedef int(*SkillFunc)(dumb_ptr<block_list>, dumb_ptr<block_list>, @@ -388,17 +380,6 @@ void skill_area_sub(dumb_ptr<block_list> bl, } -/* 範囲スキル使用処理小分けここまで - * ------------------------------------------------------------------------- - */ - -// these variables are set in the 'else' branches, -// and used in the (recursive) 'if' branch -// TODO kill it, kill it with fire. -static BlockId skill_area_temp_id; -static int skill_area_temp_hp; - - /*========================================== * スキル使用(詠唱完了、ID指定攻撃系) * (スパゲッティに向けて1歩前進!(ダメポ)) @@ -712,8 +693,7 @@ void skill_status_change_end(dumb_ptr<block_list> bl, StatusChange type, TimerDa { eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data; int opt_flag = 0, calc_flag = 0; - short *sc_count; - Option *option; + Opt0 *option; Opt1 *opt1; Opt2 *opt2; Opt3 *opt3; @@ -728,8 +708,6 @@ void skill_status_change_end(dumb_ptr<block_list> bl, StatusChange type, TimerDa sc_data = battle_get_sc_data(bl); if (not sc_data) return; - sc_count = battle_get_sc_count(bl); - nullpo_retv(sc_count); option = battle_get_option(bl); nullpo_retv(option); opt1 = battle_get_opt1(bl); @@ -753,8 +731,6 @@ void skill_status_change_end(dumb_ptr<block_list> bl, StatusChange type, TimerDa // whether we are the timer or a cancel no longer matters assert (!sc_data[type].timer); - assert ((*sc_count) > 0); - (*sc_count)--; switch (type) { /* 異常の種類ごとの処理 */ @@ -835,7 +811,6 @@ void skill_status_change_timer(TimerData *tid, tick_t tick, BlockId id, StatusCh dumb_ptr<block_list> bl; dumb_ptr<map_session_data> sd = nullptr; eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data; - //short *sc_count; //使ってない? if ((bl = map_id2bl(id)) == nullptr) return; @@ -847,8 +822,6 @@ void skill_status_change_timer(TimerData *tid, tick_t tick, BlockId id, StatusCh if (bl->bl_type == BL::PC) sd = bl->is_player(); - //sc_count=battle_get_sc_count(bl); //使ってない? - if (sc_data[type].spell_invocation) { // Must report termination magic::spell_effect_report_termination(sc_data[type].spell_invocation, @@ -936,8 +909,7 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type, { dumb_ptr<map_session_data> sd = nullptr; eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data; - short *sc_count; - Option *option; + Opt0 *option; Opt1 *opt1; Opt2 *opt2; Opt3 *opt3; @@ -949,8 +921,6 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type, sc_data = battle_get_sc_data(bl); if (not sc_data) return 0; - sc_count = battle_get_sc_count(bl); - nullpo_retz(sc_count); option = battle_get_option(bl); nullpo_retz(option); opt1 = battle_get_opt1(bl); @@ -994,7 +964,6 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type, /* 継ぎ足しができない状態異常である時は状態異常を行わない */ { - (*sc_count)--; sc_data[type].timer.cancel(); } } @@ -1080,8 +1049,6 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type, if (opt_flag) /* optionの変更 */ clif_changeoption(bl); - (*sc_count)++; /* ステータス異常の数 */ - 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, @@ -1110,8 +1077,7 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type, int skill_status_change_clear(dumb_ptr<block_list> bl, int type) { eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data; - short *sc_count; - Option *option; + Opt0 *option; Opt1 *opt1; Opt2 *opt2; Opt3 *opt3; @@ -1120,8 +1086,6 @@ int skill_status_change_clear(dumb_ptr<block_list> bl, int type) sc_data = battle_get_sc_data(bl); if (not sc_data) return 0; - sc_count = battle_get_sc_count(bl); - nullpo_retz(sc_count); option = battle_get_option(bl); nullpo_retz(option); opt1 = battle_get_opt1(bl); @@ -1131,18 +1095,15 @@ int skill_status_change_clear(dumb_ptr<block_list> bl, int type) opt3 = battle_get_opt3(bl); nullpo_retz(opt3); - if (*sc_count == 0) - return 0; for (StatusChange i : erange(StatusChange(), StatusChange::MAX_STATUSCHANGE)) { if (sc_data[i].timer) skill_status_change_end(bl, i, nullptr); } - *sc_count = 0; *opt1 = Opt1::ZERO; *opt2 = Opt2::ZERO; *opt3 = Opt3::ZERO; - *option = Option::ZERO; + *option = Opt0::ZERO; if (type == 0 || type & 2) clif_changeoption(bl); @@ -1319,4 +1280,5 @@ skill_name_db& skill_lookup_by_name(XString name) return ner; return skill_names[num_names - 1]; } +} // namespace map } // namespace tmwa diff --git a/src/map/skill.hpp b/src/map/skill.hpp index ec353ce..23881d4 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -20,16 +20,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. +#include "../mmo/skill.t.hpp" + #include "fwd.hpp" -#include "skill.t.hpp" #include "skill-pools.hpp" -#include "../strings/fwd.hpp" #include "../strings/rstring.hpp" #include "../strings/literal.hpp" -#include "../generic/fwd.hpp" #include "../generic/array.hpp" #include "map.hpp" @@ -37,6 +36,8 @@ namespace tmwa { +namespace map +{ constexpr int MAX_SKILL_PRODUCE_DB = 150; constexpr int MAX_SKILL_ARROW_DB = 150; constexpr int MAX_SKILL_ABRA_DB = 350; @@ -60,8 +61,6 @@ struct skill_db_ int weapon; Array<int, MAX_SKILL_LEVEL> castnodex; }; -extern -earray<skill_db_, SkillID, SkillID::MAX_SKILL_DB> skill_db; struct skill_name_db { @@ -75,9 +74,6 @@ struct skill_name_db {} }; -// used only by @skillid for iteration - should be depublicized -extern struct skill_name_db skill_names[]; - skill_name_db& skill_lookup_by_id(SkillID id); skill_name_db& skill_lookup_by_name(XString name); @@ -135,11 +131,6 @@ void skill_reload(void); // Max. # of active entries in the skill pool constexpr int MAX_SKILL_POOL = 3; -// Max. # of skills that may be classified as pool skills in db/skill_db.txt -constexpr int MAX_POOL_SKILLS = 128; - -extern Array<SkillID, MAX_POOL_SKILLS> skill_pool_skills; // All pool skills -extern int skill_pool_skills_size; // Number of entries in skill_pool_skills // Yields all active skills in the skill pool; no more than MAX_SKILL_POOL. Return is number of skills. int skill_pool(dumb_ptr<map_session_data> sd, SkillID *skills); @@ -166,4 +157,5 @@ int skill_power_bl(dumb_ptr<block_list> bl, SkillID skill); // [Fate] Remember that a certain skill ID belongs to a pool skill void skill_pool_register(SkillID id); +} // namespace map } // namespace tmwa diff --git a/src/map/skill.t.hpp b/src/map/skill.t.hpp deleted file mode 100644 index d0e3926..0000000 --- a/src/map/skill.t.hpp +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once -// skill.t.hpp - Old-style skills. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> -// -// 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 <http://www.gnu.org/licenses/>. - -#include "fwd.hpp" - -#include <cstdint> - -#include "../generic/enum.hpp" - - -namespace tmwa -{ -// TODO remove most of these as their corresponding SkillIDs get deleted. -enum class StatusChange : uint16_t -{ - // indices into (map_session_data).status_change - SC_SENDMAX = 256, - - // sometimes means "none", sometimes not - NEGATIVE1 = 0xffff, - - // 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 - - SC_WEIGHT50 = 35, // ? sort of used - SC_WEIGHT90 = 36, // definitely used - SC_SPEEDPOTION0 = 37, // item script - - SC_HEALING = 70, // item script - - SC_POISON = 132, // bad; actually used - - SC_ATKPOT = 185, // item script - SC_MATKPOT = 186, // unused, but kept for parallel - -// Added for Fate's spells - SC_HIDE = 194, // Hide from `detect' magic (PCs only) - SC_SHEARED = 194, // Has been sheared (mobs only) - SC_HALT_REGENERATE = 195, // Suspend regeneration - SC_FLYING_BACKPACK = 196, // Flying backpack - SC_MBARRIER = 197, // Magical barrier, magic resistance (val1 : power (%)) - SC_HASTE = 198, // `Haste' spell (val1 : power) - SC_PHYS_SHIELD = 199, // `Protect' spell, reduce damage (val1: power) - MAX_STATUSCHANGE = 200, -}; - -enum class SkillID : uint16_t -{ - // TODO: Remove these! - NEGATIVE = 0xffff, - ZERO = 0x0000, - ONE = 0x0001, - - // Basic skills. - // These should probably be made unconditional. - NV_EMOTE = 1, // - NV_TRADE = 2, // - NV_PARTY = 3, // - - AC_OWL = 45, // Mallard's Eye - - NPC_SELFDESTRUCTION = 175, // - - NPC_POISON = 178, // - - NPC_SUMMONSLAVE = 198, // - NPC_EMOTION = 199, // - - TMW_SKILLPOOL = 339, // skill pool size - - // magic skills - TMW_MAGIC = 340, // - TMW_MAGIC_LIFE = 341, // - TMW_MAGIC_WAR = 342, // - TMW_MAGIC_TRANSMUTE = 343, // - TMW_MAGIC_NATURE = 344, // - TMW_MAGIC_ETHER = 345, // - TMW_MAGIC_DARK = 346, // - TMW_MAGIC_LIGHT = 347, // - - // focusable skills - TMW_BRAWLING = 350, // - TMW_LUCKY_COUNTER = 351, // - TMW_SPEED = 352, // - TMW_RESIST_POISON = 353, // - TMW_ASTRAL_SOUL = 354, // - TMW_RAGING = 355, // - - // Note: this value is also hard-coded in common/mmo.hpp - MAX_SKILL_DB = 474, // not 450 -}; - -namespace e -{ -enum class SkillFlags : uint16_t -{ - ZERO = 0x00, - // is a pool skill - POOL_FLAG = 0x01, - // is an active pool skill - POOL_ACTIVE = 0x02, - // pool skill has been activated (used for clif) - POOL_ACTIVATED = 0x04, -}; -ENUM_BITWISE_OPERATORS(SkillFlags) -} -using e::SkillFlags; -} // namespace tmwa diff --git a/src/map/storage.cpp b/src/map/storage.cpp index a6e6a0d..1327146 100644 --- a/src/map/storage.cpp +++ b/src/map/storage.cpp @@ -25,10 +25,11 @@ #include "../generic/db.hpp" #include "../mmo/ids.hpp" -#include "../mmo/mmo.hpp" +#include "../high/mmo.hpp" #include "chrif.hpp" #include "clif.hpp" +#include "globals.hpp" #include "intif.hpp" #include "itemdb.hpp" #include "map.hpp" @@ -39,27 +40,22 @@ namespace tmwa { -static -Map<AccountId, Storage> storage_db; - +namespace map +{ void do_final_storage(void) { storage_db.clear(); } -Storage *account2storage(AccountId account_id) +Borrowed<Storage> account2storage(AccountId account_id) { - Storage *stor = storage_db.search(account_id); - if (stor == nullptr) - { - stor = storage_db.init(account_id); - stor->account_id = account_id; - } + P<Storage> stor = storage_db.init(account_id); + stor->account_id = account_id; return stor; } // Just to ask storage, without creation -Storage *account2storage2(AccountId account_id) +Option<Borrowed<Storage>> account2storage2(AccountId account_id) { return storage_db.search(account_id); } @@ -81,12 +77,11 @@ int storage_storageopen(dumb_ptr<map_session_data> sd) if (sd->state.storage_open) return 1; //Already open? - Storage *stor = storage_db.search(sd->status_key.account_id); - if (stor == nullptr) + P<Storage> stor = TRY_UNWRAP(storage_db.search(sd->status_key.account_id), { //Request storage. intif_request_storage(sd->status_key.account_id); return 1; - } + }); if (stor->storage_status) return 1; //Already open/player already has it open... @@ -104,15 +99,13 @@ int storage_storageopen(dumb_ptr<map_session_data> sd) *------------------------------------------ */ static -int storage_additem(dumb_ptr<map_session_data> sd, Storage *stor, +int storage_additem(dumb_ptr<map_session_data> sd, P<Storage> stor, Item *item_data, int amount) { - struct item_data *data; - if (!item_data->nameid || amount <= 0) return 1; - data = itemdb_search(item_data->nameid); + P<struct item_data> data = itemdb_search(item_data->nameid); if (!itemdb_isequip2(data)) { //Stackable @@ -152,7 +145,7 @@ int storage_additem(dumb_ptr<map_session_data> sd, Storage *stor, *------------------------------------------ */ static -int storage_delitem(dumb_ptr<map_session_data> sd, Storage *stor, +int storage_delitem(dumb_ptr<map_session_data> sd, P<Storage> stor, SOff0 n, int amount) { @@ -178,11 +171,8 @@ int storage_delitem(dumb_ptr<map_session_data> sd, Storage *stor, */ int storage_storageadd(dumb_ptr<map_session_data> sd, IOff0 index, int amount) { - Storage *stor; - nullpo_retz(sd); - stor = account2storage2(sd->status_key.account_id); - nullpo_retz(stor); + P<Storage> stor = TRY_UNWRAP(account2storage2(sd->status_key.account_id), return 0); if ((stor->storage_amount > MAX_STORAGE) || !stor->storage_status) return 0; // storage full / storage closed @@ -213,12 +203,10 @@ int storage_storageadd(dumb_ptr<map_session_data> sd, IOff0 index, int amount) */ int storage_storageget(dumb_ptr<map_session_data> sd, SOff0 index, int amount) { - Storage *stor; PickupFail flag; nullpo_retz(sd); - stor = account2storage2(sd->status_key.account_id); - nullpo_retz(stor); + P<Storage> stor = TRY_UNWRAP(account2storage2(sd->status_key.account_id), return 0); if (!index.ok()) return 0; @@ -243,11 +231,8 @@ int storage_storageget(dumb_ptr<map_session_data> sd, SOff0 index, int amount) */ int storage_storageclose(dumb_ptr<map_session_data> sd) { - Storage *stor; - nullpo_retz(sd); - stor = account2storage2(sd->status_key.account_id); - nullpo_retz(stor); + P<Storage> stor = TRY_UNWRAP(account2storage2(sd->status_key.account_id), return 0); clif_storageclose(sd); if (stor->storage_status) @@ -275,12 +260,9 @@ int storage_storageclose(dumb_ptr<map_session_data> sd) */ int storage_storage_quit(dumb_ptr<map_session_data> sd) { - Storage *stor; - nullpo_retz(sd); - stor = account2storage2(sd->status_key.account_id); - if (stor) + P<Storage> stor = TRY_UNWRAP(account2storage2(sd->status_key.account_id), return 0); { chrif_save(sd); //Invokes the storage saving as well. stor->storage_status = 0; @@ -292,11 +274,7 @@ int storage_storage_quit(dumb_ptr<map_session_data> sd) int storage_storage_save(AccountId account_id, int final) { - Storage *stor; - - stor = account2storage2(account_id); - if (!stor) - return 0; + P<Storage> stor = TRY_UNWRAP(account2storage2(account_id), return 0); if (stor->dirty) { @@ -320,9 +298,8 @@ int storage_storage_save(AccountId account_id, int final) //Ack from Char-server indicating the storage was saved. [Skotlex] int storage_storage_saved(AccountId account_id) { - Storage *stor = account2storage2(account_id); + P<Storage> stor = TRY_UNWRAP(account2storage2(account_id), return 0); - if (stor) { //Only mark it clean if it's not in use. [Skotlex] if (stor->dirty && stor->storage_status == 0) @@ -334,4 +311,5 @@ int storage_storage_saved(AccountId account_id) } return 0; } +} // namespace map } // namespace tmwa diff --git a/src/map/storage.hpp b/src/map/storage.hpp index f3875c8..5f118c3 100644 --- a/src/map/storage.hpp +++ b/src/map/storage.hpp @@ -22,23 +22,24 @@ #include "fwd.hpp" -#include "../generic/fwd.hpp" +#include "../proto2/net-Storage.hpp" -#include "../mmo/fwd.hpp" - -#include "clif.t.hpp" +#include "../mmo/clif.t.hpp" namespace tmwa { +namespace map +{ int storage_storageopen(dumb_ptr<map_session_data> sd); int storage_storageadd(dumb_ptr<map_session_data> sd, IOff0 index, int amount); int storage_storageget(dumb_ptr<map_session_data> sd, SOff0 index, int amount); int storage_storageclose(dumb_ptr<map_session_data> sd); void do_final_storage(void); -Storage *account2storage(AccountId account_id); -Storage *account2storage2(AccountId account_id); +Borrowed<Storage> account2storage(AccountId account_id); +Option<Borrowed<Storage>> account2storage2(AccountId account_id); int storage_storage_quit(dumb_ptr<map_session_data> sd); int storage_storage_save(AccountId account_id, int final); int storage_storage_saved(AccountId account_id); +} // namespace map } // namespace tmwa diff --git a/src/map/tmw.cpp b/src/map/tmw.cpp index 60b5027..df76720 100644 --- a/src/map/tmw.cpp +++ b/src/map/tmw.cpp @@ -28,13 +28,16 @@ #include "../io/cxxstdio.hpp" +#include "../net/timer.hpp" + #include "../mmo/human_time_diff.hpp" -#include "../mmo/utils.hpp" #include "atcommand.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "chrif.hpp" #include "clif.hpp" +#include "globals.hpp" #include "intif.hpp" #include "map.hpp" #include "pc.hpp" @@ -44,8 +47,10 @@ namespace tmwa { +namespace map +{ static -void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, int length); +void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, std::chrono::hours length); static bool tmw_CheckChatLameness(dumb_ptr<map_session_data> sd, XString message); @@ -53,21 +58,21 @@ bool tmw_CheckChatLameness(dumb_ptr<map_session_data> sd, XString message); int tmw_CheckChatSpam(dumb_ptr<map_session_data> sd, XString message) { nullpo_retr(1, sd); - TimeT now = TimeT::now(); + tick_t now = gettick(); if (pc_isGM(sd)) return 0; if (now > sd->chat_reset_due) { - sd->chat_reset_due = static_cast<time_t>(now) + battle_config.chat_spam_threshold; + sd->chat_reset_due = now + battle_config.chat_spam_threshold; sd->chat_lines_in = 0; } if (now > sd->chat_repeat_reset_due) { sd->chat_repeat_reset_due = - static_cast<time_t>(now) + (battle_config.chat_spam_threshold * 60); + now + (battle_config.chat_spam_threshold * 60); sd->chat_total_repeats = 0; } @@ -100,20 +105,20 @@ int tmw_CheckChatSpam(dumb_ptr<map_session_data> sd, XString message) return 1; } - if (battle_config.chat_spam_ban && + if (battle_config.chat_spam_ban != std::chrono::hours::zero() && (sd->chat_lines_in >= battle_config.chat_spam_warn || sd->chat_total_repeats >= battle_config.chat_spam_warn)) { - clif_displaymessage(sd->sess, "WARNING: You are about to be automatically banned for spam!"_s); - clif_displaymessage(sd->sess, "WARNING: Please slow down, do not repeat, and do not SHOUT!"_s); + clif_displaymessage(sd->sess, "##1WARNING : ##BYou are about to be automatically banned for spam!"_s); + clif_displaymessage(sd->sess, "##1WARNING : ##BPlease slow down, do not repeat, and do not SHOUT!"_s); } return 0; } -void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, int length) +void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, std::chrono::hours length) { - if (length == 0 || sd->auto_ban_info.in_progress) + if (length == std::chrono::hours::zero() || sd->auto_ban_info.in_progress) return; sd->auto_ban_info.in_progress = 1; @@ -124,7 +129,7 @@ void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, int length) tmw_GmHackMsg(hack_msg); AString fake_command = STRPRINTF("@autoban %s %dh (%s spam)"_fmt, - sd->status_key.name, length, reason); + sd->status_key.name, static_cast<uint16_t>(length.count()), reason); log_atcommand(sd, fake_command); AString anotherbuf = STRPRINTF("You have been banned for %s spamming. Please do not spam."_fmt, @@ -133,7 +138,7 @@ void tmw_AutoBan(dumb_ptr<map_session_data> sd, ZString reason, int length) clif_displaymessage(sd->sess, anotherbuf); /* type: 2 - ban(year, month, day, hour, minute, second) */ HumanTimeDiff ban_len {}; - ban_len.hour = length; + ban_len.hour = length.count(); chrif_char_ask_name(AccountId(), sd->status_key.name, 2, ban_len); clif_setwaitclose(sd->sess); } @@ -162,8 +167,9 @@ bool tmw_CheckChatLameness(dumb_ptr<map_session_data>, XString message) // Sends a whisper to all GMs void tmw_GmHackMsg(ZString line) { - intif_wis_message_to_gm(wisp_server_name, - GmLevel::from(static_cast<uint32_t>(battle_config.hack_info_GM_level)), + intif_wis_message_to_gm(WISP_SERVER_NAME, + battle_config.hack_info_GM_level, line); } +} // namespace map } // namespace tmwa diff --git a/src/map/tmw.hpp b/src/map/tmw.hpp index ffd6f7f..bc0043c 100644 --- a/src/map/tmw.hpp +++ b/src/map/tmw.hpp @@ -21,13 +21,12 @@ #include "fwd.hpp" -#include "../strings/fwd.hpp" - -#include "../generic/fwd.hpp" - namespace tmwa { +namespace map +{ int tmw_CheckChatSpam(dumb_ptr<map_session_data> sd, XString message); void tmw_GmHackMsg(ZString line); +} // namespace map } // namespace tmwa diff --git a/src/map/trade.cpp b/src/map/trade.cpp index c52377d..c03609c 100644 --- a/src/map/trade.cpp +++ b/src/map/trade.cpp @@ -25,7 +25,9 @@ #include "../io/cxxstdio.hpp" #include "battle.hpp" +#include "battle_conf.hpp" #include "clif.hpp" +#include "globals.hpp" #include "itemdb.hpp" #include "map.hpp" #include "npc.hpp" @@ -37,6 +39,8 @@ namespace tmwa { +namespace map +{ /*========================================== * 取引要請を相手に送る *------------------------------------------ @@ -129,7 +133,6 @@ void trade_tradeack(dumb_ptr<map_session_data> sd, int type) void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount) { dumb_ptr<map_session_data> target_sd; - struct item_data *id; int trade_i; int trade_weight = 0; int free_ = 0; @@ -156,7 +159,7 @@ void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount) for (IOff0 i : IOff0::iter()) { if (!target_sd->status.inventory[i].nameid - && target_sd->inventory_data[i] == nullptr) + && target_sd->inventory_data[i].is_none()) free_++; } for (trade_i = 0; trade_i < TRADE_MAX; trade_i++) @@ -164,26 +167,31 @@ void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount) if (sd->deal_item_amount[trade_i] == 0) { // calculate trade weight + // note: 'abort' branch is protected by 'amount' check above trade_weight += - sd->inventory_data[index.unshift()]->weight * amount; + TRY_UNWRAP(sd->inventory_data[index.unshift()], abort())->weight * amount; // determine if item is a stackable already in receivers inventory, and up free count for (IOff0 i : IOff0::iter()) { - if (target_sd->status.inventory[i].nameid == - sd->status.inventory[index.unshift()].nameid - && target_sd->inventory_data[i] != nullptr) + if (target_sd->status.inventory[i].nameid != + sd->status.inventory[index.unshift()].nameid) + continue; + + OMATCH_BEGIN_SOME (id, target_sd->inventory_data[i]) { - id = target_sd->inventory_data[i]; if (id->type != ItemType::WEAPON && id->type != ItemType::ARMOR && id->type != ItemType::_7 && id->type != ItemType::_8) { free_++; - break; + goto break_outer1; } } + OMATCH_END (); + break_outer1: + break; } if (target_sd->weight + trade_weight > @@ -218,28 +226,32 @@ void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount) else { // calculate weight for stored deal + // note: 'abort' branch is protected by 'amount' check above trade_weight += - sd->inventory_data[sd->deal_item_index[trade_i].unshift() - ]->weight * + TRY_UNWRAP(sd->inventory_data[sd->deal_item_index[trade_i].unshift() + ], abort())->weight * sd->deal_item_amount[trade_i]; // count free stackables in stored deal for (IOff0 i : IOff0::iter()) { - if (target_sd->status.inventory[i].nameid == + if (target_sd->status.inventory[i].nameid != sd->status. - inventory[sd->deal_item_index[trade_i].unshift()].nameid - && target_sd->inventory_data[i] != nullptr) + inventory[sd->deal_item_index[trade_i].unshift()].nameid) + continue; + OMATCH_BEGIN_SOME (id, target_sd->inventory_data[i]) { - id = target_sd->inventory_data[i]; if (id->type != ItemType::WEAPON && id->type != ItemType::ARMOR && id->type != ItemType::_7 && id->type != ItemType::_8) { free_++; - break; + goto break_outer2; } } + OMATCH_END (); + break_outer2: + break; } } // used a slot, but might be cancelled out by stackable checks above @@ -465,4 +477,5 @@ void trade_verifyzeny(dumb_ptr<map_session_data> sd) } } } +} // namespace map } // namespace tmwa diff --git a/src/map/trade.hpp b/src/map/trade.hpp index 91ed954..569524b 100644 --- a/src/map/trade.hpp +++ b/src/map/trade.hpp @@ -22,13 +22,13 @@ #include "fwd.hpp" -#include "../generic/fwd.hpp" - -#include "clif.t.hpp" +#include "../mmo/clif.t.hpp" namespace tmwa { +namespace map +{ void trade_traderequest(dumb_ptr<map_session_data> sd, BlockId target_id); void trade_tradeack(dumb_ptr<map_session_data> sd, int type); void trade_tradeadditem(dumb_ptr<map_session_data> sd, IOff2 index, int amount); @@ -36,4 +36,5 @@ void trade_tradeok(dumb_ptr<map_session_data> sd); void trade_tradecancel(dumb_ptr<map_session_data> sd); void trade_tradecommit(dumb_ptr<map_session_data> sd); void trade_verifyzeny(dumb_ptr<map_session_data> sd); +} // namespace map } // namespace tmwa |