コード例 #1
0
ファイル: Room.cs プロジェクト: Alan-Baylis/Paramancer
    // Dig a maze
    static void do_maze(room rp)
    {
        S.Log("do_maze()");
        //SPOT sp;
        int   starty, startx;
        Coord pos = new Coord();

        for (int i = 0; i < R14.NUMLINES / 3; i++)
        {
            for (int j = 0; j < R14.NUMCOLS / 3; j++)
            {
                maze[i, j] = new SPOT();
            }
        }

        Maxy   = rp.Size.y;
        Maxx   = rp.Size.x;
        Starty = rp.PosUL.y;
        Startx = rp.PosUL.x;
        starty = (R14.rnd(rp.Size.y) / 2) * 2;
        startx = (R14.rnd(rp.Size.x) / 2) * 2;
        pos.y  = starty + Starty;
        pos.x  = startx + Startx;
        putpass(pos);
        dig(starty, startx);
    }
コード例 #2
0
ファイル: Chase.cs プロジェクト: Alan-Baylis/Paramancer
    // find the proper destination for the monster
    public static Coord find_dest(THING tp)
    {
        THING obj;
        int   prob = R14.monsters[tp.MonsType - 'A'].CarryProb;

        if ((prob) <= 0 || tp.CurrRoom == Agent.Plyr.CurrRoom || see_monst(tp))
        {
            return(R14.hero);
        }
        for (obj = Dungeon.ItemList; obj != null; obj = R14.next(obj))
        {
            if (obj.ItemType == Char.SCROLL && obj.Which == Scroll.S_SCARE)
            {
                continue;
            }
            if (Dungeon.roomin(obj.ItemPos) == tp.CurrRoom && R14.rnd(100) < prob)
            {
                for (tp = Dungeon.MobList; tp != null; tp = R14.next(tp))
                {
                    if (tp.t_dest.Equals(obj.ItemPos))
                    {
                        break;
                    }
                }
                if (tp == null)
                {
                    return(obj.ItemPos);
                }
            }
        }
        return(R14.hero);
    }
コード例 #3
0
ファイル: Chase.cs プロジェクト: Alan-Baylis/Paramancer
    // Return TRUE if the hero can see the monster
    public static bool see_monst(THING mp)
    {
        int y, x;

        if (R14.on(Agent.Plyr, ISBLIND))
        {
            return(false);
        }
        if (R14.on(mp, ISINVIS) && !R14.on(Agent.Plyr, CANSEE))
        {
            return(false);
        }

        y = mp.t_pos.y;
        x = mp.t_pos.x;
        if (dist(y, x, R14.hero.y, R14.hero.x) < R14.LAMPDIST)
        {
            if (y != R14.hero.y && x != R14.hero.x &&
                !Dungeon.walkable___step_ok(Dungeon.GetCharAt(y, R14.hero.x)) &&
                !Dungeon.walkable___step_ok(Dungeon.GetCharAt(R14.hero.y, x)))
            {
                return(false);
            }
            return(true);
        }

        if (mp.CurrRoom != Agent.Plyr.CurrRoom)
        {
            return(false);
        }

        return((bool)!((mp.CurrRoom.Flags & Room.ISDARK) == Room.ISDARK));
    }
コード例 #4
0
    static public void Wield(THING t)
    {
        THING  oW;    //old weapon
        string sp;

        oW = Agent.CurrWeapon;

        if (!Item.IsDetachable(Agent.CurrWeapon))
        {
            Agent.CurrWeapon = oW;
            return;
        }

        Agent.CurrWeapon = oW;


        sp = Item.GetDescription(t);
        Agent.CurrWeapon = t;

        if (!Agent.Terse)
        {
            R14.addmsg("You are now ");
        }
        R14.AddToLog("wielding {0} ({1})", sp, t.CharInPack);
    }
コード例 #5
0
    static public void Teleport()
    {
        Coord c = new Coord();

        Dungeon.find_floor(null, out c, 0, true);
        if (Dungeon.roomin(c) != Agent.Plyr.CurrRoom)
        {
            Dungeon.leave_room(R14.hero);
            R14.hero = c;
            Dungeon.enter_room(R14.hero);
        }
        else
        {
            R14.hero = c;
            R14.glanceAroundHero___look(true);
        }

        //let teleportation break being "held"/rooted      apparently only a Flytrap can root you?
        if (R14.on(Agent.Plyr, Mob.ISHELD))
        {
            Agent.Plyr.t_flags               &= ~Mob.ISHELD;
            Agent.NumTimesFlytrapHasHit       = 0;
            R14.monsters['F' - 'A'].Stats.Dmg = "000x0";
        }
        Agent.no_move = 0;
        Agent.running = false;
    }
コード例 #6
0
    //was 'fallpos'
    static public bool PickRndPosAroundYX(Coord o, Coord n)
    {
        int y, x, cnt, ch;

        cnt = 0;
        for (y = o.y - 1; y <= o.y + 1; y++)
        {
            for (x = o.x - 1; x <= o.x + 1; x++)
            {
                //make certain the spot is empty.
                //if it is, put the object there, set it in the level list, & and re-draw the room if he can see it
                if (y == R14.hero.y && x == R14.hero.x)
                {
                    continue;
                }
                if (((ch = Dungeon.GetCharAt(y, x)) == Char.FLOOR || ch == Char.PASSAGE) &&
                    R14.rnd(++cnt) == 0)
                {
                    n.y = y;
                    n.x = x;
                }
            }
        }
        return((bool)(cnt != 0));
    }
コード例 #7
0
    // setup stone settings
    static Ring()
    {
        S.Log("Ring stones");
        int i, j;

        for (i = 0; i < NSTONES; i++)
        {
            used[i] = false;
        }

        for (i = 0; i < Ring.MAX; i++)
        {
            do
            {
                j = R14.rnd(NSTONES);
            }while
            (used[j]);

            used[j]        = true;
            StonesUsed[i]  = StonesPossible[j].st_name;
            Data[i].Worth += StonesPossible[j].st_value;

            string s = StonesUsed[i][0].ToString().ToUpper();
            s /***/ += StonesUsed[i].Substring(1) + " (" + Data[i].Worth + ")";
            S.Log(s);
        }
    }
コード例 #8
0
    static public bool HeroCanSee(int y, int x)
    {
        room rer;
        var  tp = new Coord();

        if (R14.on(Agent.Plyr, Mob.ISBLIND))
        {
            return(false);
        }
        if (dist(y, x, R14.hero.y, R14.hero.x) < R14.LAMPDIST)
        {
            if ((flat(y, x) & F_PASS) != 0)
            {
                if
                (y != R14.hero.y &&
                 x != R14.hero.x &&
                 !walkable___step_ok(GetCharAt(y, R14.hero.x)) &&
                 !walkable___step_ok(GetCharAt(R14.hero.y, x))
                )
                {
                    return(false);
                }
            }

            return(true);
        }

        //we can only see if the hero in the same room as the coordinate and the room is lit or if it is close.
        tp.y = y;
        tp.x = x;
        return((bool)((rer = roomin(tp)) == Agent.Plyr.CurrRoom && !((rer.Flags & Room.ISDARK) != 0)));
    }
コード例 #9
0
 // Give a pack to a monster if it deserves one
 public static void give_pack(THING tp)
 {
     if (Agent.LevelOfMap >= Agent.dyn___max_level && R14.rnd(100) < monsters[tp.MonsType - 'A'].CarryProb)
     {
         attach(ref tp.Pack, GetNewRandomThing());
     }
 }
コード例 #10
0
    // Add a door or possibly a secret door.  Also enters the door in
    // the exits array of the room.
    static void door(room rm, Coord cp)
    {
        PLACE pp;

        rm.Exits[rm.NumExits++] = cp;

        if ((rm.Flags & Room.ISMAZE) == Room.ISMAZE)
        {
            return;
        }

        pp = PlaceAt(cp.y, cp.x);
        if (R14.rnd(10) + 1 < Agent.LevelOfMap && R14.rnd(5) == 0)
        {
            if (cp.y == rm.PosUL.y || cp.y == rm.PosUL.y + rm.Size.y - 1)
            {
                pp.p_ch = '-';
            }
            else
            {
                pp.p_ch = '|';
            }
            pp.p_flags &= ~Dungeon.F_REAL;
        }
        else
        {
            pp.p_ch = Char.DOOR;
        }
    }
コード例 #11
0
    // Set up the initial goodies for a weapon
    public static void Init(THING weap, int which)
    {
        init_weaps iwp;

        weap.ItemType            = Char.WEAPON;
        weap.Which               = which;
        iwp                      = init_dam[which];
        weap.swing___o_damage    = iwp.iw_dam;
        weap.o_hurldmg           = iwp.iw_hrl;
        weap.neededTo___o_launch = iwp.iw_launch;
        weap.o_flags             = iwp.iw_flags;
        weap.PlusToHit           = 0;
        weap.PlusToDmg           = 0;

        if (which == DAGGER)
        {
            weap.Count   = R14.rnd(4) + 2;
            weap.o_group = group++;
        }
        else if ((weap.o_flags & Item.ISMANY) == Item.ISMANY)
        {
            weap.Count   = R14.rnd(8) + 8;
            weap.o_group = group++;
        }
        else
        {
            weap.Count   = 1;
            weap.o_group = 0;
        }
    }
コード例 #12
0
ファイル: Agent.cs プロジェクト: Alan-Baylis/Paramancer
    static public void LevelUpPlayerMaybe(Vector2 v)
    {
        int goal = Agent.ExperienceGoals[Agent.Plyr.Stats.Level - 1];

        if (Agent.Plyr.Stats.Xp >= goal)
        {
            Agent.Plyr.Stats.Xp -= goal;             //keeping leftover xp

            int ol = Agent.Plyr.Stats.Level; /* old XpLevel */ Agent.Plyr.Stats.Level++;
            int nl = Agent.Plyr.Stats.Level;             /* new XpLevel */

            Agent.LeveledUp = DateTime.Now;
            //S.Rend.Particles.Add(new renderer.Particle("Level " + Agent.Plyr.Stats.Level, v, Vector2.One * 0.75f, Vector2.UnitY/10, DateTime.Now.AddSeconds(3), S.Yellow) );

            if (nl > ol)
            {
                int add = R14.RollDice(nl - ol, 10);
                Agent.Plyr.Stats.HpMax += add;
                Agent.Plyr.Stats.Hp    += add;
                S.Log("Welcome to level " + nl, Color.yellow);
                S.Log("Health +" + add, Color.green);
                //S.Audio.Play("Powerup2", 4);
            }
        }
    }
コード例 #13
0
    // Does the missile hit the monster?
    static bool hit_monster(int y, int x, THING obj)
    {
        Coord mp = new Coord();

        mp.y = y;
        mp.x = x;
        return(R14.PlayerAttacks(mp, obj, true));
    }
コード例 #14
0
        internal void PopFrame(UIntPtr *framePointer,
                               UIntPtr calleeSaveMask,
                               bool framePointerOmitted,
                               bool hasTransitionRecord)
        {
            UIntPtr *calleeSaveStart;

            if (framePointerOmitted)
            {
                calleeSaveStart = framePointer - 1;
            }
            else
            {
                VTable.Assert((calleeSaveMask & 0x100) == 0,
                              "EBP should not be callee saved");
                calleeSaveStart = framePointer;
                EBP.PopFrameReg(ref calleeSaveStart);
            }
            if (hasTransitionRecord)
            {
                calleeSaveStart -=
                    sizeof(CallStack.TransitionRecord) / sizeof(UIntPtr);
            }

            // Note: the order in which these appear is important!
            if ((calleeSaveMask & 0x1) != 0)
            {
                EBX.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x80) != 0)
            {
                EBP.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x4) != 0)
            {
                ESI.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x2) != 0)
            {
                EDI.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x8) != 0)
            {
                R12.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x10) != 0)
            {
                R13.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x20) != 0)
            {
                R14.PopFrameReg(ref calleeSaveStart);
            }
            if ((calleeSaveMask & 0x40) != 0)
            {
                R15.PopFrameReg(ref calleeSaveStart);
            }
        }
コード例 #15
0
    //aggravate all the monsters on this level
    public static void aggravate()
    {
        THING mp;

        for (mp = MobList; mp != null; mp = R14.next(mp))
        {
            S.Log("*** NEED TO DO: 'runto(mp.t_pos)' in aggravate()");
        }
    }
コード例 #16
0
ファイル: Fight.cs プロジェクト: Alan-Baylis/Paramancer
    // Called to put a monster to death
    static public void killed(THING tp, bool pr)
    {
        string mname;

        Agent.Plyr.Stats.Xp += tp.Stats.Xp;

        // if the monster was a venus flytrap, un-hold him
        switch (tp.MonsType)
        {
        case 'F':
            Agent.Plyr.t_flags           &= ~Mob.ISHELD;
            Agent.NumTimesFlytrapHasHit   = 0;
            monsters['F' - 'A'].Stats.Dmg = "000x0";
            break;

        case 'L':
        {
            THING gold;

            if (Weapon.PickRndPosAroundYX(tp.t_pos, tp.CurrRoom.GoldPos) &&
                Agent.LevelOfMap >= Agent.dyn___max_level)
            {
                gold           = new THING();
                gold.ItemType  = Char.GOLD;
                gold.o_goldval = GOLDCALC();

                if (save(VS_MAGIC))
                {
                    gold.o_goldval += GOLDCALC() + GOLDCALC() + GOLDCALC() + GOLDCALC();
                }

                attach(ref tp.Pack, gold);
            }
        }
        break;
        }

        // get rid of the monster
        mname = R14.GetMonsterName(tp);
        remove_mon(tp.t_pos, tp, true);

        if (pr)
        {
            if (!Agent.Terse)
            {
                addmsg("you have ");
            }

            addmsg("defeated ");
            R14.AddToLog(mname);
        }

        Agent.LevelUpPlayerMaybe(Agent.Pos);
    }
コード例 #17
0
    // Pick a room that is really there
    public static int rnd_room()
    {
        int rm;

        do
        {
            rm = R14.rnd(MAXROOMS);
        } while ((rooms[rm].Flags & Room.ISGONE) == Room.ISGONE);

        return(rm);
    }
コード例 #18
0
 internal void ClearCalleeSaves()
 {
     EBX.ClearCalleeReg();
     ESI.ClearCalleeReg();
     EDI.ClearCalleeReg();
     EBP.ClearCalleeReg();
     R12.ClearCalleeReg();
     R13.ClearCalleeReg();
     R14.ClearCalleeReg();
     R15.ClearCalleeReg();
 }
コード例 #19
0
 internal void SetCalleeSaves(SpillContext *context)
 {
     EBX.SetCalleeReg(&context->bx);
     EDI.SetCalleeReg(&context->di);
     ESI.SetCalleeReg(&context->si);
     EBP.SetCalleeReg(&context->bp);
     R12.SetCalleeReg(&context->r12);
     R13.SetCalleeReg(&context->r13);
     R14.SetCalleeReg(&context->r14);
     R15.SetCalleeReg(&context->r15);
 }
コード例 #20
0
 void SetCalleeSaves(CalleeSaveRegistersX64 *calleeSaveRegisters)
 {
     EBX.SetCalleeReg(&calleeSaveRegisters->EBX);
     EDI.SetCalleeReg(&calleeSaveRegisters->EDI);
     ESI.SetCalleeReg(&calleeSaveRegisters->ESI);
     EBP.SetCalleeReg(&calleeSaveRegisters->EBP);
     R12.SetCalleeReg(&calleeSaveRegisters->R12);
     R13.SetCalleeReg(&calleeSaveRegisters->R13);
     R14.SetCalleeReg(&calleeSaveRegisters->R14);
     R15.SetCalleeReg(&calleeSaveRegisters->R15);
 }
コード例 #21
0
 internal void ScanLiveRegs(UIntPtr mask,
                            NonNullReferenceVisitor referenceVisitor)
 {
     EDI.ScanLiveReg((mask >> 2) & 0x3, referenceVisitor);
     ESI.ScanLiveReg((mask >> 4) & 0x3, referenceVisitor);
     EBX.ScanLiveReg((mask >> 0) & 0x3, referenceVisitor);
     R12.ScanLiveReg((mask >> 6) & 0x3, referenceVisitor);
     R13.ScanLiveReg((mask >> 8) & 0x3, referenceVisitor);
     R14.ScanLiveReg((mask >> 10) & 0x3, referenceVisitor);
     R15.ScanLiveReg((mask >> 12) & 0x3, referenceVisitor);
     EBP.ScanLiveReg((mask >> 14) & 0x3, referenceVisitor);
 }
コード例 #22
0
    public const int MAXTRIES = 10;     /* max number of tries to put down a monster */

    // Add a treasure room
    public static void treas_room()
    {
        int   nm;
        THING tp;
        room  rp;
        int   spots, num_monst;
        var   mp = new Coord();

        rp    = rooms[rnd_room()];
        spots = (rp.Size.y - 2) * (rp.Size.x - 2) - MINTREAS;
        if (spots > (MAXTREAS - MINTREAS))
        {
            spots = (MAXTREAS - MINTREAS);
        }
        num_monst = nm = R14.rnd(spots) + MINTREAS;

        while (nm-- != 0)
        {
            find_floor(rp, out mp, 2 * MAXTRIES, false);
            tp         = R14.GetNewRandomThing();
            tp.ItemPos = mp;
            R14.attach(ref Dungeon.ItemList, tp);
            Dungeon.SetCharAt(mp.y, mp.x, (char)tp.ItemType);
        }

        //fill up room with monsters from the next level down
        if ((nm = R14.rnd(spots) + MINTREAS) < num_monst + 2)
        {
            nm = num_monst + 2;
        }
        spots = (rp.Size.y - 2) * (rp.Size.x - 2);
        if (nm > spots)
        {
            nm = spots;
        }

        Agent.LevelOfMap++;

        while (nm-- != 0)
        {
            spots = 0;

            if (find_floor(rp, out mp, MAXTRIES, true))
            {
                tp = new THING();
                R14.new_monster(tp, R14.randmonster(false), mp);
                tp.t_flags |= Mob.ISMEAN;       /* no sloughers in THIS room */
                R14.give_pack(tp);
            }
        }

        Agent.LevelOfMap--;
    }
コード例 #23
0
    static public void UseSlot(int slot)
    {
        int i = -1;

        for (THING t = Agent.Plyr.Pack; t != null; t = R14.next(t))
        {
            i++;

            if (slot == i && IsDetachable(t))
            {
                Use(t);
            }
        }
    }
コード例 #24
0
    // populate map with items
    public static void put_things()
    {
        int   i;
        THING obj;

        /* Once you have found the amulet, the only way to get new stuff is go down into the dungeon */
        if (Agent.amulet && Agent.LevelOfMap < Agent.dyn___max_level)
        {
            return;
        }
        /* check for treasure rooms, and if so, put it in */
        if (R14.rnd(TREAS_ROOM) == 0)
        {
            treas_room();
        }

        // make items
        for (i = 0; i < MAXOBJ; i++)
        {
            int roll = R14.rnd(100);
            //Console.WriteLine("rolled " + roll);
            if (roll < 36)
            {
                obj = R14.GetNewRandomThing();
                R14.attach(ref Dungeon.ItemList, obj);
                find_floor(null, out obj.ItemPos, 0, false);
                SetCharAt(obj.ItemPos.y, obj.ItemPos.x, (char)obj.ItemType);
            }
        }

        // place amulet... if far in enough & hasn't found
        if (Agent.LevelOfMap >= AMULETLEVEL && !Agent.amulet)
        {
            obj = new THING();
            R14.attach(ref ItemList, obj);
            obj.PlusToHit        = 0;
            obj.PlusToDmg        = 0;
            obj.swing___o_damage = "0x0";
            obj.o_hurldmg        = "0x0";
            obj.Ac       = 11;
            obj.ItemType = Char.AMULET;

            /*
             * Put it somewhere
             */
            find_floor(null, out obj.ItemPos, 0, false);
            Dungeon.SetCharAt(obj.ItemPos.y, obj.ItemPos.x, Char.AMULET);
        }
    }
コード例 #25
0
    // add a passage character or secret passage here
    static void putpass(Coord c)
    {
        PLACE pp;

        pp          = PlaceAt(c.y, c.x);
        pp.p_flags |= F_PASS;
        if (R14.rnd(10) + 1 < Agent.LevelOfMap && R14.rnd(40) == 0)
        {
            pp.p_flags &= ~F_REAL;
        }
        else
        {
            pp.p_ch = Char.PASSAGE;
        }
    }
コード例 #26
0
    // Find the unclaimed object at y, x
    public static THING find_obj(int y, int x)
    {
        THING obj;

        for (obj = ItemList; obj != null; obj = R14.next(obj))
        {
            if (obj.ItemPos.y == y && obj.ItemPos.x == x)
            {
                return(obj);
            }
        }

        /* NOTREACHED */
        return(null);
    }
コード例 #27
0
    public static int pick_one(ObjectData[] info, int nitems)
    {
        int roll = R14.rnd(100);
        int j    = 0;

        foreach (var item in info)
        {
            if (roll < item.Probability)
            {
                return(j);
            }

            j++;
        }
        return(0);
    }
コード例 #28
0
    // Pick a new monster and add it to the list
    public static void new_monster(THING tp, char type, Coord cp)
    {
        monster mp;
        int     lev_add;

        if ((lev_add = Agent.LevelOfMap - Dungeon.AMULETLEVEL) < 0)
        {
            lev_add = 0;
        }

        attach(ref Dungeon.MobList, tp);
        tp.MonsType   = type;
        tp.t_disguise = type;
        tp.t_pos      = cp;
        //here we used to move (cursor?) to hero Coord
        tp.CurrRoom = Dungeon.roomin(cp);
        Dungeon.SetMonster(cp.y, cp.x, tp);
        mp             = monsters[tp.MonsType - 'A'];
        tp.Stats       = new stats();
        tp.Stats.Level = mp.Stats.Level + lev_add;
        tp.Stats.HpMax = tp.Stats.Hp = RollDice(tp.Stats.Level, 8);
        tp.Stats.Ac    = mp.Stats.Ac - lev_add;
        tp.Stats.Dmg   = mp.Stats.Dmg;
        tp.Stats.Str   = mp.Stats.Str;
        tp.Stats.Xp    = mp.Stats.Xp + lev_add * 10 + getFactorConsideringLevelAndHpMax(tp);
        tp.t_flags     = mp.Flags;
        if (Agent.LevelOfMap > 29)
        {
            tp.t_flags |= Mob.ISHASTE;
        }
        tp.t_turn = true;
        tp.Pack   = null;

        if (Ring.ISWEARING(Ring.R_AGGR))
        {
            Mob.runto(cp);
        }
        if (type == 'X')
        {
            tp.t_disguise = R14.rnd_thing();
        }

        Console.Write("new_monster() = " + GetMonsterName(tp) + "     ");
        Console.Write("Level: " + tp.Stats.Level);
        Console.WriteLine("     HpMax: " + tp.Stats.HpMax);
    }
コード例 #29
0
    // Do special checks for dropping or unweilding|unwearing|unringing
    //static bool dropcheck(THING t)
    static public bool IsDetachable(THING t)
    {
        if (t == null)
        {
            return(true);    //don't think this is needed anymore, since we're reacting to something thats there already
        }
        //if not wearable
        if (t != Agent.cur_armor && t != Agent.CurrWeapon &&
            t != Agent.cur_ring[Ring.LEFT] && t != Agent.cur_ring[Ring.RIGHT])
        {
            return(true);
        }

        if ((t.o_flags & IsCursed) != 0)
        {
            S.Log("You can't remove " + t.Name + ".  It appears to be cursed");
            return(false);
        }

        if (t == Agent.CurrWeapon)
        {
            Agent.CurrWeapon = null;
        }
        else if (t == Agent.cur_armor)
        {
            //waste_time();
            Agent.cur_armor = null;
        }
        else
        {
            //remove ring's magic effects
            Agent.cur_ring[t == Agent.cur_ring[Ring.LEFT] ? Ring.LEFT : Ring.RIGHT] = null;
            switch (t.Which)
            {
            case Ring.R_ADDSTR:
                R14.ModifyStrength(-t.Ac);
                break;

            case Ring.R_SEEINVIS:
                R14.unsee(0);
                R14.extinguish(R14.unsee);
                break;
            }
        }
        return(true);
    }
コード例 #30
0
    // Set up order for list
    static void set_order(int[] order, int numthings)
    {
        int i, r, t;

        for (i = 0; i < numthings; i++)
        {
            order[i] = i;
        }

        for (i = numthings; i > 0; i--)
        {
            r            = R14.rnd(i);
            t            = order[i - 1];
            order[i - 1] = order[r];
            order[r]     = t;
        }
    }