// Function from file: spellbook.dm
        public override dynamic Topic(string href = null, ByTable href_list = null, dynamic hsrc = null)
        {
            Mob            H      = null;
            SpellbookEntry E      = null;
            int            result = 0;

            base.Topic(href, href_list, (object)(hsrc));
            H = Task13.User;

            if (H.stat != 0 || H.restrained())
            {
                return(null);
            }

            if (!(H is Mob_Living_Carbon_Human))
            {
                return(1);
            }

            if (H.mind.special_role == "apprentice")
            {
                this.temp = "If you got caught sneaking a peak from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not.";
                return(null);
            }
            E = null;

            if (this.loc == H || Map13.GetDistance(this, H) <= 1 && this.loc is Tile)
            {
                H.set_machine(this);

                if (Lang13.Bool(href_list["buy"]))
                {
                    E = this.entries[String13.ParseNumber(href_list["buy"])];

                    if (E != null && E.CanBuy(H, this))
                    {
                        if (E.Buy(H, this))
                        {
                            if (Lang13.Bool(E.limit))
                            {
                                E.limit--;
                            }
                            this.uses -= E.cost;
                        }
                    }
                }
                else if (Lang13.Bool(href_list["refund"]))
                {
                    E = this.entries[String13.ParseNumber(href_list["refund"])];

                    if (E != null && E.refundable)
                    {
                        result = E.Refund(H, this);

                        if (result > 0)
                        {
                            if (!(E.limit == null))
                            {
                                E.limit += result;
                            }
                            this.uses += result;
                        }
                    }
                }
                else if (Lang13.Bool(href_list["page"]))
                {
                    this.tab = GlobalFuncs.sanitize(href_list["page"]);
                }
            }
            this.attack_self(H);
            return(null);
        }
        // Function from file: spellbook.dm
        public override dynamic attack_self(dynamic user = null, dynamic flag = null, bool?emp = null)
        {
            string         dat        = null;
            ByTable        cat_dat    = null;
            dynamic        category   = null;
            SpellbookEntry E          = null;
            int?           i          = null;
            string         spell_info = null;
            dynamic        category2  = null;


            if (!Lang13.Bool(this.owner))
            {
                user.WriteMsg("<span class='notice'>You bind the spellbook to yourself.</span>");
                this.owner = user;
                return(null);
            }

            if (user != this.owner)
            {
                user.WriteMsg("<span class='warning'>The " + this.name + " does not recognize you as it's owner and refuses to open!</span>");
                return(null);
            }
            ((Mob)user).set_machine(this);
            dat     = "";
            dat    += "<ul id=\"tabs\">";
            cat_dat = new ByTable();

            foreach (dynamic _a in Lang13.Enumerate(this.categories))
            {
                category = _a;

                cat_dat[category] = "<hr>";
                dat += new Txt("<li><a ").item((this.tab == category ? "class=selected" : "")).str(" href='byond://?src=").Ref(this).str(";page=").item(category).str("'>").item(category).str("</a></li>").ToString();
            }
            dat += "<li><a><b>Points remaining : " + this.uses + "</b></a></li>";
            dat += "</ul>";
            E    = null;
            i    = null;
            i    = 1;

            while ((i ?? 0) <= this.entries.len)
            {
                spell_info  = "";
                E           = this.entries[i];
                spell_info += E.GetInfo();

                if (E.CanBuy(user, this))
                {
                    spell_info += new Txt("<a href='byond://?src=").Ref(this).str(";buy=").item(i).str("'>").item(E.buy_word).str("</A><br>").ToString();
                }
                else
                {
                    spell_info += "<span>Can't " + E.buy_word + "</span><br>";
                }

                if (E.CanRefund(user, this))
                {
                    spell_info += new Txt("<a href='byond://?src=").Ref(this).str(";refund=").item(i).str("'>Refund</A><br>").ToString();
                }
                spell_info += "<hr>";

                if (Lang13.Bool(cat_dat[E.category]))
                {
                    cat_dat[E.category] += spell_info;
                }
                i++;
            }

            foreach (dynamic _b in Lang13.Enumerate(this.categories))
            {
                category2 = _b;

                dat += "<div class=\"" + (this.tab == category2 ? "tabContent" : "tabContent hide") + "\" id=\"" + category2 + "\">";
                dat += this.GetCategoryHeader(category2);
                dat += cat_dat[category2];
                dat += "</div>";
            }
            Interface13.Browse(user, this.wrap(dat), "window=spellbook;size=700x500");
            GlobalFuncs.onclose(user, "spellbook");
            return(null);
        }