#!/usr/bin/perl -w

# This is the package Basics. It contains some basic stuff for use with
# Building.pm.

# usage: see Building.pm

# (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/02/99] v0.0.2 - fixed arc (still alpha)
# [09/11/99] v0.0.3 - added bends and ramp, frame
# [09/21/99] v0.0.4 - frame (for windows, doors), roundBottom, roundTop,
#                     added supportBeamSingle&Double
# [10/08/99] v0.0.5 - complitely rewritten bends, added Spiral
#                     added breakBlock, added smallWall, column, supportColumn
# 1999-11-07 v0.0.6 - added: frameGrid, grid
#                     fixed: frame takes named parameters and honours facing
# 1999-11-10 v0.0.7 - fixed: supportBeamSingle, Double, rotateByFacing
#                            grid can have 0 bars 
#                     added: basin (for flowerbeds, pools) etc
# 1999-11-24 v0.0.8 - added: basicRoom, bair, carpet, putDoor
#                            frame uses innertexture, floortexure, blockwidth
#                     remov: braised (is now left(), right())
#                     fixed: column to use pyramind instead of wedge
#                            frame thickness, frame adds light
#                            basicRoom honours noWall
# 1999-12-05 v0.0.9 - added: putLight (from VHouse.pm), bed, dome, arc => arch
# 1999-12-24 v0.1.0 - added: brokenHole, tunnelRoom, atticRoom
#                     fixed: bed => Insides, dome has now correct texture 
#                            carpet => putCarpet, carpet is now vroom conform
# 2000-01-07 v0.1.1 - fixed: bug in frameGrid when barsx/barsz != 1
#                     added: crossattic to attic room
#                            wedgeWall
#                     jds:   arch now takes "light" parameter that is passed to
#                            to addLight() function.
# 2000-02-22 v0.1.2 - added: sidetexture/clobberwall, onesided in frame()
#                            noedge stuff
#                            portal, 
#                     remove: outer/innerspiral
# 2000-03-03 v0.1.3 - added: balcony, brush

package Cowgen::Architecture::Basics;
require Exporter;
@ISA    = qw(Exporter);
@EXPORT = qw( 
            portal arch dome column supportColumn innerBend outerBend  
            balcony
            frame breakBlock smallWall grid frameGrid basin 
            carpet putCarpet 
            putFrames putLight brokenHole
            roundBottom roundTop
            supportBeamSingle supportBeamDouble

            bslabs bfilled bhollow basroom bair
            bsimple bbig bsmall bmedium bhuge btiny
            binterior bleft bright bback bframes bcellar bcenter bwedge 
            bsides bchimney
            wedgeWall 
            roundtop roundbottom
            floodBrush
            );
#@EXPORT_OK = qw();

use Cowgen::Bunch;
use Cowgen::Dromed;
use strict 'vars';

sub bhuge { 4; }
sub bbig { 3; }
sub bmedium { 2; }
sub bsmall { 1; }
sub btiny { 0; }
sub bsimple {0x100;}
sub bwedge {0x200;} # for stairs

sub binterior { 0x01;} # does a house get interior stuff or is it only from outside?
sub bleft 
  { 
  # has house left side? spiral stairs to right/left
  0x200;
  }     
sub bright 
  { 
  # has house right side? raising of stairs on the left/right side
  0x400;
  }    
sub bback { 0x08;}     # has house back side?
sub bframes { 0x10;}   # door frames
sub bcellar { 0x20; }  # do you need a cellar?
sub bsides { 0x40; }   # do we need house sides?
sub bchimney { 0x80; } # do we need a chimney?
sub bcenter { 0x100; } # do we need a chimney?

sub roundbottom { 1; }
sub roundtop { 2; }

sub bslabs { 0; }
sub bfilled { 1; }
sub bhollow { 2; }

sub bair 
  { 
  # add this flag to make stairs carved out from solid by air brushes
  # instead of solid slabs/blocks, if you give this, then basroom() is unnecc.
  4;
  } 

sub basroom
  { 
  # if you create bunch into the solid void, to make air room around it
  return 0x1000;
  }

sub roundBottom
  { 
# creates a halfPipe with a round floor, by using as few
# as possible brushes but getting a better rounding then with circles. The size
# is given as a factor >= 1 (due to wedges used). $kind is air(), solid() etc
# can be used to cut out a round from a solid brush (air) or to add a round
# tunnel bottom to an air brush (solid)
# w is the width that gets inserted between the tow halves - 0 for circle
  my ($x,$y,$z,$sizex,$sizez,$thick,$w,$kind,$t) = @_;
  my $b = Cowgen::Bunch->create("roundbottom##num##");
  $sizex = int($sizex)/2; $w = $w / 2;  $sizez = int($sizez)/2;
  my $h = $z - 3.5*$sizez; $kind += MNOROOM if ($kind == air());
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+4.5*$sizex,$h + $sizez*2,$sizex*4,$thick,$sizez*4,$t,0,0,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-4.5*$sizex,$h + $sizez*2,$sizex*4,$thick,$sizez*4,$t);
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+6*$sizex,$h + $sizez*4,$sizex,$thick,$sizez*3,$t,0,0,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-6*$sizex,$h + $sizez*4,$sizex,$thick,$sizez*3,$t);
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+5.5*$sizex,$h + $sizez*3.25,$sizex,$thick,$sizez*1.5,$t,0,0,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-5.5*$sizex,$h + $sizez*3.25,$sizex,$thick,$sizez*1.5,$t);
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+3.25*$sizex,$h + $sizez,$sizex*1.5,$thick,$sizez,$t,0,0,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-3.25*$sizex,$h + $sizez,$sizex*1.5,$thick,$sizez,$t);
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+2.5*$sizex,$h + $sizez*0.5,$sizex*3,$thick,$sizez,$t,0,0,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-2.5*$sizex,$h + $sizez*0.5,$sizex*3,$thick,$sizez,$t);
  return $b; # round bottom
  }

sub roundTop
  { 
# creates a round ceiling or a window top, by using as few
# as possible brushes but getting a better rounding then with circles. The size
# is given as a factor >= 1 (due to wedges used). $kind is air(), solid() etc
# can be used to cut out a round from a solid brush (air) or to add a round
# top to an window (solid)
# w is the width that gets inserted between the tow halves - 0 for circle
  my ($x,$y,$z,$sizex,$sizez,$thick,$w,$kind,$t) = @_;
  my $b = Cowgen::Bunch->create("roundtop##num##");
  $sizex = int($sizex)/2; $w = $w / 2; $sizez = int($sizez)/2; 
  my $h = $z + 3.5*$sizez; $kind += MNOROOM if ($kind == air());
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+4.5*$sizex,$h - $sizez*2,$sizex*4,$thick,$sizez*4,$t,0,180,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-4.5*$sizex,$h - $sizez*2,$sizex*4,$thick,$sizez*4,$t,0,180);
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+6*$sizex,$h - $sizez*4,$sizex*1,$thick,$sizez*3,$t,0,180,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-6*$sizex,$h - $sizez*4,$sizex*1,$thick,$sizez*3,$t,0,180);
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+5.5*$sizex,$h - $sizez*3.25,$sizex*1,$thick,$sizez*1.5,$t,0,180,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-5.5*$sizex,$h - $sizez*3.25,$sizex*1,$thick,$sizez*1.5,$t,0,180);
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+3.25*$sizex,$h - $sizez,$sizex*1.5,$thick,$sizez,$t,0,180,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-3.25*$sizex,$h - $sizez,$sizex*1.5,$thick,$sizez,$t,0,180);
  $b->addBrush ($kind,wedge(),
    $x,$y+$w+2.5*$sizex,$h - $sizez*0.5,$sizex*3,$thick,$sizez,$t,0,180,180);
  $b->addBrush ($kind,wedge(),
    $x,$y-$w-2.5*$sizex,$h - $sizez*0.5,$sizex*3,$thick,$sizez,$t,0,180);
  return $b; # round bottom
  }

sub supportBeamSingle
  {
  # create a support beam for using to support a buildings upper part,
  # w (width) is beam thickness
  # try supportBeamSingle( 0,0,0, 2, 12, 16, { texture => $wood} );  
  # named parameters:  texture, facing
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $t = $p->{texture} || -1;
  my $facing = $p->{facing} || aSouth();
  my $b = Cowgen::Bunch->create("supportbeam##num##");
  $b->addBrush (solid(),cube(),$x-$l/2+$w/2,$y,$z,$w,$w,$h,$t);
  $b->addBrush (solid(),wedge(),$x+$w/2,$y,$z+$h/2-$l/2+$w/2,$l-$w,$w,$l-$w,$t,0,180,270);
  $b->addBrush (air()+MNOROOM,wedge(),$x,$y,$z+$h/2-$l/2+$w,$l-$w*2,$w,$l-$w*2,$t,0,180,270);
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # support beam
  }

sub supportBeamDouble
  {
  # create a double support beam (actually two singleSupportBeam()
  # for using to support a buildings upper part,
  # w (width) is beam width, try 0,0,0,2,12,16, spacing = 4
  # named parameters: facing, texture, spacing (between the two parts; defaults to 0)
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $t = $p->{texture} || -1;
  my $facing = $p->{facing} || aSouth(); # keep for later
  $p->{facing} = aSouth(); # disable rotate
  my $s = $p->{spacing} || 0;
  my $l1 = ($l-$s)/2;
  my $b = supportBeamSingle($x-$l/2+$l1/2,$y,$z,$w,$l1,$h,$p); 
  $p->{facing} = flipFacing($facing);
  $b->merge( supportBeamSingle($x+$l/2-$l1/2,$y,$z,$w,$l1,$h,$p) );
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # support beam
  }

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

sub arch
  {
  # creates an arc by using a solid block and an air circle, aligned on the
  # y axis.
  # named parameters: facing, texture (outer and default), innertexture
  # sides (6..10), align (for cylinder, align_by_sides() or vertices),
  # thick (thickness at top), floortexture, ceilingtexture,
  # spacing (for multiple arches), count (default 1),
  # tunnel (default yes()), light (passed to addLight).
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $b = Cowgen::Bunch->create("arch##num##");
  print "$b\n";
  my $t = $p->{texture} || -1;
  my $ti = $p->{innertexture} || $t;
  my $ft = $p->{floortexture} || $t;
  my $ct = $p->{ceilingtexture} || $t;
  my $c = abs($p->{sides} || 6);  
  my $a = $p->{align} || ALIGN_BY_VERTICES;
  my $facing = $p->{facing} || aSouth(); # keep for later
  my $gr = $p->{gridsize} || 0.25;
  my $s = $p->{spacing} || 0;
  my $k = $p->{count} || 1;

  # jds; lighting and tunnel control
  my $light = $p->{light} || 0;
  my $tunnel = yes();	# default: build the tunnel cube
  $tunnel = $p->{tunnel} if (defined $p->{tunnel});

  my $th;
  $th = snap2Grid($gr, $p->{thick} || 0.20*$h ); # default to 20% of height
  if (0 == $s)
    {
    $l = $l*$k; $k = 1;
    }

  my ($jj,$kk,$nn,$mm);     # loop parameters
  $nn = $s+$l;          # spacing between arch centers
  $kk = $k*$nn;         # loop limit
  $mm = (($k-1)*$nn)/2;     # initial "x" offset

  # carve the cube tunnel
  # jds; tunnel is optional
  my $br;

  if (yes() == $tunnel)
    {
	$br = $b->addBrush (air()+MNOROOM,cube(),$x,$y,$z,$w,$l+2*$mm,$h,$ti);
	$b->setTexture ($br, bottom(), $ft) if ($ft != $ti);
	$b->setTexture ($br, top(), $ct) if ($ct != $ti);
    }

  # build the arch array
  for ($jj = 0; $jj < $kk; $jj += $nn)
     {
      $br = $b->addBrush(solid(),cube(),
                $x-$mm+$jj,
                $y,
                $z+$h/4,
                $w,
                $l,
                $h/2,
                $t);
    }
  # jds; place lights between arches, if any
  if ((0 != $light) && (1 < $k))
    {
	$kk -= $nn/2;
	for ($jj = $nn/2; $jj < $kk; $jj += $nn)
	  {
		$br = $b->addLight(	omni(),		# kind
						$x-$mm+$jj,		# "x"
						$y,			# "y"
						$z+($h-$th)/2,	# "z"
						$light);		# brightness or hash parm
	  }
    }

  # place a cylinder through all   
  $b->setFaces($c);
  $b->addBrush 
    (air()+MNOROOM+$a,cylinder(),$x,$y,$z,$w,$h-2*$th,$l+2*$mm,$ti,
    0,90,0);
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # arch
  } # arch
         
sub dome
  { 
  # creates a dome by carving it out with two air cylinders from a solid
  # the dome will be really only half of its height, the rest will be an
  # empty cube (since there are no half-cylinders in dromed). uses 4 brushes
  # only, inspired by kfgecko
  # named parameters: facing, texture (of dome), walltexture (of the walls below
  # dome and of cylinder walls)
  my ($x,$y,$z,$w,$h,$l,$p) = @_;
  my $b = Cowgen::Bunch->create("dome##num##");
  my $t = $p->{texture} || -1;
  my $wt = $p->{walltexture} || $t;
  my $facing = $p->{facing} || aSouth(); # keep for later
  $b->addBrush (solid()+MROOM,cube(),$x,$y,$z,$w,$l,$h,$t); 
  $b->setFaces(10);
  my $br = $b->addBrush (air()+MNOROOM,cylinder(),$x,$y,$z,$h,$h,$l,$t,90,90,0);
  $b->setTexture($br,top(),$wt); $b->setTexture($br,bottom(),$wt);
  $br = $b->addBrush (air()+MNOROOM,cylinder(),$x,$y,$z,$h,$h,$w,$t,90,90,90);
  $b->setTexture($br,top(),$wt); $b->setTexture($br,bottom(),$wt);
  $b->addBrush (air()+MNOROOM,cube(),$x,$y,$z-$h/4,$w,$l,$h/2,$wt);
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # dome
  }

sub carpet
  { 
  # creates a "carpet" using a solid brush
  # named parameters: texture, facing
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  $p = {} if (!defined $p);
  my $t = $p->{texture} || -1;
  my $facing = $p->{facing} || aSouth();
  my $b = Cowgen::Bunch->create("carpet##num##");
  $b->addBrush (solid(),cube(),$x,$y,$z,$w,$l,$h,$t);
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # carpet
  }

sub putCarpet
  { 
  # creates a "carpet" by placing a solid brush into the floor (this avoids 
  # problems we would get by placing an air brush on top of the floor and 
  # makes the height != 0 case using only 1 brush instead of 2). parameters 
  # are $x,$y,$w,$l,$h,$bunch,$bx,
  # $by,$bh,$bw,$bl,$bh,$p, works only for unrotated aircubes..need we more? 
  # x,y are relatively to the center of the brush (0,0 for center), b? are 
  # the brush dimensions
  # named parameters: texture, thick (thickness of carpet sticking out, 
  # defaults to 0), facing
  my ($x,$y,$w,$l,$h,$b,$bx,$by,$bz,$bw,$bl,$bh,$p) = @_;
  return if (!defined $b);
  my $txt = $p->{texture} || -1; 
  my $thick = $p->{thick} || 0;
  my $facing = $p->{facing} || aSouth();
  my $z = $bz - $bh/2-$h/2+$thick; # floor
  $x = $bx+$x; $y = $by+$y; 
  $b->addBrush (solid(),cube(),$x,$y,$z,$w,$l,$h,$txt);
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # carpet
  }

sub putFrames
  {
  # give dimensions (x,y,z,w,l,h) and place where to put frame (aSouth, 
  # aNorth), the door length (wallthickness), as well as a opening 
  # description hash containing texture,
  # width, height, framethick (for frame) and frametexture, blockwidth and 
  # blocktexture (last two ones for "fake" doors/windows. This creates a 
  # then bunch with doors/windows, or what ever opening you are creating.
  my ($x,$y,$z,$w,$l,$h,$dl,$p) = @_;
  my $place = $p->{place} || aSouth();
  my $dh = $p->{height} || 7;
  my $dw = $p->{width} || 3.5;
  my $n = $p->{name} || "frame##num##";
  my @coords;
  push (@coords, $x+$l/2, $y, $z, aSouth()) if (($place & aSouth()) != 0);
  push (@coords, $x-$l/2, $y, $z, aNorth()) if (($place & aNorth()) != 0);
  push (@coords, $x, $y+$w/2, $z, aEast()) if (($place & aEast()) != 0);
  push (@coords, $x, $y-$w/2, $z, aWest()) if (($place & aWest()) != 0);
  push (@coords, $x, $y, $z+$h/2, aSouth()) if (($place & aTop()) != 0);
  push (@coords, $x, $y, $z-$h/2, aSouth()) if (($place & aBottom()) != 0);
  my $bu = Cowgen::Bunch->create($n);
  my $j = scalar @coords; my $p1 = {};
  foreach (keys %{$p}) { $p1->{$_} = $p->{$_}; }
  my $i;
  for ($i = 0; $i < $j; $i += 4)
    {
    $p1->{facing} = $coords[$i+3];
    $bu->merge ( frame ($coords[$i],$coords[$i+1],$coords[$i+2], $dw, $dl, $dh, $p1) );
    }
  $bu; # openings
  }

sub brokenHole
  { 
  # creates a "broken"-looking, (more or less) irregular-shaped hole 
  # facing south.
  # yin/zin and yout/zout are the length of the spikes in Y/Z axis going in
  # and out from the holes virtual border (which is defined as 
  # yv=w-yaout and zv=h-zaout, and yaout and zaout range from 0..2*yout resp
  # 0..2*zout). The hole has a minimum inner rectangle of size width = yv, 
  # length = l, zv
  # named parameters: win (defaults to 0), hin (defaults to 0), 
  # hout (defaults to h*.25), wout (defaults to w*0.25), facing, texture,
  # gridsize,
  # (textur of inner walls)
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $txt = $p->{texture} || -1; 
  my $facing = $p->{facing} || aSouth();
  my $yout = abs($p->{yout} || $w*0.25);
  my $zout = abs($p->{zout} || $h*0.25);
  my $yin = abs($p->{yin} || 0);
  my $zin = abs($p->{zin} || 0);
  my $grid = $p->{gridsize} || 0.25;
  my $wh = $p->{waterheight} || 0;
  my $b = Cowgen::Bunch->create("brokenHole##num##");
  my @pa = ( 180,0,0,180,0,180, 0,0,180,0,0,0, 90,180,180, 90,180,0, 90,180,0,90,0,0);
  # not ready yet
  # add room
  $b->addRoom ($x,$y,$z,$w,$l,$h);
  # add inner rectangle
  #$b->addBrush (air()+MNOROOM,cube(),$x,$y,$z,$yv,$l,$zv,$txt);
  #$b->addBrush (water(),cube(),$x,$y,$z,$w,$l,$h,$txt);
  $b; # brokenHole
  }

sub basin
  { 
  # creates a basin for a flowerbed/pool whatever
  # named parameters: thick (width of frame around it), texture (outside and 
  # default), facing, innertexture (textur of inner walls), floortexture 
  # (texture of inner floor), floorthick (thickness of floor), waterheight 
  # (0 = disable, otherwise fill with water)
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $otxt = $p->{texture} || -1; 
  my $itxt = $p->{innertexture} || $otxt;
  my $ftxt = $p->{floortexture} || $itxt;
  my $facing = $p->{facing} || aSouth();
  my $thick = $p->{thick}*2 || 1;
  my $fthick = $p->{floorthick} || 1;
  my $wh = $p->{waterheight} || 0;
  my $b = Cowgen::Bunch->create("basin##num##");
  $b->addBrush (solid(),cube(),$x,$y,$z,$w,$l,$h,$otxt);
  my $br = $b->addBrush (MNOROOM+air(),
   cube(),$x,$y,$z+$fthick/2,$w-$thick,$l-$thick,$h-$fthick,$itxt);
  $b->setTexture ($br,bottom(),$ftxt) if ($ftxt != $itxt);
  if ($wh > 0) 
    {
    $br = $b->addBrush (water()+MFLOW,
     cube(),$x,$y,$z-$fthick+$wh/2,$w-$thick,$l-$thick,$wh,$itxt);
    $b->setTexture ($br,bottom(),$ftxt) if ($ftxt != $itxt);
    }
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # basin
  }

sub frame
  { 
  # creates an frame for usage as window/door frame, if blockwidth > 0 no room
  # will be flagged for the air brush, lit is the amount of the omni light that
  # will be used as the light, 0 for disable, if > 0 the blocklittexture is used
  # for the block instead of blocktexture. The light will be always added at the south
  # side (assuming looking from inside), so if your window looks to north, use facing = aNorth() 
  # named parameters: framethick (width of frame), texture, facing, innertexture, floortexture
  # blockthick (width of block to bar frame with), blocktexture, blocklittexture,
  # light, lightdistance, sidetexture (if placing a hollow frame into wall, this will create 
  # the illusion of outside when using sky()), oneside (create only inner side when putting
  # a block
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $txt = $p->{texture} || -1;
  my $facing = $p->{facing} || aSouth();
  my $thick = $p->{framethick} || 0.5;
  my $ft = $p->{floortexture} || $txt;
  my $it = $p->{innertexture} || $txt;
  my $st = $p->{sidetexture} || $it;
  my $b = Cowgen::Bunch->create("frame##num##");
  $b->addBrush (solid(),cube(),$x,$y,$z,$w,$l,$h,$txt);
  my $bw = $p->{blockthick} || 0;
  my $bt = $p->{blocktexture} || $txt;
  my $blt = $p->{blocklittexture} || $bt;
  my $li = $p->{light} || 0;
  $bt = $blt if ($li > 0);
  my $ld = $p->{lightdistance} || 0.125; $ld = 0.125 if ($ld < 0.125);
  my $os = $p->{onesided} || 0;
  my $type = air(); $type += MNOROOM if ($bw > 0);
  $thick = 0 if (($w-$thick*2 < 0) || ($h-$thick*2 < 0) );
  my $br;
  if ($os > 0)
    {
    $br = $b->addBrush ($type,cube(),$x-($l+$bw)/4,$y,$z,$w-$thick*2,($l-$bw)/2,$h-$thick*2,$it);
    $b->setTexture($br,north(),$bt) if ($bt != $it);
    }
  else
    {
    $br = $b->addBrush ($type,cube(),$x,$y,$z,$w-$thick*2,$l,$h-$thick*2,$it); 
    if ($st != $it)
      {
      $b->setTexture($br,south(),$st); $b->setTexture($br,north(),$st);
      }
    }
  floodBrush($x,$y,$z,$w,$l,$h,$p->{flood},$b,$txt,$ft);
  $b->setTexture ($br, bottom(), $ft) if ($ft != $txt);
  if (($bw > 0) && ($os == 0))
    {
    $br = $b->addBrush (solid(),cube(),$x,$y,$z,$w-$thick*2,$bw,$h-$thick*2,$bt);
    } # endif bw > 0
  $b->addLight (omni(),$x+$bw/2+$ld,$y,$z,$li,0,1) if ($li > 0);
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # frame
  }

sub portal
  { 
  # creates an portal (like arch) for usage as clobbering up walls in room
  # first create a solid, then "cut-in" the arch
  # named parameters: texture (outside; default), facing, floortexture, ceilingtexture,
  # sidetexture (if placing a hollow portal into wall, this will create 
  # the illusion of outside when using sky()), 
  # innertexture, thick (thickness of frame around), sides (6..10),
  # archheight (relativ ro absolute)
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $txt = $p->{texture} || -1;
  my $facing = $p->{facing} || aSouth();
  my $ft = $p->{floortexture} || $txt;
  my $ct = $p->{ceilingtexture} || $txt;
  my $st = $p->{sidetexture} || $txt;
  my $it = $p->{innertexture} || $txt;
  my $grid = $p->{gridsize} || 0.25;
  my $ah = rel2abs($p->{archheight},$h);  # height of upper arch
  my $sides = $p->{sides} || 10;  # faces for cylinder
  my $thick = $p->{thick} || 1;
  my $b = Cowgen::Bunch->create("portal##num##");
  my $hc = snap2Grid($grid,$h - $ah);
  # outer solid
  my $br = $b->addBrush (solid(),cube(),$x,$y,$z-$h/2+$hc/2,$w,$l,$hc,$txt);
  $b->setFaces(10);
  $b->addBrush (solid(),cylinder(),$x,$y,$z+$h/2-$ah,$w,$ah*2,$l,$txt,0,90,0);
  # inner air
  $w = $w-$thick*2; 
  $br = $b->addBrush (air(),cube(),$x,$y,$z-$h/2+$hc/2+$thick/2,$w,$l,$hc-$thick,$it);
  $b->setTexture ($br,north(),$st) if ($st != $txt);  
  $b->setTexture ($br,south(),$st) if ($st != $txt);  
  $b->setTexture($br,bottom(),$ft) if ($ft != $txt);
  $b->setTexture($br,top(),$ct) if ($ct != $txt);
  $b->setFaces(10);
  $br = $b->addBrush (air(),cylinder(),$x,$y,$z+$h/2-$ah,$w,$ah*2-$thick*2,$l,$it,0,90,0);
  $b->setTexture ($br,top(),$st) if ($st != $txt);  
  $b->setTexture ($br,bottom(),$st) if ($st != $txt);  
  floodBrush($x,$y,$z,$w,$l,$h,$p->{flood},$b,$txt,$ft);
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # portal
  }

sub floodBrush
  {
  # take bunch and add a water flood to it if defined room->{flood} 
  # parameters: x,y,z,w,l,h, waterline (absolut), bunch_to_add_to, texture, 
  # floortexture
  my ($x,$y,$z,$w,$l,$h,$f,$b,$t,$ft) = @_;
  if ((defined $f) && ($f > ($z - $h/2)))
    {
    my $f = min($f,$z+$h/2); # make upper bound
    $f = $f - ($z-$h/2);
    my $br = $b->addBrush (flood(),cube(),$x,$y,$z-$h/2+$f/2,$w,$l,$f,$t);
    $b->setTexture($br,bottom(),$ft);
    }
  }

sub grid
  { 
  # creates a grid (or cross) for usage as window/door cross
  # barsx,barsy = 1,1 for cross
  # named parameters: barsx, barsz, thick, texture, facing (aSouth/aNorth etc)
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $barsy = $p->{barsy}; $barsy = 1 if (!defined $barsy);
  my $barsz = $p->{barsz}; $barsz = 1 if (!defined $barsz);
  $barsy = abs($barsy); $barsz = abs($barsz);
  my $txt = $p->{texture} || -1;
  my $thick = $p->{thick} || 1;
  my $facing = $p->{facing} || aSouth();
  my $b = Cowgen::Bunch->create("grid##num##");
  my ($ya,$za,$yy,$zz,$dxa,$dya,$dza,$dw,$dl);
  my $airy = ($w-$thick*$barsy)/($barsy+1);
  my $airz = ($h-$thick*$barsz)/($barsz+1);
  $ya = $y - $w/2 + $airy + $thick / 2;
  for ($yy = 0; $yy < $barsy; $yy++)
    { 
    $b->addBrush (solid(),cube(),$x,$ya,$z,$thick,$thick,$h,$txt);
    $ya += $airy+$thick;
    }  
  $za = $z - $h/2 + $airz + $thick / 2;
  for ($zz = 0; $zz < $barsz; $zz++)
    {
    $b->addBrush (solid(),cube(),$x,$y,$za,$w,$thick,$thick,$txt);
    $za += $airz+$thick;
    }
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # grid
  }

sub frameGrid
  {
  # creates an frame with a grid inside
  # named parameters: framethick, gridthick, frametexture,
  # facing, gridtexture, barsx, barsz
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $thick = $p->{framethick} || 1;
  my $fp = { thick => $thick, texture => $p->{frametexture},
          facing => $p->{facing} 
        };
  my $b = frame ($x,$y,$z,$w,$l,$h,$fp);
  $fp->{texture} = $p->{gridtexture};
  $fp->{thick} = $p->{gridthick};
  $fp->{barsx} = $p->{barsx}; $fp->{barsz} = $p->{barsz};
  $b->merge( grid ($x,$y,$z,$w-$thick,$l,$h-$thick,$fp) );
  return $b; # framegrid
  }

sub smallWall
  {
  # creates a small wall with columns, using wedges for colum tops
  # x,y,z = center, l,w,h = dimensions, $p - reference to hash
  # numcolumns = number of columns to add (evenly spaced), wallheight is
  # the height of the wall, and headheight the height of the top of
  # the column, walltext the texture for the wall, coltext for columns
  # wallwidth
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $facing = $p->{facing} || aSouth();
  my $b = Cowgen::Bunch->create("smallwall##num##");
  $b->addBrush (solid(),cube(),$x,$y,$z-$h/2+$p->{wallheight}/2,
    $w,$p->{wallwidth},$p->{wallheight},$p->{walltext});
  $p->{numcolumns} = 0 if (!defined $p->{numcolumns});
  my $yi = $y-$w/2+$p->{columnwidth}/2;
  $p->{texture} = $p->{coltext} || -1;
  if ($p->{numcolumns} > 0) 
    {
    # add first
    $b->merge ( column ($x,$yi,$z,$p->{columnwidth},$l,$h, $p ) );
    }
  if ($p->{numcolumns} > 1)
    {
    my $t = ($w - $p->{columnwidth}) / ($p->{numcolumns}-1);
    $t = 1 if ($t < 1);
    do
      {
      $yi += $t;
      $b->merge ( column ($x,$yi,$z,$p->{columnwidth},$l,$h, $p ) );
      } while ($yi+$t < ($y+$w/2));
    }
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # small walll
  }

sub column
  { 
  # creates a column.
  # x,y,z - center, w,h,l, dimensions
  # named parametrs: headheight (height of top spike - 0 for none),
  # texture
  my $b = Cowgen::Bunch->create("column##num##");
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $facing = $p->{facing} || aSouth();
  my $t = $p->{texture} || -1;
  $p->{headheight} = 0 if(!defined $p->{headheight});
  $b->addBrush (solid(),cube(),$x,$y,$z-$p->{headheight}/2,$w,$l,$h-$p->{headheight},$t);
  if ($p->{headheight} > 0)
    {
    $b->addBrush (solid()+align_by_sides(),pyramid()+foursided(),
      $x,$y,$z+$h/2-$p->{headheight}/2,
      $w,$l,$p->{headheight},$t);
    }
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # column
  }

sub supportColumn
  { 
  # creates a support column (for breaking up large, boring walls :).
  # x,y,z - center, w,h,l, dimensions
  # named parameters: headheight (height of top spike - 0 for none,
  # baseheight (height of running bend around base of column - 0
  # for none; not including top wedge),
  # basetopheight (height of top wedge around base),
  # basewidth (width of running bend around base of column),
  # coltext (col texture), basetext, facing,
  # collength (length of column - rest will be wedge)
  my $b = Cowgen::Bunch->create("supportColumn##num##");
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $facing = $p->{facing} || aSouth();
  $p->{headheight} = 0 if(!defined $p->{headheight});
  $p->{baseheight} = 0 if(!defined $p->{baseheight});
  $p->{basewidth} = 0 if ($p->{baseheight} == 0);
  $p->{collength} = $l if ((!defined $p->{collength}) || ($p->{collength} > $l));
  my $r = $l - $p->{collength} - $p->{basewidth};
  # base around column
  if ($p->{baseheight} > 0) 
    {
    $b->addBrush (solid(),cube(),$x,$y,$z-$h/2+$p->{baseheight}/2,
     $w,$l,$p->{baseheight},$p->{basetext});
    $b->addBrush (solid(),wedge(),$x,$y-$w/2+$p->{basewidth}/2,$z-$h/2+$p->{baseheight}+$p->{basetopheight}/2,
     $p->{basewidth},$l,$p->{basetopheight},$p->{basetext},0,0,180);
    $b->addBrush (solid(),wedge(),$x,$y+$w/2-$p->{basewidth}/2,$z-$h/2+$p->{baseheight}+$p->{basetopheight}/2,
     $p->{basewidth},$l,$p->{basetopheight},$p->{basetext},0,0,0);
    $b->addBrush (solid(),wedge(),$x+$l/2-$p->{basewidth}/2,$y,$z-$h/2+$p->{baseheight}+$p->{basetopheight}/2,
     $p->{basewidth},$w-$p->{basewidth}*2,$p->{basetopheight},$p->{basetext},0,0,270);
    $b->addBrush (air()+MNOROOM,wedge(),$x+$l/2-$p->{basewidth}/2,$y,$z-$h/2+$p->{baseheight}+$p->{basetopheight}/2,
     $p->{basewidth},$w,$p->{basetopheight},$p->{basetext},0,180,90);
    }
  #column
  my $bh = $h-$p->{baseheight} - $p->{basetopheight}-$p->{headheight};
  $b->addBrush (solid(),cube(),$x-$l/2+$p->{collength}/2,$y, $z+$h/2-$p->{headheight}-$bh/2,
    $w-$p->{basewidth}*2,$p->{collength},$bh,$p->{coltext});
  if ($r > 0)
    {
    $b->addBrush (solid(),wedge(),$x+$l/2-$r/2-$p->{basewidth},$y,$z+$h/2-$p->{headheight}-$bh/2,
     $r,$w-$p->{basewidth}*2,$bh,$p->{coltext},0,0,270);
    }
  if ($p->{headheight} > 0)
    {
    $b->addBrush (solid(),wedge(),$x-$l/2+$p->{collength}/2,$y,$z+$h/2-$p->{headheight}/2,
     $p->{collength},$w-$p->{basewidth}*2,$p->{headheight},$p->{coltext},0,0,270);
    }
  $b->rotateByFacing($x,$y,$z,$facing);
  return $b; # supportColumn
  }

sub outerBend
  {
  # $sa = startangle, $ea = endangle, $ea > $sa
  # $gridsize = 1, 0.5, 0.25 etc, $t = texture,
  # $steps is the steps for a full circle
  my ($x,$y,$z,$radius,$h,$sa,$ea,$steps,$flags,$gridsize,$t,$t2) = @_;
  my ($b,$a1,$a2,$w,$l,$a1o,$a2o,@p1,@p2,$w1,$l1);

  $b = Cowgen::Bunch->create("outerbend##num##"); 
  $b->addBrush (air()+MROOM,cube(), $x,$y,$z,$radius*2,$radius*2,$h, $t2) 
   if (($flags & basroom()) != 0);
  $sa += 360 while ($sa < 0);
  $sa -= 360 while ($sa > 360);
  $ea += 360 while ($ea < 0);
  $steps = 10 if ((!defined $steps) || ($steps == 0));
  $steps = 360 / $steps;
  for ($a1o = $sa; $a1o < $ea; $a1o += $steps)
    {
    $a2o = $a1o+$steps; $a2o = $ea if ($a2o > $ea);
    $a1 = $a1o; $a2 = $a2o; $a2 -= 360 while ($a2 > 360); $a1 -= 360 while ($a1 > 360);
    # make wedge from a1 -> a2
    @p1 = snap2Grid( rotatePoint ($x,$y+$radius,$z,$x,$y,$z,0,0,$a1) , $gridsize);
    @p2 = snap2Grid( rotatePoint ($x,$y+$radius,$z,$x,$y,$z,0,0,$a2) , $gridsize);
    $w = $p2[0] - $p1[0]; $l = $p2[1] - $p1[1];
    $w1 = $w; $l1 = $l; 
    $a = 270; 
    if (($w > 0) && ($l < 0))
#   if (($a1 >= 90) && ($a1 < 180))
      {
      $a = $w; $w = $l; $l = $a; $a = 0;
      }
     if (($w > 0) && ($l > 0))
#   if (($a1 >= 180) && ($a1 < 270))
      {
      $a = 90;
      }
    if ($a1 >= 270)
      {
      $a = $w; $w = $l; $l = $a; $a = 180;
      }
    if (($w == 0) || ($l == 0))
      {
      if ($w1 == 0)
        {
        $w1 = $gridsize; 
        $w1 = -$w1 if ( $p1[0]+$w1/2-$x < $radius );
        }
      if ($l1 == 0)
        {
        $l1 = $gridsize; 
        $l1 = -$l1 if ( $p1[1]+$l1/2-$y < $radius );
        }
      $b->addBrush(solid(),cube(),$p1[0]+$w1/2,$p1[1]+$l1/2,$z,$l1,$w1,$h,$t);
      }
    else
      {
      $b->addBrush(solid(),wedge(),$w1/2+$p1[0],$l1/2+$p1[1],$z,$w,$h,$l,$t,0,90,$a);
      }
    }
  return $b; # bend
  }

sub innerBend
  {
  # $sa = startangle, $ea = endangle, $ea > $sa
  # $gridsize = 1, 0.5, 0.25 etc, $t = texture,
  # $steps is the steps for a full circle
  my ($x,$y,$z,$radius,$h,$sa,$ea,$steps,$flags,$gridsize,$t,$t2) = @_;
  my ($b,$a1,$a2,$w,$l,$a1o,$a2o,@p1,@p2);

  $b = Cowgen::Bunch->create("innerbend##num##"); 
  $b->addBrush (air()+MROOM,cube(), $x,$y,$z,$radius*2,$radius*2,$h, $t2) 
   if (($flags & basroom()) != 0);
  $sa += 360 while ($sa < 0);
  $sa -= 360 while ($sa > 360);
  $ea += 360 while ($ea < 0);
  $steps = 10 if ((!defined $steps) || ($steps == 0));
  $steps = 360 / $steps;
  my ($w1,$l1);
  for ($a1o = $sa; $a1o < $ea; $a1o += $steps)
    {
    $a2o = $a1o+$steps; $a2o = $ea if ($a2o > $ea);
    $a1 = $a1o; $a2 = $a2o; $a2 -= 360 while ($a2 > 360); $a1 -= 360 while ($a1 > 360);
    # make wedge from a1 -> a2
    @p1 = snap2Grid( rotatePoint ($x,$y+$radius,$z,$x,$y,$z,0,0,$a1) , $gridsize);
    @p2 = snap2Grid( rotatePoint ($x,$y+$radius,$z,$x,$y,$z,0,0,$a2) , $gridsize);
    $w = $p2[0] - $p1[0]; $l = $p2[1] - $p1[1];
    $w1 = $w; $l1 = $l; 
    $a = 90; 
    if (($w > 0) && ($l < 0))
      {
      $a = $w; $w = $l; $l = $a; $a = 180;
      }
    if (($w > 0) && ($l > 0))
      {
      $a = 270;
      }
    if ($a1 >= 270)
      {
      $a = $w; $w = $l; $l = $a; $a = 0;
      }
    if (($w == 0) || ($l == 0))
      {
      if ($w1 == 0)
        {
        $w1 = $gridsize; 
        $w1 = -$w1 if ( $p1[0]+$w1/2-$x > $radius );
        }
      if ($l1 == 0)
        {
        $l1 = $gridsize; 
        $l1 = -$l1 if ( $p1[1]+$l1/2-$y > $radius );
        }
      $b->addBrush(solid(),cube(),$p1[0]+$w1/2,$p1[1]+$l1/2,$z,$l1,$w1,$h,$t);
      }
    else
      {
      $b->addBrush(solid(),wedge(),$w1/2+$p1[0],$l1/2+$p1[1],$z,$w,$h,$l,$t,0,90,$a);
      }
    }
  return $b; # bend
  }

sub putLight
  {
  # generate a light at absolute position
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  $p = {} if (!defined $p);
  my $b = Cowgen::Bunch->create("light##num##");
  my $li = $p->{light} || 128; my $hu = $p->{h} || 0;
  my $sa = $p->{s} || 1.0;
  $b->addLight (omni(),$x,$y,$z,$li,$hu,$sa);
  return $b;
  }

sub breakBlock
  {
  # take a solid cube and break it into pieces (not ready yet)
  # $gridsize = 1, 0.5, 0.25 etc
  my ($bunch,$flags,$gridsize) = @_;
  my ($b,$br,@d,@t,$x1,$y1);

  $b = Cowgen::Bunch->create("broken##num##"); 
  my $id = $bunch->firstBrush(solid(),cube());

  @d = $bunch->brushDimensions ( $id );
  @t = $bunch->brushTextures ($id );
#  print "$t[0] $t[1] $t[2] $t[3] $t[4] $t[5]\n";
#  print "$d[0] $d[1] $d[2]  $d[3] $d[4] $d[5]\n";
  return $b if ($d[6] == 0);
  my ($w,$l,$x2,$y2,$px,$py,$pz);
  $x1 = $d[0]-$d[3]/2; $x2 = $d[0]+$d[3]/2;
  $y1 = $d[1]-$d[4]/2; $y2 = $d[1]+$d[4]/2;
  
  # create random split points
  $w = rand($d[3]*0.6)+$d[3]*0.2; 
  $l = rand($d[4]*0.5)+$d[4]*0.1; 
  print "px rand: $px  py rand: $py\n";
  ($w,$l,$pz) = snap2Grid($w,$l,$d[2],$gridsize);
  print "w: $w  l: $l\n";
  $px = $x1+$l/2; $py = $y1+$w/2; # center
  print "$px $py \n";

  $b->addBrush (air()+MNOROOM,wedge(), $px,$py,$pz, $w, $d[5], $l, $t[0], 0,90,0); 
  $b->addBrush (solid(),wedge(), $px,$py,$pz-2, $w, $d[5], $l, $t[4], 0,90,0,
    $t[1],$t[0],$t[1],$t[5],$t[4]); 
 
  $w = rand($d[3]*0.5)+$d[3]*0.1; 
  $l = rand($d[4]*0.6)+$d[4]*0.2; 
#  print "px rand: $px  py rand: $py\n";
  ($w,$l,$pz) = snap2Grid($w,$l,$d[2],$gridsize);
 # print "w: $w  l: $l\n";
  $px = $x2-$l/2; $py = $y2-$w/2; # center
#  print "$px $py \n";

  $b->addBrush (air()+MNOROOM,wedge(), $px,$py,$pz, $w, $d[5], $l, $t[0], 0,90,180); 
  $b->addBrush (solid(),wedge(), $px,$py,$pz-2, $w, $d[5], $l, $t[4], 0,90,180,
    $t[1],$t[0],$t[1],$t[5],$t[4]); 

  return $b;
  }

sub wedgeWall
  {
  # create a 45 degree wall by using two wedges (solid and air) and
  # honour wallthick (wall inner and outer line are plane). Defaults to
  # facing southwest with the outer wall
  # named parameters: texture, innertexture (defaults to texture),
  # wallthick (defaults to 0.5), facing, gridsize
  my $b = Cowgen::Bunch->create("wedgeWall##num##");
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $facing = $p->{facing} || aSouth();
  my $t = $p->{texture} || -1;
  my $ti = $p->{innertexture} || $t;
  my $wt = $p->{wallthick} || 0.5;
  my $grid = $p->{gridsize} || 0.25;
  $b->addBrush (solid(),wedge(),$x,$y,$z,$w,$l,$h,$t,0,0,0);
  my $x1 = snapLength($grid,$wt*$w/$l);
  $b->addBrush (air()+MNOROOM,wedge(),$x-$x1/2,$y+$wt/2,$z,
   $w-$wt,$l-$x1,$h,$t,0,0,0);
  $b->rotateByFacing($x,$y,$z,$facing);
  $b; # wedgeWall
  }

sub balcony
  {
  # create a balcony
  # named parameters: texture, innertexture,
  # facing, gridsize, framethick (wall thickness), floorthick (default framethick),
  my $b = Cowgen::Bunch->create("balcony##num##");
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $facing = $p->{facing} || aSouth();
  my $txt = $p->{texture} || -1;
  my $itxt = $p->{innertexture} || $txt;
  my $thick = rel2abs($p->{framethick},$l) || 0.5;
  my $fthick = rel2abs($p->{floorthick},$l) || $thick;
  my $grid = $p->{gridsize} || 0.25;
  $b->addBrush (solid(),cube(),$x,$y,$z,$w,$l,$h,$txt);
  $b->addBrush (air()+MNOROOM,cube(),$x-$thick/2,$y,$z+$fthick/2,
    $w-$thick*2,$l-$thick,$h-$fthick,$itxt);
  $b->rotateByFacing($x,$y,$z,$facing);
  $b; # balcony
  }

sub brush
  {
  # wrapper for bunch::brush
  # named parameters: centerx, centery, centerw, centerl
  my $b = Cowgen::Bunch->create("brush##num##");
  my ($x,$y,$z,$w,$l,$h,$p) = @_;
  my $e = copyHash($p);
  my $facing = $e->{facing} || aSouth();
  $e->{kind} = $p->{brkind};  $e->{type} = $p->{brtype};
  $b->brush ($x,$y,$z,$w,$l,$h,$e);
  $b->rotateByFacing($x,$y,$z,$facing);
  $b; # brush
  }

1;

