Implementing Zig proposal from Wiki


Questions, Explanations, Howtos

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Tuesday, 2nd July 2013, 06:12

Implementing Zig proposal from Wiki

Couple of questions in no particular order
- can monsters use portal projectile if so which spell slot? also slots for blinking? See code -- alas it seems they cannot
- how/where is the ziggurat code called?
- what is the difference between you.depth and env.absdepth0?
- how to disable cTele on a floor? :arrow: using lua for this
- how to waken monsters (other than with noise)? :arrow: set using BEH_WANDER
- how to make * | and "good scrolls" items? :arrow: also using lua



Differences from ziggurat.lua
C++
(eventually) more types of layout
glass windows/wall to see outside (still todo: lightening and birds ect.)
Xom and Chei floors (WiP)
Mutagenic floor (don't kill me)
no same floors per zig -32 different floors
monster weights slightly different

Still Todo
-hd pool --done
-loot chamber for slime floors -- using lua
-copy the hexagon , circle and ellipse floor generators --using lua
  Code:
/**
 * @file
 * @brief The dungeon builder for Ziggurats.
 **/

#include "AppHdr.h"

#include "lua.h"
#include "coordit.h"
#include "dungeon.h"
#include "items.h"
#include "mapmark.h"
#include "maps.h"
#include "mgen_data.h"
#include "misc.h"
#include "mon-place.h"
#include "mon-pick.h"
#include "colour.h"

#include "terrain.h"

#include "dlua.h"
#include "cluautil.h"
#include "l_defs.h"

enum zig_floor_t
{
    //elemental
    ZIGF_AIR,
    ZIGF_EARTH,
    ZIGF_FIRE,
    ZIGF_ICE,
   
    //location
    ZIGF_ORC,
    ZIGF_ELF,
    ZIGF_DWARF,
   
    ZIGF_LAIR,
    ZIGF_SHOAL,
    ZIGF_SNAKE,
    ZIGF_SPIDER,
    ZIGF_SWAMP,
    ZIGF_SLIME,
   
    ZIGF_VAULTS,
    ZIGF_FOREST,
    ZIGF_CRYPT,
    ZIGF_TOMB,
   
    ZIGF_GEHENNA,
    ZIGF_COCYTIS,
    ZIGF_DIS,
    ZIGF_TARTARUS,
   
    ZIGF_ABYSS,
    ZIGF_ZOT,
    ZIGF_PAN,
   
    //monster sets
    ZIGF_DRACONIAN,
    ZIGF_DRAGON,
    ZIGF_RANGED,
    ZIGF_GIANT,
    ZIGF_HOLY,
    ZIGF_MUTAGENIC,
   
    //gods not (necessarily) represented in the above
    ZIGF_XOM,
    ZIGF_CHEI,
//    ZIGF_FEDHAS,
//    ZIGF_NEMELEX,
//    ZIGF_OKAWARU,
    NUM_ZIGF
   
};

static struct {
    zig_floor_t floor;
    int tot_weights;
    level_id lid;
    int lid_wieght;
    int orig_hd_pool;
    int hdpool;
    bool chamber;// slime foors
    bool genawake;
} _floors[27];
static bool water;
static int current_floor;//0-26
static int eccentricity;//0-100
static int tod; // 1 == halo, 0 == nothing, -1 == night (umbra,gloom?)
static int _mon_weights[MAX_MONS_ALLOC];

static void _random_xom_mons( coord_def at, bool awake);
static void _chei_mons(monster* mon);
static void _loot(coord_def at );
static zig_floor_t _random_floor_for_level()
{
    int i;
    bool done;
    do
    {
        done = true;
        i = random2(NUM_ZIGF);
        for (int j=0; j<current_floor; j++) {
            done = done && (_floors[i].floor != i);
           
           
        }
    } while (!done);
    return  _floors[current_floor].floor = (zig_floor_t)i;
};

static const char* zigf2string(zig_floor_t floor)
{
    switch (floor) {
           
        case ZIGF_AIR:
            return "Air";
        case ZIGF_ABYSS:
            return "Abyss";
        case ZIGF_CHEI:
            return "Chei";
        case ZIGF_COCYTIS:
            return "Coc";
        case ZIGF_CRYPT:
            return "Crypt";
        case ZIGF_DIS:
            return "Dis";
        case ZIGF_DRACONIAN:
            return "Drac";
        case ZIGF_DRAGON:
            return "Dragon";
        case ZIGF_DWARF:
            return "Dwarf";
        case ZIGF_EARTH:
            return "Earth";
        case ZIGF_ELF:
            return "Elf";
        case ZIGF_FIRE:
            return "Fire";
        case ZIGF_FOREST:
            return "Forest";
        case ZIGF_GEHENNA:
            return "Geh";
        case ZIGF_GIANT:
            return "Giant";
        case ZIGF_HOLY:
            return "Holy";
        case ZIGF_ICE:
            return "Ice";
        case ZIGF_LAIR:
            return "Lair";
        case ZIGF_MUTAGENIC:
            return "Mut";
        case ZIGF_ORC:
            return "Orc";
        case ZIGF_PAN:
            return "Pan";
        case ZIGF_RANGED:
            return "Range";
        case ZIGF_SHOAL:
            return "Shoal";
        case ZIGF_SLIME:
            return "Slime";
        case ZIGF_SNAKE:
            return "Snake";
        case ZIGF_SPIDER:
            return "Spider";
        case ZIGF_SWAMP:
            return "Swamp";
        case ZIGF_TARTARUS:
            return "Tar";
        case ZIGF_TOMB:
            return "Tomb";
        case ZIGF_VAULTS:
            return "Vault";
        case ZIGF_XOM:
            return "Xom";
        case ZIGF_ZOT:
            return "Zot";
            break;
           
        default:
            return "";
            break;
    }
   
};
LUARET1( zig_floor_type , string, zigf2string(_floors[current_floor].floor));

vector<coord_def> build_isos_tri ( int level, int windows, bool mirrored);


LUAFN(zig_dump)
{
    dprf("Dumping zig info:");
    for (int i = 0 ; i < 27; i++)
    {
        dprf("--------------------------------");
        dprf("Floor: %d",i);
        dprf("Floor type: %s",zigf2string(_floors[i].floor));
        if (_floors[i].lid_wieght)
            dprf("Weight for native branch %d", _floors[i].lid_wieght);
        dprf("Total weight %d", _floors[i].lid_wieght +  _floors[i].tot_weights);
        dprf("Hd pool of %d",_floors[i].orig_hd_pool );
        dprf("Hd left in pool %d", _floors[i].hdpool);
        if (_floors[i].genawake)
            dprf("Monster generated awake");
        if (_floors[i].chamber)
            dprf("Generated a loot chamber");
    }
   
    return 1;
}

LUARET1(zig_tod, number , tod);
LUARET1(zig_exc, number , eccentricity);
LUAFN(zig_set_level)
{
   
    current_floor = luaL_checknumber(ls, 1);
    return 1;
}

LUAFN(zig_init)
{
    current_floor = env.absdepth0;
   
    for (int i=0; i < MAX_MONS_ALLOC; i++) {
        _mon_weights[i] = 0;
       
        if (current_floor == 0 ){
            _floors[i].floor = NUM_ZIGF;
            _floors[i].chamber = false;
           
        }
       
    }
    if (current_floor == 0)
    {
        water        = coinflip();
        eccentricity = random2(101);
        tod          = random2(3)-1;
    }
    _floors[current_floor].lid = level_id(NUM_BRANCHES);
    zig_floor_t type = _random_floor_for_level();
   
   
    _floors[current_floor].genawake  =  current_floor > 4 + random2(21);
    bool noctele =  current_floor > 6 + random2(19);
    /*if (noctele)
        //Todo: moredirect
        dlua.callfn("dgn.change_level_flags", "no_tele_control");
    */
    int  area    = 30 + (current_floor +18) * current_floor;
    _floors[current_floor].hdpool  = _floors[current_floor].orig_hd_pool = you.depth * (you.depth +8);

#define emaw(a,b,c) env.mons_alloc[a] = (monster_type) b;\
_mon_weights[a] = c;\
_floors[current_floor].tot_weights +=c
   
#define natives( weight ,place ) \
_floors[current_floor].lid_wieght= weight;\
_floors[current_floor].lid = level_id::parse_level_id(place)
   
    switch (type)
    {
        case ZIGF_AIR:
       
           
            emaw(0, MONS_INSUBSTANTIAL_WISP ,10);
            emaw(1, MONS_AIR_ELEMENTAL      ,10);
            emaw(2, MONS_VAPOUR             ,10);
            emaw(3, MONS_TITAN              ,10);
            emaw(4, MONS_STORM_DRAGON       ,10);
            emaw(5, MONS_ELECTRIC_GOLEM     ,10);
            emaw(6, MONS_SPRIGGAN_AIR_MAGE  ,10);
            break;
       
        case ZIGF_EARTH:
       
            emaw(0,  MONS_EARTH_ELEMENTAL    ,10);
            emaw(1,  MONS_CRYSTAL_GOLEM      ,10);
            emaw(2,  MONS_GARGOYLE           ,10);
            emaw(3,  MONS_GOLEM              ,10);
            emaw(4,  MONS_STONE_GIANT        ,10);
            emaw(5,  MONS_STONE_GOLEM        ,10);
            emaw(6,  MONS_METAL_GARGOYLE     ,10);
            emaw(7,  MONS_MOLTEN_GARGOYLE    ,10);
            emaw(8,  MONS_IRON_GOLEM         ,10);
            emaw(9,  MONS_IRON_DRAGON        ,10);
            emaw(10, MONS_IRON_TROLL         ,10);
            emaw(11, MONS_QUICKSILVER_DRAGON ,10);
       
            break;
        case ZIGF_FIRE:
       
            emaw(0, MONS_FIRE_ELEMENTAL ,10);
            emaw(1, MONS_FIRE_DRAKE     ,10);
            emaw(2, MONS_HELL_HOUND     ,10);
            emaw(3, MONS_EFREET         ,10);
            emaw(4, MONS_DRAGON         ,10);
            emaw(5, MONS_FIRE_GIANT     ,10);
            emaw(6, MONS_ORB_OF_FIRE    ,10);
           
            break;
       
        case ZIGF_ICE:
       
            emaw(0, MONS_ICE_BEAST       ,10);
            emaw(1, MONS_POLAR_BEAR      ,10);
            emaw(2, MONS_FREEZING_WRAITH ,10);
            emaw(3, MONS_ICE_DRAGON      ,10);
            emaw(4, MONS_FROST_GIANT     ,10);
            emaw(5, MONS_ICE_DEVIL       ,10);
            emaw(6, MONS_ICE_FIEND       ,10);
            emaw(7, MONS_SIMULACRUM      ,20);
            emaw(8, MONS_BLIZZARD_DEMON  ,10);
            break;
       
        case ZIGF_ORC:
       
            natives(310 - 10* you.depth, "Orc:$");
           
            emaw(0, MONS_ORC_WARLORD      ,10);
            emaw(1, MONS_ORC_HIGH_PRIEST  , 5);
            emaw(2, MONS_ORC_KNIGHT       ,10);
            emaw(3, MONS_ORC_SORCERER     , 5);
            emaw(4, MONS_STONE_GIANT      ,10);
            emaw(5, MONS_MOTH_OF_WRATH, max(0, you.depth - 20));
           
            break;
       
           
        case ZIGF_ELF:
       
            natives(300 - 10*you.depth, "Elf:$");
           
            emaw(0,  MONS_DEEP_ELF_SORCERER      ,10);
            emaw(1,  MONS_DEEP_ELF_BLADEMASTER   ,10);
            emaw(2,  MONS_DEEP_ELF_MASTER_ARCHER ,10);
            emaw(3,  MONS_DEEP_ELF_ANNIHILATOR   ,10);
            emaw(4,  MONS_DEEP_ELF_DEMONOLOGIST  ,10);
            emaw(5,  MONS_DEEP_ELF_DEATH_MAGE    ,10);
            emaw(6,  MONS_DEEP_ELF_HIGH_PRIEST   ,10);
            break;
       
        case ZIGF_DWARF:
            //natives(10 ,"Dwarf:$");
       
            emaw(0, MONS_DEEP_DWARF_SCION        ,10);
            emaw(1, MONS_DEEP_DWARF_NECROMANCER  ,10);
            emaw(2, MONS_DEEP_DWARF_BERSERKER    ,10);
            emaw(3, MONS_DEEP_DWARF              ,20);
            emaw(4, MONS_DEEP_DWARF_ARTIFICER    ,10);
            emaw(5, MONS_DEEP_DWARF_DEATH_KNIGHT ,10);
            emaw(6, MONS_UNBORN_DEEP_DWARF       ,10);
            break;
       
        case ZIGF_LAIR:
       
            natives(85, "Lair:$");
           
            emaw(0, MONS_CATOBLEPAS    ,6);
            emaw(1, MONS_DIRE_ELEPHANT ,6);
            emaw(2, MONS_HELLEPHANT    ,3);   
            break;
       
        case ZIGF_SHOAL:
            natives(125, "Shoal:$");
            emaw(0, MONS_MERFOLK_AQUAMANCER ,15);
            emaw(1, MONS_MERFOLK_IMPALER    , 4);
            break;
           
        case ZIGF_SNAKE:
            natives(90, "Snake:$");
           
            emaw(0, MONS_GREATER_NAGA      ,5);
            emaw(1, MONS_GUARDIAN_SERPENT  ,5);
            break;
           
        case ZIGF_SPIDER:
       
            natives(110, "Spider:$");
           
            emaw(0, MONS_GHOST_MOTH          ,15);
            emaw(1, MONS_RED_WASP            ,10);
            emaw(2, MONS_ORB_SPIDER          ,10);
            emaw(3, MONS_MOTH_OF_SUPPRESSION , 5);
           
            break;
       
        case ZIGF_SWAMP:
            natives(120, "Swamp:$");
            emaw(0, MONS_HYDRA        ,10);
            emaw(1, MONS_SWAMP_DRAGON ,10);
            emaw(2, MONS_GREEN_DEATH  , 6);
            emaw(3, MONS_DEATH_DRAKE  , 4);
            break;
           
        case ZIGF_SLIME:
            natives(10, "Slime:$");
            _floors[current_floor].chamber = true;
            break;
           
        case ZIGF_VAULTS:
       
            natives(60, "Vaults:$");
            emaw(0, MONS_ANCIENT_LICH, max(0 , you.depth - 12));
            break;
       
        case ZIGF_FOREST:
            natives(180, "Forest:$");
            emaw(0, MONS_SATYR            ,10);
            emaw(1, MONS_TENGU_REAVER     , 5);
            emaw(2, MONS_SPRIGGAN_DEFENDER, 5);
            break;
           
        case ZIGF_CRYPT:
            natives(180, "Crypt:$");
            emaw(0, MONS_VAMPIRE_KNIGHT   ,14);
            emaw(1, MONS_LICH             , 3);
            emaw(2, MONS_UNBORN_DEEP_DWARF, 2);
            emaw(3, MONS_CURSE_TOE        , 1);
            break;
           
        case ZIGF_TOMB:
            natives(200, "Tomb:$");
            emaw(0,MONS_GREATER_MUMMY , you.depth +5);
            break;
           
        case ZIGF_GEHENNA:
            natives(460, "Geh:$");
            emaw(0, MONS_BRIMSTONE_FIEND ,10);
            emaw(1, MONS_BALRUG          ,30);
            break;
           
        case ZIGF_COCYTIS:
            natives(460, "Coc:$");
            emaw(0, MONS_ICE_FIEND      ,10);
            emaw(1, MONS_BLIZZARD_DEMON ,30);
            break;
           
        case ZIGF_DIS:
            natives(460, "Dis:$");
            emaw(0, MONS_HELL_SENTINEL  ,10);
            emaw(1, MONS_IRON_DRAGON    ,20);
            emaw(2, MONS_DANCING_WEAPON ,10);
            break;
           
        case ZIGF_TARTARUS:
            natives(460, "Tar:$");
            emaw(0, MONS_SHADOW_FIEND ,10);
            emaw(1, MONS_SHADOW_DEMON ,20);
            emaw(2, MONS_CURSE_TOE    ,10);
            break;
           
        case ZIGF_ABYSS:
            natives(10, "Abyss");
            break;
           
        case ZIGF_ZOT:
            natives(10, "Zot:$");
            break;
           
        case ZIGF_PAN:
            natives(45, "Pan");
            emaw(0, MONS_PANDEMONIUM_LORD ,  max(0, you.depth -5 ));
           
        case ZIGF_DRACONIAN:
            // see zig_mons(coord_def,bool)
            break;
           
        case ZIGF_DRAGON:
       
            emaw(0,  MONS_SWAMP_DRAKE        , 5);
            emaw(1,  MONS_FIRE_DRAKE         , 5);
            emaw(2,  MONS_DEATH_DRAKE        ,10);
            emaw(3,  MONS_STEAM_DRAGON       , 5);
            emaw(4,  MONS_SWAMP_DRAGON       ,10);
            emaw(5,  MONS_DRAGON             ,10);
            emaw(6,  MONS_ICE_DRAGON         ,10);
            emaw(7,  MONS_STORM_DRAGON       ,10);
            emaw(8,  MONS_IRON_DRAGON        ,10);
            emaw(9,  MONS_SHADOW_DRAGON      ,10);
            emaw(10, MONS_QUICKSILVER_DRAGON ,10);
            emaw(11, MONS_GOLDEN_DRAGON      ,10);
            emaw(12, MONS_MOTTLED_DRAGON     ,10);
            emaw(13, MONS_WYVERN             ,10);
            emaw(14, MONS_HYDRA              ,10);
            break;
       
        case ZIGF_RANGED:
       
       
            emaw(0, MONS_CENTAUR                ,10);
            emaw(1, MONS_YAKTAUR                ,10);
            emaw(3, MONS_CENTAUR_WARRIOR        ,10);
            emaw(4, MONS_YAKTAUR_CAPTAIN        ,10);
            emaw(5, MONS_CYCLOPS                ,10);
            emaw(6, MONS_STONE_GIANT            ,10);
            emaw(7, MONS_MERFOLK_JAVELINEER     ,10);
            emaw(8, MONS_DEEP_ELF_MASTER_ARCHER ,10);
            break;
       
        case ZIGF_GIANT:
            emaw(0, MONS_HILL_GIANT   ,10);
            emaw(1, MONS_CYCLOPS      ,10);
            emaw(2, MONS_STONE_GIANT  ,10);
            emaw(3, MONS_FIRE_GIANT   ,10);
            emaw(4, MONS_FROST_GIANT  ,10);
            emaw(5, MONS_ETTIN        ,10);
            emaw(6, MONS_TITAN        ,10);
           
            break;
           
        case ZIGF_HOLY:
            emaw(0, MONS_DAEVA        ,10);
            emaw(1, MONS_ANGEL        ,10);
            emaw(2, MONS_CHERUB       ,10);
            emaw(3, MONS_PEARL_DRAGON ,10);
            emaw(4, MONS_OPHAN        ,10);
            emaw(5, MONS_APIS         ,10);
            emaw(6, MONS_PALADIN      ,10);
            emaw(7, MONS_PHOENIX      , 5);
            emaw(8, MONS_SILVER_STAR  , 5);
            emaw(9, MONS_SERAPH       , 3);
            break;
           
        case ZIGF_MUTAGENIC:
            emaw(0, MONS_POLYMOTH             ,10);
            emaw(1, MONS_NEQOXEC              ,10);
            emaw(2, MONS_CACODEMON            ,10);
            emaw(3, MONS_SHINING_EYE          ,10);
            emaw(4, MONS_SHAPESHIFTER         ,20);
            emaw(5, MONS_GLOWING_SHAPESHIFTER ,20);
            emaw(6, MONS_UGLY_THING           ,10);
            emaw(7, MONS_VERY_UGLY_THING      ,10);
            emaw(8, MONS_GUARDIAN_SERPENT     ,10);
            emaw(9, MONS_WRETCHED_STAR        , 4);
            break;
           
        case ZIGF_XOM:
            // for the sake of a non emaw weighting
            // doesn't matter where
            //see _xom_mons for the rest
            natives(80 , "D:1");
            emaw(0, MONS_SHAPESHIFTER          ,10);
            emaw(1, MONS_GLOWING_SHAPESHIFTER  ,10);
            emaw(2, MONS_UGLY_THING            ,10);
            emaw(3, MONS_VERY_UGLY_THING       ,10);
            emaw(4, MONS_KILLER_KLOWN          ,10);
           
            break;

        case ZIGF_CHEI:
            //all these have haste stripped in _chei_mons
            emaw(0,  MONS_ELEPHANT_SLUG         ,10);
            emaw(1,  MONS_GREATER_NAGA          ,10);
            emaw(2,  MONS_CYCLOPS               ,10);
            emaw(3,  MONS_CRYSTAL_GOLEM         ,10);
            emaw(4,  MONS_NAGA_MAGE             ,10);
            emaw(5,  MONS_BOULDER_BEETLE        ,10);
            emaw(6,  MONS_SATYR                 ,10);
            emaw(7,  MONS_YAKTAUR_CAPTAIN       ,10);
            emaw(8,  MONS_DEEP_TROLL_EARTH_MAGE ,10);
            emaw(9,  MONS_VAMPIRE_MAGE          ,10);
            emaw(10, MONS_ANCIENT_LICH          , 5);
            emaw(11, MONS_GARGOYLE              , 5);
            emaw(12, MONS_SPHINX                , 5);
            emaw(13, MONS_GUARDIAN_MUMMY        ,10);
            break;
/*
 
        case ZIGF_FEDHAS:
            break;
        case ZIGF_NEMELEX:
            break;
        case ZIGF_OKAWARU:
            break;
*/
        case NUM_ZIGF:
        default:
            //die?
            break;
    };
#undef natives
#undef emaw   
    /*vector<coord_def> (*build)(int , int , bool )
            = random_choose_weighted(1, &build_isos_tri,
                                     0, &build_equi_tri,
                                     0, &build_gold_rect,
                                    -1);
    vector<coord_def> loot_and_mons = build(current_floor,random2(4),current_floor % 2);
    for (int i =0; i< loot_and_mons.size(); i++)
    {
        //zig_monster( loot_and_mons[i], awake);
        //_loot(loot_and_mons[i]);
    }
    mprf("hd pool for floor %d:%d",current_floor,_floors[current_floor].hdpool);
     */
    return 1;
}

LUARET1(zig_surrounds, string, water ? "water" : "lava");
LUAFN(zig_monster)
{
   
    coord_def at = coord_def(luaL_checkint(ls, 1),luaL_checkint(ls, 2));
    const bool awake = _floors[current_floor].genawake;
    monster_type mon = MONS_0;
   
    bool xom_retype = false;
    bool pool_adjusted;
    const int lidw = _floors[current_floor].lid_wieght;
    if (x_chance_in_y(lidw ,lidw +_floors[current_floor].tot_weights ))
    {
        mon = pick_monster(_floors[current_floor].lid);
        xom_retype = true;
    }
    else if (_floors[current_floor].floor == ZIGF_DRACONIAN)
        mon = random_draconian_monster_species();
    //XXX: there has to be a better way to do this
    else
    {
       
#define weight(a) _mon_weights[(a)],env.mons_alloc[(a)]
#define aweights(a) weight(a) , weight(a+1),weight(a+2),weight(a+3)
        mon = random_choose_weighted(aweights(0),
                                     aweights(4),
                                     aweights(8),
                                     aweights(12),
                                     aweights(16),
                                   
                                     -1);
#undef aweights
#undef weight
       
    }
   
    ASSERT(mon != MONS_0);
    mgen_data mg(mon);
   
    if (_floors[current_floor].lid.branch != NUM_BRANCHES)
    {
        mg.place = _floors[current_floor].lid;
    }
    monster * mons;
    mg.pos = at;
    mg.flags |=  MG_FORCE_PLACE;
    if (awake)
        mg.behaviour = BEH_WANDER;
    else
        mg.behaviour = BEH_SLEEP;
    if (_floors[current_floor].floor == ZIGF_XOM && xom_retype)
    {
         _random_xom_mons( at, awake);
    }
    else
    {
        if (mg.hd > _floors[current_floor].hdpool)
        {
            lua_pushboolean(ls, 0);
            return 1;
        }
        mons = place_monster(mg, true,false);
    }
    if (_floors[current_floor].floor == ZIGF_CHEI)
    {
        _chei_mons(mons);
        pool_adjusted = true;
    }
   
    if (!pool_adjusted)
        _floors[current_floor].hdpool -= mons->hit_dice;
    lua_pushboolean(ls, 1);
    return 1;
}
// Todo: add Chimeras!!!! (and Xom effects)
void _random_xom_mons( coord_def at,  bool awake)
{
    monster_type newmon = pick_monster_no_rarity(
                            //Todo: add more branches and weight properly
                            random_choose_weighted(10,  BRANCH_MAIN_DUNGEON,
                                                   10,  BRANCH_PANDEMONIUM,
                                                   -1));
    mgen_data mg(newmon);
    monster* mon;
    mg.pos = at;
   
    //invert hd
    //Todo: better inversion
    //hd 3 OoF anyone? how about hd 33 rat with forestorm?
    if (coinflip())
        mg.hd = max(3, 30-mg.hd);
    if (one_chance_in(5))
    {
        mg.hd += random2(5);
    }
    //Don't exceed the hd pool for this floor
    mg.hd = min(mg.hd , _floors[current_floor].hdpool);
   
    mg.flags |=  MG_FORCE_PLACE;
    if (awake)
        mg.behaviour = BEH_WANDER;
    else
        mg.behaviour = BEH_SLEEP;
    mon = place_monster(mg, true,false);
   
    mon->god = GOD_XOM;
   
    int num_spells = 0;
    if (!mon->flags & M_FAKE_SPELLS)
    {
        for (int i=0; i<6; i++)
        {
            if (mon->spells[i] != SPELL_NO_SPELL && mon->spells[i]!= SPELL_MELEE)
                num_spells++;
        }
    }
   
    //Todo: add more random spell appropriate for
    //  -Xom
    //  -Correct spell slot
    //*storm is currently a place holder
    static const spell_type X1[]=
    {
        SPELL_FIRE_STORM,
        SPELL_MAGIC_DART,
        SPELL_LEHUDIBS_CRYSTAL_SPEAR,
        SPELL_THROW_FROST,
        SPELL_CHAIN_LIGHTNING,
        SPELL_BOLT_OF_INACCURACY
       
       
    };
    static const spell_type X2[]=
    {
        SPELL_ICE_STORM,
       
    };
    static const spell_type X3[]=
    {
        SPELL_SHATTER,
           
           
    };
    static const spell_type X4[]=
    {
        SPELL_TORNADO,
     
    };
    static const spell_type X5[]=
    {
        SPELL_TOMB_OF_DOROKLOHE,
       
    };
    static const spell_type X6[]=
    {
        SPELL_SUMMON_BUTTERFLIES,
       
    };
    static const spell_type* Xom_spells[6]=
    {
        (spell_type*)&X1,
        (spell_type*)&X2,
        (spell_type*)&X3,
        (spell_type*)&X4,
        (spell_type*)&X5,
        (spell_type*)&X6,
           
    };
   
       
    if (coinflip())
    {
        num_spells = 6 - num_spells;
        for (int i=0; i<6; i++)
        {
            if (x_chance_in_y(num_spells  , 6))
                mon->spells[i] = RANDOM_ELEMENT(Xom_spells[i]);
        }
        mon->flags |= M_SPELLCASTER;
    }
   
    //Todo: Set more flags?
    if (one_chance_in(10))
    {
        mon->flags |= M_INVIS;
    }
    if (one_chance_in(10))
    {
        mon->flags |= M_SEE_INVIS;
    }
    if (one_chance_in(10))
    {
        mon->flags |= M_BATTY;
    }
    //Todo: meddle with resists
    //brimstone fiend with rF-?
    //Xom roars with laughter
   
   
    //swap AC/ev
    if (coinflip())
    {
        int tmp = mon->ac;
        mon->ac = mon->ev;
        mon->ev = tmp;
    }
   
}

void _chei_mons(monster * mon)
{
    _floors[current_floor].hdpool -= mon->hit_dice;
    int mod =2 + random2(4);
    mon->speed -= mod;
    mon->speed = max(mon->speed, 3);//3?
    mon->hit_dice += mod - 1;
   
    mon->hit_dice= max(mon->hit_dice, 33);
    // should be interesting when monsters get invocations
    // need to disable slouch or make it affect only you and your allies
    // also limit the (ab)use of step from time
    mon->god = GOD_CHEIBRIADOS;
    if (mon->can_use_spells() )
    {
        for (int i=0; i<6; i++)
        {
            if (mon->spells[i] == SPELL_HASTE)
                mon->spells[i] = mon->is_fighter() ? SPELL_BLINK_CLOSE     :
                                 mon->is_archer()  ? SPELL_BLINK_AWAY      :
                                                     SPELL_CONTROLLED_BLINK;
            else if (mon->spells[i] == SPELL_HASTE_OTHER)
                mon->spells[i] = coinflip() ? SPELL_SLOW
                                            : SPELL_MIGHT_OTHER;
           
            if (mon->is_archer() && i == 4  //4? Correct slot
                                 && one_chance_in(3)) //Too nasty?
            {
                mon->spells[i] = SPELL_PORTAL_PROJECTILE;
            }
           
            if (i==3 && // 3?
                (mon->spells[i]== SPELL_NO_SPELL ||
                 mon->spells[i] ==SPELL_MELEE )
                && one_chance_in(3))
            {
                    mon->spells[i] = SPELL_BLINK_CLOSE;
            }
        }
       
    }
   
}


vector<coord_def> build_isos_tri(int level, int windows, bool mirrored)
{
    coord_def top = coord_def( (GXM-1)/2 - ((GXM-1)/2)*level/27, GYM/2);
    if (mirrored)
        top  = coord_def(GXM,0) - top;
    const int baseline = GXM -top.x;
    const int height = level* 3 -1;
    const int width  = level * 2;
    bool last_window = false;
    vector<coord_def> ret;
    int minx,maxx;
   
    for (int i = 0; i < GXM;  i++)
    {
        for (int j = 0; j < GYM; j++)
        {
            //if it's outside the triangle
            //below the base or above the top
            if ((mirrored &&
                 ((i < baseline -1 || i > top.x +1)
                //in between the base and top but still outside
                || abs(j-35) +1 >  i *width/height))
               
                ||
               
                ((i > baseline +1 || i < top.x -1)
                || abs(j-GYM/2) +1 >  i *width/height))
               
            {
                grd[i][j] = water ? DNGN_DEEP_WATER : DNGN_LAVA;
                env.pgrid[i][j] |= FPROP_NO_TELE_INTO;
                //Todo: sunlight/nighttime (halo and umbra)
                //Todo: birds and lightning
            }
            //else if it's on the border
            else if ((mirrored && (i *width/height >  abs(j-GYM/2) - 1
                    && i *width/height < abs(j-GYM/2) + 1))
                    ||
                    ((GXM-i) *width/height > abs(j-GYM/2) - 1 &&
                    (GXM-i) *width/height < abs(j-GYM) + 1))

            {
                //clump windows together
                if ((last_window && coinflip())
                    || one_chance_in(windows+1))
                {
                    last_window = true;
                    grd[i][j] = DNGN_CLEAR_PERMAROCK_WALL;
                }
                else
                {
                    last_window = false;
                    grd[i][j] = DNGN_PERMAROCK_WALL;
                }
            }
            //else it's inside
            else
            {
                grd[i][j] = DNGN_FLOOR;
                if (j < minx)
                    minx = j;
                else if (j > maxx)
                    maxx = j;
                   
                //if (i,j) is on the two rows next to the base line
                // here be loot and monsters
                if (i - baseline >0 && i-baseline < 3)
                    ret.push_back(coord_def(i,j));
            }
            if (i==0 || j==0 || i== GXM || j== GYM)
            {
                grd[i][j] =  water ? DNGN_OPEN_SEA : DNGN_LAVA_SEA;
            }
           
           
        }
       
    }
       
   
    grd(top) = DNGN_STONE_ARCH;
    grd[baseline +1][GYM/2] = DNGN_STONE_STAIRS_DOWN_I;
    ASSERT(minx!=maxx);
    //TODO: Portal to next zig floor
    //grd[baseline +(mirrored ? 1:-1)][minx] = level == 26 ? DNGN_FLOOR : DNGN_PORTAL_ZIGGURAT;
    //grd[baseline +(mirrored ? 1:-1)][maxx] = level == 26 ? DNGN_FLOOR : DNGN_?
    return ret;
   
}

static const struct luaL_reg zig_dlib[] =
{
    {"exc"       , zig_exc        },
    {"tod"       , zig_tod        },
    {"set_level" , zig_set_level  },
    {"dump"      , zig_dump       },
    {"surrounds" , zig_surrounds  },
    {"floor_type", zig_floor_type },
    {"monster"   , zig_monster    },
    {"init"      , zig_init       },
    {NULL,NULL}
};


void dluaopen_zig(lua_State *ls)
{
    luaL_openlib(ls, "zig", zig_dlib, 0);
}


Last edited by Grandiloquent Gentleman on Friday, 5th July 2013, 05:21, edited 3 times in total.

Vestibule Violator

Posts: 1567

Joined: Friday, 21st January 2011, 22:56

Post Tuesday, 2nd July 2013, 14:28

Re: Implimenting Zig proposal from Wiki

What proposal?

Zig code is in /dat/dlua/ziggurat.lua. Not sure how it is called. To disable cTele, check how it is done in ziggurat.lua. You can generate monsters awake, I think with the generate_awake flag, but I'm not sure. You make * and | with * and |. Probably mumra or bh can tell you more about dungeon building in C++, including whether it makes sense.
User avatar

Dungeon Master

Posts: 291

Joined: Wednesday, 6th June 2012, 18:59

Post Tuesday, 2nd July 2013, 14:53

Re: Implimenting Zig proposal from Wiki

The proposal you're looking at is from 2011. A lot has changed since then and many of the suggestions there no longer apply.

If you want to start up a discussion on zigs again, I would start from scratch; my current perception is that not a lot needs to change there right now.
Last edited by sgrunt on Tuesday, 2nd July 2013, 23:09, edited 1 time in total.
User avatar

Dungeon Master

Posts: 762

Joined: Thursday, 25th April 2013, 02:43

Post Tuesday, 2nd July 2013, 21:53

Re: Implementing Zig proposal from Wiki

sgrunt wrote:The proposal you're looking at is from 2011. A lot has changed since then and many of the suggestions there no longer apply.

If you want to start up a discussion on zigs again, I would start from scratch; my current perception is that not a lot needs to change there right now.
Grunt, you sort of accidentally linked to this: Image
Here is the actual proposal.
On IRC my nick is reaverb. I play online under the name reaver, though.

For this message the author reaver has received thanks:
Grandiloquent Gentleman

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Wednesday, 3rd July 2013, 00:08

Re: Implementing Zig proposal from Wiki

I think there are some good ideas here and it's great that someone wants to look at overhauling Zig code. Since that discussion is pretty old and in particular a lot of change were made recently by HangedMan, it might be worth dropping into the ##crawl-dev IRC channel to get some feedback off more people.

One advantage of keeping things in Lua would be you could take advantage of various shape/layout functions I've been working on, although maybe they could still be interfaced somehow.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Wednesday, 3rd July 2013, 13:45

Re: Implementing Zig proposal from Wiki

would interfacing the C++ code to Lua work?
(I have absolutely no idea how to do this, better check out l_*.cc files then :roll: )

that way I wont need to work out how to do the portals and * and | items
also saves me from finding out where to call the code from :D

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Wednesday, 3rd July 2013, 18:34

Re: Implementing Zig proposal from Wiki

Basically instead of modifying the grid directly like you're doing here, instead you can have functions that accept a reference to a mapdef, and modify glyphs on that mapdef instead. This is how much of the existing API works.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Thursday, 4th July 2013, 12:13

Re: Implementing Zig proposal from Wiki

OK, after looking through various l_*.cc files I have one more
- What is the difference between clua and dlua and which one should this library be? currently a dlua
(See OP for updated code)

Also please help come up with more absurd spells for Xom floor :D
and various todos in the code - TiA
Last edited by Grandiloquent Gentleman on Thursday, 4th July 2013, 12:29, edited 2 times in total.

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Thursday, 4th July 2013, 12:20

Re: Implementing Zig proposal from Wiki

Yep, dlua is right; that's the internally-used API, whereas clua can be used in e.g. player configs; dungeon building functions have to stay out of there otherwise players could cheat riotously ;)

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Thursday, 4th July 2013, 14:18

Re: Implementing Zig proposal from Wiki

OK, how do I add a new file to the project so that I can make it? add to the make file? (Don't know how)
for now I'll simply tack it onto the bottom of another.

(shadowed static non-static declaration error later, not really fixed)

OK, it's complied happily (without kicking and screaming) but it's crashing whenever I try to enter a Zig
and get the rather unhelpful message "Unable to generate level for 'Zig:1'!"
What are the make options for debugging, to make that message somewhat more helpful?
I am assuming that it is the lua side of things that are screwing up here and not the c++, so is there a way of
keeping crawl running and enter the lua interpreter to see if I can find out whats going on and preferably
get a lua stack trace?

edit: never mind I was trying to declare a function zig() with a library also named zig
fixed this and now getting trap 6 Wipheee! with even less info than before
Hmm. The zig.monster(x,y) is working fine as is the zig.init()

here's the ziggurat.lua see if you can spot any glaring errors
  Code:
------------------------------------------------------------------------------
-- ziggurat.lua:
--
-- Code for ziggurats.
--
-- Important notes:
-- ----------------
-- Functions that are attached to Lua markers' onclimb properties
-- cannot be closures, because Lua markers must be saved and closure
-- upvalues cannot (yet) be saved.
------------------------------------------------------------------------------

require("dlua/lm_toll.lua")

-- Deepest you can go in a ziggurat - at this point it's beyond
-- obvious that we're not challenging the player, and one could hope
-- she has enough loot by now.
ZIGGURAT_MAX = 27

function ziga()
  if not dgn.persist.ziggurat then
    dgn.persist.ziggurat = { entry_fee = 0 }
    -- Initialise here to handle ziggurats accessed directly by &P.
    initialise_ziggurat(dgn.persist.ziggurat)
  end
  return dgn.persist.ziggurat
end

local wall_colours = {
  "blue", "red", "lightblue", "magenta", "green", "white"
}

function ziggurat_wall_colour()
  return util.random_from(wall_colours)
end

function initialise_ziggurat(z, portal)
  if portal then
    z.entry_fee = portal.props.amount
  end

  -- Any given ziggurat will use the same builder for all its levels,
  -- and the same colours for outer walls. Before choosing the builder,
  -- we specify a global excentricity. If zig_exc=0, then the ellipses
  -- will be circles etc. It is not the actual excentricity but some
  -- value between 0 and 100. For deformed ellipses and rectangles, make
  -- sure that the map is wider than it is high for the sake of ASCII.
  zig.init()
  --z.zig_exc = crawl.random2(101)
  z.builder = ziggurat_choose_builder()
  z.colour = ziggurat_wall_colour()
end

function callback.ziggurat_initialiser(portal)
  dgn.persist.ziggurat = { }
  initialise_ziggurat(dgn.persist.ziggurat, portal)
end

-- Common setup for ziggurat entry vaults.
function ziggurat_portal(e, spawnrange)
  local d = crawl.roll_dice
  local entry_fee =
    10 * math.floor(200 + d(3,200) / 3 + d(10) * d(10) * d(10))

  local function stair()
    return toll_stair {
      amount = entry_fee,
      toll_desc = "to enter a ziggurat",
      desc = "gateway to a ziggurat",
      overview = "Ziggurat",
      overview_note = "" .. entry_fee .. " gp",
      dst = "Zig:1",
      dstname = "ziggurat",
      floor = "stone_arch",
      feat_tile = "dngn_portal_ziggurat_gone",
      onclimb = "callback.ziggurat_initialiser"
    }
  end

  if spawnrange == "shallow" then
    e.tags("chance_shallow_zig extra")
    e.chance("1%")
  elseif spawnrange == "deep" then
    e.tags("chance_zig extra allow_dup luniq_zig")
    e.chance("5%")
  elseif spawnrange == "pan" then
    e.tags("chance_pan_zig extra allow_dup")
    e.chance("8%")
  end

  e.lua_marker("O", stair)
  e.kfeat("O = enter_portal_vault")
  e.tile("O = dngn_portal_ziggurat")
end

-- Common setup for ziggurat levels.
function ziggurat_level(e)
  e.tags("allow_dup")
  e.tags("no_dump")
  e.orient("encompass")
  zig.init()
  if crawl.game_started() then
    ziggurat_build_level(e)
  end
end

-----------------------------------------------------------------------------
-- Ziggurat level builders.

beh_wander = mons.behaviour("wander")

function ziggurat_awaken_all(mons)
  mons.beh = beh_wander
end

function ziggurat_build_level(e)
  local builder = ziga().builder

  -- Deeper levels can have all monsters awake.
  -- Does never happen at depths 1-4; does always happen at depths 25-27.
  local generate_awake = you.depth() > 4 + crawl.random2(21)
  ziga().monster_hook = generate_awake and global_function("ziggurat_awaken_all")

  -- Deeper levels may block controlled teleports.
  -- Does never happen at depths 1-6; does always happen at depths 25-27.
  if you.depth() > 6 + crawl.random2(19) then
    dgn.change_level_flags("no_tele_control")
  end

  if builder then
    return ziggurat_builder_map[builder](e)
  end
end

local zigstair = dgn.gridmark

-- the estimated total map area for ziggurat maps of given depth
-- this is (almost) independent of the layout type
local function map_area()
  return 30 + 18*you.depth() + you.depth()*you.depth()
end

local function clamp_in(val, low, high)
  if val < low then
    return low
  elseif val > high then
    return high
  else
    return val
  end
end

local function clamp_in_bounds(x, y)
  return clamp_in(x, 2, dgn.GXM - 3), clamp_in(y, 2, dgn.GYM - 3)
end

local function set_wall_tiles()
  local tileset = {
    blue      = "wall_zot_blue",
    red       = "wall_zot_red",
    lightblue = "wall_zot_cyan",
    magenta   = "wall_zot_magenta",
    green     = "wall_zot_green",
    white     = "wall_vault"
  }

  local wall = tileset[zig().colour]
  if (wall == nil) then
    wall = "wall_vault"
  end
  dgn.change_rock_tile(wall)
end

local function with_props(spec, props)
  local spec_table = type(spec) == "table" and spec or { spec = spec }
  return util.cathash(spec_table, props)
end

local function spec_fn(specfn)
  return { specfn = specfn }
end

local function spec_if(fn, spec)
  local spec_table = type(spec) == "table" and spec or { spec = spec }
  return util.cathash(spec_table, { cond = fn })
end

local function depth_ge(lev)
  return function ()
           return you.depth() >= lev
         end
end

local function depth_range(low, high)
  return function ()
           return you.depth() >= low and you.depth() <= high
         end
end

local function depth_lt(lev)
  return function ()
           return you.depth() < lev
         end
end

local function zig_monster_fn(spec)
  local mfn = dgn.monster_fn(spec)
  return function (x, y)
           local mons = mfn(x, y)
           if mons then
             local monster_hook = ziga().monster_hook
             if monster_hook then
               monster_hook(mons)
             end
           end
           return mons
         end
end

local function monster_creator_fn(arg)
  local atyp = type(arg)
  if atyp == "string" then
    local mcreator = zig_monster_fn(arg)

    local function mspec(x, y, nth)
      return mcreator(x, y)
    end
    return { fn = mspec, spec = arg }
  elseif atyp == "table" then
    if not arg.cond or arg.cond() then
      local spec = arg.spec or arg.specfn()
      return util.cathash(monster_creator_fn(spec), arg)
    end
  elseif atyp == "function" then
    return { fn = arg }
  end
end

local mons_populations = { }

local function mset(...)
  util.foreach({ ... }, function (spec)
                          table.insert(mons_populations, spec)
                        end)
end

local function mset_if(condition, ...)
  mset(unpack(util.map(util.curry(spec_if, condition), { ... })))
end

mset(with_props("place:Slime:$", { jelly_protect = true }),
     "place:Snake:$ w:90 / greater naga w:5 / guardian serpent w:5",
     with_props("place:Lair:$ w:85 / catoblepas w:6 / dire elephant w:6 / " ..
                "hellephant w:3", { weight = 5 }),
     "place:Spider:$ w:110 / ghost moth w:15 / red wasp / " ..
                "orb spider / moth of suppression w:5",
     "place:Crypt:$ w:180 / vampire knight w:14 / lich w:3 / " ..
                "unborn deep dwarf w:2 / curse toe w:1",
     "place:Forest:$ w:180 / satyr / tengu reaver w:5 / " ..
                "spriggan defender w:5",
     "place:Abyss",
     "place:Swamp:$ w:120 / hydra / swamp dragon / " ..
                "green death w:6 / death drake w:4",
     "place:Shoals:$ w:125 / merfolk aquamancer w:15 / merfolk impaler w:4 / " ..
                "merfolk javelineer w:4 / siren w:2",
     with_props("place:Coc:$ w:460 / Ice Fiend / " ..
                 "blizzard demon w:30", { weight = 5 }),
     with_props("place:Geh:$ w:460 / Brimstone Fiend / " ..
                 "balrug w:30", { weight = 5 }),
     with_props("place:Dis:$ w:460 / Hell Sentinel / " ..
                 "dancing weapon / iron dragon w:20", { weight = 5 }),
     with_props("place:Tar:$ w:460 / Shadow Fiend / " ..
                 "curse toe / shadow demon w:20", { weight = 5 }),
     with_props("daeva / angel / cherub / pearl dragon / shedu band / ophan / " ..
                "apis / paladin / w:5 phoenix / w:5 silver star", { weight = 2 }),
     with_props("hill giant / cyclops / stone giant / fire giant / " ..
                "frost giant / ettin / titan", { weight = 2 }),
     with_props("fire elemental / fire drake / hell hound / efreet / " ..
                "dragon / fire giant / orb of fire", { weight = 2 }),
     with_props("ice beast / polar bear / freezing wraith / ice dragon / " ..
                "frost giant / ice devil / ice fiend / simulacrum w:20 / " ..
                "blizzard demon", { weight = 2 }),
     with_props("insubstantial wisp / air elemental / vapour / titan / " ..
                "storm dragon / electric golem / spriggan air mage", { weight = 2 }),
     with_props("swamp drake / fire drake / death drake / steam dragon / " ..
                "swamp dragon / dragon / ice dragon / storm dragon / " ..
                "iron dragon / shadow dragon / quicksilver dragon / " ..
                "golden dragon / mottled dragon / wyvern / hydra", { weight = 2 }),
     with_props("centaur / yaktaur / centaur warrior / yaktaur captain / " ..
                "cyclops / stone giant / merfolk javelineer / " ..
                "deep elf master archer", { weight = 2 }))

-- spec_fn can be used to wrap a function that returns a monster spec.
-- This is useful if you want to adjust monster weights in the spec
-- wrt to depth in the ziggurat. At level-generation time, the spec
-- returned by this function will also be used to init the monster
-- population (with dgn.set_random_mon_list). As an example:
mset(spec_fn(function ()
               local d = math.max(0, you.depth() - 12)
               return "place:Vaults:$ w:60 / ancient lich w:" .. d
             end))

mset(spec_fn(function ()
               local d = math.max(0, you.depth() - 5)
               return "place:Pan w:45 / pandemonium lord w:" .. d
             end))

mset(spec_fn(function ()
               local d = you.depth() + 5
               return "place:Tomb:$ w:200 / greater mummy w:" .. d
             end))

mset(spec_fn(function ()
               local d = 300 - 10 * you.depth()
               return "place:Elf:$ w:" .. d .. " / deep elf sorcerer / " ..
                 "deep elf blademaster / deep elf master archer / " ..
                 "deep elf annihilator / deep elf demonologist"
             end))

mset(spec_fn(function ()
               local d = 310 - 10 * you.depth()
               local e = math.max(0, you.depth() - 20)
               return "place:Orc:$ w:" .. d .. " / orc warlord / orc knight / " ..
                 "orc high priest w:5 / orc sorcerer w:5 / stone giant / " ..
                 "moth of wrath w:" .. e
             end))


local drac_creator = zig_monster_fn("random draconian")
local function mons_drac_gen(x, y, nth)
  if nth == 1 then
    dgn.set_random_mon_list("random draconian")
  end
  return drac_creator(x, y)
end

local pan_lord_fn = zig_monster_fn("pandemonium lord")
local pan_critter_fn = zig_monster_fn("place:Pan")

local function mons_panlord_gen(x, y, nth)
  if nth == 1 then
    dgn.set_random_mon_list("place:Pan")
    return pan_lord_fn(x, y)
  else
    return pan_critter_fn(x, y)
  end
end

mset_if(depth_ge(6), mons_drac_gen)
mset_if(depth_ge(8), mons_panlord_gen)

function ziggurat_monster_creators()
  return util.map(monster_creator_fn, mons_populations)
end

local function ziggurat_vet_monster(fmap)
  local fn = fmap.fn
  fmap.fn = function (x, y, nth, hdmax)
              if nth >= dgn.MAX_MONSTERS then
                return nil
              end
              for i = 1, 10 do
                local mons = fn(x, y, nth)
                if mons then
                  -- Discard zero-exp monsters, and monsters that explode
                  -- the HD limit.
                  if mons.experience == 0 or mons.hd > hdmax * 1.3 then
                    mons.dismiss()
                  else
                    -- Monster is ok!
                    return mons
                  end
                end
              end
              -- Give up.
              return nil
            end
  return fmap
end

local function choose_monster_set()
  return ziggurat_vet_monster(
           util.random_weighted_from(
             'weight',
             ziggurat_monster_creators()))
end

-- Function to find travel-safe squares, excluding closed doors.
local dgn_passable = dgn.passable_excluding("closed_door")

local function ziggurat_create_monsters(p, mfn)
  local hd_pool = you.depth() * (you.depth() + 8)
-- (was depth * (depth + 8) before and too easy)

  local nth = 1

--  local function mons_do_place(p)
--    if hd_pool > 0 then
--      local mons = mfn(p.x, p.y, nth, hd_pool)
--
--      if mons then
--        nth = nth + 1
--       hd_pool = hd_pool - mons.hd
--
--        if nth >= dgn.MAX_MONSTERS then
--          hd_pool = 0
--        end
--      else
        -- Can't find any suitable monster for the HD we have left.
--        hd_pool = 0
--      end
--    end
--  end

  local function mons_place(point)
    if hd_pool <= 0 then
      return true
    elseif not dgn.mons_at(point.x, point.y) then
      --mons_do_place(point)
      zig.monster(point.x, point.y)
    end
  end

  dgn.find_adjacent_point(p, mons_place, dgn_passable)
end

local function ziggurat_create_loot_at(c)
  -- Basically, loot grows linearly with depth. However, the entry fee
  -- affects the loot randomly (separatedly on each stage).
  local depth = you.depth()
  local nloot = depth
  nloot = nloot + crawl.random2(math.floor(nloot * zig().entry_fee / 10000))

  local function find_free_space(nspaces)
    local spaces = { }
    local function add_spaces(p)
      if nspaces <= 0 then
        return true
      else
        table.insert(spaces, p)
        nspaces = nspaces - 1
      end
    end
    dgn.find_adjacent_point(c, add_spaces, dgn_passable)
    return spaces
  end

  -- dgn.good_scrolls is a list of items with total weight 1000
  local good_loot = dgn.item_spec("* no_pickup w:7000 / " .. dgn.good_scrolls)
  local super_loot = dgn.item_spec("| no_pickup w:7000 /" ..
                                   "potion of experience no_pickup w:200 /" ..
                                   "potion of cure mutation no_pickup w:200 /" ..
                                   "potion of porridge no_pickup w:100 /" ..
                                   "wand of heal wounds no_pickup w:10 / " ..
                                   "wand of hasting no_pickup w:10 / " ..
                                   dgn.good_scrolls)

  local loot_spots = find_free_space(nloot * 4)

  if #loot_spots == 0 then
    return
  end

  local curspot = 0
  local function next_loot_spot()
    curspot = curspot + 1
    if curspot > #loot_spots then
      curspot = 1
    end
    return loot_spots[curspot]
  end

  local function place_loot(what)
    local p = next_loot_spot()
    dgn.create_item(p.x, p.y, what)
  end

  for i = 1, nloot do
    if crawl.one_chance_in(depth) then
      for j = 1, 4 do
        place_loot(good_loot)
      end
    else
      place_loot(super_loot)
    end
  end
end

-- Suitable for use in loot vaults.
function ziggurat_loot_spot(e, key)
  e.lua_marker(key, portal_desc { ziggurat_loot = "X" })
  e.kfeat(key .. " = .")
  e.marker("@ = lua:props_marker({ door_restrict=\"veto\" })")
  e.kfeat("@ = +")
end

local has_loot_chamber = false

local function ziggurat_create_loot_vault(entry, exit)
  local inc = (exit - entry):sgn()

  local connect_point = exit - inc * 3
  local map = dgn.map_by_tag("ziggurat_loot_chamber")

  if not map then
    return exit
  end

  local function place_loot_chamber()
    local res = dgn.place_map(map, true, true)
    if res then
      has_loot_chamber = true
    end
    return res
  end

  local function good_loot_bounds(map, px, py, xs, ys)
    local vc = dgn.point(px + math.floor(xs / 2),
                         py + math.floor(ys / 2))


    local function safe_area()
      local p = dgn.point(px, py)
      local sz = dgn.point(xs, ys)
      local floor = dgn.fnum("floor")
      return dgn.rectangle_forall(p, p + sz - 1,
                                  function (c)
                                    return dgn.grid(c.x, c.y) == floor
                                  end)
    end

    local linc = (exit - vc):sgn()
    -- The map's positions should be at the same increment to the exit
    -- as the exit is to the entrance, else reject the place.
    return (inc == linc) and safe_area()
  end

  local function connect_loot_chamber()
    return dgn.with_map_bounds_fn(good_loot_bounds, place_loot_chamber)
  end

  local res = dgn.with_map_anchors(connect_point.x, connect_point.y,
                                   connect_loot_chamber)
  if not res then
    return exit
  else
    -- Find the square to drop the loot.
    local lootx, looty = dgn.find_marker_position_by_prop("ziggurat_loot")

    if lootx and looty then
      return dgn.point(lootx, looty)
    else
      return exit
    end
  end
end

local function ziggurat_locate_loot(entrance, exit, jelly_protect)
  if jelly_protect then
    return ziggurat_create_loot_vault(entrance, exit)
  else
    return exit
  end
end

local function ziggurat_place_pillars(c)
  local range = crawl.random_range
  local floor = dgn.fnum("floor")

  local map, vplace = dgn.resolve_map(dgn.map_by_tag("ziggurat_pillar"))

  if not map then
    return
  end

  local name = dgn.name(map)

  local size = dgn.point(dgn.mapsize(map))

  -- Does the pillar want to be centered?
  local centered = string.find(dgn.tags(map), " centered ")

  local function good_place(p)
    local function good_square(where)
      return dgn.grid(where.x, where.y) == floor
    end
    return dgn.rectangle_forall(p, p + size - 1, good_square)
  end

  local function place_pillar()
    if centered then
      if good_place(c) then
        return dgn.place_map(map, true, true, c.x, c.y)
      end
    else
      for i = 1, 100 do
        local offset = range(-15, -size.x)
        local offsets = {
          dgn.point(offset, offset) - size + 1,
          dgn.point(offset - size.x + 1, -offset),
          dgn.point(-offset, -offset),
          dgn.point(-offset, offset - size.y + 1)
        }

        offsets = util.map(function (o)
                             return o + c
                           end, offsets)

        if util.forall(offsets, good_place) then
          local function replace(at, hflip, vflip)
            dgn.reuse_map(vplace, at.x, at.y, hflip, vflip)
          end

          replace(offsets[1], false, false)
          replace(offsets[2], false, true)
          replace(offsets[3], true, false)
          replace(offsets[4], false, true)
          return true
        end
      end
    end
  end

  for i = 1, 5 do
    if place_pillar() then
      break
    end
  end
end

local function ziggurat_stairs(entry, exit)
  zigstair(entry.x, entry.y, "stone_arch", "stone_stairs_up_i")

  if you.depth() < ZIGGURAT_MAX then
    zigstair(exit.x, exit.y, "stone_stairs_down_i")
  end

  zigstair(exit.x, exit.y + 1, "exit_portal_vault")
  zigstair(exit.x, exit.y - 1, "exit_portal_vault")
end

local function ziggurat_furnish(centre, entry, exit)
  has_loot_chamber = false
  local monster_generation = choose_monster_set()

  if type(monster_generation.spec) == "string" then
    dgn.set_random_mon_list(monster_generation.spec)
  end

  -- Identify where we're going to place loot, but don't actually put
  -- anything down until we've placed pillars.
  local lootspot = ziggurat_locate_loot(entry, exit,
    monster_generation.jelly_protect)

  if not has_loot_chamber then
    -- Place pillars if we did not create a loot chamber.
    ziggurat_place_pillars(centre)
  end

  ziggurat_create_loot_at(lootspot)

  ziggurat_create_monsters(exit, monster_generation.fn)

  local function needs_colour(p)
    return not dgn.in_vault(p.x, p.y)
      and dgn.grid(p.x, p.y) == dgn.fnum("permarock_wall")
  end

  dgn.colour_map(needs_colour, zig().colour)
  set_wall_tiles()
end

-- builds ziggurat maps consisting of two overimposed rectangles
local function ziggurat_rectangle_builder(e)
  local grid = dgn.grid
  dgn.fill_grd_area(0, 0, dgn.GXM - 1, dgn.GYM - 1, "permarock_wall")

  local area = map_area()
  area = math.floor(area*3/4)

  local cx, cy = dgn.GXM / 2, dgn.GYM / 2

  -- exc is the local eccentricity for the two rectangles
  -- exc grows with depth as 0-1, 1, 1-2, 2, 2-3 ...
  local exc = math.floor(you.depth() / 2)
  if ((you.depth()-1) % 2) == 0 and crawl.coinflip() then
    exc = exc + 1
  end

  local a = math.floor(math.sqrt(area+4*exc*exc))
  local b = a - 2*exc
  local a2 = math.floor(a / 2) + (a % 2)
  local b2 = math.floor(b / 2) + (b % 2)
  local x1, y1 = clamp_in_bounds(cx - a2, cy - b2)
  local x2, y2 = clamp_in_bounds(cx + a2, cy + b2)
  dgn.fill_grd_area(x1, y1, x2, y2, "floor")

  local zig_exc = zig.exc()
  local nx1 = cx + y1 - cy
  local ny1 = cy + x1 - cx + math.floor(you.depth()/2*(200-zig_exc)/300)
  local nx2 = cx + y2 - cy
  local ny2 = cy + x2 - cx - math.floor(you.depth()/2*(200-zig_exc)/300)
  nx1, ny1 = clamp_in_bounds(nx1, ny1)
  nx2, ny2 = clamp_in_bounds(nx2, ny2)
  dgn.fill_grd_area(nx1, ny1, nx2, ny2, "floor")

  local entry = dgn.point(x1, cy)
  local exit = dgn.point(x2, cy)

  if you.depth() % 2 == 0 then
    entry, exit = exit, entry
  end

  ziggurat_stairs(entry, exit)
  ziggurat_furnish(dgn.point(cx, cy), entry, exit)
end

-- builds elliptic ziggurat maps
-- given the area, half axes a and b are determined by:
-- pi*a*b=area,
-- a=b for zig_exc=0,
-- a=b*3/2 for zig_exc=100
local function ziggurat_ellipse_builder(e)
  local grid = dgn.grid
  dgn.fill_grd_area(0, 0, dgn.GXM - 1, dgn.GYM - 1, "permarock_wall")

  local zig_exc = zig.exc()

  local area = map_area()
  local b = math.floor(math.sqrt(200*area/(200+zig_exc) * 100/314))
  local a = math.floor(b * (200+zig_exc) / 200)
  local cx, cy = dgn.GXM / 2, dgn.GYM / 2

  local floor = dgn.fnum("floor")

  for x=0, dgn.GXM-1 do
    for y=0, dgn.GYM-1 do
      if b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= a*a*b*b then
        grid(x, y, floor)
      end
    end
  end

  local entry = dgn.point(cx-a+2, cy)
  local exit  = dgn.point(cx+a-2, cy)

  if you.depth() % 2 == 0 then
    entry, exit = exit, entry
  end

  ziggurat_stairs(entry, exit)
  ziggurat_furnish(dgn.point(cx, cy), entry, exit)
end


-- builds hexagonal ziggurat maps
local function ziggurat_hexagon_builder(e)
  local grid = dgn.grid
  dgn.fill_grd_area(0, 0, dgn.GXM - 1, dgn.GYM - 1, "permarock_wall")

  local zig_exc = zig.exc()

  local c = dgn.point(dgn.GXM, dgn.GYM) / 2
  local area = map_area()

  local a = math.floor(math.sqrt(2 * area / math.sqrt(27))) + 2
  local b = math.floor(a*math.sqrt(3)/4)

  local left = dgn.point(math.floor(c.x - (a + math.sqrt(2 * a)) / 2),
                         c.y)
  local right = dgn.point(2 * c.x - left.x, c.y)

  local floor = dgn.fnum("floor")

  for x = 1, dgn.GXM - 2 do
    for y = 1, dgn.GYM - 2 do
      local dlx = x - left.x
      local drx = x - right.x
      local dly = y - left.y
      local dry = y - right.y

      if dlx >= dly and drx <= dry
        and dlx >= -dly and drx <= -dry
        and y >= c.y - b and y <= c.y + b then
        grid(x, y, floor)
      end
    end
  end

  local entry = left + dgn.point(1,0)
  local exit  = right - dgn.point(1, 0)

  if you.depth() % 2 == 0 then
    entry, exit = exit, entry
  end

  ziggurat_stairs(entry, exit)
  ziggurat_furnish(c, entry, exit)
end

----------------------------------------------------------------------

ziggurat_builder_map = {
  rectangle = ziggurat_rectangle_builder,
  ellipse = ziggurat_ellipse_builder,
  hex = ziggurat_hexagon_builder
}

local ziggurat_builders = util.keys(ziggurat_builder_map)

function ziggurat_choose_builder()
  return util.random_from(ziggurat_builders)
end

I'll post again in the morning with (hpoefully) my progress. coding at 11pm is probably no the best idea.
Last edited by Grandiloquent Gentleman on Thursday, 4th July 2013, 14:41, edited 1 time in total.
User avatar

Dungeon Master

Posts: 4031

Joined: Thursday, 16th December 2010, 20:37

Location: France

Post Thursday, 4th July 2013, 14:25

Re: Implementing Zig proposal from Wiki

Grandiloquent Gentleman wrote:OK, how do I add a new file to the project so that I can make it? add to the make file? (Don't know how)

Add it to Makefile.obj
<+Grunt> You dereference an invalid pointer! Ouch! That really hurt! The game dies...

Vestibule Violator

Posts: 1567

Joined: Friday, 21st January 2011, 22:56

Post Thursday, 4th July 2013, 14:50

Re: Implementing Zig proposal from Wiki

There's some stuff about zigs in http://s-z.org/crawl-dev/%23%23crawl-dev-20130704.log starting at 08:55:47.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Friday, 5th July 2013, 05:12

Re: Implementing Zig proposal from Wiki

Woohoo! I entered a zig without it crashing
Too bad it was filled to the brim with monsters.

...aaand I just realised it might be a good idea to save the static data. Whoops!

Currently I know I need to
- write a save(&writer) to marshall the data
- write a load(&reader) to unmarshall the data

Where do those functions need to be called from?

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Friday, 5th July 2013, 06:04

Re: Implementing Zig proposal from Wiki

What static data do you need to save exactly? I can't see anything in the patch that couldn't be worked out at runtime.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Friday, 5th July 2013, 08:45

Re: Implementing Zig proposal from Wiki

Well currently the random chooser is the only thing to guarantee that no floor is chosen twice,(though it doesn't need to be like this but it would ensure diversity)
but mostly I want to save it for the purposes of debugging so that I can see what happened and when.
Obviously when I stop crashing it no longer needs to be saved.

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Friday, 5th July 2013, 09:01

Re: Implementing Zig proposal from Wiki

Marshalling/unmarshalling code can be tricky, I wouldn't really recommend this for temporary debugging code. However saving a list of zig floor types isn't really too hard; marshalling code is all in tags.cc. An easier way might be to write to the you.props hash, e.g. you.props["zig_floor_1"] = foo. This all already gets automatically marshalled and unmarshalled for you.

For debugging just using dprf (with a debug build) is the most straightforward way to see what's going on. If you're running tiles, you can launch it from a command prompt and then printf will write to the command prompt (or just print in lua).

It's also possible to get a proper debug environment set up, i.e. so you can breakpoint and pause execution, step through code, inspect variables, etc. I use Visual Studio 2012 for this on Windows (the Express version works too).

Edit: Note, if you are doing anything in tags.cc, first read the file docs/develop/save_compatibility.txt.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Friday, 5th July 2013, 12:58

Re: Implementing Zig proposal from Wiki

Ah, I didn't think of you.props["blah"], thanks. will use that.

The stacktraces all seem to be one of
  Code:
 bit_vector::operator|=(bit_vector const&)
 _losight_quadrant(SquareArray<bool, 8>&, los_param const&, int, int)
 losight(SquareArray<bool, 8>&, coord_def const&, opacity_func const&, circle_def const&)
 _update_globallos_at(coord_def const&, los_type)
 cell_see_cell(coord_def const&, coord_def const&, los_type)
 actor::see_cell(coord_def const&) const
 noise_grid::noise_perceived_position(actor*, coord_def const&, noise_t const&) const
 noise_grid::apply_noise_effects(coord_def const&, int, noise_t const&, int)
 noise_grid::propagate_noise()
 apply_noises()
 world_reacts()

if the crash is not from going down stairs or
  Code:
 std::string::_Rep::_S_empty_rep_storage + 24
 _losight_quadrant(SquareArray<bool, 8>&, los_param const&, int, int) + 385
 losight(SquareArray<bool, 8>&, coord_def const&, opacity_func const&, circle_def const&) + 103
 _update_globallos_at(coord_def const&, los_type) + 102
 cell_see_cell(coord_def const&, coord_def const&, los_type) + 70
 los_glob::see_cell(coord_def const&) const + 48
radius_iterator::is_valid_square(coord_def const&) const + 62
 radius_iterator::advance(bool) + 96
 show_init(bool) + 52
 viewwindow(bool, bool) + 227
 down_stairs(dungeon_feature_type) + 6181
if going down the stairs

I've limited the amount of monsters that can spawn which was causing a crash where you landed on a square already occupied by a monster.
I don't actually know what is causing the crash.

How does one enable a debug build with all those features? Xcode doesn't like compiling crawl.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Saturday, 6th July 2013, 07:56

Re: Implementing Zig proposal from Wiki

how you do use util.random_weighted_from and what are the types of its arguments?
because I wanted to do
  Code:
function(z)
--z.floor_weights is a table like {floor_type1 = weight1,floor_type2 = weight2 ...}
local floor = util.random_weighted_from(z.floor_weights )
ziggurat.set_floor type(floor)
z.floor_weights[floor]  = z.floor_weights[floor] / 2


as a way to maintain diversity for the floors, but that doesn't work (idea stolen from acquirement weighting).

In other news the only floor I am reliably crashing on is Shoals, probably due to trying to place aquatic creatures on land.

Also what is attribute #24 because seems to have (bogus?) very large and sometimes negative values in the crashdumps.

And random_draconian_monster_species() is only returning jobless draconians. how do I get ones with jobs as well?

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Saturday, 6th July 2013, 13:45

Re: Implementing Zig proposal from Wiki

Grandiloquent Gentleman wrote:How does one enable a debug build with all those features? Xcode doesn't like compiling crawl.


Normally:
  Code:
make debug

or
  Code:
make debug TILES=y


...But I'm not sure how builds work on mac.

Grandiloquent Gentleman wrote:how you do use util.random_weighted_from and what are the types of its arguments?


The first parameter is which field you want to take the weight from (alternately it can be a function closure to choose a weight):
  Code:
local weights = { { weight = 100, floor_type = "foo" }, { weight = 200, floor_type = "bar" } }
local chosen = util.random_weighted_from("weight", weights)


So you'll have to modify your weights table a bit to use this.

Grandiloquent Gentleman wrote:Also what is attribute #24 because seems to have (bogus?) very large and sometimes negative values in the crashdumps.


ATTR_SEEN_INVIS_SEED (the list can be seen in the attribute_type enum in enum.h). So this is a random seed and therefore intended to be large and random.

Grandiloquent Gentleman wrote:And random_draconian_monster_species() is only returning jobless draconians. how do I get ones with jobs as well?


Can't you just use "random draconian" in an mspec (like the old Zig code did)?

Normally when random_draconian_monster_species is called, the job has already been selected, so it's only for selecting
a base colour.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Saturday, 6th July 2013, 14:36

Re: Implementing Zig proposal from Wiki

thanks, I'll try the random weighting now.

The monster selection is in c++,so I don't Know. I'll post again in the morning when I'm more with it.

Ah, ATTR_SEEN_INVIS_SEED. I always get confused by the #if tag_major stuff.I think I was thinking it was something more important than that.

Now I've only got to find out what's going on when Shoals is selected and I'm (mostly) all done :D

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Saturday, 6th July 2013, 15:37

Re: Implementing Zig proposal from Wiki

The #if stuff is often marking code that's due for deletion at the next major version bump, i.e. when we next break save compatibility so loads of old enums can be removed, and existing enums can be reordered with impunity.

Basically when you see:

  Code:
#if TAG_MAJOR_VERSION == 34


That means "delete this code at the next version, 35".

Sometimes you'll see:
  Code:
#if TAG_MAJOR_VERSION > 34


And that means "this code isn't currently used but after the version bump it will replace some removed code elsewhere".

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Sunday, 7th July 2013, 04:00

Re: Implementing Zig proposal from Wiki

After much trial and error I finally have util.random_weighted_from working ( it returns a table not a field duh!). Yay!

Shoals is still crashing. :evil:
the stacktrace is useless and doesn't tell me much.
  Code:
Obtained 26 stack frames.
0x0000000103f6d853: write_stack_trace(__sFILE*, int) + 179
0x0000000103f73348: do_crash_dump() + 792
0x0000000103f6dd4d: _crash_signal_handler(int) + 589
0x00007fff8d00692a: _sigtramp + 26
0x0000000000000000: 0x0 + 0
0x00007fff8d05ddfa: abort + 143
0x00007fff8c7dbf87: abort_message + 257
0x00007fff8c7d9936: default_terminate() + 28
0x00007fff91ecc907: _objc_terminate() + 111
0x00007fff8c7d9965: safe_handler_caller(void (*)()) + 8
0x00007fff8c7d99c0: __cxa_bad_typeid + 0
0x00007fff8c7dabb7: __cxa_current_exception_type + 0
0x00007fff91ecc5f5: objc_exception_rethrow + 40
0x00007fff8d91f40b: -[NSAppleEventManager + 484
0x00007fff8d91f1bd: _NSAppleEventManagerGenericHandler + 106
0x00007fff94dc8f68: aeDispatchAppleEvent(AEDesc const*, AEDesc*, unsigned int, unsigned char*) + 307
0x00007fff94dc8dc9: dispatchEventAndSendReply(AEDesc const*, AEDesc*) + 37
0x00007fff94dc8c89: aeProcessAppleEvent + 318
0x00007fff8bfb2e29: AEProcessAppleEvent + 100
0x00007fff8b2fd2a6: _DPSNextEvent + 1456
0x00007fff8b2fc862: -[NSApplication + 128
0x00007fff8b2f3c03: -[NSApplication + 517
0x00000001044f37c5: CustomApplicationMain + 293
0x00000001044f3685: main + 181
0x0000000103ee40f4: start + 52
0x0000000000000001: 0x0 + 1

Were the Shoal zig floors flooded?If so that may explain the crashes, if not, I'm out of ideas as to why. :(
OK changing
  Code:
natives(125, "Shoal:$");

to is macro expanded form and changing the parse_level_id to the standard constructor fixes the problem. wtf?

and somewhere along the line I have broken the Xom floor random monsters. Yay I managed to unbreak it!

Also mass inner flame and sputterflies is hilarious! Xom approves the use of this combination!

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Monday, 8th July 2013, 15:37

Re: Implementing Zig proposal from Wiki

OK, it's almost ready aside balancing issues (I've mostly been &G'ing my way through the floors), which I can't fix without proper play testing.
And my rate of attempting zigs as about 9/~80,000 and completion about 6/~80,000 so I can't do that myself, and besides I'm already having too
much fun play testing your demigod branch ;) . (Btw, where do you want feedback, bug reports ect. for that to go?)

It should hopefully be easier to modify the weights, change the composition of the floors, add new floors and debug.
There are still plenty of todo's XXX's and other optional things that I havn't done, because either I don't know how or its a balance issue I'm not sure about.

Also I accidentally committed some of my progress, how do I generate a patch, diff'ing against a previous commit? I usually use
  Code:
git diff > foo.patch

but I assume that won't work. Its only the updated files that I've already posted, a small amount for declaring and opening the lua library (l_libs.h, dlua.cc) and the addition to Makefile.obj so I can re-update them if need be.
User avatar

Dungeon Master

Posts: 291

Joined: Wednesday, 6th June 2012, 18:59

Post Monday, 8th July 2013, 16:11

Re: Implementing Zig proposal from Wiki

Grandiloquent Gentleman wrote:Also I accidentally committed some of my progress, how do I generate a patch

For ease of use by the rest of us, commit the rest of your changes and use
  Code:
git format-patch origin/master
or
  Code:
git format-patch HEAD^^
(the latter assumes two commits).

If you want to end up with a total of one commit, you can use
  Code:
git rebase -i <last_stock_commit>
to squash commits together, or you can:
  Code:
git reset --soft HEAD^
(assumes only one commit past stock), add the rest of the changes to the commit, and
  Code:
git commit -c ORIG_HEAD

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Tuesday, 9th July 2013, 08:07

Re: Implementing Zig proposal from Wiki

Hmm, turns out
  Code:
git diff <previous commit>

works fine

Here it is
  Code:
diff --git a/crawl-ref/source/Makefile.obj b/crawl-ref/source/Makefile.obj
index 6d9038f..fe896ac 100644
--- a/crawl-ref/source/Makefile.obj
+++ b/crawl-ref/source/Makefile.obj
@@ -110,6 +110,7 @@ l_subvault.o \
 l_travel.o \
 l_view.o \
 l_you.o \
+l_zig.o \
 lang-fake.o \
 lev-pand.o \
 libutil.o \
diff --git a/crawl-ref/source/dat/dlua/ziggurat.lua b/crawl-ref/source/dat/dlua/ziggurat.lua
index 1ae2f6f..4d4eda5 100644
--- a/crawl-ref/source/dat/dlua/ziggurat.lua
+++ b/crawl-ref/source/dat/dlua/ziggurat.lua
@@ -8,6 +8,8 @@
 -- Functions that are attached to Lua markers' onclimb properties
 -- cannot be closures, because Lua markers must be saved and closure
 -- upvalues cannot (yet) be saved.
+-- monster generation (placement and choice) in done in l_zig.cc
+-- accessed via ziggurat.*
 ------------------------------------------------------------------------------
 
 require("dlua/lm_toll.lua")
@@ -18,9 +20,9 @@ require("dlua/lm_toll.lua")
 ZIGGURAT_MAX = 27
 
 function zig()
-  if not dgn.persist.ziggurat then
+    if not dgn.persist.ziggurat then
     dgn.persist.ziggurat = { entry_fee = 0 }
-    -- Initialise here to handle ziggurats accessed directly by &P.
+    -- Initialise here to handle ziggurats accessed directly by &pq.
     initialise_ziggurat(dgn.persist.ziggurat)
   end
   return dgn.persist.ziggurat
@@ -34,6 +36,31 @@ function ziggurat_wall_colour()
   return util.random_from(wall_colours)
 end
 
+function choose_random_floor(z)
+   
+    local info = z.floors_info
+    local weights = {}
+    util.foreach(info,  function (i)
+                            table.insert(weights,
+                            --To ensure diversity we halve the weight for each time the floor is used
+                                         { weight = (2 ^ (8 - i.times_chosen)),
+                                           ftype = i.ftype
+                                         })
+                        end
+                 )
+    local floor = util.random_weighted_from("weight",weights)
+   
+
+    ziggurat.set_floor_type(floor.ftype)
+        util.foreach(info,
+                 function(x)
+                    if floor.ftype == x.ftype then
+                        x.times_chosen = x.times_chosen + 1
+                    end
+                 end)
+end
+
 function initialise_ziggurat(z, portal)
   if portal then
     z.entry_fee = portal.props.amount
@@ -45,10 +72,49 @@ function initialise_ziggurat(z, portal)
   -- will be circles etc. It is not the actual excentricity but some
   -- value between 0 and 100. For deformed ellipses and rectangles, make
   -- sure that the map is wider than it is high for the sake of ASCII.
-


   z.zig_exc = crawl.random2(101)
   z.builder = ziggurat_choose_builder()
   z.colour = ziggurat_wall_colour()
+  --ftype Must match l_zig.cc::string2zigf
+  z.floors_info =  {{ times_chosen = 0 ,ftype = "Air"    },
+                    { times_chosen = 0 ,ftype = "Abyss"  },
+                    { times_chosen = 0 ,ftype = "Chei"   },
+                    { times_chosen = 0 ,ftype = "Coc"    },
+                    { times_chosen = 0 ,ftype = "Crypt"  },
+                    { times_chosen = 0 ,ftype = "Dis"    },
+                    { times_chosen = 0 ,ftype = "Drac"   },
+                    { times_chosen = 0 ,ftype = "Dragon" },
+                    { times_chosen = 0 ,ftype = "Dwarf"  },
+                    { times_chosen = 0 ,ftype = "Earth"  },
+                    { times_chosen = 0 ,ftype = "Elf"    },
+                    { times_chosen = 0 ,ftype = "Fire"   },
+                    { times_chosen = 0 ,ftype = "Forest" },
+                    { times_chosen = 0 ,ftype = "Geh"    },
+                    { times_chosen = 0 ,ftype = "Holy"   },
+                    { times_chosen = 0 ,ftype = "Ice"    },
+                    { times_chosen = 0 ,ftype = "Lair"   },
+                    { times_chosen = 0 ,ftype = "Mut"    },
+                    { times_chosen = 0 ,ftype = "Orc"    },
+                    { times_chosen = 0 ,ftype = "Pan"    },
+                    { times_chosen = 0 ,ftype = "Range"  },
+                    { times_chosen = 0 ,ftype = "Shoal"  },
+                    { times_chosen = 0 ,ftype = "Slime"  },
+                    { times_chosen = 0 ,ftype = "Snake"  },
+                    { times_chosen = 0 ,ftype = "Spider" },
+                    { times_chosen = 0 ,ftype = "Swamp"  },
+                    { times_chosen = 0 ,ftype = "Tar"    },
+                    { times_chosen = 0 ,ftype = "Tomb"   },
+                    { times_chosen = 0 ,ftype = "Vault"  },
+                    { times_chosen = 0 ,ftype = "Xom"    },
+                    { times_chosen = 0 ,ftype = "Zot"    }
+                   }
+  --Todo Implement these in the builders
+  z.surrounds = crawl.coinflip() and "lava" or "water"
+  --  1 == halo (sunlight is halo), 0 == nothing, -1 == night (umbra,gloom?)
+  z.tod = crawl.random2(3) - 1
+   
 end
 
 function callback.ziggurat_initialiser(portal)
@@ -58,6 +124,7 @@ end
 
 -- Common setup for ziggurat entry vaults.
 function ziggurat_portal(e, spawnrange)

   local d = crawl.roll_dice
   local entry_fee =
     10 * math.floor(200 + d(3,200) / 3 + d(10) * d(10) * d(10))
@@ -95,10 +162,13 @@ end
 
 -- Common setup for ziggurat levels.
 function ziggurat_level(e)
+  ziggurat.set_level(you.depth())
   e.tags("allow_dup")
   e.tags("no_dump")
   e.orient("encompass")
-
+  ziggurat.set_level(you.depth())
+  choose_random_floor(zig())
+  ziggurat.init()
   if crawl.game_started() then
     ziggurat_build_level(e)
   end
@@ -107,22 +177,24 @@ end
 -----------------------------------------------------------------------------
 -- Ziggurat level builders.
 
-beh_wander = mons.behaviour("wander")
-
-function ziggurat_awaken_all(mons)
-  mons.beh = beh_wander
-end
 
 function ziggurat_build_level(e)
+
   local builder = zig().builder
 
   -- Deeper levels can have all monsters awake.
   -- Does never happen at depths 1-4; does always happen at depths 25-27.
+  -- Now done in l_zig.cc
-  local generate_awake = you.depth() > 4 + crawl.random2(21)
-  zig().monster_hook = generate_awake and global_function("ziggurat_awaken_all")
+
   -- Deeper levels may block controlled teleports.
   -- Does never happen at depths 1-6; does always happen at depths 25-27.
+  -- not done in l_zig.cc because I don't know how
   if you.depth() > 6 + crawl.random2(19) then
     dgn.change_level_flags("no_tele_control")
   end
@@ -171,189 +243,10 @@ local function set_wall_tiles()
   dgn.change_rock_tile(wall)
 end
 
-local function with_props(spec, props)
-  local spec_table = type(spec) == "table" and spec or { spec = spec }
-  return util.cathash(spec_table, props)
-end
-
-local function spec_fn(specfn)
-  return { specfn = specfn }
-end
-
-local function spec_if(fn, spec)
-  local spec_table = type(spec) == "table" and spec or { spec = spec }
-  return util.cathash(spec_table, { cond = fn })
-end
-
-local function depth_ge(lev)
-  return function ()
-           return you.depth() >= lev
-         end
-end
-
-local function depth_range(low, high)
-  return function ()
-           return you.depth() >= low and you.depth() <= high
-         end
-end
-
-local function depth_lt(lev)
-  return function ()
-           return you.depth() < lev
-         end
-end
-
-local function zig_monster_fn(spec)
-  local mfn = dgn.monster_fn(spec)
-  return function (x, y)
-           local mons = mfn(x, y)
-           if mons then
-             local monster_hook = zig().monster_hook
-             if monster_hook then
-               monster_hook(mons)
-             end
-           end
-           return mons
-         end
-end
-
-local function monster_creator_fn(arg)
-  local atyp = type(arg)
-  if atyp == "string" then
-    local mcreator = zig_monster_fn(arg)
-
-    local function mspec(x, y, nth)
-      return mcreator(x, y)
-    end
-    return { fn = mspec, spec = arg }
-  elseif atyp == "table" then
-    if not arg.cond or arg.cond() then
-      local spec = arg.spec or arg.specfn()
-      return util.cathash(monster_creator_fn(spec), arg)
-    end
-  elseif atyp == "function" then
-    return { fn = arg }
-  end
-end
-
-local mons_populations = { }
-
-local function mset(...)
-  util.foreach({ ... }, function (spec)
-                          table.insert(mons_populations, spec)
-                        end)
-end
-
-local function mset_if(condition, ...)
-  mset(unpack(util.map(util.curry(spec_if, condition), { ... })))
-end
 
-mset(with_props("place:Slime:$", { jelly_protect = true }),
-     "place:Snake:$ w:90 / greater naga w:5 / guardian serpent w:5",
-     with_props("place:Lair:$ w:85 / catoblepas w:6 / dire elephant w:6 / " ..
-                "hellephant w:3", { weight = 5 }),
-     "place:Spider:$ w:110 / ghost moth w:15 / red wasp / " ..
-                "orb spider / moth of suppression w:5",
-     "place:Crypt:$ w:180 / vampire knight w:14 / lich w:3 / " ..
-                "unborn deep dwarf w:2 / curse toe w:1",
-     "place:Forest:$ w:180 / satyr / tengu reaver w:5 / " ..
-                "spriggan defender w:5",
-     "place:Abyss",
-     "place:Swamp:$ w:120 / hydra / swamp dragon / " ..
-                "green death w:6 / death drake w:4",
-     "place:Shoals:$ w:125 / merfolk aquamancer w:15 / merfolk impaler w:4 / " ..
-                "merfolk javelineer w:4 / siren w:2",
-     with_props("place:Coc:$ w:460 / Ice Fiend / " ..
-                 "blizzard demon w:30", { weight = 5 }),
-     with_props("place:Geh:$ w:460 / Brimstone Fiend / " ..
-                 "balrug w:30", { weight = 5 }),
-     with_props("place:Dis:$ w:460 / Hell Sentinel / " ..
-                 "dancing weapon / iron dragon w:20", { weight = 5 }),
-     with_props("place:Tar:$ w:460 / Shadow Fiend / " ..
-                 "curse toe / shadow demon w:20", { weight = 5 }),
-     with_props("daeva / angel / cherub / pearl dragon / shedu band / ophan / " ..
-                "apis / paladin / w:5 phoenix / w:5 silver star", { weight = 2 }),
-     with_props("hill giant / cyclops / stone giant / fire giant / " ..
-                "frost giant / ettin / titan", { weight = 2 }),
-     with_props("fire elemental / fire drake / hell hound / efreet / " ..
-                "dragon / fire giant / orb of fire", { weight = 2 }),
-     with_props("ice beast / polar bear / freezing wraith / ice dragon / " ..
-                "frost giant / ice devil / ice fiend / simulacrum w:20 / " ..
-                "blizzard demon", { weight = 2 }),
-     with_props("insubstantial wisp / air elemental / vapour / titan / " ..
-                "storm dragon / electric golem / spriggan air mage", { weight = 2 }),
-     with_props("swamp drake / fire drake / death drake / steam dragon / " ..
-                "swamp dragon / dragon / ice dragon / storm dragon / " ..
-                "iron dragon / shadow dragon / quicksilver dragon / " ..
-                "golden dragon / mottled dragon / wyvern / hydra", { weight = 2 }),
-     with_props("centaur / yaktaur / centaur warrior / yaktaur captain / " ..
-                "cyclops / stone giant / merfolk javelineer / " ..
-                "deep elf master archer", { weight = 2 }))
-
--- spec_fn can be used to wrap a function that returns a monster spec.
--- This is useful if you want to adjust monster weights in the spec
--- wrt to depth in the ziggurat. At level-generation time, the spec
--- returned by this function will also be used to init the monster
--- population (with dgn.set_random_mon_list). As an example:
-mset(spec_fn(function ()
-               local d = math.max(0, you.depth() - 12)
-               return "place:Vaults:$ w:60 / ancient lich w:" .. d
-             end))
-
-mset(spec_fn(function ()
-               local d = math.max(0, you.depth() - 5)
-               return "place:Pan w:45 / pandemonium lord w:" .. d
-             end))
-
-mset(spec_fn(function ()
-               local d = you.depth() + 5
-               return "place:Tomb:$ w:200 / greater mummy w:" .. d
-             end))
-
-mset(spec_fn(function ()
-               local d = 300 - 10 * you.depth()
-               return "place:Elf:$ w:" .. d .. " / deep elf sorcerer / " ..
-                 "deep elf blademaster / deep elf master archer / " ..
-                 "deep elf annihilator / deep elf demonologist"
-             end))
-
-mset(spec_fn(function ()
-               local d = 310 - 10 * you.depth()
-               local e = math.max(0, you.depth() - 20)
-               return "place:Orc:$ w:" .. d .. " / orc warlord / orc knight / " ..
-                 "orc high priest w:5 / orc sorcerer w:5 / stone giant / " ..
-                 "moth of wrath w:" .. e
-             end))
-
-
-local drac_creator = zig_monster_fn("random draconian")
-local function mons_drac_gen(x, y, nth)
-  if nth == 1 then
-    dgn.set_random_mon_list("random draconian")
-  end
-  return drac_creator(x, y)
-end
-
-local pan_lord_fn = zig_monster_fn("pandemonium lord")
-local pan_critter_fn = zig_monster_fn("place:Pan")
-
-local function mons_panlord_gen(x, y, nth)
-  if nth == 1 then
-    dgn.set_random_mon_list("place:Pan")
-    return pan_lord_fn(x, y)
-  else
-    return pan_critter_fn(x, y)
-  end
-end
-
-mset_if(depth_ge(6), mons_drac_gen)
-mset_if(depth_ge(8), mons_panlord_gen)
-
-function ziggurat_monster_creators()
-  return util.map(monster_creator_fn, mons_populations)
-end
-
-local function ziggurat_vet_monster(fmap)   
-  local fn = fmap.fn
-  fmap.fn = function (x, y, nth, hdmax)
-              if nth >= dgn.MAX_MONSTERS then
-                return nil
-              end
-              for i = 1, 10 do
-                local mons = fn(x, y, nth)
-                if mons then
-                  -- Discard zero-exp monsters, and monsters that explode
-                  -- the HD limit.
-                  if mons.experience == 0 or mons.hd > hdmax * 1.3 then
-                    mons.dismiss()
-                  else
-                    -- Monster is ok!
-                    return mons
-                  end
-                end
-              end
-              -- Give up.
-             return nil
-           end
-   return fmap
- end
@@ -378,45 +271,21 @@ local function ziggurat_vet_monster(fmap)
   return fmap
 end
 
-local function choose_monster_set()
-  return ziggurat_vet_monster(
-           util.random_weighted_from(
-             'weight',
-             ziggurat_monster_creators()))
-end
+
 
 -- Function to find travel-safe squares, excluding closed doors.
 local dgn_passable = dgn.passable_excluding("closed_door")
 
 local function ziggurat_create_monsters(p, mfn)
-  local hd_pool = you.depth() * (you.depth() + 8)
 -- (was depth * (depth + 8) before and too easy)
 
-  local nth = 1
-
-  local function mons_do_place(p)
-    if hd_pool > 0 then
-      local mons = mfn(p.x, p.y, nth, hd_pool)
-
-      if mons then
-        nth = nth + 1
-        hd_pool = hd_pool - mons.hd
-
-        if nth >= dgn.MAX_MONSTERS then
-          hd_pool = 0
-        end
-      else
-        -- Can't find any suitable monster for the HD we have left.
-        hd_pool = 0
-      end
-    end
-  end
-
   local function mons_place(point)
-    if hd_pool <= 0 then
+    if ziggurat.hd_pool() <= 0 then
       return true
     elseif not dgn.mons_at(point.x, point.y) then
-      mons_do_place(point)
+      return ziggurat.monster(point.x, point.y)
     end
   end
 
@@ -445,6 +314,8 @@ local function ziggurat_create_loot_at(c)
   end
 
   -- dgn.good_scrolls is a list of items with total weight 1000
+  -- Optional: give more !cMut at least one !restAb /HW (rot, stat drain for undead) on mutagenic floors
+  -- and/or give some !beneMut
   local good_loot = dgn.item_spec("* no_pickup w:7000 / " .. dgn.good_scrolls)
   local super_loot = dgn.item_spec("| no_pickup w:7000 /" ..
                                    "potion of experience no_pickup w:200 /" ..
@@ -639,18 +510,11 @@ local function ziggurat_stairs(entry, exit)
 end
 
 local function ziggurat_furnish(centre, entry, exit)
-  has_loot_chamber = false
-  local monster_generation = choose_monster_set()
-
-  if type(monster_generation.spec) == "string" then
-    dgn.set_random_mon_list(monster_generation.spec)
-  end
+  local has_loot_chamber = ziggurat.floor_type() == "Slime"
 
   -- Identify where we're going to place loot, but don't actually put
   -- anything down until we've placed pillars.
-  local lootspot = ziggurat_locate_loot(entry, exit,
-    monster_generation.jelly_protect)
-
+  local lootspot = ziggurat_locate_loot(entry, exit, has_loot_chamber)
   if not has_loot_chamber then
     -- Place pillars if we did not create a loot chamber.
     ziggurat_place_pillars(centre)
@@ -658,7 +522,7 @@ local function ziggurat_furnish(centre, entry, exit)
 
   ziggurat_create_loot_at(lootspot)
 
-  ziggurat_create_monsters(exit, monster_generation.fn)
+  ziggurat_create_monsters(exit, ziggurat.monster)
 
   local function needs_colour(p)
     return not dgn.in_vault(p.x, p.y)
diff --git a/crawl-ref/source/dlua.cc b/crawl-ref/source/dlua.cc
index 90425ab..cbd00fb 100644
--- a/crawl-ref/source/dlua.cc
+++ b/crawl-ref/source/dlua.cc
@@ -288,6 +288,7 @@ void init_dungeon_lua()
     dluaopen_monsters(dlua);
     dluaopen_you(dlua);
     dluaopen_dgn(dlua);
+    dluaopen_zig(dlua);
 
     luaL_openlib(dlua, "feat", feat_dlib, 0);
     luaL_openlib(dlua, "spells", spells_dlib, 0);
diff --git a/crawl-ref/source/l_libs.h b/crawl-ref/source/l_libs.h
index 947234d..8961a9c 100644
--- a/crawl-ref/source/l_libs.h
+++ b/crawl-ref/source/l_libs.h
@@ -56,6 +56,7 @@ extern const struct luaL_reg feat_dlib[];
 extern const struct luaL_reg spells_dlib[];
 extern const struct luaL_reg los_dlib[];
 extern const struct luaL_reg mapmarker_dlib[];
+extern const struct luaL_reg zig_dlib[];
 
 void luaopen_dgnevent(lua_State *ls);
 void luaopen_mapmarker(lua_State *ls);
@@ -72,7 +73,7 @@ void dluaopen_monsters(lua_State *ls);
 void dluaopen_you(lua_State *ls);
 void dluaopen_dgn(lua_State *ls);
 void dluaopen_colour(lua_State *ls);
-
+void dluaopen_zig(lua_State *ls);
 /*
  * Some shared helper functions.
  */
diff --git a/crawl-ref/source/l_zig.cc b/crawl-ref/source/l_zig.cc
new file mode 100644
index 0000000..85739d4
--- /dev/null
+++ b/crawl-ref/source/l_zig.cc
@@ -0,0 +1,1042 @@
+/**
+ * @file
+ * @brief Bindings for ziggurat monster and floor selection and debugging
+ **/
+
+#include "AppHdr.h"
+
+#include "clua.h"
+#include "dlua.h"
+#include "cluautil.h"
+#include "l_defs.h"
+#include "lua.h"
+#include "coordit.h"
+#include "dungeon.h"
+#include "items.h"
+#include "mapmark.h"
+#include "maps.h"
+#include "mgen_data.h"
+#include "misc.h"
+#include "mon-place.h"
+#include "mon-pick.h"
+#include "colour.h"
+
+#include "terrain.h"
+
+
+enum zig_floor_t
+{
+    //elemental
+    ZIGF_AIR,
+    ZIGF_EARTH,
+    ZIGF_FIRE,
+    ZIGF_ICE,
+   
+    //location
+    ZIGF_ORC,
+    ZIGF_ELF,
+    ZIGF_DWARF,
+   
+    ZIGF_LAIR,
+    ZIGF_SHOAL,
+    ZIGF_SNAKE,
+    ZIGF_SPIDER,
+    ZIGF_SWAMP,
+    ZIGF_SLIME,
+   
+    ZIGF_VAULTS,
+    ZIGF_FOREST,
+    ZIGF_CRYPT,
+    ZIGF_TOMB,
+   
+    ZIGF_GEHENNA,
+    ZIGF_COCYTIS,
+    ZIGF_DIS,
+    ZIGF_TARTARUS,
+   
+    ZIGF_ABYSS,
+    ZIGF_ZOT,
+    ZIGF_PAN,
+   
+    //monster sets
+    ZIGF_DRACONIAN,
+    ZIGF_DRAGON,
+    ZIGF_RANGED,
+    ZIGF_GIANT,
+    ZIGF_HOLY,
+    ZIGF_MUTAGENIC,
+   
+    //gods not (necessarily) represented in the above
+    ZIGF_XOM,
+    ZIGF_CHEI,
+//    ZIGF_FEDHAS,
+//    ZIGF_NEMELEX,
+//    ZIGF_OKAWARU,
+    NUM_ZIGF
+   
+};
+
+static struct {
+    zig_floor_t floor;
+    int tot_weights;
+    level_id lid;
+    int lid_weight;
+    int orig_hd_pool;
+    int hdpool;
+    bool genawake;
+} _floors[27];
+
+static int current_floor;//0-26
+
+
+static int _mon_weights[MAX_MONS_ALLOC];
+static int num_mons;
+//those chosen from env.mons_alloc
+static int num_mons_special;
+
+static void _random_xom_mons( coord_def at, bool awake);
+static void _chei_mons(monster* mon);
+
+static const char* zigf2string(zig_floor_t floor)
+{
+    switch (floor) {
+           
+        case ZIGF_AIR:          return "Air";
+        case ZIGF_ABYSS:        return "Abyss";
+        case ZIGF_CHEI:         return "Chei";
+        case ZIGF_COCYTIS:      return "Coc";
+        case ZIGF_CRYPT:        return "Crypt";
+        case ZIGF_DIS:          return "Dis";
+        case ZIGF_DRACONIAN:    return "Drac";
+        case ZIGF_DRAGON:       return "Dragon";
+        case ZIGF_DWARF:        return "Dwarf";
+        case ZIGF_EARTH:        return "Earth";
+        case ZIGF_ELF:          return "Elf";
+        case ZIGF_FIRE:         return "Fire";
+        case ZIGF_FOREST:       return "Forest";
+        case ZIGF_GEHENNA:      return "Geh";
+        case ZIGF_GIANT:        return "Giant";
+        case ZIGF_HOLY:         return "Holy";
+        case ZIGF_ICE:          return "Ice";
+        case ZIGF_LAIR:         return "Lair";
+        case ZIGF_MUTAGENIC:    return "Mut";
+        case ZIGF_ORC:          return "Orc";
+        case ZIGF_PAN:          return "Pan";
+        case ZIGF_RANGED:       return "Range";
+        case ZIGF_SHOAL:        return "Shoal";
+        case ZIGF_SLIME:        return "Slime";
+        case ZIGF_SNAKE:        return "Snake";
+        case ZIGF_SPIDER:       return "Spider";
+        case ZIGF_SWAMP:        return "Swamp";
+        case ZIGF_TARTARUS:     return "Tar";
+        case ZIGF_TOMB:         return "Tomb";
+        case ZIGF_VAULTS:       return "Vault";
+        case ZIGF_XOM:          return "Xom";
+        case ZIGF_ZOT:          return "Zot";
+        default:                return "";
+        break;
+    }
+   
+};
+
+static zig_floor_t string2zigf(string str)
+{
+
+#ifdef DEBUG_ZIGS
+    printf("Arg = %s\n", str.c_str());
+#endif
+    if      (str == "Air")      {return ZIGF_AIR;      }
+    else if (str == "Abyss")    {return ZIGF_ABYSS;    }
+    else if (str == "Chei")     {return ZIGF_CHEI;     }
+    else if (str == "Coc")      {return ZIGF_COCYTIS;  }
+    else if (str == "Crypt")    {return ZIGF_CRYPT;    }
+    else if (str == "Dis")      {return ZIGF_DIS;      }
+    else if (str == "Drac")     {return ZIGF_DRACONIAN;}
+    else if (str == "Dragon")   {return ZIGF_DRAGON;   }
+    else if (str == "Dwarf")    {return ZIGF_DWARF;    }
+    else if (str == "Earth")    {return ZIGF_EARTH;    }
+    else if (str == "Elf")      {return ZIGF_ELF;      }
+    else if (str == "Fire")     {return ZIGF_FIRE;     }
+    else if (str == "Forest")   {return ZIGF_FOREST;   }
+    else if (str == "Geh")      {return ZIGF_GEHENNA;  }
+    else if (str == "Giant")    {return ZIGF_GIANT;    }
+    else if (str == "Holy")     {return ZIGF_HOLY;     }
+    else if (str == "Ice")      {return ZIGF_ICE;      }
+    else if (str == "Lair")     {return ZIGF_LAIR;     }
+    else if (str == "Mut")      {return ZIGF_MUTAGENIC;}
+    else if (str == "Orc")      {return ZIGF_ORC;      }
+    else if (str == "Pan")      {return ZIGF_PAN;      }
+    else if (str == "Range")    {return ZIGF_RANGED;   }
+    else if (str == "Shoal")    {return ZIGF_SHOAL;    }
+    else if (str == "Slime")    {return ZIGF_SLIME;    }
+    else if (str == "Snake")    {return ZIGF_SNAKE;    }
+    else if (str == "Spider")   {return ZIGF_SPIDER;   }
+    else if (str == "Swamp")    {return ZIGF_SWAMP;    }
+    else if (str == "Tar")      {return ZIGF_TARTARUS; }
+    else if (str == "Tomb")     {return ZIGF_TOMB;     }
+    else if (str == "Vault")    {return ZIGF_VAULTS;   }
+    else if (str == "Xom")      {return ZIGF_XOM;      }
+    else if (str == "Zot")      {return ZIGF_ZOT;      }
+    else                        {return NUM_ZIGF;      }
+   
+};
+LUARET1( zig_floor_type , string, zigf2string(_floors[current_floor].floor));
+
+LUAFN(zig_dump_all)
+{
+    printf("Dumping zig info:");
+    printf("Current floor %d", current_floor);
+    for (int i = 0 ; i < current_floor; i++)
+    {
+        printf("--------------------------------");
+        printf("Floor: %d\n",i);
+        printf("Floor type: %s\n",zigf2string(_floors[i].floor));
+        if (_floors[i].lid_weight)
+            printf("Weight for native branch: %d\n", _floors[i].lid_weight);
+        printf("Total weight %d\n", _floors[i].lid_weight +  _floors[i].tot_weights);
+        printf("Hd pool of %d\n",_floors[i].orig_hd_pool );
+        printf("Hd left in pool %d\n", _floors[i].hdpool);
+        if (_floors[i].genawake)
+            printf("Monster generated awake\n");
+       
+       
+    }
+   
+    return 1;
+}
+
+
+LUAFN(zig_set_level)
+{
+    // - 1 because C's arrays start at 0, another - 1
+    //because current_floor needs to be incremented at the start of zig_init
+    current_floor = luaL_checknumber(ls, 1) - 2;
+    return 1;
+}
+LUAFN(zig_set_floor_type)
+{
+    size_t l;
+    // + 1 because this needs to be called before zig_init
+    _floors[current_floor +1].floor = string2zigf(string(luaL_checklstring(ls, 1, &l)).c_str());
+    return 1;
+}
+LUAFN(zig_init)
+{
+    if (current_floor == -1 )
+    {
+        //Place any initial initialsation here
+    }
+    current_floor++;
+    for (int i=0; i < MAX_MONS_ALLOC; i++)
+    {
+        _mon_weights[i] = 0;
+    }
+    _floors[current_floor].lid = level_id(NUM_BRANCHES);
+   
+    zig_floor_t type = _floors[current_floor].floor;
+    _floors[current_floor].lid_weight = 0;
+    _floors[current_floor].tot_weights = 0;
+    num_mons = 0;
+    num_mons_special = 0;
+    _floors[current_floor].genawake  =  current_floor > 4 + random2(21);
+    _floors[current_floor].hdpool  = _floors[current_floor].orig_hd_pool = you.depth * (you.depth +8);
+
+#define emaw(a,b,c) env.mons_alloc[a] = (monster_type) b;\
+_mon_weights[a] = c;\
+_floors[current_floor].tot_weights +=c
+#ifdef DEBUG_ZIGS   
+#define natives( weight ,place ) \
+_floors[current_floor].lid_weight= weight;\
+_floors[current_floor].lid = level_id::parse_level_id(place);\
+dprf("Branch %d:%d",_floors[current_floor].lid.branch,_floors[current_floor].lid.depth)
+#else
+#define natives( weight ,place ) \
+_floors[current_floor].lid_weight= weight;\
+_floors[current_floor].lid = level_id::parse_level_id(place)
+#endif
+    switch (type)
+    {
+        case ZIGF_AIR:
+       
+           
+            emaw(0, MONS_INSUBSTANTIAL_WISP ,10);
+            emaw(1, MONS_AIR_ELEMENTAL      ,10);
+            emaw(2, MONS_VAPOUR             ,10);
+            emaw(3, MONS_TITAN              ,10);
+            emaw(4, MONS_STORM_DRAGON       ,10);
+            emaw(5, MONS_ELECTRIC_GOLEM     ,10);
+            emaw(6, MONS_SPRIGGAN_AIR_MAGE  ,10);
+            break;
+       
+        case ZIGF_EARTH:
+       
+            emaw(0,  MONS_EARTH_ELEMENTAL       ,10);
+            emaw(1,  MONS_CRYSTAL_GOLEM         ,10);
+            emaw(2,  MONS_GARGOYLE              ,10);
+            emaw(3,  MONS_DEEP_TROLL_EARTH_MAGE ,10);
+            emaw(4,  MONS_STONE_GIANT           ,10);
+            emaw(5,  MONS_STONE_GOLEM           ,10);
+            emaw(6,  MONS_METAL_GARGOYLE        ,10);
+            emaw(7,  MONS_MOLTEN_GARGOYLE       ,10);
+            emaw(8,  MONS_IRON_GOLEM            ,10);
+            emaw(9,  MONS_IRON_DRAGON           ,10);
+            emaw(10, MONS_IRON_TROLL            ,10);
+            emaw(11, MONS_QUICKSILVER_DRAGON    ,10);
+            //XXX: Why do we not have any deep dwarf earth mages (aside Jorgun)?
+       
+            break;
+        case ZIGF_FIRE:
+       
+            emaw(0, MONS_FIRE_ELEMENTAL ,10);
+            emaw(1, MONS_FIRE_DRAKE     ,10);
+            emaw(2, MONS_HELL_HOUND     ,10);
+            emaw(3, MONS_EFREET         ,10);
+            emaw(4, MONS_DRAGON         ,10);
+            emaw(5, MONS_FIRE_GIANT     ,10);
+            emaw(6, MONS_ORB_OF_FIRE    ,10);
+           
+            break;
+       
+        case ZIGF_ICE:
+       
+            emaw(0, MONS_ICE_BEAST       ,10);
+            emaw(1, MONS_POLAR_BEAR      ,10);
+            emaw(2, MONS_FREEZING_WRAITH ,10);
+            emaw(3, MONS_ICE_DRAGON      ,10);
+            emaw(4, MONS_FROST_GIANT     ,10);
+            emaw(5, MONS_ICE_DEVIL       ,10);
+            emaw(6, MONS_BLIZZARD_DEMON  ,10);
+            emaw(7, MONS_SIMULACRUM      ,20);
+            emaw(8, MONS_ICE_FIEND       ,10);
+            break;
+       
+        case ZIGF_ORC:
+       
+            natives(310 - 10* you.depth, "Orc:$");
+           
+            emaw(0, MONS_ORC_WARLORD      ,10);
+            emaw(1, MONS_ORC_HIGH_PRIEST  , 5);
+            emaw(2, MONS_ORC_KNIGHT       ,10);
+            emaw(3, MONS_ORC_SORCERER     , 5);
+            emaw(4, MONS_STONE_GIANT      ,10);
+            emaw(5, MONS_MOTH_OF_WRATH, max(0, you.depth - 10));
+           
+            break;
+       
+           
+        case ZIGF_ELF:
+       
+            natives(300 - 10*you.depth, "Elf:$");
+           
+            emaw(0,  MONS_DEEP_ELF_SORCERER      ,10);
+            emaw(1,  MONS_DEEP_ELF_BLADEMASTER   ,10);
+            emaw(2,  MONS_DEEP_ELF_MASTER_ARCHER ,10);
+            emaw(3,  MONS_DEEP_ELF_ANNIHILATOR   ,10);
+            emaw(4,  MONS_DEEP_ELF_DEMONOLOGIST  ,10);
+            emaw(5,  MONS_DEEP_ELF_DEATH_MAGE    ,10);
+            emaw(6,  MONS_DEEP_ELF_HIGH_PRIEST   ,10);
+            break;
+       
+        case ZIGF_DWARF:
+            //Todo: Uncomment if/when dwarf gets added as a (non-wiz mode) branch
+            //natives(140 ,"Dwarf:$");
+       
+            emaw(0, MONS_DEEP_DWARF_SCION        ,10);
+            emaw(1, MONS_DEEP_DWARF_NECROMANCER  ,10);
+            emaw(2, MONS_DEEP_DWARF_BERSERKER    ,10);
+            emaw(3, MONS_DEEP_DWARF              ,20);
+            emaw(4, MONS_DEEP_DWARF_ARTIFICER    ,10);
+            emaw(5, MONS_DEEP_DWARF_DEATH_KNIGHT ,10);
+            emaw(6, MONS_UNBORN_DEEP_DWARF       ,10);
+            //XXX: No deep dwarf earth mages???
+            break;
+       
+        case ZIGF_LAIR:
+       
+            natives(95, "Lair:$");
+           
+            emaw(0, MONS_CATOBLEPAS    ,9 + you.depth/2);
+            emaw(1, MONS_DIRE_ELEPHANT ,6 + you.depth);
+            emaw(2, MONS_HELLEPHANT    ,3 + you.depth*3/2);
+            break;
+       
+        case ZIGF_SHOAL:
+            //XXX: natives with "Shoal:$" doesn't work?
+            //natives(125, "Shoal:$");
+            _floors[current_floor].lid_weight= 125;
+            _floors[current_floor].lid = level_id(BRANCH_SHOALS);
+            _floors[current_floor].lid.depth = 5;
+           
+            emaw(0, MONS_MERFOLK_AQUAMANCER ,15);
+            emaw(1, MONS_MERFOLK_IMPALER    , 4);
+            break;
+           
+        case ZIGF_SNAKE:
+            natives(90, "Snake:$");
+           
+            emaw(0, MONS_GREATER_NAGA      ,5 + you.depth/3);
+            emaw(1, MONS_GUARDIAN_SERPENT  ,5 + you.depth/2) ;
+            break;
+           
+        case ZIGF_SPIDER:
+       
+            natives(110, "Spider:$");
+           
+            emaw(0, MONS_GHOST_MOTH          , 5);
+            emaw(1, MONS_RED_WASP            ,10);
+            emaw(2, MONS_ORB_SPIDER          ,10);
+            emaw(3, MONS_MOTH_OF_SUPPRESSION , 5);
+            emaw(4, MONS_EMPEROR_SCORPION    ,15);
+           
+            break;
+       
+        case ZIGF_SWAMP:
+            natives(120, "Swamp:$");
+            emaw(0, MONS_HYDRA        ,10);
+            emaw(1, MONS_SWAMP_DRAGON ,10);
+            emaw(2, MONS_GREEN_DEATH  , 6);
+            emaw(3, MONS_DEATH_DRAKE  , 4);
+            break;
+           
+        case ZIGF_SLIME:
+            natives(10, "Slime:$");
+            //loot chamber set in ziggurat.lua:ziggurat_furnish
+            break;
+           
+        case ZIGF_VAULTS:
+       
+            natives(60, "Vaults:$");
+            emaw(0, MONS_ANCIENT_LICH, max(0 , you.depth - 12));
+            break;
+       
+        case ZIGF_FOREST:
+            natives(180, "Forest:$");
+            emaw(0, MONS_SATYR            ,10);
+            emaw(1, MONS_TENGU_REAVER     , 5);
+            emaw(2, MONS_SPRIGGAN_DEFENDER, 5);
+            break;
+           
+        case ZIGF_CRYPT:
+            natives(180, "Crypt:$");
+            emaw(0, MONS_VAMPIRE_KNIGHT   ,14);
+            emaw(1, MONS_LICH             , 3);
+            emaw(2, MONS_UNBORN_DEEP_DWARF, 2);
+            emaw(3, MONS_CURSE_TOE        , 1);
+            emaw(4, MONS_PROFANE_SERVITOR , 1);
+            break;
+           
+        case ZIGF_TOMB:
+            natives(200, "Tomb:$");
+            emaw(0,MONS_GREATER_MUMMY , you.depth +5);
+            break;
+           
+        case ZIGF_GEHENNA:
+            natives(460, "Geh:$");
+            emaw(0, MONS_BRIMSTONE_FIEND ,10);
+            emaw(1, MONS_BALRUG          ,30);
+            break;
+           
+        case ZIGF_COCYTIS:
+            natives(460, "Coc:$");
+            emaw(0, MONS_ICE_FIEND      ,10);
+            emaw(1, MONS_BLIZZARD_DEMON ,30);
+            break;
+           
+        case ZIGF_DIS:
+            natives(460, "Dis:$");
+            emaw(0, MONS_HELL_SENTINEL  ,10);
+            emaw(1, MONS_IRON_DRAGON    ,20);
+            emaw(2, MONS_DANCING_WEAPON ,10);
+            break;
+           
+        case ZIGF_TARTARUS:
+            natives(460, "Tar:$");
+            emaw(0, MONS_SHADOW_FIEND ,10);
+            emaw(1, MONS_SHADOW_DEMON ,20);
+            emaw(2, MONS_CURSE_TOE    ,10);
+            break;
+           
+        case ZIGF_ABYSS:
+            natives(450, "Abyss");
+            //Todo: add more weight to the higher tier abyss monsters
+            //i.e. less 3's and 4's and 5's
+            break;
+           
+        case ZIGF_ZOT:
+            natives(10, "Zot:$");
+            break;
+           
+        case ZIGF_PAN:
+            natives(45, "Pan");
+            emaw(0, MONS_PANDEMONIUM_LORD ,  max(0, you.depth -5 ));
+           
+        case ZIGF_DRACONIAN:
+            // see zig_mons(coord_def,bool)
+            break;
+           
+        case ZIGF_DRAGON:
+       
+            emaw(0,  MONS_SWAMP_DRAKE        , 5);
+            emaw(1,  MONS_FIRE_DRAKE         , 5);
+            emaw(2,  MONS_DEATH_DRAKE        ,10);
+            emaw(3,  MONS_STEAM_DRAGON       , 5);
+            emaw(4,  MONS_SWAMP_DRAGON       ,10);
+            emaw(5,  MONS_DRAGON             ,10);
+            emaw(6,  MONS_ICE_DRAGON         ,10);
+            emaw(7,  MONS_STORM_DRAGON       ,10);
+            emaw(8,  MONS_IRON_DRAGON        ,10);
+            emaw(9,  MONS_SHADOW_DRAGON      ,10);
+            emaw(10, MONS_QUICKSILVER_DRAGON ,10);
+            emaw(11, MONS_GOLDEN_DRAGON      ,10);
+            emaw(12, MONS_MOTTLED_DRAGON     ,10);
+            emaw(13, MONS_WYVERN             , 5);
+            //XXX: Why are there hydras on a dragon floor?
+            emaw(14, MONS_HYDRA              ,10);
+            break;
+       
+        case ZIGF_RANGED:
+            emaw(0, MONS_CENTAUR                , 5);
+            emaw(1, MONS_YAKTAUR                , 5);
+            emaw(2, MONS_CENTAUR_WARRIOR        , 5);
+            emaw(3, MONS_YAKTAUR_CAPTAIN        , 5);
+            emaw(4, MONS_CYCLOPS                ,10);
+            emaw(5, MONS_STONE_GIANT            ,10);
+            emaw(6, MONS_MERFOLK_JAVELINEER     ,10);
+            emaw(7, MONS_DEEP_ELF_MASTER_ARCHER ,10);
+            break;
+       
+        case ZIGF_GIANT:
+            emaw(0, MONS_HILL_GIANT   , 5);
+            emaw(1, MONS_CYCLOPS      ,10);
+            emaw(2, MONS_STONE_GIANT  ,10);
+            emaw(3, MONS_FIRE_GIANT   ,10);
+            emaw(4, MONS_FROST_GIANT  ,10);
+            emaw(5, MONS_ETTIN        ,10);
+            emaw(6, MONS_TITAN        ,15);
+           
+            break;
+           
+        case ZIGF_HOLY:
+            emaw(0, MONS_DAEVA        ,10);
+            emaw(1, MONS_ANGEL        ,15);
+            emaw(2, MONS_CHERUB       ,15);
+            emaw(3, MONS_PEARL_DRAGON ,10);
+            emaw(4, MONS_OPHAN        ,10);
+            emaw(5, MONS_APIS         ,15);
+            emaw(6, MONS_PALADIN      ,15);
+            emaw(7, MONS_PHOENIX      ,10);
+            emaw(8, MONS_SILVER_STAR  , 5);
+            emaw(9, MONS_SERAPH       , 1);
+            break;
+           
+        case ZIGF_MUTAGENIC:
+            emaw(0, MONS_POLYMOTH             ,10);
+            emaw(1, MONS_NEQOXEC              ,10);
+            emaw(2, MONS_CACODEMON            , 8);
+            emaw(3, MONS_SHINING_EYE          ,10);
+            emaw(4, MONS_SHAPESHIFTER         ,20);
+            emaw(5, MONS_GLOWING_SHAPESHIFTER ,20);
+            emaw(6, MONS_UGLY_THING           ,10);
+            emaw(7, MONS_VERY_UGLY_THING      ,10);
+            emaw(8, MONS_GUARDIAN_SERPENT     ,10);
+            emaw(9, MONS_WRETCHED_STAR        , 4);
+            //Anything else? sky beast is a bit too wimpy
+            break;
+           
+        case ZIGF_XOM:
+            // for the sake of a non emaw weighting
+            // doesn't matter where
+            //see _xom_mons for the rest
+            natives(80 , "D:1");
+            emaw(0, MONS_SHAPESHIFTER          ,10);
+            emaw(1, MONS_GLOWING_SHAPESHIFTER  ,10);
+            emaw(2, MONS_UGLY_THING            ,10);
+            emaw(3, MONS_VERY_UGLY_THING       ,10);
+            emaw(4, MONS_KILLER_KLOWN          ,10);
+           
+            break;
+
+        case ZIGF_CHEI:
+            //all these have haste stripped in _chei_mons
+            emaw(0,  MONS_ELEPHANT_SLUG         ,10);
+            emaw(1,  MONS_GREATER_NAGA          ,10);
+            emaw(2,  MONS_CYCLOPS               ,10);
+            emaw(3,  MONS_CRYSTAL_GOLEM         ,10);
+            emaw(4,  MONS_NAGA_MAGE             ,10);
+            emaw(5,  MONS_BOULDER_BEETLE        ,10);
+            emaw(6,  MONS_SATYR                 ,10);
+            emaw(7,  MONS_YAKTAUR_CAPTAIN       ,10);
+            emaw(8,  MONS_DEEP_TROLL_EARTH_MAGE ,10);
+            emaw(9,  MONS_VAMPIRE_MAGE          ,10);
+            emaw(10, MONS_ANCIENT_LICH          , 5);
+            emaw(11, MONS_GARGOYLE              , 5);
+            emaw(12, MONS_SPHINX                , 5);
+            emaw(13, MONS_GUARDIAN_MUMMY        ,10);
+            break;
+
+        case NUM_ZIGF:
+        default:
+            dprf("Got floor type: %d", (int)type);
+            die("invalid floor type");
+            break;
+    };
+#undef natives
+#undef emaw   
+    return 1;
+}
+
+//use to limit the number of monsters placed from env.mons_alloc
+//where said monsters are what makes the floor challenging
+//i.e. pan lords in pan floors
+//numbers here are currently arbitrary and only here for balancing
+//Todo: Change/add too these numbers after playtesting
+static bool veto_special(int num)
+{
+    switch (_floors[current_floor].floor) {
+        case ZIGF_PAN:
+            return num > 15;
+        case ZIGF_SPIDER:
+            return num > 20;
+        case ZIGF_TOMB:
+            return num > 20;
+           
+        default:
+            return false;
+            break;
+    }
+   
+   
+   
+}
+
+LUAFN(zig_monster)
+{
+    int how_many;
+    mgen_data mg;
+    monster_type mon = MONS_0;
+    bool xom_retype    = false;
+    bool pool_adjusted = false;
+    coord_def at = coord_def(luaL_checkint(ls, 1),luaL_checkint(ls, 2));
+    const bool awake = _floors[current_floor].genawake;
+    const int totw = _floors[current_floor].tot_weights;
+    how_many = you.depth * 5;
+    if (_floors[current_floor].floor == ZIGF_HOLY)
+    {
+        //waaay too many monsters on holy floors otherwise
+        how_many = div_rand_round(how_many, 3) ;
+    }
+   
+    //Todo: work out actual appropriate number of monsters
+    if (num_mons >= how_many)
+    {
+        lua_pushboolean(ls, 1);
+        return 1;
+    }
+#ifdef DEBUG_ZIGS
+    dprf("Monster generated %d ( of %d allowed) \n", num_mons, how_many);
+#endif
+    ASSERT(totw + _floors[current_floor].lid_weight >0  || _floors[current_floor].floor == ZIGF_DRACONIAN);
+    if (_floors[current_floor].floor == ZIGF_DRACONIAN)
+    {
+        mon = coinflip() ? RANDOM_BASE_DRACONIAN : RANDOM_NONBASE_DRACONIAN;
+    }
+    else if (x_chance_in_y(totw ,totw +_floors[current_floor].lid_weight )
+             || _floors[current_floor].lid_weight == 0
+             //&& !veto_special(num_mons_special)
+             )
+    {
+       
+#define weight(a) _mon_weights[(a)],env.mons_alloc[(a)]
+
+        mon = random_choose_weighted(weight(0),
+                                     weight(1),
+                                     weight(2),
+                                     weight(3),
+                                     weight(4),
+                                     weight(5),
+                                     weight(6),
+                                     weight(7),
+                                     weight(8),
+                                     weight(8),
+                                     weight(9),
+                                     weight(10),
+                                     weight(11),
+                                     weight(12),
+                                     weight(13),
+                                     weight(14),
+                                     weight(15),
+                                     weight(16),
+                                     weight(17),
+                                     weight(18),
+                                     weight(19),
+                                     0);
+        num_mons_special++;
+#undef weight
+#ifdef DEBUG_ZIGS
+        dprf("Monster is from env.mons_alloc \n");
+#endif
+       
+    }
+    else
+    {
+#ifdef DEBUG_ZIGS
+        printf("Monster is native \n");
+#endif
+        if(!_floors[current_floor].lid.is_valid())
+        {
+            printf(" Bad floor Branch =%d, Depth = %d",
+                   _floors[current_floor].lid.branch, _floors[current_floor].lid.depth );
+        }
+        int i =0;
+        do {
+            printf("in loop %d\n" ,i);
+            mon = pick_monster(_floors[current_floor].lid);
+        } while ((mon == MONS_SHARK || mon == MONS_KRAKEN || mon == MONS_NO_MONSTER) && i< 1000);
+       
+        xom_retype = true;
+
+       
+    }
+     
+    ASSERT(mon != MONS_0);
+   
+    mg = mgen_data(mon);
+    //if (_floors[current_floor].lid.is_valid())
+    //{
+    //    mg.place = _floors[current_floor].lid;
+    //}
+    monster * mons;
+    mg.pos = at;
+   
+    if (awake)
+        mg.behaviour = BEH_WANDER;
+    else
+        mg.behaviour = BEH_SLEEP;
+    if (_floors[current_floor].floor == ZIGF_XOM && xom_retype)
+    {
+        _random_xom_mons(at, awake);
+        pool_adjusted = true;
+        num_mons++;
+    }
+    else
+    {
+        if (mg.hd > _floors[current_floor].hdpool)
+        {
+            lua_pushboolean(ls, 1);
+            return 1;
+        }
+#ifdef DEBUG_ZIGS
+            printf("About to place monster %d\n",mon);
+#endif
+        mons = place_monster(mg);
+#ifdef DEBUG_ZIGS
+            printf("Placed monster %d\n",mon);
+#endif
+        num_mons++;
+    }
+    if (_floors[current_floor].floor == ZIGF_CHEI)
+    {
+        _chei_mons(mons);
+        pool_adjusted = true;
+    }
+   
+    if (!pool_adjusted)
+        _floors[current_floor].hdpool -= mons->hit_dice;
+    lua_pushboolean(ls, 0);
+    return 1;
+}
+// Optional: add Chimeras!!!! (and Xom effects)
+void _random_xom_mons( coord_def at,  bool awake)
+{
+   
+   
+    monster_type newmon = pick_monster_no_rarity(BRANCH_MAIN_DUNGEON);
+    mgen_data mg(newmon);
+    monster* mon;
+    mg.pos = at;
+   
+    //invert hd
+    //Todo: better inversion?
+    //
+    if (coinflip())
+        mg.hd = max(3, 30-mg.hd);
+    if (one_chance_in(5))
+    {
+        mg.hd += random2(5);
+    }
+    //Don't exceed the hd pool for this floor
+    mg.hd = min(mg.hd , _floors[current_floor].hdpool);
+   
+    if (awake)
+        mg.behaviour = BEH_WANDER;
+    else
+        mg.behaviour = BEH_SLEEP;
+
+    mon = place_monster(mg);
+    mon->god = GOD_XOM;
+   
+    //XXX: Xom floors seem to use much less of the available monster numbers
+    //Probably due to the hd fiddling. Adding a 2/3 here to try to change that
+    _floors[current_floor].hdpool -= mon->hit_dice*2/3;
+    int num_spells = 0;
+    if (!mon->flags & M_FAKE_SPELLS)
+    {
+        for (int i=0; i<6; i++)
+        {
+            if (mon->spells[i] != SPELL_NO_SPELL && mon->spells[i]!= SPELL_MELEE)
+                num_spells++;
+        }
+    }
+   
+    //Todo: add more random spell appropriate for
+    //  -Xom
+    //  -Monsters
+    //  -Correct spell slot
+    //*storm is currently a place holder
+    static const spell_type X1[]=
+    {
+        SPELL_FIRE_STORM,
+        SPELL_MAGIC_DART,
+        SPELL_LEHUDIBS_CRYSTAL_SPEAR,
+        SPELL_THROW_FROST,
+        SPELL_CHAIN_LIGHTNING,
+        SPELL_BOLT_OF_INACCURACY
+       
+       
+    };
+    static const spell_type X2[]=
+    {
+        SPELL_ICE_STORM,
+       
+    };
+    static const spell_type X3[]=
+    {
+        SPELL_SHATTER,
+           
+           
+    };
+    static const spell_type X4[]=
+    {
+        SPELL_ISKENDERUNS_MYSTIC_BLAST
+     
+    };
+    static const spell_type X5[]=
+    {
+        SPELL_TOMB_OF_DOROKLOHE,
+       
+    };
+    static const spell_type X6[]=
+    {
+        SPELL_SUMMON_BUTTERFLIES,
+       
+    };
+    static const spell_type* Xom_spells[6]=
+    {
+        (spell_type*)&X1,
+        (spell_type*)&X2,
+        (spell_type*)&X3,
+        (spell_type*)&X4,
+        (spell_type*)&X5,
+        (spell_type*)&X6,
+           
+    };
+   
+       
+    if (coinflip())
+    {
+        num_spells = 6 - num_spells;
+        for (int i=0; i<6; i++)
+        {
+            if (x_chance_in_y(num_spells  , 6))
+                mon->spells[i] = RANDOM_ELEMENT(Xom_spells[i]);
+        }
+        mon->flags |= M_SPELLCASTER;
+    }
+   
+    //Optional: Set more flags?
+    if (one_chance_in(10))
+    {
+        mon->flags |= M_INVIS;
+    }
+    if (one_chance_in(10))
+    {
+        mon->flags |= M_SEE_INVIS;
+    }
+    if (one_chance_in(10))
+    {
+        mon->flags |= M_BATTY;
+    }
+    //Optional: meddle with resists
+    //brimstone fiend with rF-?
+    //Xom roars with laughter
+   
+   
+    //swap AC/ev
+    if (coinflip())
+    {
+        int tmp = mon->ac;
+        mon->ac = mon->ev;
+        mon->ev = tmp;
+    }
+   
+}
+
+void _chei_mons(monster * mon)
+{
+    _floors[current_floor].hdpool -= mon->hit_dice;
+    int mod =2 + random2(4);
+    mon->speed -= mod;
+    mon->speed = max(mon->speed, 3);//3?
+    mon->hit_dice += mod - 1;
+   
+    mon->hit_dice= max(mon->hit_dice, 33);
+    // Note: should be interesting when monsters get invocations
+    // need to disable slouch or make it affect only you and your allies
+    // also limit the (ab)use of step from time
+    mon->god = GOD_CHEIBRIADOS;
+    if (mon->can_use_spells() )
+    {
+        for (int i=0; i<6; i++)
+        {
+            if (mon->spells[i] == SPELL_HASTE)
+                mon->spells[i] = mon->is_fighter() ? SPELL_BLINK_CLOSE     :
+                                 mon->is_archer()  ? SPELL_BLINK_AWAY      :
+                                                     SPELL_CONTROLLED_BLINK;
+            else if (mon->spells[i] == SPELL_HASTE_OTHER)
+                mon->spells[i] = coinflip() ? SPELL_SLOW
+                                            : SPELL_MIGHT_OTHER;
+            /* XXX:Alas no monster portal projectile - Yet.
+            if (mon->is_archer() && i == 4  //4? Correct slot
+                                 && one_chance_in(3)) //Too nasty?
+            {
+                mon->spells[i] = SPELL_PORTAL_PROJECTILE;
+            }
+            */
+            if (i==3 && //XXX: 3?
+                (mon->spells[i]== SPELL_NO_SPELL ||
+                 mon->spells[i] ==SPELL_MELEE )
+                && one_chance_in(3))
+            {
+                    mon->spells[i] = SPELL_BLINK_CLOSE;
+            }
+        }
+       
+    }
+   
+}
+
+LUAFN(zig_hd_pool)
+{
+    lua_pushinteger(ls, _floors[current_floor].hdpool);
+    return 1;
+}
+//Todo: Move this to lua
+/*
+vector<coord_def> build_isos_tri(int level, int windows, bool mirrored)
+{
+    coord_def top = coord_def( (GXM-1)/2 - ((GXM-1)/2)*level/27, GYM/2);
+    if (mirrored)
+        top  = coord_def(GXM,0) - top;
+    const int baseline = GXM -top.x;
+    const int height = level* 3 -1;
+    const int width  = level * 2;
+    bool last_window = false;
+    vector<coord_def> ret;
+    int minx,maxx;
+   
+    for (int i = 0; i < GXM;  i++)
+    {
+        for (int j = 0; j < GYM; j++)
+        {
+            //if it's outside the triangle
+            //below the base or above the top
+            if ((mirrored &&
+                 ((i < baseline -1 || i > top.x +1)
+                //in between the base and top but still outside
+                || abs(j-35) +1 >  i *width/height))
+               
+                ||
+               
+                ((i > baseline +1 || i < top.x -1)
+                || abs(j-GYM/2) +1 >  i *width/height))
+               
+            {
+                grd[i][j] = water ? DNGN_DEEP_WATER : DNGN_LAVA;
+                env.pgrid[i][j] |= FPROP_NO_TELE_INTO;
+                //Optional: todo sunlight/nighttime (halo and umbra)
+                //Optional: todo birds (harpys? bats?) and lightning (vapour?)
+            }
+            //else if it's on the border
+            else if ((mirrored && (i *width/height >  abs(j-GYM/2) - 1
+                    && i *width/height < abs(j-GYM/2) + 1))
+                    ||
+                    ((GXM-i) *width/height > abs(j-GYM/2) - 1 &&
+                    (GXM-i) *width/height < abs(j-GYM) + 1))
+
+            {
+                //clump windows together
+                if ((last_window && coinflip())
+                    || one_chance_in(windows+1))
+                {
+                    last_window = true;
+                    grd[i][j] = DNGN_CLEAR_PERMAROCK_WALL;
+                }
+                else
+                {
+                    last_window = false;
+                    grd[i][j] = DNGN_PERMAROCK_WALL;
+                }
+            }
+            //else it's inside
+            else
+            {
+                grd[i][j] = DNGN_FLOOR;
+                if (j < minx)
+                    minx = j;
+                else if (j > maxx)
+                    maxx = j;
+                   
+                //if (i,j) is on the two rows next to the base line
+                // here be loot and monsters
+                if (i - baseline >0 && i-baseline < 3)
+                    ret.push_back(coord_def(i,j));
+            }
+            if (i==0 || j==0 || i== GXM || j== GYM)
+            {
+                grd[i][j] =  water ? DNGN_OPEN_SEA : DNGN_LAVA_SEA;
+            }
+           
+           
+        }
+       
+    }
+       
+   
+    grd(top) = DNGN_STONE_ARCH;
+    grd[baseline +1][GYM/2] = DNGN_STONE_STAIRS_DOWN_I;
+    ASSERT(minx!=maxx);
+    // Portal to next zig floor -- doing in lua
+    //grd[baseline +(mirrored ? 1:-1)][minx] = level == 26 ? DNGN_FLOOR : DNGN_PORTAL_ZIGGURAT;
+    //grd[baseline +(mirrored ? 1:-1)][maxx] = level == 26 ? DNGN_FLOOR : DNGN_?
+    return ret;
+   
+}
+*/
+static const struct luaL_reg zig_dlib[] =
+{
+    { "set_floor_type", zig_set_floor_type },
+    { "set_level"     , zig_set_level      },
+    { "dump_all"      , zig_dump_all       },
+    { "floor_type"    , zig_floor_type     },
+    { "monster"       , zig_monster        },
+    { "init"          , zig_init           },
+    { "hd_pool"       , zig_hd_pool        },
+    { NULL            , NULL               }
+};
+
+
+void dluaopen_zig(lua_State *ls)
+{
+    luaL_openlib(ls, "ziggurat", zig_dlib, 0);
+}
+


Dont apply this yet, in particular the spell sets for Xom floors are very unfinished (because I don't know what spells are allowed in what slots) , but implementing the other todos XXX's and Optionals is a good idea as well.

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Tuesday, 9th July 2013, 08:43

Re: Implementing Zig proposal from Wiki

Grandiloquent Gentleman wrote:I'm already having too
much fun play testing your demigod branch ;) . (Btw, where do you want feedback, bug reports ect. for that to go?)


I guess either start a new thread, or comment on the wiki page: https://crawl.develz.org/wiki/doku.php?id=dcss:brainstorm:species:demigod

There was an ancient thread about it but quite a lot has changed about the implementation since then anyway.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Tuesday, 9th July 2013, 12:37

Re: Implementing Zig proposal from Wiki

Do you think It would be worth making a CYC (or GDD) thread to populate the list of spells of Xom monsters?
(Also I created a Wiki [url="crawl.develz.org/wiki/doku.php?id=dcss:brainstorm:species:demigod_feedback"]page[/url] for the feedback but can't seem to get it to list in brainstorm:species, and I apoligise for the in soviet Russia Joke I couldn't resist)

Dungeon Master

Posts: 1613

Joined: Thursday, 16th December 2010, 21:54

Post Tuesday, 9th July 2013, 17:19

Re: Implementing Zig proposal from Wiki

Zigs probably shouldn't really be messing with monster spell lists - and special-casing a bunch of monsters that are mostly entirely unrelated to Chei certainly doesn't seem very thematic, anyway. If the re-implementation makes it easier to adjust the simpler stuff like layouts and monster placements and so on then that's pretty cool though.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Wednesday, 10th July 2013, 04:37

Re: Implementing Zig proposal from Wiki

Actually, I thought the chei set was pretty thematic,with an emphasis on "time" (e.g Ancient Lich, Sphinx) "Slow" (e.g. greater naga, cyclops, guardian mummy, elephant slug ect.) we could remove satyr, Yaktur Captain and DTEM but I would like to keep the Vampire mage. (the extra translocations spells are to (hopefully) reduce the ability of the player to kite the monsters (not that this would be a good idea for the player anyway), and what sane chei worshiper wouldn't invest in tloc?) The blinks are also a replacement for haste for those that would normally have it.

Also, do you have any reservations with meddling with stuff on Xom floors( its about the only thing that make it interesting , other than a few Killer Klowns)?

Layouts are still done in lua ( so their difficulty to change hasn't changed ), but the ability to adjust weights, monster sets add new types of floors should be easier and more intuitive. As should the ability change the weightings of the floors (depending on depth, number of times used ect.)

Also a few other things curse skulls are still being generated as central "furnishings" even though they can now move(I don't know where to change this).

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Friday, 12th July 2013, 06:29

Re: Implementing Zig proposal from Wiki

OK, I'm working on surrounding the zig with lava or deep water. To restrict the player from leaving I need to set no_tele_into.
I would also like to set daytime/nighttime using halo (sunshine is halo) and umbra. How are these done in lua? (done through kfeat/prop?)
Also how do I stop stuff spawning in the water/lava?
  Code:
local function ziggurat_ellipse_builder(e)
  local grid = dgn.grid

  local zig_exc = zig().zig_exc

  local area = map_area()
  local b = math.floor(math.sqrt(200*area/(200+zig_exc) * 100/314))
  local a = math.floor(b * (200+zig_exc) / 200)
  local cx, cy = dgn.GXM / 2, dgn.GYM / 2

  local floor     = dgn.fnum("floor")
  local prwall    = dgn.fnum("permarock_wall")
  local surround  = dgn.fnum(zig().surrounds)
  local perimeter = dgn.fnum(zig().surrounds == "deep_water" and "open_sea" or "endless_lava")
 
  for x=0, dgn.GXM-1 do
    for y=0, dgn.GYM-1 do
      if x==0 or y== 0  or x == dgn.GXM-1 or y== dgn.GYM-1 then
          grid(x, y, perimeter)
      elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= a*a*b*b then
        grid(x, y, floor)
      elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= (a+2)*(a+1)*(b+2)*(b+1) then
        grid(x, y, prwall)
      else
        grid(x, y, surround)
       
      end
    end
  end

  local entry = dgn.point(cx-a+2, cy)
  local exit  = dgn.point(cx+a-2, cy)

  if you.depth() % 2 == 0 then
    entry, exit = exit, entry
  end

  ziggurat_stairs(entry, exit)
  ziggurat_furnish(dgn.point(cx, cy), entry, exit)
end

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Friday, 12th July 2013, 08:04

Re: Implementing Zig proposal from Wiki

Grandiloquent Gentleman wrote:OK, I'm working on surrounding the zig with lava or deep water. To restrict the player from leaving I need to set no_tele_into.


Use e.kmask(...) or e.kprop(...) for stuff like this (I can't remember which one offhand).

Grandiloquent Gentleman wrote:I would also like to set daytime/nighttime using halo (sunshine is halo) and umbra. How are these done in lua?


I'd avoid using halos. Better to just set a tileset for the level, and modify a feature description if needed. Halos can't even be manipulated from Lua as far as I know.

Grandiloquent Gentleman wrote:Also how do I stop stuff spawning in the water/lava?


If you tag the whole map with no_monster_gen this should cover it?

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Friday, 12th July 2013, 08:29

Re: Implementing Zig proposal from Wiki

mumra wrote:I'd avoid using halos. Better to just set a tileset for the level, and modify a feature description if needed. Halos can't even be manipulated from Lua as far as I know.


Pardon my ignorance, but how do I do that. Is that like the holy pan with golden statues and silver and golden walls and stuff?

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Friday, 12th July 2013, 09:02

Re: Implementing Zig proposal from Wiki

Look at the wizlab maps and Lua code; they do a lot of manipulation of tilesets and features.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Friday, 12th July 2013, 12:30

Re: Implementing Zig proposal from Wiki

no_monster_gen is working
e.kprop is what I want but It assigns a property to a glyph. I suppose that I could assign no_tele_into to a glyph and then assign it to a point, but I don't know how to do that. Doing this would also allow me to the tile manipulation as well (Off I go to find out which tiles I need to use :| ).

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Friday, 12th July 2013, 13:18

Re: Implementing Zig proposal from Wiki

What you probably want to do is start out by filling the map with the glyph you're going to use (look for an extend_map call, you can specify the fill glyph there); then draw the zig walls and floors over the top with normal glyphs. So then at the end you can refer to the original fill glyph when defining the outsides.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Thursday, 18th July 2013, 10:10

Re: Implementing Zig proposal from Wiki

OK, after a very inefficient rebase to master, I have a very silly question: How do you draw glyphs onto the map? Curently I am doing this, which is not working.

  Code:
  local grid = dgn.grid
  local zig_exc = zig().zig_exc
  local area = map_area()
  local b = math.floor(math.sqrt(200*area/(200+zig_exc) * 100/314))
  local a = math.floor(b * (200+zig_exc) / 200)
  local cx, cy = dgn.GXM / 2, dgn.GYM / 2
  local floor     = dgn.fnum("floor")
  local prwall    = dgn.fnum("permarock_wall")
  local perimeter = dgn.fnum(zig().surrounds == "deep_water" and "open_sea" or "endless_lava")
  local window    = dgn.fnum("clear_permarock_wall")

  e.kprop( zig().surrounds == "deep_water" and "w" or "l" .. " = no_tele_into")
  extend_map({width = dgn.GXM , height = dgn.GYM , fill = zig().surrounds == "deep_water" and 'w' or 'l'})
    for x=0, dgn.GXM-1 do
        for y=0, dgn.GYM-1 do
            if x==0 or y== 0  or x == dgn.GXM-1 or y== dgn.GYM-1 then
                grid(x, y, perimeter)
            elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= a*a*b*b then
                grid(x, y, floor)
            elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= (a+2)*(a)*(b+2)*(b) then
                if x == cx or y == cy then
                    grid(x, y, window)
                else
                    grid(x, y, prwall)
            end
        end
      end

Oh, and if there's a less hacky way of doing string to char conversion, do tell.

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Thursday, 18th July 2013, 14:33

Re: Implementing Zig proposal from Wiki

  Code:
local perimeter = zig().surrounds and "w" or "l"
-- In your loop:
e.mapgrd[x][y] = perimeter


Or you can do:

  Code:
e.mapgrd[x][y] = 'p' -- Perimeter
-- At the end:
e.subst('p = ' .. zig().surrounds and "w" or "l")


Or even:

  Code:
e.mapgrd[x][y] = 'p' -- Perimeter
-- At the end:
e.kfeat('p = ' .. zig().surrounds and "deep_water" or "lava")


etc.

Maybe you should look at some of the other layouts in dat/des/builder ... they all use (except Vaults) use various methods of drawing to mapgrd rather than grid. There are a lot of useful drawing functions that utilise mapgrd (these are partially documented in docs/devwiki) The only problem this could cause that I'm aware of is the way zig code uses subvaults to place pillars. At one point (ages ago, in my very first implementation of V) I tried similar code to place subvaults on a mapgrd-built map and this caused a crash. However if this is still the case and you run into that problem I'd rather fix the crash than force you to use grid because of it. mapgrd is generally more pleasant to work with.

Edit: note - I realised after I wrote this that I got the zig().surrounds usage wrong but anyway this should give you the idea.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Monday, 22nd July 2013, 06:00

Re: Implementing Zig proposal from Wiki

  Code:
  dprf("ziggurat_ellipse_builder(e)")
  local zig_exc = zig().zig_exc

  local area = map_area()
  local b = math.floor(math.sqrt(200*area/(200+zig_exc) * 100/314))
  local a = math.floor(b * (200+zig_exc) / 200)
  local cx, cy = dgn.GXM / 2, dgn.GYM / 2
 
  local surrounds, perimeter = zig().surrounds and "w","open_sea" or "l","endless_lava"
 
  dprf("extend map call pending")
  e.extend_map{width=gxm, height=gym, fill=perimeter}
  dprf("done")
  dprf("about to enter the loop")
  for x=1, dgn.GXM do
    for y=1, dgn.GYM do
      dprf("in loop", x,y)
      if x==1 or y== 1  or x == dgn.GXM or y== dgn.GYM then
        dprf("e.mapgrd[x][y] = 'p'")
        mapgrd[x][y] = "X"
      elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= a*a*b*b then
        dprf("e.mapgrd[x][y] = '.'")
        e.mapgrd[x][y] = "."
      elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= (a+2)*(a)*(b+2)*(b) then
        if x == cx or y == cy then
          dprf("e.mapgrd[x][y] = 'o'")
          e.mapgrd[x][y] = "o"
        else
          dprf("e.mapgrd[x][y] = 'X'")
          e.mapgrd[x][y] = "X"
        end
      else
        dprf("e.mapgrd[x][y] = surrounds")
        e.mapgrd[x][y] = surrounds
         
      end
    end
  end
  e.kprop( surrounds .. " = no_tele_into")
  e.kfeat("p = " .. perimeter)
  dprf("finished the loop")
  local entry = dgn.point(cx - a + 2, cy)
  local exit  = dgn.point(cx + a - 2, cy)

  if you.depth() % 2 == 0 then
    entry, exit = exit, entry
  end

  ziggurat_stairs(entry, exit)
  ziggurat_furnish(dgn.point(cx, cy), entry, exit)


something is not working.
For printouts I get
  Code:
ziggurat_ellipse_builder(e)
extend map call pending

which means It is failing at the extend_map call (I tried this with and w/o e.) and If I remove that It fails on the first mapgrd indexing ( again with and w/o e. and starting from 1 .. dgn.G(X|Y)M and from 0 .. dgn.G(X|Y)M - 1.

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Monday, 22nd July 2013, 11:39

Re: Implementing Zig proposal from Wiki

I'm not sure but I think the way you're assigning surrounds and perimeter might not work. Check the value of perimeter before the extend_map call. I think I've tried to do variable assignation like this before and it failed.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Monday, 22nd July 2013, 13:15

Re: Implementing Zig proposal from Wiki

  Code:
  dprf("ziggurat_ellipse_builder(e)")
  local zig_exc = zig().zig_exc

  local area = map_area()
  local b = math.floor(math.sqrt(200*area/(200+zig_exc) * 100/314))
  local a = math.floor(b * (200+zig_exc) / 200)
  local cx, cy = dgn.GXM / 2, dgn.GYM / 2

  local surrounds = zig().surrounds and 'w' or 'l'
  local perimeter = zig().surrounds and "open_sea" or "endless_lava"
  dprf("surrounds = ", surrounds)
  e.kprop( surrounds .. " = no_tele_into")
  e.kfeat("p = " .. perimeter)
  dprf("extend map call pending")
  e.extend_map{width=gxm, height=gym, fill=surrounds}
  dprf("done")
  dprf("about to enter the loop")
  for x=0, dgn.GXM - 1 do
    for y=0, dgn.GYM  - 1 do
      dprf("in loop", x,y)
      if x == 0 or y == 0  or x == dgn.GXM -1 or y == dgn.GYM -1 then
        --dprf("e.mapgrd[x][y] = 'p'")
        --e.mapgrd[x][y] = 'p'
      elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= a*a*b*b then
        dprf("e.mapgrd[x][y] = '.'")
        e.mapgrd[x][y] = '.'
      elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= (a+2)*(a)*(b+2)*(b) then
        if x == cx or y == cy then
          dprf("e.mapgrd[x][y] = 'o'")
          e.mapgrd[x][y] = 'o'
        else
          dprf("e.mapgrd[x][y] = 'X'")
          e.mapgrd[x][y] = 'X'
        end
      else
        --dprf("e.mapgrd[x][y] = surrounds")
        --e.mapgrd[x][y] = surrounds
         
      end
    end
  end

  dprf("finished the loop")
  local entry = dgn.point(cx - a + 2, cy)
  local exit  = dgn.point(cx + a - 2, cy)

  if you.depth() % 2 == 0 then
    entry, exit = exit, entry
  end

  ziggurat_stairs(entry, exit)
  ziggurat_furnish(dgn.point(cx, cy), entry, exit)


the extend_map call is now working but the first indexing to e.mapgrd fails

  Code:
choose_random_floor(z)
chose   Zot
ziggurat_ellipse_builder(e)
surrounds =    w
extend map call pending
done
about to enter the loop
in loop   0   0
in loop   0   1
in loop   0   2
in loop   0   3
in loop   0   4
in loop   0   5
in loop   0   6
in loop   0   7
in loop   0   8
in loop   0   9
in loop   0   10
in loop   0   11
in loop   0   12
in loop   0   13
in loop   0   14
in loop   0   15
in loop   0   16
in loop   0   17
in loop   0   18
in loop   0   19
in loop   0   20
in loop   0   21
in loop   0   22
in loop   0   23
in loop   0   24
in loop   0   25
in loop   0   26
in loop   0   27
in loop   0   28
in loop   0   29
in loop   0   30
in loop   0   31
in loop   0   32
in loop   0   33
in loop   0   34
in loop   0   35
in loop   0   36
in loop   0   37
in loop   0   38
in loop   0   39
in loop   0   40
in loop   0   41
in loop   0   42
in loop   0   43
in loop   0   44
in loop   0   45
in loop   0   46
in loop   0   47
in loop   0   48
in loop   0   49
in loop   0   50
in loop   0   51
in loop   0   52
in loop   0   53
in loop   0   54
in loop   0   55
in loop   0   56
in loop   0   57
in loop   0   58
in loop   0   59
in loop   0   60
in loop   0   61
in loop   0   62
in loop   0   63
in loop   0   64
in loop   0   65
in loop   0   66
in loop   0   67
in loop   0   68
in loop   0   69
in loop   1   0
in loop   1   1
in loop   1   2
in loop   1   3
in loop   1   4
in loop   1   5
in loop   1   6
in loop   1   7
in loop   1   8
in loop   1   9
in loop   1   10
in loop   1   11
in loop   1   12
in loop   1   13
in loop   1   14
in loop   1   15
in loop   1   16
in loop   1   17
in loop   1   18
in loop   1   19
in loop   1   20
in loop   1   21
in loop   1   22
in loop   1   23
in loop   1   24
in loop   1   25
in loop   1   26
in loop   1   27
in loop   1   28
in loop   1   29
in loop   1   30
in loop   1   31
in loop   1   32
in loop   1   33
in loop   1   34
in loop   1   35
in loop   1   36
in loop   1   37
in loop   1   38
in loop   1   39
in loop   1   40
in loop   1   41
in loop   1   42
in loop   1   43
in loop   1   44
in loop   1   45
in loop   1   46
in loop   1   47
in loop   1   48
in loop   1   49
in loop   1   50
in loop   1   51
in loop   1   52
in loop   1   53
in loop   1   54
in loop   1   55
in loop   1   56
in loop   1   57
in loop   1   58
in loop   1   59
in loop   1   60
in loop   1   61
in loop   1   62
in loop   1   63
in loop   1   64
in loop   1   65
in loop   1   66
in loop   1   67
in loop   1   68
in loop   1   69
in loop   2   0

continues on until

  Code:
in loop   35   34
in loop   35   35
e.mapgrd[x][y] = 'o'


and fails (again irrespective of e.mapgrd[x][y] or mapgrd[x][y]).

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Monday, 22nd July 2013, 22:54

Re: Implementing Zig proposal from Wiki

Couple of things I notice:

- You should use math.floor when calculating cx and cy, since you might end up with a fractional value, so "x == cx or y == cy" might not work
- Should that be "and" instead of "or"?
- Kind of looks like there is something wrong with the 'o' glyph but I don't know why, is it being set to anything?
- gxm and gym seem to be uninitialised in your extend_map call, since elsewhere you are using dgn.GXM and dgn.GYM instead

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Tuesday, 23rd July 2013, 01:28

Re: Implementing Zig proposal from Wiki

GXM and GYM are 70 and 80 so fractions shouldn't be an issue (and aren't because it's trying to place an 'o' )

I don't think the issue is the 'o' glyph. If I uncomment the commented lines in the loop, it fails on the first index of mapgrd no matter what the glyph is.

I completely missed the uninitialised gym, and gxm but fixing that doesn't seem to have solved the problem.

mumra wrote:Couple of things I notice:
- Should that be "and" instead of "or"?

which line?

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Tuesday, 23rd July 2013, 15:30

Re: Implementing Zig proposal from Wiki

Grandiloquent Gentleman wrote:GXM and GYM are 70 and 80 so fractions shouldn't be an issue (and aren't because it's trying to place an 'o' )


I tend to worry about floating point errors when dividing numbers anyway, but Lua possibly works around such problems.
Grandiloquent Gentleman wrote:I completely missed the uninitialised gym, and gxm but fixing that doesn't seem to have solved the problem.


Can you post the updated code, that was the only reason why assigning mapgrd should fail. Can you also post the map header (i.e. the bit from the .des file)?

Grandiloquent Gentleman wrote:
mumra wrote:Couple of things I notice:
- Should that be "and" instead of "or"?

which line?


I meant in "if x == cx or y == cy" because I thought you were trying to draw a single glyph in the center but maybe you intended to draw a cross?

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Wednesday, 24th July 2013, 03:07

Re: Implementing Zig proposal from Wiki

The des file is just
  Code:
NAME: ziggurat1
PLACE: Zig
: ziggurat_level(_G)
MAP
ENDMAP


This is the lua code
  Code:
------------------------------------------------------------------------------
-- ziggurat.lua:
--
-- Code for ziggurats.
--
-- Important notes:
-- ----------------
-- Functions that are attached to Lua markers' onclimb properties
-- cannot be closures, because Lua markers must be saved and closure
-- upvalues cannot (yet) be saved.
-- monster generation (placement and choice) in done in l_zig.cc
-- accessed via ziggurat.*
------------------------------------------------------------------------------

require("dlua/lm_toll.lua")

-- Deepest you can go in a ziggurat - at this point it's beyond
-- obvious that we're not challenging the player, and one could hope
-- she has enough loot by now.
ZIGGURAT_MAX = 27
DEBUG = false
function set_debug()
    DEBUG = true
end

function dprf( ...)
    --if DEBUG then
        print( unpack(arg))
    --end
end

function zig()
    if not dgn.persist.ziggurat then
    dgn.persist.ziggurat = { entry_fee = 0 }
    -- Initialise here to handle ziggurats accessed directly by &pq.
    initialise_ziggurat(dgn.persist.ziggurat)
  end
  return dgn.persist.ziggurat
end

local wall_colours = {
  "blue", "red", "lightblue", "magenta", "green", "white"
}

function choose_random_floor(z)
    dprf("choose_random_floor(z)")
    local info = z.floors_info
    local weights = {}
    util.foreach(info,  function (i)
                            table.insert(weights,
                            --To ensure diversity we halve the weight for each time the floor is used
                                         { weight = (2 ^ (8 - i.times_chosen)),
                                           ftype = i.ftype
                                         })
                        end
                 )
    local floor = util.random_weighted_from("weight",weights)
    dprf("chose" ,floor and floor.ftype or "Air")
    if not floor then
        ziggurat.set_floor_type("Air")
    end
    ziggurat.set_floor_type(floor.ftype)
        util.foreach(info,
                 function(x)
                    if floor.ftype == x.ftype then
                        x.times_chosen = x.times_chosen + 1
                    end
                 end)
end

function initialise_ziggurat(z, portal)
  if portal then
    z.entry_fee = portal.props.amount
  end

  -- Any given ziggurat will use the same builder for all its levels,
  -- and the same colours for outer walls. Before choosing the builder,
  -- we specify a global excentricity. If zig_exc=0, then the ellipses
  -- will be circles etc. It is not the actual excentricity but some
  -- value between 0 and 100. For deformed ellipses and rectangles, make
  -- sure that the map is wider than it is high for the sake of ASCII.
 
 
  z.zig_exc = crawl.random2(101)
  z.builder = util.random_from(util.keys(ziggurat_builder_map))
  z.colour  = util.random_from(wall_colours)
  --ftype Must match l_zig.cc::string2zigf
  z.floors_info =  {{ times_chosen = 0 ,ftype = "Air"    },
                    { times_chosen = 0 ,ftype = "Abyss"  },
                    { times_chosen = 0 ,ftype = "Chei"   },
                    { times_chosen = 0 ,ftype = "Coc"    },
                    { times_chosen = 0 ,ftype = "Crypt"  },
                    { times_chosen = 0 ,ftype = "Dis"    },
                    { times_chosen = 0 ,ftype = "Drac"   },
                    { times_chosen = 0 ,ftype = "Dragon" },
                    { times_chosen = 0 ,ftype = "Dwarf"  },
                    { times_chosen = 0 ,ftype = "Earth"  },
                    { times_chosen = 0 ,ftype = "Elf"    },
                    { times_chosen = 0 ,ftype = "Fire"   },
                    { times_chosen = 0 ,ftype = "Forest" },
                    { times_chosen = 0 ,ftype = "Geh"    },
                    { times_chosen = 0 ,ftype = "Holy"   },
                    { times_chosen = 0 ,ftype = "Ice"    },
                    { times_chosen = 0 ,ftype = "Lair"   },
                    { times_chosen = 0 ,ftype = "Mut"    },
                    { times_chosen = 0 ,ftype = "Orc"    },
                    { times_chosen = 0 ,ftype = "Pan"    },
                    { times_chosen = 0 ,ftype = "Range"  },
                    { times_chosen = 0 ,ftype = "Shoal"  },
                    { times_chosen = 0 ,ftype = "Slime"  },
                    { times_chosen = 0 ,ftype = "Snake"  },
                    { times_chosen = 0 ,ftype = "Spider" },
                    { times_chosen = 0 ,ftype = "Swamp"  },
                    { times_chosen = 0 ,ftype = "Tar"    },
                    { times_chosen = 0 ,ftype = "Tomb"   },
                    { times_chosen = 0 ,ftype = "Vault"  },
                    { times_chosen = 0 ,ftype = "Xom"    },
                    { times_chosen = 0 ,ftype = "Zot"    }
                   }
  --Todo Implement these in the builders
  z.surrounds = crawl.coinflip()
  --  1 == halo (sunlight is halo), 0 == nothing, -1 == night (umbra,gloom?)
  z.tod = crawl.random2(3) - 1
   
end

function callback.ziggurat_initialiser(portal)
  dgn.persist.ziggurat = { }
  initialise_ziggurat(dgn.persist.ziggurat, portal)
end

-- Common setup for ziggurat entry vaults.
function ziggurat_portal(e, spawnrange)
  dprf("ziggurat_portal(e, spawnrange)")
  local d = crawl.roll_dice
  local entry_fee =
    10 * math.floor(200 + d(3,200) / 3 + d(10) * d(10) * d(10))

  local function stair()
    return toll_stair {
      amount = entry_fee,
      toll_desc = "to enter a ziggurat",
      desc = "gateway to a ziggurat",
      overview = "Ziggurat",
      overview_note = "" .. entry_fee .. " gp",
      dst = "Zig:1",
      dstname = "ziggurat",
      floor = "stone_arch",
      feat_tile = "dngn_portal_ziggurat_gone",
      onclimb = "callback.ziggurat_initialiser"
    }
  end

  if spawnrange == "shallow" then
    e.tags("chance_shallow_zig extra")
    e.chance("1%")
  elseif spawnrange == "deep" then
    e.tags("chance_zig extra allow_dup luniq_zig")
    e.chance("5%")
  elseif spawnrange == "pan" then
    e.tags("chance_pan_zig extra allow_dup")
    e.chance("8%")
  end

  e.lua_marker("O", stair)
  e.kfeat("O = enter_portal_vault")
  e.tile("O = dngn_portal_ziggurat")
end

-- Common setup for ziggurat levels.
function ziggurat_level(e)
  e.tags("allow_dup")
  e.tags("no_dump")
  e.orient("encompass")
  e.tags("no_monster_gen")
  ziggurat.set_level(you.depth())
  choose_random_floor(zig())
  ziggurat.init()
  if crawl.game_started() then
    ziggurat_build_level(e)
  end
end

-----------------------------------------------------------------------------
-- Ziggurat level builders.


function ziggurat_build_level(e)

  local builder = zig().builder

  -- Deeper levels can have all monsters awake.
  -- Does never happen at depths 1-4; does always happen at depths 25-27.
  -- Done in l_zig.cc
  -- Deeper levels may block controlled teleports.
  -- Does never happen at depths 1-6; does always happen at depths 25-27.
  -- not done in l_zig.cc because I don't know how
  if you.depth() > 6 + crawl.random2(19) then
    dgn.change_level_flags("no_tele_control")
  end

  if builder then
    return ziggurat_builder_map[builder](e)
  end
end

local zigstair = dgn.gridmark

-- the estimated total map area for ziggurat maps of given depth
-- this is (almost) independent of the layout type
local function map_area()
  return 30 + 18*you.depth() + you.depth()*you.depth()
end

local function clamp_in(val, low, high)
  if val < low then
    return low
  elseif val > high then
    return high
  else
    return val
  end
end

local function clamp_in_bounds(x, y)
  return clamp_in(x, 2, dgn.GXM - 3), clamp_in(y, 2, dgn.GYM - 3)
end

local function set_wall_tiles()
  local tileset = {
    blue      = "wall_zot_blue",
    red       = "wall_zot_red",
    lightblue = "wall_zot_cyan",
    magenta   = "wall_zot_magenta",
    green     = "wall_zot_green",
    white     = "wall_vault"
  }

  local wall = tileset[zig().colour]
  if (wall == nil) then
    wall = "wall_vault"
  end
  dgn.change_rock_tile(wall)
end


local function ziggurat_vet_monster(fmap)
   
  local fn = fmap.fn
  fmap.fn = function (x, y, nth, hdmax)
              if nth >= dgn.MAX_MONSTERS then
                return nil
              end
              for i = 1, 10 do
                local mons = fn(x, y, nth)
                if mons then
                  -- Discard zero-exp monsters, and monsters that explode
                  -- the HD limit.
                  if mons.experience == 0 or mons.hd > hdmax * 1.3 then
                    mons.dismiss()
                  else
                    -- Monster is ok!
                    return mons
                  end
                end
              end
              -- Give up.
              return nil
            end
  return fmap
end



-- Function to find travel-safe squares, excluding closed doors.
local dgn_passable = dgn.passable_excluding("closed_door")

local function ziggurat_create_monsters(p, mfn)
--  local hd_pool = you.depth() * (you.depth() + 8)
-- (was depth * (depth + 8) before and too easy)

  local function mons_place(point)
    if ziggurat.hd_pool() <= 0 then
      return true
    elseif not dgn.mons_at(point.x, point.y) then
      --mons_do_place(point)
      return ziggurat.monster(point.x, point.y)
    end
  end

  dgn.find_adjacent_point(p, mons_place, dgn_passable)
end

local function ziggurat_create_loot_at(c)
  -- Basically, loot grows linearly with depth. However, the entry fee
  -- affects the loot randomly (separatedly on each stage).
  local depth = you.depth()
  local nloot = depth
  nloot = nloot + crawl.random2(math.floor(nloot * zig().entry_fee / 10000))

  local function find_free_space(nspaces)
    local spaces = { }
    local function add_spaces(p)
      if nspaces <= 0 then
        return true
      else
        table.insert(spaces, p)
        nspaces = nspaces - 1
      end
    end
    dgn.find_adjacent_point(c, add_spaces, dgn_passable)
    return spaces
  end

  -- dgn.good_scrolls is a list of items with total weight 1000
  -- Optional: give more !cMut at least one !restAb /HW (rot, stat drain for undead) on mutagenic floors
  -- and/or give some !bMut
  local good_loot = dgn.item_spec("* no_pickup w:7000 / " .. dgn.good_scrolls)
  local super_loot = dgn.item_spec("| no_pickup w:7000 /" ..
                                   "potion of experience no_pickup w:200 /" ..
                                   "potion of cure mutation no_pickup w:200 /" ..
                                   "potion of porridge no_pickup w:100 /" ..
                                   "wand of heal wounds no_pickup w:10 / " ..
                                   "wand of hasting no_pickup w:10 / " ..
                                   dgn.good_scrolls)

  local loot_spots = find_free_space(nloot * 4)

  if #loot_spots == 0 then
      dprf("no free space!")
    return
  end

  local curspot = 0
  local function next_loot_spot()
    curspot = curspot + 1
    if curspot > #loot_spots then
      curspot = 1
    end
    return loot_spots[curspot]
  end

  local function place_loot(what)
    local p = next_loot_spot()
    dgn.create_item(p.x, p.y, what)
  end

  for i = 1, nloot do
    if crawl.one_chance_in(depth) then
      for j = 1, 4 do
        place_loot(good_loot)
      end
    else
      place_loot(super_loot)
    end
  end
end

-- Suitable for use in loot vaults.
function ziggurat_loot_spot(e, key)
  e.lua_marker(key, portal_desc { ziggurat_loot = "X" })
  e.kfeat(key .. " = .")
  e.marker("@ = lua:props_marker({ door_restrict=\"veto\" })")
  e.kfeat("@ = +")
end

local has_loot_chamber = false

local function ziggurat_create_loot_vault(entry, exit)
  local inc = (exit - entry):sgn()

  local connect_point = exit - inc * 3
  local map = dgn.map_by_tag("ziggurat_loot_chamber")

  if not map then
    return exit
  end

  local function place_loot_chamber()
    local res = dgn.place_map(map, true, true)
    if res then
      has_loot_chamber = true
    end
    return res
  end

  local function good_loot_bounds(map, px, py, xs, ys)
    local vc = dgn.point(px + math.floor(xs / 2),
                         py + math.floor(ys / 2))


    local function safe_area()
      local p = dgn.point(px, py)
      local sz = dgn.point(xs, ys)
      local floor = dgn.fnum("floor")
      return dgn.rectangle_forall(p, p + sz - 1,
                                  function (c)
                                    return dgn.grid(c.x, c.y) == floor
                                  end)
    end

    local linc = (exit - vc):sgn()
    -- The map's positions should be at the same increment to the exit
    -- as the exit is to the entrance, else reject the place.
    return (inc == linc) and safe_area()
  end

  local function connect_loot_chamber()
    return dgn.with_map_bounds_fn(good_loot_bounds, place_loot_chamber)
  end

  local res = dgn.with_map_anchors(connect_point.x, connect_point.y,
                                   connect_loot_chamber)
  if not res then
    return exit
  else
    -- Find the square to drop the loot.
    local lootx, looty = dgn.find_marker_position_by_prop("ziggurat_loot")

    if lootx and looty then
      return dgn.point(lootx, looty)
    else
      return exit
    end
  end
end

local function ziggurat_locate_loot(entrance, exit, jelly_protect)
  if jelly_protect then
    return ziggurat_create_loot_vault(entrance, exit)
  else
    return exit
  end
end

local function ziggurat_place_pillars(c)
  dprf("ziggurat_place_pillars(c)")
  local range = crawl.random_range
  local floor = dgn.fnum("floor")

  local map, vplace = dgn.resolve_map(dgn.map_by_tag("ziggurat_pillar"))
 
  if not map then
    return
  end
  dprf("found pillar")
  local name = dgn.name(map)

  local size = dgn.point(dgn.mapsize(map))

  -- Does the pillar want to be centered?
  local centered = string.find(dgn.tags(map), " centered ")
  dprf(centered and "Centered" or "Not centered")
  local function good_place(p)
    local function good_square(where)
      return dgn.grid(where.x, where.y) == floor
    end
    return dgn.rectangle_forall(p, p + size - 1, good_square)
  end

  local function place_pillar()
    if centered then
      if good_place(c) then
        return dgn.place_map(map, true, true, c.x, c.y)
      end
    else
      for i = 1, 100 do
        local offset = range(-15, -size.x)
        local offsets = {
          dgn.point(offset, offset) - size + 1,
          dgn.point(offset - size.x + 1, -offset),
          dgn.point(-offset, -offset),
          dgn.point(-offset, offset - size.y + 1)
        }

        offsets = util.map(function (o)
                             return o + c
                           end, offsets)

        if util.forall(offsets, good_place) then
          local function replace(at, hflip, vflip)
            dgn.reuse_map(vplace, at.x, at.y, hflip, vflip)
          end

          replace(offsets[1], false, false)
          replace(offsets[2], false, true)
          replace(offsets[3], true, false)
          replace(offsets[4], false, true)
          return true
        end
      end
    end
  end

  for i = 1, 5 do
    if place_pillar() then
      break
    end
  end
end

local function ziggurat_stairs(entry, exit)
  zigstair(entry.x, entry.y, "stone_arch", "stone_stairs_up_i")

  if you.depth() < ZIGGURAT_MAX then
    zigstair(exit.x, exit.y, "stone_stairs_down_i")
  else
    --Todo: Put "of the Ziggurat" artifact here
  end

  zigstair(exit.x, exit.y + 1, "exit_portal_vault")
  zigstair(exit.x, exit.y - 1, "exit_portal_vault")
end

local function ziggurat_furnish(centre, entry, exit)
  local has_loot_chamber = ziggurat.floor_type() == "Slime"

  -- Identify where we're going to place loot, but don't actually put
  -- anything down until we've placed pillars.
  local lootspot = ziggurat_locate_loot(entry, exit, has_loot_chamber)
  if not has_loot_chamber then
    -- Place pillars if we did not create a loot chamber.
    ziggurat_place_pillars(centre)
  end

  ziggurat_create_loot_at(lootspot)

  ziggurat_create_monsters(exit, ziggurat.monster)

  local function needs_colour(p)
    return not dgn.in_vault(p.x, p.y)
      and dgn.grid(p.x, p.y) == dgn.fnum("permarock_wall")
  end

  dgn.colour_map(needs_colour, zig().colour)
  set_wall_tiles()
end

local zig_mons = {
    "harpy",
    "bat",
    "vapour"
   
}

-- builds ziggurat maps consisting of two overimposed rectangles
local function ziggurat_rectangle_builder(e)
  local grid = dgn.grid
  dgn.fill_grd_area(0, 0, dgn.GXM - 1, dgn.GYM - 1, "permarock_wall")

  local area = map_area()
  area = math.floor(area*3/4)

  local cx, cy = dgn.GXM / 2, dgn.GYM / 2

  -- exc is the local eccentricity for the two rectangles
  -- exc grows with depth as 0-1, 1, 1-2, 2, 2-3 ...
  local exc = math.floor(you.depth() / 2)
  if ((you.depth()-1) % 2) == 0 and crawl.coinflip() then
    exc = exc + 1
  end

  local a = math.floor(math.sqrt(area+4*exc*exc))
  local b = a - 2*exc
  local a2 = math.floor(a / 2) + (a % 2)
  local b2 = math.floor(b / 2) + (b % 2)
  local x1, y1 = clamp_in_bounds(cx - a2, cy - b2)
  local x2, y2 = clamp_in_bounds(cx + a2, cy + b2)
  dgn.fill_grd_area(x1, y1, x2, y2, "floor")

  local zig_exc = zig().zig_exc
  local nx1 = cx + y1 - cy
  local ny1 = cy + x1 - cx + math.floor(you.depth()/2*(200-zig_exc)/300)
  local nx2 = cx + y2 - cy
  local ny2 = cy + x2 - cx - math.floor(you.depth()/2*(200-zig_exc)/300)
  nx1, ny1 = clamp_in_bounds(nx1, ny1)
  nx2, ny2 = clamp_in_bounds(nx2, ny2)
  dgn.fill_grd_area(nx1, ny1, nx2, ny2, "floor")

  local entry = dgn.point(x1, cy)
  local exit = dgn.point(x2, cy)

  if you.depth() % 2 == 0 then
    entry, exit = exit, entry
  end

  ziggurat_stairs(entry, exit)
  ziggurat_furnish(dgn.point(cx, cy), entry, exit)
end

-- builds elliptic ziggurat maps
-- given the area, half axes a and b are determined by:
-- pi*a*b=area,
-- a=b for zig_exc=0,
-- a=b*3/2 for zig_exc=100

local function ziggurat_ellipse_builder(e)
  dprf("ziggurat_ellipse_builder(e)")
  local zig_exc = zig().zig_exc

  local area = map_area()
  local b = math.floor(math.sqrt(200*area/(200+zig_exc) * 100/314))
  local a = math.floor(b * (200+zig_exc) / 200)
  local cx, cy = dgn.GXM / 2, dgn.GYM / 2

  local surrounds = zig().surrounds and 'w' or 'l'
  local perimeter = zig().surrounds and "open_sea" or "endless_lava"
  dprf("surrounds = ", surrounds)
  e.kprop( surrounds .. " = no_tele_into")
  e.kfeat("p = " .. perimeter)
  dprf("extend map call pending")
  e.extend_map{width=dgn.GXM, height=dgn.GYM, fill=surrounds}
  dprf("done")
  dprf("about to enter the loop")
  for x=0, dgn.GXM - 1 do
    for y=0, dgn.GYM  - 1 do
      dprf("in loop", x,y)
      if x == 0 or y == 0  or x == dgn.GXM -1 or y == dgn.GYM -1 then
        --dprf("e.mapgrd[x][y] = 'p'")
        --e.mapgrd[x][y] = 'p'
      elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= a*a*b*b then
        dprf("e.mapgrd[x][y] = '.'")
        mapgrd[x][y] = '.'
      elseif b*b*(cx-x)*(cx-x) + a*a*(cy-y)*(cy-y) <= (a+2)*(a)*(b+2)*(b) then
        if x == cx or y == cy then
          dprf("e.mapgrd[x][y] = 'o'")
          mapgrd[x][y] = 'o'
        else
          dprf("e.mapgrd[x][y] = 'X'")
          mapgrd[x][y] = 'X'
        end
      else
        --dprf("e.mapgrd[x][y] = surrounds")
        --e.mapgrd[x][y] = surrounds
         
      end
    end
  end

  dprf("finished the loop")
  local entry = dgn.point(cx - a + 2, cy)
  local exit  = dgn.point(cx + a - 2, cy)

  if you.depth() % 2 == 0 then
    entry, exit = exit, entry
  end

  ziggurat_stairs(entry, exit)
  ziggurat_furnish(dgn.point(cx, cy), entry, exit)
end


-- builds hexagonal ziggurat maps
local function ziggurat_hexagon_builder(e)
  local grid = dgn.grid
  dgn.fill_grd_area(0, 0, dgn.GXM - 1, dgn.GYM - 1, "permarock_wall")

  local zig_exc = zig().zig_exc

  local c = dgn.point(dgn.GXM, dgn.GYM) / 2
  local area = map_area()

  local a = math.floor(math.sqrt(2 * area / math.sqrt(27))) + 2
  local b = math.floor(a*math.sqrt(3)/4)

  local left = dgn.point(math.floor(c.x - (a + math.sqrt(2 * a)) / 2),
                         c.y)
  local right = dgn.point(2 * c.x - left.x, c.y)

  local floor = dgn.fnum("floor")

  for x = 1, dgn.GXM - 2 do
    for y = 1, dgn.GYM - 2 do
      local dlx = x - left.x
      local drx = x - right.x
      local dly = y - left.y
      local dry = y - right.y

      if dlx >= dly and drx <= dry
        and dlx >= -dly and drx <= -dry
        and y >= c.y - b and y <= c.y + b then
        grid(x, y, floor)
      end
    end
  end

  local entry = left + dgn.point(1,0)
  local exit  = right - dgn.point(1, 0)

  if you.depth() % 2 == 0 then
    entry, exit = exit, entry
  end

  ziggurat_stairs(entry, exit)
  ziggurat_furnish(c, entry, exit)
end
----------------------------------------------------------------------
ziggurat_builder_map = {
--  rectangle = ziggurat_rectangle_builder,
  ellipse = ziggurat_ellipse_builder,
--  hex = ziggurat_hexagon_builder
}


mumra wrote:I meant in "if x == cx or y == cy" because I thought you were trying to draw a single glyph in the center but maybe you intended to draw a cross?


I am trying to draw an ellipse with half axes a and b(floor,loot monsters ect.), and one with half axes a+2 and b+2 (permarock wall) with the first overlaid onto the second. So a cross on the second ellipse that will only leave the edges.

Btw I am attempting the ellipse builder because that is the only one that I can understand mathematically, I cant for the life of me figure out what is supposed to be going on in the rectangle builder let alone the hex builder.

Oh, and is dprf exposed to lua, I just hacked together one because I needed it.

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Wednesday, 24th July 2013, 07:32

Re: Implementing Zig proposal from Wiki

I think you may need to do a debug build of crawl so you might see an error when whatever goes wrong is going wrong?

You can use crawl.dpr to do a debug print (but you still need a debug build to see dprf output). I sometimes use a plain "print" statement, which writes to the console (start crawl from the command line since this allows you to see more error messages and so on anyway), this is often easier and simpler than writing to crawl's message window anyway.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Wednesday, 24th July 2013, 12:01

Re: Implementing Zig proposal from Wiki

Hmm, if I do &^t and enter the interpreter and do
  Code:
_G.mapgrd[5][5] ='o'

I get repl1: attempt to index field 'mapgrd' a nil value
Wow, even weirder If I try to build the floor a zig floor on D:1 by doing
  Code:
ziggurat_level(_G)

It tells me I'm trying to call a table value with
  Code:
e.tags("blah")

Dungeon Master

Posts: 1531

Joined: Saturday, 5th March 2011, 06:29

Post Tuesday, 30th July 2013, 15:46

Re: Implementing Zig proposal from Wiki

Ok, I think the problem might be availability of the 'e' variable.

Firstly, _G is only available in the context of a mapdef's Lua; so yeah, this is expected to fail when using &^t.

So if a level is built using code in an .lua file, you need to pass _G over into the function, and usually this is done using an 'e' parameter.

Do you actually pass e on into other functions you are calling that need mapgrd?

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Tuesday, 6th August 2013, 12:26

Re: Implementing Zig proposal from Wiki

Hmm, OK I fixed the e.mapgrd problem (I have absolutely no idea how, but, oh well)

I now have a problem that the builder is not running all my code before complaining that the level is invalid (a portal exit didn't generate).
Is there a time limit as how long a level can build for before running validity checks

My console out put is

  Code:
choose_random_floor(z)
chose   Elf
Arg = Elf
Done
Calling ziggurat.init()
Done
ziggurat_build_level(e)
ziggurat_ellipse_builder(e)
surrounds =    w
extend map call pending
done
about to enter the loop
in loop   0   0
in loop   0   1
in loop   0   2
in loop   0   3
in loop   0   4
in loop   0   5
in loop   0   6
...
in loop   75   55
in loop   75   56
in loop   75   57
in loop   75   58
in loop   75   59
in loop   75   60
in loop   75   61
in loop   75   62


stopping before x==79,y==69? (in a loop from x=0 to GXM-1,y=0 to GYM-1)
I should get a "finished the loop" print out after the rest of the in loop x y and then continue executing the rest of the function.

Swamp Slogger

Posts: 174

Joined: Thursday, 15th November 2012, 02:12

Post Tuesday, 20th August 2013, 09:20

Re: Implementing Zig proposal from Wiki

Weird, it complains no exit portal was generated (and bails) if I try to build Zig:1 (via both &pq> and &~q1) but not If I build Zig:2 (via &~q2) which is also generated with no exit portal or downstairs or snail statue (stone arch), but otherwise generated to my liking.

Return to Coding

Who is online

Users browsing this forum: No registered users and 6 guests

cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.