#!/usr/bin/perl -w

# This is the package Building. It contains some basic buildings and 
# archetecture stuff for use with Bunch.pm 

# usage: see bot, maze, stairs, fireplace and so on 

# (c) Copyright by Tels 1999-2000. All Rights Reserved. Do NOT distribute.
# Updates and support on bloodgate.com/thief
# Thanx to Doug Church at TLG

# [08/28/99] v0.0.1 - from scratch
# [09/10/99] v0.0.2 - some stubs (ramp, bendhallway)
# [09/12/99] v0.0.3 - finished stairs
# [09/18/99] v0.0.4 - added ramp to stairs, fireplace
# [09/20/99] v0.0.5 - tunnel (also good for round windows)
# [09/28/99] v0.0.6 - house, spiral stairs (yippee!)
# [09/30/99] v0.0.7 - added frame around tunnels
# [10/06/99] v0.0.8 - more and better houses, citywall
# 1999-11-06 v0.0.9 - added: shed privyShed toolShed
#                     fixed: fireplace takes named parameters
# 1999-11-10 v0.1.0 - fixed: added rotateByFacing and "named params" for most 
#                     moved fireplace to Inside.pm
# 1999-12-09 v0.1.1 - delet: house, house1, window
#                     fixed: spiralstairs take named parameters
# 1999-12-25 v0.1.2 - added: barBridge
#                     fixed: bridge uses named parameters
# 2000-01-07 v0.1.3 - added: oriel
#                     fixed: moved to ./Architecture
 
package Cowgen::Architecture::Building;
require Exporter;
@ISA    = qw(Exporter);
@EXPORT = qw( 
             barBridge bridge stairs spiralstairs towerstairs bendHallway ramp 
             tunnel hallway cityWall oriel
             shed privyShed toolShed
             );
#@EXPORT_OK = qw();

use Cowgen::Architecture::Basics;
use Cowgen::Dromed;
use Cowgen::Bunch;

sub bridge 
  { 
  # create a bridge facing south, $flags unused yet
  # rheight is the height of the raising left/right (0 to disable),
  # rwidth its thickness, rtext its texture
  # named parameters: facing, rheight, rwidth, toptexture,sidetexture,bottomtexture,rtexture
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  $p = {} if (!defined $p);
  my $facing = $p->{facing} || aSouth();
  my $toptext = $p->{toptexture} || -1;
  my $sidetext = $p->{sidetexture} || $toptext;
  my $bottomtext = $p->{bottomtexture} || $toptext;
  my $rtext = $p->{rtexture} || $toptext;
  my $rwidth = $p->{rwidth} || 1;
  my $rheight = $p->{rheight} || $width;
  my $grid = $p->{gridsize} || 0.25;

  my $b = Cowgen::Bunch->create("bridge##num##");
  $r = $l / 6; # 1/6 ramp length
  my $brush;
  $brush = $b->addBrush (solid(),wedge(),$x+$l/2-$r-$r/4,$y,$z-$h/4,
                  $r/2,$w,$h/2,
                  $toptext,0,180,90,$bottomtext);
  $brush = $b->addBrush (solid(),wedge(),$x-$l/2+$r+$r/4,$y,$z-$h/4,
                  $r/2,$w,$h/2,
                  $toptext,0,180,270,$bottomtext);
  $brush = $b->addBrush (solid(),cube(),$x,$y,$z+$h/4,
                  $w,$l-$r*2,$h/2,
                  $toptext,0,0,0,$bottomtext);
  if ($rheight > 0)
    {
    $brush = $b->addBrush (solid(),cube(),$x,$y+$w/2-$rwidth/2,$z+$h/2+$rheight/2,
                  $rwidth,$l-$r*2,$rheight,$rtext);
    $brush = $b->addBrush (solid(),cube(),$x,$y-$w/2+$rwidth/2,$z+$h/2+$rheight/2,
                  $rwidth,$l-$r*2,$rheight,$rtext);
    $brush = $b->addBrush (solid(),cube(),$x+$l/2-$r/2,$y+$w/2-$rwidth/2,
                  $z+$rheight/2,$rwidth,$r,$h+$rheight,$rtext);
    $brush = $b->addBrush (solid(),cube(),$x-$l/2+$r/2,$y-$w/2+$rwidth/2,
                  $z+$rheight/2,$rwidth,$r,$h+$rheight,$rtext);
    $brush = $b->addBrush (solid(),cube(),$x-$l/2+$r/2,$y+$w/2-$rwidth/2,
                  $z+$rheight/2,$rwidth,$r,$h+$rheight,$rtext);
    $brush = $b->addBrush (solid(),cube(),$x+$l/2-$r/2,$y-$w/2+$rwidth/2,
                  $z+$rheight/2,$rwidth,$r,$h+$rheight,$rtext);
    $brush = $b->addBrush (air()+MNOROOM,wedge(),$x+$l/2-$r/2,$y+$w/2-$rwidth/2,$z+$rheight,
                  $r,$rwidth,$h,$rtext,0,180,90);
    $brush = $b->addBrush (air()+MNOROOM,wedge(),$x-$l/2+$r/2,$y+$w/2-$rwidth/2,$z+$rheight,
                  $r,$rwidth,$h,$rtext,0,180,270);
    $brush = $b->addBrush (air()+MNOROOM,wedge(),$x+$l/2-$r/2,$y-$w/2+$rwidth/2,$z+$rheight,
                  $r,$rwidth,$h,$rtext,0,180,90);
    $brush = $b->addBrush (air()+MNOROOM,wedge(),$x-$l/2+$r/2,$y-$w/2+$rwidth/2,$z+$rheight,
                  $r,$rwidth,$h,$rtext,0,180,270);
    }
  # later because of texture
  $brush = $b->addBrush (solid(),wedge(),$x+$l/2-$r/2,$y,$z,
                  $r,$w,$h,
                  $toptext,0,0,270,$bottomtext);
  $brush = $b->addBrush (solid(),wedge(),$x-$l/2+$r/2,$y,$z,
                  $r,$w,$h,
                  $toptext,0,0,90,$bottomtext);
# $b->rotateByFacing ($x,$y,$z, $facing);
  return $b; # bridge
  }

sub barBridge 
  { 
  # create a bridge with some deco woodwork facing south, optimized to take lesser brushes
  # named parameters: thick (base thickness), texture (texture of base), bartexture 
  # (texture of bars), barwidth (thickness of bars), gridsize
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  $p = {} if (!defined $p);
  my $facing = $p->{facing} || aSouth();
  my $thick = $p->{thick} || 2;
  my $bw = $p->{barwidth} || 1;
  my $t = $p->{texture} || -1;
  my $bars = abs(int($p->{bars} || 1));
  my $space = abs($p->{space} || 0.5);
  my $bt = $p->{bartexture} || $t;
  my $grid = $p->{gridsize} || 0.25;
  my $b = Cowgen::Bunch->create("barBridge##num##");
  my ($br);
  # lay wedges to make 45 deg bars
  my $wl = ($l-$bw)/2; # wedge length
  my $wh = ($h-$bw-$thick); # wedge height, height - base - barwidth for top
  $b->addBrush (solid(),wedge(),$x-$l/2+$wl/2,$y,$z-$h/2+$wh/2+$thick,
                  $wl,$w,$wh,$bt,0,0,90);
  $b->addBrush (solid(),wedge(),$x+$l/2-$wl/2,$y,$z-$h/2+$wh/2+$thick,
                  $wl,$w,$wh,$bt,0,0,270);
  my $wh1 = $wh-$bw; my $wl1;
  ($wl1) = snap2Grid($grid,$wh1*$wl/$wh); 
  $b->addBrush (air()+MNOROOM,wedge(),$x-$wl1/2-$bw/2,$y,$z-$h/2+$wh1/2+$thick,
                  $wl1,$w,$wh1,$bt,0,0,90);
  $b->addBrush (air()+MNOROOM,wedge(),$x+$wl1/2+$bw/2,$y,$z-$h/2+$wh1/2+$thick,
                  $wl1,$w,$wh1,$bt,0,0,270);
  # middle bar
  $b->addBrush (solid(),cube(),$x,$y,$z,$w,$bw,$h,$bt);
  # left/right bar
  $b->addBrush (solid(),cube(),$x,$y,$z-$h/2+$bw/2,$w,$l,$bw,$bt);
  # air walkway
  $b->addBrush (air()+MNOROOM,cube(),$x,$y,$z-$bw/2,
                      $w-$bw*2,$l,$h-$bw,$bt);
  # lay base
  $b->addBrush (solid(),cube(),$x,$y,$z-$h/2+$thick/2-0.25,$w-$bw*2,$l,$thick-0.5,$t);
  if ($bars > 2)
    {
    my ($baw,$i);
    ($baw) = snapLength($grid,($l-($bars-1)*$space)/$bars);
    # first/last bar get corected size due to grid
    my $co = $l- (($bars-2)*$baw+($bars-1)*$space); $co = $co / 2; 
    if ($baw > 0)
      {
      my $sta = $x-$l/2+$co+$space/2;
      for ($i = 1; $i < $bars; $i++)
        {
        $b->addBrush (air()+MNOROOM,cube(),$sta,$y,$z-$h/2+$thick/2-0.25,$w-$bw*2,
                            $space,$thick-0.5,$t,0,0,0,$t,$bt,$t,$bt);
        $sta += ($baw+$space)
        }
      }  
    }
  ($wl) = snap2Grid($grid,$w/3);
  $b->addBrush (air()+MNOROOM,wedge(),$x,$y+$w/2-$wl/2,$z+$h/2-$bw/2,
                  $wl,$bw,$bw,$bt,180,0,0);
  $b->addBrush (air()+MNOROOM,wedge(),$x,$y-$w/2+$wl/2,$z+$h/2-$bw/2,
                  $wl,$bw,$bw,$bt,180,0,180);
  $b->addBrush (solid(),wedge(),$x,$y+$w/2-$wl/2-$bw,$z+$h/2-$bw/2-$bw,
                  $wl,$bw,$bw,$bt,180,0,0);
  $b->addBrush (solid(),wedge(),$x,$y-$w/2+$wl/2+$bw,$z+$h/2-$bw/2-$bw,
                  $wl,$bw,$bw,$bt,180,0,180);
  $b->rotateByFacing ($x,$y,$z, $facing);
  return $b; # bridge
  }

sub stairs 
  { 
  # create stairs in y axis, rising to south by steps steps each stepheight
  # heigh and stepdepth deep, the parameter l is the length, if it is > then 
  # stepdepth*steps, the the last step will be "zoomed", thus creating a 
  # landing platform (landingtexture will applied to this steps top). A l 
  # < then steps*stepdepth will be adjusted for you, lazy ones want to set l to 0.
  # a raising is done by adding left() and/or right() to flags. h > the 
  # stepheight*steps will create a headroom above the stairs if basroom() or
  # bair() is given. A h of 0 will add no headroom. A bwedge() added to flags will 
  # then create a ramped headroom, leaving headroomheight.
  # named parameters: steps (count of steps), stepdepth (depth of one step),
  # stepheight (height of one step), texture (default  for all others)
  # flags (slabs(); filled(); left(); right(); bair()),
  # toptexture (texture of topface), facetexture (texture), sidetexture (texture at side),
  # raisingwidth (only if raised()), raisingheight (only if raised()), 
  # rtoptexture (raising top texture), landingtexture (toptexture of last step, defaults
  # to toptexture), headroomheight,
  # rsidetexture (raising side texture), facing
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my ($yi);
  my $facing = $p->{facing} || aSouth();
  my $steps = $p->{steps} || 16;
  my $sheight = $p->{stepheight} || 0;
  my $sdepth = $p->{stepdepth} || 0;
  my $grid = $p->{gridsize} || 0.25;
  my $rw = $p->{raisingwidth} || 2;
  my $rh = $p->{raisingheight} || 0;
  my $hh = $p->{headroomheight} || 0;
  my $txt = $p->{texture} || -1;
  # fix sizes if caller was lazy
  $sdepth = snap2Grid($grid,$l/$steps) if ($sdepth == 0);
  $sheight = snap2Grid($grid,$h/$steps) if ($sheight == 0);
  $l = $sdepth*$steps if ($l < $sdepth*$steps);
  $h = $sheight*(1+$steps)+$rh+$hh if ($h < $sheight*(1+$steps)+$rh+$hh);
#  $hh = $h-$sheight*$steps-$rh if ($hh > ($h-$sheight*$steps-$rh));
  my $flags = $p->{flags} || 0;
  my $toptext = $p->{toptexture} || $txt;
  my $ltext = $p->{landingtexture} || $toptext;
  my $facetext = $p->{facetexture} || $toptext;
  my $sidetext = $p->{sidetexture} || $toptext;
  my $rttext = $p->{rtoptexture} || $toptext;
  my $rstext = $p->{rsidetexture} || $sidetext; 
  my $b = Cowgen::Bunch->create("stairs##num##");
  my $xo = $x-$l/2; my $zo = $z-$h/2;
  my $i = abs($steps); $xo += $sdepth / 2; $zo += $sheight / 2;
  my $sh = $sheight; $c = 0; my $brush;
  if (($flags & basroom()) != 0)
    {
    $brush = $b->addBrush (air()+MROOM,cube(),$x,$y,$z,$w,$l,
                           $h,$sidetext);
    }
  my ($sd,$bt); $bt = $toptext; $sd = $sdepth;
  while ($i > 0)
    { 
    if ($i == 1)
      {
      # last step 
      $sd = $l-($steps-1)*$sdepth; # landing size 
      $xo += (-$sdepth/2+$sd/2); 
      $bt = $ltext;
      }
    $brush = $b->addBrush (solid(),cube(),$xo,$y,$zo,$w,$sd,$sh,$sidetext);
    $b->setTexture($brush,top(),$bt);
    $b->setTexture($brush,north(),$facetext);
    if (($flags & bfilled()) != 0)
      {
      $zo += ($sheight / 2); $sh += ($sheight);
      }
    else
      {
      $zo += $sheight;
      }
    $xo += $sdepth; 
    $i--;
    }
  # raisings
  my $xi = $x-$l/2+$steps*$sdepth/2;
  my $zi = $z-$h/2+($steps+1)*$sheight/2;
  if (($flags & bleft()) != 0)
    {
    $yi = $y+$w/2-$rw/2;
    $b->addBrush (solid(),cube(),$xi,$yi,$zi+$rh/2,
                  $rw,$steps*$sdepth,$steps*$sheight+$sheight+$rh,
                  $rttext,0,0,0,$rstext);
    $b->addBrush (air()+MNOROOM,wedge(),$xi,$yi,$zi+$sheight/2+$rh,
                  $steps*$sdepth,$rw,$steps*$sheight,
                  $rttext,0,180,270,$rstext);
    }
  if (($flags & bright()) != 0)
    {
    $yi = $y-$w/2+$rw/2;
    $b->addBrush (solid(),cube(),$xi,$yi,$zi+$rh/2,
                  $rw,$steps*$sdepth,$steps*$sheight+$sheight+$rh,
                  $rttext,0,0,0,$rstext);
    $b->addBrush (air()+MNOROOM,wedge(),$xi,$yi,$zi+$sheight/2+$rh,
                  $steps*$sdepth,$rw,$steps*$sheight,
                  $rttext,0,180,270,$rstext);
    }
  if (($flags & bwedge()) != 0)
    {
    $zi = $z+$h/2-$steps*$sheight/2;
    $b->addBrush (solid(),wedge(),$xi,$y,$zi,
                  $steps*$sdepth,$w,$steps*$sheight,
                  $rttext,0,180,270,$rstext);
    }
  $b->rotateByFacing ($x,$y,$z, $facing);
  return $b; # stairs
  }

sub cornerstairs
  {
  # not ready yet. create stairs with a landing in the corner in a south/east corner, rising to 
  # south and then to west, by steps steps each stepheight, 
  # heigh and stepdepth deep, the parameter l is the length, if it is > then 
  # stepdepth*steps, the the last step will be "zoomed", thus creating a 
  # landing platform (landingtexture will applied to this steps top). A l 
  # < then steps*stepdepth will be adjusted for you, lazy ones want to set l to 0.
  # a raising is done by adding left() and/or right() to flags. h > the 
  # stepheight*steps will create a headroom above the stairs if basroom() or
  # bair() is given. A h of 0 will add no headroom. A bwedge() added to flags will 
  # then create a ramped headroom, leaving headroomheight.
  # named parameters: steps (count of steps), stepdepth (depth of one step),
  # stepheight (height of one step), 
  # flags (slabs(); filled(); left(); right(); bair()),
  # toptexture (texture of topface), facetexture (texture), sidetexture (texture at side),
  # raisingwidth (only if raised()), raisingheight (only if raised()), 
  # rtoptexture (raising top texture), landingtexture (toptexture of last step, defaults
  # to toptexture), headroomheight,
  # rsidetexture (raising side texture), facing
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $facing = $p->{facing} || aSouth(); # keep for later
  my $steps = $p->{steps} || 16;
  my $sheight = $p->{stepheight} || 1;
  my $sdepth = $p->{stepdepth} || 2;
  my $rw = $p->{raisingwidth} || 2;
  my $rh = $p->{raisingheight} || 0;
  my $hh = $p->{headroomheight} || 0;
  # fix sizes if caller was lazy
  $l = $sdepth*$steps if ($l < $sdepth*$steps);
  $h = $sheight*(1+$steps)+$rh+$hh if ($h < $sheight*(1+$steps)+$rh+$hh);
#  $hh = $h-$sheight*$steps-$rh if ($hh > ($h-$sheight*$steps-$rh));
  my $flags = $p->{flags} || 0;
  my $toptext = $p->{toptexture} || -1;
  my $ltext = $p->{landingtexture} || $toptext;
  my $facetext = $p->{facetexture} || $toptext;
  my $sidetext = $p->{sidetexture} || $toptext;
  my $rttext = $p->{rtoptexture} || $toptext;
  my $rstext = $p->{rsidetexture} || $sidetext; 
  my $b = Cowgen::Bunch->create("stairs##num##");
  my $xo = $x-$l/2; my $zo = $z-$h/2;
  my $i = abs($steps); $xo += $sdepth / 2; $zo += $sheight / 2;
  my $sh = $sheight; $c = 0; my $brush;
  }

sub cityWall
  {
  # erect a big city or fortress wall, with water, catwalk etc. 
  # named parameters: facing, waterwidth, skytext (sky texture),
  # stonetext, waterdeep, waterwalltext, wallthick, innerheight
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $b = Cowgen::Bunch->create("cityWall##num##");
  my $facing = $p->{facing} || aSouth();
  my $stxt = $p->{stonetext} || -1;
  my $skytxt = $p->{skytext} || sky();

# debug:
#  $b->addBrush (air()+MNOROOM,cube(),$x,$y,$z,$w,$l,$h,
#    $p->{stonetext});

  $b->addBrush (air()+MROOM,cube(),$x+$l/2-$p->{waterwidth}/2-2,$y,$z,
    $pw,$p->{waterwidth}+4,$h,$stext,0,0,0,$stext,
    $skytxt,$skytxt,$skytxt,$skytxt,$stext);

  my $ww = $l - 10 - $p->{waterwidth};
  my $hh = $p->{headheight}; # height of head cube
  $b->addBrush (air()+MROOM,cube(),$x-$l/2+6+$ww/2,$y,$z+$h/2-$hh/2,
    $w,$ww,$hh,$stext,0,0,0, 0,0,0,0,0,$stext);

  $b->addBrush (solid(),wedge(),$x+$l/2-$p->{waterwidth}-2,$y,
    $z-$h/2+$p->{waterdeep}+4,4,$w,8,$stext,0,0,270);
 
  $b->addBrush (solid(),wedge(),$x+$l/2-$p->{waterwidth}+1,$y,
    $z-$h/2+$p->{waterdeep}/2,
    2,$w,$p->{waterdeep},$p->{waterwalltext},0,0,270);

  $b->addBrush (solid(),wedge(),$x+$l/2-1,$y,
    $z-$h/2+$p->{waterdeep}/2,
    2,$w,$p->{waterdeep},$p->{waterwalltext},0,0,90);

  $b->addBrush (flood(),cube(),$x+$l/2-$p->{waterwidth}/2,$y,
    $z-$h/2+($p->{waterdeep}-2)/2,
    $w,$p->{waterwidth},$p->{waterdeep}-2,$p->{waterwalltext});
  
  my $ww2 = $ww - $p->{wallthick}*2;
  $b->addBrush (air()+MROOM,cube(),$x-$l/2+6+$ww2/2+$p->{wallthick},$y,
    $z+$h/2-$hh-$p->{walkwayheight}/2,
    $w,$ww2, $p->{walkwayheight},$p->{stonetext});

  # inner walkway
  $b->addBrush (air()+MROOM,cube(),$x-$l/2+6+$ww2/2+$p->{wallthick}*0.5,$y,
    $z+$h/2-$hh-$p->{walkwayheight}-$p->{wallthick}-$p->{innerheight}/2,
    $w,$ww2, $p->{innerheight},$p->{stonetext},0,0,0);

  $b->addBrush (air()+MROOM,cube(),$x-$p->{length}/2+3,$y,$z+$p->{waterdeep}/2,
    $w,6, $h-$p->{waterdeep},$stext,0,0,0,0,$p->{skytext},0,$skytext,$skytext);

  $b->addBrush (solid(),wedge(),$x-$l/2+4,$y,$z-$h/2+$p->{waterdeep}+6,
    4,$w,12,$stext,90,0,0);

  # top of wall
  $b->addBrush (solid(),wedge(),$x-$l/2+6+$p->{wallthick}/2,$y,
    $z+$h/2-$hh+1,$p->{wallthick},$w,2,$stext,0,0,90);

  # inner edge
  $b->addBrush (solid(),wedge(),$x-$l/2+6-$p->{wallthick}+$ww-1,$y,
    $z+$h/2-$hh+2-$p->{walkwayheight},2,$w,4,$stext,0,0,90);

  # add blocks on top
  my $c = $p->{width}/8; # count
  my $i;
  for ($i = 0; $i<$c; $i++)
    {
    $b->addBrush (solid(),cube(),$x-$l/2+6+$ww-$p->{wallthick}/2,
      $y-$w/2+$i*8+2, $z+$h/2-$hh+2,$p->{wallthick},4,4,$stext,0,0,0);
    }
  # woodden walkway not ready yet

  $b->rotateByFacing ($x,$y,$z, $facing);
  return $b; # cityWall
  }

sub tunnel
  {
  # create a hallway with a round ceiling and/or a round bottom
  # give roundtop(), roundbottom() or both as $flags. 0 gives
  # default roundtop+roundbottom, $height is the height between
  # start of ceiling/floor roundings.
  # $sizex and $sizez are factors >= 1
  my ($x,$y,$z,$flags,$sizex,$sizez,$length,$height,$width,$thick,$text) = @_;
  my $b = Cowgen::Bunch->create("tunnel##num##");
  $flags = roundbottom()+roundtop() if ($flags == 0);
  $h = $height; $height = $height + $sizez*6.5; 
  $b->addBrush (solid,cube(),$x,$y,$z,$sizex*6.5+$width+$thick,$length,
    $height+$thick,$text) if ($thick > 0);
  $b->addBrush (air()+MROOM,cube(),$x,$y,$z,$sizex*6.5+$width,$length,$height,$text);
  $b->merge ( roundBottom ($x,$y,$z-$h/2-$sizez*1.5,$sizex,$sizez,$length,$width,solid(),$text) ) 
   if (($flags & roundbottom()) != 0);
  $b->merge ( roundTop ($x,$y,$z+$h/2+$sizez*1.5,$sizex,$sizez,$length,$width,solid(),$text) ) 
   if (($flags & roundtop()) != 0);
  return $b; # tunnel
  }

sub hallway
  {
  # create a hallway with a flat/round ceiling and a flat bottom
  # and a support arc at the start, then multiply this $count times
  # give roundtop() or 0 as $flags.
  # $sizex and $sizez are factors >= 1
  # width = width between top roundings, height = height of bottom part,
  # length is simple all length
  my ($x,$y,$z,$width,$length,$height,$cw,$count,$flags,$sizex,$sizez,$t1,$t2,$t3) = @_;
  my ($h,$w);
  my $b = Cowgen::Bunch->create("hallway##num##");
  my $bu = Cowgen::Bunch->create("hallway##num##");
#  print "Building tunnel $sizex"."x$sizez with length $length\n";
  $h = $height; $height = $height + $sizez*6.5; 
  $w = $sizex*6.5+$width;
  $b->addBrush (air()+MROOM,cube(),$x,$y,$z,$sizex*6.5+$width,$length,$height,$t1,0,0,0,$t2);
  $b->merge ( roundTop ($x,$y,$z+$h/2+$sizez*1.5,$sizex,$sizez,$length,$width,solid(),$t1) ) 
   if (($flags & roundtop()) != 0);
  $b->addBrush (solid(),cube(),$x+$length/2-$cw/2,$y+$w/2-$cw/2,$z,$cw,$cw,$height,$t2);
  $b->addBrush (solid(),cube(),$x+$length/2-$cw/2,$y-$w/2+$cw/2,$z,$cw,$cw,$height,$t2);
  $b->addBrush (air()+MNOROOM,cube(),$x-$cw/2,$y,$z-$height/2+$cw/2,$sizex*6.5+$width,$length-$cw,$cw,$t3);
  $b->addBrush (air()+MNOROOM,cube(),$x+$length/2-$cw/2,$y,$z-$height/2+$cw/2,$width+$sizex*6.5-$cw*2,$cw,$cw,$t3);
  $b->addBrush (solid(),cube(),$x+$length/2-$cw/2,$y,$z+$height/2-$cw/2,$width+$sizex*6.5-$cw*2,$cw,$cw,$t2);
  $b->merge ( roundTop ($x+$length/2-$cw/2,$y,$z+$h/2+$sizez*1.5-$cw,$sizex,$sizez,
   $cw,$width-$cw*2,solid(),$t2) ) if (($flags & roundtop()) != 0);
  $bu->merge( $b->cloneRow( $count, $length, 0, 0 ));
  return $bu; # hallway
  }

sub spiralstairs 
  { 
  # create spiral stairs starting facing south, rising bleft/bright by steps per 1/4 circle,
  # each stepheight, d is the outer diameter (actually with/height since it is rectangular)
  # isteps the count of steps between each 1/4 circle, parts = 1/8 circles
  # $id = inner diameter, 0 for disable column, ot = outer texture, it = innertexture
  # flags = bright() is default, asroom(), bleft()
  # theight = height of air room above the last step

  my ($x,$y,$z,$d,$id,$steps,$isteps,$sheight,$parts,$spart,$theight,$flags,$tt,$ft,$ot,$it) = @_;
  my ($oz,$isteps2,$i,@m,$v,$height,$swidth2,$air); # @m: midpoints of each 1/4
  my $swidth = ($d/$steps); # step width (make sure d is dividable to something gridaligned!
  $m[0] = $x+$d/2+$swidth*$isteps/2; $m[1] = $y+$d/2+$swidth*$isteps/2;
  $m[2] = $m[0]; $m[3] = $y-$d/2-$swidth*$isteps/2;
  $m[4] = $x-$d/2-$swidth*$isteps/2; $m[5] = $m[3];
  $m[6] = $m[4]; $m[7] = $m[1];
  $oz = $z; $swidth2 = $swidth/2; $air = air()+MNOROOM;
  $isteps2 = $isteps/2;
  my $bunch = Cowgen::Bunch->create("spiralstairs##num##");
  $height = ($steps+$isteps2)*$sheight*$parts+$theight;
  if (($flags & basroom()) != 0)
    { 
    # add room around it
    $bunch->addBrush (air()+MROOM,cube(),
      $x,$y,$oz,$d*2+$isteps*$swidth,$d*2+$isteps*$swidth,$height,$ot);
    }
  $z = $z-$height/2+$sheight/2;
  if (($flags & bleft()) != 0)
    {
    $sheight = -$sheight;
    $z = $z+$height-$theight+$sheight;
    }
  $v = $spart; $parts = $spart+$parts;
  for ($i = $spart; $i<$parts; $i++)
    {
    if ($v == 0)
      {
      # lay isteps
      for ($j = 0; $j < $isteps2;$j++)
        {
        $bunch->addBrush (solid(),cube(),$x+$swidth2+$j*$swidth,$m[1],
          $z,$d,$swidth,$sheight,$tt,0,0,0,$ft,0,$ft);
        $z += $sheight;
        }
      # now lay 1/4 part, carve out
      for ($j = 0; $j < $steps; $j++)
        {
        $bunch->addBrush (solid,wedge(),$m[0]+$j*$swidth2-$d/2+$swidth2,$m[1],
         $z,($j+1)*$swidth,$sheight,$d,$tt,0,90,270,$ft,0,$ft); 
        if ($j > 0)
          {
          $bunch->addBrush ($air,wedge(),$m[0]+($j-1)*$swidth2-$d/2+$swidth2,$m[1],
          $z,($j)*$swidth,$sheight,$d,$tt,0,90,270,$ft,$ot); 
          }
        $z += $sheight;
        }
      } # part 0
    if ($v == 1)
      {
      # add
      for ($j = $steps-1; $j >= 0; $j--)
        {
        $bunch->addBrush (solid,wedge(),$m[0],$m[1]+$j*$swidth2-$d/2+$swidth2,$z,
         $d,$sheight,($j+1)*$swidth,$tt,0,90,90,$ft,$ft); 
        if ($j > 0)
          {
          $bunch->addBrush ($air,wedge(),$m[0],$m[1]+($j-1)*$swidth2-$d/2+$swidth2,$z,
           $d,$sheight,$j*$swidth,$tt,0,90,90,$ft,0,$ot); 
          }
        $z += $sheight;
        }
      # lay isteps
      for ($j = $isteps2-1; $j >= 0; $j--)
        {
        $bunch->addBrush (solid(),cube(),$m[2],$y+$swidth2+$j*$swidth,
          $z,$swidth,$d,$sheight, $tt,0,0,0,0,$ft,0,$ft);
        $z += $sheight;
        }
      } # part 1
    if ($v == 2)
      {
      # lay isteps
      for ($j = 0; $j < $isteps2; $j++)
        {
        $bunch->addBrush (solid(),cube(),$m[2],$y-$swidth2-$j*$swidth,
          $z,$swidth,$d,$sheight, $tt,0,0,0,0,$ft,0,$ft);
        $z += $sheight;
        }
      for ($j = 0; $j < $steps; $j++)
        {
        $bunch->addBrush (solid,wedge(),$m[2],$m[3]-$j*$swidth2+$d/2-$swidth2,$z,
         ($j+1)*$swidth,$sheight,$d,$tt,0,90,180,$ft,0,$ft); 
        if ($j > 0)
          {
          $bunch->addBrush ($air,wedge(),$m[2],$m[3]-($j-1)*$swidth2+$d/2-$swidth2,$z,
           ($j)*$swidth,$sheight,$d,$tt,0,90,180,$ft,0,$ot); 
          }
        $z += $sheight;
        }
      } # part 2
    if ($v == 3)
      {
      # add
      for ($j = $steps-1; $j >= 0; $j--)
        {
        $bunch->addBrush (solid,wedge(),$m[2]+$j*$swidth2-$d/2+$swidth2,$m[3],$z,
         $d,$sheight,($j+1)*$swidth,$tt,0,90,0,$ft,$ft); 
        if ($j > 0)
          {
        $bunch->addBrush ($air,wedge(),$m[2]+($j-1)*$swidth2-$d/2+$swidth2,$m[3],$z,
         $d,$sheight,$j*$swidth,$ot,0,90,0,$ft); 
          }
        $z += $sheight;
        }
      # lay isteps
      for ($j = $isteps2-1; $j >= 0; $j--)
        {
        $bunch->addBrush (solid(),cube(),$x+$swidth2+$j*$swidth,$m[3],
          $z,$d,$swidth,$sheight,$tt,0,0,0,$ft,$ft,$ft);
        $z += $sheight;
        }
      } # part 3
    if ($v == 4)
      {
      # lay isteps
      for ($j = 0; $j < $isteps2; $j++)
        {
        $bunch->addBrush (solid(),cube(),$x-$swidth2-$j*$swidth,$m[3],
          $z,$d,$swidth,$sheight, $tt,0,0,0,$ft,$ft,$ft);
        $z += $sheight;
        }
      # carve out
      for ($j = 0; $j < $steps; $j++)
        {
        $bunch->addBrush (solid,wedge(),$m[4]-$j*$swidth2+$d/2-$swidth2,$m[5],$z,
         ($j+1)*$swidth,$sheight,$d,$tt,0,90,90,$ft,0,$ft); 
        if ($j > 0)
          {
          $bunch->addBrush ($air,wedge(),$m[4]-($j-1)*$swidth2+$d/2-$swidth2,$m[5],$z,
           $j*$swidth,$sheight,$d,$tt,0,90,90,$ft,$ot); 
          }
        $z += $sheight;
        }
      } # part 4
    if ($v == 5)
      {
      for ($j = $steps-1; $j >= 0; $j--)
        {
        $bunch->addBrush (solid,wedge(),$m[4],$m[5]-$j*$swidth2+$d/2-$swidth2,$z,
         $d,$sheight,($j+1)*$swidth,$tt,0,90,270,$ft,$ft); 
        if ($j > 0)
          {
         $bunch->addBrush ($air,wedge(),$m[4],$m[5]-($j-1)*$swidth2+$d/2-$swidth2,$z,
           $d,$sheight,$j*$swidth,$tt,0,90,270,$ft,0,$ot); 
          }
        $z += $sheight;
        }
      # lay isteps
      for ($j = ($isteps2-1); $j >= 0; $j--)
        {
        $bunch->addBrush (solid(),cube(),$m[4],$y-$swidth2-$j*$swidth,
          $z,$swidth,$d,$sheight, $tt,0,0,0,$ft,$ft,$ft,$ft);
        $z += $sheight;
        }
      } # part 5
    if ($v == 6)
      {
      # lay isteps
      for ($j = 0; $j < $isteps2; $j++)
        {
        $bunch->addBrush (solid(),cube(),$m[4],$y+$swidth2+$j*$swidth,
          $z,$swidth,$d,$sheight, $tt,0,0,0,0,$ft,0,$ft);
        $z += $sheight;
        }
      # carve out
      for ($j = 0; $j <$steps; $j++)
        {
        $bunch->addBrush (solid(),wedge(),$m[6],$m[7]+$j*$swidth2-$d/2+$swidth2,$z,
         ($j+1)*$swidth,$sheight,$d,$tt,0,90,0,$ft,$ot,$ft); 
        if ($j > 0)
          {
          $bunch->addBrush ($air,wedge(),$m[6],$m[7]+($j-1)*$swidth2-$d/2+$swidth2,$z,
           $j*$swidth,$sheight,$d,$tt,0,90,0,$ft,$ot); 
          }
        $z += $sheight;
        }
      } # part 6
    if ($v == 7)
      {
      # add
      for ($j = $steps-1; $j >= 0; $j--)
        {
        $bunch->addBrush (solid,wedge(),$m[6]-$j*$swidth2+$d/2-$swidth2,$m[7],$z,
         $d,$sheight,($j+1)*$swidth,$tt,0,90,180,$ft,$ft); 
        if ($j > 0)
          {
          $bunch->addBrush ($air,wedge(),$m[6]-($j-1)*$swidth2+$d/2-$swidth2,$m[7],$z,
           $d,$sheight,$j*$swidth,$tt,0,90,180,$ft,0,$ot); 
          }
        $z += $sheight;
        }
      # lay isteps
      for ($j = ($isteps2-1); $j >= 0; $j--)
        {
        $bunch->addBrush (solid(),cube(),$x-$swidth2-$j*$swidth,$m[1],
          $z,$d,$swidth,$sheight,$tt,0,0,0,$ft,$ft,$ft);
        $z += $sheight;
        }
      } # part 7
    $v++; $v = 0 if ($v == 8);
    }
  if ($id > 0)
    {
    $bunch->addBrush (solid(),cube(),$x,$y,$oz,$id,$id,$height,$it); 
    }
  return $bunch; # spiralstairs
  }

sub towerstairs 
  { 
  # create some stair in a rectangle, suited for a rectangular tower
  # parameter h is only for creating an air room around it, steps will
  # always start at z-h/2 (floor level).
  # named parameters: texture (step texture and used as default),
  # sidetexture (step side texture), walltexture (only used if 
  # flags = basroom()), flags (basroom() bleft() bright()),
  # toptexture, facetexture, stepwidth, stepdepth, stepheight, steps (count of steps
  # at one side to produce holes or overlap between steps), startpart (1/4 circle
  # to start with; 0..3; 0 = southwest), 
  # parts (how many 1/4 circles to lay) 
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
 
  my $b = Cowgen::Bunch->create("towerstairs##num##");
  my $txt = $p->{texture} || -1;
  my $wtxt = $p->{walltexture} || $txt;
  my $stxt = $p->{sidetexture} || $txt;
  my $ttxt = $p->{toptexture} || $txt;
  my $ftxt = $p->{facetexture} || $txt;
  my $stepdepth = $p->{stepdepth} || 4;
  my $stepwidth = $p->{stepwidth} || 4;
  my $stepheight = $p->{stepheight} || 1;
  my $flags = $p->{flags} || 0;
  $b->addBrush(air()+MROOM,cube(), $x,$y,$z,$w,$l,$h,$wtxt) 
   if (($flags & basroom()) != 0);
  my $i = $p->{startpart} || 0; $i = abs(int($i)) & 0x3;
  my $parts = $p->{parts} || 1;
  my @dir = ( 1,0,0,1, 0,1,-1,0, -1,0,0,-1, 0,-1, 1,0); # step advance directions
  my $w1 = ($w-$stepwidth)/2; my $l1 = ($l-$stepwidth)/2;
  my @st = ( 0,-$w1, $l1,0, 0,$w1, -$l1,0 ); # step start directions
  my @st2 = ( $l1,-$w1, $l1,$w1, -$l1,$w1, -$l1,-$w1 ); # step start directions edge
  my @r = ( 0, 90, 180, 270); # step rotation
  my $steps = abs(($p->{steps} || 4) >> 1);
  my $k = $i * 4; # index into @dir
  my $za = $z-$h/2+$stepheight/2;
  $n = 0; $xsw = ($w/2-$stepdepth)/($steps); $ysw = ($l/2-$stepdepth)/($steps);
  for ( ; $n < $parts; $n++)
    {
    $xs = $xsw*$dir[$k]; $ys = $ysw*$dir[$k+1]; # get dir for first 1/8 part
    $xa = $st[$i*2]+$x+$dir[$k]*$stepdepth/2; $ya = $st[$i*2+1]+$y+$dir[$k+1]*$stepdepth/2;
    $k += 2;
    for ($j = 0; $j <= $steps; $j ++)
      {    
      $br = $b->addBrush(solid(),cube(), $xa,$ya,$za,$stepwidth,$stepdepth,$stepheight,
      $stxt,$r[$i],0,0);
      $b->setTexture ($br,top(),$ttxt) if ($ttxt != $stxt);
      $b->setTexture ($br,side1(),$ftxt) if ($ftxt != $stxt);
      $xa += $xs; $ya += $ys; $za += $stepheight;
      }
    $xa -= $xs; $ya -= $ys; # doesnt work for stepdepth != stepwidth
    $xs = $xsw*$dir[$k]; $ys = $ysw*$dir[$k+1]; # get dir 
    $xa += $xs; $ya += $ys;
    for ($j = 1; $j <= $steps; $j ++)
      {    
      $br = $b->addBrush(solid(),cube(), $xa,$ya,$za,$stepwidth,$stepdepth,$stepheight,
      $stxt,$r[($i+1)&0x3],0,0);
      $b->setTexture ($br,top(),$ttxt) if ($ttxt != $stxt);
      $b->setTexture ($br,side1(),$ftxt) if ($ftxt != $stxt);
      $xa += $xs; $ya += $ys; $za += $stepheight;
      }
    $k += 2; $k = $k & 0xF;
    $i ++; $i = $i & 0x3;
    }
  return $b; # towerstairs
  }

sub bendHallway
  {
  # create a bend hallway by combining outer and inner bend
  my ($x,$y,$z,$radiusouter,$radiusinner,$height,$sa,$ea,$steps,$flags,
   $gridsize,$to,$ti,$f) = @_;
  my $b = innerBend($x,$y,$z,$radiusinner,$height,$sa,$ea,$steps,0,
   $gridsize,$ti,$tf);
  $b->merge ( outerBend($x,$y,$z,$radiusouter,$height,$sa,$ea,$steps, 
   $flags,$gridsize,$to,$tf) );
  return $b; # bend
  }

sub shed
  {
  # creates a small shed with a wedge as roof, can place windows/doors in
  # any wall
  # named parameters: rooftoptexture, rooftexture, walltexture, frametexture,
  # wallthick, facing (for roof only), roofthick,
  # placewindow (add aNorth;aSouth etc to place in that wall a window),
  # placedoor (like placewindow), doorwidth(defaults to 3.5), doorheight 
  # (defaults to 7)
  # windowwidth (defaults to 3), windowheight (defaults to 4), windowgrid 
  # (yes/no),
  # doorframe (yes/no), walltexture, floortexture, ceilingtexture, roofheight
  # roofspacing (distance between outer roof edge and outer wall; 
  # defaults to 0.25)
  # flags (basroom)
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my ($i,$cnt,@p,$dwf,$k,@r);
  # grab params
  my $pd = $p->{placedoor} || aSouth();
  my $pw = $p->{placewindow} || aSouth();
  my $thick = $p->{wallthick} || 1;
  my $rh = $p->{roofheight} || 1; $rh = 2 if ($rh < 1);
  my $rs = $p->{roofspacing} || 0.25; $rs = 0.25 if ($rh < 0);
  my $rt = $p->{roofthick} || 0.25; $rt = 0.25 if ($rt < 0.25);
  my $rtt = $p->{rooftoptexture} || $p->{rooftexture};
  my $dh = $p->{doorheight} || 7;
  my $dw = $p->{doorheight} || 3.5;
  my $facing = $p->{facing} || aSouth();  
  $dfw = 0;
  $dfw = $p->{doorframe} if ((defined $p->{doorframe}) && ($p->{doorframe} == yes()));
  # now lets go
  my $b = Cowgen::Bunch->create("shed##num##");
  $b->addBrush (air()+MROOM,cube(), $x,$y,$z, $w,$l,$h,$p->{walltexture})
   if ((defined $p->{flags}) && ( ($p->{flags} & basroom()) != 0));
  $b->addBrush (solid(),cube(), $x,$y,$z+$rt/2, $w-$rs,$l-$rs,$h-$rt, $p->{walltexture});
  $b->addBrush (solid(),wedge(), $x,$y,$z+$h/2-$rt-$rh/2, $w,$l,$rh, $rtt, 180,0,90);
  $b->addBrush (solid(),cube(), $x,$y,$z+$h/2-$rt/2, $w,$l, $rt, $rtt);
  $b->addBrush (air()+MNOROOM,wedge(), $x,$y,$z+$h/2-$rh/2, $w,$l,$rh, $rtt,180,0,90);
  # inside
  $b->addBrush (air()+MNOROOM,cube(), $x,$y,$z+$rt/2-$rh/2,
    $w-$rs-$thick*2,$l-$rs-$thick*2,$h-$rt-$rh, $p->{walltexture});
  $b->addBrush (air()+MNOROOM,wedge(), $x,$y,$z+$h/2-$rh-$rh/2,
    $w-$rs-$thick*2,$l-$rs-$thick*2,$rh, $p->{walltexture},0,0,90);
  
  # place door/windows
  @p = genCubesByFacing ($x,$y,$z, $x-$w/2+$rs, $y, $z-$h/2+$dh/2+$dfw,$dw+$dfw*2,$dh+$dfw*2,$thick, 
   $pd);
  $cnt = (scalar @p / 7); $k = 0;   
  my $pf = { thick => $dfw, texture => $p->{frametexture}, };
  for ($j = 0; $j < $cnt; $j ++)
    {
    $pf->{facing} = $p[$k+6];
    $b->merge ( frame ( $p[$k], $p[$k+1], $p[$k+2], $p[$k+3], $p[$k+4], $p[$k+5], $pf));
    $k += 7;
    }
  $b->rotateByFacing ($x,$y,$z, $facing);
  return $b; # shed
  }

sub oriel
  {
  # create a oriel with a window in it, that replaces part of the wall,
  # honour wallthick.
  # named parameters: texture, innertexture (defaults to texture),
  # wallthick (defaults to 0.5), facing, gridsize, centerwidth (width
  # of the center wall, defaults to 0.25 of width)
  my $b = Cowgen::Bunch->create("oriel##num##");
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $facing = $p->{facing} || aSouth();
  my $t = $p->{texture} || -1;
  my $ti = $p->{innertexture} || $t;
  $wt = $p->{wallthick} || 0.5;
  $grid = $p->{gridsize} || 0.25;
  $cs = rel2abs($p->{centerwidth} || -0.25,$w);
  my $e = copyhash($p);
  $e->{facing} = aSouth();
  $b->merge ( wedgeWall ($x,$y,$z,$w,$l,$h,$e));
  $e->{facing} = aEast();
  $b->merge ( wedgeWall ($x,$y,$z,$w,$l,$h,$e));
  # center
  $b->addBrush (solid(),cube(),$x,$y,$z,$cw,$wt,$h,$t);
  # clobber wall
  $b->addBrush (air()+MNOROOM,cube(),$x,$y,$z,$cw,$wt,$h,$ti);
  $b->purgeGroups();
  $b->rotateByFacing($x,$y,$z,$facing);
  $b; # oriel
  } 
1;

