From 1a651243bb2c8e18baa9aac30ac52a62185074e7 Mon Sep 17 00:00:00 2001
From: Ben Longbons <b.r.longbons@gmail.com>
Date: Sun, 30 Mar 2014 23:14:12 -0700
Subject: Be stricter about most arrays

---
 src/admin/ladmin.cpp        |  18 ++--
 src/char/char.cpp           |  44 +++++----
 src/char/char.hpp           |   4 +-
 src/char/int_party.cpp      |   1 +
 src/char/int_storage.cpp    |   4 +-
 src/char/inter.cpp          |   6 +-
 src/generic/const_array.cpp |   3 -
 src/generic/const_array.hpp | 132 ---------------------------
 src/generic/enum.hpp        |  39 ++++++--
 src/generic/intern-pool.hpp |   1 +
 src/generic/matrix.hpp      |   6 ++
 src/io/cxxstdio.hpp         |   1 -
 src/login/login.cpp         |  34 ++++---
 src/map/atcommand.hpp       |   2 -
 src/map/battle.cpp          |  44 ++++-----
 src/map/battle.hpp          |   2 +-
 src/map/clif.cpp            |   6 +-
 src/map/clif.hpp            |   2 -
 src/map/intif.cpp           |   1 +
 src/map/intif.hpp           |   2 -
 src/map/magic-expr-eval.hpp |   4 +-
 src/map/magic-expr.cpp      | 174 +++++++++++++++++------------------
 src/map/magic-expr.hpp      |   8 +-
 src/map/magic-stmt.cpp      |  54 +++++------
 src/map/magic-v2.cpp        |   6 +-
 src/map/map.cpp             |  31 ++++---
 src/map/map.hpp             |  22 ++---
 src/map/mapflag.cpp         |  64 ++++++-------
 src/map/mob.cpp             |  30 +++---
 src/map/npc.cpp             |  18 ++--
 src/map/npc.hpp             |   8 +-
 src/map/pc.cpp              |  21 +++--
 src/map/script.cpp          |  40 ++++----
 src/map/script.hpp          |   4 +-
 src/map/skill-pools.cpp     |   2 +-
 src/map/skill.cpp           |  16 ++--
 src/map/skill.hpp           |  15 ++-
 src/map/tmw.hpp             |   2 -
 src/map/trade.cpp           |   8 +-
 src/mmo/core.cpp            |   2 +-
 src/mmo/core.hpp            |   4 +-
 src/mmo/dumb_ptr.hpp        |   2 -
 src/mmo/extract.hpp         |   2 -
 src/mmo/mmo.hpp             |  29 ++++--
 src/mmo/utils.hpp           |   1 -
 src/range/slice.cpp         |   1 +
 src/range/slice.hpp         |  74 +++++++++++++++
 src/range/slice.tcc         | 216 ++++++++++++++++++++++++++++++++++++++++++++
 src/range/slice_test.cpp    |  89 ++++++++++++++++++
 src/strings/base.hpp        |   2 +
 src/strings/pair.hpp        |   4 +-
 51 files changed, 802 insertions(+), 503 deletions(-)
 delete mode 100644 src/generic/const_array.cpp
 delete mode 100644 src/generic/const_array.hpp
 create mode 100644 src/range/slice.cpp
 create mode 100644 src/range/slice.hpp
 create mode 100644 src/range/slice.tcc
 create mode 100644 src/range/slice_test.cpp

(limited to 'src')

diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp
index f889b19..b56fd65 100644
--- a/src/admin/ladmin.cpp
+++ b/src/admin/ladmin.cpp
@@ -2840,34 +2840,36 @@ void term_func(void)
 //------------------------
 // Main function of ladmin
 //------------------------
-int do_init(int argc, ZString *argv)
+int do_init(Slice<ZString> argv)
 {
+    ZString argv0 = argv.pop_front();
     bool loaded_config_yet = false;
-    for (int i = 1; i < argc; ++i)
+    while (argv)
     {
-        if (argv[i].startswith('-'))
+        ZString argvi = argv.pop_front();
+        if (argvi.startswith('-'))
         {
-            if (argv[i] == "--help")
+            if (argvi == "--help")
             {
                 PRINTF("Usage: %s [--help] [--version] [files...]\n",
-                        argv[0]);
+                        argv0);
                 exit(0);
             }
-            else if (argv[i] == "--version")
+            else if (argvi == "--version")
             {
                 PRINTF("%s\n", CURRENT_VERSION_STRING);
                 exit(0);
             }
             else
             {
-                FPRINTF(stderr, "Unknown argument: %s\n", argv[i]);
+                FPRINTF(stderr, "Unknown argument: %s\n", argvi);
                 runflag = false;
             }
         }
         else
         {
             loaded_config_yet = true;
-            runflag &= load_config_file(argv[i], admin_confs);
+            runflag &= load_config_file(argvi, admin_confs);
         }
     }
 
diff --git a/src/char/char.cpp b/src/char/char.cpp
index b74df46..91ff2e2 100644
--- a/src/char/char.cpp
+++ b/src/char/char.cpp
@@ -46,11 +46,11 @@
 #include "../poison.hpp"
 
 static
-struct mmo_map_server server[MAX_MAP_SERVERS];
+Array<struct mmo_map_server, MAX_MAP_SERVERS> server;
 static
-Session *server_session[MAX_MAP_SERVERS];
+Array<Session *, MAX_MAP_SERVERS> server_session;
 static
-int server_freezeflag[MAX_MAP_SERVERS];    // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed
+Array<int, MAX_MAP_SERVERS> server_freezeflag;    // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed
 static
 int anti_freeze_enable = 0;
 static
@@ -313,6 +313,7 @@ AString mmo_char_tostr(struct CharPair *cp)
         }
     str_p += '\t';
 
+    assert (p->global_reg_num < GLOBAL_REG_NUM);
     for (int i = 0; i < p->global_reg_num; i++)
         if (p->global_reg[i].str)
             str_p += STRPRINTF("%s,%d ",
@@ -411,14 +412,14 @@ bool extract(XString str, CharPair *cp)
 
     if (inventory.size() > MAX_INVENTORY)
         return false;
-    std::copy(inventory.begin(), inventory.end(), p->inventory);
+    std::copy(inventory.begin(), inventory.end(), p->inventory.begin());
     // number of inventory items is not saved - it just detects nameid 0
 
     // cart was here - no longer supported
 
     for (struct skill_loader& sk : skills)
     {
-        if (sk.id > MAX_SKILL)
+        if (sk.id >= MAX_SKILL)
             return false;
         p->skill[sk.id].lv = sk.level;
         p->skill[sk.id].flags = sk.flags;
@@ -426,7 +427,7 @@ bool extract(XString str, CharPair *cp)
 
     if (vars.size() > GLOBAL_REG_NUM)
         return false;
-    std::copy(vars.begin(), vars.end(), p->global_reg);
+    std::copy(vars.begin(), vars.end(), p->global_reg.begin());
     p->global_reg_num = vars.size();
 
     return true;
@@ -964,8 +965,10 @@ int mmo_char_send006b(Session *s, struct char_session_data *sd)
 }
 
 static
-int set_account_reg2(int acc, int num, struct global_reg *reg)
+int set_account_reg2(int acc, Slice<global_reg> reg)
 {
+    size_t num = reg.size();
+    assert (num < ACCOUNT_REG2_NUM);
     int c = 0;
     for (CharPair& cd : char_keys)
     {
@@ -1309,7 +1312,7 @@ void parse_tologin(Session *ls)
                 if (RFIFOREST(ls) < 4 || RFIFOREST(ls) < RFIFOW(ls, 2))
                     return;
                 {
-                    struct global_reg reg[ACCOUNT_REG2_NUM];
+                    Array<struct global_reg, ACCOUNT_REG2_NUM> reg;
                     int j, p, acc;
                     acc = RFIFOL(ls, 4);
                     for (p = 8, j = 0;
@@ -1319,7 +1322,7 @@ void parse_tologin(Session *ls)
                         reg[j].str = stringish<VarName>(RFIFO_STRING<32>(ls, p));
                         reg[j].value = RFIFOL(ls, p + 32);
                     }
-                    set_account_reg2(acc, j, reg);
+                    set_account_reg2(acc, Slice<struct global_reg>(reg.begin(), j));
 
                     size_t len = RFIFOW(ls, 2);
                     uint8_t buf[len];
@@ -1980,7 +1983,7 @@ void parse_frommap(Session *ms)
                 if (RFIFOREST(ms) < 4 || RFIFOREST(ms) < RFIFOW(ms, 2))
                     return;
                 {
-                    struct global_reg reg[ACCOUNT_REG2_NUM];
+                    Array<struct global_reg, ACCOUNT_REG2_NUM> reg;
                     int p, j;
                     int acc = RFIFOL(ms, 4);
                     for (p = 8, j = 0;
@@ -1990,7 +1993,7 @@ void parse_frommap(Session *ms)
                         reg[j].str = stringish<VarName>(RFIFO_STRING<32>(ms, p));
                         reg[j].value = RFIFOL(ms, p + 32);
                     }
-                    set_account_reg2(acc, j, reg);
+                    set_account_reg2(acc, Slice<struct global_reg>(reg.begin(), j));
                     // loginサーバーへ送る
                     if (login_session)
                     {
@@ -2831,34 +2834,37 @@ bool char_confs(XString key, ZString value)
     return sum;
 }
 
-int do_init(int argc, ZString *argv)
+int do_init(Slice<ZString> argv)
 {
+    ZString argv0 = argv.pop_front();
+
     bool loaded_config_yet = false;
-    for (int i = 1; i < argc; ++i)
+    while (argv)
     {
-        if (argv[i].startswith('-'))
+        ZString argvi = argv.pop_front();
+        if (argvi.startswith('-'))
         {
-            if (argv[i] == "--help")
+            if (argvi == "--help")
             {
                 PRINTF("Usage: %s [--help] [--version] [files...]\n",
-                        argv[0]);
+                        argv0);
                 exit(0);
             }
-            else if (argv[i] == "--version")
+            else if (argvi == "--version")
             {
                 PRINTF("%s\n", CURRENT_VERSION_STRING);
                 exit(0);
             }
             else
             {
-                FPRINTF(stderr, "Unknown argument: %s\n", argv[i]);
+                FPRINTF(stderr, "Unknown argument: %s\n", argvi);
                 runflag = false;
             }
         }
         else
         {
             loaded_config_yet = true;
-            runflag &= load_config_file(argv[i], char_confs);
+            runflag &= load_config_file(argvi, char_confs);
         }
     }
 
diff --git a/src/char/char.hpp b/src/char/char.hpp
index 377b1c6..f765b39 100644
--- a/src/char/char.hpp
+++ b/src/char/char.hpp
@@ -3,8 +3,6 @@
 
 # include "../strings/fwd.hpp"
 
-# include "../generic/const_array.hpp"
-
 # include "../mmo/ip.hpp"
 # include "../mmo/mmo.hpp"
 
@@ -17,7 +15,7 @@ struct mmo_map_server
     IP4Address ip;
     short port;
     int users;
-    MapName maps[MAX_MAP_PER_SERVER];
+    Array<MapName, MAX_MAP_PER_SERVER> maps;
 };
 
 const CharPair *search_character(CharName character_name);
diff --git a/src/char/int_party.cpp b/src/char/int_party.cpp
index c0bedde..864972d 100644
--- a/src/char/int_party.cpp
+++ b/src/char/int_party.cpp
@@ -381,6 +381,7 @@ void mapif_party_leaved(int party_id, int account_id, CharName name)
 static
 void mapif_party_membermoved(struct party *p, int idx)
 {
+    assert (idx < MAX_PARTY);
     unsigned char buf[29];
 
     WBUFW(buf, 0) = 0x3825;
diff --git a/src/char/int_storage.cpp b/src/char/int_storage.cpp
index 0794c8f..d2782b4 100644
--- a/src/char/int_storage.cpp
+++ b/src/char/int_storage.cpp
@@ -83,8 +83,10 @@ bool extract(XString str, struct storage *p)
 
     if (storage_items.size() > MAX_STORAGE)
         return false;
-    std::copy(storage_items.begin(), storage_items.end(), p->storage_);
+    std::copy(storage_items.begin(), storage_items.end(), p->storage_.begin());
 
+    if (p->storage_amount != storage_items.size())
+        PRINTF("WARNING: storage desync for %d\n", p->account_id);
     return true;
 }
 
diff --git a/src/char/inter.cpp b/src/char/inter.cpp
index c287844..0c41c86 100644
--- a/src/char/inter.cpp
+++ b/src/char/inter.cpp
@@ -35,7 +35,7 @@ AString accreg_txt = "save/accreg.txt";
 struct accreg
 {
     int account_id, reg_num;
-    struct global_reg reg[ACCOUNT_REG_NUM];
+    Array<struct global_reg, ACCOUNT_REG_NUM> reg;
 };
 static
 Map<int, struct accreg> accreg_db;
@@ -63,6 +63,7 @@ int inter_recv_packet_length[] =
 static
 AString inter_accreg_tostr(struct accreg *reg)
 {
+    assert(reg->reg_num < ACCOUNT_REG_NUM);
     MString str;
     str += STRPRINTF("%d\t", reg->account_id);
     for (int j = 0; j < reg->reg_num; j++)
@@ -85,7 +86,7 @@ bool extract(XString str, struct accreg *reg)
 
     if (vars.size() > ACCOUNT_REG_NUM)
         return false;
-    std::copy(vars.begin(), vars.end(), reg->reg);
+    std::copy(vars.begin(), vars.end(), reg->reg.begin());
     reg->reg_num = vars.size();
     return true;
 }
@@ -263,6 +264,7 @@ void mapif_account_reg_reply(Session *s, int account_id)
     }
     else
     {
+        assert (reg->reg_num < ACCOUNT_REG_NUM);
         int j, p;
         for (j = 0, p = 8; j < reg->reg_num; j++, p += 36)
         {
diff --git a/src/generic/const_array.cpp b/src/generic/const_array.cpp
deleted file mode 100644
index 0c09333..0000000
--- a/src/generic/const_array.cpp
+++ /dev/null
@@ -1,3 +0,0 @@
-#include "const_array.hpp"
-
-#include "../poison.hpp"
diff --git a/src/generic/const_array.hpp b/src/generic/const_array.hpp
deleted file mode 100644
index 1c70f5d..0000000
--- a/src/generic/const_array.hpp
+++ /dev/null
@@ -1,132 +0,0 @@
-#ifndef TMWA_GENERIC_CONST_ARRAY_HPP
-#define TMWA_GENERIC_CONST_ARRAY_HPP
-//    const_array.hpp - just a pointer-to-const and a length
-//
-//    Copyright © 2011-2012 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 "../sanity.hpp"
-
-# include <cstring>
-
-# include <iterator>
-# include <ostream>
-# include <vector>
-
-# ifdef WORKAROUND_GCC46_COMPILER
-// constexpr is buggy with templates in this version
-// Is this still needed now that const_string is removed?
-#  define constexpr /* nothing */
-# endif
-
-// TODO see if I ever actually use this, and not the subclass
-template<class T>
-class const_array
-{
-    const T *d;
-    size_t n;
-public:
-    typedef const T *iterator;
-    typedef std::reverse_iterator<iterator> reverse_iterator;
-
-    constexpr
-    const_array(std::nullptr_t)
-    : d(nullptr), n(0)
-    {}
-
-    constexpr
-    const_array(const T *p, size_t z)
-    : d(p), n(z)
-    {}
-
-    constexpr
-    const_array(const T *b, const T *e)
-    : d(b), n(e - b)
-    {}
-
-    const_array(std::initializer_list<T> list)
-    : d(list.begin()), n(list.size())
-    {}
-
-    // Implicit conversion from std::vector
-    const_array(const std::vector<T>& v)
-    : d(v.data()), n(v.size())
-    {}
-
-    // but disallow conversion from a temporary
-    const_array(std::vector<T>&&) = delete;
-
-    // Oops. see src/warnings.hpp
-    constexpr
-    const T *data() const { return d; }
-    constexpr
-    size_t size() const { return n; }
-    constexpr
-    bool empty() const { return not n; }
-    constexpr explicit
-    operator bool() const { return n; }
-
-    constexpr
-    std::pair<const_array, const_array> cut(size_t o) const
-    {
-        return {const_array(d, o), const_array(d + o, n - o)};
-    }
-
-    constexpr
-    const_array first(size_t o) const
-    {
-        return cut(o).first;
-    }
-
-    constexpr
-    const_array last(size_t l) const
-    {
-        return cut(size() - l).second;
-    }
-
-    constexpr
-    const_array after(size_t o) const
-    {
-        return cut(o).second;
-    }
-
-    constexpr
-    iterator begin() const { return d; }
-    constexpr
-    iterator end() const { return d + n; }
-    constexpr
-    reverse_iterator rbegin() const { return reverse_iterator(end()); }
-    constexpr
-    reverse_iterator rend() const { return reverse_iterator(begin()); }
-
-    constexpr
-    const T& front() const { return *begin(); }
-    constexpr
-    const T& back() const { return *rbegin(); }
-
-    // This probably shouldn't be used, but I'm adding it for porting.
-    T& operator[](size_t i)
-    {
-        return const_cast<T&>(d[i]);
-    }
-};
-
-# ifdef WORKAROUND_GCC46_COMPILER
-#  undef constexpr
-# endif
-
-#endif // TMWA_GENERIC_CONST_ARRAY_HPP
diff --git a/src/generic/enum.hpp b/src/generic/enum.hpp
index 6f29981..29e5c36 100644
--- a/src/generic/enum.hpp
+++ b/src/generic/enum.hpp
@@ -3,6 +3,8 @@
 
 # include "../sanity.hpp"
 
+# include <cassert>
+
 # include <type_traits>
 
 # include "../compat/iter.hpp"
@@ -10,17 +12,27 @@
 template<class T, class E, E max>
 struct earray
 {
+    constexpr static
+    size_t size()
+    {
+        return static_cast<size_t>(max);
+    }
+
     // no ctor/dtor and one public member variable for easy initialization
-    T _data[size_t(max)];
+    T _data[size()];
 
     T& operator[](E v)
     {
-        return _data[size_t(v)];
+        auto i = static_cast<size_t>(v);
+        assert (i < size());
+        return _data[i];
     }
 
     const T& operator[](E v) const
     {
-        return _data[size_t(v)];
+        auto i = static_cast<size_t>(v);
+        assert (i < size());
+        return _data[i];
     }
 
     T *begin()
@@ -30,7 +42,7 @@ struct earray
 
     T *end()
     {
-        return _data + size_t(max);
+        return _data + size();
     }
 
     const T *begin() const
@@ -40,7 +52,7 @@ struct earray
 
     const T *end() const
     {
-        return _data + size_t(max);
+        return _data + size();
     }
 
     friend bool operator == (const earray& l, const earray& r)
@@ -54,23 +66,30 @@ struct earray
     }
 };
 
-template<class T, class E>
+template<class T, class E, E max>
 class eptr
 {
+    constexpr static
+    size_t size()
+    {
+        return static_cast<size_t>(max);
+    }
+
     T *_data;
 public:
     eptr(std::nullptr_t=nullptr)
     : _data(nullptr)
     {}
 
-    template<E max>
     eptr(earray<T, E, max>& arr)
     : _data(arr._data)
     {}
 
-    T& operator [](E v)
+    T& operator [](E v) const
     {
-        return _data[size_t(v)];
+        auto i = static_cast<size_t>(v);
+        assert (i < size());
+        return _data[i];
     }
 
     explicit operator bool()
@@ -109,6 +128,8 @@ struct remove_enum<E, true>
 };
 
 
+// This really should just go in a namespace
+// that's how I use it anyway ...
 # define ENUM_BITWISE_OPERATORS(E)      \
 inline                                  \
 E operator & (E l, E r)                 \
diff --git a/src/generic/intern-pool.hpp b/src/generic/intern-pool.hpp
index 69f20ef..f9c1e8f 100644
--- a/src/generic/intern-pool.hpp
+++ b/src/generic/intern-pool.hpp
@@ -29,6 +29,7 @@ public:
 
     ZString outtern(size_t sz) const
     {
+        assert (sz < names.size());
         return names[sz];
     }
 
diff --git a/src/generic/matrix.hpp b/src/generic/matrix.hpp
index 3530ba7..b337249 100644
--- a/src/generic/matrix.hpp
+++ b/src/generic/matrix.hpp
@@ -3,6 +3,8 @@
 
 # include "../sanity.hpp"
 
+# include <cassert>
+
 # include "../compat/memory.hpp"
 
 template<class T>
@@ -34,10 +36,14 @@ public:
 
     T& ref(size_t x, size_t y)
     {
+        assert (x < _xs);
+        assert (y < _ys);
         return _data[x + y * _xs];
     }
     const T& ref(size_t x, size_t y) const
     {
+        assert (x < _xs);
+        assert (y < _ys);
         return _data[x + y * _xs];
     }
 
diff --git a/src/io/cxxstdio.hpp b/src/io/cxxstdio.hpp
index 9edeebb..66419df 100644
--- a/src/io/cxxstdio.hpp
+++ b/src/io/cxxstdio.hpp
@@ -26,7 +26,6 @@
 
 # include "../compat/cast.hpp"
 
-# include "../generic/const_array.hpp"
 # include "../generic/enum.hpp"
 
 # include "fwd.hpp"
diff --git a/src/login/login.cpp b/src/login/login.cpp
index c9b285f..ddc3d89 100644
--- a/src/login/login.cpp
+++ b/src/login/login.cpp
@@ -114,11 +114,11 @@ static
 int display_parse_fromchar = 0;    // 0: no, 1: yes (without packet 0x2714), 2: all packets
 
 static
-struct mmo_char_server server[MAX_SERVERS];
+Array<struct mmo_char_server, MAX_SERVERS> server;
 static
-Session *server_session[MAX_SERVERS];
+Array<Session *, MAX_SERVERS> server_session;
 static
-int server_freezeflag[MAX_SERVERS];    // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed
+Array<int, MAX_SERVERS> server_freezeflag;    // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed
 static
 int anti_freeze_enable = 0;
 static
@@ -158,13 +158,14 @@ void SessionDeleter::operator()(SessionData *)
 #pragma GCC diagnostic pop
 
 constexpr int AUTH_FIFO_SIZE = 256;
-struct
+struct AuthFifo
 {
     int account_id, login_id1, login_id2;
     IP4Address ip;
     SEX sex;
     int delflag;
-} auth_fifo[AUTH_FIFO_SIZE];
+};
+Array<AuthFifo, AUTH_FIFO_SIZE> auth_fifo;
 static
 int auth_fifo_pos = 0;
 
@@ -184,7 +185,7 @@ struct AuthData
     IP4Address last_ip;           // save of last IP of connection
     VString<254> memo;             // a memo field
     int account_reg2_num;
-    struct global_reg account_reg2[ACCOUNT_REG2_NUM];
+    Array<struct global_reg, ACCOUNT_REG2_NUM> account_reg2;
 };
 static
 std::vector<AuthData> auth_data;
@@ -447,6 +448,7 @@ AString mmo_auth_tostr(const AuthData *p)
             p->memo,
             p->ban_until_time);
 
+    assert (p->account_reg2_num < ACCOUNT_REG2_NUM);
     for (int i = 0; i < p->account_reg2_num; i++)
         if (p->account_reg2[i].str)
             str += STRPRINTF("%s,%d ",
@@ -522,7 +524,7 @@ bool extract(XString line, AuthData *ad)
 
     if (vars.size() > ACCOUNT_REG2_NUM)
         return false;
-    std::copy(vars.begin(), vars.end(), ad->account_reg2);
+    std::copy(vars.begin(), vars.end(), ad->account_reg2.begin());
     ad->account_reg2_num = vars.size();
 
     return true;
@@ -3827,34 +3829,36 @@ bool login_confs(XString key, ZString value)
 //------------------------------
 // Main function of login-server
 //------------------------------
-int do_init(int argc, ZString *argv)
+int do_init(Slice<ZString> argv)
 {
+    ZString argv0 = argv.pop_front();
     bool loaded_config_yet = false;
-    for (int i = 1; i < argc; ++i)
+    while (argv)
     {
-        if (argv[i].startswith('-'))
+        ZString argvi = argv.pop_front();
+        if (argvi.startswith('-'))
         {
-            if (argv[i] == "--help")
+            if (argvi == "--help")
             {
                 PRINTF("Usage: %s [--help] [--version] [files...]\n",
-                        argv[0]);
+                        argv0);
                 exit(0);
             }
-            else if (argv[i] == "--version")
+            else if (argvi == "--version")
             {
                 PRINTF("%s\n", CURRENT_VERSION_STRING);
                 exit(0);
             }
             else
             {
-                FPRINTF(stderr, "Unknown argument: %s\n", argv[i]);
+                FPRINTF(stderr, "Unknown argument: %s\n", argvi);
                 runflag = false;
             }
         }
         else
         {
             loaded_config_yet = true;
-            runflag &= load_config_file(argv[i], login_confs);
+            runflag &= load_config_file(argvi, login_confs);
         }
     }
 
diff --git a/src/map/atcommand.hpp b/src/map/atcommand.hpp
index 16d1efc..45759a6 100644
--- a/src/map/atcommand.hpp
+++ b/src/map/atcommand.hpp
@@ -3,8 +3,6 @@
 
 # include "../strings/fwd.hpp"
 
-# include "../generic/const_array.hpp"
-
 # include "map.hpp"
 
 bool is_atcommand(Session *s, dumb_ptr<map_session_data> sd,
diff --git a/src/map/battle.cpp b/src/map/battle.cpp
index 7e563b8..49a0538 100644
--- a/src/map/battle.cpp
+++ b/src/map/battle.cpp
@@ -166,7 +166,7 @@ int battle_get_max_hp(dumb_ptr<block_list> bl)
 int battle_get_str(dumb_ptr<block_list> bl)
 {
     int str = 0;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(bl);
     sc_data = battle_get_sc_data(bl);
@@ -189,7 +189,7 @@ int battle_get_str(dumb_ptr<block_list> bl)
 int battle_get_agi(dumb_ptr<block_list> bl)
 {
     int agi = 0;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(bl);
     sc_data = battle_get_sc_data(bl);
@@ -211,7 +211,7 @@ int battle_get_agi(dumb_ptr<block_list> bl)
 int battle_get_vit(dumb_ptr<block_list> bl)
 {
     int vit = 0;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(bl);
     sc_data = battle_get_sc_data(bl);
@@ -233,7 +233,7 @@ int battle_get_vit(dumb_ptr<block_list> bl)
 int battle_get_int(dumb_ptr<block_list> bl)
 {
     int int_ = 0;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(bl);
     sc_data = battle_get_sc_data(bl);
@@ -255,7 +255,7 @@ int battle_get_int(dumb_ptr<block_list> bl)
 int battle_get_dex(dumb_ptr<block_list> bl)
 {
     int dex = 0;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(bl);
     sc_data = battle_get_sc_data(bl);
@@ -277,7 +277,7 @@ int battle_get_dex(dumb_ptr<block_list> bl)
 int battle_get_luk(dumb_ptr<block_list> bl)
 {
     int luk = 0;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(bl);
     sc_data = battle_get_sc_data(bl);
@@ -300,7 +300,7 @@ static
 int battle_get_flee(dumb_ptr<block_list> bl)
 {
     int flee = 1;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_retr(1, bl);
     sc_data = battle_get_sc_data(bl);
@@ -329,7 +329,7 @@ static
 int battle_get_hit(dumb_ptr<block_list> bl)
 {
     int hit = 1;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_retr(1, bl);
     sc_data = battle_get_sc_data(bl);
@@ -357,7 +357,7 @@ static
 int battle_get_flee2(dumb_ptr<block_list> bl)
 {
     int flee2 = 1;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_retr(1, bl);
     sc_data = battle_get_sc_data(bl);
@@ -389,7 +389,7 @@ static
 int battle_get_critical(dumb_ptr<block_list> bl)
 {
     int critical = 1;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_retr(1, bl);
     sc_data = battle_get_sc_data(bl);
@@ -415,7 +415,7 @@ int battle_get_critical(dumb_ptr<block_list> bl)
 static
 int battle_get_baseatk(dumb_ptr<block_list> bl)
 {
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     int batk = 1;
 
     nullpo_retr(1, bl);
@@ -442,7 +442,7 @@ int battle_get_baseatk(dumb_ptr<block_list> bl)
 static
 int battle_get_atk(dumb_ptr<block_list> bl)
 {
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     int atk = 0;
 
     nullpo_ret(bl);
@@ -518,7 +518,7 @@ int battle_get_atk_2(dumb_ptr<block_list> bl)
 static
 int battle_get_matk1(dumb_ptr<block_list> bl)
 {
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     nullpo_ret(bl);
     sc_data = battle_get_sc_data(bl);
     if (bl->bl_type == BL::MOB)
@@ -563,7 +563,7 @@ int battle_get_matk2(dumb_ptr<block_list> bl)
  */
 int battle_get_def(dumb_ptr<block_list> bl)
 {
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     int def = 0;
 
     nullpo_ret(bl);
@@ -599,7 +599,7 @@ int battle_get_def(dumb_ptr<block_list> bl)
  */
 int battle_get_mdef(dumb_ptr<block_list> bl)
 {
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     int mdef = 0;
 
     nullpo_ret(bl);
@@ -634,7 +634,7 @@ int battle_get_mdef(dumb_ptr<block_list> bl)
  */
 int battle_get_def2(dumb_ptr<block_list> bl)
 {
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     int def2 = 1;
 
     nullpo_retr(1, bl);
@@ -715,7 +715,7 @@ interval_t battle_get_adelay(dumb_ptr<block_list> bl)
         return bl->is_player()->aspd * 2;
     else
     {
-        eptr<struct status_change, StatusChange> sc_data = battle_get_sc_data(bl);
+        eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data = battle_get_sc_data(bl);
         interval_t adelay = std::chrono::seconds(4);
         int aspd_rate = 100;
         if (bl->bl_type == BL::MOB)
@@ -743,7 +743,7 @@ interval_t battle_get_amotion(dumb_ptr<block_list> bl)
         return bl->is_player()->amotion;
     else
     {
-        eptr<struct status_change, StatusChange> sc_data = battle_get_sc_data(bl);
+        eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data = battle_get_sc_data(bl);
         interval_t amotion = std::chrono::seconds(2);
         int aspd_rate = 100;
         if (bl->bl_type == BL::MOB)
@@ -846,7 +846,7 @@ int battle_get_stat(SP stat_id, dumb_ptr<block_list> bl)
 }
 
 // StatusChange系の所得
-eptr<struct status_change, StatusChange> battle_get_sc_data(dumb_ptr<block_list> bl)
+eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> battle_get_sc_data(dumb_ptr<block_list> bl)
 {
     nullpo_retr(nullptr, bl);
 
@@ -1064,7 +1064,7 @@ struct Damage battle_calc_mob_weapon_attack(dumb_ptr<block_list> src,
     BF flag;
     int ac_flag = 0;
     ATK dmg_lv = ATK::ZERO;
-    eptr<struct status_change, StatusChange> sc_data, t_sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data, t_sc_data;
 
     nullpo_retr(wd, src);
     nullpo_retr(wd, target);
@@ -1337,7 +1337,7 @@ struct Damage battle_calc_pc_weapon_attack(dumb_ptr<block_list> src,
     int div_;
     BF flag;
     ATK dmg_lv = ATK::ZERO;
-    eptr<struct status_change, StatusChange> sc_data, t_sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data, t_sc_data;
     int atkmax_ = 0, atkmin_ = 0;  //二刀流用
     int watk, watk_;
     bool da = false;
@@ -1959,7 +1959,7 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
         tick_t tick)
 {
     dumb_ptr<map_session_data> sd = NULL;
-    eptr<struct status_change, StatusChange> t_sc_data = battle_get_sc_data(target);
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> t_sc_data = battle_get_sc_data(target);
     struct Damage wd;
 
     nullpo_retr(ATK::ZERO, src);
diff --git a/src/map/battle.hpp b/src/map/battle.hpp
index fd452e8..5c62785 100644
--- a/src/map/battle.hpp
+++ b/src/map/battle.hpp
@@ -78,7 +78,7 @@ Race battle_get_race(dumb_ptr<block_list> bl);
 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> battle_get_sc_data(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);
diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index a5b02cd..3290013 100644
--- a/src/map/clif.cpp
+++ b/src/map/clif.cpp
@@ -2021,7 +2021,7 @@ int clif_misceffect(dumb_ptr<block_list> bl, int type)
 int clif_changeoption(dumb_ptr<block_list> bl)
 {
     uint8_t buf[32];
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(bl);
 
@@ -2435,7 +2435,7 @@ int clif_damage(dumb_ptr<block_list> src, dumb_ptr<block_list> dst,
         int div, DamageType type, int damage2)
 {
     unsigned char buf[256];
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(src);
     nullpo_ret(dst);
@@ -2800,7 +2800,7 @@ int clif_skill_damage(dumb_ptr<block_list> src, dumb_ptr<block_list> dst,
         int div, SkillID skill_id, int skill_lv, int type)
 {
     unsigned char buf[64];
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(src);
     nullpo_ret(dst);
diff --git a/src/map/clif.hpp b/src/map/clif.hpp
index c346f7d..571f68e 100644
--- a/src/map/clif.hpp
+++ b/src/map/clif.hpp
@@ -7,8 +7,6 @@
 
 # include "../strings/fwd.hpp"
 
-# include "../generic/const_array.hpp"
-
 # include "../mmo/ip.hpp"
 # include "../mmo/timer.t.hpp"
 
diff --git a/src/map/intif.cpp b/src/map/intif.cpp
index 7f7be73..b2b95fe 100644
--- a/src/map/intif.cpp
+++ b/src/map/intif.cpp
@@ -104,6 +104,7 @@ void intif_saveaccountreg(dumb_ptr<map_session_data> sd)
     int j, p;
 
     nullpo_retv(sd);
+    assert (sd->status.account_reg_num < ACCOUNT_REG_NUM);
 
     WFIFOW(char_session, 0) = 0x3004;
     WFIFOL(char_session, 4) = sd->bl_id;
diff --git a/src/map/intif.hpp b/src/map/intif.hpp
index ac2740c..7028ed9 100644
--- a/src/map/intif.hpp
+++ b/src/map/intif.hpp
@@ -3,8 +3,6 @@
 
 # include "../strings/fwd.hpp"
 
-# include "../generic/const_array.hpp"
-
 # include "map.hpp"
 
 int intif_parse(Session *);
diff --git a/src/map/magic-expr-eval.hpp b/src/map/magic-expr-eval.hpp
index 0783dc8..4de2f81 100644
--- a/src/map/magic-expr-eval.hpp
+++ b/src/map/magic-expr-eval.hpp
@@ -1,6 +1,8 @@
 #ifndef TMWA_MAP_MAGIC_EXPR_EVAL_HPP
 #define TMWA_MAP_MAGIC_EXPR_EVAL_HPP
 
+# include "../range/slice.hpp"
+
 # include "../strings/zstring.hpp"
 
 # include "magic-interpreter.hpp"
@@ -8,7 +10,7 @@
 /* Helper definitions for dealing with functions and operations */
 
 int magic_signature_check(ZString opname, ZString funname, ZString signature,
-        int args_nr, val_t *args, int line, int column);
+        Slice<val_t> args, int line, int column);
 
 void magic_area_rect(map_local **m, int *x, int *y, int *width, int *height,
         area_t& area);
diff --git a/src/map/magic-expr.cpp b/src/map/magic-expr.cpp
index 1be1b0b..1881afc 100644
--- a/src/map/magic-expr.cpp
+++ b/src/map/magic-expr.cpp
@@ -261,7 +261,7 @@ void make_spell(val_t *v)
 }
 
 static
-int fun_add(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_add(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ARG_TYPE(0) == TYPE::INT && ARG_TYPE(1) == TYPE::INT)
     {
@@ -296,21 +296,21 @@ int fun_add(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_sub(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_sub(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGINT(0) - ARGINT(1);
     return 0;
 }
 
 static
-int fun_mul(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_mul(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGINT(0) * ARGINT(1);
     return 0;
 }
 
 static
-int fun_div(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_div(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (!ARGINT(1))
         return 1;               /* division by zero */
@@ -319,7 +319,7 @@ int fun_div(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_mod(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_mod(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (!ARGINT(1))
         return 1;               /* division by zero */
@@ -328,35 +328,35 @@ int fun_mod(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_or(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_or(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGINT(0) || ARGINT(1);
     return 0;
 }
 
 static
-int fun_and(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_and(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGINT(0) && ARGINT(1);
     return 0;
 }
 
 static
-int fun_not(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_not(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = !ARGINT(0);
     return 0;
 }
 
 static
-int fun_neg(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_neg(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ~ARGINT(0);
     return 0;
 }
 
 static
-int fun_gte(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_gte(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ARG_TYPE(0) == TYPE::STRING || ARG_TYPE(1) == TYPE::STRING)
     {
@@ -374,7 +374,7 @@ int fun_gte(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_lt(dumb_ptr<env_t> env, val_t *result, const_array<val_t> args)
+int fun_lt(dumb_ptr<env_t> env, val_t *result, Slice<val_t> args)
 {
     fun_gte(env, result, args);
     RESULTINT = !RESULTINT;
@@ -382,7 +382,7 @@ int fun_lt(dumb_ptr<env_t> env, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_gt(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_gt(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ARG_TYPE(0) == TYPE::STRING || ARG_TYPE(1) == TYPE::STRING)
     {
@@ -400,7 +400,7 @@ int fun_gt(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_lte(dumb_ptr<env_t> env, val_t *result, const_array<val_t> args)
+int fun_lte(dumb_ptr<env_t> env, val_t *result, Slice<val_t> args)
 {
     fun_gt(env, result, args);
     RESULTINT = !RESULTINT;
@@ -408,7 +408,7 @@ int fun_lte(dumb_ptr<env_t> env, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_eq(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_eq(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ARG_TYPE(0) == TYPE::STRING || ARG_TYPE(1) == TYPE::STRING)
     {
@@ -440,7 +440,7 @@ int fun_eq(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_ne(dumb_ptr<env_t> env, val_t *result, const_array<val_t> args)
+int fun_ne(dumb_ptr<env_t> env, val_t *result, Slice<val_t> args)
 {
     fun_eq(env, result, args);
     RESULTINT = !RESULTINT;
@@ -448,56 +448,56 @@ int fun_ne(dumb_ptr<env_t> env, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_bitand(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_bitand(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGINT(0) & ARGINT(1);
     return 0;
 }
 
 static
-int fun_bitor(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_bitor(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGINT(0) | ARGINT(1);
     return 0;
 }
 
 static
-int fun_bitxor(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_bitxor(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGINT(0) ^ ARGINT(1);
     return 0;
 }
 
 static
-int fun_bitshl(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_bitshl(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGINT(0) << ARGINT(1);
     return 0;
 }
 
 static
-int fun_bitshr(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_bitshr(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGINT(0) >> ARGINT(1);
     return 0;
 }
 
 static
-int fun_max(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_max(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = max(ARGINT(0), ARGINT(1));
     return 0;
 }
 
 static
-int fun_min(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_min(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = min(ARGINT(0), ARGINT(1));
     return 0;
 }
 
 static
-int fun_if_then_else(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_if_then_else(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ARGINT(0))
         magic_copy_var(result, &args[1]);
@@ -606,7 +606,7 @@ int magic_location_in_area(map_local *m, int x, int y, dumb_ptr<area_t> area)
 }
 
 static
-int fun_is_in(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_is_in(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = magic_location_in_area(ARGLOCATION(0).m,
                                         ARGLOCATION(0).x,
@@ -615,7 +615,7 @@ int fun_is_in(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_skill(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_skill(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ENTITY_TYPE(0) != BL::PC
         // don't convert to enum until after the range check
@@ -633,18 +633,18 @@ int fun_skill(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_his_shroud(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_his_shroud(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = (ENTITY_TYPE(0) == BL::PC && ARGPC(0)->state.shroud_active);
     return 0;
 }
 
-#define BATTLE_GETTER(name)                                                 \
-static                                                                      \
-int fun_get_##name(dumb_ptr<env_t>, val_t *result, const_array<val_t> args) \
-{                                                                           \
-    RESULTINT = battle_get_##name(ARGENTITY(0));                            \
-    return 0;                                                               \
+#define BATTLE_GETTER(name)                                             \
+static                                                                  \
+int fun_get_##name(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)   \
+{                                                                       \
+    RESULTINT = battle_get_##name(ARGENTITY(0));                        \
+    return 0;                                                           \
 }
 
 BATTLE_GETTER(str)
@@ -659,28 +659,28 @@ BATTLE_GETTER(mdef)
 BATTLE_GETTER(def)
 BATTLE_GETTER(max_hp)
 static
-int fun_get_dir(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_get_dir(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTDIR = battle_get_dir(ARGENTITY(0));
     return 0;
 }
 
-#define MMO_GETTER(name)                                                    \
-static                                                                      \
-int fun_get_##name(dumb_ptr<env_t>, val_t *result, const_array<val_t> args) \
-{                                                                           \
-    if (ENTITY_TYPE(0) == BL::PC)                                           \
-        RESULTINT = ARGPC(0)->status.name;                                  \
-    else                                                                    \
-        RESULTINT = 0;                                                      \
-    return 0;                                                               \
+#define MMO_GETTER(name)                                                \
+static                                                                  \
+int fun_get_##name(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)   \
+{                                                                       \
+    if (ENTITY_TYPE(0) == BL::PC)                                       \
+        RESULTINT = ARGPC(0)->status.name;                              \
+    else                                                                \
+        RESULTINT = 0;                                                  \
+    return 0;                                                           \
 }
 
 MMO_GETTER(sp)
 MMO_GETTER(max_sp)
 
 static
-int fun_name_of(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_name_of(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ARG_TYPE(0) == TYPE::ENTITY)
     {
@@ -702,7 +702,7 @@ int fun_name_of(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 
 /* [Freeyorp] I'm putting this one in as name_of seems to have issues with summoned or spawned mobs. */
 static
-int fun_mob_id(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_mob_id(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ENTITY_TYPE(0) != BL::MOB)
         return 1;
@@ -727,14 +727,14 @@ void COPY_LOCATION(location_t& dest, block_list& src)
 }
 
 static
-int fun_location(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_location(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     COPY_LOCATION(RESULTLOCATION, *(ARGENTITY(0)));
     return 0;
 }
 
 static
-int fun_random(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_random(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     int delta = ARGINT(0);
     if (delta < 0)
@@ -752,7 +752,7 @@ int fun_random(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_random_dir(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_random_dir(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ARGINT(0))
         RESULTDIR = random_::choice({DIR::S, DIR::SW, DIR::W, DIR::NW, DIR::N, DIR::NE, DIR::E, DIR::SE});
@@ -762,14 +762,14 @@ int fun_random_dir(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_hash_entity(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_hash_entity(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARGENTITY(0)->bl_id;
     return 0;
 }
 
 int                            // ret -1: not a string, ret 1: no such item, ret 0: OK
-magic_find_item(const_array<val_t> args, int index, struct item *item_, int *stackable)
+magic_find_item(Slice<val_t> args, int index, struct item *item_, int *stackable)
 {
     struct item_data *item_data;
     int must_add_sequentially;
@@ -801,7 +801,7 @@ magic_find_item(const_array<val_t> args, int index, struct item *item_, int *sta
 }
 
 static
-int fun_count_item(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_count_item(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> chr = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : NULL;
     int stackable;
@@ -817,7 +817,7 @@ int fun_count_item(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_is_equipped(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_is_equipped(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> chr = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : NULL;
     int stackable;
@@ -843,28 +843,28 @@ int fun_is_equipped(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_is_married(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_is_married(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = (ENTITY_TYPE(0) == BL::PC && ARGPC(0)->status.partner_id);
     return 0;
 }
 
 static
-int fun_is_dead(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_is_dead(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = (ENTITY_TYPE(0) == BL::PC && pc_isdead(ARGPC(0)));
     return 0;
 }
 
 static
-int fun_is_pc(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_is_pc(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = (ENTITY_TYPE(0) == BL::PC);
     return 0;
 }
 
 static
-int fun_partner(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_partner(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ENTITY_TYPE(0) == BL::PC && ARGPC(0)->status.partner_id)
     {
@@ -877,7 +877,7 @@ int fun_partner(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_awayfrom(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_awayfrom(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     location_t *loc = &ARGLOCATION(0);
     int dx = dirx[ARGDIR(1)];
@@ -896,14 +896,14 @@ int fun_awayfrom(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_failed(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_failed(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = ARG_TYPE(0) == TYPE::FAIL;
     return 0;
 }
 
 static
-int fun_npc(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_npc(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     NpcName name = stringish<NpcName>(ARGSTR(0));
     RESULTENTITY = npc_name2id(name);
@@ -911,7 +911,7 @@ int fun_npc(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_pc(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_pc(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     CharName name = stringish<CharName>(ARGSTR(0));
     RESULTENTITY = map_nick2sd(name);
@@ -919,7 +919,7 @@ int fun_pc(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_distance(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_distance(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ARGLOCATION(0).m != ARGLOCATION(1).m)
         RESULTINT = 0x7fffffff;
@@ -930,7 +930,7 @@ int fun_distance(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_rdistance(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_rdistance(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ARGLOCATION(0).m != ARGLOCATION(1).m)
         RESULTINT = 0x7fffffff;
@@ -944,7 +944,7 @@ int fun_rdistance(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_anchor(dumb_ptr<env_t> env, val_t *result, const_array<val_t> args)
+int fun_anchor(dumb_ptr<env_t> env, val_t *result, Slice<val_t> args)
 {
     dumb_ptr<teleport_anchor_t> anchor = magic_find_anchor(ARGSTR(0));
 
@@ -964,7 +964,7 @@ int fun_anchor(dumb_ptr<env_t> env, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_line_of_sight(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_line_of_sight(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     block_list e1, e2;
 
@@ -1020,14 +1020,14 @@ void magic_random_location(location_t *dest, dumb_ptr<area_t> area)
 }
 
 static
-int fun_pick_location(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_pick_location(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     magic_random_location(&result->v.v_location, ARGAREA(0));
     return 0;
 }
 
 static
-int fun_read_script_int(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_read_script_int(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     dumb_ptr<block_list> subject_p = ARGENTITY(0);
     VarName var_name = stringish<VarName>(ARGSTR(1));
@@ -1041,7 +1041,7 @@ int fun_read_script_int(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_read_script_str(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_read_script_str(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     dumb_ptr<block_list> subject_p = ARGENTITY(0);
     VarName var_name = stringish<VarName>(ARGSTR(1));
@@ -1055,7 +1055,7 @@ int fun_read_script_str(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_rbox(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_rbox(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     location_t loc = ARGLOCATION(0);
     int radius = ARGINT(1);
@@ -1071,7 +1071,7 @@ int fun_rbox(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_running_status_update(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_running_status_update(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     if (ENTITY_TYPE(0) != BL::PC && ENTITY_TYPE(0) != BL::MOB)
         return 1;
@@ -1082,28 +1082,28 @@ int fun_running_status_update(dumb_ptr<env_t>, val_t *result, const_array<val_t>
 }
 
 static
-int fun_status_option(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_status_option(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = (bool((ARGPC(0))->status.option & static_cast<Option>(ARGINT(1))));
     return 0;
 }
 
 static
-int fun_element(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_element(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = static_cast<int>(battle_get_element(ARGENTITY(0)).element);
     return 0;
 }
 
 static
-int fun_element_level(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_element_level(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = battle_get_element(ARGENTITY(0)).level;
     return 0;
 }
 
 static
-int fun_is_exterior(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_is_exterior(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
 #warning "Evil assumptions!"
     RESULTINT = ARGLOCATION(0).m->name_[4] == '1';
@@ -1111,14 +1111,14 @@ int fun_is_exterior(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_contains_string(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_contains_string(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = NULL != strstr(ARGSTR(0).c_str(), ARGSTR(1).c_str());
     return 0;
 }
 
 static
-int fun_strstr(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_strstr(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     const char *offset = strstr(ARGSTR(0).c_str(), ARGSTR(1).c_str());
     RESULTINT = offset - ARGSTR(0).c_str();
@@ -1126,14 +1126,14 @@ int fun_strstr(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_strlen(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_strlen(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = strlen(ARGSTR(0).c_str());
     return 0;
 }
 
 static
-int fun_substr(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_substr(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     const char *src = ARGSTR(0).c_str();
     const int slen = strlen(src);
@@ -1159,14 +1159,14 @@ int fun_substr(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_sqrt(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_sqrt(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     RESULTINT = static_cast<int>(sqrt(ARGINT(0)));
     return 0;
 }
 
 static
-int fun_map_level(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_map_level(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
 #warning "Evil assumptions!"
     RESULTINT = ARGLOCATION(0).m->name_[4] - '0';
@@ -1174,7 +1174,7 @@ int fun_map_level(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_map_nr(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_map_nr(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
 #warning "Evil assumptions!"
     MapName mapname = ARGLOCATION(0).m->name_;
@@ -1185,7 +1185,7 @@ int fun_map_nr(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_dir_towards(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_dir_towards(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     int dx;
     int dy;
@@ -1251,7 +1251,7 @@ int fun_dir_towards(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
 }
 
 static
-int fun_extract_healer_xp(dumb_ptr<env_t>, val_t *result, const_array<val_t> args)
+int fun_extract_healer_xp(dumb_ptr<env_t>, val_t *result, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> sd = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : NULL;
 
@@ -1526,10 +1526,10 @@ TYPE type_key(char ty_key)
 }
 
 int magic_signature_check(ZString opname, ZString funname, ZString signature,
-        int args_nr, val_t *args, int line, int column)
+        Slice<val_t> args, int line, int column)
 {
     int i;
-    for (i = 0; i < args_nr; i++)
+    for (i = 0; i < args.size(); i++)
     {
         val_t *arg = &args[i];
         char ty_key = signature[i];
@@ -1553,8 +1553,8 @@ int magic_signature_check(ZString opname, ZString funname, ZString signature,
         if (!ty_key)
         {
             FPRINTF(stderr,
-                     "[magic-eval]:  L%d:%d: Too many arguments (%d) to %s `%s'\n",
-                     line, column, args_nr, opname, funname);
+                     "[magic-eval]:  L%d:%d: Too many arguments (%zu) to %s `%s'\n",
+                     line, column, args.size(), opname, funname);
             return 1;
         }
 
@@ -1640,9 +1640,9 @@ void magic_eval(dumb_ptr<env_t> env, val_t *dest, dumb_ptr<expr_t> expr)
 
             for (i = 0; i < args_nr; ++i)
                 magic_eval(env, &arguments[i], expr->e.e_funapp.args[i]);
-            if (magic_signature_check("function", f->name, f->signature, args_nr, arguments,
+            if (magic_signature_check("function", f->name, f->signature, Slice<val_t>(arguments, args_nr),
                         expr->e.e_funapp.line_nr, expr->e.e_funapp.column)
-                    || f->fun(env, dest, const_array<val_t>(arguments, args_nr)))
+                    || f->fun(env, dest, Slice<val_t>(arguments, args_nr)))
                 dest->ty = TYPE::FAIL;
             else
             {
diff --git a/src/map/magic-expr.hpp b/src/map/magic-expr.hpp
index 79fdc3f..b2b0dd2 100644
--- a/src/map/magic-expr.hpp
+++ b/src/map/magic-expr.hpp
@@ -3,6 +3,8 @@
 
 # include "magic-interpreter.hpp"
 
+# include "../range/slice.hpp"
+
 # include "../strings/fwd.hpp"
 # include "../strings/zstring.hpp"
 
@@ -24,14 +26,14 @@ struct fun_t
     ZString name;
     ZString signature;
     char ret_ty;
-    int (*fun)(dumb_ptr<env_t> env, val_t *result, const_array<val_t> arga);
+    int (*fun)(dumb_ptr<env_t> env, val_t *result, Slice<val_t> arga);
 };
 
 struct op_t
 {
     ZString name;
     ZString signature;
-    int (*op)(dumb_ptr<env_t> env, const_array<val_t> arga);
+    int (*op)(dumb_ptr<env_t> env, Slice<val_t> arga);
 };
 
 /**
@@ -72,7 +74,7 @@ void magic_copy_var(val_t *dest, val_t *src);
 void magic_random_location(location_t *dest, dumb_ptr<area_t> area);
 
 // ret -1: not a string, ret 1: no such item, ret 0: OK
-int magic_find_item(const_array<val_t> args, int index, struct item *item, int *stackable);
+int magic_find_item(Slice<val_t> args, int index, struct item *item, int *stackable);
 
 # define GET_ARG_ITEM(index, dest, stackable)                   \
      switch (magic_find_item(args, index, &dest, &stackable))   \
diff --git a/src/map/magic-stmt.cpp b/src/map/magic-stmt.cpp
index 4063a68..081651c 100644
--- a/src/map/magic-stmt.cpp
+++ b/src/map/magic-stmt.cpp
@@ -298,7 +298,7 @@ dumb_ptr<npc_data> local_spell_effect(map_local *m, int x, int y, int effect,
 }
 
 static
-int op_sfx(dumb_ptr<env_t>, const_array<val_t> args)
+int op_sfx(dumb_ptr<env_t>, Slice<val_t> args)
 {
     interval_t delay = static_cast<interval_t>(ARGINT(2));
 
@@ -319,7 +319,7 @@ int op_sfx(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_instaheal(dumb_ptr<env_t> env, const_array<val_t> args)
+int op_instaheal(dumb_ptr<env_t> env, Slice<val_t> args)
 {
     dumb_ptr<block_list> caster = (env->VAR(VAR_CASTER).ty == TYPE::ENTITY)
         ? map_id2bl(env->VAR(VAR_CASTER).v.v_int) : NULL;
@@ -340,7 +340,7 @@ int op_instaheal(dumb_ptr<env_t> env, const_array<val_t> args)
 }
 
 static
-int op_itemheal(dumb_ptr<env_t> env, const_array<val_t> args)
+int op_itemheal(dumb_ptr<env_t> env, Slice<val_t> args)
 {
     dumb_ptr<block_list> subject = ARGENTITY(0);
     if (subject->bl_type == BL::PC)
@@ -370,7 +370,7 @@ using e::Shroud;
 #define ARGCHAR(n) (ARGENTITY(n)->is_player())
 
 static
-int op_shroud(dumb_ptr<env_t>, const_array<val_t> args)
+int op_shroud(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> subject = ARGCHAR(0);
     Shroud arg = static_cast<Shroud>(ARGINT(1));
@@ -389,7 +389,7 @@ int op_shroud(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_reveal(dumb_ptr<env_t>, const_array<val_t> args)
+int op_reveal(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> subject = ARGCHAR(0);
 
@@ -400,7 +400,7 @@ int op_reveal(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_message(dumb_ptr<env_t>, const_array<val_t> args)
+int op_message(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> subject = ARGCHAR(0);
 
@@ -419,7 +419,7 @@ void timer_callback_kill_npc(TimerData *, tick_t, int npc_id)
 }
 
 static
-int op_messenger_npc(dumb_ptr<env_t>, const_array<val_t> args)
+int op_messenger_npc(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<npc_data> npc;
     location_t *loc = &ARGLOCATION(0);
@@ -476,7 +476,7 @@ void entity_warp(dumb_ptr<block_list> target, map_local *destm, int destx, int d
 }
 
 static
-int op_move(dumb_ptr<env_t>, const_array<val_t> args)
+int op_move(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<block_list> subject = ARGENTITY(0);
     DIR dir = ARGDIR(1);
@@ -491,7 +491,7 @@ int op_move(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_warp(dumb_ptr<env_t>, const_array<val_t> args)
+int op_warp(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<block_list> subject = ARGENTITY(0);
     location_t *loc = &ARGLOCATION(1);
@@ -502,7 +502,7 @@ int op_warp(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_banish(dumb_ptr<env_t>, const_array<val_t> args)
+int op_banish(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<block_list> subject = ARGENTITY(0);
 
@@ -529,7 +529,7 @@ void record_status_change(dumb_ptr<invocation> invocation_, int bl_id,
 }
 
 static
-int op_status_change(dumb_ptr<env_t> env, const_array<val_t> args)
+int op_status_change(dumb_ptr<env_t> env, Slice<val_t> args)
 {
     dumb_ptr<block_list> subject = ARGENTITY(0);
     int invocation_id = env->VAR(VAR_INVOCATION).ty == TYPE::INVOCATION
@@ -550,7 +550,7 @@ int op_status_change(dumb_ptr<env_t> env, const_array<val_t> args)
 }
 
 static
-int op_stop_status_change(dumb_ptr<env_t>, const_array<val_t> args)
+int op_stop_status_change(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<block_list> subject = ARGENTITY(0);
 
@@ -561,7 +561,7 @@ int op_stop_status_change(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_override_attack(dumb_ptr<env_t> env, const_array<val_t> args)
+int op_override_attack(dumb_ptr<env_t> env, Slice<val_t> args)
 {
     dumb_ptr<block_list> psubject = ARGENTITY(0);
     int charges = ARGINT(1);
@@ -602,7 +602,7 @@ int op_override_attack(dumb_ptr<env_t> env, const_array<val_t> args)
 }
 
 static
-int op_create_item(dumb_ptr<env_t>, const_array<val_t> args)
+int op_create_item(dumb_ptr<env_t>, Slice<val_t> args)
 {
     struct item item;
     dumb_ptr<block_list> entity = ARGENTITY(0);
@@ -640,7 +640,7 @@ bool AGGRAVATION_MODE_MAKES_AGGRESSIVE(int n)
 }
 
 static
-int op_aggravate(dumb_ptr<env_t>, const_array<val_t> args)
+int op_aggravate(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<block_list> victim = ARGENTITY(2);
     int mode = ARGINT(1);
@@ -675,7 +675,7 @@ enum class MonsterAttitude
 };
 
 static
-int op_spawn(dumb_ptr<env_t>, const_array<val_t> args)
+int op_spawn(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<area_t> area = ARGAREA(0);
     dumb_ptr<block_list> owner_e = ARGENTITY(1);
@@ -766,7 +766,7 @@ const char *get_invocation_name(dumb_ptr<env_t> env)
 }
 
 static
-int op_injure(dumb_ptr<env_t> env, const_array<val_t> args)
+int op_injure(dumb_ptr<env_t> env, Slice<val_t> args)
 {
     dumb_ptr<block_list> caster = ARGENTITY(0);
     dumb_ptr<block_list> target = ARGENTITY(1);
@@ -816,7 +816,7 @@ int op_injure(dumb_ptr<env_t> env, const_array<val_t> args)
 }
 
 static
-int op_emote(dumb_ptr<env_t>, const_array<val_t> args)
+int op_emote(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<block_list> victim = ARGENTITY(0);
     int emotion = ARGINT(1);
@@ -826,7 +826,7 @@ int op_emote(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_set_script_variable(dumb_ptr<env_t>, const_array<val_t> args)
+int op_set_script_variable(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : NULL;
     VarName varname = stringish<VarName>(ARGSTR(1));
@@ -841,7 +841,7 @@ int op_set_script_variable(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_set_script_str(dumb_ptr<env_t>, const_array<val_t> args)
+int op_set_script_str(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : NULL;
     VarName varname = stringish<VarName>(ARGSTR(1));
@@ -856,7 +856,7 @@ int op_set_script_str(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_set_hair_colour(dumb_ptr<env_t>, const_array<val_t> args)
+int op_set_hair_colour(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : NULL;
 
@@ -869,7 +869,7 @@ int op_set_hair_colour(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_set_hair_style(dumb_ptr<env_t>, const_array<val_t> args)
+int op_set_hair_style(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : NULL;
 
@@ -882,7 +882,7 @@ int op_set_hair_style(dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_drop_item_for (dumb_ptr<env_t>, const_array<val_t> args)
+int op_drop_item_for (dumb_ptr<env_t>, Slice<val_t> args)
 {
     struct item item;
     int stackable;
@@ -908,7 +908,7 @@ int op_drop_item_for (dumb_ptr<env_t>, const_array<val_t> args)
 }
 
 static
-int op_gain_exp(dumb_ptr<env_t>, const_array<val_t> args)
+int op_gain_exp(dumb_ptr<env_t>, Slice<val_t> args)
 {
     dumb_ptr<map_session_data> c = (ENTITY_TYPE(0) == BL::PC) ? ARGPC(0) : NULL;
 
@@ -1468,7 +1468,7 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete)
                     int newpos = run_script_l(
                             ScriptPointer(&*e->e.e_script, invocation_->script_pos),
                             message_recipient, invocation_->bl_id,
-                            3, arg);
+                            arg);
                     /* Returns the new script position, or -1 once the script is finished */
                     if (newpos != -1)
                     {
@@ -1498,10 +1498,10 @@ interval_t spell_run(dumb_ptr<invocation> invocation_, int allow_delete)
                     magic_eval(invocation_->env, &args[i], e->e.e_op.args[i]);
 
                 if (!magic_signature_check("effect", op->name, op->signature,
-                                            e->e.e_op.args_nr, args,
+                                            Slice<val_t>(args, e->e.e_op.args_nr),
                                             e->e.e_op.line_nr,
                                             e->e.e_op.column))
-                    op->op(invocation_->env, const_array<val_t>(args, e->e.e_op.args_nr));
+                    op->op(invocation_->env, Slice<val_t>(args, e->e.e_op.args_nr));
 
                 for (i = 0; i < e->e.e_op.args_nr; i++)
                     magic_clear_var(&args[i]);
diff --git a/src/map/magic-v2.cpp b/src/map/magic-v2.cpp
index 1fad61d..41d29cd 100644
--- a/src/map/magic-v2.cpp
+++ b/src/map/magic-v2.cpp
@@ -254,7 +254,7 @@ namespace magic_v2
         return true;
     }
     static
-    bool op_effect(io::LineSpan span, ZString name, const_array<dumb_ptr<expr_t>> argv, dumb_ptr<effect_t>& effect)
+    bool op_effect(io::LineSpan span, ZString name, Slice<dumb_ptr<expr_t>> argv, dumb_ptr<effect_t>& effect)
     {
         op_t *op = magic_get_op(name);
         if (!op)
@@ -290,7 +290,7 @@ namespace magic_v2
         return retval;
     }
     static
-    bool fun_expr(io::LineSpan span, ZString name, const_array<dumb_ptr<expr_t>> argv, dumb_ptr<expr_t>& expr)
+    bool fun_expr(io::LineSpan span, ZString name, Slice<dumb_ptr<expr_t>> argv, dumb_ptr<expr_t>& expr)
     {
         fun_t *fun = magic_get_fun(name);
         if (!fun)
@@ -322,7 +322,7 @@ namespace magic_v2
         e[0] = left;
         e[1] = right;
         dumb_ptr<expr_t> rv;
-        if (!fun_expr(span, name, const_array<dumb_ptr<expr_t>>(e, 2), rv))
+        if (!fun_expr(span, name, e, rv))
             abort();
         return rv;
     }
diff --git a/src/map/map.cpp b/src/map/map.cpp
index e5a3341..f701730 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -72,7 +72,7 @@ Map<int, struct charid2nick> charid_db;
 static
 int users = 0;
 static
-dumb_ptr<block_list> object[MAX_FLOORITEM];
+Array<dumb_ptr<block_list>, MAX_FLOORITEM> object;
 static
 int first_free_object_id = 0, last_object_id = 0;
 
@@ -546,6 +546,7 @@ int map_addobject(dumb_ptr<block_list> bl)
  */
 int map_delobjectnofree(int id, BL type)
 {
+    assert (id < MAX_FLOORITEM);
     if (!object[id])
         return 0;
 
@@ -581,6 +582,7 @@ int map_delobjectnofree(int id, BL type)
  */
 int map_delobject(int id, BL type)
 {
+    assert (id < MAX_FLOORITEM);
     dumb_ptr<block_list> obj = object[id];
 
     if (obj == NULL)
@@ -603,6 +605,7 @@ int map_delobject(int id, BL type)
 void map_foreachobject(std::function<void(dumb_ptr<block_list>)> func,
         BL type)
 {
+    assert (last_object_id < MAX_FLOORITEM);
     std::vector<dumb_ptr<block_list>> bl_list;
     for (int i = 2; i <= last_object_id; i++)
     {
@@ -634,6 +637,7 @@ void map_foreachobject(std::function<void(dumb_ptr<block_list>)> func,
  */
 void map_clearflooritem_timer(TimerData *tid, tick_t, int id)
 {
+    assert (id < MAX_FLOORITEM);
     dumb_ptr<block_list> obj = object[id];
     assert (obj && obj->bl_type == BL::ITEM);
     dumb_ptr<flooritem_data> fitem = obj->is_item();
@@ -1669,48 +1673,49 @@ bool map_confs(XString key, ZString value)
  * Map-Server Init and Command-line Arguments [Valaris]
  *------------------------------------------------------
  */
-int do_init(int argc, ZString *argv)
+int do_init(Slice<ZString> argv)
 {
+    ZString argv0 = argv.pop_front();
     runflag &= magic_init0();
 
     bool loaded_config_yet = false;
-    for (int i = 1; i < argc; ++i)
+    while (argv)
     {
-        if (argv[i].startswith('-'))
+        ZString argvi = argv.pop_front();
+        if (argvi.startswith('-'))
         {
-            if (argv[i] == "--help")
+            if (argvi == "--help")
             {
                 PRINTF("Usage: %s [--help] [--version] [--write_atcommand_config outfile] [files...]\n",
-                        argv[0]);
+                        argv0);
                 exit(0);
             }
-            else if (argv[i] == "--version")
+            else if (argvi == "--version")
             {
                 PRINTF("%s\n", CURRENT_VERSION_STRING);
                 exit(0);
             }
-            else if (argv[i] == "--write-atcommand-config")
+            else if (argvi == "--write-atcommand-config")
             {
-                ++i;
-                if (i == argc)
+                if (!argv)
                 {
                     PRINTF("Missing argument\n");
                     exit(1);
                 }
-                ZString filename = argv[i];
+                ZString filename = argv.pop_front();
                 atcommand_config_write(filename);
                 exit(0);
             }
             else
             {
-                FPRINTF(stderr, "Unknown argument: %s\n", argv[i]);
+                FPRINTF(stderr, "Unknown argument: %s\n", argvi);
                 runflag = false;
             }
         }
         else
         {
             loaded_config_yet = true;
-            runflag &= load_config_file(argv[i], map_confs);
+            runflag &= load_config_file(argvi, map_confs);
         }
     }
 
diff --git a/src/map/map.hpp b/src/map/map.hpp
index 0f9f3d1..cb57c6b 100644
--- a/src/map/map.hpp
+++ b/src/map/map.hpp
@@ -113,7 +113,7 @@ public:
 struct walkpath_data
 {
     unsigned char path_len, path_pos, path_half;
-    DIR path[MAX_WALKPATH];
+    Array<DIR, MAX_WALKPATH> path;
 };
 struct status_change
 {
@@ -173,7 +173,7 @@ struct map_session_data : block_list, SessionData
     unsigned char tmw_version;  // tmw client version
     CharKey status_key;
     CharData status;
-    struct item_data *inventory_data[MAX_INVENTORY];
+    Array<struct item_data *, MAX_INVENTORY> inventory_data;
     earray<short, EQUIP, EQUIP::COUNT> equip_index;
     int weight, max_weight;
     MapName mapname_;
@@ -275,8 +275,8 @@ struct map_session_data : block_list, SessionData
     short sc_count;
 
     int trade_partner;
-    int deal_item_index[10];
-    int deal_item_amount[10];
+    Array<int, TRADE_MAX> deal_item_index;
+    Array<int, TRADE_MAX> deal_item_amount;
     int deal_zeny;
     short deal_locked;
 
@@ -292,7 +292,7 @@ struct map_session_data : block_list, SessionData
     int pvp_lastusers;
 
     std::list<NpcEvent> eventqueuel;
-    Timer eventtimer[MAX_EVENTTIMER];
+    Array<Timer, MAX_EVENTTIMER> eventtimer;
 
     struct
     {
@@ -349,7 +349,7 @@ struct npc_data : block_list
     short flag;
 
     std::list<RString> eventqueuel;
-    Timer eventtimer[MAX_EVENTTIMER];
+    Array<Timer, MAX_EVENTTIMER> eventtimer;
     short arenaflag;
 
 private:
@@ -517,13 +517,7 @@ struct map_local : map_abstract
     MapFlags flag;
     struct point save;
     struct point resave;
-    dumb_ptr<npc_data> npc[MAX_NPC_PER_MAP];
-    struct
-    {
-        int drop_id;
-        int drop_type;
-        int drop_per;
-    } drop_list[MAX_DROP_PER_MAP];
+    Array<dumb_ptr<npc_data>, MAX_NPC_PER_MAP> npc;
 };
 
 struct map_remote : map_abstract
@@ -535,6 +529,8 @@ struct map_remote : map_abstract
 inline
 MapCell read_gatp(map_local *m, int x, int y)
 {
+    assert (0 <= x && x < m->xs);
+    assert (0 <= y && y < m->ys);
     return m->gat[x + y * m->xs];
 }
 
diff --git a/src/map/mapflag.cpp b/src/map/mapflag.cpp
index 097f4d1..51af30a 100644
--- a/src/map/mapflag.cpp
+++ b/src/map/mapflag.cpp
@@ -40,41 +40,41 @@ bool extract<MapFlag, void, void>(XString str, MapFlag *mf)
 {
     const struct
     {
-        char str[32];
+        ZString str;
         MapFlag id;
     } flags[] =
     {
-        //{"alias", MapFlag::ALIAS},
-        //{"nomemo", MapFlag::NOMEMO},
-        {"noteleport", MapFlag::NOTELEPORT},
-        {"noreturn", MapFlag::NORETURN},
-        {"monster_noteleport", MapFlag::MONSTER_NOTELEPORT},
-        {"nosave", MapFlag::NOSAVE},
-        //{"nobranch", MapFlag::NOBRANCH},
-        {"nopenalty", MapFlag::NOPENALTY},
-        {"pvp", MapFlag::PVP},
-        {"pvp_noparty", MapFlag::PVP_NOPARTY},
-        //{"pvp_noguild", MapFlag::PVP_NOGUILD},
-        //{"pvp_nightmaredrop", MapFlag::PVP_NIGHTMAREDROP},
-        {"pvp_nocalcrank", MapFlag::PVP_NOCALCRANK},
-        //{"gvg", MapFlag::GVG},
-        //{"gvg_noparty", MapFlag::GVG_NOPARTY},
-        //{"nozenypenalty", MapFlag::NOZENYPENALTY},
-        //{"notrade", MapFlag::NOTRADE},
-        //{"noskill", MapFlag::NOSKILL},
-        {"nowarp", MapFlag::NOWARP},
-        {"nowarpto", MapFlag::NOWARPTO},
-        {"nopvp", MapFlag::NOPVP},
-        //{"noicewall", MapFlag::NOICEWALL},
-        {"snow", MapFlag::SNOW},
-        {"fog", MapFlag::FOG},
-        {"sakura", MapFlag::SAKURA},
-        {"leaves", MapFlag::LEAVES},
-        {"rain", MapFlag::RAIN},
-        {"no_player_drops", MapFlag::NO_PLAYER_DROPS},
-        {"town", MapFlag::TOWN},
-        {"outside", MapFlag::OUTSIDE},
-        {"resave", MapFlag::RESAVE},
+        //{ZString("alias"), MapFlag::ALIAS},
+        //{ZString("nomemo"), MapFlag::NOMEMO},
+        {ZString("noteleport"), MapFlag::NOTELEPORT},
+        {ZString("noreturn"), MapFlag::NORETURN},
+        {ZString("monster_noteleport"), MapFlag::MONSTER_NOTELEPORT},
+        {ZString("nosave"), MapFlag::NOSAVE},
+        //{ZString("nobranch"), MapFlag::NOBRANCH},
+        {ZString("nopenalty"), MapFlag::NOPENALTY},
+        {ZString("pvp"), MapFlag::PVP},
+        {ZString("pvp_noparty"), MapFlag::PVP_NOPARTY},
+        //{ZString("pvp_noguild"), MapFlag::PVP_NOGUILD},
+        //{ZString("pvp_nightmaredrop"), MapFlag::PVP_NIGHTMAREDROP},
+        {ZString("pvp_nocalcrank"), MapFlag::PVP_NOCALCRANK},
+        //{ZString("gvg"), MapFlag::GVG},
+        //{ZString("gvg_noparty"), MapFlag::GVG_NOPARTY},
+        //{ZString("nozenypenalty"), MapFlag::NOZENYPENALTY},
+        //{ZString("notrade"), MapFlag::NOTRADE},
+        //{ZString("noskill"), MapFlag::NOSKILL},
+        {ZString("nowarp"), MapFlag::NOWARP},
+        {ZString("nowarpto"), MapFlag::NOWARPTO},
+        {ZString("nopvp"), MapFlag::NOPVP},
+        //{ZString("noicewall"), MapFlag::NOICEWALL},
+        {ZString("snow"), MapFlag::SNOW},
+        {ZString("fog"), MapFlag::FOG},
+        {ZString("sakura"), MapFlag::SAKURA},
+        {ZString("leaves"), MapFlag::LEAVES},
+        {ZString("rain"), MapFlag::RAIN},
+        {ZString("no_player_drops"), MapFlag::NO_PLAYER_DROPS},
+        {ZString("town"), MapFlag::TOWN},
+        {ZString("outside"), MapFlag::OUTSIDE},
+        {ZString("resave"), MapFlag::RESAVE},
     };
     for (auto& pair : flags)
         if (str == pair.str)
diff --git a/src/map/mob.cpp b/src/map/mob.cpp
index 61dcfb6..c4a4304 100644
--- a/src/map/mob.cpp
+++ b/src/map/mob.cpp
@@ -1305,7 +1305,7 @@ int mob_can_reach(dumb_ptr<mob_data> md, dumb_ptr<block_list> bl, int range)
 int mob_target(dumb_ptr<mob_data> md, dumb_ptr<block_list> bl, int dist)
 {
     dumb_ptr<map_session_data> sd;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     MobMode mode;
 
     nullpo_ret(md);
@@ -3560,15 +3560,15 @@ bool extract<MobSkillCondition, void, void>(XString str, MobSkillCondition *msc)
 {
     const struct
     {
-        char str[32];
+        ZString str;
         MobSkillCondition id;
     } cond1[] =
     {
-        {"always", MobSkillCondition::MSC_ALWAYS},
-        {"myhpltmaxrate", MobSkillCondition::MSC_MYHPLTMAXRATE},
-        {"notintown", MobSkillCondition::MSC_NOTINTOWN},
-        {"slavelt", MobSkillCondition::MSC_SLAVELT},
-        {"slavele", MobSkillCondition::MSC_SLAVELE},
+        {ZString("always"), MobSkillCondition::MSC_ALWAYS},
+        {ZString("myhpltmaxrate"), MobSkillCondition::MSC_MYHPLTMAXRATE},
+        {ZString("notintown"), MobSkillCondition::MSC_NOTINTOWN},
+        {ZString("slavelt"), MobSkillCondition::MSC_SLAVELT},
+        {ZString("slavele"), MobSkillCondition::MSC_SLAVELE},
     };
     for (auto& pair : cond1)
         if (str == pair.str)
@@ -3584,14 +3584,14 @@ bool extract<MobSkillState, void, void>(XString str, MobSkillState *mss)
 {
     const struct
     {
-        char str[32];
+        ZString str;
         MobSkillState id;
     } state[] =
     {
-        {"any", MobSkillState::ANY},
-        {"idle", MobSkillState::MSS_IDLE},
-        {"walk", MobSkillState::MSS_WALK},
-        {"attack", MobSkillState::MSS_ATTACK},
+        {ZString("any"), MobSkillState::ANY},
+        {ZString("idle"), MobSkillState::MSS_IDLE},
+        {ZString("walk"), MobSkillState::MSS_WALK},
+        {ZString("attack"), MobSkillState::MSS_ATTACK},
     };
     for (auto& pair : state)
         if (str == pair.str)
@@ -3607,12 +3607,12 @@ bool extract<MobSkillTarget, void, void>(XString str, MobSkillTarget *mst)
 {
     const struct
     {
-        char str[32];
+        ZString str;
         MobSkillTarget id;
     } target[] =
     {
-        {"target", MobSkillTarget::MST_TARGET},
-        {"self", MobSkillTarget::MST_SELF},
+        {ZString("target"), MobSkillTarget::MST_TARGET},
+        {ZString("self"), MobSkillTarget::MST_SELF},
     };
     for (auto& pair : target)
         if (str == pair.str)
diff --git a/src/map/npc.cpp b/src/map/npc.cpp
index dd75691..514c042 100644
--- a/src/map/npc.cpp
+++ b/src/map/npc.cpp
@@ -200,7 +200,7 @@ void npc_timer_event(NpcEvent eventname)
  */
 static
 void npc_event_doall_sub(NpcEvent key, struct event_data *ev,
-        int *c, ScriptLabel name, int rid, int argc, argrec_t *argv)
+        int *c, ScriptLabel name, int rid, Slice<argrec_t> argv)
 {
     ScriptLabel p = key.label;
 
@@ -209,45 +209,45 @@ 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,
-                argc, argv);
+                argv);
         (*c)++;
     }
 }
 
-int npc_event_doall_l(ScriptLabel name, int rid, int argc, argrec_t *args)
+int npc_event_doall_l(ScriptLabel name, int rid, Slice<argrec_t> args)
 {
     int c = 0;
 
     for (auto& pair : ev_db)
-        npc_event_doall_sub(pair.first, &pair.second, &c, name, rid, argc, args);
+        npc_event_doall_sub(pair.first, &pair.second, &c, name, rid, args);
     return c;
 }
 
 static
 void npc_event_do_sub(NpcEvent key, struct event_data *ev,
-        int *c, NpcEvent name, int rid, int argc, argrec_t *argv)
+        int *c, NpcEvent name, int rid, Slice<argrec_t> argv)
 {
     nullpo_retv(ev);
 
     if (name == key)
     {
         run_script_l(ScriptPointer(ev->nd->scr.script.get(), ev->pos), rid, ev->nd->bl_id,
-                argc, argv);
+                argv);
         (*c)++;
     }
 }
 
-int npc_event_do_l(NpcEvent name, int rid, int argc, argrec_t *args)
+int npc_event_do_l(NpcEvent name, int rid, Slice<argrec_t> args)
 {
     int c = 0;
 
     if (!name.npc)
     {
-        return npc_event_doall_l(name.label, rid, argc, args);
+        return npc_event_doall_l(name.label, rid, args);
     }
 
     for (auto& pair : ev_db)
-        npc_event_do_sub(pair.first, &pair.second, &c, name, rid, argc, args);
+        npc_event_do_sub(pair.first, &pair.second, &c, name, rid, args);
     return c;
 }
 
diff --git a/src/map/npc.hpp b/src/map/npc.hpp
index 93f2030..8d2dc5b 100644
--- a/src/map/npc.hpp
+++ b/src/map/npc.hpp
@@ -51,17 +51,17 @@ void npc_delsrcfile(XString);
 bool do_init_npc(void);
 int npc_event_do_oninit(void);
 
-int npc_event_doall_l(ScriptLabel name, int rid, int argc, struct argrec_t *argv);
-int npc_event_do_l(NpcEvent name, int rid, int argc, struct argrec_t *argv);
+int npc_event_doall_l(ScriptLabel name, int rid, Slice<argrec_t> argv);
+int npc_event_do_l(NpcEvent name, int rid, Slice<argrec_t> argv);
 inline
 int npc_event_doall(ScriptLabel name)
 {
-    return npc_event_doall_l(name, 0, 0, NULL);
+    return npc_event_doall_l(name, 0, nullptr);
 }
 inline
 int npc_event_do(NpcEvent name)
 {
-    return npc_event_do_l(name, 0, 0, NULL);
+    return npc_event_do_l(name, 0, nullptr);
 }
 
 void npc_timerevent_start(dumb_ptr<npc_data_script> nd);
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index 10c2b22..343e0fc 100644
--- a/src/map/pc.cpp
+++ b/src/map/pc.cpp
@@ -589,7 +589,7 @@ static
 int pc_isequip(dumb_ptr<map_session_data> sd, int n)
 {
     struct item_data *item;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     //転生や養子の場合の元の職業を算出する
 
     nullpo_ret(sd);
@@ -1074,7 +1074,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
                         };
                         run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0),
                                 sd->bl_id, 0,
-                                2, arg);
+                                arg);
                     }
                     sd->state.lr_flag = 0;
                 }
@@ -1091,7 +1091,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
                     sd->attackrange += sd->inventory_data[index]->range;
                     run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0),
                             sd->bl_id, 0,
-                            2, arg);
+                            arg);
                 }
             }
             else if (sd->inventory_data[index]->type == ItemType::ARMOR)
@@ -1104,7 +1104,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
                 sd->watk += sd->inventory_data[index]->atk;
                 run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0),
                         sd->bl_id, 0,
-                        2, arg);
+                        arg);
             }
         }
     }
@@ -1130,7 +1130,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
             sd->state.lr_flag = 2;
             run_script_l(ScriptPointer(sd->inventory_data[index]->equip_script.get(), 0),
                     sd->bl_id, 0,
-                    2, arg);
+                    arg);
             sd->state.lr_flag = 0;
             sd->arrow_atk += sd->inventory_data[index]->atk;
         }
@@ -2702,7 +2702,7 @@ void pc_attack_timer(TimerData *, tick_t tick, int id)
 {
     dumb_ptr<map_session_data> sd;
     dumb_ptr<block_list> bl;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     int dist, range;
 
     sd = map_id2sd(id);
@@ -3514,10 +3514,10 @@ int pc_damage(dumb_ptr<block_list> src, dumb_ptr<map_session_data> sd,
             {"@victimrid", sd->bl_id},
             {"@victimlvl", sd->status.base_level},
         };
-        npc_event_doall_l(stringish<ScriptLabel>("OnPCKilledEvent"), sd->bl_id, 3, arg);
-        npc_event_doall_l(stringish<ScriptLabel>("OnPCKillEvent"), src->bl_id, 3, arg);
+        npc_event_doall_l(stringish<ScriptLabel>("OnPCKilledEvent"), sd->bl_id, arg);
+        npc_event_doall_l(stringish<ScriptLabel>("OnPCKillEvent"), src->bl_id, arg);
     }
-    npc_event_doall_l(stringish<ScriptLabel>("OnPCDieEvent"), sd->bl_id, 0, NULL);
+    npc_event_doall_l(stringish<ScriptLabel>("OnPCDieEvent"), sd->bl_id, nullptr);
 
     return 0;
 }
@@ -4084,6 +4084,7 @@ int pc_readglobalreg(dumb_ptr<map_session_data> sd, VarName reg)
 
     nullpo_ret(sd);
 
+    assert (sd->status.global_reg_num < GLOBAL_REG_NUM);
     for (i = 0; i < sd->status.global_reg_num; i++)
     {
         if (sd->status.global_reg[i].str == reg)
@@ -4109,6 +4110,7 @@ int pc_setglobalreg(dumb_ptr<map_session_data> sd, VarName reg, int val)
         sd->die_counter = val;
         pc_calcstatus(sd, 0);
     }
+    assert (sd->status.global_reg_num < GLOBAL_REG_NUM);
     if (val == 0)
     {
         for (i = 0; i < sd->status.global_reg_num; i++)
@@ -4155,6 +4157,7 @@ int pc_readaccountreg(dumb_ptr<map_session_data> sd, VarName reg)
 
     nullpo_ret(sd);
 
+    assert (sd->status.account_reg_num < ACCOUNT_REG_NUM);
     for (i = 0; i < sd->status.account_reg_num; i++)
     {
         if (sd->status.account_reg[i].str == reg)
diff --git a/src/map/script.cpp b/src/map/script.cpp
index cbb6d96..18055b8 100644
--- a/src/map/script.cpp
+++ b/src/map/script.cpp
@@ -78,20 +78,20 @@ Map<ScriptLabel, int> scriptlabel_db;
 UPMap<RString, const ScriptBuffer> userfunc_db;
 
 static
-const char *pos_str[11] =
-{
-    "Head",
-    "Body",
-    "Left hand",
-    "Right hand",
-    "Robe",
-    "Shoes",
-    "Accessory 1",
-    "Accessory 2",
-    "Head 2",
-    "Head 3",
-    "Not Equipped",
-};
+Array<ZString, 11> pos_str //=
+{{
+    ZString("Head"),
+    ZString("Body"),
+    ZString("Left hand"),
+    ZString("Right hand"),
+    ZString("Robe"),
+    ZString("Shoes"),
+    ZString("Accessory 1"),
+    ZString("Accessory 2"),
+    ZString("Head 2"),
+    ZString("Head 3"),
+    ZString("Not Equipped"),
+}};
 
 static
 struct Script_Config
@@ -2202,8 +2202,8 @@ void builtin_strcharinfo(ScriptState *st)
 // indexed by the equip_* in db/const.txt
 // TODO change to use EQUIP
 static
-EPOS equip[11] =
-{
+Array<EPOS, 11> equip //=
+{{
     EPOS::HAT,
     EPOS::MISC1,
     EPOS::SHIELD,
@@ -2215,7 +2215,7 @@ EPOS equip[11] =
     EPOS::TORSO,
     EPOS::LEGS,
     EPOS::ARROW,
-};
+}};
 
 /*==========================================
  * GetEquipID(Pos);     Pos: 1-10
@@ -4693,11 +4693,11 @@ void run_script_main(ScriptState *st, const ScriptBuffer *rootscript)
  */
 int run_script(ScriptPointer sp, int rid, int oid)
 {
-    return run_script_l(sp, rid, oid, 0, NULL);
+    return run_script_l(sp, rid, oid, nullptr);
 }
 
 int run_script_l(ScriptPointer sp, int rid, int oid,
-        int args_nr, argrec_t *args)
+        Slice<argrec_t> args)
 {
     struct script_stack stack;
     ScriptState st;
@@ -4717,7 +4717,7 @@ int run_script_l(ScriptPointer sp, int rid, int oid,
     st.scriptp = sp;
     st.rid = rid;
     st.oid = oid;
-    for (i = 0; i < args_nr; i++)
+    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);
diff --git a/src/map/script.hpp b/src/map/script.hpp
index 8c072b3..ea087c2 100644
--- a/src/map/script.hpp
+++ b/src/map/script.hpp
@@ -6,6 +6,8 @@
 
 # include <vector>
 
+# include "../range/slice.hpp"
+
 # include "../strings/rstring.hpp"
 # include "../strings/astring.hpp"
 # include "../strings/zstring.hpp"
@@ -153,7 +155,7 @@ struct argrec_t
     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, int, int, int, argrec_t *args);
+int run_script_l(ScriptPointer, int, int, Slice<argrec_t> args);
 int run_script(ScriptPointer, int, int);
 
 struct ScriptLabel;
diff --git a/src/map/skill-pools.cpp b/src/map/skill-pools.cpp
index 31998f8..2ac3a93 100644
--- a/src/map/skill-pools.cpp
+++ b/src/map/skill-pools.cpp
@@ -7,7 +7,7 @@
 
 #include "../poison.hpp"
 
-SkillID skill_pool_skills[MAX_POOL_SKILLS];
+Array<SkillID, MAX_POOL_SKILLS> skill_pool_skills;
 int skill_pool_skills_size = 0;
 
 void skill_pool_register(SkillID id)
diff --git a/src/map/skill.cpp b/src/map/skill.cpp
index 72d7f7a..c29704a 100644
--- a/src/map/skill.cpp
+++ b/src/map/skill.cpp
@@ -232,7 +232,7 @@ int skill_attack(BF attack_type, dumb_ptr<block_list> src,
         SkillID skillid, int skilllv, tick_t tick, BCT flag)
 {
     struct Damage dmg;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     int type, lv, damage;
 
     nullpo_ret(src);
@@ -550,7 +550,7 @@ int skill_castend_nodamage_id(dumb_ptr<block_list> src, dumb_ptr<block_list> bl,
 interval_t skill_castfix(dumb_ptr<block_list> bl, interval_t interval)
 {
     dumb_ptr<mob_data> md;        // [Valaris]
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     int dex;
     int castrate = 100;
     SkillID skill;
@@ -601,7 +601,7 @@ interval_t skill_castfix(dumb_ptr<block_list> bl, interval_t interval)
  */
 interval_t skill_delayfix(dumb_ptr<block_list> bl, interval_t interval)
 {
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_retr(interval_t::zero(), bl);
 
@@ -663,7 +663,7 @@ int skill_castcancel(dumb_ptr<block_list> bl, int)
  */
 int skill_status_change_active(dumb_ptr<block_list> bl, StatusChange type)
 {
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
 
     nullpo_ret(bl);
     if (bl->bl_type != BL::PC && bl->bl_type != BL::MOB)
@@ -682,7 +682,7 @@ int skill_status_change_active(dumb_ptr<block_list> bl, StatusChange type)
 
 void skill_status_change_end(dumb_ptr<block_list> bl, StatusChange type, TimerData *tid)
 {
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     int opt_flag = 0, calc_flag = 0;
     short *sc_count;
     Option *option;
@@ -806,7 +806,7 @@ void skill_status_change_timer(TimerData *tid, tick_t tick, int id, StatusChange
 {
     dumb_ptr<block_list> bl;
     dumb_ptr<map_session_data> sd = NULL;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     //short *sc_count; //使ってない?
 
     if ((bl = map_id2bl(id)) == NULL)
@@ -907,7 +907,7 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type,
         interval_t tick, int spell_invocation)
 {
     dumb_ptr<map_session_data> sd = NULL;
-    eptr<struct status_change, StatusChange> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     short *sc_count;
     Option *option;
     Opt1 *opt1;
@@ -1081,7 +1081,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> sc_data;
+    eptr<struct status_change, StatusChange, StatusChange::MAX_STATUSCHANGE> sc_data;
     short *sc_count;
     Option *option;
     Opt1 *opt1;
diff --git a/src/map/skill.hpp b/src/map/skill.hpp
index b612268..ece2ab5 100644
--- a/src/map/skill.hpp
+++ b/src/map/skill.hpp
@@ -22,17 +22,16 @@ struct skill_db_
     SkillFlags poolflags;
     int max_raise; // `max' is the global max, `max_raise' is the maximum attainable via skill-ups
     int num_k;
-    int cast[MAX_SKILL_LEVEL], delay[MAX_SKILL_LEVEL];
-    int upkeep_time[MAX_SKILL_LEVEL], upkeep_time2[MAX_SKILL_LEVEL];
+    Array<int, MAX_SKILL_LEVEL> cast, delay;
+    Array<int, MAX_SKILL_LEVEL> upkeep_time, upkeep_time2;
     bool castcancel;
     int cast_def_rate;
     int inf2, maxcount;
-    int hp[MAX_SKILL_LEVEL], sp[MAX_SKILL_LEVEL], mhp[MAX_SKILL_LEVEL],
-        hp_rate[MAX_SKILL_LEVEL], sp_rate[MAX_SKILL_LEVEL],
-        zeny[MAX_SKILL_LEVEL];
+    Array<int, MAX_SKILL_LEVEL> hp, sp, mhp,
+        hprate, sp_rate,
+        zeny;
     int weapon;
-    int itemid[10], amount[10];
-    int castnodex[MAX_SKILL_LEVEL];
+    Array<int, MAX_SKILL_LEVEL> castnodex;
 };
 extern
 earray<skill_db_, SkillID, SkillID::MAX_SKILL_DB> skill_db;
@@ -115,7 +114,7 @@ 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 SkillID skill_pool_skills[MAX_POOL_SKILLS];  // All pool skills
+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.
diff --git a/src/map/tmw.hpp b/src/map/tmw.hpp
index 65e71fb..65eb28a 100644
--- a/src/map/tmw.hpp
+++ b/src/map/tmw.hpp
@@ -3,8 +3,6 @@
 
 # include "../strings/fwd.hpp"
 
-# include "../generic/const_array.hpp"
-
 # include "../mmo/dumb_ptr.hpp"
 
 # include "map.hpp"
diff --git a/src/map/trade.cpp b/src/map/trade.cpp
index e9c5fd4..bd6e2bf 100644
--- a/src/map/trade.cpp
+++ b/src/map/trade.cpp
@@ -137,7 +137,7 @@ void trade_tradeadditem(dumb_ptr<map_session_data> sd, int index, int amount)
                     && target_sd->inventory_data[i] == NULL)
                     free_++;
             }
-            for (trade_i = 0; trade_i < 10; trade_i++)
+            for (trade_i = 0; trade_i < TRADE_MAX; trade_i++)
             {
                 if (sd->deal_item_amount[trade_i] == 0)
                 {
@@ -238,7 +238,7 @@ void trade_tradeok(dumb_ptr<map_session_data> sd)
 
     nullpo_retv(sd);
 
-    for (trade_i = 0; trade_i < 10; trade_i++)
+    for (trade_i = 0; trade_i < TRADE_MAX; trade_i++)
     {
         if (sd->deal_item_amount[trade_i] >
             sd->status.inventory[sd->deal_item_index[trade_i] - 2].amount
@@ -272,7 +272,7 @@ void trade_tradecancel(dumb_ptr<map_session_data> sd)
 
     if ((target_sd = map_id2sd(sd->trade_partner)) != NULL)
     {
-        for (trade_i = 0; trade_i < 10; trade_i++)
+        for (trade_i = 0; trade_i < TRADE_MAX; trade_i++)
         {                       //give items back (only virtual)
             if (sd->deal_item_amount[trade_i] != 0)
             {
@@ -352,7 +352,7 @@ void trade_tradecommit(dumb_ptr<map_session_data> sd)
                 }
                 sd->trade_partner = 0;
                 target_sd->trade_partner = 0;
-                for (trade_i = 0; trade_i < 10; trade_i++)
+                for (trade_i = 0; trade_i < TRADE_MAX; trade_i++)
                 {
                     if (sd->deal_item_amount[trade_i] != 0)
                     {
diff --git a/src/mmo/core.cpp b/src/mmo/core.cpp
index 1a9a52e..26b5128 100644
--- a/src/mmo/core.cpp
+++ b/src/mmo/core.cpp
@@ -83,7 +83,7 @@ int main(int argc, char **argv)
     ZString *args = static_cast<ZString *>(alloca(argc * sizeof(ZString)));
     for (int i = 0; i < argc; ++i)
         args[i] = ZString(strings::really_construct_from_a_pointer, argv[i], nullptr);
-    do_init(argc, args);
+    do_init(Slice<ZString>(args, argc));
 
     if (!runflag)
     {
diff --git a/src/mmo/core.hpp b/src/mmo/core.hpp
index 1788ece..7a26f7d 100644
--- a/src/mmo/core.hpp
+++ b/src/mmo/core.hpp
@@ -3,6 +3,8 @@
 
 # include "../sanity.hpp"
 
+# include "../range/slice.hpp"
+
 # include "../strings/fwd.hpp"
 
 /// core.c contains a server-independent main() function
@@ -13,7 +15,7 @@ extern volatile bool runflag;
 
 /// This is an external function defined by each server
 /// This function must register stuff for the parse loop
-extern int do_init(int, ZString *);
+extern int do_init(Slice<ZString>);
 
 /// Cleanup function called whenever a signal kills us
 /// or when if we manage to exit() gracefully.
diff --git a/src/mmo/dumb_ptr.hpp b/src/mmo/dumb_ptr.hpp
index 98c6308..9632945 100644
--- a/src/mmo/dumb_ptr.hpp
+++ b/src/mmo/dumb_ptr.hpp
@@ -29,8 +29,6 @@
 # include "../strings/zstring.hpp"
 # include "../strings/xstring.hpp"
 
-# include "../generic/const_array.hpp"
-
 // unmanaged new/delete-able pointer
 // should be replaced by std::unique_ptr<T>
 template<class T>
diff --git a/src/mmo/extract.hpp b/src/mmo/extract.hpp
index 0ea9eb9..622281b 100644
--- a/src/mmo/extract.hpp
+++ b/src/mmo/extract.hpp
@@ -25,8 +25,6 @@
 
 # include "../strings/xstring.hpp"
 
-# include "../generic/const_array.hpp"
-
 # include "mmo.hpp"
 # include "utils.hpp"
 
diff --git a/src/mmo/mmo.hpp b/src/mmo/mmo.hpp
index 6b3cd53..f867492 100644
--- a/src/mmo/mmo.hpp
+++ b/src/mmo/mmo.hpp
@@ -21,6 +21,7 @@ constexpr int MAX_MAP_PER_SERVER = 512;
 constexpr int MAX_INVENTORY = 100;
 constexpr int MAX_AMOUNT = 30000;
 constexpr int MAX_ZENY = 1000000000;     // 1G zeny
+constexpr int TRADE_MAX = 10;
 
 enum class SkillID : uint16_t;
 constexpr SkillID MAX_SKILL = SkillID(474); // not 450
@@ -43,6 +44,20 @@ constexpr int MAX_PARTY = 12;
 # define MIN_CLOTH_COLOR battle_config.min_cloth_color
 # define MAX_CLOTH_COLOR battle_config.max_cloth_color
 
+template<class T, size_t n>
+struct Array
+{
+    T data[n];
+public:
+    T& operator [](size_t i) { assert (i < n); return data[i]; }
+    const T& operator [](size_t i) const { assert (i < n); return data[i]; }
+
+    T *begin() { return data + 0; }
+    T *end() { return data + n; }
+    const T *begin() const { return data + 0; }
+    const T *end() const { return data + n; }
+};
+
 struct AccountName : VString<23> {};
 struct AccountPass : VString<23> {};
 struct AccountCrypt : VString<39> {};
@@ -319,14 +334,14 @@ struct CharData
     unsigned int mapport;
 
     struct point last_point, save_point;
-    struct item inventory[MAX_INVENTORY];
+    Array<struct item, MAX_INVENTORY> inventory;
     earray<skill_value, SkillID, MAX_SKILL> skill;
     int global_reg_num;
-    struct global_reg global_reg[GLOBAL_REG_NUM];
+    Array<struct global_reg, GLOBAL_REG_NUM> global_reg;
     int account_reg_num;
-    struct global_reg account_reg[ACCOUNT_REG_NUM];
+    Array<struct global_reg, ACCOUNT_REG_NUM> account_reg;
     int account_reg2_num;
-    struct global_reg account_reg2[ACCOUNT_REG2_NUM];
+    Array<struct global_reg, ACCOUNT_REG2_NUM> account_reg2;
 };
 
 struct CharPair
@@ -345,11 +360,9 @@ struct storage
     int account_id;
     short storage_status;
     short storage_amount;
-    struct item storage_[MAX_STORAGE];
+    Array<struct item, MAX_STORAGE> storage_;
 };
 
-//struct map_session_data;
-
 struct GM_Account
 {
     int account_id;
@@ -371,7 +384,7 @@ struct party
     PartyName name;
     int exp;
     int item;
-    struct party_member member[MAX_PARTY];
+    Array<struct party_member, MAX_PARTY> member;
 };
 
 #endif // TMWA_MMO_MMO_HPP
diff --git a/src/mmo/utils.hpp b/src/mmo/utils.hpp
index 5e9de26..40ff595 100644
--- a/src/mmo/utils.hpp
+++ b/src/mmo/utils.hpp
@@ -10,7 +10,6 @@
 # include "../strings/fwd.hpp"
 # include "../strings/vstring.hpp"
 
-# include "../generic/const_array.hpp"
 # include "../generic/operators.hpp"
 
 # include "../io/fwd.hpp"
diff --git a/src/range/slice.cpp b/src/range/slice.cpp
new file mode 100644
index 0000000..5b23447
--- /dev/null
+++ b/src/range/slice.cpp
@@ -0,0 +1 @@
+#include "slice.hpp"
diff --git a/src/range/slice.hpp b/src/range/slice.hpp
new file mode 100644
index 0000000..f645595
--- /dev/null
+++ b/src/range/slice.hpp
@@ -0,0 +1,74 @@
+#ifndef TMWA_GENERIC_SLICE_HPP
+#define TMWA_GENERIC_SLICE_HPP
+//    slice.hpp - a borrowed array
+//
+//    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 "../sanity.hpp"
+
+# include <cstddef>
+
+# include <type_traits>
+
+# include <vector>
+
+template<class T>
+class Slice
+{
+    T *_begin;
+    T *_end;
+public:
+    class iterator;
+
+    Slice(std::nullptr_t);
+    Slice(T *b, T *e);
+    Slice(T *b, size_t l);
+    template<class U, typename=typename std::enable_if<sizeof(T) == sizeof(U) && std::is_base_of<T, U>::value>::type>
+    Slice(Slice<U> o);
+    template<size_t n, typename=typename std::enable_if<sizeof(T) != 1>::type>
+    Slice(T (&arr)[n]);
+    // TODO: come up with something else once using ranges (wrap all containers?)
+    Slice(std::vector<T>& vec);
+
+    iterator begin() const;
+    iterator end() const;
+    T *data() const;
+    size_t size() const;
+    operator bool() const;
+    bool operator not() const;
+    T& front() const;
+    T& back() const;
+    T& pop_front();
+    T& pop_back();
+    __attribute__((deprecated("use iterators instead")))
+    T& operator[](size_t o);
+
+    Slice slice_t(size_t o) const;
+    Slice slice_h(size_t o) const;
+    Slice rslice_t(size_t no) const;
+    Slice rslice_h(size_t no) const;
+    Slice islice_t(iterator it) const;
+    Slice islice_h(iterator it) const;
+    Slice lslice(size_t o, size_t l) const;
+    Slice pslice(size_t b, size_t e) const;
+    Slice islice(iterator b, iterator e) const;
+};
+
+# include "slice.tcc"
+
+#endif // TMWA_GENERIC_SLICE_HPP
diff --git a/src/range/slice.tcc b/src/range/slice.tcc
new file mode 100644
index 0000000..3a1ceb5
--- /dev/null
+++ b/src/range/slice.tcc
@@ -0,0 +1,216 @@
+//    strings/base.tcc - Inline functions for strings/base.hpp
+//
+//    Copyright © 2013 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 <cassert>
+
+#include <algorithm>
+
+// simple pointer-wrapping iterator
+template<class T>
+class Slice<T>::iterator
+{
+    typedef iterator X;
+
+    T *_ptr;
+public:
+    typedef ptrdiff_t difference_type;
+    typedef T value_type;
+    typedef T *pointer;
+    typedef T& reference;
+    typedef std::random_access_iterator_tag iterator_category;
+
+    iterator(T *p=nullptr) : _ptr(p) {}
+
+    // iterator
+    reference operator *() const { return *_ptr; }
+    X& operator ++() { ++_ptr; return *this; }
+    // equality comparable
+    friend bool operator == (X l, X r) { return l._ptr == r._ptr; }
+    // input iterator
+    friend bool operator != (X l, X r) { return !(l == r); }
+    pointer operator->() const { return _ptr; }
+    X operator++ (int) { X out = *this; ++*this; return out; }
+    // forward iterator is mostly semantical, and the ctor is above
+    // bidirectional iterator
+    X& operator --() { --_ptr; return *this; }
+    X operator-- (int) { X out = *this; --*this; return out; }
+    // random access iterator
+    X& operator += (difference_type n) { _ptr += n; return *this; }
+    friend X operator + (X a, difference_type n) { return a += n; }
+    friend X operator + (difference_type n, X a) { return a += n; }
+    X& operator -= (difference_type n) { _ptr -= n; return *this; }
+    friend X operator - (X a, difference_type n) { return a -= n; }
+    friend difference_type operator - (X b, X a) { return b._ptr - a._ptr; }
+    reference operator[](difference_type n) const { return _ptr[n]; }
+    friend bool operator < (X a, X b) { return a._ptr < b._ptr; }
+    friend bool operator > (X a, X b) { return b < a; }
+    friend bool operator >= (X a, X b) { return !(a < b); }
+    friend bool operator <= (X a, X b) { return !(a > b); }
+};
+
+template<class T>
+Slice<T>::Slice(std::nullptr_t) : _begin(nullptr), _end(nullptr)
+{}
+
+template<class T>
+Slice<T>::Slice(T *b, T *e) : _begin(b), _end(e)
+{}
+
+template<class T>
+Slice<T>::Slice(T *b, size_t l) : _begin(b), _end(b + l)
+{}
+
+template<class T>
+template<class U, typename>
+Slice<T>::Slice(Slice<U> o) : _begin(o.data()), _end(o.data() + o.size())
+{}
+
+template<class T>
+template<size_t n, typename>
+Slice<T>::Slice(T (&arr)[n]) : _begin(arr), _end(arr + n)
+{}
+
+template<class T>
+Slice<T>::Slice(std::vector<T>& vec) : _begin(&*vec.begin()), _end(&*vec.end())
+{}
+
+
+template<class T>
+typename Slice<T>::iterator Slice<T>::begin() const
+{
+    return _begin;
+}
+
+template<class T>
+typename Slice<T>::iterator Slice<T>::end() const
+{
+    return _end;
+}
+
+template<class T>
+T *Slice<T>::data() const
+{
+    return _begin;
+}
+
+template<class T>
+size_t Slice<T>::size() const
+{
+    return _end - _begin;
+}
+
+template<class T>
+Slice<T>::operator bool() const
+{
+    return _begin != _end;
+}
+
+template<class T>
+bool Slice<T>::operator not() const
+{
+    return _begin == _end;
+}
+
+template<class T>
+T& Slice<T>::front() const
+{
+    return _begin[0];
+}
+
+template<class T>
+T& Slice<T>::back() const
+{
+    return _end[-1];
+}
+
+template<class T>
+T& Slice<T>::pop_front()
+{
+    ++_begin;
+    return _begin[0 - 1];
+}
+
+template<class T>
+T& Slice<T>::pop_back()
+{
+    --_end;
+    return _end[-1 + 1];
+}
+
+template<class T>
+T& Slice<T>::operator[](size_t o)
+{
+    assert (o < size());
+    return _begin[o];
+}
+
+
+template<class T>
+Slice<T> Slice<T>::slice_t(size_t o) const
+{
+    return Slice(_begin + o, _end);
+}
+
+template<class T>
+Slice<T> Slice<T>::slice_h(size_t o) const
+{
+    return Slice(_begin, _begin + o);
+}
+
+template<class T>
+Slice<T> Slice<T>::rslice_t(size_t no) const
+{
+    return Slice(_end - no, _end);
+}
+
+template<class T>
+Slice<T> Slice<T>::rslice_h(size_t no) const
+{
+    return Slice(_begin, _end - no);
+}
+
+template<class T>
+Slice<T> Slice<T>::islice_t(iterator it) const
+{
+    return Slice(&*it, _end);
+}
+
+template<class T>
+Slice<T> Slice<T>::islice_h(iterator it) const
+{
+    return Slice(_begin, &*it);
+}
+
+template<class T>
+Slice<T> Slice<T>::lslice(size_t o, size_t l) const
+{
+    return Slice(_begin + o, _begin + o + l);
+}
+
+template<class T>
+Slice<T> Slice<T>::pslice(size_t b, size_t e) const
+{
+    return Slice(_begin + b, _begin + e);
+}
+
+template<class T>
+Slice<T> Slice<T>::islice(iterator b, iterator e) const
+{
+    return Slice(&*b, &*e);
+}
diff --git a/src/range/slice_test.cpp b/src/range/slice_test.cpp
new file mode 100644
index 0000000..5bd2748
--- /dev/null
+++ b/src/range/slice_test.cpp
@@ -0,0 +1,89 @@
+#include "slice.hpp"
+
+#include <gtest/gtest.h>
+
+TEST(slice, slice)
+{
+    int init[] = {1, 2, 3, 4, 5};
+
+    Slice<int> slice(std::begin(init), std::end(init));
+    EXPECT_EQ(slice.data(), init);
+    EXPECT_EQ(slice.size(), 5);
+
+    Slice<int> head = slice.slice_h(2);
+    Slice<int> tail = slice.slice_t(2);
+    EXPECT_EQ(head.size(), 2);
+    EXPECT_EQ(tail.size(), 3);
+    EXPECT_EQ(head.front(), 1);
+    EXPECT_EQ(head.back(), 2);
+    EXPECT_EQ(tail.front(), 3);
+    EXPECT_EQ(tail.back(), 5);
+
+    head = slice.rslice_h(3);
+    tail = slice.rslice_t(3);
+    EXPECT_EQ(head.size(), 2);
+    EXPECT_EQ(tail.size(), 3);
+    EXPECT_EQ(head.front(), 1);
+    EXPECT_EQ(head.back(), 2);
+    EXPECT_EQ(tail.front(), 3);
+    EXPECT_EQ(tail.back(), 5);
+
+    head = slice.islice_h(slice.begin() + 2);
+    tail = slice.islice_t(slice.end() - 3);
+    EXPECT_EQ(head.size(), 2);
+    EXPECT_EQ(tail.size(), 3);
+    EXPECT_EQ(head.front(), 1);
+    EXPECT_EQ(head.back(), 2);
+    EXPECT_EQ(tail.front(), 3);
+    EXPECT_EQ(tail.back(), 5);
+
+    tail = slice.lslice(1, 3);
+    EXPECT_EQ(tail.size(), 3);
+    EXPECT_EQ(tail.front(), 2);
+    EXPECT_EQ(tail.back(), 4);
+
+    tail = slice.pslice(1, 4);
+    EXPECT_EQ(tail.size(), 3);
+    EXPECT_EQ(tail.front(), 2);
+    EXPECT_EQ(tail.back(), 4);
+
+    tail = slice.islice(slice.begin() + 1, slice.end() - 1);
+    EXPECT_EQ(tail.size(), 3);
+    EXPECT_EQ(tail.front(), 2);
+    EXPECT_EQ(tail.back(), 4);
+
+    head = slice;
+    while (head)
+    {
+        size_t headsize = head.size();
+        EXPECT_EQ(head.back(), headsize);
+        EXPECT_EQ(head.pop_back(), headsize);
+    }
+
+    tail = slice;
+    while (!!tail)
+    {
+        size_t tailsize = tail.size();
+        EXPECT_EQ(tail.front(), 6 - tailsize);
+        EXPECT_EQ(tail.pop_front(), 6 - tailsize);
+    }
+}
+
+TEST(slice, cast)
+{
+    struct Foo
+    {
+        int x;
+    };
+    struct Bar : Foo
+    {
+    };
+
+    Bar bars[2] = {Bar(), Bar()};
+
+    Slice<Bar> slice(bars, 2);
+    Slice<Foo> foos(slice);
+
+    EXPECT_EQ(foos.size(), slice.size());
+    EXPECT_EQ(&foos.end()[-1], &slice.end()[-1]);
+}
diff --git a/src/strings/base.hpp b/src/strings/base.hpp
index cd8d46f..c5181da 100644
--- a/src/strings/base.hpp
+++ b/src/strings/base.hpp
@@ -31,6 +31,8 @@
 // than would probably be necessary in an ideal language.
 namespace strings
 {
+    // TODO reimplement some things in terms of Slice and Slice::iterator?
+
     // simple pointer-wrapping iterator that can be used to get distinct
     // types for different containers.
     template<class Tag>
diff --git a/src/strings/pair.hpp b/src/strings/pair.hpp
index a519ddb..a592a91 100644
--- a/src/strings/pair.hpp
+++ b/src/strings/pair.hpp
@@ -28,9 +28,7 @@
 namespace strings
 {
     // TODO instead typedef ranges::Contiguous<const char>
-    // or whatever it becomes once it exists.
-    // const_array is just a hack, as evidenced by the fact
-    // that it's not really const.
+    // or whatever it becomes once it exists (probably grown from Slice).
     class XPair
     {
         const char *_begin;
-- 
cgit v1.2.3-70-g09d2