summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/atcommand.cpp762
-rw-r--r--src/map/atcommand.hpp14
-rw-r--r--src/map/battle.cpp628
-rw-r--r--src/map/battle.hpp132
-rw-r--r--src/map/battle.t.hpp3
-rw-r--r--src/map/chrif.cpp370
-rw-r--r--src/map/chrif.hpp23
-rw-r--r--src/map/clif.cpp399
-rw-r--r--src/map/clif.hpp42
-rw-r--r--src/map/clif.t.hpp717
-rw-r--r--src/map/consts.hpp (renamed from src/map/magic-expr-eval.cpp)15
-rw-r--r--src/map/fwd.hpp37
-rw-r--r--src/map/globals.cpp149
-rw-r--r--src/map/globals.hpp109
-rw-r--r--src/map/grfio.cpp13
-rw-r--r--src/map/grfio.hpp7
-rw-r--r--src/map/intif.cpp20
-rw-r--r--src/map/intif.hpp13
-rw-r--r--src/map/itemdb.cpp173
-rw-r--r--src/map/itemdb.hpp17
-rw-r--r--src/map/magic-expr-eval.hpp3
-rw-r--r--src/map/magic-expr.cpp198
-rw-r--r--src/map/magic-expr.hpp13
-rw-r--r--src/map/magic-expr.py8
-rw-r--r--src/map/magic-interpreter-base.cpp27
-rw-r--r--src/map/magic-interpreter-base.hpp12
-rw-r--r--src/map/magic-interpreter.hpp23
-rw-r--r--src/map/magic-interpreter.py212
-rw-r--r--src/map/magic-interpreter.t.hpp3
-rw-r--r--src/map/magic-stmt.cpp85
-rw-r--r--src/map/magic-stmt.hpp9
-rw-r--r--src/map/magic-stmt.py8
-rw-r--r--src/map/magic-v2.cpp51
-rw-r--r--src/map/magic-v2.hpp3
-rw-r--r--src/map/magic.cpp6
-rw-r--r--src/map/magic.hpp9
-rw-r--r--src/map/main.cpp5
-rw-r--r--src/map/map.cpp523
-rw-r--r--src/map/map.hpp151
-rw-r--r--src/map/map.py34
-rw-r--r--src/map/map.t.hpp10
-rw-r--r--src/map/mapflag.cpp7
-rw-r--r--src/map/mapflag.hpp7
-rw-r--r--src/map/mapflag.py10
-rw-r--r--src/map/mob.cpp151
-rw-r--r--src/map/mob.hpp16
-rw-r--r--src/map/mob.t.hpp3
-rw-r--r--src/map/npc-internal.hpp (renamed from src/map/magic-interpreter.cpp)20
-rw-r--r--src/map/npc-parse.cpp807
-rw-r--r--src/map/npc-parse.hpp44
-rw-r--r--src/map/npc.cpp956
-rw-r--r--src/map/npc.hpp27
-rw-r--r--src/map/party.cpp143
-rw-r--r--src/map/party.hpp15
-rw-r--r--src/map/path.cpp17
-rw-r--r--src/map/path.hpp5
-rw-r--r--src/map/pc.cpp978
-rw-r--r--src/map/pc.hpp27
-rw-r--r--src/map/pc.t.hpp3
-rw-r--r--src/map/quest.cpp135
-rw-r--r--src/map/quest.hpp51
-rw-r--r--src/map/script-buffer.hpp45
-rw-r--r--src/map/script-call-internal.hpp100
-rw-r--r--src/map/script-call-internal.tcc79
-rw-r--r--src/map/script-call.cpp926
-rw-r--r--src/map/script-call.hpp67
-rw-r--r--src/map/script-call.t.hpp48
-rw-r--r--src/map/script-fun.cpp3164
-rw-r--r--src/map/script-fun.hpp41
-rw-r--r--src/map/script-parse-internal.hpp67
-rw-r--r--src/map/script-parse.cpp851
-rw-r--r--src/map/script-parse.hpp36
-rw-r--r--src/map/script-parse.py553
-rw-r--r--src/map/script-persist.hpp128
-rw-r--r--src/map/script-persist.py37
-rw-r--r--src/map/script-startup-internal.hpp36
-rw-r--r--src/map/script-startup.cpp265
-rw-r--r--src/map/script-startup.hpp34
-rw-r--r--src/map/script.cpp5100
-rw-r--r--src/map/script.hpp277
-rw-r--r--src/map/script.py25
-rw-r--r--src/map/skill-pools.cpp22
-rw-r--r--src/map/skill-pools.hpp3
-rw-r--r--src/map/skill.cpp68
-rw-r--r--src/map/skill.hpp18
-rw-r--r--src/map/skill.t.hpp136
-rw-r--r--src/map/storage.cpp62
-rw-r--r--src/map/storage.hpp13
-rw-r--r--src/map/tmw.cpp34
-rw-r--r--src/map/tmw.hpp7
-rw-r--r--src/map/trade.cpp43
-rw-r--r--src/map/trade.hpp7
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>(&param1, &param2, &param3)))
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 = &pm;
+ 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