#!/usr/bin/perl # gtkchar4e version 1.1.1 by Rob Knop (rknop@pobox.com) # Last updated 2011-09-02 # # http://www.pobox.com/~rknop/Omar/gurps/gtkchar4e.html # # GURPS is a trademark of Steve Jackson Games, and its rules and art are # copyrighted by Steve Jackson Games . All rights are reserved by Steve # Jackson Games. This game aid is the original creation of Rob Knop and is # released for free distribution, and not for resale, under the # permissions granted in the Steve Jackson Games Online Policy at: # # http://www.sjgames.com/general/online_policy.html use Gtk2 '-init'; use Gtk2::SimpleList; use XML::Writer; use XML::Twig; use IO::File; use File::Basename; use strict; $main::wincount=0; my ($i,$char,$win); if (@ARGV) { foreach $i (@ARGV) { $char=new GTKChar::Char; $char->readXML($i); $win=new GTKChar::CharWin($char); $win->{'win'}->set_title($char->name()); } } else { $char=new GTKChar::Char; $win=new GTKChar::CharWin($char); } $win->aboutbox(); Gtk2->main; exit; # ********************************************************************** # ********************************************************************** # ********************************************************************** # the character class # # Advantages, etc., are stored as arrays of hash refs # name # basepoints = base char points # modifiers = array ref of hash refs # each element # name # modifier = percent modifier # notes = array ref of strings # cost (NOT SAVED; calculated) # # Languages are stored as an array of hash refs # name # primary (flag) # written: int (1=broken, 2=accented, 3=native) # spoken: int (1=broken, 2=accented, 3=native) # cost (NOT SAVED; calculated) # # Skills are stored as an array of hash refs # name # optspec # difficulty (e, a, h, v, !) (base difficulty; do not corect for specialty) # baseatt # points # deafultbase # defaultoffset # bonus (array of array refs: bonus/penalty, description) # relrank (NOT SAVED; calculated) # level (NOT SAVED; calculated) # defaulting (NOT SAVED; calculated) # # Techniques is an array of hash refs # name # difficulty (a, h) # skill # skilloffset # points # level (NOT SAVED; calculated) # # Equipment is an array of hash refs # number # name # location # weight (per individual item) # cost (per individual item) # carried (flag) # # Armor is a hash ref # torso # skull # eyes # face # neck # head (REALLY IS GROIN!!) # larm # rarm # lhand # rhand # lleg # rleg # lfoot # rfoot # # weapons is an array of hash refs # weapon # damage # st # reach # skill # parry # notes # # rangedweapons is an array of hash refs # weapon # damage # acc # range (string) # rof # shots # st # bulk # rcl # notes package GTKChar::Char; sub new { my $type=shift; my $self={}; bless $self,'GTKChar::Char'; $self->{'name'}=''; $self->{'soundbyte'}=''; $self->{'keywords'}=''; $self->{'description'}=''; $self->{'personality'}=''; $self->{'background'}=''; $self->{'notes'}=''; $self->{'unspentpoints'}=0; $self->{'st'}=10; $self->{'templst'}=10; $self->{'dx'}=10; $self->{'templdx'}=10; $self->{'iq'}=10; $self->{'templiq'}=10; $self->{'ht'}=10; $self->{'templht'}=10; $self->{'extrahp'}=0; $self->{'templextrahp'}=0; $self->{'curhp'}=10; $self->{'extrafat'}=0; $self->{'templextrafat'}=0; $self->{'curfat'}=10; $self->{'extrawill'}=0; $self->{'templextrawill'}=0; $self->{'extraper'}=0; $self->{'templextraper'}=0; $self->{'extraspeed'}=0; $self->{'templextraspeed'}=0; $self->{'extramove'}=0; $self->{'templextramove'}=0; $self->{'dodgebonus'}=0; $self->{'parry'}=0; $self->{'block'}=0; $self->{'stcost'}=0; $self->{'dxcost'}=0; $self->{'iqcost'}=0; $self->{'htcost'}=0; $self->{'hpcost'}=0; $self->{'willcost'}=0; $self->{'percost'}=0; $self->{'fatcost'}=0; $self->{'speedcost'}=0; $self->{'movecost'}=0; $self->{'sm'}=0; $self->{'cultures'}=[]; $self->{'languages'}=[]; $self->{'advantages'}=[]; # Includes perks $self->{'disadvantages'}=[]; $self->{'quirks'}=[]; $self->{'skills'}=[]; $self->{'techniques'}=[]; $self->{'equipment'}=[]; $self->{'armor'}={'torso'=>0, # Just DR; list it in equip 'skull'=>0, 'eyes'=>0, 'face'=>0, 'neck'=>0, 'head'=>0, 'larm'=>0, 'rarm'=>0, 'lhand'=>0, 'rhand'=>0, 'lleg'=>0, 'rleg'=>0, 'lfoot'=>0, 'rfoot'=>0}; $self->{'weapons'}=[]; $self->{'rangedweapons'}=[]; $self->{'filename'}=''; $self->{'attpoints'}=0; $self->{'adpoints'}=0; $self->{'disadpoints'}=0; $self->{'quirkpoints'}=0; $self->{'langpoints'}=0; $self->{'skillpoints'}=0; $self->{'techniquepoints'}=0; $self->{'totalpoints'}=0; return $self; } # ********************************************************************** sub name { return $_[0]->{'name'}; } sub setname { $_[0]->{'name'}=$_[1]; } sub soundbyte { return $_[0]->{'soundbyte'}; } sub setsoundbyte { $_[0]->{'soundbyte'}=$_[1]; } sub keywords { return $_[0]->{'keywords'}; } sub setkeywords { $_[0]->{'keywords'}=$_[1]; } sub description { return $_[0]->{'description'}; } sub setdescription { $_[0]->{'description'}=$_[1]; } sub personality { return $_[0]->{'personality'}; } sub setpersonality { $_[0]->{'personality'}=$_[1]; } sub background { return $_[0]->{'background'}; } sub setbackground { $_[0]->{'background'}=$_[1]; } sub notes { return $_[0]->{'notes'}; } sub setnotes { $_[0]->{'notes'}=$_[1]; } sub unspentpoints { return $_[0]->{'unspentpoints'}; } sub setunspentpoints { $_[0]->{'unspentpoints'}=$_[1]; } sub filename { return $_[0]->{'filename'}; } sub setfilename { $_[0]->{'filename'}=$_[1]; } sub st { return $_[0]->{'st'}; } sub setst { $_[0]->{'st'}=$_[1]; } sub dx { return $_[0]->{'dx'}; } sub setdx { $_[0]->{'dx'}=$_[1]; } sub iq { return $_[0]->{'iq'}; } sub setiq { $_[0]->{'iq'}=$_[1]; } sub ht { return $_[0]->{'ht'}; } sub setht { $_[0]->{'ht'}=$_[1]; } sub templst { return $_[0]->{'templst'}; } sub settemplst { $_[0]->{'templst'}=$_[1]; } sub templdx { return $_[0]->{'templdx'}; } sub settempldx { $_[0]->{'templdx'}=$_[1]; } sub templiq { return $_[0]->{'templiq'}; } sub settempliq { $_[0]->{'templiq'}=$_[1]; } sub templht { return $_[0]->{'templht'}; } sub settemplht { $_[0]->{'templht'}=$_[1]; } sub hp { return $_[0]->{'st'}+$_[0]->{'extrahp'}; } sub extrahp { $_[0]->{'extrahp'}; } sub setextrahp { $_[0]->{'extrahp'}=$_[1]; } sub templextrahp { return $_[0]->{'templextrahp'}; } sub settemplextrahp { $_[0]->{'templextrahp'}=$_[1]; } sub curhp { return $_[0]->{'curhp'}; } sub setcurhp { $_[0]->{'curhp'}=$_[1]; } sub fat { return $_[0]->{'ht'}+$_[0]->{'extrafat'}; } sub extrafat { $_[0]->{'extrafat'}; } sub setextrafat { $_[0]->{'extrafat'}=$_[1]; } sub templextrafat { return $_[0]->{'templextrafat'}; } sub settemplextrafat { $_[0]->{'templextrafat'}=$_[1]; } sub curfat { return $_[0]->{'curfat'}; } sub setcurfat { $_[0]->{'curfat'}=$_[1]; } sub stcost { return $_[0]->{'stcost'}; } sub dxcost { return $_[0]->{'dxcost'}; } sub iqcost { return $_[0]->{'iqcost'}; } sub htcost { return $_[0]->{'htcost'}; } sub hpcost { return $_[0]->{'hpcost'}; } sub willcost { return $_[0]->{'willcost'}; } sub percost { return $_[0]->{'percost'}; } sub fatcost { return $_[0]->{'fatcost'}; } sub speedcost { return $_[0]->{'speedcost'}; } sub movecost { return $_[0]->{'movecost'}; } sub will { return $_[0]->{'iq'}+$_[0]->{'extrawill'}; } sub extrawill { $_[0]->{'extrawill'}; } sub setextrawill { $_[0]->{'extrawill'}=$_[1]; } sub templextrawill { return $_[0]->{'templextrawill'}; } sub settemplextrawill { $_[0]->{'templextrawill'}=$_[1]; } sub per { return $_[0]->{'iq'}+$_[0]->{'extraper'}; } sub extraper { $_[0]->{'extraper'}; } sub setextraper { $_[0]->{'extraper'}=$_[1]; } sub templextraper { return $_[0]->{'templextraper'}; } sub settemplextraper { $_[0]->{'templextraper'}=$_[1]; } sub speed { return ($_[0]->{'dx'}+$_[0]->{'ht'})/4+$_[0]->{'extraspeed'}; } sub extraspeed { return $_[0]->{'extraspeed'}; } sub setextraspeed { $_[0]->{'extraspeed'}=$_[1]; } sub templextraspeed { return $_[0]->{'templextraspeed'}; } sub settemplextraspeed { $_[0]->{'templextraspeed'}=$_[1]; } sub basicmove { return int($_[0]->speed())+$_[0]->{'extramove'}; } sub extramove { return $_[0]->{'extramove'}; } sub setextramove { $_[0]->{'extramove'}=$_[1]; } sub templextramove { return $_[0]->{'templextramove'}; } sub settemplextramove { $_[0]->{'templextramove'}=$_[1]; } sub move { my ($encumbrance,$lbs)=$_[0]->encumbrance(); if ($encumbrance>=5) { return 0; } return int($_[0]->basicmove()*(1-0.2*$encumbrance)); } sub basiclift { my $bl=$_[0]->st()**2/5; if ($bl>10) { $bl=int($bl+0.5); } $bl; } sub basicdodge { return int($_[0]->speed())+3; } sub dodgebonus { return $_[0]->{'dodgebonus'}; } sub setdodgebonus { $_[0]->{'dodgebonus'}=$_[1]; } sub dodge { return $_[0]->basicdodge() + $_[0]->dodgebonus() - ($_[0]->encumbrance())[0]; } #sub dodge { return $_[0]->basicdodge(); } sub parry { return $_[0]->{'parry'}; } sub block { return $_[0]->{'block'}; } sub setparry { $_[0]->{'parry'}=$_[1]; } sub setblock { $_[0]->{'block'}=$_[1]; } sub sm { return $_[0]->{'sm'}; } sub setsm { $_[0]->{'sm'}=$_[1]; } sub cultures { return $_[0]->{'cultures'}; } sub languages { return $_[0]->{'languages'}; } sub advantages { return $_[0]->{'advantages'}; } sub disadvantages { return $_[0]->{'disadvantages'}; } sub quirks { return $_[0]->{'quirks'}; } sub skills { return $_[0]->{'skills'}; } sub setskills { $_[0]->{'skills'}=$_[1]; } sub techniques { return $_[0]->{'techniques'}; } sub settechniques { $_[0]->{'techniques'}=$_[1]; } sub equipment { return $_[0]->{'equipment'}; } sub setequipment { $_[0]->{'equipment'}=$_[1]; } sub armor { return $_[0]->{'armor'}; } sub setarmor { $_[0]->{'armor'}=$_[1]; } sub weapons { return $_[0]->{'weapons'}; } sub setweapons { $_[0]->{'weapons'}=$_[1]; } sub rangedweapons { return $_[0]->{'rangedweapons'}; } sub setrangedweapons { $_[0]->{'rangedweapons'}=$_[1]; } # ********************************************************************** # Return (encumbrance penalty--absolute value-- , weight carried--lbs--) sub encumbrance { my $self=shift; my ($item,$wt,$encumb,$bl); $wt=0; $encumb=5; foreach $item (@{$self->equipment()}) { if ($item->{'carried'}) { $wt+=$item->{'number'}*$item->{'weight'}; } } $bl=$self->basiclift(); if ($wt<=$bl) { $encumb=0; } elsif ($wt<=2*$bl) { $encumb=1; } elsif ($wt<=3*$bl) { $encumb=2; } elsif ($wt<=6*$bl) { $encumb=3; } elsif ($wt<=10*$bl) { $encumb=4; } ($encumb,$wt); } # ********************************************************************** sub equipcost { my $self=shift; my ($tot,$equip); $tot=0; foreach $equip (@{$self->{'equipment'}}) { $tot+=$equip->{'number'}*$equip->{'cost'}; } return $tot; } # ********************************************************************** sub traitcost { my $self=shift; my $trait=shift; my ($mod,$cost,$totmod); $cost=$trait->{'basepoints'}; if (!$cost) { $cost=0; } $totmod=0; foreach $mod (@{$trait->{'modifiers'}}) { $totmod+=$mod->{'modifier'}; } if ($totmod<-80) { $totmod=-80; } $cost+=$cost*$totmod/100; $cost=int($cost) + ($cost-int($cost)>0.01 ? 1 : 0); return $cost; } # ********************************************************************** sub addtrait { my $self=shift; my $which=shift; # advantages, disadvantages, or quirks my $name=shift; my $basepoints=shift; my $modifiers=shift; my $notes=shift; my ($ordinal,$ad); if (@_) { $ordinal=shift; } else { $ordinal=scalar @{$self->{$which}}; } if ($ordinal<0) { $ordinal=0; } if ($ordinal>scalar @{$self->{$which}}) { $ordinal=scalar @{$self->{$which}}; } $ad={'name' => $name, 'basepoints' => $basepoints, 'modifiers' => $modifiers, 'notes' => $notes}; $ad->{'cost'}=$self->traitcost($ad); splice @{$self->{$which}},$ordinal,0,($ad); } # ********************************************************************** sub removetrait { my $self=shift; my $which=shift; my $ordinal=shift; return if ($ordinal<0 || $ordinal>=scalar @{$self->{$which}}); splice @{$self->{$which}},$ordinal,1; } # ********************************************************************** sub skilllevel { my $self=shift; my $skill=shift; my ($cost,$baseatt,$baselevel,$l,$defaulting,$baseatlvl); my ($level,$def,$cleandef,$cleansk,$effpoints,$bonus,$boner,$sk,$relrank); # Baselevel ($baseatt)=($skill->{'baseatt'}=~/^\s*(.*\S)\s*$/); $baseatt=lc($baseatt); if ($baseatt eq 'st') { $baselevel=$self->st(); } if ($baseatt eq 'dx') { $baselevel=$self->dx(); } if ($baseatt eq 'iq') { $baselevel=$self->iq(); } if ($baseatt eq 'ht') { $baselevel=$self->ht(); } if ($baseatt eq 'will') { $baselevel=$self->will(); } if ($baseatt eq 'per') { $baselevel=$self->per(); } $baseatlvl=$baselevel; if ($skill->{'difficulty'} eq 'e') { $baselevel-=1; } if ($skill->{'difficulty'} eq 'a') { $baselevel-=2; } if ($skill->{'difficulty'} eq 'h') { $baselevel-=3; } if ($skill->{'difficulty'} eq 'v') { $baselevel-=4; } if ($skill->{'difficulty'} eq '!') { $baselevel-=4; } # See if defaulting helps ($cleandef)=($skill->{'defaultbase'}=~/^\s*(.*\S)\s*$/); if ($cleandef) { $cleandef=lc($cleandef); } $def=0; if ($cleandef) { if ($cleandef eq 'st') { $def=$self->st()+$skill->{'defaultoffset'}; } if ($cleandef eq 'dx') { $def=$self->dx()+$skill->{'defaultoffset'}; } if ($cleandef eq 'iq') { $def=$self->iq()+$skill->{'defaultoffset'}; } if ($cleandef eq 'ht') { $def=$self->ht()+$skill->{'defaultoffset'}; } if ($cleandef eq 'will') { $def=$self->will() + $skill->{'defaultoffset'}; } if ($cleandef eq 'per') { $def=$self->per() + $skill->{'defaultoffset'}; } if ($def==0) { foreach $sk (@{$self->{'skills'}}) { next if (!($sk->{'points'})); ($cleansk)=($sk->{'name'}=~/^\s*(.*\S)\s*$/); $cleansk=lc($cleansk); if ($cleandef eq $cleansk) { if ($sk->{'level'}==0) { return ''; } # Can't do this yet $def=$sk->{'level'}+$skill->{'defaultoffset'}; } } } } if ($def>$baselevel) { $defaulting=1; $l=$def; } else { $defaulting=0; $l=$baselevel; } # Now figure out the relrank and level of the skill if (($l-$baselevel)==0) { $effpoints=0; } elsif (($l-$baselevel)==1) { $effpoints=1; } elsif (($l-$baselevel)==2) { $effpoints=2; } else { $effpoints=($l-$baselevel-2)*4; } if ($skill->{'difficulty'} eq '!') { $effpoints+=int($skill->{'points'}/3); } else { ($skill->{'points'}) && ($effpoints+=$skill->{'points'}); } $level=$baselevel; if ($effpoints==0) { $level=0; } elsif ($effpoints==1) { $level+=1; } elsif ($effpoints==2 || $effpoints==3) { $level+=2; } else { $level+=3+int(($effpoints-4)/4); } # See if this is pure default (is this a hack/spaghetti way to do it?) if ($level==0 && $def!=0) { $level=$def; } # Bonuses $bonus=0; foreach $boner (@{$skill->{'bonus'}}) { $bonus+=$boner->[0]; } $level+=$bonus; # Figure out relrank and store results $relrank=$level-$baseatlvl; $skill->{'relrank'}=$relrank; $skill->{'level'}=$level; $skill->{'defaulting'}=$defaulting; return 1; } # ********************************************************************** # won't calculate levels; recalculate the whole character for that sub addskill { my $self=shift; my $name=shift; my $optspec=shift; my $difficulty=shift; my $baseatt=shift; my $points=shift; my $defaultbase=shift; my $defaultoffset=shift; my $bonus=shift; # arry ref (array of array refs) my ($ordinal,$skill); if (@_) { $ordinal=shift; } else { $ordinal=scalar @{$self->{'skills'}}; } ($difficulty)=($difficulty=~/^\s*(.*\S)\s*$/); if (!defined($difficulty)) { $difficulty='a'; } ($baseatt)=($baseatt=~/^\s*(.*\S)\s*$/); if (!defined($baseatt)) { $baseatt='iq'; } $skill={'name' => $name, 'optspec' => $optspec, 'difficulty' => $difficulty, 'baseatt' => $baseatt, 'points' => $points, 'defaultbase' => $defaultbase, 'defaultoffset' => $defaultoffset, 'bonus' => $bonus }; splice @{$self->{'skills'}},$ordinal,0,($skill); } # ********************************************************************** # calcskilllevels sub calcskilllevels { my $self=shift; my ($iter,$skill,$done,$technique,$level,$i); foreach $i (0..$#{$self->{'skills'}}) { $self->{'skills'}->[$i]->{'level'}=0; $self->{'skills'}->[$i]->{'relrank'}=0; $self->{'skills'}->[$i]->{'defaulting'}=0; } $iter=0; while (!$done && $iter<5) { $done=1; foreach $i (0..$#{$self->{'skills'}}) { if (!$self->skilllevel($self->{'skills'}->[$i])) { $done=0; } } ++$iter; } if (!$done) { return 0; } # Techniques foreach $technique (@{$self->{'techniques'}}) { $level=0; if (($skill)=grep($_->{'name'}=~/^\s*$technique->{'skill'}\s*$/i, @{$self->{'skills'}})) { $level=$skill->{'level'}+$technique->{'skilloffset'}; } if (!$level) { if (uc($technique->{'skill'}) eq 'ST') { $level=$self->st()+$technique->{'skilloffset'}; } if (uc($technique->{'skill'}) eq 'DX') { $level=$self->dx()+$technique->{'skilloffset'}; } if (uc($technique->{'skill'}) eq 'IQ') { $level=$self->iq()+$technique->{'skilloffset'}; } if (uc($technique->{'skill'}) eq 'HT') { $level=$self->ht()+$technique->{'skilloffset'}; } if (uc($technique->{'skill'}) eq 'PER') { $level=$self->per()+$technique->{'skilloffset'}; } if (uc($technique->{'skill'}) eq 'WILL') { $level=$self->will()+$technique->{'skilloffset'}; } } if ($technique->{'difficulty'} eq 'h') { if ($technique->{'points'}>=2) { $level+=$technique->{'points'}-1; } } else { ($technique->{'points'}) && ($level+=$technique->{'points'}); } $technique->{'level'}=$level; } return 1; } # ********************************************************************** # addlanguage sub addlanguage { my $self=shift; my $name=shift; my $primary=shift; my $written=shift; my $spoken=shift; push @{$self->{'languages'}},{ 'name'=>$name, 'primary'=>$primary, 'written'=>$written, 'spoken'=>$spoken }; } # ********************************************************************** # calccharvalue; returns (total,atts,ads,disads,quirks,langs,skills,techniques) # Also updates all other stuff in the character sub calccharvalue { my $self=shift; my ($total,$atts,$ads,$disads,$quirks,$skills,$langs,$techniques); my ($diff,$cost,$lang,$ad,$mod,$totmod,$skill,$technique); if (!$self->calcskilllevels()) { print STDERR "ERROR getting skill levels!!!\n"; return 0; } $total=$atts=$ads=$disads=$quirks=$skills=$langs=$techniques=0; # Attributes $diff=$self->{'st'}-$self->{'templst'}; $cost=10; if ($self->sm()>0) { $cost-=($self->sm()>8 ? 8 : $self->sm()); } if (grep($_->{'name'}=~/^\s*no\s*fine\*manipulators\s*/i, @{$self->{'disadvantages'}})) { $cost-=4; } if ($cost<2) { $cost=2; } $self->{'stcost'}=$cost*$diff; $atts+=$cost*$diff; $diff=$self->{'dx'}-$self->{'templdx'}; $cost=20; if (grep($_->{'name'}=~/^\s*no\s*fine\*manipulators\s*/i, @{$self->{'disadvantages'}})) { $cost=12; } $self->{'dxcost'}=$cost*$diff; $atts+=$cost*$diff; $diff=$self->{'iq'}-$self->{'templiq'}; $self->{'iqcost'}=20*$diff; $atts+=20*$diff; $diff=$self->{'ht'}-$self->{'templht'}; $self->{'htcost'}=10*$diff; $atts+=10*$diff; $diff=$self->{'extrahp'}-$self->{'templextrahp'}; $cost=$diff*2; if ($self->sm()>0) { $cost-= 0.1*$cost* ($self->sm()>8 ? 8 : $self->sm()); $cost=int($cost) + ($cost-int($cost)>0.01 ? 1 : 0); } $self->{'hpcost'}=$cost; $atts+=$cost; $diff=$self->{'extrafat'}-$self->{'templextrafat'}; $cost=$diff*3; $self->{'fatcost'}=$cost; $atts+=$cost; $diff=$self->{'extrawill'}-$self->{'templextrawill'}; $cost=$diff*5; $self->{'willcost'}=$cost; $atts+=$cost; $diff=$self->{'extraper'}-$self->{'templextraper'}; $cost=$diff*5; $self->{'percost'}=$cost; $atts+=$cost; $diff=$self->{'extraspeed'}-$self->{'templextraspeed'}; $cost=$diff*20; $cost=int($cost) + ($cost-int($cost)>0.01 ? 1 : 0); $self->{'speedcost'}=$cost; $atts+=$cost; $diff=$self->{'extramove'}-$self->{'templextramove'}; $cost=$diff*5; $self->{'movecost'}=$cost; $atts+=$cost; # Languages foreach $lang (@{$self->{'languages'}}) { if ($lang->{'primary'}) { $cost = $lang->{'written'}-3 + $lang->{'spoken'}-3; } else { $cost = $lang->{'written'} + $lang->{'spoken'}; } $lang->{'cost'}=$cost; $langs+=$cost; } # Advantages foreach $ad (@{$self->{'advantages'}}) { $ad->{'cost'}=$self->traitcost($ad); $ads+=$ad->{'cost'}; } # Disadvantages foreach $ad (@{$self->{'disadvantages'}}) { $ad->{'cost'}=$self->traitcost($ad); $disads+=$ad->{'cost'}; } # Quirks foreach $ad (@{$self->{'quirks'}}) { $cost=$ad->{'basepoints'}; $quirks+=$cost; } # Skills foreach $skill (@{$self->{'skills'}}) { ($skill->{'points'}) && ($skills+=$skill->{'points'}); } # Techniques foreach $technique (@{$self->{'techniques'}}) { ($technique->{'points'}) && ($techniques+=$technique->{'points'}); } # Done $total=$atts+$ads+$disads+$quirks+$langs+$skills+$techniques; $self->{'attpoints'}=$atts; $self->{'adpoints'}=$ads; $self->{'disadpoints'}=$disads; $self->{'quirkpoints'}=$quirks; $self->{'langpoints'}=$langs; $self->{'skillpoints'}=$skills; $self->{'techniquepoints'}=$techniques; $self->{'totalpoints'}=$total; return $total; } # ********************************************************************** # writeXML sub writeXML { my $self=shift; my $filename=shift; my ($trait,$traits,$ad,$mod); my $ofp=new IO::File(">$filename"); # my $writer=new XML::Writer(OUTPUT => $ofp , NEWLINES => 1); my $writer=new XML::Writer(OUTPUT => $ofp , DATA_MODE => 1); $writer->xmlDecl(); $writer->startTag('character' , 'name' => $self->name(), 'unspentpoints' => $self->unspentpoints()); $writer->startTag('soundbyte'); $writer->characters($self->soundbyte()); $writer->endTag('soundbyte'); $writer->startTag('keywords'); $writer->characters($self->keywords()); $writer->endTag('keywords'); $writer->startTag('description'); $writer->characters($self->description()); $writer->endTag('description'); $writer->startTag('personality'); $writer->characters($self->personality()); $writer->endTag('personality'); $writer->startTag('background'); $writer->characters($self->background()); $writer->endTag('background'); $writer->startTag('notes'); $writer->characters($self->notes()); $writer->endTag('notes'); $writer->emptyTag('st' , 'templ'=>$self->templst() , 'val'=>$self->st()); $writer->emptyTag('dx' , 'templ'=>$self->templdx() , 'val'=>$self->dx()); $writer->emptyTag('iq' , 'templ'=>$self->templiq() , 'val'=>$self->iq()); $writer->emptyTag('ht' , 'templ'=>$self->templht() , 'val'=>$self->ht()); $writer->emptyTag('hp' , 'extra'=>$self->extrahp(), 'templextra'=>$self->templextrahp(), 'cur'=>$self->curhp()); $writer->emptyTag('fat' , 'extra'=>$self->extrafat(), 'templextra'=>$self->templextrafat(), 'cur'=>$self->curfat()); $writer->emptyTag('will' , 'extra'=>$self->extrawill(), 'templextra'=>$self->templextrawill()); $writer->emptyTag('per' , 'extra'=>$self->extraper(), 'templextra'=>$self->templextraper()); $writer->emptyTag('speed' , 'extra'=>$self->extraspeed(), 'templextra'=>$self->templextraspeed()); $writer->emptyTag('move' , 'extra'=>$self->extramove(), 'templextra'=>$self->templextramove()); $writer->emptyTag('sm' , 'val'=>$self->sm()); $writer->emptyTag('dodge' , 'bonus'=>$self->dodgebonus()); $writer->emptyTag('parry' , 'val'=>$self->parry()); $writer->emptyTag('block' , 'val'=>$self->block()); # Advantages, Disadvantages, Quirks foreach $trait (('advantage','disadvantage','quirk')) { if ($trait eq 'advantage') { $traits=$self->advantages(); } if ($trait eq 'disadvantage') { $traits=$self->disadvantages(); } if ($trait eq 'quirk') { $traits=$self->quirks(); } foreach $ad (@$traits) { $writer->startTag($trait , 'name'=>$ad->{'name'} , 'basepoints'=>$ad->{'basepoints'}); foreach $mod (@{$ad->{'modifiers'}}) { $writer->emptyTag('modifier' , 'name'=>$mod->{'name'}, 'value'=>$mod->{'modifier'}); } foreach $mod (@{$ad->{'notes'}}) { $writer->startTag('notes'); $writer->characters($mod); $writer->endTag('notes'); } $writer->endTag($trait); } } # Cultures and Languages foreach $trait(@{$self->languages()}) { $writer->emptyTag('language' , 'name'=>$trait->{'name'}, 'primary'=>$trait->{'primary'}, 'written'=>$trait->{'written'}, 'spoken'=>$trait->{'spoken'}); } # Skills foreach $trait (@{$self->skills()}) { $writer->startTag('skill' , 'name'=>$trait->{'name'} , 'optspec'=>$trait->{'optspec'} , 'difficulty'=>$trait->{'difficulty'} , 'baseatt'=>$trait->{'baseatt'}, 'points'=>$trait->{'points'}, 'defaultbase'=>$trait->{'defaultbase'}, 'defaultoffset'=>$trait->{'defaultoffset'}); foreach $mod (@{$trait->{'bonus'}}) { $writer->emptyTag('bonus' , 'val'=>$mod->[0], 'description'=>$mod->[1]); } $writer->endTag('skill'); } # Techniques foreach $trait (@{$self->techniques()}) { $writer->emptyTag('technique' , 'name'=>$trait->{'name'}, 'difficulty'=>$trait->{'difficulty'}, 'skill'=>$trait->{'skill'}, 'skilloffset'=>$trait->{'skilloffset'}, 'points'=>$trait->{'points'}); } # Equipment foreach $trait (@{$self->equipment()}) { $writer->emptyTag('equipment' , 'number'=>$trait->{'number'}, 'name'=>$trait->{'name'}, 'location'=>$trait->{'location'}, 'weight'=>$trait->{'weight'}, 'cost'=>$trait->{'cost'}, 'carried'=>$trait->{'carried'}); } # Armor $trait=$self->armor(); $writer->emptyTag('armor', 'torso'=>$trait->{'torso'}, 'skull'=>$trait->{'skull'}, 'eyes'=>$trait->{'eyes'}, 'face'=>$trait->{'face'}, 'neck'=>$trait->{'neck'}, 'head'=>$trait->{'head'}, 'larm'=>$trait->{'larm'}, 'rarm'=>$trait->{'rarm'}, 'lhand'=>$trait->{'lhand'}, 'rhand'=>$trait->{'rhand'}, 'lleg'=>$trait->{'lleg'}, 'rleg'=>$trait->{'rleg'}, 'lfoot'=>$trait->{'lfoot'}, 'rfoot'=>$trait->{'rfoot'}); # Weapons foreach $trait (@{$self->weapons()}) { $writer->startTag('weapon' , 'name'=>$trait->{'weapon'}, 'damage'=>$trait->{'damage'}, 'st'=>$trait->{'st'} , 'reach'=>$trait->{'reach'}, 'skill'=>$trait->{'skill'} , 'parry'=>$trait->{'parry'}); $writer->characters($trait->{'notes'}); $writer->endTag('weapon'); } # RangedWeapons foreach $trait (@{$self->rangedweapons()}) { $writer->startTag('rangedweapon' , 'name'=>$trait->{'weapon'}, 'damage'=>$trait->{'damage'}, 'acc'=>$trait->{'acc'},'range'=>$trait->{'range'}, 'rof'=>$trait->{'rof'},'shots'=>$trait->{'shots'}, 'st'=>$trait->{'st'},'bulk'=>$trait->{'bulk'}, 'rcl'=>$trait->{'rcl'}); $writer->characters($trait->{'notes'}); $writer->endTag('rangedweapon'); } # Done $writer->endTag('character'); $writer->end(); $ofp->close(); $self->{'filename'} = $filename; } # ********************************************************************** # readXML sub readXML { my $self=shift; my $filename=shift; my ($twig,$root,$el,$trait,@traits,$mod,@mods,$mods,$notes,$thing,@things); $twig=XML::Twig->new(); $twig->parsefile($filename); $root=$twig->root(); $self->setname($root->att('name')); $self->setunspentpoints($root->att('unspentpoints')); $el=$root->first_child('soundbyte'); $self->setsoundbyte($el->text()); $el=$root->first_child('keywords'); $self->setkeywords($el->text()); $el=$root->first_child('description'); $self->setdescription($el->text()); $el=$root->first_child('personality'); $self->setpersonality($el->text()); $el=$root->first_child('background'); $self->setbackground($el->text()); $el=$root->first_child('notes'); $self->setnotes($el->text()); $el=$root->first_child('st'); $self->setst($el->att('val')); $self->settemplst($el->att('templ')); $el=$root->first_child('dx'); $self->setdx($el->att('val')); $self->settempldx($el->att('templ')); $el=$root->first_child('iq'); $self->setiq($el->att('val')); $self->settempliq($el->att('templ')); $el=$root->first_child('ht'); $self->setht($el->att('val')); $self->settemplht($el->att('templ')); $el=$root->first_child('hp'); $self->setextrahp($el->att('extra')); $self->settemplextrahp($el->att('templextra')); $self->setcurhp($el->att('cur')); $el=$root->first_child('fat'); $self->setextrafat($el->att('extra')); $self->settemplextrafat($el->att('templextra')); $self->setcurfat($el->att('cur')); $el=$root->first_child('will'); $self->setextrawill($el->att('extra')); $self->settemplextrawill($el->att('templextra')); $el=$root->first_child('per'); $self->setextraper($el->att('extra')); $self->settemplextraper($el->att('templextra')); $el=$root->first_child('move'); $self->setextramove($el->att('extra')); $self->settemplextramove($el->att('templextra')); $el=$root->first_child('speed'); $self->setextraspeed($el->att('extra')); $self->settemplextraspeed($el->att('templextra')); $el=$root->first_child('sm'); $self->setsm($el->att('val')); ($el=$root->first_child('dodge')) && ($self->setdodgebonus($el->att('bonus'))); ($el=$root->first_child('parry')) && ($self->setparry($el->att('val'))); ($el=$root->first_child('block')) && ($self->setblock($el->att('val'))); #Advantages, Disadvantages, and Quirks foreach $thing (('advantage','disadvantage','quirk')) { @traits=$root->children($thing); foreach $trait (@traits){ $mods=[]; $notes=[]; @mods=$trait->children('modifier'); foreach $mod (@mods) { push @$mods, {'name'=>$mod->att('name'), 'modifier'=>$mod->att('value')}; } @mods=$trait->children('notes'); foreach $mod (@mods) { push @$notes,$mod->text(); } $self->addtrait($thing.'s' , $trait->att('name'), $trait->att('basepoints'),$mods,$notes); } } # Cultures and Languages @traits=$root->children('language'); foreach $trait (@traits) { $self->addlanguage($trait->att('name'),$trait->att('primary'), $trait->att('written'),$trait->att('spoken')); } # Skills @traits=$root->children('skill'); foreach $trait (@traits) { $mods=[]; @mods=$trait->children('bonus'); foreach $mod (@mods) { push @$mods,[$mod->att('val'),$mod->att('description')]; } $self->addskill($trait->att('name'),$trait->att('optspec'), $trait->att('difficulty'),$trait->att('baseatt'), $trait->att('points'),$trait->att('defaultbase'), $trait->att('defaultoffset'),$mods); } # Techniques @things=(); @traits=$root->children('technique'); foreach $trait (@traits) { $thing={ 'name' => $trait->att('name'), 'difficulty' => $trait->att('difficulty'), 'skill' => $trait->att('skill'), 'skilloffset' => $trait->att('skilloffset'), 'points' => $trait->att('points'), 'level' => '' }; push @things,$thing; } $self->settechniques([@things]); # Equipment @things=(); @traits=$root->children('equipment'); foreach $trait (@traits) { $thing={ 'number' => $trait->att('number'), 'name' => $trait->att('name'), 'location' => $trait->att('location'), 'weight' => $trait->att('weight'), 'cost' => $trait->att('cost'), 'carried' => $trait->att('carried') }; push @things,$thing; } $self->setequipment([@things]); # Armor $el=$root->first_child('armor'); $trait={}; foreach $thing ('torso','skull','eyes','face','neck', 'head','larm','rarm','lhand','rhand', 'lleg','rleg','lfoot','rfoot') { $trait->{$thing}=$el->att($thing); } $self->setarmor($trait); # Weapons @things=(); @traits=$root->children('weapon'); foreach $trait (@traits) { $thing={ 'weapon' => $trait->att('name'), 'damage' => $trait->att('damage'), 'st' => $trait->att('st'), 'reach' => $trait->att('reach'), 'skill' => $trait->att('skill'), 'parry' => $trait->att('parry'), 'notes' => $trait->text() }; push @things,$thing; } $self->setweapons([@things]); # Rangedweapons @things=(); @traits=$root->children('rangedweapon'); foreach $trait (@traits) { $thing={ 'weapon' => $trait->att('name'), 'damage' => $trait->att('damage'), 'acc' => $trait->att('acc'), 'range' => $trait->att('range'), 'rof' => $trait->att('rof'), 'shots' => $trait->att('shots'), 'st' => $trait->att('st'), 'bulk' => $trait->att('bulk'), 'rcl' => $trait->att('rcl'), 'notes' => $trait->text() }; push @things,$thing; } $self->setrangedweapons([@things]); # Done $self->{'filename'}=$filename; $self->calccharvalue(); $root=''; $twig=''; } # ********************************************************************** # writeText sub writeText { my $self=shift; my $filename=shift; my ($text,$total,$atts,$ads,$disads,$quirks,$langs,$skills,$techniques); my ($trait,$mod,$which,$val1,$val2,%val,$skill); my ($name,$loc); open OFP,">$filename"; print OFP $self->{'name'}."\n\n"; print OFP $self->{'soundbyte'}."\n\n"; $text=¶graphfill($self->{'keywords'},72,' '); substr $text,0,9,'keywords:'; print OFP "$text\n\n"; $self->calccharvalue(); printf OFP "Attributes : %5d\n",$self->{'attpoints'}; printf OFP "Advantages : %5d\n",$self->{'adpoints'}; printf OFP "Disadvantages : %5d\n",$self->{'disadpoints'}; printf OFP "Quirks : %5d\n",$self->{'quirkpoints'}; printf OFP "Languages : %5d\n",$self->{'langpoints'}; printf OFP "Skills : %5d\n",$self->{'skillpoints'}; printf OFP "Techniques : %5d\n",$self->{'techniquepoints'}; printf OFP "---------------------\n"; printf OFP "TOTAL : %5d\n\n",$self->{'totalpoints'}; printf OFP "Unspent Points : ".$self->{'unspentpoints'}."\n\n"; printf OFP "ST : %3d [%3d] HP : %3d [%3d] (Current: %3d)\n", $self->{'st'},$self->{'stcost'},$self->hp(),$self->{'hpcost'}, $self->curhp(); printf OFP "DX : %3d [%3d] Will : %3d [%3d]\n",$self->{'dx'}, $self->{'dxcost'},$self->will(),$self->{'willcost'}; printf OFP "IQ : %3d [%3d] Per : %3d [%3d]\n",$self->{'iq'}, $self->{'iqcost'},$self->per(),$self->{'percost'}; printf OFP "HT : %3d [%3d] Fat : %3d [%3d] (Current: %3d)\n", $self->{'ht'},$self->{'htcost'},$self->fat(),$self->{'fatcost'}, $self->curfat(); print OFP "\nSize Modifier: ".$self->{'sm'}." ". "Basic Lift: ".$self->basiclift()."\n\n"; printf OFP "Basic Speed: %.2f [%d] (%f extra, %d from templ)\n", $self->speed(),$self->speedcost(),$self->extraspeed(), $self->templextraspeed(); ($val1,$val2)=$self->encumbrance(); printf OFP "Current Move: %d [%d] (%d extra, %d from templ, -%d for %.2f". "lbs carried)\n\n",$self->move(),$self->movecost(),$self->extramove(), $self->templextramove(),$val1,$val2; # Dodge / Parry / Block printf OFP "Dodge: %d (%d bonus, -%d for %.2f lbs carried)\n", $self->dodge(),$self->dodgebonus(),$val1,$val2; print OFP "Parry: ".$self->parry()."\n"; print OFP "Block: ".$self->block()."\n\n"; # Armor print OFP "Armor DR\n=======\n"; %val=%{$self->armor()}; printf OFP "torso: %5s groin: %5s lleg: %5s\n", $val{'torso'},$val{'head'},$val{'lleg'}; printf OFP "skull: %5s larm: %5s rleg: %5s\n", $val{'skull'},$val{'larm'},$val{'rleg'}; printf OFP " eyes: %5s rarm: %5s lfoot: %5s\n", $val{'eyes'},$val{'rarm'},$val{'lfoot'}; printf OFP " face: %5s lhand: %5s rfoot: %5s\n", $val{'face'},$val{'lhand'},$val{'rfoot'}; printf OFP " neck: %5s rhand: %5s\n\n", $val{'neck'},$val{'rhand'}; # Languages print OFP "Languages\n=========\n"; foreach $trait (@{$self->{'languages'}}) { printf OFP $trait->{'name'}." (Spoken: ". ('None','Broken','Accented','Native')[$trait->{'spoken'}]. " ; Written: ". ('None','Broken','Accented','Native')[$trait->{'written'}]. ") [".$trait->{'cost'}."]\n"; } print OFP "\n"; # Ads/Disads/Quirks foreach $which ('advantages','disadvantages','quirks') { print OFP ucfirst($which)."\n"; print OFP "=" x length($which); print OFP "\n"; foreach $trait (@{$self->{$which}}) { printf OFP "%4d ",$trait->{'cost'}; print OFP $trait->{'name'}; print OFP "\n"; foreach $mod (@{$trait->{'modifiers'}}) { print OFP " ".(($mod->{'modifier'}>0) ? '+' : ''). $mod->{'modifier'}."%: ". $mod->{'name'}."\n"; } foreach $mod (@{$trait->{'notes'}}) { $text=¶graphfill($mod,70,' '); substr $text,0,2,''; print OFP "$text"; } } print OFP "\n"; } # Skills print OFP "Skills\n======\n"; foreach $skill (@{$self->skills()}) { if ($skill->{'name'}!~/^ *$/) { printf OFP "%4d (%1s)%s%+d %s-%d", $skill->{'points'},uc($skill->{'difficulty'}),$skill->{'baseatt'}, $skill->{'relrank'},$skill->{'name'},$skill->{'level'}; if ($skill->{'defaulting'}) { printf OFP " (Def: %s%+d)",$skill->{'defaultbase'}, $skill->{'defaultoffset'}; } if (@{$skill->{'bonus'}}) { print OFP " ("; foreach $mod (0..$#{$skill->{'bonus'}}) { printf OFP "%+d, %s", $skill->{'bonus'}->[$mod]->[0], $skill->{'bonus'}->[$mod]->[1]; if ($mod!=$#{$skill->{'bonus'}}) { print OFP "; "; } } print OFP ")"; } } print OFP "\n"; } print OFP "\n"; # Techniques print OFP "Techniques\n==========\n"; foreach $skill (@{$self->techniques()}) { if ($skill->{'name'}!~/^ *$/) { printf OFP "%4d (%1s) %s-%d [%s%+d]", $skill->{'points'},uc($skill->{'difficulty'}),$skill->{'name'}, $skill->{'level'},$skill->{'skill'},$skill->{'skilloffset'}; } print OFP "\n"; } print OFP "\n"; # Weapons print OFP "Weapons Damage ST Reach Skill Parry\n"; print OFP "=============================================================================\n"; foreach $val1 (@{$self->weapons()}) { printf OFP "%-20s %-10s %2d %5s %5d %5d %s\n", $val1->{'weapon'},$val1->{'damage'},$val1->{'st'}, $val1->{'reach'},$val1->{'skill'},$val1->{'parry'}, $val1->{'notes'}; } print OFP "\n"; # Ranged Weapons print OFP "Ranged Weapon Damage Acc Range RoF Shots ST Bulk Rcl\n"; print OFP "==============================================================================\n"; foreach $val1 (@{$self->rangedweapons()}) { printf OFP "%-20s %10s %3d %7s %4d %5s %2d %4d %3d %s\n", $val1->{'weapon'},$val1->{'damage'},$val1->{'acc'},$val1->{'range'}, $val1->{'rof'},$val1->{'shots'},$val1->{'st'},$val1->{'bulk'}, $val1->{'rcl'},$val1->{'notes'}; } print OFP "\n"; # Equipment print OFP "Num Equipment Location Wt/ea \$/ea Carried?\n"; print OFP "=================================================================================================================\n"; foreach $val1 (@{$self->{'equipment'}}) { $name = $val1->{'name'}; if (length($name) > 64) { $name = substr($name, 0, 64); } $loc = $val1->{'location'}; if (length($loc) > 16) { $loc = substr($name, 0, 16); } printf OFP "%-3d %-64s %-16s %7.2f %10.2f %1s\n", $val1->{'number'},$val1->{'name'},$val1->{'location'}, $val1->{'weight'},$val1->{'cost'}, $val1->{'carried'}?'X':' '; } ($val1,$val2)=$self->encumbrance(); printf OFP "\nTotal Weight Carried: %.2f (encumb: -%d)\n",$val2,$val1; printf OFP "Total Equipment Budget: \$%.2f\n\n",$self->equipcost(); # Lots of descriptive text and notes print OFP "Description:\n"; print OFP ¶graphfill($self->{'description'},72,' '); print OFP "\n"; print OFP "Personality:\n"; print OFP ¶graphfill($self->{'personality'},72,' '); print OFP "\n"; print OFP "Background:\n"; print OFP ¶graphfill($self->{'background'},72,' '); print OFP "\n"; print OFP "Notes:\n"; print OFP ¶graphfill($self->{'notes'},72,' '); print OFP "\n"; close OFP; } # ********************************************************************** # paragraphfill takes text which may or may not have newlines or \r's, # replaces them all with spaces, and then returns a block of text # with \n's such that no line is longer than 72 characters (if possible). # Note that all double spaces get converted to a single space! This may or # may not be a tragedy, but I'm leaning towards not. # # This *does* split paragraphs with a \n\n where it currently finds a \n\s*\n sub paragraphfill { my $intext=shift; my $fillcol=shift; my $prefix=""; if (@_) { $prefix=shift; } my $firstprefix=$prefix; if (@_) { $firstprefix=shift; } my ($text,@texts,@words,$line,$outtext,$word); @texts=split(/\n\s*\n/,$intext); $outtext=''; foreach $text (@texts) { if ($outtext) { $outtext.="\n"; } $text=~s/\r\s*/ /g; $text=~s/\n\s*/ /g; @words=split(' ',$text); # Will nuke double-spaces .... $line=$firstprefix; while (defined($word=shift(@words))) { if (length($line)>0 && (length($line)+length($word)>$fillcol-1)) { if (substr($line,-1,1) eq ' ') { chop $line; } $outtext.=$line."\n"; $line=$prefix; } $line.=$word." "; } if ($line) { if (substr($line,-1,1) eq ' ') { chop $line; } $outtext.=$line."\n"; $line=$prefix; } } $outtext; } # ********************************************************************** # ********************************************************************** # ********************************************************************** # Character Interface # ********************************************************************** # ********************************************************************** # ********************************************************************** package GTKChar::CharWin; sub new { my $type=shift; my $char=shift; my ($mainvbox,$vbox,$hbox,$but,$lab,$nb,$pagevbox,$pagehbox,$wid); my ($tab,$lbs,$encumbrance,$n,$iter,$lang,$list,$val,$scrl,$row,$col); my ($menu,$menubar,$menuitem); my $self={}; bless $self,'GTKChar::CharWin'; $self->{'aboutboxmade'}=''; $self->{'char'}=$char; $self->{'win'}=Gtk2::Window->new(); $self->{'win'}->signal_connect(destroy_event=>sub { $self->closeWin() }); $self->{'win'}->signal_connect(delete_event=>sub { $self->closeWin() }); $mainvbox=Gtk2::VBox->new(); $self->{'win'}->add($mainvbox); $mainvbox->show(); # **************************************** # Menu Bar $menubar=Gtk2::MenuBar->new(); $mainvbox->pack_start($menubar,0,0,0); $menubar->show(); $menuitem=Gtk2::MenuItem->new("File"); $menu=Gtk2::Menu->new(); $menuitem->set_submenu($menu); $menubar->append($menuitem); $menuitem->show(); $menuitem=Gtk2::MenuItem->new("New"); $menu->append($menuitem); $menuitem->show(); $menuitem->signal_connect(activate => sub { my $char=new GTKChar::Char; my $win=new GTKChar::CharWin($char); }); $menuitem=Gtk2::MenuItem->new("Open"); $menu->append($menuitem); $menuitem->show(); $menuitem->signal_connect(activate => sub { $self->loadChar(); }); $menuitem=Gtk2::SeparatorMenuItem->new(); $menu->append($menuitem); $menuitem->show(); $menuitem=Gtk2::MenuItem->new("Save"); $menu->append($menuitem); $menuitem->show(); $menuitem->signal_connect(activate => \&saveChar , $self); $menuitem=Gtk2::MenuItem->new("Text Export"); $menu->append($menuitem); $menuitem->show(); $menuitem->signal_connect(activate => \&textExport , $self); $menuitem=Gtk2::SeparatorMenuItem->new(); $menu->append($menuitem); $menuitem->show(); $menuitem=Gtk2::MenuItem->new("Close"); $menu->append($menuitem); $menuitem->show(); $menuitem->signal_connect(activate => sub { $self->closeWin(); }); $menuitem=Gtk2::MenuItem->new("Quit"); $menu->append($menuitem); $menuitem->show(); $menuitem->signal_connect(activate => sub { exit; }); $menuitem=Gtk2::MenuItem->new("Help"); $menu=Gtk2::Menu->new(); $menuitem->set_submenu($menu); $menubar->append($menuitem); $menuitem->show(); $menuitem=Gtk2::MenuItem->new("About"); $menu->append($menuitem); $menuitem->show(); $menuitem->signal_connect(activate => sub { $self->aboutbox(); }); # **************************************** # Name, Soundbyte $hbox=Gtk2::HBox->new(); $mainvbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Name:"); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'name'}=Gtk2::Entry->new(); $self->{'name'}->append_text($char->name()); $hbox->pack_start($self->{'name'},1,1,0); $self->{'name'}->show(); $but=Gtk2::Button->new("Update"); $but->signal_connect(clicked => \&updateEverything , $self); $hbox->pack_start($but,0,0,0); $but->show(); $hbox=Gtk2::HBox->new(); $mainvbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Soundbyte:"); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'soundbyte'}=Gtk2::Entry->new(); $self->{'soundbyte'}->append_text($char->soundbyte()); $hbox->pack_start($self->{'soundbyte'},1,1,0); $self->{'soundbyte'}->show(); # Notebook $nb=Gtk2::Notebook->new(); $mainvbox->pack_start($nb,1,1,0); $nb->show(); # **************************************** # Page of basic info $pagehbox=Gtk2::HBox->new(); $nb->append_page($pagehbox,"General"); $pagehbox->show(); $vbox=Gtk2::VBox->new(); $pagehbox->pack_start($vbox,1,1,0); $vbox->show(); $lab=Gtk2::Label->new("Description"); $vbox->pack_start($lab,0,0,0); $lab->show(); $wid=Gtk2::ScrolledWindow->new(); $vbox->pack_start($wid,1,1,0); $wid->set_policy("GTK_POLICY_NEVER","GTK_POLICY_ALWAYS"); $wid->show(); $self->{'description'}=Gtk2::TextView->new(); $self->{'description'}->set_wrap_mode("GTK_WRAP_WORD"); $wid->add($self->{'description'}); $self->{'description'}->show(); $self->{'description'}->get_buffer()->set_text($char->description()); $lab=Gtk2::Label->new("Personality"); $vbox->pack_start($lab,0,0,0); $lab->show(); $wid=Gtk2::ScrolledWindow->new(); $vbox->pack_start($wid,1,1,0); $wid->set_policy("GTK_POLICY_NEVER","GTK_POLICY_ALWAYS"); $wid->show(); $self->{'personality'}=Gtk2::TextView->new(); $self->{'personality'}->set_wrap_mode("GTK_WRAP_WORD"); $self->{'personality'}->set_wrap_mode("GTK_WRAP_WORD"); $wid->add($self->{'personality'}); $self->{'personality'}->show(); $self->{'personality'}->get_buffer()->set_text($char->personality()); $lab=Gtk2::Label->new("Background"); $vbox->pack_start($lab,0,0,0); $lab->show(); $wid=Gtk2::ScrolledWindow->new(); $vbox->pack_start($wid,1,1,0); $wid->set_policy("GTK_POLICY_NEVER","GTK_POLICY_ALWAYS"); $wid->show(); $self->{'background'}=Gtk2::TextView->new(); $self->{'background'}->set_wrap_mode("GTK_WRAP_WORD"); $wid->add($self->{'background'}); $self->{'background'}->show(); $self->{'background'}->get_buffer()->set_text($char->background()); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Keywords:"); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'keywords'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'keywords'},1,1,0); $self->{'keywords'}->show(); $self->{'keywords'}->append_text($char->keywords()); $vbox=Gtk2::VBox->new(); $pagehbox->pack_start($vbox,0,0,0); $vbox->show(); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Atts: "); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'atttotal'}=Gtk2::Label->new($char->{'attpoints'}); $hbox->pack_start($self->{'atttotal'},0,0,0); $self->{'atttotal'}->show(); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Ads: "); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'adtotal'}=Gtk2::Label->new($char->{'adpoints'}); $hbox->pack_start($self->{'adtotal'},0,0,0); $self->{'adtotal'}->show(); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Disad: "); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'disadtotal'}=Gtk2::Label->new($char->{'disadpoints'}); $hbox->pack_start($self->{'disadtotal'},0,0,0); $self->{'disadtotal'}->show(); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Quirk: "); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'quirktotal'}=Gtk2::Label->new($char->{'quirkpoints'}); $hbox->pack_start($self->{'quirktotal'},0,0,0); $self->{'quirktotal'}->show(); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Lang: "); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'langtotal'}=Gtk2::Label->new($char->{'langpoints'}); $hbox->pack_start($self->{'langtotal'},0,0,0); $self->{'langtotal'}->show(); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Skill: "); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'skilltotal'}=Gtk2::Label->new($char->{'skillpoints'}); $hbox->pack_start($self->{'skilltotal'},0,0,0); $self->{'skilltotal'}->show(); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Tchnq: "); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'techniquetotal'}=Gtk2::Label->new($char->{'techniquepoints'}); $hbox->pack_start($self->{'techniquetotal'},0,0,0); $self->{'techniquetotal'}->show(); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("TOTAL: "); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'total'}=Gtk2::Label->new($char->{'totalpoints'}); $hbox->pack_start($self->{'total'},0,0,0); $self->{'total'}->show(); $self->{'unspent'}=Gtk2::Entry->new(); $self->{'unspent'}->append_text($char->unspentpoints()); $vbox->pack_end($self->{'unspent'},0,0,0); $self->{'unspent'}->show(); $lab=Gtk2::Label->new("Unspent Points"); $vbox->pack_end($lab,0,0,0); $lab->show(); # **************************************** # Page of Attributes, Langauges, and Armor DR $pagehbox=Gtk2::HBox->new(); $nb->append_page($pagehbox,"Attributes"); $pagehbox->show(); $pagevbox=Gtk2::VBox->new(); $pagehbox->pack_start($pagevbox,1,1,4); $pagevbox->show(); $tab=Gtk2::Table->new(6,10); $pagevbox->pack_start($tab,0,0,0); $tab->show(); $lab=Gtk2::Label->new("ST:"); $tab->attach($lab,0,1,2,3,'fill','fill',2,2); $lab->show(); $lab=Gtk2::Label->new("DX:"); $tab->attach($lab,0,1,3,4,'fill','fill',2,2); $lab->show(); $lab=Gtk2::Label->new("IQ:"); $tab->attach($lab,0,1,4,5,'fill','fill',2,2); $lab->show(); $lab=Gtk2::Label->new("HT:"); $tab->attach($lab,0,1,5,6,'fill','fill',2,2); $lab->show(); $self->{'st'}=Gtk2::Entry->new(); $tab->attach($self->{'st'},1,2,2,3,'fill','fill',2,2); $self->{'st'}->show(); $self->{'st'}-> set_size_request($self->{'st'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'st'}->append_text($char->st()); $self->{'st'}->signal_connect(activate => \&updateEverything, $self); $self->{'dx'}=Gtk2::Entry->new(); $tab->attach($self->{'dx'},1,2,3,4,'fill','fill',2,2); $self->{'dx'}->show(); $self->{'dx'}-> set_size_request($self->{'dx'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'dx'}->append_text($char->dx()); $self->{'dx'}->signal_connect(activate => \&updateEverything, $self); $self->{'iq'}=Gtk2::Entry->new(); $tab->attach($self->{'iq'},1,2,4,5,'fill','fill',2,2); $self->{'iq'}->show(); $self->{'iq'}-> set_size_request($self->{'iq'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'iq'}->append_text($char->iq()); $self->{'iq'}->signal_connect(activate => \&updateEverything, $self); $self->{'ht'}=Gtk2::Entry->new(); $tab->attach($self->{'ht'},1,2,5,6,'fill','fill',2,2); $self->{'ht'}->show(); $self->{'ht'}-> set_size_request($self->{'ht'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'ht'}->append_text($char->ht()); $self->{'ht'}->signal_connect(activate => \&updateEverything, $self); $self->{'stpoints'}=Gtk2::Label->new("[".$char->stcost()."]"); $tab->attach($self->{'stpoints'},2,3,2,3,'fill','fill',2,2); $self->{'stpoints'}->show(); $self->{'dxpoints'}=Gtk2::Label->new("[".$char->dxcost."]"); $tab->attach($self->{'dxpoints'},2,3,3,4,'fill','fill',2,2); $self->{'dxpoints'}->show(); $self->{'iqpoints'}=Gtk2::Label->new("[".$char->iqcost."]"); $tab->attach($self->{'iqpoints'},2,3,4,5,'fill','fill',2,2); $self->{'iqpoints'}->show(); $self->{'htpoints'}=Gtk2::Label->new("[".$char->htcost."]"); $tab->attach($self->{'htpoints'},2,3,5,6,'fill','fill',2,2); $self->{'htpoints'}->show(); $lab=Gtk2::Label->new("Fm."); $tab->attach($lab,3,4,0,1,'fill','fill',2,2); $lab->show(); $lab=Gtk2::Label->new("Tmpl"); $tab->attach($lab,3,4,1,2,'fill','fill',2,2); $lab->show(); $self->{'templst'}=Gtk2::Entry->new(); $tab->attach($self->{'templst'},3,4,2,3,'fill','fill',2,2); $self->{'templst'}->show(); $self->{'templst'}-> set_size_request($self->{'templst'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'templst'}->append_text($char->templst()); $self->{'templst'}->signal_connect(activate => \&updateEverything, $self); $self->{'templdx'}=Gtk2::Entry->new(); $tab->attach($self->{'templdx'},3,4,3,4,'fill','fill',2,2); $self->{'templdx'}->show(); $self->{'templdx'}-> set_size_request($self->{'templdx'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'templdx'}->append_text($char->templdx()); $self->{'templdx'}->signal_connect(activate => \&updateEverything, $self); $self->{'templiq'}=Gtk2::Entry->new(); $tab->attach($self->{'templiq'},3,4,4,5,'fill','fill',2,2); $self->{'templiq'}->show(); $self->{'templiq'}-> set_size_request($self->{'templiq'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'templiq'}->append_text($char->templiq()); $self->{'templiq'}->signal_connect(activate => \&updateEverything, $self); $self->{'templht'}=Gtk2::Entry->new(); $tab->attach($self->{'templht'},3,4,5,6,'fill','fill',2,2); $self->{'templht'}->show(); $self->{'templht'}-> set_size_request($self->{'templht'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'templht'}->append_text($char->templht()); $self->{'templht'}->signal_connect(activate => \&updateEverything, $self); $lab=Gtk2::Label->new("HP:"); $tab->attach($lab,4,5,2,3,'fill','fill',2,2); $lab->show(); $lab=Gtk2::Label->new("Will:"); $tab->attach($lab,4,5,3,4,'fill','fill',2,2); $lab->show(); $lab=Gtk2::Label->new("Per:"); $tab->attach($lab,4,5,4,5,'fill','fill',2,2); $lab->show(); $lab=Gtk2::Label->new("Fat:"); $tab->attach($lab,4,5,5,6,'fill','fill',2,2); $lab->show(); $self->{'hp'}=Gtk2::Label->new($char->st()+$char->extrahp()); $tab->attach($self->{'hp'},5,6,2,3,'fill','fill',2,2); $self->{'hp'}->show(); $self->{'will'}=Gtk2::Label->new($char->iq()+$char->extrawill()); $tab->attach($self->{'will'},5,6,3,4,'fill','fill',2,2); $self->{'will'}->show(); $self->{'per'}=Gtk2::Label->new($char->iq()+$char->extraper()); $tab->attach($self->{'per'},5,6,4,5,'fill','fill',2,2); $self->{'per'}->show(); $self->{'fat'}=Gtk2::Label->new($char->ht()+$char->extrafat()); $tab->attach($self->{'fat'},5,6,5,6,'fill','fill',2,2); $self->{'fat'}->show(); $lab=Gtk2::Label->new("Extra"); $tab->attach($lab,6,7,1,2,'fill','fill',2,2); $lab->show(); $self->{'extrahp'}=Gtk2::Entry->new(); $tab->attach($self->{'extrahp'},6,7,2,3,'fill','fill',2,2); $self->{'extrahp'}->show(); $self->{'extrahp'}-> set_size_request($self->{'extrahp'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'extrahp'}->append_text($char->extrahp()); $self->{'extrahp'}->signal_connect(activate => \&updateEverything, $self); $self->{'extrawill'}=Gtk2::Entry->new(); $tab->attach($self->{'extrawill'},6,7,3,4,'fill','fill',2,2); $self->{'extrawill'}->show(); $self->{'extrawill'}-> set_size_request($self->{'extrawill'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'extrawill'}->append_text($char->extrawill()); $self->{'extrawill'}->signal_connect(activate => \&updateEverything, $self); $self->{'extraper'}=Gtk2::Entry->new(); $tab->attach($self->{'extraper'},6,7,4,5,'fill','fill',2,2); $self->{'extraper'}->show(); $self->{'extraper'}-> set_size_request($self->{'extraper'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'extraper'}->append_text($char->extraper()); $self->{'extraper'}->signal_connect(activate => \&updateEverything, $self); $self->{'extrafat'}=Gtk2::Entry->new(); $tab->attach($self->{'extrafat'},6,7,5,6,'fill','fill',2,2); $self->{'extrafat'}->show(); $self->{'extrafat'}-> set_size_request($self->{'extrafat'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'extrafat'}->append_text($char->extrafat()); $self->{'extrafat'}->signal_connect(activate => \&updateEverything, $self); $self->{'hppoints'}=Gtk2::Label->new("[".$char->hpcost()."]"); $tab->attach($self->{'hppoints'},7,8,2,3,'fill','fill',2,2); $self->{'hppoints'}->show(); $self->{'willpoints'}=Gtk2::Label->new("[".$char->willcost()."]"); $tab->attach($self->{'willpoints'},7,8,3,4,'fill','fill',2,2); $self->{'willpoints'}->show(); $self->{'perpoints'}=Gtk2::Label->new("[".$char->percost()."]"); $tab->attach($self->{'perpoints'},7,8,4,5,'fill','fill',2,2); $self->{'perpoints'}->show(); $self->{'fatpoints'}=Gtk2::Label->new("[".$char->fatcost()."]"); $tab->attach($self->{'fatpoints'},7,8,5,6,'fill','fill',2,2); $self->{'fatpoints'}->show(); $lab=Gtk2::Label->new("Fm."); $tab->attach($lab,8,9,0,1,'fill','fill',2,2); $lab->show(); $lab=Gtk2::Label->new("Tmpl"); $tab->attach($lab,8,9,1,2,'fill','fill',2,2); $lab->show(); $self->{'templextrahp'}=Gtk2::Entry->new(); $tab->attach($self->{'templextrahp'},8,9,2,3,'fill','fill',2,2); $self->{'templextrahp'}->show(); $self->{'templextrahp'}-> set_size_request($self->{'templextrahp'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'templextrahp'}->append_text($char->templextrahp()); $self->{'templextrahp'}->signal_connect(activate => \&updateEverything, $self); $self->{'templextrawill'}=Gtk2::Entry->new(); $tab->attach($self->{'templextrawill'},8,9,3,4,'fill','fill',2,2); $self->{'templextrawill'}->show(); $self->{'templextrawill'}-> set_size_request($self->{'templextrawill'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'templextrawill'}->append_text($char->templextrawill()); $self->{'templextrawill'}->signal_connect(activate => \&updateEverything, $self); $self->{'templextraper'}=Gtk2::Entry->new(); $tab->attach($self->{'templextraper'},8,9,4,5,'fill','fill',2,2); $self->{'templextraper'}->show(); $self->{'templextraper'}-> set_size_request($self->{'templextraper'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'templextraper'}->append_text($char->templextraper()); $self->{'templextraper'}->signal_connect(activate => \&updateEverything, $self); $self->{'templextrafat'}=Gtk2::Entry->new(); $tab->attach($self->{'templextrafat'},8,9,5,6,'fill','fill',2,2); $self->{'templextrafat'}->show(); $self->{'templextrafat'}-> set_size_request($self->{'templextrafat'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'templextrafat'}->append_text($char->templextrafat()); $self->{'templextrafat'}->signal_connect(activate => \&updateEverything, $self); $lab=Gtk2::Label->new("Current"); $tab->attach($lab,9,10,1,2,'fill','fill',2,2); $lab->show(); $self->{'curhp'}=Gtk2::Entry->new(); $tab->attach($self->{'curhp'},9,10,2,3,'fill','fill',2,2); $self->{'curhp'}->show(); $self->{'curhp'}-> set_size_request($self->{'curhp'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'curhp'}->append_text($char->curhp()); $self->{'curfat'}=Gtk2::Entry->new(); $tab->attach($self->{'curfat'},9,10,5,6,'fill','fill',2,2); $self->{'curfat'}->show(); $self->{'curfat'}-> set_size_request($self->{'curfat'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'curfat'}->append_text($char->curfat()); $hbox=Gtk2::HBox->new(); $pagevbox->pack_start($hbox,0,0,2); $hbox->show(); $lab=Gtk2::Label->new("Basic Speed:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'speed'}=Gtk2::Label->new($char->speed()); $hbox->pack_start($self->{'speed'},0,0,2); $self->{'speed'}->show(); $self->{'speedcost'}=Gtk2::Label->new("[".$char->speedcost()."]"); $hbox->pack_start($self->{'speedcost'},0,0,2); $self->{'speedcost'}->show(); $lab=Gtk2::Label->new("Extra:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'extraspeed'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'extraspeed'},0,0,2); $self->{'extraspeed'}->append_text($char->extraspeed()); $self->{'extraspeed'}->show(); $self->{'extraspeed'}-> set_size_request($self->{'extraspeed'}->get_pango_context()-> get_font_description()-> get_size() * 4 / Gtk2::Pango->scale); $self->{'extraspeed'}->signal_connect(activate => \&updateEverything, $self); $lab=Gtk2::Label->new("Fm. Tmpl:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'templextraspeed'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'templextraspeed'},0,0,2); $self->{'templextraspeed'}->append_text($char->templextraspeed()); $self->{'templextraspeed'}->show(); $self->{'templextraspeed'}-> set_size_request($self->{'templextraspeed'}->get_pango_context()-> get_font_description()-> get_size() * 4 / Gtk2::Pango->scale); $self->{'templextraspeed'}->signal_connect(activate => \&updateEverything, $self); $hbox=Gtk2::HBox->new(); $pagevbox->pack_start($hbox,0,0,2); $hbox->show(); $lab=Gtk2::Label->new("Cur. Move:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'move'}=Gtk2::Label->new($char->move()); $hbox->pack_start($self->{'move'},0,0,2); $self->{'move'}->show(); $lab=Gtk2::Label->new("Basic Move:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'basicmove'}=Gtk2::Label->new($char->basicmove()); $hbox->pack_start($self->{'basicmove'},0,0,2); $self->{'basicmove'}->show(); $self->{'movecost'}=Gtk2::Label->new("[".$char->movecost()."]"); $hbox->pack_start($self->{'movecost'},0,0,2); $self->{'movecost'}->show(); $lab=Gtk2::Label->new("Extra:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'extramove'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'extramove'},0,0,2); $self->{'extramove'}->append_text($char->extramove()); $self->{'extramove'}->show(); $self->{'extramove'}-> set_size_request($self->{'extramove'}->get_pango_context()-> get_font_description()-> get_size() * 2 / Gtk2::Pango->scale); $self->{'extramove'}->signal_connect(activate => \&updateEverything, $self); $lab=Gtk2::Label->new("Fm. Tmpl:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'templextramove'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'templextramove'},0,0,2); $self->{'templextramove'}->append_text($char->templextramove()); $self->{'templextramove'}->show(); $self->{'templextramove'}-> set_size_request($self->{'templextramove'}->get_pango_context()-> get_font_description()-> get_size() * 2 / Gtk2::Pango->scale); $self->{'templextramove'}->signal_connect(activate => \&updateEverything, $self); $hbox=Gtk2::HBox->new(); $pagevbox->pack_start($hbox,0,0,2); $hbox->show(); $lab=Gtk2::Label->new("Basic Lift:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'basiclift'}=Gtk2::Label->new($char->basiclift()); $hbox->pack_start($self->{'basiclift'},0,0,2); $self->{'basiclift'}->show(); ($encumbrance,$lbs)=$char->encumbrance(); $lab=Gtk2::Label->new("Lbs Carried:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'lbscarried'}=Gtk2::Label->new($lbs); $hbox->pack_start($self->{'lbscarried'},0,0,2); $self->{'lbscarried'}->show(); $lab=Gtk2::Label->new("Encumb.:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'encumbrance'}=Gtk2::Label->new($encumbrance); $hbox->pack_start($self->{'encumbrance'},0,0,2); $self->{'encumbrance'}->show(); $lab=Gtk2::Label->new("Size Mod:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'sizemod'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'sizemod'},0,0,2); $self->{'sizemod'}->append_text($char->sm()); $self->{'sizemod'}->show(); $self->{'sizemod'}-> set_size_request($self->{'sizemod'}->get_pango_context()-> get_font_description()-> get_size()*3 / Gtk2::Pango->scale); $self->{'sizemod'}->signal_connect(activate => \&updateEverything , $self); $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('automatic','always'); $pagevbox->pack_start($scrl,1,1,2); $scrl->show(); $self->{'langs'}=Gtk2::SimpleList->new('Language' => 'text', 'Spoken' => 'text', 'Written' => 'text', 'Cost' => 'int'); $scrl->add($self->{'langs'}); $self->{'langs'}->show(); $self->{'langs'}-> set_size_request(-1,$self->{'langs'}->get_pango_context()-> get_font_description()->get_size()*8 / Gtk2::Pango->scale); $self->{'langs'}->set_headers_visible(1); $self->{'langs'}->set_reorderable(1); $self->{'langs'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); $self->{'langs'}->get_column(0)->set_expand(1); $self->{'langs'}->get_column(1)->set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'langs'}->get_column(1)-> set_fixed_width($self->{'langs'}->get_pango_context()-> get_font_description()-> get_size() * 8 / Gtk2::Pango->scale); $self->{'langs'}->get_column(2)->set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'langs'}->get_column(2)-> set_fixed_width($self->{'langs'}->get_pango_context()-> get_font_description()-> get_size() * 8 / Gtk2::Pango->scale); $self->{'langs'}->get_column(3)->set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'langs'}->get_column(3)-> set_fixed_width($self->{'langs'}->get_pango_context()-> get_font_description()-> get_size() * 4 / Gtk2::Pango->scale); foreach $lang (@{$char->languages()}) { push @{$self->{'langs'}->{data}}, [$lang->{'name'}, ('None','Broken','Accented','Native')[$lang->{'spoken'}], ('None','Broken','Accented','Native')[$lang->{'written'}], $lang->{'cost'}]; } $hbox=Gtk2::HBox->new(); $pagevbox->pack_start($hbox,0,0,0); $hbox->show(); $but=Gtk2::Button->new("Add"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&addLang , $self); $but=Gtk2::Button->new("Edit"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&editLang , $self); $but=Gtk2::Button->new("Delete"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&deleteLang , $self); # Armor $pagevbox=Gtk2::VBox->new(); $pagehbox->pack_start($pagevbox,0,0,4); $pagevbox->show(); $tab=Gtk2::Table->new(4,8); $pagevbox->pack_start($tab,0,0,0); $tab->show(); $lab=Gtk2::Label->new("Armor DR"); $tab->attach($lab,0,2,0,1,'fill','fill',2,2); $lab->show(); $row=1; $col=0; foreach $val ('torso','skull','eyes','face','neck', 'head','larm','rarm','lhand','rhand', 'lleg','rleg','lfoot','rfoot') { if ($val eq 'head') { $lab=Gtk2::Label->new('groin'); #### HACK ALERT } else { $lab=Gtk2::Label->new($val); } $tab->attach($lab,$col,$col+1,$row,$row+1,'fill','fill',2,2); $lab->show(); $self->{"dr$val"}=Gtk2::Entry->new(); $self->{"dr$val"}->append_text($char->armor()->{$val}); $tab->attach($self->{"dr$val"},$col+1,$col+2,$row,$row+1, 'fill','fill',2,2); $self->{"dr$val"}->set_width_chars(4); $self->{"dr$val"}->show(); if (++$row>7) { $row=1; $col+=2; } } $hbox=Gtk2::HBox->new($val); $pagevbox->pack_start($hbox,0,0,4); $hbox->show(); $lab=Gtk2::Label->new("Dodge: "); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'dodge'}=Gtk2::Label->new($char->dodge()); $hbox->pack_start($self->{'dodge'},0,0,2); $self->{'dodge'}->show(); $hbox=Gtk2::HBox->new($val); $pagevbox->pack_start($hbox,0,0,2); $hbox->show(); $lab=Gtk2::Label->new("...dodge bonus:"); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'dodgebonus'}=Gtk2::Entry->new(); $self->{'dodgebonus'}->append_text($char->dodgebonus()); $self->{'dodgebonus'}->set_width_chars(3); $hbox->pack_start($self->{'dodgebonus'},0,0,2); $self->{'dodgebonus'}->show(); $self->{'dodgebonus'}->signal_connect(activate => \&updateEverything, $self); $hbox=Gtk2::HBox->new(); $pagevbox->pack_start($hbox,0,0,2); $hbox->show(); $lab=Gtk2::Label->new("Parry: "); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'parry'}=Gtk2::Entry->new(); $self->{'parry'}->append_text($char->parry()); $self->{'parry'}->set_width_chars(3); $hbox->pack_start($self->{'parry'},0,0,2); $self->{'parry'}->show(); $hbox=Gtk2::HBox->new(); $pagevbox->pack_start($hbox,0,0,2); $hbox->show(); $lab=Gtk2::Label->new("Block: "); $hbox->pack_start($lab,0,0,2); $lab->show(); $self->{'block'}=Gtk2::Entry->new(); $self->{'block'}->append_text($char->block()); $self->{'block'}->set_width_chars(3); $hbox->pack_start($self->{'block'},0,0,2); $self->{'block'}->show(); # **************************************** # Page of Ads/Disads/Quirks $pagehbox=Gtk2::HBox->new(); $nb->append_page($pagehbox,"Ad/Disad"); $pagehbox->show(); $vbox=Gtk2::VBox->new(); $pagehbox->pack_start($vbox,1,1,2); $vbox->show(); $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('automatic','always'); $vbox->pack_start($scrl,1,1,0); $scrl->show(); # $self->{'advantages'}=Gtk2::SimpleList->new('Cost' => 'text', # 'Advantages' => 'text'); # $scrl->add($self->{'advantages'}); # $self->{'advantages'}->show(); # $self->{'advantages'}->set_headers_visible(1); # $self->{'advantages'}->get_column(0)-> # set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); # $self->{'advantages'}->get_column(0)-> # set_fixed_width($self->{'advantages'}->get_pango_context()-> # get_font_description()-> # get_size() * 3 / Gtk2::Pango->scale); # $self->{'advantages'}->get_column(1)-> # set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); # $self->{'advantages'}->get_column(1)->set_expand(1); # $self->filladwid($self->{'advantages'},$char->advantages()); $self->{'adstore'}=Gtk2::TreeStore->new('Glib::String','Glib::String'); $self->{'rendtext'}=Gtk2::CellRendererText->new; $self->{'advantages'}=Gtk2::TreeView->new_with_model($self->{'adstore'}); $self->{'advantages'}-> append_column(Gtk2::TreeViewColumn-> new_with_attributes("Cost",$self->{'rendtext'}, text=>0)); $self->{'advantages'}-> append_column(Gtk2::TreeViewColumn-> new_with_attributes("Advantage",$self->{'rendtext'}, text=>1)); $scrl->add($self->{'advantages'}); $self->{'advantages'}->show(); $self->{'advantages'}->set_reorderable(1); $self->{'advantages'}->set_headers_visible(0); $self->{'advantages'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'advantages'}->get_column(0)-> set_fixed_width($self->{'advantages'}->get_pango_context()-> get_font_description()-> get_size() * 5 / Gtk2::Pango->scale); $self->{'advantages'}->get_column(1)-> set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); $self->{'advantages'}->get_column(1)->set_expand(1); $self->filladwid($self->{'adstore'},$char->advantages()); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $self->{'newadcost'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newadcost'},0,0,2); $self->{'newadcost'}->show(); $self->{'newadcost'}-> set_size_request($self->{'newadcost'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale,-1); $self->{'newad'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newad'},1,1,2); $self->{'newad'}->show(); $self->{'newad'}->signal_connect(activate => \&addAdvantage, $self); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $but=Gtk2::Button->new("Add"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&addAdvantage , $self); $but=Gtk2::Button->new("Edit"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&editAdvantage , $self); $but=Gtk2::Button->new("Delete"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&removeAdvantage , $self); $vbox=Gtk2::VBox->new(); $pagehbox->pack_start($vbox,1,1,2); $vbox->show(); $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('automatic','always'); $vbox->pack_start($scrl,1,1,0); $scrl->show(); # $self->{'disadvantages'}=Gtk2::SimpleList->new('Cost' => 'text', # 'Disadvantages' => 'text'); # $scrl->add($self->{'disadvantages'}); # $self->{'disadvantages'}->show(); # $self->{'disadvantages'}->set_headers_visible(1); # $self->{'disadvantages'}->get_column(0)-> # set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); # $self->{'disadvantages'}->get_column(0)-> # set_fixed_width($self->{'disadvantages'}->get_pango_context()-> # get_font_description()-> # get_size() * 3 / Gtk2::Pango->scale); # $self->{'disadvantages'}->get_column(1)-> # set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); # $self->{'disadvantages'}->get_column(1)->set_expand(1); # $self->filladwid($self->{'disadvantages'},$char->disadvantages()); $self->{'disadstore'}=Gtk2::TreeStore->new('Glib::String','Glib::String'); $self->{'disadvantages'}=Gtk2::TreeView->new_with_model($self->{'disadstore'}); $self->{'disadvantages'}-> append_column(Gtk2::TreeViewColumn-> new_with_attributes("Cost",$self->{'rendtext'}, text=>0)); $self->{'disadvantages'}-> append_column(Gtk2::TreeViewColumn-> new_with_attributes("Disadvantage",$self->{'rendtext'}, text=>1)); $scrl->add($self->{'disadvantages'}); $self->{'disadvantages'}->show(); $self->{'disadvantages'}->set_reorderable(1); $self->{'disadvantages'}->set_headers_visible(0); $self->{'disadvantages'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'disadvantages'}->get_column(0)-> set_fixed_width($self->{'disadvantages'}->get_pango_context()-> get_font_description()-> get_size() * 5 / Gtk2::Pango->scale); $self->{'disadvantages'}->get_column(1)-> set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); $self->{'disadvantages'}->get_column(1)->set_expand(1); $self->filladwid($self->{'disadstore'},$char->disadvantages()); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $self->{'newdisadcost'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newdisadcost'},0,0,2); $self->{'newdisadcost'}->show(); $self->{'newdisadcost'}-> set_size_request($self->{'newdisadcost'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale,-1); $self->{'newdisad'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newdisad'},1,1,2); $self->{'newdisad'}->show(); $self->{'newdisad'}->signal_connect(activate => \&addDisadvantage , $self); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $but=Gtk2::Button->new("Add"); $hbox->pack_start($but,1,1,0); $but->signal_connect(clicked => \&addDisadvantage , $self); $but->show(); $but=Gtk2::Button->new("Edit"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&editDisadvantage , $self); $but=Gtk2::Button->new("Delete"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&removeDisadvantage , $self); $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('automatic','always'); $vbox->pack_start($scrl,1,1,2); $scrl->show(); # $self->{'quirks'}=Gtk2::SimpleList->new('Cost' => 'text', # 'Quirk' => 'text'); # $scrl->add($self->{'quirks'}); # $self->{'quirks'}->show(); # $self->{'quirks'}->set_headers_visible(1); # $self->{'quirks'}->get_column(0)-> # set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); # $self->{'quirks'}->get_column(0)-> # set_fixed_width($self->{'quirks'}->get_pango_context()-> # get_font_description()-> # get_size() * 3 / Gtk2::Pango->scale); # $self->{'quirks'}->get_column(1)-> # set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); # $self->{'quirks'}->get_column(1)->set_expand(1); # $self->filladwid($self->{'quirks'},$char->quirks()); $self->{'quirkstore'}=Gtk2::TreeStore->new('Glib::String','Glib::String'); $self->{'quirks'}=Gtk2::TreeView->new_with_model($self->{'quirkstore'}); $self->{'quirks'}-> append_column(Gtk2::TreeViewColumn-> new_with_attributes("Cost",$self->{'rendtext'}, text=>0)); $self->{'quirks'}-> append_column(Gtk2::TreeViewColumn-> new_with_attributes("Quirk",$self->{'rendtext'}, text=>1)); $scrl->add($self->{'quirks'}); $self->{'quirks'}->show(); $self->{'quirks'}->set_reorderable(1); $self->{'quirks'}->set_headers_visible(0); $self->{'quirks'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'quirks'}->get_column(0)-> set_fixed_width($self->{'quirks'}->get_pango_context()-> get_font_description()-> get_size() * 5 / Gtk2::Pango->scale); $self->{'quirks'}->get_column(1)-> set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); $self->{'quirks'}->get_column(1)->set_expand(1); $self->filladwid($self->{'quirkstore'},$char->quirks()); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $self->{'newquirk'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newquirk'},1,1,2); $self->{'newquirk'}->show(); $self->{'newquirk'}->signal_connect(activate => \&addQuirk , $self); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $but=Gtk2::Button->new("Add"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&addQuirk , $self); $but=Gtk2::Button->new("Edit"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&editQuirk , $self); $but=Gtk2::Button->new("Delete"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&removeQuirk , $self); # **************************************** # Page of Skills and Techniques $pagehbox=Gtk2::HBox->new(); $nb->append_page($pagehbox,"Skills"); $pagehbox->show(); $vbox=Gtk2::VBox->new(); $pagehbox->pack_start($vbox,1,1,2); $vbox->show(); $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('automatic','always'); $vbox->pack_start($scrl,1,1,0); $scrl->show(); $self->{'skills'}=Gtk2::SimpleList->new('Cost' => 'text', 'Skill' => 'text', 'SkillVar' => 'scalar' ); $scrl->add($self->{'skills'}); $self->{'skills'}->show(); $self->{'skills'}->set_headers_visible(1); $self->{'skills'}->set_reorderable(1); $self->{'skills'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'skills'}->get_column(0)-> set_fixed_width($self->{'skills'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'skills'}->get_column(1)-> set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); $self->{'skills'}->get_column(1)->set_expand(1); $self->{'skills'}->get_column(2)->set_visible(0); $self->fillskillwid($self->{'skills'},$char->skills()); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $self->{'newskillpoints'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newskillpoints'},0,0,2); $self->{'newskillpoints'}->show(); $self->{'newskillpoints'}-> set_size_request($self->{'newskillpoints'}->get_pango_context()-> get_font_description()-> get_size() * 2 / Gtk2::Pango->scale,-1); $self->{'newskillatt'}=Gtk2::ComboBox->new_text(); foreach $val ('ST','DX','IQ','HT','Will','Per') { $self->{'newskillatt'}->append_text($val); } $self->{'newskillatt'}->set_active(1); $hbox->pack_start($self->{'newskillatt'},0,0,2); $self->{'newskillatt'}->show(); $self->{'newskilldiff'}=Gtk2::ComboBox->new_text(); foreach $val ('E','A','H','VH','!') { $self->{'newskilldiff'}->append_text($val); } $self->{'newskilldiff'}->set_active(1); $hbox->pack_start($self->{'newskilldiff'},0,0,2); $self->{'newskilldiff'}->show(); $self->{'newskill'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newskill'},1,1,2); $self->{'newskill'}->show(); $self->{'newskill'}->signal_connect(activate => \&addSkill , $self); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $but=Gtk2::Button->new("Add"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&addSkill , $self); $but=Gtk2::Button->new("Edit"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&editSkill , $self); $but=Gtk2::Button->new("Delete"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&deleteSkill , $self); $vbox=Gtk2::VBox->new(); $pagehbox->pack_start($vbox,1,1,2); $vbox->show(); $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('automatic','always'); $vbox->pack_start($scrl,1,1,0); $scrl->show(); $self->{'techniques'}=Gtk2::SimpleList->new('Cost' => 'text', 'Technique' => 'text', 'TechniqueVar' => 'scalar'); $scrl->add($self->{'techniques'}); $self->{'techniques'}->show(); $self->{'techniques'}->set_headers_visible(1); $self->{'techniques'}->set_reorderable(1); $self->{'techniques'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'techniques'}->get_column(0)-> set_fixed_width($self->{'techniques'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'techniques'}->get_column(1)-> set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); $self->{'techniques'}->get_column(1)->set_expand(1); $self->{'techniques'}->get_column(2)->set_visible(0); $self->filltechniquewid($self->{'techniques'},$char->techniques()); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $self->{'newtechniquepoints'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newtechniquepoints'},0,0,2); $self->{'newtechniquepoints'}->show(); $self->{'newtechniquepoints'}-> set_size_request($self->{'newtechniquepoints'}->get_pango_context()-> get_font_description()-> get_size() * 2 / Gtk2::Pango->scale,-1); $self->{'newtechniquediff'}=Gtk2::ComboBox->new_text(); foreach $val ('A','H') { $self->{'newtechniquediff'}->append_text($val); } $self->{'newtechniquediff'}->set_active(0); $hbox->pack_start($self->{'newtechniquediff'},0,0,2); $self->{'newtechniquediff'}->show(); $self->{'newtechnique'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newtechnique'},1,1,2); $self->{'newtechnique'}->show(); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $lab=Gtk2::Label->new("Base Skill:"); $hbox->pack_start($lab,0,0,0); $lab->show(); $self->{'newtechniqueskill'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newtechniqueskill'},1,1,0); $self->{'newtechniqueskill'}->show(); $self->{'newtechniqueskilldef'}=Gtk2::Entry->new(); $hbox->pack_start($self->{'newtechniqueskilldef'},0,0,0); $self->{'newtechniqueskilldef'}->show(); $self->{'newtechniqueskilldef'}-> set_size_request($self->{'newtechniqueskilldef'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale,-1); $self->{'newtechniqueskilldef'}->signal_connect(activate => \&addTechnique , $self); $hbox=Gtk2::HBox->new(); $vbox->pack_start($hbox,0,0,0); $hbox->show(); $but=Gtk2::Button->new("Add"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&addTechnique , $self); $but=Gtk2::Button->new("Edit"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&editTechnique , $self); $but=Gtk2::Button->new("Delete"); $hbox->pack_start($but,1,1,0); $but->show(); $but->signal_connect(clicked => \&deleteTechnique , $self); # **************************************** # Page of Equipment $pagevbox=Gtk2::VBox->new(); $nb->append_page($pagevbox,"Equipment"); $pagevbox->show(); $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('automatic','always'); $pagevbox->pack_start($scrl,1,1,0); $scrl->show(); $self->{'equipment'}=Gtk2::SimpleList->new('Num' => 'text', 'Item' => 'text', 'Location' => 'text', 'Wt/Ea' => 'text', '$$/Ea' => 'text', 'Carried' => 'bool'); $scrl->add($self->{'equipment'}); $self->{'equipment'}->set_headers_visible(1); $self->{'equipment'}->set_reorderable(1); $self->{'equipment'}->show(); $self->{'equipment'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'equipment'}->get_column(0)-> set_fixed_width($self->{'equipment'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'equipment'}->set_column_editable(0,1); $self->{'equipment'}->get_column(1)-> set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); $self->{'equipment'}->get_column(1)->set_expand(1); $self->{'equipment'}->set_column_editable(1,1); $self->{'equipment'}->get_column(2)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'equipment'}->get_column(2)->set_expand(1); $self->{'equipment'}->set_column_editable(2,1); $self->{'equipment'}->get_column(3)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'equipment'}->get_column(3)-> set_fixed_width($self->{'equipment'}->get_pango_context()-> get_font_description()-> get_size() * 5 / Gtk2::Pango->scale); $self->{'equipment'}->set_column_editable(3,1); $self->{'equipment'}->get_column(4)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'equipment'}->get_column(4)-> set_fixed_width($self->{'equipment'}->get_pango_context()-> get_font_description()-> get_size() * 5 / Gtk2::Pango->scale); $self->{'equipment'}->set_column_editable(4,1); @{$self->{'equipment'}->{data}}=(); foreach $val (@{$char->equipment()}) { push @{$self->{'equipment'}->{data}}, [$val->{'number'},$val->{'name'},$val->{'location'}, $val->{'weight'},$val->{'cost'},$val->{'carried'}]; } $hbox=new Gtk2::HBox(); $pagevbox->pack_start($hbox,0,0,0); $hbox->show(); $but=new Gtk2::Button("Add"); $hbox->pack_start($but,0,0,0); $but->show(); $but->signal_connect(clicked => \&addEquipment,$self); $but=new Gtk2::Button("Del"); $hbox->pack_start($but,0,0,0); $but->show(); $but->signal_connect(clicked => \&delFromSimpleList,$self->{'equipment'}); $self->{'lbscarried2'}=Gtk2::Label->new($lbs); $hbox->pack_end($self->{'lbscarried2'},0,0,4); $self->{'lbscarried2'}->show(); $lab=Gtk2::Label->new("Lbs Carried: "); $hbox->pack_end($lab,0,0,0); $lab->show(); $self->{'totalequipcost'}=Gtk2::Label->new($char->equipcost()); $hbox->pack_end($self->{'totalequipcost'},0,0,4); $self->{'totalequipcost'}->show(); $lab=Gtk2::Label->new("Equipment Budget: "); $hbox->pack_end($lab,0,0,0); $lab->show(); # **************************************** # Page of Weapons and Ranged Weapons $pagevbox=Gtk2::VBox->new(); $nb->append_page($pagevbox,"Weapons"); $pagevbox->show(); $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('automatic','always'); $pagevbox->pack_start($scrl,1,1,0); $scrl->show(); $self->{'weapons'}=Gtk2::SimpleList->new('Weapon' => 'text', 'Damage' => 'text', 'ST' => 'text', 'Reach' => 'text', 'Skill' => 'text', 'Parry' => 'text', 'Notes' => 'text'); $scrl->add($self->{'weapons'}); $self->{'weapons'}->set_headers_visible(1); $self->{'weapons'}->set_reorderable(1); $self->{'weapons'}->show(); $self->{'weapons'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'weapons'}->get_column(0)-> set_fixed_width($self->{'weapons'}->get_pango_context()-> get_font_description()-> get_size() * 20 / Gtk2::Pango->scale); $self->{'weapons'}->set_column_editable(0,1); $self->{'weapons'}->get_column(1)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'weapons'}->get_column(1)-> set_fixed_width($self->{'weapons'}->get_pango_context()-> get_font_description()-> get_size() * 12 / Gtk2::Pango->scale); $self->{'weapons'}->set_column_editable(1,1); $self->{'weapons'}->get_column(2)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'weapons'}->get_column(2)-> set_fixed_width($self->{'weapons'}->get_pango_context()-> get_font_description()-> get_size() * 2 / Gtk2::Pango->scale); $self->{'weapons'}->set_column_editable(2,1); $self->{'weapons'}->get_column(3)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'weapons'}->get_column(3)-> set_fixed_width($self->{'weapons'}->get_pango_context()-> get_font_description()-> get_size() * 4 / Gtk2::Pango->scale); $self->{'weapons'}->set_column_editable(3,1); $self->{'weapons'}->get_column(4)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'weapons'}->get_column(4)-> set_fixed_width($self->{'weapons'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'weapons'}->set_column_editable(4,1); $self->{'weapons'}->get_column(5)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'weapons'}->get_column(5)-> set_fixed_width($self->{'weapons'}->get_pango_context()-> get_font_description()-> get_size() * 4 / Gtk2::Pango->scale); $self->{'weapons'}->set_column_editable(5,1); $self->{'weapons'}->get_column(6)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'weapons'}->get_column(6)-> set_fixed_width($self->{'weapons'}->get_pango_context()-> get_font_description()-> get_size() * 5 / Gtk2::Pango->scale); $self->{'weapons'}->set_column_editable(6,1); @{$self->{'weapons'}->{data}}=(); foreach $val (@{$char->weapons()}) { push @{$self->{'weapons'}->{data}}, [$val->{'weapon'},$val->{'damage'},$val->{'st'}, $val->{'reach'},$val->{'skill'},$val->{'parry'},$val->{'notes'}]; } $hbox=new Gtk2::HBox(); $pagevbox->pack_start($hbox,0,0,0); $hbox->show(); $but=new Gtk2::Button("Add"); $hbox->pack_start($but,0,0,0); $but->show(); $but->signal_connect(clicked => \&addWeapon,$self); $but=new Gtk2::Button("Del"); $hbox->pack_end($but,0,0,0); $but->show(); $but->signal_connect(clicked => \&delFromSimpleList,$self->{'weapons'}); $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('automatic','always'); $pagevbox->pack_start($scrl,1,1,0); $scrl->show(); $self->{'rangedweapons'}=Gtk2::SimpleList->new('Weapon' => 'text', 'Damage' => 'text', 'Acc' => 'text', 'Range' => 'text', 'RoF' => 'text', 'Shots' => 'text', 'ST' => 'text', 'Bulk' => 'text', 'Rcl' => 'text', 'Notes' => 'text'); $scrl->add($self->{'rangedweapons'}); $self->{'rangedweapons'}->set_headers_visible(1); $self->{'rangedweapons'}->set_reorderable(1); $self->{'rangedweapons'}->show(); $self->{'rangedweapons'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(0)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 12 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(0,1); $self->{'rangedweapons'}->get_column(1)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(1)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 10 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(1,1); $self->{'rangedweapons'}->get_column(2)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(2)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 4 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(2,1); $self->{'rangedweapons'}->get_column(3)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(3)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 6 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(3,1); $self->{'rangedweapons'}->get_column(4)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(4)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(4,1); $self->{'rangedweapons'}->get_column(5)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(5)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 6 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(5,1); $self->{'rangedweapons'}->get_column(6)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(6)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(6,1); $self->{'rangedweapons'}->get_column(7)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(7)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(7,1); $self->{'rangedweapons'}->get_column(8)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(8)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 3 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(8,1); $self->{'rangedweapons'}->get_column(9)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'rangedweapons'}->get_column(9)-> set_fixed_width($self->{'rangedweapons'}->get_pango_context()-> get_font_description()-> get_size() * 5 / Gtk2::Pango->scale); $self->{'rangedweapons'}->set_column_editable(9,1); @{$self->{'rangedweapons'}->{data}}=(); foreach $val (@{$char->rangedweapons()}) { push @{$self->{'rangedweapons'}->{data}}, [$val->{'weapon'},$val->{'damage'},$val->{'acc'},$val->{'range'}, $val->{'rof'},$val->{'shots'},$val->{'st'},$val->{'bulk'}, $val->{'rcl'},$val->{'notes'}]; } $hbox=new Gtk2::HBox(); $pagevbox->pack_start($hbox,0,0,0); $hbox->show(); $but=new Gtk2::Button("Add"); $hbox->pack_start($but,0,0,0); $but->show(); $but->signal_connect(clicked => \&addRangedWeapon,$self); $but=new Gtk2::Button("Del"); $hbox->pack_end($but,0,0,0); $but->show(); $but->signal_connect(clicked => \&delFromSimpleList, $self->{'rangedweapons'}); # Page of Notes $scrl=Gtk2::ScrolledWindow->new(); $scrl->set_policy('never','always'); $nb->append_page($scrl,"Notes"); $scrl->show(); $self->{'notes'}=Gtk2::TextView->new(); $self->{'notes'}->set_wrap_mode("GTK_WRAP_WORD"); $self->{'notes'}->show(); $scrl->add($self->{'notes'}); $self->{'notes'}->get_buffer()->set_text($char->notes()); # Show window $self->{'win'}->show(); ++$main::wincount; $self; } # ********************************************************************** # closewin -- add an Are You Sure! sub closeWin { my $self=shift; $self->{'win'}->hide(); $self->{'char'}=''; $self=''; exit if (--$main::wincount<=0); } # ********************************************************************** # About sub aboutbox { my $self=shift; my ($mainvbox,$hbox,$vbox,$lab,$but,$text,$abouttext); if (!$self->{'aboutboxmade'}) { $self->{'aboutbox'}=Gtk2::Window->new(); $self->{'aboutbox'}->set_title("About gtkchar4e"); $mainvbox=Gtk2::VBox->new(); $self->{'aboutbox'}->add($mainvbox); $mainvbox->show(); $lab=Gtk2::Label->new("gtkchar4e"); $mainvbox->pack_start($lab,0,0,8); $lab->show(); $text=Gtk2::TextView->new(); $text->set_wrap_mode("GTK_WRAP_WORD"); $text->set_editable(''); $text->set_size_request(500,-1); $text->show(); $mainvbox->pack_start($text,0,0,8); $abouttext="gtkchar 1.1.1\n\n". "A Perl/GTK character editor for GURPS/4e characters.\n\n". "GURPS is a trademark of Steve Jackson Games, and its rules ". "and art are copyrighted by Steve Jackson Games . All rights ". "are reserved by Steve Jackson Games. This game aid is the ". "original creation of Rob Knop and is released for free ". "distribution, and not for resale, under the permissions ". "granted in the Steve Jackson Games Online Policy at:\n". " http://www.sjgames.com/general/online_policy.htm\n\n". "http://www.pobox.com/~rknop/php/Omar/gurps/gtkchar4e.html\n"; $text->get_buffer()->set_text($abouttext); $but=Gtk2::Button->new("Coolness"); $but->signal_connect(clicked => sub { my $wid=shift; my $win=shift; $win->hide(); } , $self->{'aboutbox'}); $mainvbox->pack_start($but,0,0,0); $but->show(); $self->{'aboutbox'}-> signal_connect("delete-event" => sub { my $wid=shift; my $ev=shift; my $win=shift; $win->hide(); 1; }, $self->{'aboutbox'}); $self->{'aboutboxmade'}=1; } $self->{'aboutbox'}->show(); } # ********************************************************************** # filladwid sub filladwid { my $self=shift; my $wid=shift; my $adv=shift; my ($ad,$mod,$row,$kid,$sign); $wid->clear(); foreach $ad (@$adv) { $row=$wid->append(undef); if ($ad->{'name'}!~/^\s*$/) { $wid->set($row,0=>$ad->{'cost'}); $wid->set($row,1=>$ad->{'name'}); } else { $wid->set($row,0=>""); $wid->set($row,1=>""); } foreach $mod (@{$ad->{'modifiers'}}) { $kid=$wid->append($row); $wid->set($kid,0=>''); $sign=($mod->{'modifier'}>0) ? '+' : ''; $wid->set($kid,1=>$sign.$mod->{'modifier'}.'%: '.$mod->{'name'}); } foreach $mod (@{$ad->{'notes'}}) { $kid=$wid->append($row); $wid->set($kid,0=>''); $wid->set($kid,1=>$mod); } } } # ********************************************************************** # fillskillwid, renderskillwidline sub fillskillwid { my $self=shift; my $wid=shift; my $skills=shift; my ($skill,$line,@widline); @{$wid->{data}}=(); foreach $skill (@$skills) { @widline=renderskillwidline($skill); push @{$wid->{data}},[$widline[0],$widline[1],$skill]; } } sub renderskillwidline { my $skill=shift; my $line=''; if ($skill->{'name'}=~/^\s*$/) { return ('',''); } if ($skill->{'difficulty'} eq 'v' || $skill->{'difficulty'} eq '!') { $line.="(VH) "; } else { $line.="(".uc($skill->{'difficulty'}).")"; } $line.=uc($skill->{'baseatt'}); if (!($skill->{'relrank'})) { $skill->{'relrank'}=0; } if ($skill->{'relrank'}>=0) { $line.="+"; } $line.=$skill->{'relrank'}." "; $line.=$skill->{'name'}; if ($skill->{'optspec'}) { $line.=" (".$skill->{'optspec'}.")"; } $line.="-".$skill->{'level'}; return ($skill->{'points'},$line); } # ********************************************************************** # filltechniquewid sub filltechniquewid { my $self=shift; my $wid=shift; my $techniques=shift; my ($technique,$line); @{$wid->{data}}=(); foreach $technique (@$techniques) { if ($technique->{'name'}=~/^\s*$/) { push @{$wid->{data}},['','',$technique]; next; } $line="(".uc($technique->{'difficulty'}).") "; $line.=$technique->{'name'}; $line.="-".$technique->{'level'}; $line.=" [".$technique->{'skill'}."]"; push @{$wid->{data}},[$technique->{'points'},$line,$technique]; } } # ********************************************************************** # addEquipment, delFromSimpleList callbacks sub addEquipment { my $wid=shift; my $self=shift; push @{$self->{'equipment'}->{data}}, ['0','(New Item)','','0','0',0]; } sub delFromSimpleList { my $wid=shift; my $list=shift; my $i; my @which=$list->get_selected_indices(); @which = sort { $b <=> $a } @which; foreach $i (@which) { splice @{$list->{data}},$i,1; } } # ********************************************************************** # addWeapon, addRangedWeapon sub addWeapon { my $wid=shift; my $self=shift; push @{$self->{'weapons'}->{data}}, ['(New Weapon)','','','','','','']; } sub addRangedWeapon { my $wid=shift; my $self=shift; push @{$self->{'rangedweapons'}->{data}}, ['(New Weapon)','','','','','','','','','']; } # ********************************************************************** # updateEverything -- Updates the character from the GUI, # calculates what needs calculating, and updates the GUI # from what was calculated. May be called as a callback, # so *never* try to call this in object oriented form... # Always call it with the GTKChar::CharWin thingie as the second # argument. sub updateEverything { my $wid=shift; my $self=shift; my ($buf,$char,$val,$tmp,$equipment,$weapons,$encumbrance,$lbs,@sel,$sel); my ($langs,$skills,$techniques,$mods,$notes,$name,@kidstack,%base); my ($written,$spoken,$first,$lang,$armor,$which,$store,$row,$kid,$done); $char=$self->{'char'}; $char->setname($self->{'name'}->get_text()); $char->setsoundbyte($self->{'soundbyte'}->get_text); $buf=$self->{'description'}->get_buffer(); $char->setdescription($buf->get_text($buf->get_start_iter(), $buf->get_end_iter(),1)); $buf=$self->{'personality'}->get_buffer(); $char->setpersonality($buf->get_text($buf->get_start_iter(), $buf->get_end_iter(),1)); $buf=$self->{'background'}->get_buffer(); $char->setbackground($buf->get_text($buf->get_start_iter(), $buf->get_end_iter(),1)); $char->setkeywords($self->{'keywords'}->get_text()); $char->setunspentpoints($self->{'unspent'}->get_text()); $char->setst($self->{'st'}->get_text()); $char->setdx($self->{'dx'}->get_text()); $char->setiq($self->{'iq'}->get_text()); $char->setht($self->{'ht'}->get_text()); $char->settemplst($self->{'templst'}->get_text()); $char->settempldx($self->{'templdx'}->get_text()); $char->settempliq($self->{'templiq'}->get_text()); $char->settemplht($self->{'templht'}->get_text()); $char->setextrahp($self->{'extrahp'}->get_text()); $char->setextrawill($self->{'extrawill'}->get_text()); $char->setextraper($self->{'extraper'}->get_text()); $char->setextrafat($self->{'extrafat'}->get_text()); $char->settemplextrahp($self->{'templextrahp'}->get_text()); $char->settemplextrawill($self->{'templextrawill'}->get_text()); $char->settemplextraper($self->{'templextraper'}->get_text()); $char->settemplextrafat($self->{'templextrafat'}->get_text()); $char->setcurhp($self->{'curhp'}->get_text()); $char->setcurfat($self->{'curfat'}->get_text()); $char->setextraspeed($self->{'extraspeed'}->get_text()); $char->settemplextraspeed($self->{'templextraspeed'}->get_text()); $char->setextramove($self->{'extramove'}->get_text()); $char->settemplextramove($self->{'templextramove'}->get_text()); $char->setsm($self->{'sizemod'}->get_text()); # Armor $armor={}; foreach $val ('torso','skull','eyes','face','neck', 'head','larm','rarm','lhand','rhand', 'lleg','rleg','lfoot','rfoot') { $armor->{$val}=$self->{"dr$val"}->get_text(); } $char->setarmor($armor); $char->setdodgebonus($self->{'dodgebonus'}->get_text()); $char->setparry($self->{'parry'}->get_text()); $char->setblock($self->{'block'}->get_text()); # Parse Advantages, Disadvantages, and Quirks # Note that this tries to deal with the user doing really stupid # things with the tree.... foreach $which ('Advantages','Disadvantages','Quirks') { if ($which eq 'Advantages') { $store=$self->{'adstore'}; } elsif ($which eq 'Disadvantages') { $store=$self->{'disadstore'}; } else { $store=$self->{'quirkstore'}; } # save base costs %base=(); foreach $val (@{$char->{lc($which)}}) { $base{$val->{'name'}}=$val->{'basepoints'}; } $char->{lc($which)}=[]; for ( $row=$store->get_iter_first() ; $row ; $row=$store->iter_next($row) ) { $mods=[]; $notes=[]; $val=$store->get($row,0); $name=$store->get($row,1); @kidstack=(); $kid=$store->iter_children($row); while ($kid || @kidstack) { if (!defined($kid)) { $kid=pop @kidstack; } next if (!defined($kid)); $val=$store->get($kid,1); if ($val=~/^\s*([\+\-]?\d+)\%\:?\s*(.*)$/) { push @$mods,{'name'=>$2,'modifier'=>int($1)}; } else { push @$notes,$val; } if (defined($store->iter_children($kid))) { push @kidstack,$store->iter_next($kid); $kid=$store->iter_children($kid); } else { $kid=$store->iter_next($kid); } } $char->addtrait(lc($which),$name,$base{$name},$mods,$notes); } # Geez, I hope that worked } # Parse all languages out of the listview $char->{'languages'}=[]; $langs=[]; $first=1; foreach $val (@{$self->{'langs'}->{data}}) { if ($val->[1] eq 'Broken') { $spoken=1; } elsif ($val->[1] eq 'Accented') { $spoken=2; } elsif ($val->[1] eq 'Native') { $spoken=3; } else { $spoken=0; } if ($val->[2] eq 'Broken') { $written=1; } elsif ($val->[2] eq 'Accented') { $written=2; } elsif ($val->[2] eq 'Native') { $written=3; } else { $written=0; } $char->addlanguage($val->[0],$first,$written,$spoken); $first=''; } # For Skills and Techniques, we have to pull the array out of # the widgets and stick the skill variables in the array # in the character object. The user may have reordered, # added, deleted. We also have to redo the widgets, because # defaults or attributes may have changed. $skills=[]; foreach $val (@{$self->{'skills'}->{data}}) { push @$skills,$val->[2]; } $char->setskills($skills); $self->fillskillwid($self->{'skills'},$char->skills()); $techniques=[]; foreach $val (@{$self->{'techniques'}->{data}}) { push @$techniques,$val->[2]; } $char->settechniques($techniques); $self->filltechniquewid($self->{'techniques'},$char->techniques()); # Equipment uses the Gtk2::SimpleList interface # for inline editing of single-line data, so we gotta parse it $equipment=[]; foreach $val (@{$self->{'equipment'}->{data}}) { push @$equipment,{ 'number' => $val->[0], 'name' => $val->[1], 'location' => $val->[2], 'weight' => $val->[3], 'cost' => $val->[4], 'carried' => $val->[5]}; } $char->setequipment($equipment); #foreach $val (@{$char->equipment()}) { # print STDERR $val->{'number'}.' of '. # $val->{'name'}.' at '.$val->{'location'}.' ; '. # $val->{'weight'}.' lbs each, '.$val->{'cost'}.' each. '; # if ($val->{'carried'}) { print STDERR "Carried\n"; } # else { print STDERR "Not Carried\n"; } #} $weapons=[]; foreach $val (@{$self->{'weapons'}->{data}}) { push @$weapons,{ 'weapon' => $val->[0], 'damage' => $val->[1], 'st' => $val->[2], 'reach' => $val->[3], 'skill' => $val->[4], 'parry' => $val->[5], 'notes' => $val->[6] }; } $char->setweapons($weapons); $weapons=[]; foreach $val (@{$self->{'rangedweapons'}->{data}}) { push @$weapons,{ 'weapon' => $val->[0], 'damage' => $val->[1], 'acc' => $val->[2], 'range' => $val->[3], 'rof' => $val->[4], 'shots' => $val->[5], 'st' => $val->[6], 'bulk' => $val->[7], 'rcl' => $val->[8], 'notes' => $val->[9] }; } $char->setrangedweapons($weapons); $buf=$self->{'notes'}->get_buffer(); $char->setnotes($buf->get_text($buf->get_start_iter(), $buf->get_end_iter(),1)); # Whew!! # Now that we've parsed the GUI, update the character $char->calccharvalue(); # Now update the GUI back.... $self->{'atttotal'}->set_label($char->{'attpoints'}); $self->{'adtotal'}->set_label($char->{'adpoints'}); $self->{'disadtotal'}->set_label($char->{'disadpoints'}); $self->{'quirktotal'}->set_label($char->{'quirkpoints'}); $self->{'langtotal'}->set_label($char->{'langpoints'}); $self->{'skilltotal'}->set_label($char->{'skillpoints'}); $self->{'techniquetotal'}->set_label($char->{'techniquepoints'}); $self->{'total'}->set_label($char->{'totalpoints'}); $self->{'stpoints'}->set_label("[".$char->stcost()."]"); $self->{'dxpoints'}->set_label("[".$char->dxcost()."]"); $self->{'iqpoints'}->set_label("[".$char->iqcost()."]"); $self->{'htpoints'}->set_label("[".$char->htcost()."]"); $self->{'hp'}->set_label($char->hp()); $self->{'will'}->set_label($char->will()); $self->{'per'}->set_label($char->per()); $self->{'fat'}->set_label($char->fat()); $self->{'hppoints'}->set_label("[".$char->hpcost()."]"); $self->{'willpoints'}->set_label("[".$char->willcost()."]"); $self->{'perpoints'}->set_label("[".$char->percost()."]"); $self->{'fatpoints'}->set_label("[".$char->fatcost()."]"); $self->{'speed'}->set_label($char->speed()); $self->{'speedcost'}->set_label("[".$char->speedcost()."]"); $self->{'move'}->set_label($char->move()); $self->{'basicmove'}->set_label($char->basicmove()); $self->{'movecost'}->set_label("[".$char->movecost()."]"); $self->{'basiclift'}->set_label($char->basiclift()); ($encumbrance,$lbs)=$char->encumbrance(); $self->{'lbscarried'}->set_label($lbs); $self->{'lbscarried2'}->set_label($lbs); $self->{'totalequipcost'}->set_label($char->equipcost()); $self->{'encumbrance'}->set_label($encumbrance); $self->{'dodge'}->set_label($char->dodge()); # Try to keep the same advantages et al. selected foreach $which ('Advantages','Disadvantages','Quirks') { if ($which eq 'Advantages') { $store=$self->{'adstore'}; } elsif ($which eq 'Disadvantages') { $store=$self->{'disadstore'}; } else { $store=$self->{'quirkstore'}; } $sel=$self->{lc($which)}->get_selection()->get_selected(); if (defined($sel)) { $val=$store->get($sel,1); if ($val=~/^\s*[\+\-]?\d*\%\:?\s*(.*)$/) { $val=$2; } } else { $val=''; } $self->filladwid($store,$char->{lc($which)}); if ($val) { $done=''; for ( $row=$store->get_iter_first() ; $row && !$done ; $row=$store->iter_next($row) ) { $buf=$store->get($row,1); if ($buf=~/^\s*[\+\-]?\d*\%\:?\s*(.*)$/) { $buf=$2; } if ($buf eq $val) { $self->{lc($which)}->get_selection()->select_iter($row); last; } for ( $kid=$store->iter_children($row) ; $kid ; $kid=$store->iter_next($kid) ) { $buf=$store->get($kid,1); if ($buf=~/^\s*[\+\-]?\d*\%\:?\s*(.*)$/) { $buf=$2; } if ($buf eq $val) { $self->{lc($which)}->get_selection()-> select_iter($row); $done=1; last; } } } } } # Try to keep the same things selected in skills and techniques... @sel=$self->{'skills'}->get_selected_indices(); $self->fillskillwid($self->{'skills'},$char->skills()); $self->{'skills'}->select(@sel); @sel=$self->{'techniques'}->get_selected_indices(); $self->filltechniquewid($self->{'techniques'},$char->techniques()); $self->{'techniques'}->select(@sel); # Update languages just so the cost is right. This is a bit of a # clumsy way to do it. @{$self->{'langs'}->{data}}=(); foreach $lang (@{$char->languages()}) { push @{$self->{'langs'}->{data}}, [$lang->{'name'}, ('None','Broken','Accented','Native')[$lang->{'spoken'}], ('None','Broken','Accented','Native')[$lang->{'written'}], $lang->{'cost'}]; } # Set window title $self->{'win'}->set_title($char->name()); } # ********************************************************************** # findAdvantageOrdinal -- Figures out the ordinal (index into character # array) that corresponds to the selected advantage. Returns # the size of the array if nothing is selected sub findAdvantageOrdinal { my $self=shift; my $wid=shift; my $store=shift; my $adlist=shift;; my ($i,$done,$sel,$row,$nkids,$kid); $sel=$wid->get_selection(); $done=0; for ($row=$store->get_iter_first() ; $row ; $row=$store->iter_next($row)) { last if ($sel->iter_is_selected($row)); $nkids=$store->iter_n_children($row); foreach $i (0..$nkids-1) { $kid=$store->iter_nth_child($row,$i); if ($sel->iter_is_selected($kid)) { $done=1; last; } } $done && last; } if (!defined($row)) { return scalar @$adlist; } foreach $i (0..$#$adlist) { if ($adlist->[$i]->{'name'} eq $store->get($row,1)) { return $i; } } return scalar @$adlist; } # ********************************************************************** # addAdvantage, removeAdvantage sub addAdvantage { my $wid=shift; my $self=shift; my ($ordinal,$name,$cost,$it); $ordinal=$self->findAdvantageOrdinal($self->{'advantages'}, $self->{'adstore'}, $self->{'char'}->advantages()); ++$ordinal; $name=$self->{'newad'}->get_text(); $cost=$self->{'newadcost'}->get_text(); $self->{'char'}->addtrait('advantages',$name,$cost,[],[],$ordinal); $self->filladwid($self->{'adstore'},$self->{'char'}->advantages()); $self->{'newad'}->set_text(""); $self->{'newadcost'}->set_text(""); $self->{'newadcost'}->grab_focus(); for ( $it=$self->{'adstore'}->get_iter_first() ; $self->{'adstore'}->iter_next($it) ; $it=$self->{'adstore'}->iter_next($it) ) { ($ordinal--) || last; } $self->{'advantages'}->get_selection()->select_iter($it); updateEverything(0,$self); } sub removeAdvantage { my $wid=shift; my $self=shift; my ($ordinal); $ordinal=$self->findAdvantageOrdinal($self->{'advantages'}, $self->{'adstore'}, $self->{'char'}->advantages()); $self->{'char'}->removetrait('advantages',$ordinal); $self->filladwid($self->{'adstore'},$self->{'char'}->advantages()); updateEverything(0,$self); } # ********************************************************************** # addDisadvantage, removeDisdvantage sub addDisadvantage { my $wid=shift; my $self=shift; my ($ordinal,$name,$cost,$it); $ordinal=$self->findAdvantageOrdinal($self->{'disadvantages'}, $self->{'disadstore'}, $self->{'char'}->disadvantages()); ++$ordinal; $name=$self->{'newdisad'}->get_text(); $cost=$self->{'newdisadcost'}->get_text(); $self->{'char'}->addtrait('disadvantages',$name,$cost,[],[],$ordinal); $self->filladwid($self->{'disadstore'}, $self->{'char'}->disadvantages()); $self->{'newdisad'}->set_text(""); $self->{'newdisadcost'}->set_text(""); $self->{'newdisadcost'}->grab_focus(); for ( $it=$self->{'disadstore'}->get_iter_first() ; $self->{'disadstore'}->iter_next($it) ; $it=$self->{'disadstore'}->iter_next($it) ) { ($ordinal--) || last; } $self->{'disadvantages'}->get_selection()->select_iter($it); updateEverything(0,$self); } sub removeDisadvantage { my $wid=shift; my $self=shift; my ($ordinal); $ordinal=$self->findAdvantageOrdinal($self->{'disadvantages'}, $self->{'disadstore'}, $self->{'char'}->disadvantages()); $self->{'char'}->removetrait('disadvantages',$ordinal); $self->filladwid($self->{'disadstore'}, $self->{'char'}->disadvantages()); updateEverything(0,$self); } # ********************************************************************** # addQuirk, removeQuirk sub addQuirk { my $wid=shift; my $self=shift; my ($ordinal,$name,$it); $ordinal=$self->findAdvantageOrdinal($self->{'quirks'}, $self->{'quirkstore'}, $self->{'char'}->quirks()); ++$ordinal; $name=$self->{'newquirk'}->get_text(); $self->{'char'}->addtrait('quirks',$name,-1,[],[],$ordinal); $self->filladwid($self->{'quirkstore'},$self->{'char'}->quirks()); $self->{'newquirk'}->set_text(""); $self->{'newquirk'}->grab_focus(); for ( $it=$self->{'quirkstore'}->get_iter_first() ; $self->{'quirkstore'}->iter_next($it) ; $it=$self->{'quirkstore'}->iter_next($it) ) { ($ordinal--) || last; } $self->{'quirks'}->get_selection()->select_iter($it); updateEverything(0,$self); } sub removeQuirk { my $wid=shift; my $self=shift; my ($ordinal); $ordinal=$self->findAdvantageOrdinal($self->{'quirks'}, $self->{'quirkstore'}, $self->{'char'}->quirks()); $self->{'char'}->removetrait('quirks',$ordinal); $self->filladwid($self->{'quirkstore'},$self->{'char'}->quirks()); updateEverything(0,$self); } # ********************************************************************** # editAdvantage, editDisadvantage sub editAdvantage { $_[1]->editAdDisad($_[1]->{'advantages'},$_[1]->{'adstore'}, $_[1]->{'char'}->advantages(), 'Advantages'); } sub editDisadvantage { $_[1]->editAdDisad($_[1]->{'disadvantages'},$_[1]->{'disadstore'}, $_[1]->{'char'}->disadvantages(), 'Disadvantages'); } sub editQuirk { $_[1]->editAdDisad($_[1]->{'quirks'},$_[1]->{'quirkstore'}, $_[1]->{'char'}->quirks(), 'Quirks'); } sub editAdDisad { my $self=shift; my $inwid=shift; my $instore=shift; my $adlist=shift; my $which=shift; my ($ordinal,$hbox,$tab,$mainvbox,$wid,$ad,$mod,$val,$scrl); $ordinal=$self->findAdvantageOrdinal($inwid,$instore,$adlist); return if ($ordinal>=scalar @$adlist); $ad=$adlist->[$ordinal]; $self->{'addisadwinwhich'}=$which; $self->{'addisadwinordinal'}=$ordinal; $self->{'win'}->set_sensitive(''); $self->{'addisadwin'}=Gtk2::Window->new(); $mainvbox=Gtk2::VBox->new(); $self->{'addisadwin'}->add($mainvbox); $mainvbox->show(); $tab=Gtk2::Table->new(2,2); $mainvbox->pack_start($tab,0,0,0); $tab->show(); $wid=Gtk2::Label->new("Base Cost"); $tab->attach($wid,0,1,0,1,'shrink','fill',2,0); $wid->show(); $wid=Gtk2::Label->new($which); $tab->attach($wid,1,2,0,1,'expand','fill',2,0); $wid->show(); $self->{'addisadwincost'}=Gtk2::Entry->new(); $self->{'addisadwincost'}->set_text($ad->{'basepoints'}); $tab->attach($self->{'addisadwincost'},0,1,1,2,'shrink','fill',2,0); $self->{'addisadwincost'}->show(); $self->{'addisadwinname'}=Gtk2::Entry->new(); $self->{'addisadwinname'}->set_text($ad->{'name'}); $tab->attach($self->{'addisadwinname'},1,2,1,2,'expand','fill',2,0); $self->{'addisadwinname'}->show(); $scrl=Gtk2::ScrolledWindow->new(); $mainvbox->pack_start($scrl,1,1,0); $scrl->set_policy("GTK_POLICY_NEVER","GTK_POLICY_ALWAYS"); $scrl->show(); $self->{'addisadwinmods'}=Gtk2::SimpleList-> new('+/-%' => 'text' , 'Enhancement/Limitation' => 'text'); $scrl->add($self->{'addisadwinmods'}); $self->{'addisadwinmods'}->show(); $self->{'addisadwinmods'}-> set_size_request(-1, $self->{'addisadwinmods'}->get_pango_context()-> get_font_description()->get_size() * 10 / Gtk2::Pango->scale); $self->{'addisadwinmods'}->set_headers_visible(1); $self->{'addisadwinmods'}->set_reorderable(1); $self->{'addisadwinmods'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'addisadwinmods'}->get_column(0)-> set_fixed_width($self->{'addisadwinmods'}->get_pango_context()-> get_font_description()-> get_size() * 6 / Gtk2::Pango->scale); $self->{'addisadwinmods'}->set_column_editable(0,1); $self->{'addisadwinmods'}->get_column(1)->set_expand(1); $self->{'addisadwinmods'}->set_column_editable(1,1); foreach $mod (@{$ad->{'modifiers'}}) { $val=($mod->{'modifier'}>0) ? '+' : ''; $val.=$mod->{'modifier'}; $val.='%'; push @{$self->{'addisadwinmods'}->{data}},[$val,$mod->{'name'}]; } $hbox=Gtk2::HBox->new(); $mainvbox->pack_start($hbox,0,0,0); $hbox->show(); $wid=Gtk2::Button->new("Add New"); $hbox->pack_start($wid,0,0,0); $wid->show(); $wid->signal_connect(clicked => sub { my ($wid,$self)=@_; push @{$self->{'addisadwinmods'}->{data}},['+0%','(Modifier)']; } , $self); $wid=Gtk2::Button->new("Delete"); $hbox->pack_end($wid,0,0,0); $wid->show(); $wid->signal_connect(clicked => sub { my ($wid,$self)=@_; my @sel=$self->{'addisadwinmods'}->get_selected_indices(); @sel = sort { $b<=>$a } @sel; my $sel; if (@sel) { foreach $sel (@sel) { splice @{$self->{'addisadwinmods'}->{data}},$sel,1; } } } , $self); $wid=Gtk2::Label->new("Notes:"); $mainvbox->pack_start($wid,0,0,2); $wid->show(); $scrl=Gtk2::ScrolledWindow->new(); $mainvbox->pack_start($scrl,1,1,0); $scrl->set_policy("GTK_POLICY_AUTOMATIC","GTK_POLICY_ALWAYS"); $scrl->show(); $self->{'addisadwinnotes'}=Gtk2::TextView->new(); $self->{'addisadwinnotes'}->set_wrap_mode('none'); $scrl->add($self->{'addisadwinnotes'}); $self->{'addisadwinnotes'}->show(); $val=$self->{'addisadwinnotes'}->get_pango_context()-> get_font_description()->get_size() / Gtk2::Pango->scale; $self->{'addisadwinnotes'}->set_size_request($val*25,$val*6); foreach $val (@{$ad->{'notes'}}) { $self->{'addisadwinnotes'}->get_buffer()-> insert($self->{'addisadwinnotes'}->get_buffer()->get_end_iter, "$val\n"); } $hbox=Gtk2::HBox->new(); $mainvbox->pack_start($hbox,0,0,4); $hbox->show(); $wid=Gtk2::Button->new("Save"); $hbox->pack_start($wid,0,0,0); $wid->show(); $wid->signal_connect(clicked => \&saveEditAdDisad , $self); $wid=Gtk2::Button->new("Cancel"); $hbox->pack_end($wid,0,0,0); $wid->show(); $wid->signal_connect(clicked => \&closeEditAdDisad , $self); $self->{'addisadwin'}-> signal_connect(delete_event => sub { closeEditAdDisad($_[0],$_[2]); } , $self); $self->{'addisadwin'}-> signal_connect(destroy_event => sub { closeEditAdDisad($_[0],$_[2]); } , $self); $self->{'addisadwin'}->show(); } sub saveEditAdDisad { my $wid=shift; my $self=shift; my %store = ( 'advantages' => 'adstore', 'disadvantages' => 'disadstore', 'quirks' => 'quirkstore' ); my ($mod,$buf,$txt,@txt,$val,$which); my $ad={}; # basepoints, name, modifiers{name,modifier}, notes $ad->{'basepoints'}=$self->{'addisadwincost'}->get_text(); $ad->{'name'}=$self->{'addisadwinname'}->get_text(); $ad->{'modifiers'}=[]; foreach $mod (@{$self->{'addisadwinmods'}->{data}}) { ($val)=($mod->[0]=~/^\s*([\+\-]?\d*)\s*\%?/); if (!$val) { $val=0; } $val=int($val); push @{$ad->{'modifiers'}},{'name' => $mod->[1], 'modifier' => $val}; } $buf=$self->{'addisadwinnotes'}->get_buffer(); $txt=$buf->get_text($buf->get_start_iter(),$buf->get_end_iter(),1); @txt=split(/\n/,$txt); $ad->{'notes'}=[]; foreach $txt (@txt) { push @{$ad->{'notes'}},$txt; } $which=lc($self->{'addisadwinwhich'}); $self->{'char'}->removetrait($which,$self->{'addisadwinordinal'}); $self->{'char'}->addtrait($which, $ad->{'name'}, $ad->{'basepoints'}, $ad->{'modifiers'}, $ad->{'notes'}, $self->{'addisadwinordinal'}); $self->filladwid($self->{$store{$which}},$self->{'char'}->{$which}); &updateEverything(0,$self); # Recalc point totals &closeEditAdDisad(0,$self); } sub closeEditAdDisad { my $wid=shift; my $self=shift; my (@keys,$key); $self->{'addisadwin'}->hide(); $self->{'win'}->set_sensitive(1); @keys=grep(/^addisadwin/,keys(%$self)); foreach $key (@keys) { delete $self->{$key}; } } # ********************************************************************** # addLang, editLang, deleteLang sub addLang { my $wid=shift; my $self=shift; push @{$self->{'langs'}->{data}},['New Language','None','None',0]; $self->{'langs'}->select($#{$self->{'langs'}->{data}}); editLang(0,$self); } sub editLang { my $junk=shift; my $self=shift; my (@sel,$sel,$mainvbox,$hbox,$tab,$wid,$val,$line,$i); @sel=$self->{'langs'}->get_selected_indices(); (@sel) || return; $sel=$sel[0]; return if ($sel<0 || $sel>$#{$self->{'langs'}->{data}}); $self->{'langwindex'}=$sel; $line=$self->{'langs'}->{data}->[$sel]; $self->{'win'}->set_sensitive(''); $self->{'langwin'}=Gtk2::Window->new(); $mainvbox=Gtk2::VBox->new(); $self->{'langwin'}->add($mainvbox); $mainvbox->show(); $tab=Gtk2::Table->new(2,3); $mainvbox->pack_start($tab,0,0,1); $tab->show(); $wid=Gtk2::Label->new("Language:"); $tab->attach($wid,0,1,0,1,'fill','fill',0,0); $wid->show(); $self->{'langwinlang'}=Gtk2::Entry->new(); $self->{'langwinlang'}->set_text($line->[0]); $tab->attach($self->{'langwinlang'},1,2,0,1,'expand','fill',0,0); $self->{'langwinlang'}->show(); $wid=Gtk2::Label->new("Spoken:"); $tab->attach($wid,0,1,1,2,'fill','fill',0,0); $wid->show(); $self->{'langwinspoken'}=Gtk2::ComboBox->new_text(); $i=0; foreach $val ('None','Broken','Accented','Native') { $self->{'langwinspoken'}->append_text($val); if ($line->[1] eq $val) { $self->{'langwinspoken'}->set_active($i); } ++$i } $tab->attach($self->{'langwinspoken'},1,2,1,2,'expand','fill',0,0); $self->{'langwinspoken'}->show(); $wid=Gtk2::Label->new("Written:"); $tab->attach($wid,0,1,2,3,'fill','fill',0,0); $wid->show(); $self->{'langwinwritten'}=Gtk2::ComboBox->new_text(); $i=0; foreach $val ('None','Broken','Accented','Native') { $self->{'langwinwritten'}->append_text($val); if ($line->[2] eq $val) { $self->{'langwinwritten'}->set_active($i); } ++$i; } $tab->attach($self->{'langwinwritten'},1,2,2,3,'expand','fill',0,0); $self->{'langwinwritten'}->show(); $hbox=Gtk2::HBox->new(); $mainvbox->pack_start($hbox,0,0,4); $hbox->show(); $wid=Gtk2::Button->new("Save Language"); $hbox->pack_start($wid,1,1,0); $wid->show(); $wid->signal_connect(clicked => \&saveEditedLanguage , $self); $wid=Gtk2::Button->new("Cancel"); $hbox->pack_start($wid,1,1,0); $wid->show(); $wid->signal_connect(clicked => \&closeEditLangWin , $self); $self->{'langwin'}->show(); } sub saveEditedLanguage { my $wid=shift; my $self=shift; my ($name,$written,$spoken); $name=$self->{'langwinlang'}->get_text(); $written=('None','Broken','Accented','Native')[$self->{'langwinwritten'} ->get_active()]; $spoken=('None','Broken','Accented','Native')[$self->{'langwinspoken'} ->get_active()]; $self->{'langs'}->{data}->[$self->{'langwindex'}] = [$name,$spoken,$written,0]; &updateEverything(0,$self); &closeEditLangWin(0,$self); } sub closeEditLangWin() { my $wid=shift; my $self=shift; my (@keys,$key); $self->{'langwin'}->hide(); $self->{'win'}->set_sensitive(1); @keys=grep(/^langwin/,keys(%$self)); foreach $key (@keys) { delete $self->{$key}; } } sub deleteLang { my $wid=shift; my $self=shift; my (@sel,$sel); @sel=$self->{'langs'}->get_selected_indices(); (@sel) || return; $sel=$sel[0]; return if ($sel<0 || $sel>$#{$self->{'langs'}->{data}}); splice @{$self->{'langs'}->{data}},$sel,1; } # ********************************************************************** # addSkill, deleteSkill sub addSkill { my $wid=shift; my $self=shift; my ($ordinal,$name,$cost,$att,$diff,@sel,$skill,$costline,$lineline); @sel=$self->{'skills'}->get_selected_indices(); if (@sel) { $ordinal=$sel[0]; } else { $ordinal=scalar @{$self->{'skills'}->{data}}; } if ($ordinal<0) { $ordinal=0; } if ($ordinal>scalar @{$self->{'skills'}->{data}}) { $ordinal=scalar @{$self->{'skills'}->{data}}; } $cost=$self->{'newskillpoints'}->get_text(); $name=$self->{'newskill'}->get_text(); $att=('ST','DX','IQ','HT','Will','Per')[$self->{'newskillatt'}-> get_active()]; $diff=('e','a','h','v','!')[$self->{'newskilldiff'}->get_active()]; $skill={ 'name' => $name , 'optspec' => '', 'difficulty' => $diff, 'baseatt' => $att, 'points' => $cost, 'defaultbase' => '', 'defaultoffset' => '', 'bonus' => [], 'relrank' => '', 'level' => '', 'defaulting' => '' }; ($costline,$lineline)=&renderskillwidline($skill); splice @{$self->{'skills'}->{data}},$ordinal,0,([$costline,$lineline, $skill]); $self->{'skills'}->select($ordinal); $self->{'newskillpoints'}->set_text(''); $self->{'newskill'}->set_text(''); $self->{'newskillpoints'}->grab_focus(); &updateEverything(0,$self); } sub deleteSkill { my $wid=shift; my $self=shift; my ($sel,@sel); @sel=$self->{'skills'}->get_selected_indices(); foreach $sel (@sel) { splice @{$self->{'skills'}->{data}},$sel,1; } &updateEverything(0,$self); } # ********************************************************************** # editSkill sub editSkill { my $inwid=shift; my $self=shift; my (@sel,$sel,$skill,$wid,$mainvbox,$tab,$hbox,$val,$i); @sel=$self->{'skills'}->get_selected_indices(); (@sel) || return; $sel=$sel[0]; return if ($sel<0 || $sel>$#{$self->{'skills'}->{data}}); $skill=$self->{'skills'}->{data}->[$sel]->[2]; $self->{'win'}->set_sensitive(''); $self->{'skillwin'}=Gtk2::Window->new(); $mainvbox=Gtk2::VBox->new(); $self->{'skillwin'}->add($mainvbox); $mainvbox->show(); $tab=Gtk2::Table->new(4,2); $mainvbox->pack_start($tab,0,0,1); $tab->show(); $wid=Gtk2::Label->new("Skill:"); $tab->attach($wid,0,1,0,1,'fill','fill',0,0); $wid->show(); $self->{'skillwinskill'}=Gtk2::Entry->new(); $self->{'skillwinskill'}->set_text($skill->{'name'}); $tab->attach($self->{'skillwinskill'},1,2,0,1,'expand','fill',0,0); $self->{'skillwinskill'}->show(); $wid=Gtk2::Label->new("Points:"); $tab->attach($wid,0,1,1,2,'fill','fill',0,0); $wid->show(); $self->{'skillwinpoints'}=Gtk2::Entry->new(); $self->{'skillwinpoints'}->set_text($skill->{'points'}); $tab->attach($self->{'skillwinpoints'},1,2,1,2,'expand','fill',0,0); $self->{'skillwinpoints'}->show(); $wid=Gtk2::Label->new("Opt. Spec.:"); $tab->attach($wid,0,1,2,3,'fill','fill',0,0); $wid->show(); $self->{'skillwinoptspec'}=Gtk2::Entry->new(); $self->{'skillwinoptspec'}->set_text($skill->{'optspec'}); $tab->attach($self->{'skillwinoptspec'},1,2,2,3,'expand','fill',0,0); $self->{'skillwinoptspec'}->show(); $wid=Gtk2::Label->new("Att/Diff:"); $tab->attach($wid,0,1,3,4,'fill','fill',0,0); $wid->show(); $hbox=Gtk2::HBox->new(); $tab->attach($hbox,1,2,3,4,'fill','fill',0,0); $hbox->show(); $self->{'skillwinatt'}=Gtk2::ComboBox->new_text(); $i=0; foreach $val ('ST','DX','IQ','HT','Will','Per') { $self->{'skillwinatt'}->append_text($val); if (lc($skill->{'baseatt'}) eq lc($val)) { $self->{'skillwinatt'}->set_active($i); } ++$i; } $hbox->pack_start($self->{'skillwinatt'},0,0,0); $self->{'skillwinatt'}->show(); $self->{'skillwindiff'}=Gtk2::ComboBox->new_text(); $i=0; foreach $val ('E','A','H','VH','!') { $self->{'skillwindiff'}->append_text($val); if (($val eq 'VH') && ($skill->{'difficulty'} eq 'v')) { $self->{'skillwindiff'}->set_active($i); } else { if (lc($val) eq lc($skill->{'difficulty'})) { $self->{'skillwindiff'}->set_active($i); } } ++$i; } $hbox->pack_start($self->{'skillwindiff'},0,0,0); $self->{'skillwindiff'}->show(); $wid=Gtk2::Label->new("Default:"); $tab->attach($wid,0,1,4,5,'fill','fill',0,0); $wid->show(); $hbox=Gtk2::HBox->new(); $tab->attach($hbox,1,2,4,5,'expand','fill',0,0); $hbox->show(); $self->{'skillwindefaultbase'}=Gtk2::Entry->new(); $self->{'skillwindefaultbase'}->set_text($skill->{'defaultbase'}); $hbox->pack_start($self->{'skillwindefaultbase'},1,1,0); $self->{'skillwindefaultbase'}->show(); $self->{'skillwindefaultoffset'}=Gtk2::Entry->new(); $self->{'skillwindefaultoffset'}->set_text($skill->{'defaultoffset'}); $hbox->pack_start($self->{'skillwindefaultoffset'},0,0,0); $self->{'skillwindefaultoffset'}->show(); $self->{'skillwindefaultoffset'}-> set_size_request($self->{'skillwindefaultoffset'}-> get_pango_context()-> get_font_description()-> get_size() * 4 / Gtk2::Pango->scale , -1); $self->{'skillwinbonus'}=Gtk2::SimpleList->new('Bonus' => 'text', 'Description' => 'text'); $mainvbox->pack_start($self->{'skillwinbonus'},1,1,2); $self->{'skillwinbonus'}->show(); $self->{'skillwinbonus'}-> set_size_request(-1, $self->{'skillwinbonus'}->get_pango_context()-> get_font_description()-> get_size()*8 / Gtk2::Pango->scale); $self->{'skillwinbonus'}->set_headers_visible(1); $self->{'skillwinbonus'}->set_reorderable(1); $self->{'skillwinbonus'}->get_column(0)-> set_sizing("GTK_TREE_VIEW_COLUMN_FIXED"); $self->{'skillwinbonus'}->get_column(0)-> set_fixed_width($self->{'skillwinbonus'}->get_pango_context()-> get_font_description()-> get_size()*5 / Gtk2::Pango->scale); $self->{'skillwinbonus'}->set_column_editable(0,1); $self->{'skillwinbonus'}->get_column(1)-> set_sizing("GTK_TREE_VIEW_COLUMN_AUTOSIZE"); $self->{'skillwinbonus'}->get_column(1)->set_expand(1); $self->{'skillwinbonus'}->set_column_editable(1,1); foreach $val (@{$skill->{'bonus'}}) { push @{$self->{'skillwinbonus'}->{data}},[@$val]; } $hbox=Gtk2::HBox->new(); $mainvbox->pack_start($hbox,0,0,0); $hbox->show(); $wid=Gtk2::Button->new("Add Bonus"); $hbox->pack_start($wid,0,0,0); $wid->show(); $wid->signal_connect(clicked => sub { push @{$_[1]->{'skillwinbonus'}->{data}},[0,"(New Bonus)"]; } , $self); $wid=Gtk2::Button->new("Del Bonus"); $hbox->pack_end($wid,0,0,0); $wid->show(); $wid->signal_connect(clicked => sub { my $wid=shift; my $self=shift; my (@sel,$sel); @sel=$self->{'skillwinbonus'}->get_selected_indices(); @sel = sort { $b <=> $a } @sel; foreach $sel (@sel) { splice @{$self->{'skillwinbonus'}->{data}},$sel,1; } } , $self); $hbox=Gtk2::HBox->new(); $mainvbox->pack_start($hbox,0,0,4); $hbox->show(); $wid=Gtk2::Button->new("Save Skill"); $hbox->pack_start($wid,1,1,0); $wid->show(); $wid->signal_connect(clicked => \&saveEditedSkill , $self); $wid=Gtk2::Button->new("Cancel"); $hbox->pack_start($wid,1,1,0); $wid->show(); $wid->signal_connect(clicked => \&closeEditSkillWin , $self); $self->{'skillwin'}-> signal_connect(delete_event => sub { closeEditSkillWin($_[0],$_[2]); } , $self); $self->{'skillwin'}-> signal_connect(destroy_event => sub { closeEditSkillWin($_[0],$_[2]); } , $self); $self->{'skillwin'}->show(); } sub saveEditedSkill { my $inwid=shift; my $self=shift; my (@sel,$sel,$skill,$costline,$lineline); @sel=$self->{'skills'}->get_selected_indices(); (@sel) || return; $sel=$sel[0]; return if ($sel<0 || $sel>$#{$self->{'skills'}->{data}}); $skill=$self->{'skills'}->{data}->[$sel]->[2]; $skill->{'name'}=$self->{'skillwinskill'}->get_text(); $skill->{'points'}=$self->{'skillwinpoints'}->get_text(); $skill->{'optspec'}=$self->{'skillwinoptspec'}->get_text(); $skill->{'defaultbase'}=$self->{'skillwindefaultbase'}->get_text(); $skill->{'defaultoffset'}=$self->{'skillwindefaultoffset'}->get_text(); $skill->{'bonus'}=[@{$self->{'skillwinbonus'}->{data}}]; $skill->{'baseatt'}=('ST','DX','IQ', 'HT','Will','Per')[$self->{'skillwinatt'}-> get_active()]; $skill->{'difficulty'}=('e','a','h','v','!')[$self->{'skillwindiff'}-> get_active()]; &updateEverything(0,$self); &closeEditSkillWin(0,$self); } sub closeEditSkillWin { my $wid=shift; my $self=shift; my (@keys,$key); $self->{'skillwin'}->hide(); $self->{'win'}->set_sensitive(1); @keys=grep(/^skillwin/,keys(%$self)); foreach $key (@keys) { delete $self->{$key}; } } # ********************************************************************** # addTechnique, deleteTechnique sub addTechnique { my $wid=shift; my $self=shift; my ($ordinal,$name,$cost,$att,$diff,@sel,$technique,$skill,$skilloff); @sel=$self->{'techniques'}->get_selected_indices(); if (@sel) { $ordinal=$sel[0]; } else { $ordinal=scalar @{$self->{'techniques'}->{data}}; } if ($ordinal<0) { $ordinal=0; } if ($ordinal>scalar @{$self->{'techniques'}->{data}}) { $ordinal=scalar @{$self->{'techniques'}->{data}}; } $cost=$self->{'newtechniquepoints'}->get_text(); $name=$self->{'newtechnique'}->get_text(); $diff=('a','h')[$self->{'newtechniquediff'}->get_active()]; $skill=$self->{'newtechniqueskill'}->get_text(); $skilloff=$self->{'newtechniqueskilldef'}->get_text(); $technique={ 'name' => $name , 'difficulty' => $diff, 'skill' => $skill, 'skilloffset' => $skilloff, 'points' => $cost, 'level' => '' }; splice @{$self->{'techniques'}->{data}},$ordinal,0,([$cost,$name, $technique]); $self->{'techniques'}->select($ordinal); $self->{'newtechniquepoints'}->set_text(''); $self->{'newtechnique'}->set_text(''); $self->{'newtechniqueskill'}->set_text(''); $self->{'newtechniqueskilldef'}->set_text(''); $self->{'newtechniquepoints'}->grab_focus(); &updateEverything(0,$self); } sub deleteTechnique { my $wid=shift; my $self=shift; my ($sel,@sel); @sel=$self->{'techniques'}->get_selected_indices(); foreach $sel (@sel) { splice @{$self->{'techniques'}->{data}},$sel,1; } &updateEverything(0,$self); } # ********************************************************************** # editTechnique sub editTechnique { my $inwid=shift; my $self=shift; my (@sel,$sel,$technique,$wid,$mainvbox,$tab,$hbox,$val,$i); @sel=$self->{'techniques'}->get_selected_indices(); (@sel) || return; $sel=$sel[0]; return if ($sel<0 || $sel>$#{$self->{'techniques'}->{data}}); $technique=$self->{'techniques'}->{data}->[$sel]->[2]; $self->{'win'}->set_sensitive(''); $self->{'techniquewin'}=Gtk2::Window->new(); $mainvbox=Gtk2::VBox->new(); $self->{'techniquewin'}->add($mainvbox); $mainvbox->show(); $tab=Gtk2::Table->new(4,2); $mainvbox->pack_start($tab,0,0,1); $tab->show(); $wid=Gtk2::Label->new("Technique:"); $tab->attach($wid,0,1,0,1,'fill','fill',0,0); $wid->show(); $self->{'techniquewintechnique'}=Gtk2::Entry->new(); $self->{'techniquewintechnique'}->set_text($technique->{'name'}); $tab->attach($self->{'techniquewintechnique'},1,2,0,1,'expand','fill',0,0); $self->{'techniquewintechnique'}->show(); $wid=Gtk2::Label->new("Points:"); $tab->attach($wid,0,1,1,2,'fill','fill',0,0); $wid->show(); $self->{'techniquewinpoints'}=Gtk2::Entry->new(); $self->{'techniquewinpoints'}->set_text($technique->{'points'}); $tab->attach($self->{'techniquewinpoints'},1,2,1,2,'expand','fill',0,0); $self->{'techniquewinpoints'}->show(); $wid=Gtk2::Label->new("Difficulty:"); $tab->attach($wid,0,1,3,4,'fill','fill',0,0); $wid->show(); $hbox=Gtk2::HBox->new(); $tab->attach($hbox,1,2,3,4,'fill','fill',0,0); $hbox->show(); $self->{'techniquewindiff'}=Gtk2::ComboBox->new_text(); $i=0; foreach $val ('A','H') { $self->{'techniquewindiff'}->append_text($val); if (lc($val) eq lc($technique->{'difficulty'})) { $self->{'techniquewindiff'}->set_active($i); } ++$i; } $hbox->pack_start($self->{'techniquewindiff'},0,0,0); $self->{'techniquewindiff'}->show(); $wid=Gtk2::Label->new("Skill/Def:"); $tab->attach($wid,0,1,4,5,'fill','fill',0,0); $wid->show(); $hbox=Gtk2::HBox->new(); $tab->attach($hbox,1,2,4,5,'expand','fill',0,0); $hbox->show(); $self->{'techniquewinskill'}=Gtk2::Entry->new(); $self->{'techniquewinskill'}->set_text($technique->{'skill'}); $hbox->pack_start($self->{'techniquewinskill'},1,1,0); $self->{'techniquewinskill'}->show(); $self->{'techniquewinskilloffset'}=Gtk2::Entry->new(); $self->{'techniquewinskilloffset'}->set_text($technique->{'skilloffset'}); $hbox->pack_start($self->{'techniquewinskilloffset'},0,0,0); $self->{'techniquewinskilloffset'}->show(); $self->{'techniquewinskilloffset'}-> set_size_request($self->{'techniquewinskilloffset'}-> get_pango_context()-> get_font_description()-> get_size() * 4 / Gtk2::Pango->scale , -1); $hbox=Gtk2::HBox->new(); $mainvbox->pack_start($hbox,0,0,4); $hbox->show(); $wid=Gtk2::Button->new("Save Technique"); $hbox->pack_start($wid,1,1,0); $wid->show(); $wid->signal_connect(clicked => \&saveEditedTechnique , $self); $wid=Gtk2::Button->new("Cancel"); $hbox->pack_start($wid,1,1,0); $wid->show(); $wid->signal_connect(clicked => \&closeEditTechniqueWin , $self); $self->{'techniquewin'}-> signal_connect(delete_event => sub { closeEditTechniqueWin($_[0],$_[2]); } , $self); $self->{'techniquewin'}-> signal_connect(destroy_event => sub { closeEditTechniqueWin($_[0],$_[2]); } , $self); $self->{'techniquewin'}->show(); } sub saveEditedTechnique { my $inwid=shift; my $self=shift; my (@sel,$sel,$technique,$costline,$lineline); @sel=$self->{'techniques'}->get_selected_indices(); (@sel) || return; $sel=$sel[0]; return if ($sel<0 || $sel>$#{$self->{'techniques'}->{data}}); $technique=$self->{'techniques'}->{data}->[$sel]->[2]; $technique->{'name'}=$self->{'techniquewintechnique'}->get_text(); $technique->{'points'}=$self->{'techniquewinpoints'}->get_text(); $technique->{'skill'}=$self->{'techniquewinskill'}->get_text(); $technique->{'skilloffset'}=$self->{'techniquewinskilloffset'}->get_text(); $technique->{'difficulty'}=('a','h')[$self->{'techniquewindiff'}-> get_active()]; &updateEverything(0,$self); &closeEditTechniqueWin(0,$self); } sub closeEditTechniqueWin { my $wid=shift; my $self=shift; my (@keys,$key); $self->{'techniquewin'}->hide(); $self->{'win'}->set_sensitive(1); @keys=grep(/^techniquewin/,keys(%$self)); foreach $key (@keys) { delete $self->{$key}; } } # ********************************************************************** # saveChar -- write an XML file sub saveChar { my $wid=shift; my $self=shift; my ($res, $fname, @tmp); # Make sure character is up to date &updateEverything(0,$self); if (!defined($self->{'savebox'}) || !$self->{'savebox'}) { $self->{'savebox'} = new Gtk2::FileChooserDialog( 'Save Character', $self->{'win'}, 'save', 'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok'); } $self->{'savebox'}->set_title('Save Character ' . $self->{'char'}->name()); if (defined($self->{'currentdir'}) && $self->{'currentdir'}) { $self->{'savebox'}->set_current_folder($self->{'currentdir'}); } if ($self->{'char'}->filename()) { @tmp = File::Basename::fileparse($self->{'char'}->filename()); $fname = $tmp[0]; $self->{'savebox'}->set_current_name($fname); } $res = $self->{'savebox'}->run(); if ($res eq 'ok') { $self->{'char'}->writeXML($self->{'savebox'}->get_filename()); $self->{'currentdir'} = $self->{'savebox'}->get_current_folder(); } $self->{'savebox'}->hide(); } # ********************************************************************** # loadChar -- put up a filebox for a character name, open a new window sub loadChar { my $self=shift; my $res; if (!defined($self->{'loadbox'}) || !$self->{'loadbox'}) { $self->{'loadbox'} = new Gtk2::FileChooserDialog( 'Load Character', $self->{'win'}, 'open', 'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok'); } if (defined($self->{'currentdir'}) && $self->{'currentdir'}) { $self->{'loadbox'}->set_current_folder($self->{'currentdir'}); } $res = $self->{'loadbox'}->run(); if ($res eq 'ok') { my $char=new GTKChar::Char(); $char->readXML($self->{'loadbox'}->get_filename()); my $win=new GTKChar::CharWin($char); >KChar::CharWin::updateEverything('',$win); $win->{'currentdir'} = $self->{'loadbox'}->get_current_folder(); } $self->{'loadbox'}->hide(); } # ********************************************************************** # textExport sub textExport { my $wid=shift; my $self=shift; my (@tmp, $fname, $res); # Make sure character is up to date &updateEverything(0,$self); # Figure out the filename if (!defined($self->{'textexportbox'})) { $self->{'textexportbox'} = new Gtk2::FileChooserDialog( 'Text Export', $self->{'win'}, 'save', 'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok'); } $self->{'textexportbox'}->set_title('Save Character ' . $self->{'char'}->name()); if (defined($self->{'currentdir'}) && $self->{'currentdir'}) { $self->{'textexportbox'}->set_current_folder($self->{'currentdir'}); } if ($self->{'char'}->filename()) { @tmp = File::Basename::fileparse($self->{'char'}->filename()); $fname = $tmp[0]; $fname =~ s/xml$/txt/; print "Setting current name to $fname\n"; $self->{'textexportbox'}->set_current_name($fname); } $res = $self->{'textexportbox'}->run(); if ($res eq 'ok') { $self->{'char'}->writeText($self->{'textexportbox'}->get_filename()); } $self->{'textexportbox'}->hide(); }