summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWushin <pasekei@gmail.com>2015-05-19 21:50:41 -0500
committerWushin <pasekei@gmail.com>2015-05-19 21:50:41 -0500
commit29ce27e1557404b6cd03eaab2367c5a698e6ab44 (patch)
tree28da27416f52edaa41c049a60ebcc4869ddde1f2
parent20cbf97dbaded8c2bead5b4f1a4838528c63a2a5 (diff)
parent4166a53fa3ced75afa25bd5bfb20ca87eca0e25f (diff)
downloadtmwa-29ce27e1557404b6cd03eaab2367c5a698e6ab44.tar.gz
tmwa-29ce27e1557404b6cd03eaab2367c5a698e6ab44.tar.bz2
tmwa-29ce27e1557404b6cd03eaab2367c5a698e6ab44.tar.xz
tmwa-29ce27e1557404b6cd03eaab2367c5a698e6ab44.zip
Merge pull request #89 from mekolat/sitv15.5.19
allow to make npcs sit
-rw-r--r--CHANGELOG9
-rw-r--r--src/char/char.hpp3
-rw-r--r--src/login/login.cpp4
-rw-r--r--src/login/login.hpp2
-rw-r--r--src/map/clif.cpp142
-rw-r--r--src/map/clif.hpp4
-rw-r--r--src/map/map.hpp2
-rw-r--r--src/map/npc-parse.cpp4
-rw-r--r--src/map/script-fun.cpp38
-rw-r--r--src/mmo/login.t.hpp44
-rw-r--r--src/mmo/version.hpp3
-rwxr-xr-xtools/protocol.py85
12 files changed, 222 insertions, 118 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 8790848..a4b24c8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,12 @@
+v15.5.19
+ - check client version in login server
+ - make npcs sit-able
+ - send mob attack range
+ - send mob hp properly
+ - send npc sex
+ - add builtin setnpcdirection
+ - make packet SMSG_BEING_VISIBLE follow manaplus spec
+ - make packet SMSG_BEING_MOVE follow manaplus spec
v15.5.18
- reject old incompatible clients
- check client version before sending quest data
diff --git a/src/char/char.hpp b/src/char/char.hpp
index 8adac4d..049875b 100644
--- a/src/char/char.hpp
+++ b/src/char/char.hpp
@@ -42,9 +42,6 @@ std::chrono::seconds DEFAULT_AUTOSAVE_INTERVAL = 5_min;
constexpr
GmLevel default_gm_level = GmLevel::from(0_u32);
-// increase the min version when the protocol is incompatible with old m+ versions
-#define MIN_CLIENT_VERSION 1
-
struct AuthFifoEntry
{
AccountId account_id;
diff --git a/src/login/login.cpp b/src/login/login.cpp
index 886c510..9310ad3 100644
--- a/src/login/login.cpp
+++ b/src/login/login.cpp
@@ -2494,9 +2494,7 @@ void parse_login(Session *s)
result = mmo_auth(&account, s);
if (result == -1)
{
- VERSION_2 version_2 = fixed.version_2_flags;
- if (!bool(version_2 & VERSION_2::UPDATEHOST)
- || !bool(version_2 & VERSION_2::SERVERORDER))
+ if (fixed.version < MIN_CLIENT_VERSION)
result = 5; // client too old
}
if (result == -1)
diff --git a/src/login/login.hpp b/src/login/login.hpp
index ae99558..ba42bae 100644
--- a/src/login/login.hpp
+++ b/src/login/login.hpp
@@ -18,8 +18,6 @@
// 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 "login.t.hpp"
-
#include "fwd.hpp"
#include "../strings/vstring.hpp"
diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index 897244e..bae0bf1 100644
--- a/src/map/clif.cpp
+++ b/src/map/clif.cpp
@@ -131,6 +131,9 @@ static
void clif_quitsave(Session *, dumb_ptr<map_session_data> sd);
static
+void clif_sitnpc_sub(Buffer& buf, dumb_ptr<npc_data> nd, DamageType dmg);
+
+static
void clif_delete(Session *s)
{
assert (s != char_session);
@@ -769,6 +772,8 @@ static
void clif_mob0078(dumb_ptr<mob_data> md, Buffer& buf)
{
nullpo_retv(md);
+ int max_hp = md->stats[mob_stat::MAX_HP];
+ int hp = md->hp;
Packet_Fixed<0x0078> fixed_78;
fixed_78.block_id = md->bl_id;
@@ -781,10 +786,12 @@ void clif_mob0078(dumb_ptr<mob_data> md, Buffer& buf)
fixed_78.pos.x = md->bl_x;
fixed_78.pos.y = md->bl_y;
fixed_78.pos.dir = md->dir;
- fixed_78.five1 = 5;
- fixed_78.five2 = 5;
- int level = battle_get_lv(md);
- fixed_78.level = (level > battle_config.max_lv) ? battle_config.max_lv : level;
+
+ fixed_78.gloves_or_part_of_hp = static_cast<short>(hp >> 16);
+ fixed_78.part_of_guild_id_or_part_of_hp = static_cast<short>(hp & 0xffff);
+ fixed_78.part_of_guild_id_or_part_of_max_hp = static_cast<short>(max_hp >> 16);
+ fixed_78.guild_emblem_or_part_of_max_hp = static_cast<short>(max_hp & 0xffff);
+ fixed_78.karma_or_attack_range = battle_get_range(md);
buf = create_fpacket<0x0078, 54>(fixed_78);
}
@@ -797,6 +804,8 @@ static
void clif_mob007b(dumb_ptr<mob_data> md, Buffer& buf)
{
nullpo_retv(md);
+ int max_hp = md->stats[mob_stat::MAX_HP];
+ int hp = md->hp;
Packet_Fixed<0x007b> fixed_7b;
fixed_7b.block_id = md->bl_id;
@@ -806,18 +815,18 @@ void clif_mob007b(dumb_ptr<mob_data> md, Buffer& buf)
fixed_7b.option = md->option;
fixed_7b.mob_class = md->mob_class;
// snip: stuff for monsters disguised as PCs
- fixed_7b.tick_and_maybe_part_of_guild_emblem = gettick();
- fixed_7b.max_hp = md->stats[mob_stat::MAX_HP];
- fixed_7b.hp = md->hp;
+ fixed_7b.tick = gettick();
fixed_7b.pos2.x0 = md->bl_x;
fixed_7b.pos2.y0 = md->bl_y;
fixed_7b.pos2.x1 = md->to_x;
fixed_7b.pos2.y1 = md->to_y;
- fixed_7b.five1 = 5;
- fixed_7b.five2 = 5;
- int level = battle_get_lv(md);
- fixed_7b.level = (level > battle_config.max_lv) ? battle_config.max_lv : level;
+
+ fixed_7b.gloves_or_part_of_hp = static_cast<short>(hp >> 16);
+ fixed_7b.part_of_guild_id_or_part_of_hp = static_cast<short>(hp & 0xffff);
+ fixed_7b.part_of_guild_id_or_part_of_max_hp = static_cast<short>(max_hp >> 16);
+ fixed_7b.guild_emblem_or_part_of_max_hp = static_cast<short>(max_hp & 0xffff);
+ fixed_7b.karma_or_attack_range = battle_get_range(md);
buf = create_fpacket<0x007b, 60>(fixed_7b);
}
@@ -884,11 +893,7 @@ void clif_npc0078(dumb_ptr<npc_data> nd, Buffer& buf)
fixed_78.pos.x = nd->bl_x;
fixed_78.pos.y = nd->bl_y;
fixed_78.pos.dir = nd->dir;
- fixed_78.five1 = 5;
- fixed_78.five2 = 5;
- fixed_78.zero = 0;
- fixed_78.level = 0;
-
+ fixed_78.sex = nd->sex;
buf = create_fpacket<0x0078, 54>(fixed_78);
}
@@ -951,7 +956,7 @@ int clif_spawnnpc(dumb_ptr<npc_data> nd)
if (nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS)
return 0;
-
+ /* manaplus is skipping this packet
Packet_Fixed<0x007c> fixed_7c;
fixed_7c.block_id = nd->bl_id;
fixed_7c.speed = nd->speed;
@@ -961,10 +966,18 @@ int clif_spawnnpc(dumb_ptr<npc_data> nd)
Buffer buf = create_fpacket<0x007c, 41>(fixed_7c);
clif_send(buf, nd, SendWho::AREA);
-
+ */
+ Buffer buf;
clif_npc0078(nd, buf);
clif_send(buf, nd, SendWho::AREA);
+ if(nd->sit == DamageType::SIT)
+ {
+ Buffer buff;
+ clif_sitnpc_sub(buff, nd, nd->sit);
+ clif_send(buff, nd, SendWho::AREA);
+ }
+
return 0;
}
@@ -995,15 +1008,8 @@ int clif_spawn_fake_npc_for_player(dumb_ptr<map_session_data> sd, BlockId fake_n
fixed_78.opt2 = Opt2::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;
fixed_78.pos.y = sd->bl_y;
- fixed_78.unused_pos_again.x = sd->bl_x;
- fixed_78.unused_pos_again.y = sd->bl_y;
- fixed_78.five1 = 5;
- fixed_78.five2 = 5;
- fixed_78.zero = 0;
- fixed_78.level = 0;
send_fpacket<0x0078, 54>(s, fixed_78);
return 0;
@@ -2374,6 +2380,13 @@ void clif_getareachar_npc(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data> nd)
Buffer buf;
clif_npc0078(nd, buf);
send_buffer(sd->sess, buf);
+
+ if(nd->sit == DamageType::SIT)
+ {
+ Buffer buff;
+ clif_sitnpc_sub(buff, nd, nd->sit);
+ send_buffer(sd->sess, buff);
+ }
}
/*==========================================
@@ -3190,6 +3203,85 @@ void clif_sitting(Session *, dumb_ptr<map_session_data> sd)
clif_send(buf, sd, SendWho::AREA);
}
+static
+void clif_sitnpc_sub(Buffer& buf, dumb_ptr<npc_data> nd, DamageType dmg)
+{
+ nullpo_retv(nd);
+
+ Packet_Fixed<0x008a> fixed_8a;
+ fixed_8a.src_id = nd->bl_id;
+ fixed_8a.damage_type = dmg;
+ buf = create_fpacket<0x008a, 29>(fixed_8a);
+}
+
+void clif_sitnpc_towards(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data> nd, DamageType dmg)
+{
+ nullpo_retv(nd);
+ nullpo_retv(sd);
+
+ if(!sd)
+ return;
+
+ Buffer buf;
+ clif_sitnpc_sub(buf, nd, dmg);
+ clif_send(buf, sd, SendWho::SELF);
+}
+
+void clif_sitnpc(dumb_ptr<npc_data> nd, DamageType dmg)
+{
+ nullpo_retv(nd);
+
+ Buffer buf;
+ clif_sitnpc_sub(buf, nd, dmg);
+ clif_send(buf, nd, SendWho::AREA);
+}
+
+static
+void clif_setnpcdirection_sub(Buffer& buf, dumb_ptr<npc_data> nd, DIR direction)
+{
+ nullpo_retv(nd);
+ short dir = 1 | 0;
+
+ switch (direction)
+ {
+ case DIR::S: dir = 1 | 0; break; // down
+ case DIR::SW: dir = 1 | 2; break;
+ case DIR::W: dir = 0 | 2; break; // left
+ case DIR::NW: dir = 4 | 2; break;
+ case DIR::N: dir = 4 | 0; break; // up
+ case DIR::NE: dir = 4 | 8; break;
+ case DIR::E: dir = 0 | 8; break; // right
+ case DIR::SE: dir = 1 | 8; break;
+ }
+
+ Packet_Fixed<0x009c> fixed_9c;
+ fixed_9c.block_id = nd->bl_id;
+ fixed_9c.client_dir = dir;
+ buf = create_fpacket<0x009c, 9>(fixed_9c);
+}
+
+void clif_setnpcdirection_towards(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data> nd, DIR direction)
+{
+ nullpo_retv(nd);
+ nullpo_retv(sd);
+
+ if(!sd)
+ return;
+
+ Buffer buf;
+ clif_setnpcdirection_sub(buf, nd, direction);
+ clif_send(buf, sd, SendWho::SELF);
+}
+
+void clif_setnpcdirection(dumb_ptr<npc_data> nd, DIR direction)
+{
+ nullpo_retv(nd);
+
+ Buffer buf;
+ clif_setnpcdirection_sub(buf, nd, direction);
+ clif_send(buf, nd, SendWho::AREA);
+}
+
/*==========================================
*
*------------------------------------------
diff --git a/src/map/clif.hpp b/src/map/clif.hpp
index 5117ff3..3cc308c 100644
--- a/src/map/clif.hpp
+++ b/src/map/clif.hpp
@@ -98,6 +98,10 @@ 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);
+void clif_sitnpc(dumb_ptr<npc_data> nd, DamageType dmg);
+void clif_sitnpc_towards(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data> nd, DamageType dmg);
+void clif_setnpcdirection(dumb_ptr<npc_data> nd, DIR direction);
+void clif_setnpcdirection_towards(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data> nd, DIR direction);
// trade
void clif_traderequest(dumb_ptr<map_session_data> sd, CharName name);
diff --git a/src/map/map.hpp b/src/map/map.hpp
index ce434fa..aac646b 100644
--- a/src/map/map.hpp
+++ b/src/map/map.hpp
@@ -324,6 +324,8 @@ struct npc_data : block_list
short n;
Species npc_class;
DIR dir;
+ SEX sex;
+ DamageType sit;
interval_t speed;
NpcName name;
Opt1 opt1;
diff --git a/src/map/npc-parse.cpp b/src/map/npc-parse.cpp
index 4d9fcbd..0a0d682 100644
--- a/src/map/npc-parse.cpp
+++ b/src/map/npc-parse.cpp
@@ -152,6 +152,7 @@ bool npc_load_warp(ast::npc::Warp& warp)
nd->bl_y = y;
nd->dir = DIR::S;
nd->flag = 0;
+ nd->sit = DamageType::STAND;
nd->name = warp.name.data;
nd->npc_class = WARP_CLASS;
@@ -233,6 +234,7 @@ bool npc_load_shop(ast::npc::Shop& shop)
nd->bl_id = npc_get_new_npc_id();
nd->dir = dir;
nd->flag = 0;
+ nd->sit = DamageType::STAND;
nd->name = shop.name.data;
nd->npc_class = npc_class;
nd->speed = 200_ms;
@@ -446,6 +448,7 @@ bool npc_load_script_none(ast::script::ScriptBody& body, ast::npc::ScriptNone& s
nd->bl_id = npc_get_new_npc_id();
nd->dir = DIR::S;
nd->flag = 0;
+ nd->sit = DamageType::STAND;
nd->npc_class = INVISIBLE_CLASS;
nd->speed = 200_ms;
nd->scr.script = std::move(script);
@@ -567,6 +570,7 @@ bool npc_load_script_map(ast::script::ScriptBody& body, ast::npc::ScriptMap& scr
nd->bl_id = npc_get_new_npc_id();
nd->dir = dir;
nd->flag = 0;
+ nd->sit = DamageType::STAND;
nd->npc_class = npc_class;
nd->speed = 200_ms;
nd->scr.script = std::move(script);
diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp
index 9020203..7617e0d 100644
--- a/src/map/script-fun.cpp
+++ b/src/map/script-fun.cpp
@@ -1631,6 +1631,43 @@ void builtin_setnpctimer(ScriptState *st)
npc_settimerevent_tick(nd, tick);
}
+static
+void builtin_setnpcdirection(ScriptState *st)
+{
+ dumb_ptr<npc_data> nd_;
+ DIR dir = static_cast<DIR>(conv_num(st, &AARG(0)));
+ bool save = bool(conv_num(st, &AARG(2)));
+ DamageType action;
+
+ if (HARG(3))
+ nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(3)))));
+ else
+ nd_ = map_id_is_npc(st->oid);
+
+ if (bool(conv_num(st, &AARG(1))))
+ action = DamageType::SIT;
+ else
+ action = DamageType::STAND;
+
+ if (save)
+ {
+ nd_->dir = dir;
+ nd_->sit = action;
+ }
+
+ if (st->rid)
+ {
+ dumb_ptr<map_session_data> sd = script_rid2sd(st);
+ clif_sitnpc_towards(sd, nd_, action);
+ clif_setnpcdirection_towards(sd, nd_, dir);
+ }
+ else
+ {
+ clif_sitnpc(nd_, action);
+ clif_setnpcdirection(nd_, dir);
+ }
+}
+
/*==========================================
* 天の声アナウンス
*------------------------------------------
@@ -3062,6 +3099,7 @@ BuiltinFunction builtin_functions[] =
BUILTIN(stopnpctimer, "?"_s, '\0'),
BUILTIN(getnpctimer, "i?"_s, 'i'),
BUILTIN(setnpctimer, "i?"_s, '\0'),
+ BUILTIN(setnpcdirection, "iii?"_s, '\0'),
BUILTIN(announce, "si"_s, '\0'),
BUILTIN(mapannounce, "Msi"_s, '\0'),
BUILTIN(getusers, "i"_s, 'i'),
diff --git a/src/mmo/login.t.hpp b/src/mmo/login.t.hpp
deleted file mode 100644
index f2c775a..0000000
--- a/src/mmo/login.t.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-// login.t.hpp - externally useful types from login
-//
-// 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
-{
-namespace e
-{
-enum class VERSION_2 : uint8_t
-{
- /// client supports updatehost
- UPDATEHOST = 0x01,
- /// send servers in forward order
- SERVERORDER = 0x02,
-};
-ENUM_BITWISE_OPERATORS(VERSION_2)
-}
-using e::VERSION_2;
-} // namespace tmwa
diff --git a/src/mmo/version.hpp b/src/mmo/version.hpp
index a09953f..6de3a9c 100644
--- a/src/mmo/version.hpp
+++ b/src/mmo/version.hpp
@@ -35,6 +35,9 @@ namespace tmwa
#define TMWA_SERVER_INTER 0x04
#define TMWA_SERVER_MAP 0x08
+// increase the min version when the protocol is incompatible with old m+ versions
+#define MIN_CLIENT_VERSION 1
+
// TODO now that I generate the protocol, split 'flags' out of the struct
struct Version
{
diff --git a/tools/protocol.py b/tools/protocol.py
index f13d627..86b625f 100755
--- a/tools/protocol.py
+++ b/tools/protocol.py
@@ -1456,9 +1456,6 @@ def build_context():
#md5_native = md5_h.native('md5_native')
#SaltString = md5_h.native('SaltString')
- VERSION_2 = login_th.native('VERSION_2')
-
-
Position1 = clif_th.native('Position1')
NetPosition1 = clif_th.network('NetPosition1')
Position2 = clif_th.native('Position2')
@@ -1588,8 +1585,6 @@ def build_context():
]
)
- version_2 = ctx.enum(VERSION_2, u8)
-
stats6 = ctx.struct(
'Stats6',
[
@@ -1966,7 +1961,7 @@ def build_context():
at(2, u32, 'unknown'),
at(6, account_name, 'account name'),
at(30, account_pass, 'account pass'),
- at(54, version_2, 'version 2 flags'),
+ at(54, u8, 'version'),
],
fixed_size=55,
pre=[HUMAN, 0x7531],
@@ -2249,27 +2244,30 @@ def build_context():
at(10, opt2, 'opt2'),
at(12, option, 'option'),
at(14, species, 'species'),
- at(16, u16, 'unused hair style'),
- at(18, u16, 'unused weapon'),
- at(20, u16, 'unused head bottom or species again'),
- at(22, u16, 'unused shield or part of guild emblem'),
- at(24, u16, 'unused head top or unused part of guild emblem'),
- at(26, u16, 'unused head mid or part of guild id'),
- at(28, u16, 'unused hair color or part of guild id'),
- at(30, u16, 'unused clothes color'),
- at(32, u16, 'unused 1'),
- at(34, u16, 'unused 2'),
- at(36, pos1, 'unused pos again'),
- at(39, u8, 'unused 4b'),
- at(40, u16, 'unused 5'),
- at(42, u16, 'unused zero 1'),
- at(44, u8, 'unused zero 2'),
- at(45, u8, 'unused sex'),
+ at(16, u8, 'hair style'),
+ at(17, u8, 'look'),
+ at(18, u16, 'weapon'),
+ at(20, u16, 'head bottom'),
+ at(22, u16, 'shield'),
+ at(24, u16, 'head top'),
+ at(26, u16, 'head mid'),
+ at(28, u8, 'hair color'),
+ at(29, u8, 'unused 1'),
+ at(30, u16, 'shoes or clothes color'),
+ at(32, u16, 'gloves or part of hp'),
+ at(34, u16, 'part of guild id or part of hp'),
+ at(36, u16, 'part of guild id or part of max hp'),
+ at(38, u16, 'guild emblem or part of max hp'),
+ at(40, u16, 'manner'),
+ at(42, u16, 'opt3'),
+ at(44, u8, 'karma or attack range'),
+ at(45, sex, 'sex'),
at(46, pos1, 'pos'),
- at(49, u8, 'five1'),
- at(50, u8, 'five2'),
- at(51, u8, 'zero'),
- at(52, u16, 'level'),
+ at(49, u8, 'unused 2'),
+ at(50, u8, 'unused 3'),
+ at(51, u8, 'unused 4'),
+ at(52, u8, 'unused 5'),
+ at(53, u8, 'unused 6'),
],
fixed_size=54,
pre=[BOOT, FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00b8, 0x00b9, 0x00e6, 0x00f7, 0x0143, 0x0146, 0x01d5],
@@ -2288,26 +2286,31 @@ def build_context():
at(10, opt2, 'opt2'),
at(12, option, 'option'),
at(14, species, 'mob class'),
- at(16, u16, 'unused hair style'),
+ at(16, u8, 'hair style'),
+ at(17, u8, 'look'),
at(18, u16, 'unused weapon'),
at(20, u16, 'unused head bottom'),
- at(22, tick32, 'tick and maybe part of guild emblem'),
- at(26, u16, 'unused shield or maybe part of guild emblem'),
- at(28, u16, 'unused head top or maybe part of guild id'),
- at(30, u16, 'unused head mid or maybe part of guild id'),
- at(32, u16, 'unused hair color'),
+ at(22, tick32, 'tick'),
+ at(26, u16, 'unused shield'),
+ at(28, u16, 'unused head top'),
+ at(30, u16, 'unused head mid'),
+ at(32, u8, 'unused hair color'),
+ at(33, u8, 'unused 1'),
at(34, u16, 'unused clothes color'),
- at(36, i32, 'max_hp'),
- at(40, i32, 'hp'),
- at(44, u16, 'unused 5'),
- at(46, u16, 'unused zero 1'),
- at(48, u8, 'unused zero 2'),
+ at(36, u16, 'gloves or part of hp'),
+ at(38, u16, 'part of guild id or part of hp'),
+ at(40, u16, 'part of guild id or part of max hp'),
+ at(42, u16, 'guild emblem or part of max hp'),
+ at(44, u16, 'manner'),
+ at(46, u16, 'opt3'),
+ at(48, u8, 'karma or attack range'),
at(49, u8, 'unused sex'),
at(50, pos2, 'pos2'),
- at(55, u8, 'zero'),
- at(56, u8, 'five1'),
- at(57, u8, 'five2'),
- at(58, u16, 'level'),
+ at(55, u8, 'unused 2'),
+ at(56, u8, 'unused 3'),
+ at(57, u8, 'unused 4'),
+ at(58, u8, 'unused 5'),
+ at(59, u8, 'unused 6'),
],
fixed_size=60,
pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f],