Skip to content
Snippets Groups Projects
Castle.py 140 KiB
Newer Older
################################################################################
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This is free software under the terms of the GNU General Public License
# you may redistribute it, and/or modify it.
#
# This code is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License (http://www.gnu.org/licenses/) for more details.
#
# ***** END GPL LICENSE BLOCK *****
'''
Generate an object: (OBJ_N) "Castle", complex mesh collection.
 Derived from many sources, inspired by many in the "community",
 - see documentation for details on use and credits.
'''
# mod list, in order of complexity and necessity...
#  some want, some needed, some a feature not a bug just need controls.
#
# @todo: fix dome radius -  not complete when wall width or depth < 15
#        globe (z/2) is fun but not intended design.
# @todo: check width minimum as is done for height, maybe depth too.
# @todo: fix grout for bottom of first row (adjust wall base).
#
# @todo: eleminate result of "None" if possible from  circ() and other subroutines.
# @todo: review defaults and limits for all UI entries.
#
#
# Abstract/experimental use only:
# @todo: Curve (wallSlope) inverts walls: reverse slope and adjust sizing to fit floor.
# @todo: Tunnel, paired sloped walls, need to create separate objects and adjust sizing.
# @todo: Turret/tower - uses rotated wall, need to change curvature/slope orientation/sizing
#  allow openings and placement (corners, center wall, etc.).
#
#
# wish list:
# @todo: do not allow opening overlap.
# @todo: implement "Oculus" for dome (top center opening). See "Radial" floor with door.
# @todo: make "true" roof: top of castle walls or turret, multiple levels, style options - peaked, flat.
# @todo: integrate portal with doorway.
# @todo: add stair_builder; keep (wall) steps.
# @todo: integrate balcony with shelf.
# @todo: add block shapes: triangle, hexagon, octagon, round (disc/ball), "Rocks" (Brikbot author), etc...
#        - possible window/door/opening shapes?
#
################################################################################

import bpy
from bpy.props import IntProperty, FloatProperty, FloatVectorProperty, BoolProperty, EnumProperty
from random import random

import math
from math import fmod, sqrt, sin, cos, atan

################################################################################

################################################################################

#####
# constants
#####

OBJ_N = "Castle"  # Primary object name (base/floor/foundation)
OBJ_CF = "CFloor"  # Multi-level "floor"
OBJ_CR = "CRoof"  # roofing
OBJ_WF = "CWallF"  # front, left, back, right wall objects
OBJ_WL = "CWallL"
OBJ_WB = "CWallB"
OBJ_WR = "CWallR"

# not sure if this is more efficient or not, just seems no need to import value.
cPie = 3.14159265359
cPieHlf = cPie / 2  # used to rotate walls 90 degrees (1.570796...)

BLOCK_MIN = 0.1  # Min block sizing; also used for openings.

WALL_MAX = 100
HALF_WALL = WALL_MAX / 2  # edging, openings, etc., limited to half wall limit.

WALL_MIN = BLOCK_MIN * 3  # min wall block size*3 for each dimension.
WALL_DEF = 20  # Default wall/dome size

LVL_MAX = 10  # castle levels (vertical wall repeat).

# riser BLOCK_XDEF should be 0.75 and tread BLOCK_DDEF 1.0 for stair steps.
BLOCK_XDEF = 0.75  # stanadard block width, including steps and shelf.
BLOCK_DDEF = 1.0  # standard block depth.
BLOCK_MAX = WALL_MAX  # Max block sizing.
BLOCK_VMAX = BLOCK_MAX / 2  # block variations, no negative values.

# gap 0 makes solid wall but affects other options, like openings, not in a good way.
# Negative gap creates "phantom blocks" (extraneous verts) inside the faces.
GAP_MIN = 0.01  # min space between blocks.
GAP_MAX = BLOCK_MAX / 2  # maybe later... -BLOCK_MIN # max space between blocks.

ROW_H_WEIGHT = 0.5  # Use 0.5, else create parameter:
#  0=no effect, 1=1:1 relationship, negative values allowed.

BASE_TMIN = BLOCK_MIN  # floor min thickness
BASE_TMAX = BLOCK_MAX / 2  # floor max thickness, limit to half (current) block height.

# Default Door settings
DEF_DOORW = 2.5
DEF_DOORH = 3.5
DEF_DOORX = 2.5

#####
# working variables, per option per level.
#####

# wall/block Settings
settings = {'w': 1.2, 'wv': 0.3, 'h': .6, 'hv': 0.1, 'd': 0.3, 'dv': 0.1,
            'g': 0.1, 'sdv': 0.1,
            'eoff': 0.3,
            'Steps': False, 'StepsL': False, 'StepsB': False, 'StepsO': False,
            'Shelf': False, 'ShelfO': False,
            'Radial': False, 'Slope': False}
# 'w':width 'wv':width Variation
# 'h':height 'hv':height Variation
# 'd':depth 'dv':depth Variation
# 'g':grout
# 'sdv':subdivision(distance or angle)
# 'eoff':edge offset
# passed as params since modified per object; no sense to change UI values.
# 'Steps' create steps, 'StepsL'step left, 'StepsB' fill with blocks, 'StepsO' outside of wall.
# 'Shelf' add shelf/balcony, 'ShelfO'outside of wall.
# 'Radial' create "disc"; makes dome when combined with "Slope" (globe when height/2).
# 'Slope' curve wall - forced for Dome

# dims = area of wall (centered/splitX from 3D cursor); modified for radial/dome.
dims = {'s': -10, 'e': 10, 't': 15}
# dims = {'s':0, 'e':cPie*3/2, 't':12.3} # radial
# 's' start, 'e' end, 't' top

# Apertures in wall, includes all openings for door, window, slot.
#openingSpecs = []
# openingSpecs indexes, based on order of creation.
OP_DOOR = 0
OP_PORT = 1
OP_CREN = 2
OP_SLOT = 3

openingSpecs = [{'a': False, 'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 0, 'n': 0, 'bvl': 0.0,
                 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
# 'a': active (apply to object),
# 'w': opening width, 'h': opening height,
# 'x': horizontal position, 'z': vertical position,
# 'n': repeat opening with a spacing of x,
# 'bvl': bevel the inside of the opening,
# 'v': height of the top arch, 'vl':height of the bottom arch,
# 't': thickness of the top arch, 'tl': thickness of the bottom arch


################################################################################

############################
#
# Psuedo macros:
#
# random values (in specific range).
# random value +-0.5
def rndc(): return (random() - 0.5)

# random value +-1


def rndd(): return rndc() * 2

# line/circle intercepts
# COff = distance perpendicular to the line to the center
# cRad=radius
# return the distance paralell to the line to the center of the circle at the intercept.
# @todo: eleminate result of "None" if possible from  circ() and other subroutines.


def circ(COff=0, cRad=1):
    absCOff = abs(COff)
    if absCOff > cRad:
        return None
    elif absCOff == cRad:
        return 0
    else:
        return sqrt(cRad**2 - absCOff**2)

# bevel blocks.
# pointsToAffect are: left=(4,6), right=(0,2)


def bevelBlockOffsets(offsets, bevel, pointsToAffect):
    for num in pointsToAffect:
        offsets[num] = offsets[num][:]
        offsets[num][0] += bevel

############################
#
# Simple material management.
# Return new, existing, or modified material reference.
#
# @todo: create additional materials based on diffuse options.
#


def uMatRGBSet(matName, RGBs, matMod=False, dShader='LAMBERT', dNtz=1.0):

    if matName not in bpy.data.materials:
        mtl = bpy.data.materials.new(matName)
        matMod = True
    else:
        mtl = bpy.data.materials[matName]

    if matMod:  # Set material values
        mtl.diffuse_color = RGBs
        mtl.diffuse_shader = dShader
        mtl.diffuse_intensity = dNtz

    return mtl

################################################################################

################################################################################
#
#  UI functions and object creation.
#


class add_castle(bpy.types.Operator):
    bl_idname = "mesh.add_castle"
    bl_label = OBJ_N
    bl_description = OBJ_N
    bl_options = {'REGISTER', 'UNDO'}

    # only create object when True
    # False allows modifying several parameters without creating object
    ConstructTog: BoolProperty(name="Construct", description="Generate the object", default=True)

    # Base area - set dimensions - Width (front/back) and Depth (sides),
    # floor origin/offset, thickness, and, material/color.
    cBaseW: FloatProperty(name="Width", min=WALL_MIN, max=WALL_MAX, default=WALL_DEF, description="Base Width (X)")
    cBaseD: FloatProperty(name="Depth", min=WALL_MIN, max=WALL_MAX, default=WALL_DEF, description="Base Depth (Y)")
    cBaseO: FloatProperty(name='Base', min=0, max=WALL_MAX, default=0, description="vert offset from 3D cursor.")
    cBaseT: FloatProperty(min=BASE_TMIN, max=BASE_TMAX, default=BASE_TMIN, description="Base thickness")
    cBaseRGB: FloatVectorProperty(min=0, max=1, default=(0.1, 0.1, 0.1), subtype='COLOR', size=3)
    CBaseB: BoolProperty(name="BloX", default=False, description="Block flooring")
    CBaseR: BoolProperty(default=False, description="Round flooring")
    CLvls: IntProperty(name="Levels", min=1, max=LVL_MAX, default=1)

    # current wall level parameter value display.
    CLvl: IntProperty(name="Level", min=1, max=LVL_MAX, default=1)
    #    curLvl=CLvl
    blockX: FloatProperty(name="Width", min=BLOCK_MIN, max=BLOCK_MAX, default=BLOCK_XDEF)
    blockZ: FloatProperty(name="Height", min=BLOCK_MIN, max=BLOCK_MAX, default=BLOCK_XDEF)
    blockD: FloatProperty(name="Depth", min=BLOCK_MIN, max=BLOCK_MAX, default=BLOCK_DDEF)
    # allow 0 for test cases...
    #    blockD=FloatProperty(name="Depth",min=0,max=BLOCK_MAX,default=BLOCK_DDEF)
    blockVar: BoolProperty(name="Vary", default=True, description="Randomize block sizing")
    blockWVar: FloatProperty(name="Width", min=0, max=BLOCK_VMAX, default=BLOCK_MIN, description="Random Width")
    blockHVar: FloatProperty(name="Height", min=0, max=BLOCK_VMAX, default=BLOCK_MIN, description="Random Height")
    blockDVar: FloatProperty(name="Depth", min=0, max=BLOCK_VMAX, default=BLOCK_MIN, description="Random Depth")
    blockMerge: BoolProperty(name="Merge", default=True, description="Merge closely adjoining blocks")
    # @todo: fix edgeing for mis-matched row sizing (or just call it a feature).
    EdgeOffset: FloatProperty(name="Edging", min=0, max=HALF_WALL, default=0.25, description="stagger wall ends")
    Grout: FloatProperty(name="Gap", min=GAP_MIN, max=GAP_MAX, default=0.05, description="Block separation")
    wallRGB: FloatVectorProperty(min=0, max=1, default=(0.5, 0.5, 0.5), subtype='COLOR', size=3)
    # track height with each level...?
    wallH: FloatProperty(name="Height", min=WALL_MIN, max=WALL_MAX, default=WALL_DEF)
    # Base, always true (primary object/parent)
    #    CFloor1=BoolProperty(default=True)
    CFloor2: BoolProperty(default=False)
    CFloor3: BoolProperty(default=False)
    CFloor4: BoolProperty(default=False)
    CFloor5: BoolProperty(default=False)
    CFloor6: BoolProperty(default=False)
    CFloor7: BoolProperty(default=False)
    CFloor8: BoolProperty(default=False)
    CFloor9: BoolProperty(default=False)
    CFloor10: BoolProperty(default=False)

    # Which walls to generate, per level - LVL_MAX
    wallF1: BoolProperty(default=False, description="Front")
    wallF2: BoolProperty(default=False)
    wallF3: BoolProperty(default=False)
    wallF4: BoolProperty(default=False)
    wallF5: BoolProperty(default=False)
    wallF6: BoolProperty(default=False)
    wallF7: BoolProperty(default=False)
    wallF8: BoolProperty(default=False)
    wallF9: BoolProperty(default=False)
    wallF10: BoolProperty(default=False)

    wallL1: BoolProperty(default=False, description="Left")
    wallL2: BoolProperty(default=False)
    wallL3: BoolProperty(default=False)
    wallL4: BoolProperty(default=False)
    wallL5: BoolProperty(default=False)
    wallL6: BoolProperty(default=False)
    wallL7: BoolProperty(default=False)
    wallL8: BoolProperty(default=False)
    wallL9: BoolProperty(default=False)
    wallL10: BoolProperty(default=False)

    wallB1: BoolProperty(default=False, description="Back")
    wallB2: BoolProperty(default=False)
    wallB3: BoolProperty(default=False)
    wallB4: BoolProperty(default=False)
    wallB5: BoolProperty(default=False)
    wallB6: BoolProperty(default=False)
    wallB7: BoolProperty(default=False)
    wallB8: BoolProperty(default=False)
    wallB9: BoolProperty(default=False)
    wallB10: BoolProperty(default=False)

    wallR1: BoolProperty(default=False, description="Right")
    wallR2: BoolProperty(default=False)
    wallR3: BoolProperty(default=False)
    wallR4: BoolProperty(default=False)
    wallR5: BoolProperty(default=False)
    wallR6: BoolProperty(default=False)
    wallR7: BoolProperty(default=False)
    wallR8: BoolProperty(default=False)
    wallR9: BoolProperty(default=False)
    wallR10: BoolProperty(default=False)

    # round walls per level - LVL_MAX
    wallFO1: BoolProperty(default=False, description="Front")
    wallFO2: BoolProperty(default=False)
    wallFO3: BoolProperty(default=False)
    wallFO4: BoolProperty(default=False)
    wallFO5: BoolProperty(default=False)
    wallFO6: BoolProperty(default=False)
    wallFO7: BoolProperty(default=False)
    wallFO8: BoolProperty(default=False)
    wallFO9: BoolProperty(default=False)
    wallFO10: BoolProperty(default=False)

    wallLO1: BoolProperty(default=False, description="Left")
    wallLO2: BoolProperty(default=False)
    wallLO3: BoolProperty(default=False)
    wallLO4: BoolProperty(default=False)
    wallLO5: BoolProperty(default=False)
    wallLO6: BoolProperty(default=False)
    wallLO7: BoolProperty(default=False)
    wallLO8: BoolProperty(default=False)
    wallLO9: BoolProperty(default=False)
    wallLO10: BoolProperty(default=False)

    wallBO1: BoolProperty(default=False, description="Back")
    wallBO2: BoolProperty(default=False)
    wallBO3: BoolProperty(default=False)
    wallBO4: BoolProperty(default=False)
    wallBO5: BoolProperty(default=False)
    wallBO6: BoolProperty(default=False)
    wallBO7: BoolProperty(default=False)
    wallBO8: BoolProperty(default=False)
    wallBO9: BoolProperty(default=False)
    wallBO10: BoolProperty(default=False)

    wallRO1: BoolProperty(default=False, description="Right")
    wallRO2: BoolProperty(default=False)
    wallRO3: BoolProperty(default=False)
    wallRO4: BoolProperty(default=False)
    wallRO5: BoolProperty(default=False)
    wallRO6: BoolProperty(default=False)
    wallRO7: BoolProperty(default=False)
    wallRO8: BoolProperty(default=False)
    wallRO9: BoolProperty(default=False)
    wallRO10: BoolProperty(default=False)
    #    CRoof=BoolProperty(name="Roof",default=False)

    # Select wall modifier option to view/edit
    wallOpView: EnumProperty(items=(
        ("1", "Door", ""),
        ("2", "Window", ""),
        ("3", "Slot", ""),  # "classical arrow/rifle ports
        ("4", "Crenel", ""),  # gaps along top of wall
        ("5", "Steps", ""),
        ("6", "Shelf", ""),
    ),
        name="", description="View/Edit wall modifiers", default="1")
    # could add material selection for step and shelf...

    # Radiating from one point - round (disc), with slope makes dome.
    cDome: BoolProperty(name='Dome', default=False)
    cDomeRGB: FloatVectorProperty(min=0, max=1, default=(0.3, 0.1, 0), subtype='COLOR', size=3)
    cDomeH: FloatProperty(name='Height', min=WALL_MIN, max=WALL_MAX, default=WALL_DEF / 2)
    cDomeZ: FloatProperty(name='Base', min=BASE_TMIN, max=WALL_MAX, default=WALL_DEF, description="offset from floor")
    cDomeO: FloatProperty(name='Oculus', min=0, max=HALF_WALL, default=0, description="Dome opening.")

    # holes/openings in wall - doors, windows or slots; affects block row size.
    wallDoorW: FloatProperty(name="Width", min=BLOCK_MIN, max=HALF_WALL, default=DEF_DOORW)
    wallDoorH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=DEF_DOORH)
    wallDoorX: FloatProperty(name="Indent", min=0, max=WALL_MAX, default=DEF_DOORX, description="horizontal offset from cursor")
    doorBvl: FloatProperty(name="Bevel", min=-10, max=10, default=0.25, description="Angle block face")
    doorRpt: BoolProperty(default=False, description="make multiple openings")
    doorArch: BoolProperty(name="Arch", default=True)
    doorArchC: FloatProperty(name="Curve", min=0.01, max=HALF_WALL, default=2.5, description="Arch curve height")
    doorArchT: FloatProperty(name="Thickness", min=0.001, max=HALF_WALL, default=0.75)

    wallDF1: BoolProperty(default=False, description="Front")
    wallDF2: BoolProperty(default=False)
    wallDF3: BoolProperty(default=False)
    wallDF4: BoolProperty(default=False)
    wallDF5: BoolProperty(default=False)
    wallDF6: BoolProperty(default=False)
    wallDF7: BoolProperty(default=False)
    wallDF8: BoolProperty(default=False)
    wallDF9: BoolProperty(default=False)
    wallDF10: BoolProperty(default=False)

    wallDL1: BoolProperty(default=False, description="Left")
    wallDL2: BoolProperty(default=False)
    wallDL3: BoolProperty(default=False)
    wallDL4: BoolProperty(default=False)
    wallDL5: BoolProperty(default=False)
    wallDL6: BoolProperty(default=False)
    wallDL7: BoolProperty(default=False)
    wallDL8: BoolProperty(default=False)
    wallDL9: BoolProperty(default=False)
    wallDL10: BoolProperty(default=False)

    wallDB1: BoolProperty(default=False, description="Back")
    wallDB2: BoolProperty(default=False)
    wallDB3: BoolProperty(default=False)
    wallDB4: BoolProperty(default=False)
    wallDB5: BoolProperty(default=False)
    wallDB6: BoolProperty(default=False)
    wallDB7: BoolProperty(default=False)
    wallDB8: BoolProperty(default=False)
    wallDB9: BoolProperty(default=False)
    wallDB10: BoolProperty(default=False)

    wallDR1: BoolProperty(default=False, description="Right")
    wallDR2: BoolProperty(default=False)
    wallDR3: BoolProperty(default=False)
    wallDR4: BoolProperty(default=False)
    wallDR5: BoolProperty(default=False)
    wallDR6: BoolProperty(default=False)
    wallDR7: BoolProperty(default=False)
    wallDR8: BoolProperty(default=False)
    wallDR9: BoolProperty(default=False)
    wallDR10: BoolProperty(default=False)

    # Slots, embrasure - classical slit for arrow/rifle ports.
    # need to prevent overlap with arch openings - though inversion is an interesting effect.
    SlotVW: FloatProperty(name="Width", min=BLOCK_MIN, max=HALF_WALL, default=0.5)
    SlotVH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=3.5)
    SlotVL: FloatProperty(name="Indent", min=-WALL_MAX, max=WALL_MAX, default=0, description="The x position or spacing")
    SlotVZ: FloatProperty(name="Bottom", min=-WALL_MAX, max=WALL_MAX, default=4.00)
    slotVArchT: BoolProperty(name="Top", default=True)
    slotVArchB: BoolProperty(name="Bottom", default=True)
    SlotVRpt: BoolProperty(name="Repeat", default=False)

    wallEF1: BoolProperty(default=False, description="Front")
    wallEF2: BoolProperty(default=False)
    wallEF3: BoolProperty(default=False)
    wallEF4: BoolProperty(default=False)
    wallEF5: BoolProperty(default=False)
    wallEF6: BoolProperty(default=False)
    wallEF7: BoolProperty(default=False)
    wallEF8: BoolProperty(default=False)
    wallEF9: BoolProperty(default=False)
    wallEF10: BoolProperty(default=False)

    wallEL1: BoolProperty(default=False, description="Left")
    wallEL2: BoolProperty(default=False)
    wallEL3: BoolProperty(default=False)
    wallEL4: BoolProperty(default=False)
    wallEL5: BoolProperty(default=False)
    wallEL6: BoolProperty(default=False)
    wallEL7: BoolProperty(default=False)
    wallEL8: BoolProperty(default=False)
    wallEL9: BoolProperty(default=False)
    wallEL10: BoolProperty(default=False)

    wallEB1: BoolProperty(default=False, description="Back")
    wallEB2: BoolProperty(default=False)
    wallEB3: BoolProperty(default=False)
    wallEB4: BoolProperty(default=False)
    wallEB5: BoolProperty(default=False)
    wallEB6: BoolProperty(default=False)
    wallEB7: BoolProperty(default=False)
    wallEB8: BoolProperty(default=False)
    wallEB9: BoolProperty(default=False)
    wallEB10: BoolProperty(default=False)

    wallER1: BoolProperty(default=False, description="Right")
    wallER2: BoolProperty(default=False)
    wallER3: BoolProperty(default=False)
    wallER4: BoolProperty(default=False)
    wallER5: BoolProperty(default=False)
    wallER6: BoolProperty(default=False)
    wallER7: BoolProperty(default=False)
    wallER8: BoolProperty(default=False)
    wallER9: BoolProperty(default=False)
    wallER10: BoolProperty(default=False)
    wallPort: BoolProperty(default=False, description="Window opening")
    wallPortW: FloatProperty(name="Width", min=BLOCK_MIN, max=HALF_WALL, default=2, description="Window (hole) width")
    wallPortH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=3, description="Window (hole) height")
    wallPortX: FloatProperty(name="Indent", min=-WALL_MAX, max=WALL_MAX, default=-2.5, description="The x position or spacing")
    wallPortZ: FloatProperty(name="Bottom", min=-WALL_MAX, max=WALL_MAX, default=5)
    wallPortBvl: FloatProperty(name="Bevel", min=-10, max=10, default=0.25, description="Angle block face")
    wallPortArchT: BoolProperty(name="Top", default=False)
    wallPortArchB: BoolProperty(name="Bottom", default=False)
    wallPortRpt: BoolProperty(name="Repeat", default=False)

    wallPF1: BoolProperty(default=False, description="Front")
    wallPF2: BoolProperty(default=False)
    wallPF3: BoolProperty(default=False)
    wallPF4: BoolProperty(default=False)
    wallPF5: BoolProperty(default=False)
    wallPF6: BoolProperty(default=False)
    wallPF7: BoolProperty(default=False)
    wallPF8: BoolProperty(default=False)
    wallPF9: BoolProperty(default=False)
    wallPF10: BoolProperty(default=False)

    wallPL1: BoolProperty(default=False, description="Left")
    wallPL2: BoolProperty(default=False)
    wallPL3: BoolProperty(default=False)
    wallPL4: BoolProperty(default=False)
    wallPL5: BoolProperty(default=False)
    wallPL6: BoolProperty(default=False)
    wallPL7: BoolProperty(default=False)
    wallPL8: BoolProperty(default=False)
    wallPL9: BoolProperty(default=False)
    wallPL10: BoolProperty(default=False)

    wallPB1: BoolProperty(default=False, description="Back")
    wallPB2: BoolProperty(default=False)
    wallPB3: BoolProperty(default=False)
    wallPB4: BoolProperty(default=False)
    wallPB5: BoolProperty(default=False)
    wallPB6: BoolProperty(default=False)
    wallPB7: BoolProperty(default=False)
    wallPB8: BoolProperty(default=False)
    wallPB9: BoolProperty(default=False)
    wallPB10: BoolProperty(default=False)

    wallPR1: BoolProperty(default=False, description="Right")
    wallPR2: BoolProperty(default=False)
    wallPR3: BoolProperty(default=False)
    wallPR4: BoolProperty(default=False)
    wallPR5: BoolProperty(default=False)
    wallPR6: BoolProperty(default=False)
    wallPR7: BoolProperty(default=False)
    wallPR8: BoolProperty(default=False)
    wallPR9: BoolProperty(default=False)
    wallPR10: BoolProperty(default=False)

    # Crenel = "gaps" on top of wall.
    # review and determine min for % - should allow less...
    CrenelXP: FloatProperty(name="Width %", min=0.10, max=1.0, default=0.15)
    CrenelZP: FloatProperty(name="Height %", min=0.10, max=1.0, default=0.10)

    wallCF1: BoolProperty(default=False, description="Front")
    wallCF2: BoolProperty(default=False)
    wallCF3: BoolProperty(default=False)
    wallCF4: BoolProperty(default=False)
    wallCF5: BoolProperty(default=False)
    wallCF6: BoolProperty(default=False)
    wallCF7: BoolProperty(default=False)
    wallCF8: BoolProperty(default=False)
    wallCF9: BoolProperty(default=False)
    wallCF10: BoolProperty(default=False)

    wallCL1: BoolProperty(default=False, description="Left")
    wallCL2: BoolProperty(default=False)
    wallCL3: BoolProperty(default=False)
    wallCL4: BoolProperty(default=False)
    wallCL5: BoolProperty(default=False)
    wallCL6: BoolProperty(default=False)
    wallCL7: BoolProperty(default=False)
    wallCL8: BoolProperty(default=False)
    wallCL9: BoolProperty(default=False)
    wallCL10: BoolProperty(default=False)

    wallCB1: BoolProperty(default=False, description="Back")
    wallCB2: BoolProperty(default=False)
    wallCB3: BoolProperty(default=False)
    wallCB4: BoolProperty(default=False)
    wallCB5: BoolProperty(default=False)
    wallCB6: BoolProperty(default=False)
    wallCB7: BoolProperty(default=False)
    wallCB8: BoolProperty(default=False)
    wallCB9: BoolProperty(default=False)
    wallCB10: BoolProperty(default=False)

    wallCR1: BoolProperty(default=False, description="Right")
    wallCR2: BoolProperty(default=False)
    wallCR3: BoolProperty(default=False)
    wallCR4: BoolProperty(default=False)
    wallCR5: BoolProperty(default=False)
    wallCR6: BoolProperty(default=False)
    wallCR7: BoolProperty(default=False)
    wallCR8: BoolProperty(default=False)
    wallCR9: BoolProperty(default=False)
    wallCR10: BoolProperty(default=False)
    # see "balcony" options for improved capabilities.
    # should limit x and z to wall boundaries
    ShelfX: FloatProperty(name="XOff", min=-WALL_MAX, max=WALL_MAX, default=-(WALL_DEF / 2), description="x origin")
    ShelfZ: FloatProperty(name="Base", min=-WALL_MAX, max=WALL_MAX, default=WALL_DEF, description="z origin")
    ShelfW: FloatProperty(name="Width", min=BLOCK_MIN, max=WALL_MAX, default=WALL_DEF, description="The Width of shelf area")
    # height seems to be double, check usage
    ShelfH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=0.5, description="The Height of Shelf area")
    ShelfD: FloatProperty(name="Depth", min=BLOCK_MIN, max=WALL_MAX, default=BLOCK_DDEF, description="Depth of shelf")
    ShelfOut: BoolProperty(name="Out", default=True, description="Position outside of wall")

    wallBF1: BoolProperty(default=False, description="Front")
    wallBF2: BoolProperty(default=False)
    wallBF3: BoolProperty(default=False)
    wallBF4: BoolProperty(default=False)
    wallBF5: BoolProperty(default=False)
    wallBF6: BoolProperty(default=False)
    wallBF7: BoolProperty(default=False)
    wallBF8: BoolProperty(default=False)
    wallBF9: BoolProperty(default=False)
    wallBF10: BoolProperty(default=False)

    wallBL1: BoolProperty(default=False, description="Left")
    wallBL2: BoolProperty(default=False)
    wallBL3: BoolProperty(default=False)
    wallBL4: BoolProperty(default=False)
    wallBL5: BoolProperty(default=False)
    wallBL6: BoolProperty(default=False)
    wallBL7: BoolProperty(default=False)
    wallBL8: BoolProperty(default=False)
    wallBL9: BoolProperty(default=False)
    wallBL10: BoolProperty(default=False)

    wallBB1: BoolProperty(default=False, description="Back")
    wallBB2: BoolProperty(default=False)
    wallBB3: BoolProperty(default=False)
    wallBB4: BoolProperty(default=False)
    wallBB5: BoolProperty(default=False)
    wallBB6: BoolProperty(default=False)
    wallBB7: BoolProperty(default=False)
    wallBB8: BoolProperty(default=False)
    wallBB9: BoolProperty(default=False)
    wallBB10: BoolProperty(default=False)

    wallBR1: BoolProperty(default=False, description="Right")
    wallBR2: BoolProperty(default=False)
    wallBR3: BoolProperty(default=False)
    wallBR4: BoolProperty(default=False)
    wallBR5: BoolProperty(default=False)
    wallBR6: BoolProperty(default=False)
    wallBR7: BoolProperty(default=False)
    wallBR8: BoolProperty(default=False)
    wallBR9: BoolProperty(default=False)
    wallBR10: BoolProperty(default=False)
    StepX: FloatProperty(name="XOff", min=-WALL_MAX, max=WALL_MAX, default=-(WALL_DEF / 2), description="x origin")
    StepZ: FloatProperty(name="Base", min=-WALL_MAX, max=WALL_MAX, default=0, description="z origin")
    StepW: FloatProperty(name="Width", min=BLOCK_MIN, max=WALL_MAX, default=WALL_DEF, description="Width of step area")
    StepH: FloatProperty(name="Height", min=BLOCK_MIN, max=WALL_MAX, default=WALL_DEF, description="Height of step area")
    StepD: FloatProperty(name="Depth", min=BLOCK_MIN, max=HALF_WALL, default=BLOCK_XDEF, description="Step offset (distance) from wall")
    StepV: FloatProperty(name="Riser", min=BLOCK_MIN, max=HALF_WALL, default=BLOCK_XDEF, description="Step height")
    StepT: FloatProperty(name="Tread", min=BLOCK_MIN, max=HALF_WALL, default=BLOCK_DDEF, description="Step footing/tread")
    StepL: BoolProperty(name="Slant", default=False, description="Step direction.")
    StepF: BoolProperty(name="Fill", default=False, description="Fill with supporting blocks")
    StepOut: BoolProperty(name="Out", default=False, description="Position outside of wall")

    wallSF1: BoolProperty(default=False, description="Front")
    wallSF2: BoolProperty(default=False)
    wallSF3: BoolProperty(default=False)
    wallSF4: BoolProperty(default=False)
    wallSF5: BoolProperty(default=False)
    wallSF6: BoolProperty(default=False)
    wallSF7: BoolProperty(default=False)
    wallSF8: BoolProperty(default=False)
    wallSF9: BoolProperty(default=False)
    wallSF10: BoolProperty(default=False)

    wallSL1: BoolProperty(default=False, description="Left")
    wallSL2: BoolProperty(default=False)
    wallSL3: BoolProperty(default=False)
    wallSL4: BoolProperty(default=False)
    wallSL5: BoolProperty(default=False)
    wallSL6: BoolProperty(default=False)
    wallSL7: BoolProperty(default=False)
    wallSL8: BoolProperty(default=False)
    wallSL9: BoolProperty(default=False)
    wallSL10: BoolProperty(default=False)

    wallSB1: BoolProperty(default=False, description="Back")
    wallSB2: BoolProperty(default=False)
    wallSB3: BoolProperty(default=False)
    wallSB4: BoolProperty(default=False)
    wallSB5: BoolProperty(default=False)
    wallSB6: BoolProperty(default=False)
    wallSB7: BoolProperty(default=False)
    wallSB8: BoolProperty(default=False)
    wallSB9: BoolProperty(default=False)
    wallSB10: BoolProperty(default=False)

    wallSR1: BoolProperty(default=False, description="Right")
    wallSR2: BoolProperty(default=False)
    wallSR3: BoolProperty(default=False)
    wallSR4: BoolProperty(default=False)
    wallSR5: BoolProperty(default=False)
    wallSR6: BoolProperty(default=False)
    wallSR7: BoolProperty(default=False)
    wallSR8: BoolProperty(default=False)
    wallSR9: BoolProperty(default=False)
    wallSR10: BoolProperty(default=False)
    # allow per wall and level?
    cXTest: BoolProperty(default=False)
    # wall shape modifiers
    # make the wall circular - if not curved it's a flat disc
    # Radiating from one point - round/disc; instead of square
    #    wallDisc=BoolProperty(default=False,description="Make wall disc")
    # Warp/slope/curve wall - abstract use only (except for dome use)
    wallSlope: BoolProperty(default=False, description="Curve wall")  # top to bottom mid
    CTunnel: BoolProperty(default=False, description="Tunnel wall")  # bottom to top mid
    # rotate tunnel?...
    CTower: BoolProperty(name='Tower', default=False)
    ##
    ####
    ############################################################################
    # Show the UI - present properties to user.
    # Display the toolbox options
    ############################################################################
    ####
    ##
    def draw(self, context):

        layout = self.layout

        box = layout.box()
        box.prop(self, 'ConstructTog')
        # Castle area.
        box = layout.box()
        row = box.row()
        row.prop(self, 'cBaseRGB', text='Base')
        row = box.row()
        row.prop(self, 'cBaseW')
        row.prop(self, 'cBaseD')
        # this is 0 until more complex settings allowed... like repeat or
        # per-level (like wall) selection.
        #        box.prop(self,'cBaseO') # vert offset from 3D cursor

        box.prop(self, 'cBaseT', text="Thickness")  # height/thickness of floor.
        row = box.row()
        row.prop(self, 'CBaseB')  # Blocks or slab
        if self.properties.CBaseB:
            row.prop(self, 'CBaseR', text='', icon="MESH_CIRCLE")  # Round
        box.prop(self, 'CLvls', text="Levels")  # number of floors.

        # block sizing
        box = layout.box()
        row = box.row()
        row.label(text='Blocks:')
        box.prop(self, 'blockX')
        box.prop(self, 'blockZ')
        box.prop(self, 'blockD')
        box.prop(self, 'Grout')
        box.prop(self, 'EdgeOffset')

        # "fixed" sizing unless variance enabled.
        row = box.row()
        row.prop(self, 'blockVar')  # randomize block sizing
        if self.properties.blockVar:
            row.prop(self, 'blockMerge')
            box.prop(self, 'blockHVar')
            box.prop(self, 'blockWVar')
            box.prop(self, 'blockDVar')

        box = layout.box()
        box.prop(self, 'CLvl', text="Level")  # current "Level" (UI parameter values).

        # Walls
        row = box.row()
        row.prop(self, 'wallRGB', text='Walls')
        row = box.row()  # which walls to generate - show current level value.
        row.prop(self, 'wallF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
        row.prop(self, 'wallL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
        row.prop(self, 'wallB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
        row.prop(self, 'wallR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
        row = box.row()  # round wall option
        row.prop(self, 'wallFO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
        row.prop(self, 'wallLO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
        row.prop(self, 'wallBO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
        row.prop(self, 'wallRO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
        box.prop(self, 'wallH')
        if self.properties.CLvl > 1:  # Base is first level (always true)
            box.prop(self, 'CFloor' + str(self.properties.CLvl), text="Floor")  # floor for level.

        box.prop(self, 'wallOpView')  # View/edit wall modifiers

        # Door
        if self.properties.wallOpView == "1":
            #            box.prop(self,'doorRpt',text='Dupe')
            row = box.row()  # which walls have a Door
            row.prop(self, 'wallDF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
            row.prop(self, 'wallDL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
            row.prop(self, 'wallDB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
            row.prop(self, 'wallDR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
            box.prop(self, 'wallDoorW')
            box.prop(self, 'wallDoorH')
            box.prop(self, 'wallDoorX')
            box.prop(self, 'doorBvl')

            box.prop(self, 'doorArch')
            if self.doorArch:
                box.prop(self, 'doorArchC')
                box.prop(self, 'doorArchT')

        # Window
        if self.properties.wallOpView == "2":
            #            box.prop(self,'wallPortRpt',text='Dupe')
            row = box.row()  # which walls have portal/window
            row.prop(self, 'wallPF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
            row.prop(self, 'wallPL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
            row.prop(self, 'wallPB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
            row.prop(self, 'wallPR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
            row = box.row()
            row.prop(self, 'wallPortW')
            row.prop(self, 'wallPortH')
            box.prop(self, 'wallPortX')
            box.prop(self, 'wallPortZ')
            box.prop(self, 'wallPortBvl')
            box.label(text='Arch')
            row = box.row()
            row.prop(self, 'wallPortArchT')
            row.prop(self, 'wallPortArchB')

        # Slots
        if self.properties.wallOpView == "3":
            box.prop(self, 'SlotVRpt', text='Dupe')
            row = box.row()  # which walls have embrasures/slits
            row.prop(self, 'wallEF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
            row.prop(self, 'wallEL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
            row.prop(self, 'wallEB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
            row.prop(self, 'wallER' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
            box.prop(self, 'SlotVW')
            box.prop(self, 'SlotVH')
            box.prop(self, 'SlotVL')
            box.prop(self, 'SlotVZ')
            box.label(text='Arch')
            row = box.row()
            row.prop(self, 'slotVArchT')
            row.prop(self, 'slotVArchB')

        # Crenellation: gaps in top of wall, base of dome.
        if self.properties.wallOpView == "4":
            row = box.row()  # which walls have Crenellation
            row.prop(self, 'wallCF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
            row.prop(self, 'wallCL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
            row.prop(self, 'wallCB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
            row.prop(self, 'wallCR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
            box.prop(self, 'CrenelXP')
            box.prop(self, 'CrenelZP')

        # Steps
        if self.properties.wallOpView == "5":
            row = box.row()
            row.prop(self, 'StepL')
            row.prop(self, 'StepF')
            row.prop(self, 'StepOut')
            row = box.row()  # which walls have steps
            row.prop(self, 'wallSF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
            row.prop(self, 'wallSL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
            row.prop(self, 'wallSB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
            row.prop(self, 'wallSR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
            box.prop(self, 'StepX')
            box.prop(self, 'StepZ')
            box.prop(self, 'StepW')
            box.prop(self, 'StepH')
            box.prop(self, 'StepD')
            box.prop(self, 'StepV')
            box.prop(self, 'StepT')

        # Shelf/Balcony
        if self.properties.wallOpView == "6":
            box.prop(self, 'ShelfOut')
            row = box.row()  # which walls have a shelf
            row.prop(self, 'wallBF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
            row.prop(self, 'wallBL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
            row.prop(self, 'wallBB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
            row.prop(self, 'wallBR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
            box.prop(self, 'ShelfX')
            box.prop(self, 'ShelfZ')
            box.prop(self, 'ShelfW')
            box.prop(self, 'ShelfH')
            box.prop(self, 'ShelfD')

        # Dome
        box = layout.box()
        row = box.row()
        row.prop(self, 'cDome')
        if self.properties.cDome:
            row.prop(self, 'cDomeRGB', text='')
            box.prop(self, 'cDomeH')
            box.prop(self, 'cDomeZ')
        # Oculus - opening in top of dome
        #            box.prop(self,'cDomeO')
        # Use "icon="MESH_CONE" for roof
        box = layout.box()
        row = box.row()
        row.prop(self, 'cXTest', text='Experimental')
        if self.properties.cXTest:
            box.prop(self, 'wallSlope', text='Curve')
            box.prop(self, 'CTunnel', text='Tunnel', icon="CURVE_DATA")
            box.prop(self, 'CTower', text='Tower', icon="CURVE_DATA")

    ##
    ####
    ############################################################################
    # Respond to UI - process the properties set by user.
        # Check and process UI settings to generate castle.
    ############################################################################
    ####
    ##

    def execute(self, context):

        global openingSpecs
        global dims
        # Limit UI settings relative to other parameters,
        #  set globals and effeciency variables.

        # current level cannot exceed level setting...
        if self.properties.CLvl > self.properties.CLvls:
            self.properties.CLvl = self.properties.CLvls

        castleScene = bpy.context.scene
        blockWidth = self.properties.blockX
        blockHeight = self.properties.blockZ
        blockDepth = self.properties.blockD

        # wall "area" must be at least 3 blocks.
        minWallW = blockWidth * 3  # min wall width/depth
        minWallH = blockHeight * 3  # min wall height

        if self.properties.cBaseW < minWallW:
            self.properties.cBaseW = minWallW
        CastleX = self.properties.cBaseW
        midWallW = CastleX / 2

        if self.properties.wallH < minWallH:
            self.properties.wallH = minWallH
        planeHL = self.properties.wallH
        planeHL2 = planeHL * 2
        # proportional crenel height, shared with roof positioning.
        crenelH = planeHL * self.properties.CrenelZP

        if self.properties.cBaseD < minWallW:
            self.properties.cBaseD = minWallW
        CastleD = self.properties.cBaseD
        midWallD = (CastleD + blockDepth) / 2
        #        midWallD=CastleD/2
        #        midWallD=(CastleD-blockDepth)/2

        # gap cannot reduce block below minimum.
        if self.properties.Grout > blockHeight / 2 - BLOCK_MIN:
            self.properties.Grout = blockHeight / 2 - BLOCK_MIN

        # Variance limit for minimum block height.
        # not quite right, but usuable.
        blockHMin = self.properties.Grout + BLOCK_MIN

        # floor "thickness" cannot exceed block height
        if self.properties.cBaseT > blockHeight:
            self.properties.cBaseT = blockHeight

        ############
        #
        if self.properties.cDome:
            # base can't be lower than floor
            # consider limiting base to roof or wall height by levels?
            domeLimit = self.properties.cBaseO + self.properties.cBaseT
            if self.properties.cDomeZ < domeLimit:
                self.properties.cDomeZ = domeLimit

            # base can't be higher than double wall height (per level)
            domeLimit = planeHL2 * self.properties.CLvls
            if self.properties.cDomeZ > domeLimit:
                self.properties.cDomeZ = domeLimit

            # height limit to twice smallest wall width/depth.
            if CastleD < CastleX:  # use least dimension for dome.
                domeLimit = CastleD * 2
            else:
                domeLimit = CastleX * 2

            if self.properties.cDomeH > domeLimit:
                self.properties.cDomeH = domeLimit
            domeMaxO = self.properties.cDomeH / 2

            # Dome Oculus (opening) can't be more than half dome height
            if self.properties.cDomeO > domeMaxO:
                self.properties.cDomeO = domeMaxO

        #
        ############
        #####
        #
        # After validating all the properties see if need to create...
        # @todo: fix so last object preserved... (i.e. changing levels).
        # No build if construct off
        if not self.properties.ConstructTog:
            # No build if construct off or just changing levels
            #        if not self.properties.ConstructTog or self.properties.curLvl!=self.properties.CLvl:
            #            self.properties.curLvl=self.properties.CLvl
            return {'FINISHED'}
        # rowprocessing() front/back wall, modified by sides and Dome.
        # this centers the wall, maybe add flag to center or offset from cursor...
        # if centerwall:
        dims['s'] = -midWallW
        dims['e'] = midWallW
        # else:
        #        dims['s'] = 0
        #        dims['e'] = CastleX

        dims['t'] = planeHL

        # block sizing
        settings['h'] = blockHeight
        settings['w'] = blockWidth
        settings['d'] = blockDepth

        if self.properties.blockVar:
            settings['hv'] = self.properties.blockHVar
            settings['wv'] = self.properties.blockWVar
            settings['dv'] = self.properties.blockDVar
        else:
            settings['hv'] = 0
            settings['wv'] = 0
            settings['dv'] = 0

        settings['sdv'] = blockWidth  # divisions in area.

        settings['g'] = self.properties.Grout

        settings['eoff'] = self.properties.EdgeOffset

        # when openings overlap they create inverse stonework - interesting but not the desired effect
        # if opening width == indent*2 the edge blocks fail (row of blocks cross opening) - bug.
        openingSpecs = []
        archTop = [0, 0]
        archBot = [0, 0]

        #
        ############
        #
        # Openings always set info; flag per wall, per level to enable.
        #
        ############
        #
        ############
        # Door
            # set locals
        # @todo: fix this....
        openZ = self.properties.cBaseT + 1  # track with floor - else 0

        if self.properties.doorArch:
            archTop[0] = self.properties.doorArchC
            archTop[1] = self.properties.doorArchT

        # set opening values
        openingSpecs += [{'a': False,
                          'w': self.properties.wallDoorW, 'h': self.properties.wallDoorH,
                          'x': self.properties.wallDoorX, 'z': openZ,
                          'n': self.properties.doorRpt, 'bvl': self.properties.doorBvl,
                          'v': archTop[0], 'vl':archBot[0], 't':archTop[1], 'tl':archBot[1]}]

        ############
        # Window
        archTop[0] = 0
        archTop[1] = 0
        archBot[0] = 0
        archBot[1] = 0

        if self.properties.wallPortArchT:
            archTop[0] = self.properties.wallPortW / 2
            archTop[1] = self.properties.wallPortW / 4

        if self.properties.wallPortArchB:
            archBot[0] = self.properties.wallPortW / 2
            archBot[1] = self.properties.wallPortW / 4

        # set opening values
        openingSpecs += [{'a': False,
                          'w': self.properties.wallPortW, 'h': self.properties.wallPortH,
                          'x': self.properties.wallPortX, 'z': self.properties.wallPortZ,
                          'n': self.properties.wallPortRpt, 'bvl': self.properties.wallPortBvl,
                          'v': archTop[0], 'vl':archBot[0], 't':archTop[1], 'tl':archBot[1]}]

        ############
        # crenellation, top wall gaps...
        # if crenel opening overlaps with arch opening it fills with blocks...
        # add bottom arch option?
        archBot[0] = 0
        archBot[1] = 0

        crenelW = CastleX * self.properties.CrenelXP  # Width % opening.
        crenelz = planeHL - (crenelH / 2)  # set bottom of opening

        crenelSpace = crenelW * 2  # assume standard spacing
        crenelRpt = True
        # set indent 0 (center) if opening is 50% or more of wall width, no repeat.
        if crenelSpace >= CastleX:
            crenelSpace = 0
            crenelRpt = False

        # set opening values
        openingSpecs += [{'a': False,
                          'w': crenelW, 'h': crenelH,
                          'x': crenelSpace, 'z': crenelz,
                          'n': crenelRpt, 'bvl': 0,
                          'v': archTop[0], 'vl':archBot[0], 't':archTop[1], 'tl':archBot[1]}]

        ############
        # Slots
        archTop[0] = 0
        archTop[1] = 0
        archBot[0] = 0
        archBot[1] = 0

        if self.properties.slotVArchT:
            archTop[0] = self.properties.SlotVW
            archTop[1] = self.properties.SlotVW / 2
        if self.properties.slotVArchB:
            archBot[0] = self.properties.SlotVW
            archBot[1] = self.properties.SlotVW / 2

        # set opening values
        openingSpecs += [{'a': False,
                          'w': self.properties.SlotVW, 'h': self.properties.SlotVH,
                          'x': self.properties.SlotVL, 'z': self.properties.SlotVZ,
                          'n': self.properties.SlotVRpt, 'bvl': 0,
                          'v': archTop[0], 'vl':archBot[0], 't':archTop[1], 'tl':archBot[1]}]

        ##
        ####
        ########################################################################
        # Process the user settings to generate castle.
        ########################################################################
        ####
        ##
        # Deselect all objects.
        bpy.ops.object.select_all(action='DESELECT')

        ############
        # Base/floor/foundation, main object for Castle, parent to all other elements.
        wallExtOpts = [False, False]  # no steps or shelf
        # @todo: offset door by floor height/thickness
        # @todo: replicate for "multi-floor" option-shared with roof.
        #  - will need openings (in plane) for stairs and such...
        settings['Slope'] = False  # no curve
        settings['eoff'] = 0  # no edgeing
        # need separate flag for floor/roof...
        settings['Radial'] = False

        baseMtl = uMatRGBSet('cBase_mat', self.cBaseRGB, matMod=True)

        baseDisc = False  # only when blocks and round...
        if self.properties.CBaseB:  # floor blocks
            baseDisc = self.properties.CBaseR  # rounded

        # set block "area": height, width, depth, and spacing for floor
        # - when rotated or disc shape, height is depth, depth is thickness.
        blockArea = [self.properties.cBaseD, self.properties.cBaseW, self.properties.cBaseT, self.properties.blockZ, settings['hv'], self.properties.Grout]

        # Block floor uses wall to generate... initialize location values.
        wallLoc = [castleScene.cursor_location.x, castleScene.cursor_location.y, castleScene.cursor_location.z]

        baseRotate = False  # rotate for blocks...
        if self.properties.CBaseB:  # make floor with blocks.
            saveBD = settings['d']  # put back when done with floor...
            saveBDV = settings['dv']
            settings['d'] = self.properties.cBaseT
            settings['dv'] = 0

            if baseDisc:  # make base disc shaped
                settings['Radial'] = True
                if self.properties.cBaseD < self.properties.cBaseW:  # narrowest extent
                    blockArea[0] = self.properties.cBaseD / 2
                else:
                    blockArea[0] = self.properties.cBaseW / 2

                wallLoc[1] += self.properties.cBaseW / 2  # adjust location for radius

            else:  # rotate if not disc.
                baseRotate = True

            castleObj = makeWallObj(self, castleScene, wallLoc, OBJ_N, blockArea, [], wallExtOpts, baseMtl)

            if baseRotate:
                castleObj.select_set(True)  # must select to rotate
                # rotate 90 backward
                bpy.ops.transform.rotate(value=-cPieHlf, constraint_axis=[True, False, False])
                bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
                castleObj.select_set(False)  # deselect after rotate else joined with others...

            settings['d'] = saveBD  # restore block values
            settings['dv'] = saveBDV

        else:  # make solid plane
            # set plane dimensions and location
            baseZ = self.properties.cBaseO
            baseT = baseZ + self.properties.cBaseT
            baseW = self.properties.cBaseW
            baseXO = -baseW / 2  # center
            baseXE = baseW / 2

            baseBounds = [baseXO, baseXE, baseZ, baseT, 0, self.properties.cBaseD]
            castleObj = makePlaneObj(OBJ_N, baseBounds, baseMtl, baseW)
            castleScene.objects.link(castleObj)  # must do for generation/rotation


        ####################
        # Make Floor and Walls
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612
        wallMtl = uMatRGBSet('cWall_mat', self.wallRGB, matMod=True)

        # Block floor uses wall to generate... reset location values.
        wallLoc[1] = castleScene.cursor_location.y

        wallLvl = 0  # make per level as selected...
        while wallLvl < self.properties.CLvls:  # make castle levels
            if wallLvl == 0:
                floorLvl = False  # First floor is Castle base (parent).
                wallFLvl = self.properties.wallF1
                wallLLvl = self.properties.wallL1
                wallBLvl = self.properties.wallB1
                wallRLvl = self.properties.wallR1
                wallFOLvl = self.properties.wallFO1
                wallLOLvl = self.properties.wallLO1
                wallBOLvl = self.properties.wallBO1
                wallROLvl = self.properties.wallRO1
                wallDLvlF = self.properties.wallDF1
                wallDLvlL = self.properties.wallDL1
                wallDLvlB = self.properties.wallDB1
                wallDLvlR = self.properties.wallDR1
                wallPLvlF = self.properties.wallPF1
                wallPLvlL = self.properties.wallPL1
                wallPLvlB = self.properties.wallPB1
                wallPLvlR = self.properties.wallPR1
                wallELvlF = self.properties.wallEF1
                wallELvlL = self.properties.wallEL1
                wallELvlB = self.properties.wallEB1
                wallELvlR = self.properties.wallER1
                wallCLvlF = self.properties.wallCF1
                wallCLvlL = self.properties.wallCL1
                wallCLvlB = self.properties.wallCB1
                wallCLvlR = self.properties.wallCR1
                wallSLvlF = self.properties.wallSF1
                wallSLvlL = self.properties.wallSL1
                wallSLvlB = self.properties.wallSB1
                wallSLvlR = self.properties.wallSR1
                wallBLvlF = self.properties.wallBF1
                wallBLvlL = self.properties.wallBL1
                wallBLvlB = self.properties.wallBB1
                wallBLvlR = self.properties.wallBR1

            if wallLvl == 1:
                floorLvl = self.properties.CFloor2
                wallFLvl = self.properties.wallF2
                wallLLvl = self.properties.wallL2
                wallBLvl = self.properties.wallB2
                wallRLvl = self.properties.wallR2
                wallFOLvl = self.properties.wallFO2
                wallLOLvl = self.properties.wallLO2
                wallBOLvl = self.properties.wallBO2
                wallROLvl = self.properties.wallRO2
                wallDLvlF = self.properties.wallDF2
                wallDLvlL = self.properties.wallDL2
                wallDLvlB = self.properties.wallDB2
                wallDLvlR = self.properties.wallDR2
                wallPLvlF = self.properties.wallPF2
                wallPLvlL = self.properties.wallPL2
                wallPLvlB = self.properties.wallPB2
                wallPLvlR = self.properties.wallPR2
                wallELvlF = self.properties.wallEF2
                wallELvlL = self.properties.wallEL2
                wallELvlB = self.properties.wallEB2
                wallELvlR = self.properties.wallER2
                wallCLvlF = self.properties.wallCF2
                wallCLvlL = self.properties.wallCL2
                wallCLvlB = self.properties.wallCB2
                wallCLvlR = self.properties.wallCR2
                wallSLvlF = self.properties.wallSF2
                wallSLvlL = self.properties.wallSL2
                wallSLvlB = self.properties.wallSB2
                wallSLvlR = self.properties.wallSR2
                wallBLvlF = self.properties.wallBF2
                wallBLvlL = self.properties.wallBL2
                wallBLvlB = self.properties.wallBB2
                wallBLvlR = self.properties.wallBR2

            if wallLvl == 2:
                floorLvl = self.properties.CFloor3
                wallFLvl = self.properties.wallF3
                wallLLvl = self.properties.wallL3
                wallBLvl = self.properties.wallB3
                wallRLvl = self.properties.wallR3
                wallFOLvl = self.properties.wallFO3
                wallLOLvl = self.properties.wallLO3
                wallBOLvl = self.properties.wallBO3
                wallROLvl = self.properties.wallRO3
                wallDLvlF = self.properties.wallDF3
                wallDLvlL = self.properties.wallDL3
                wallDLvlB = self.properties.wallDB3
                wallDLvlR = self.properties.wallDR3
                wallPLvlF = self.properties.wallPF3
                wallPLvlL = self.properties.wallPL3
                wallPLvlB = self.properties.wallPB3
                wallPLvlR = self.properties.wallPR3
                wallELvlF = self.properties.wallEF3
                wallELvlL = self.properties.wallEL3
                wallELvlB = self.properties.wallEB3
                wallELvlR = self.properties.wallER3
                wallCLvlF = self.properties.wallCF3
                wallCLvlL = self.properties.wallCL3
                wallCLvlB = self.properties.wallCB3
                wallCLvlR = self.properties.wallCR3
                wallSLvlF = self.properties.wallSF3
                wallSLvlL = self.properties.wallSL3
                wallSLvlB = self.properties.wallSB3
                wallSLvlR = self.properties.wallSR3
                wallBLvlF = self.properties.wallBF3
                wallBLvlL = self.properties.wallBL3
                wallBLvlB = self.properties.wallBB3
                wallBLvlR = self.properties.wallBR3

            if wallLvl == 3:
                floorLvl = self.properties.CFloor4
                wallFLvl = self.properties.wallF4
                wallLLvl = self.properties.wallL4
                wallBLvl = self.properties.wallB4
                wallRLvl = self.properties.wallR4
                wallFOLvl = self.properties.wallFO4
                wallLOLvl = self.properties.wallLO4
                wallBOLvl = self.properties.wallBO4
                wallROLvl = self.properties.wallRO4
                wallDLvlF = self.properties.wallDF4
                wallDLvlL = self.properties.wallDL4
                wallDLvlB = self.properties.wallDB4
                wallDLvlR = self.properties.wallDR4
                wallPLvlF = self.properties.wallPF4
                wallPLvlL = self.properties.wallPL4
                wallPLvlB = self.properties.wallPB4
                wallPLvlR = self.properties.wallPR4
                wallELvlF = self.properties.wallEF4
                wallELvlL = self.properties.wallEL4
                wallELvlB = self.properties.wallEB4
                wallELvlR = self.properties.wallER4
                wallCLvlF = self.properties.wallCF4
                wallCLvlL = self.properties.wallCL4
                wallCLvlB = self.properties.wallCB4
                wallCLvlR = self.properties.wallCR4
                wallSLvlF = self.properties.wallSF4
                wallSLvlL = self.properties.wallSL4
                wallSLvlB = self.properties.wallSB4
                wallSLvlR = self.properties.wallSR4
                wallBLvlF = self.properties.wallBF4
                wallBLvlL = self.properties.wallBL4
                wallBLvlB = self.properties.wallBB4
                wallBLvlR = self.properties.wallBR4

            if wallLvl == 4:
                floorLvl = self.properties.CFloor5
                wallFLvl = self.properties.wallF5
                wallLLvl = self.properties.wallL5
                wallBLvl = self.properties.wallB5
                wallRLvl = self.properties.wallR5
                wallFOLvl = self.properties.wallFO5
                wallLOLvl = self.properties.wallLO5
                wallBOLvl = self.properties.wallBO5
                wallROLvl = self.properties.wallRO5
                wallDLvlF = self.properties.wallDF5
                wallDLvlL = self.properties.wallDL5
                wallDLvlB = self.properties.wallDB5
                wallDLvlR = self.properties.wallDR5
                wallPLvlF = self.properties.wallPF5
                wallPLvlL = self.properties.wallPL5
                wallPLvlB = self.properties.wallPB5
                wallPLvlR = self.properties.wallPR5
                wallELvlF = self.properties.wallEF5
                wallELvlL = self.properties.wallEL5
                wallELvlB = self.properties.wallEB5
                wallELvlR = self.properties.wallER5
                wallCLvlF = self.properties.wallCF5
                wallCLvlL = self.properties.wallCL5
                wallCLvlB = self.properties.wallCB5
                wallCLvlR = self.properties.wallCR5
                wallSLvlF = self.properties.wallSF5
                wallSLvlL = self.properties.wallSL5
                wallSLvlB = self.properties.wallSB5
                wallSLvlR = self.properties.wallSR5
                wallBLvlF = self.properties.wallBF5
                wallBLvlL = self.properties.wallBL5
                wallBLvlB = self.properties.wallBB5
                wallBLvlR = self.properties.wallBR5

            if wallLvl == 5:
                floorLvl = self.properties.CFloor6
                wallFLvl = self.properties.wallF6
                wallLLvl = self.properties.wallL6
                wallBLvl = self.properties.wallB6
                wallRLvl = self.properties.wallR6
                wallFOLvl = self.properties.wallFO6
                wallLOLvl = self.properties.wallLO6
                wallBOLvl = self.properties.wallBO6
                wallROLvl = self.properties.wallRO6
                wallDLvlF = self.properties.wallDF6
                wallDLvlL = self.properties.wallDL6
                wallDLvlB = self.properties.wallDB6
                wallDLvlR = self.properties.wallDR6
                wallPLvlF = self.properties.wallPF6
                wallPLvlL = self.properties.wallPL6
                wallPLvlB = self.properties.wallPB6
                wallPLvlR = self.properties.wallPR6
                wallELvlF = self.properties.wallEF6
                wallELvlL = self.properties.wallEL6
                wallELvlB = self.properties.wallEB6
                wallELvlR = self.properties.wallER6
                wallCLvlF = self.properties.wallCF6
                wallCLvlL = self.properties.wallCL6
                wallCLvlB = self.properties.wallCB6
                wallCLvlR = self.properties.wallCR6
                wallSLvlF = self.properties.wallSF6
                wallSLvlL = self.properties.wallSL6
                wallSLvlB = self.properties.wallSB6
                wallSLvlR = self.properties.wallSR6
                wallBLvlF = self.properties.wallBF6
                wallBLvlL = self.properties.wallBL6
                wallBLvlB = self.properties.wallBB6
                wallBLvlR = self.properties.wallBR6

            if wallLvl == 6:
                floorLvl = self.properties.CFloor7
                wallFLvl = self.properties.wallF7
                wallLLvl = self.properties.wallL7
                wallBLvl = self.properties.wallB7
                wallRLvl = self.properties.wallR7
                wallFOLvl = self.properties.wallFO7
                wallLOLvl = self.properties.wallLO7
                wallBOLvl = self.properties.wallBO7
                wallROLvl = self.properties.wallRO7
                wallDLvlF = self.properties.wallDF7
                wallDLvlL = self.properties.wallDL7
                wallDLvlB = self.properties.wallDB7
                wallDLvlR = self.properties.wallDR7
                wallPLvlF = self.properties.wallPF7
                wallPLvlL = self.properties.wallPL7
                wallPLvlB = self.properties.wallPB7
                wallPLvlR = self.properties.wallPR7
                wallELvlF = self.properties.wallEF7
                wallELvlL = self.properties.wallEL7
                wallELvlB = self.properties.wallEB7
                wallELvlR = self.properties.wallER7
                wallCLvlF = self.properties.wallCF7
                wallCLvlL = self.properties.wallCL7
                wallCLvlB = self.properties.wallCB7
                wallCLvlR = self.properties.wallCR7
                wallSLvlF = self.properties.wallSF7
                wallSLvlL = self.properties.wallSL7
                wallSLvlB = self.properties.wallSB7
                wallSLvlR = self.properties.wallSR7
                wallBLvlF = self.properties.wallBF7
                wallBLvlL = self.properties.wallBL7
                wallBLvlB = self.properties.wallBB7
                wallBLvlR = self.properties.wallBR7

            if wallLvl == 7:
                floorLvl = self.properties.CFloor38
                wallFLvl = self.properties.wallF8
                wallLLvl = self.properties.wallL8
                wallBLvl = self.properties.wallB8
                wallRLvl = self.properties.wallR8
                wallFOLvl = self.properties.wallFO8
                wallLOLvl = self.properties.wallLO8
                wallBOLvl = self.properties.wallBO8
                wallROLvl = self.properties.wallRO8
                wallDLvlF = self.properties.wallDF8
                wallDLvlL = self.properties.wallDL8
                wallDLvlB = self.properties.wallDB8
                wallDLvlR = self.properties.wallDR8
                wallPLvlF = self.properties.wallPF8
                wallPLvlL = self.properties.wallPL8
                wallPLvlB = self.properties.wallPB8
                wallPLvlR = self.properties.wallPR8
                wallELvlF = self.properties.wallEF8
                wallELvlL = self.properties.wallEL8
                wallELvlB = self.properties.wallEB8
                wallELvlR = self.properties.wallER8
                wallCLvlF = self.properties.wallCF8
                wallCLvlL = self.properties.wallCL8
                wallCLvlB = self.properties.wallCB8
                wallCLvlR = self.properties.wallCR8
                wallSLvlF = self.properties.wallSF8
                wallSLvlL = self.properties.wallSL8
                wallSLvlB = self.properties.wallSB8
                wallSLvlR = self.properties.wallSR8
                wallBLvlF = self.properties.wallBF8
                wallBLvlL = self.properties.wallBL8
                wallBLvlB = self.properties.wallBB8
                wallBLvlR = self.properties.wallBR8

            if wallLvl == 8:
                floorLvl = self.properties.CFloor9
                wallFLvl = self.properties.wallF9
                wallLLvl = self.properties.wallL9
                wallBLvl = self.properties.wallB9
                wallRLvl = self.properties.wallR9
                wallFOLvl = self.properties.wallFO9
                wallLOLvl = self.properties.wallLO9
                wallBOLvl = self.properties.wallBO9
                wallROLvl = self.properties.wallRO9
                wallDLvlF = self.properties.wallDF9
                wallDLvlL = self.properties.wallDL9
                wallDLvlB = self.properties.wallDB9
                wallDLvlR = self.properties.wallDR9
                wallPLvlF = self.properties.wallPF9
                wallPLvlL = self.properties.wallPL9
                wallPLvlB = self.properties.wallPB9
                wallPLvlR = self.properties.wallPR9
                wallELvlF = self.properties.wallEF9
                wallELvlL = self.properties.wallEL9
                wallELvlB = self.properties.wallEB9
                wallELvlR = self.properties.wallER9
                wallCLvlF = self.properties.wallCF9
                wallCLvlL = self.properties.wallCL9
                wallCLvlB = self.properties.wallCB9
                wallCLvlR = self.properties.wallCR9
                wallSLvlF = self.properties.wallSF9
                wallSLvlL = self.properties.wallSL9
                wallSLvlB = self.properties.wallSB9
                wallSLvlR = self.properties.wallSR9
                wallBLvlF = self.properties.wallBF9
                wallBLvlL = self.properties.wallBL9
                wallBLvlB = self.properties.wallBB9
                wallBLvlR = self.properties.wallBR9

            if wallLvl == 9:
                floorLvl = self.properties.CFloor10
                wallFLvl = self.properties.wallF10
                wallLLvl = self.properties.wallL10
                wallBLvl = self.properties.wallB10
                wallRLvl = self.properties.wallR10
                wallFOLvl = self.properties.wallFO10
                wallLOLvl = self.properties.wallLO10
                wallBOLvl = self.properties.wallBO10
                wallROLvl = self.properties.wallRO10
                wallDLvlF = self.properties.wallDF10
                wallDLvlL = self.properties.wallDL10
                wallDLvlB = self.properties.wallDB10
                wallDLvlR = self.properties.wallDR10
                wallPLvlF = self.properties.wallPF10
                wallPLvlL = self.properties.wallPL10
                wallPLvlB = self.properties.wallPB10
                wallPLvlR = self.properties.wallPR10
                wallELvlF = self.properties.wallEF10
                wallELvlL = self.properties.wallEL10
                wallELvlB = self.properties.wallEB10
                wallELvlR = self.properties.wallER10
                wallCLvlF = self.properties.wallCF10
                wallCLvlL = self.properties.wallCL10
                wallCLvlB = self.properties.wallCB10
                wallCLvlR = self.properties.wallCR10
                wallSLvlF = self.properties.wallSF10
                wallSLvlL = self.properties.wallSL10
                wallSLvlB = self.properties.wallSB10
                wallSLvlR = self.properties.wallSR10
                wallBLvlF = self.properties.wallBF10
                wallBLvlL = self.properties.wallBL10
                wallBLvlB = self.properties.wallBB10
                wallBLvlR = self.properties.wallBR10

            levelBase = planeHL * wallLvl

            floorDisc = False  # affects floor and wall positioning.
            if self.properties.CBaseB:  # if blocks for floor can make disc.
                floorDisc = self.properties.CBaseR

            ############
            # make floor for each level
            if floorLvl:
                objName = OBJ_CF + str(wallLvl)

                # set plane dimensions and location
                floorBase = levelBase
                floorTop = floorBase + self.properties.cBaseT
                floorPW = self.properties.cBaseW
                floorD = self.properties.cBaseD
                floorXO = -floorPW / 2  # center
                floorXE = floorPW / 2
                floorBounds = [floorXO, floorXE, floorBase, floorTop, 0, floorD]

                floorObj = makePlaneObj(objName, floorBounds, baseMtl, floorPW)

                # adjust floor location for 3D cursor since parented to Base...
                yMod = castleScene.cursor_location.y
                if floorDisc:  # make a disc shaped floor
                    yMod += self.properties.cBaseD / 2
                floorObj.location.x -= castleScene.cursor_location.x
                floorObj.location.y -= yMod
                floorObj.location.z -= castleScene.cursor_location.z

                castleScene.objects.link(floorObj)  # must do for generation/rotation

                floorObj.parent = castleObj  # Connect to parent

            ############
            # make walls for each level
            wallSides = [wallFLvl, wallLLvl, wallBLvl, wallRLvl]
            # Wall modifiers, per level, per wall (FBLR not FLBR order to match wall build sequence).
            wallMods = [[wallDLvlF, wallPLvlF, wallELvlF, wallCLvlF, wallSLvlF, wallBLvlF, wallFOLvl],
                        [wallDLvlB, wallPLvlB, wallELvlB, wallCLvlB, wallSLvlB, wallBLvlB, wallBOLvl],
                        [wallDLvlL, wallPLvlL, wallELvlL, wallCLvlL, wallSLvlL, wallBLvlL, wallLOLvl],
                        [wallDLvlR, wallPLvlR, wallELvlR, wallCLvlR, wallSLvlR, wallBLvlR, wallROLvl]
                        ]

            wallLoc[2] = levelBase  # each segment base...
            makeWalls(self, castleScene, castleObj, wallSides, wallMods, wallLoc, wallMtl, wallLvl, floorDisc)
            wallLvl += 1

        ####################
        # Make "Tower"
        if self.properties.cXTest and self.properties.CTower:
            # no steps or shelf for tower...
            wallExtOpts[0] = False
            wallExtOpts[1] = False

            settings['Slope'] = True  # force curvature

            dims['s'] = 0.0  # radial origin
            dims['e'] = CastleX / 2  # effective tower height; affects radius.

            # set block "area": height, width, depth, and spacing.
            blockArea = [dims['e'], CastleX, blockDepth, self.properties.blockZ, settings['hv'], self.properties.Grout]

            # Make "tower" wall.
            wallLoc[0] = 0
            wallLoc[1] = 0
            wallLoc[2] = castleScene.cursor_location.z

            # generate tower...
            cTower1Obj = makeWallObj(self, castleScene, wallLoc, "CTower1", blockArea, [], wallExtOpts, wallMtl)

            cTower1Obj.select_set(True)  # must select to rotate
            # rotate 90 forward (face plant)...
            #            bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[True,False,False])
            # rotate 90 ccw along side
            bpy.ops.transform.rotate(value=-cPieHlf, constraint_axis=[False, True, False])
            bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
            cTower1Obj.select_set(False)  # deselect after rotate else joined with others...

            cTower1Obj.parent = castleObj  # Connect to parent

        ####################
        # Make "Dome"
        settings['Radial'] = self.properties.cDome
        if self.properties.cDome:
            # no steps or shelf for dome...
            wallExtOpts[0] = False
            wallExtOpts[1] = False

            settings['Slope'] = True  # force curvature
            settings['sdv'] = 0.12

            wallLoc[0] = castleScene.cursor_location.x
            wallLoc[1] = midWallD
            wallLoc[2] = self.properties.cDomeZ

            domeMtl = uMatRGBSet('cDome_mat', self.cDomeRGB, matMod=True)

            # set block "area": height, width, depth, and spacing.
            blockArea = [self.properties.cDomeH, CastleX, blockDepth, self.properties.blockZ, settings['hv'], self.properties.Grout]

            # eliminate to allow user control for start/completion by width setting.
            dims['s'] = 0.0  # complete radial

            if midWallD < midWallW:  # use shortest dimension for dome.
                dims['e'] = midWallD
            else:
                dims['e'] = midWallW

            # generate dome...
            cDomeObj = makeWallObj(self, castleScene, wallLoc, "cDome", blockArea, [], wallExtOpts, domeMtl)

            yMod = 0  # shift "location" as needed...
            if floorDisc:  # make a disc shaped floor
                yMod -= (CastleD + blockDepth) / 2
            cDomeObj.location.y += yMod

            cDomeObj.parent = castleObj  # Connect to parent

        castleObj.select_set(True)
        context.view_layer.objects.active = castleObj

        return {'FINISHED'}

################################################################################


####################
#
    # Walls
#
# when variations or openings are selected different block rows are
# generated for each wall; edges/ends don't match.
#
####################
def makeWalls(sRef, objScene, objParent, objSides, objMods, objLoc, objMat, objLvl, floorDisc):

    wallExtOpts = [False, False]  # no steps or shelf

    WobjH = sRef.properties.wallH
    WobjW = sRef.properties.cBaseW
    WobjY = sRef.properties.cBaseD
    WobjD = sRef.properties.blockD

    segWidth = sRef.properties.blockX

    # center wall...
    midWallD = (WobjY + WobjD) / 2
    midWallW = (WobjW + WobjD) / 2

    settings['eoff'] = sRef.properties.EdgeOffset
    settings['sdv'] = sRef.properties.blockX

    settings['Slope'] = False
    if sRef.properties.cXTest:
        if sRef.properties.CTunnel:
            settings['Slope'] = True  # force curve
        else:
            settings['Slope'] = sRef.properties.wallSlope  # user select curve walls...

    # passed as params since modified by wall and dome...
    settings['StepsB'] = sRef.properties.StepF  # fill under with blocks
    settings['StepsL'] = sRef.properties.StepL  # up to left
    settings['StepsO'] = sRef.properties.StepOut  # outside of wall

    # set block "area": height, width, depth, and spacing for front and back walls.
    blockArea = [WobjH, WobjW, WobjD, sRef.properties.blockZ, settings['hv'], sRef.properties.Grout]
    dims['s'] = -midWallW
    dims['e'] = midWallW

    yMod = 0  # shift front/back "location" as needed...
    if floorDisc:  # make a disc shaped floor
        yMod = WobjY

    ####################
    if objSides[0]:  # Make "front" wall
        objName = OBJ_WF + str(objLvl)

        wallRound = objMods[0][6]  # round (disc) wall...

        # adjust sizing for round wall else ensure full height.
        if wallRound:
            settings['Radial'] = True
            blockArea[0] = WobjH / 2  # half height for radius
        else:
            settings['Radial'] = False  # disable disc
            blockArea[0] = WobjH  # ensure full height

        wallSteps = False
        wallShelf = False
        wallHoles = []

        openingSpecs[OP_DOOR]['a'] = objMods[0][0]  # door
        openingSpecs[OP_PORT]['a'] = objMods[0][1]  # window
        openingSpecs[OP_SLOT]['a'] = objMods[0][2]  # slot
        openingSpecs[OP_CREN]['a'] = objMods[0][3]  # crenel
        wallExtOpts[0] = objMods[0][4]  # steps
        wallExtOpts[1] = objMods[0][5]  # shelf

        wallHoles = openList(sRef)

        objLoc[0] = 0
        if sRef.properties.cXTest and sRef.properties.CTunnel:
            objLoc[1] = WobjY / 2
        else:
            objLoc[1] = 0

        objLoc[1] -= yMod / 2  # offset for disc

        # generate wall...
        cFrontObj = makeWallObj(sRef, objScene, objLoc, objName, blockArea, wallHoles, wallExtOpts, objMat)
        cFrontObj.parent = objParent  # Connect to parent

        if wallRound:  # rotate 90 forward if round/disc
            cFrontObj.select_set(True)  # must select to rotate
            bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[True, False, False])
            bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
            cFrontObj.location.z += WobjH / 2  # adjust vertical after rotate for radius
            cFrontObj.select_set(False)  # deselect after rotate else joined with others...
    ####################
    if objSides[2]:  # Make "back" wall
        objName = OBJ_WB + str(objLvl)

        wallRound = objMods[1][6]  # round (disc) wall...

        # adjust sizing for round wall else ensure full height.
        if wallRound:
            settings['Radial'] = True
            blockArea[0] = WobjH / 2  # half height for radius
        else:
            settings['Radial'] = False  # disable disc
            blockArea[0] = WobjH  # ensure full height

        wallSteps = False
        wallShelf = False
        wallHoles = []

        openingSpecs[OP_DOOR]['a'] = objMods[1][0]  # door
        openingSpecs[OP_PORT]['a'] = objMods[1][1]  # window
        openingSpecs[OP_SLOT]['a'] = objMods[1][2]  # slot
        openingSpecs[OP_CREN]['a'] = objMods[1][3]  # crenel
        wallExtOpts[0] = objMods[1][4]  # steps
        wallExtOpts[1] = objMods[1][5]  # shelf

        wallHoles = openList(sRef)

        objLoc[0] = 0
        if sRef.properties.cXTest and sRef.properties.CTunnel:
            objLoc[1] = WobjY / 2
        else:
            objLoc[1] = WobjY

        objLoc[1] -= yMod / 2  # offset for floor disc

        # generate wall...
        cBackObj = makeWallObj(sRef, objScene, objLoc, objName, blockArea, wallHoles, wallExtOpts, objMat)
        cBackObj.parent = objParent  # Connect to parent

        cBackObj.select_set(True)  # must select to rotate

        # rotate to "reverse" face of wall, else just a mirror of front.
        bpy.ops.transform.rotate(value=cPie, constraint_axis=[False, False, True])
        bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)

        if wallRound:  # rotate 90 forward
            bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[True, False, False])
            cBackObj.location.z += WobjH / 2  # adjust vertical after rotate for radius

        cBackObj.select_set(False)  # de-select after rotate else joined with others...
    ####################
    # set block "area": height, width, depth, and spacing for side walls...
    blockArea = [WobjH, WobjY - segWidth * 2, WobjD, sRef.properties.blockZ, settings['hv'], sRef.properties.Grout]
    #    blockArea=[WobjH,WobjY-segWidth,WobjD,sRef.properties.blockZ,settings['hv'],sRef.properties.Grout]
    #    blockArea=[WobjH,WobjY,WobjD,sRef.properties.blockZ,settings['hv'],sRef.properties.Grout]
    # rowprocessing() side walls
    dims['s'] = -midWallD
    dims['e'] = midWallD
    ####################
    ####################
    if objSides[1]:  # Make "Left" wall
        objName = OBJ_WL + str(objLvl)

        wallRound = objMods[2][6]  # round (disc) wall...

        # adjust sizing for round wall else ensure full height.
        if wallRound:
            settings['Radial'] = True
            blockArea[0] = WobjH / 2  # half height for radius
        else:
            settings['Radial'] = False  # disable disc
            blockArea[0] = WobjH  # ensure full height

        wallSteps = False
        wallShelf = False
        wallHoles = []

        openingSpecs[OP_DOOR]['a'] = objMods[2][0]  # door
        openingSpecs[OP_PORT]['a'] = objMods[2][1]  # window
        openingSpecs[OP_SLOT]['a'] = objMods[2][2]  # slot
        # radius/round sizing wrong when crenel selected...
        openingSpecs[OP_CREN]['a'] = objMods[2][3]  # crenel
        wallExtOpts[0] = objMods[2][4]  # steps
        wallExtOpts[1] = objMods[2][5]  # shelf

        wallHoles = openList(sRef)

        if sRef.properties.cXTest and sRef.properties.CTunnel:
            objLoc[0] = 0
        else:
            objLoc[0] = -midWallW

        if floorDisc:  # make a disc shaped floor
            objLoc[1] = 0
        else:
            objLoc[1] = midWallD - (WobjD / 2)
        #        objLoc[1]=midWallD

        # generate wall...
        cSideLObj = makeWallObj(sRef, objScene, objLoc, objName, blockArea, wallHoles, wallExtOpts, objMat)
        cSideLObj.parent = objParent  # Connect to parent

        cSideLObj.select_set(True)  # must select to rotate
        bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)

        if wallRound:  # rotate 90 forward
            bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[True, False, False])
            cSideLObj.location.z += WobjH / 2  # adjust vertical after rotate for radius

        if sRef.properties.cXTest and sRef.properties.CTunnel:
            # rotate 90 horizontal, ccw...
            bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[False, False, True])
        else:
            # rotate 90 horizontal, cw...
            bpy.ops.transform.rotate(value=-cPieHlf, constraint_axis=[False, False, True])
        # rotate 90 forward (face plant)...
        #            bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[True,False,False])
        # rotate 90 cw along side
        #            bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[False,True,False])
        cSideLObj.select_set(False)  # deselect after rotate else joined with others...
    ####################
    if objSides[3]:  # Make "Right" wall
        objName = OBJ_WR + str(objLvl)

        wallRound = objMods[3][6]  # round (disc) wall...

        # adjust sizing for round wall else ensure full height.
        if wallRound:
            settings['Radial'] = True
            blockArea[0] = WobjH / 2  # half height for radius
        else:
            settings['Radial'] = False  # disable disc
            blockArea[0] = WobjH  # ensure full height

        wallSteps = False
        wallShelf = False
        wallHoles = []

        openingSpecs[OP_DOOR]['a'] = objMods[3][0]  # door
        openingSpecs[OP_PORT]['a'] = objMods[3][1]  # window
        openingSpecs[OP_SLOT]['a'] = objMods[3][2]  # slot
        openingSpecs[OP_CREN]['a'] = objMods[3][3]  # crenel
        wallExtOpts[0] = objMods[3][4]  # steps
        wallExtOpts[1] = objMods[3][5]  # shelf

        wallHoles = openList(sRef)

        if sRef.properties.cXTest and sRef.properties.CTunnel:
            objLoc[0] = 0
        else:
            objLoc[0] = midWallW

        if floorDisc:  # make a disc shaped floor
            objLoc[1] = 0
        else:
            objLoc[1] = midWallD - (WobjD / 2)

        cSideRObj = makeWallObj(sRef, objScene, objLoc, objName, blockArea, wallHoles, wallExtOpts, objMat)
        cSideRObj.parent = objParent  # Connect to parent
        #        objScene.objects.active=cSideRObj
        cSideRObj.select_set(True)  # must select to rotate
        bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)

        if wallRound:  # rotate 90 forward
            bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[True, False, False])
            cSideRObj.location.z += WobjH / 2  # adjust vertical after rotate for radius

        if sRef.properties.cXTest and sRef.properties.CTunnel:
            # rotate 90 horizontal, cw...
            bpy.ops.transform.rotate(value=-cPieHlf, constraint_axis=[False, False, True])
        # rotate 90 horizontal, ccw...
        else:
            bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[False, False, True])
        # rotate 90 forward (face plant)...
        #        bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[True,False,False])
        # rotate 90 cw along side
        #        bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[False,True,False])
        cSideRObj.select_set(False)  # deselect after rotate...

        return  # all done, is automatic, just a matter of detail/protocol.


################################################################################

def makeWallObj(sRef, objScene, objLoc, objName, blockArea, openList, wallExtOpts, objMat):

    settings['Steps'] = wallExtOpts[0]
    settings['Shelf'] = wallExtOpts[1]

    meshVs, meshFs = wallBuild(sRef, wallPlan(sRef, blockArea, openList), openList)

    newMesh = bpy.data.meshes.new(objName)
    newMesh.from_pydata(meshVs, [], meshFs)
    # doesn't seem to matter...
    #    newMesh.update(calc_edges=True)
    newMesh.materials.append(objMat)

    newObj = bpy.data.objects.new(objName, newMesh)
    newObj.location.x = objLoc[0]
    newObj.location.y = objLoc[1]
    newObj.location.z = objLoc[2]

    objScene.objects.link(newObj)  # must do for generation/rotation

    return newObj


########################
# Generate simple "plane" for floor objects.
#
# objArea[leftX,rightX,zBase,zTop,0,yDepth]
# objDiv will subdivide plane based on sizing;
#  - see MakeABlock(objArea,objDiv) for details.
#
def makePlaneObj(objName, objArea, objMat, objDiv):
    objVs = []
    objFs = []

    objVs, objFs = MakeABlock(objArea, objDiv)

    objMesh = bpy.data.meshes.new(objName)
    objMesh.from_pydata(objVs, [], objFs)
    objMesh.update()
    #    objMesh.update(calc_edges=True) # doesn't seem to matter...
    objMesh.materials.append(objMat)

    newObj = bpy.data.objects.new(objName, objMesh)
    newObj.location = bpy.context.scene.cursor_location

    return newObj


################################################################################
################
########################
##
#
# Blocks module/script inclusion
# - to be reduced significantly.
##
# Module notes:
#
# consider removing wedge crit for small "c" and "cl" values
# wrap around for openings on radial stonework?
# repeat for opening doesn't distribute evenly when radialized
#  - see wrap around note above.
# if opening width == indent*2 the edge blocks fail (row of blocks cross opening).
# if block width variance is 0, and edging is on, right edge blocks create a "vertical seam".
#
##
#######################
################
################################################################################
##


###################################
#
# create lists of blocks.
#
# blockArea defines the "space" (vertical, horizontal, depth) to fill,
#  and provides block height, variation, and gap/grout. May affect "door" opening.
#
# holeList identifies "openings" in area.
#
# Returns: list of rows.
#  rows = [[center height,row height,edge offset],[...]]
#
def wallPlan(sRef, blockArea, holeList):

    rows = []

    blockAreaZ = blockArea[0]
    blockAreaX = blockArea[1]
    blockAreaY = blockArea[2]
    blockHMax = blockArea[3]
    blockHVar = blockArea[4]
    blockGap = blockArea[5]
    # this is wrong! should be...?
    blockHMin = BLOCK_MIN + blockGap

    # no variation for slopes so walls match curvature
    if sRef.properties.cXTest:
        if sRef.properties.wallSlope or sRef.properties.CTunnel:
            blockHVar = 0

    rowHMin = blockHMin
    # alternate rowHMin=blockHMax-blockHVar+blockGap

    # splits are a row division, for openings
    splits = [0]  # split bottom row
    # add a split for each critical point on each opening
    for hole in holeList:
        splits += hole.crits()
    # and, a split for the top row
    splits.append(blockAreaZ)
    splits.sort()  # needed?

    # divs are the normal old row divisions, add them between the top and bottom split
    # what's with "[1:-1]" at end????
    divs = fill(splits[0], splits[-1], blockHMax, blockHMin, blockHVar)[1:-1]

    # remove the divisions that are too close to the splits, so we don't get tiny thin rows
    for i in range(len(divs) - 1, -1, -1):
        for j in range(len(splits)):
            if abs(divs[i] - splits[j]) < rowHMin:
                del(divs[i])
                break

    # now merge the divs and splits lists
    divs += splits
    divs.sort()

    # trim the rows to the bottom and top of the wall
    if divs[0] < 0:
        divs[:1] = []
    if divs[-1] > blockAreaZ:
        divs[-1:] = []

    # process each row
    divCount = len(divs) - 1  # number of divs to check
    divCheck = 0  # current div entry

    while divCheck < divCount:
        RowZ = (divs[divCheck] + divs[divCheck + 1]) / 2
        RowHeight = divs[divCheck + 1] - divs[divCheck] - blockGap
        rowEdge = settings['eoff'] * (fmod(divCheck, 2) - 0.5)

        if RowHeight < rowHMin:  # skip row if too shallow
            del(divs[divCheck + 1])  # delete next div entry
            divCount -= 1  # Adjust count for removed div entry.
            continue

        rows.append(rowOb(RowZ, RowHeight, rowEdge))

        divCheck += 1  # on to next div entry

    # set up opening object to handle the edges of the wall
    WallBoundaries = OpeningInv((dims['s'] + dims['e']) / 2, blockAreaZ / 2, blockAreaX, blockAreaZ)

    # Go over each row in the list, set up edge blocks and block sections
    for rownum in range(len(rows)):
        rowProcessing(rows[rownum], holeList, WallBoundaries)

    return rows


################################################################################
#
# Build the wall, based on rows, "holeList", and parameters;
#     geometry for the blocks, arches, steps, platforms...
#
# Return: verts and faces for wall object.
#
def wallBuild(sRef, rows, holeList):

    wallVs = []
    wallFs = []

    AllBlocks = []

    # create local references for anything that's used more than once...
    rowCount = len(rows)

    wallTop = rows[rowCount - 1].z
    #    wallTop=sRef.properties.wallH
Loading
Loading full blame...