#!/usr/bin/perl -w

# Contains som eusefull helper functions and merely some constants for
# the specific use with Thief, maybe later on I add more games.

# usage:

# use Dromed;
#
# setStyle("thief"); # or setStyle (thief()); it is also the default
# print "A solid cube would be: type: ",cube()," kind: ",solid(),"\n";

# (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/27/99] v0.0.1 - from scratch
# [08/28/99] v0.0.2 - completed
# [09/10/99] v0.0.3 - did I say "completed"? Added kind2Name and type2Name
# [09/13/99] v0.0.4 - added: flow and room
# [09/22/99] v0.0.7 - added: prisma
#                     fixed: brush2num
# [10/04/99] v0.0.8 - added: optimizeSky, xxxSided from Bunch.pm
# [10/23/99] v0.0.9 - added: area(), removed readTemplate
# 1999-11-06 v0.1.0 - added: eax stuff
#                            snap2Grid, rotatePoint, clipAngle, distance
#                            (from Bunch)
#                            snapLength, true/false/on/off/yes/no,
#                            turnbyFacingCw
#                            genCubesByFacing
#                     fixed: snap2Grid,snapLength work on arbitrary long lists
#                            pyramidfaces (jds)
# 1999-11-10 v0.1.1 - fixed: rotations like in DromEd
#                     added: sky(), flipFacing, max, min, range, omni, spot
#                            light2Name, round
# 1999-12-01 v0.1.2 - fixed: optimizeSky uses sky(), copyHash
# 1999-12-01 v0.1.2 - fixed: belt,hand,genericContents
# 2000-01-09 v0.1.3 - added: rel2abs, unreal, most o_ and l_ constants
#                            printHash (for debug)
#                     fixed: snap2Grid/snap2Length in scalar context
# 2000-01-31 v0.1.4 - added: o_obb etc, o_slay, o_humanoid etc
#                     fixed: clipAngle, PI constants
# 2000-02-13 v0.1.5 - fixed: wantarry for turnbyfacing

package Cowgen::Dromed;
require Exporter;
@ISA    = qw(Exporter);
@EXPORT = qw(
             solid air water flood evaporate water2air solid2air air2solid
             water2solid solid2water
             omni light
             blockable flow room object
             cube wedge cylinder pyramid apexpyramid dodecahedron prisma
             threesided foursided fivesided sixsided sevensided eightsided
             ninesided
             tensided
             jorge thief sshock2
             kind2Name type2Name light2Name
             optimizeSky startingPoint
             eax_generic eax_paddedCell eax_room eax_bathRoom eax_livingRoom
             eax_stoneRoom eax_auditorium eax_concertHall eax_cave eax_arena
             eax_hangar eax_carpetedHallway eax_hallway eax_stoneCorridor
             eax_alley eax_forest eax_city
             eax_mountains eax_quarry eax_plain eax_parkingLot eax_sewerPipe
             eax_underWater
             eax_drugged eax_dizzy eax_psychotic
             north west east south bottom top defaultFace
             side1 side2 side3 side4 side5 side6 side7 side8 side9
             rotatePoint snap2Grid snapLength distance clipAngle
             turnByFacingCcw genCubesByFacing flipFacing
             yes no true false on off sky
             max min range round copyHash rel2abs printHash
             aNorth aWest aEast aSouth aBottom aTop aCenterX aCenterY aCenterZ

             l_hand l_belt l_generic_contents

             l_object l_vhot l_joint l_submodel

             l_head l_heack l_abdomen l_butt l_left_shoulder l_right_shoulder
             l_left_elbow l_right_elbow l_left_wrist l_right_wrist
             l_left_fingers
             l_right_fingers l_left_hip l_right_hip l_left_knee l_right_knee
             l_left_toe l_right_toe l_tail

             l_none l_very_low l_low l_normal l_moderate l_high l_very_high
             l_absolute

             l_level_low l_level_medium l_level_high

             l_seen l_heard l_can_raycast l_have_los l_blind l_deaf l_highest
             l_firsthand

             l_stopped l_very_slow l_slow l_fast l_very_fast

             l_line_of_sight l_raycast

             l_nothing l_script_message l_play_sound_motion l_alert
             l_become_hostile l_enable_investigate l_goto_object l_frob_object
             l_wait l_mprint l_add_remove_metaprop l_add_link l_remove_link
             l_face l_signal l_dest_script_message

             l_do_inv l_no_world l_do_toolsrc l_no_tooldst l_allow_direct

             o_bounce o_destroy_on_impact o_slay_on_impact o_no_collision_sound
             o_no_result o_full_collision_sound

             o_block_pathfind o_repel

             o_normal o_not_rendered o_unlit o_editor_only

             o_junk o_item o_weapon

             o_flip o_slide o_random o_min_bright o_max_bright o_zero_bright
             o_smoothly_brighten o_smoothly_dim o_coherent o_flicker

             o_on o_reverse o_resync o_go_edge o_lap_one

             o_environmental o_no_sharp_curve o_turned_off o_one_shot
             o_music o_sync o_nofade o_destroy_obj o_auto_off

             o_obb o_sphere o_sphere_hat

             o_no_effect o_terminate o_destroy

             o_humanoid o_playerarm o_playerbowarm o_burrick o_spider o_bugbeast
             o_crayman o_constantine o_apparition o_sweel o_rope o_zombie
             o_smallspider o_frog o_cutty

             o_tweq_destroy_obj o_remove_prop o_stop_tweq o_continue o_slay_obj

             o_no_limit o_sim o_wrap o_one_bounce o_sim_small_rad o_sim_large_rad
             o_off_screen

             o_jitter_low o_jitter_high o_mul

             o_anchor o_scripts o_tweq_random o_grav o_zero_vel o_tellai o_pushout
             o_negative_velocity o_relative_velocity o_nophysics o_anchorvhot
            );
#@EXPORT_OK = qw();

use Cowgen::Vbr;
use strict 'vars';

use constant PI => 4 * atan2 1,1; # was 2*3.141592654;
use constant PI2 => 2*PI;
use constant PI2_360 => PI2/360;

use constant THIEF => 1;
use constant SSHOCK2 => 2;
use constant UNREAL => 3;

sub snap2Grid
  {
  # "snap" point(s) to grid, gridsize is 0.5, 1 etc (not 15,16 like in DromEd)
  # parameter: gridsize, list of coordinates to snap
  my ($gridsize,@p) = @_;
  $gridsize = 0.25 if ((!defined $gridsize) || ($gridsize == 0));
  my $s;
  foreach (@p)
    {
    $s = 0.5; $s = -0.5 if ($_ < 0);
    $_ = int( $_ / $gridsize + $s)*$gridsize;
    }
  return wantarray ? @p : $p[0]; # return as list or single value
  }

sub snapLength
  {
  # "snap" length(s) to the twice the gridsize,
  # gridsize is 0.5, 1 etc (not 15,16 like in DromEd)
  # parameter: gridsize, list of lengths to snap
  my ($gridsize,@l) = @_;
  $gridsize = 0.25 if ((!defined $gridsize) || ($gridsize == 0));
  $gridsize = $gridsize*2;
  my $s;
  foreach (@l)
    {
    $s = 0.5; $s = -0.5 if ($_ < 0);
    $_ = int( $_ / $gridsize + $s)*$gridsize;
    }
  return wantarray ? @l : $l[0]; # return as list or single value
  }

sub max
  {
  # return maximum element
  my $max = -1000000;
  my $m;
  foreach $m (@_) { $max = $m if ($m > $max); }
  return $max;
  }

sub min
  {
  # return minimum element
  my $min = 1000000;
  my $m;
  foreach $m (@_) { $min = $m if ($m < $min); }
  return $min;
  }

sub range
  {
  # return element that it is in range
  my ($e,$min,$max) = @_;
  $e = $min if ($e < $min);
  $e = $max if ($e > $max);
  return ($e);
  }

sub round
  {
  my ($v,$r) = @_;
  return int(($r*$v)+0.50)/$r if ($v > 0);
  return int(($r*$v)-0.50)/$r;
  }

sub rel2abs
  {
  # Take a relative value (-1 <= val < 0) and return it as an absolute
  # value based on size. Output is clipped to be between 0 and size. value
  # can be undefined and will be assumed to be 0.
  # Examples: -0.5,9 = 4.5; 0,9 = 0; 4,9 = 4; 10,9 = 9; -2,9 = 9; etc
  # Parameters: relativecoord, maxsize
  my $rel = shift || return 0;
  my $size = shift;
  return range($rel,0,$size) if ($rel >= 0);
  return range(-$rel*$size,0,$size);
  }

sub distance
  {
  # calculate disctance betweentwo 3D points (w/o sqr for speedup)
  # parameter: x,y,z, x1,y1,z2
  my ($x1,$y1,$z1,$x2,$y2,$z2) = @_;
  my ($x,$y,$z);

  $x = ($x2-$x1); $x = $x*$x; $y = ($y2-$y1); $y = $y*$y;
  $z = ($z2-$z1); $z = $z*$z;
  return ($z+$x+$y);
  }

sub clipAngle
  {
  # correct angle values of list to be between 0..360
  # usage: clipAngle(@list) or clipAngle($a1,$a2,$a3,$a4, aso)
  foreach (@_) { $_ += 360 while ($_ < 0); $_ -= 360 while ($_ >= 360); }
  }

sub rotatePoint
  {
  # rotate a point in 3D space
  # parameter: x (point to rotate),y,z, px (pivot point), py, pz, ax (angle), ay, az
  my ($x,$y,$z,$px,$py,$pz,$ax,$ay,$az) = @_;
  clipAngle($ax,$ay,$az);
  my ($x2,$y2,$z2);

  $ax = $ax*PI2_360; $ay = $ay*PI2_360; $az = $az*PI2_360;

  my $caz = cos($az); my $saz = sin($az);
  my $cax = cos($ax); my $sax = sin($ax);
  my $cay = cos($ay); my $say = sin($ay);

  $x -= $px; $y -= $py; $z -= $pz;
  $y2 = $y * $cax - $z * $sax;
  $z = $y * $sax + $z * $cax;
  $y = $y2;

  $x2 = $x * $cay + $z * $say;
  $z = -$x * $say + $z * $cay;
  $x = $x2;

  $x2 = $x * $caz - $y * $saz;
  $y = $x * $saz + $y * $caz;

  return ($x2+$px,$y+$py,$z+$pz);
  }

# rx = x
# ry = y * cos(a) - z * sin(a)
# rz = y * sin(a) + z * cos(a)
#
# rx = x * cos(a) + z * sin(a)
# ry = y
# rz = -x * sin(a) + z * cos(a)
#
# rx = x * cos(a) - y * sin(a)
# ry = x * sin(a) + y * cos(a)
# rz = z

sub turnByFacingCcw
  {
  # turn all angles in list counter-clockwise (like in DromEd) by the
  # facing (aSouth=0,aEast=90,ANorth=180,aWest=270)
  my ($facing,@r) = @_;
  foreach $a (@r)
    {
    $a += 90 if ($facing == aEast());
    $a += 180 if ($facing == aNorth());
    $a += 270 if ($facing == aWest());
    $a -= 360 if ($a >= 360);
    }
  return wantarray ? @r : $r[0]; # return as list or single value
  }

sub genCubesByFacing
  {
  # rotate a cube (facing aSouth) around a given pivot center by taking a
  # bitfield which
  # defines aSouth, aNorth etc and return as a list the coordinates of new
  # cube center
  # and dimensions (e.g. x,y,z,w,h,l,x1,y1,z1,w1,h1,l1,x2 ...).
  # use is depricated since $bunch->rotate and rotatebyfacing will do a better job
  # paramater: pivotx,pivoty,pivotz,cubew,cubel,cubeh, facingfield
  my ($px,$py,$pz,$x,$y,$z,$w,$h,$l, $facing) = @_;
  my @p;
  if (($facing & aSouth()) != 0)
    {
    push (@p, $x,$y,$z,$w,$l,$h, aSouth());
    }
  if (($facing & aEast()) != 0)
    {
    push (@p, rotatePoint ($x,$y,$z,$px,$py,$pz,90,0,0) );
    push (@p, $l,$w,$h, aEast() );
    }
  if (($facing & aNorth()) != 0)
    {
    push (@p, rotatePoint ($x,$y,$z,$px,$py,$pz,180,0,0) );
    push (@p, $w,$l,$h, aNorth() );
    }
  if (($facing & aWest()) != 0)
    {
    push (@p, rotatePoint ($x,$y,$z,$px,$py,$pz,270,0,0) );
    push (@p, $l,$w,$h, aWest() );
    }
  return @p;
  }

sub flipFacing
  {
  # take facing aSouth(), etc and flip it (aSouth <=> aNorth, aWest <=> aEst)
  my $facing = shift || aSouth();
  return aNorth() if ($facing == aSouth());
  return aSouth() if ($facing == aNorth());
  return aWest() if ($facing == aEast());
  return aEast() if ($facing == aWest());
  }

sub sky
  {
  # return 249 (nr of sky texture)
  249;
  }

sub no { 0; }
sub yes { 1; }
sub off { 0; }
sub on { 1; }
sub false { 0; }
sub true { 1; }

sub jorge { return 65535; } # actually -1
sub cube { 0; }
sub cylinder { 1; }
sub pyramid { 2; }
sub apexpyramid { 3; }
sub wedge { 4; }
sub dodecahedron { 5; }
sub prisma { 6; } # really is 3-sided cylinder

# "special" brushes
sub omni { 0; }
sub spot { 1; }

sub solid { 0; }
sub air { 1; }
sub water { 2; }
sub flood { 3; }
sub evaporate { 4; }
sub solid2water { 5; }
sub solid2air { 6; }
sub air2solid { 7; }
sub water2solid { 8; }
sub blockable { 9; }
sub room   { 0xFB; }
sub flow   { 0xFC; }
sub object { 0xFD; }
sub area   { 0xFE; }
sub light  { 0xFF; }

sub threesided { 0x3000;}
sub foursided { 0x4000; }
sub fivesided { 0x5000; }
sub sixsided { 0x6000; }
sub sevensided { 0x7000; }
sub eightsided { 0x8000; }
sub ninesided { 0x9000; }
sub tensided { 0xa000; }

# all values +1 because of 0 for default
# these are the eax_values for the mission default eax

sub eax_generic { 0x01; }
sub eax_paddedCell { 0x02; }
sub eax_room { 0x03; }
sub eax_bathRoom { 0x04; }
sub eax_livingRoom { 0x05; }
sub eax_stoneRoom { 0x06; }
sub eax_auditorium { 0x07; }
sub eax_concertHall { 0x08; }
sub eax_cave { 0x09; }
sub eax_arena { 0x0a; }
sub eax_hangar { 0x0b; }
sub eax_carpetedHallway { 0x0c; }
sub eax_hallway { 0x0d; }
sub eax_stoneCorridor { 0x0d; }
sub eax_alley { 0x0e; }
sub eax_forest { 0x0f; }
sub eax_city { 0x10; }
sub eax_mountains { 0x11; }
sub eax_quarry { 0x12; }
sub eax_plain { 0x13; }
sub eax_parkingLot { 0x14; }
sub eax_sewerPipe { 0x15; }
sub eax_underWater { 0x16; }
sub eax_drugged { 0x17; }
sub eax_dizzy { 0x18; }
sub eax_psychotic { 0x19; }

# misc obj stuff

# ld means link (data), o = object property

# contains
sub l_hand { 0xFFFFFFFF; }
sub l_belt { 0xFFFFFFFE; }
sub l_generic_contents { 0x00000000; }

# link data particleattachment datatype
sub l_object { 0x0; }
sub l_vhot { 0x1; }
sub l_joint { 0x2; }
sub l_submodel { 0x3; }

sub l_head { 0x0; }
sub l_heack { 0x1; }
sub l_abdomen { 0x2; }
sub l_butt { 0x3; }
sub l_left_shoulder { 0x4; }
sub l_right_shoulder { 0x5; }
sub l_left_elbow { 0x6; }
sub l_right_elbow { 0x7; }
sub l_left_wrist { 0x8; }
sub l_right_wrist { 0x9; }
sub l_left_fingers { 0x0a; }
sub l_right_fingers { 0x0b; }
sub l_left_hip { 0x0c; }
sub l_right_hip { 0x0d; }
sub l_left_knee { 0x0e; }
sub l_right_knee { 0x0f; }
sub l_left_toe { 0x10; }
sub l_right_toe { 0x11; }
sub l_tail { 0x12; }

# attack and others
sub l_none { 0x0; }
sub l_very_low { 0x1; }
sub l_low { 0x2; }
sub l_normal { 0x3; }
sub l_moderate { 0x3; } # some dialogs call it that way
sub l_high { 0x4; }
sub l_very_high { 0x5; }
sub l_absolute { 0x6; }

# levels
sub l_level_low { 0x0; }
sub l_level_medium { 0x1; }
sub l_level_high { 0x2; }

# awareness
sub l_seen { 0x1; }
sub l_heard { 0x2; }
sub l_can_raycast { 0x4; }
sub l_have_los { 0x8; }
sub l_blind { 0x10; }
sub l_deaf { 0x20; }
sub l_highest { 0x40; }
sub l_firsthand { 0x80; }

# awareness
sub l_stopped { 0x0; }
sub l_very_slow { 0x1; }
sub l_slow { 0x2; }
#sub l_normal { 0x3; } # already defined as 3
sub l_fast { 0x4; }
sub l_very_fast { 0x5; }

# watchobj
#sub l_none { 0x0; } # already defined
sub l_line_of_sight { 0x1; }
sub l_raycast { 0x2; }

sub l_nothing { 0x0; }
sub l_script_message { 0x1; }
sub l_play_sound_motion { 0x2; }
sub l_alert { 0x3; }
sub l_become_hostile { 0x4; }
sub l_enable_investigate { 0x5; }
sub l_goto_object { 0x6; }
sub l_frob_object { 0x7; }
sub l_wait { 0x8; }
sub l_mprint { 0x9; }
sub l_add_remove_metaprop { 0x0a; }
sub l_add_link { 0x0b; }
sub l_remove_link { 0x0c; }
sub l_face { 0x0d; }
sub l_signal { 0x0e; }
sub l_dest_script_message { 0x0f; }

# frobproxymask
sub l_do_inv { 0x1; }
sub l_no_world { 0x2; }
sub l_do_toolsrc { 0x4; }
sub l_no_tooldst { 0x8; }
sub l_allow_direct { 0x10; }

# object properties
# collision type
sub o_bounce { 0x1; }
sub o_destroy_on_impact { 0x2; }
sub o_slay_on_impact { 0x4; }
sub o_no_collision_sound { 0x8; }
sub o_no_result { 0x10; }
sub o_full_collision_sound { 0x20; }

#
sub o_block_pathfind { 0x1; }
sub o_repel { 0x2; }

# render type
sub o_normal { 0x0; }
sub o_not_rendered { 0x1; }
sub o_unlit { 0x2; }
sub o_editor_only { 0x3; }

# inventory type
sub o_junk { 0x0; }
sub o_item { 0x1; }
sub o_weapon { 0x2; }

# animlight
sub o_flip { 0x0; }
sub o_slide { 0x1; }
sub o_random { 0x2; }
sub o_min_bright { 0x3; }
sub o_max_bright { 0x4; }
sub o_zero_bright { 0x5; }
sub o_smoothly_brighten { 0x6; }
sub o_smoothly_dim { 0x7; }
sub o_coherent { 0x8; }
sub o_flicker { 0x9; }

# anim
sub o_on { 0x1; }
sub o_reverse { 0x2; }
sub o_resync { 0x4; }
sub o_go_edge { 0x8; }
sub o_lap_one { 0x10; }

# ambienthacked
sub o_environmental { 0x1; }
sub o_no_sharp_curve { 0x2; }
sub o_turned_off { 0x4; }
sub o_one_shot { 0x8; }
sub o_music { 0x10; }
sub o_sync { 0x20; }
sub o_nofade { 0x40; }
sub o_destroy_obj { 0x80; }
sub o_auto_off { 0x100; }

# slay
sub o_no_effect { 1; }
sub o_terminate { 2; }
sub o_destroy { 3; }

# slay
sub o_obb { 0; }
sub o_sphere { 1; }
sub o_sphere_hat { 2; }

# creature type
sub o_humanoid { 0; };
sub o_playerarm { 1; }
sub o_playerbowarm { 2; };
sub o_burrick { 3; }
sub o_spider { 4; }
sub o_bugbeast { 5; }
sub o_crayman { 6 };
sub o_constantine { 7; }
sub o_apparition { 8; }
sub o_sweel { 9; };
sub o_rope { 10; }
sub o_zombie { 11; }
sub o_smallspider { 12; }
sub o_frog { 13; }
sub o_cutty { 14; }

# tweq
sub o_tweq_destroy_obj { 0; };
sub o_remove_prop { 1; }
sub o_stop_tweq { 2; };
sub o_continue { 3; }
sub o_slay_obj { 4; }

sub o_no_limit { 0x1; };
sub o_sim { 0x2; }
sub o_wrap { 0x4; };
sub o_one_bounce { 0x8; }
sub o_sim_small_rad { 0x10; }
sub o_sim_large_rad { 0x20; }
sub o_off_screen { 0x40; }

sub o_jitter_low { 0x1; }
sub o_jitter_high { 0x2; }
sub o_mul { 0x4; }

sub o_anchor { 0x1; }
sub o_scripts { 0x2; }
sub o_tweq_random { 0x4; }
sub o_grav { 0x8; }
sub o_zero_vel { 0x10; }
sub o_tellai { 0x20; }
sub o_pushout { 0x40; }
sub o_negative_velocity { 0x80; }
sub o_relative_velocity { 0x100; }
sub o_nophysics { 0x200; }
sub o_anchorvhot { 0x400; }

# jds (fixed by tels)
# face stuff
# -2 is second to last face (even if brush has no top face
# -1 is last face

sub defaultFace {0;}
sub side1 {1;}
sub side2 {2;}
sub side3 {3;}
sub side4 {4;}
sub side5 {5;}
sub side6 {6;}
sub side7 {7;}
sub side8 {8;}
sub north {1;}
sub west {2;}
sub south {3;}
sub east {4;}
sub top {-2;}
sub bottom {-1;}

# for align/facing

sub aCenterX {0x100;}
sub aCenterY {0x200;}
sub aCenterZ {0x400;}
sub aNorth {1;}
sub aWest {2;}
sub aSouth {4;}
sub aEast {8;}
sub aTop {16;}
sub aBottom {32;}
sub aLeft {64;}
sub aRight {128;}

sub kind2Name
  {
  # return name for a given brush kind
  my ($kind) = @_;
  my @t = ("cube","cylinder","pyramid","corner-apex pyramid","wedge","dodecahedron","prisma");
  return $t[$kind] || "unknown";
  }

sub light2Name
  {
  # return name for a given brush light
  my ($kind) = @_;
  my @t = ("omni","spot");
  return $t[$kind] || "unknown";
  }

sub type2Name
  {
  # return name for a given type
  my ($type) = @_;
  my @t = ("solid","air","water","flood","evaporate","solid to water","solid to air","air to solid","water to solid","blockable");
  return $t[$type] || "unknown";
  }

sub optimizeSky
  {
  # set scale of all sky textures to 24
  my ($bunch) = @_;
  $bunch->scaleTexture(sky(),24); # easy isnt it? ;)
  }

sub copyHash
  {
  # when you write $copy = $hash; then you are just creating another reference
  # to the same hash, not a new hash. Modifying $copy is modifying $hash, too,
  # which is probably not what you want. use $copy = copyHash($hash) instead.
  # does no recurse copy!!
  my $hash = shift || {};
  my $copy = {};
  foreach (keys %$hash) { $copy->{$_} = $hash->{$_}; }
  $copy;
  }

sub printHash
  {
  # print a hash contents
  my $hash = shift || {};
  print "Printing hash:\n"; foreach (keys %$hash) { print " $_ => $hash->{$_}\n"; }
  }

1;

