--- /dev/null
+{
+"classname" "__init_dedicated_server"
+// brush 0
+{
+( 0 0 0 ) ( 0 1 0 ) ( 0 0 1 ) common/caulk 0 0 0 0.5 0.5 0 0 0
+( 0 0 0 ) ( 1 0 0 ) ( 0 1 0 ) common/caulk 0 0 0 0.5 0.5 0 0 0
+( 0 0 0 ) ( 0 0 1 ) ( 1 0 0 ) common/caulk 0 0 0 0.5 0.5 0 0 0
+( 1 1 1 ) ( 1 1 0 ) ( 1 0 1 ) common/caulk 0 0 0 0.5 0.5 0 0 0
+( 1 1 1 ) ( 1 0 1 ) ( 0 1 1 ) common/caulk 0 0 0 0.5 0.5 0 0 0
+( 1 1 1 ) ( 0 1 1 ) ( 1 1 0 ) common/caulk 0 0 0 0.5 0.5 0 0 0
+}
+}
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my $dt = 0.5;
+my $pattern = '';
+my $time = 0;
+my $staccato = 0.25;
+my @script = ();
+
+while(<DATA>)
+{
+ chomp;
+ my (@arg) = split /\s+/, $_;
+ if($arg[0] eq 'time')
+ {
+ $time = $arg[1];
+ }
+ elsif($arg[0] eq 'bpm')
+ {
+ $dt = 60.0 / $arg[1];
+ }
+ elsif($arg[0] eq 'pattern')
+ {
+ $pattern = $arg[1];
+ }
+ elsif($arg[0] eq 'range')
+ {
+ my ($begin, $end) = ($arg[1], $arg[2]);
+ my $n = $end - $begin;
+ for(0..($n - 1))
+ {
+ my $char = substr $pattern, ($_ % length $pattern), 1;
+ push @script, [$char, $time, 1], [$char, $time + $dt * (1 - $staccato), 0]
+ unless $char eq '_';
+ $time += $dt;
+ }
+ }
+}
+
+for(sort { $a->[0] cmp $b->[0] or $a->[1] <=> $b->[1] } @script)
+{
+ printf "%s %f %d\n", @$_;
+}
+
+__DATA__
+time 0.200
+bpm 254
+pattern aaa_____aaa_____
+range 0 32
+pattern aaa_b__caaa_bccc
+range 32 160
+pattern aaa_b__caaa_b__c
+range 160 272
+pattern aaa_b__caaa_b_bb
+range 272 288
+pattern abc_c_c_c_c_c_c_
+range 288 352
+pattern aaa_b__caaa_bccc
+range 352 480
+pattern aaa_b__caaa_b__c
+range 480 592
+pattern aaa_b__caaa_b_bb
+range 592 608
+pattern aaa_b__caaa_b__c
+range 608 656
+pattern aaa_b__caaa_b_bb
+range 656 672
+pattern aaa_b__caaa_b__c
+range 672 720
+pattern aaa_b__caaa_b_bb
+range 720 736
+pattern aaa_b__caaa_b__c
+range 736 864
+pattern a_______________
+range 864 865
--- /dev/null
+#!/usr/bin/ruby
+
+require 'yaml'
+
+def preprocess(s)
+ return s.split(/\r?\n/).find_all() do |l| l[0, 2] != '//' end
+end
+
+base = ARGV[0]
+
+mapstr = File.read("#{base}.map")
+entstr = File.read("#{base}.ent")
+
+# backup
+File.open("#{base}.map~", "w") do |fh|
+ fh.write(mapstr)
+end
+File.open("#{base}.ent~", "w") do |fh|
+ fh.write(entstr)
+end
+
+def brushparse(l, index)
+ brush = []
+ nest = 0
+ while index < l.length()
+ case l[index]
+ when '}'
+ nest -= 1
+ if nest < 0
+ break
+ else
+ brush << '}'
+ end
+ when '{'
+ nest += 1
+ brush << '{'
+ when %r{^(.*)$}
+ brush << $1
+ when ''
+ true
+ else
+ raise SyntaxError, "in brush: " + l[index]
+ end
+ index += 1
+ end
+ return index, brush
+end
+
+def entparse(l, index)
+ entity = {}
+ brushes = []
+ while index < l.length()
+ case l[index]
+ when '}'
+ break
+ when '{'
+ index, brush = brushparse(l, index + 1)
+ brushes << brush
+ when %r{^"([^"]*)" "(.*)"$}
+ entity[$1] = $2
+ when ''
+ true
+ else
+ raise SyntaxError, "in entity: " + l[index]
+ end
+ index += 1
+ end
+ if brushes != []
+ entity[:brushes] = brushes
+ end
+ return index, entity
+end
+
+def mapparse(l)
+ allents = []
+ index = 0
+ while index < l.length()
+ case l[index]
+ when '{'
+ index, entity = entparse(l, index + 1)
+ allents << entity
+ when ''
+ true
+ else
+ raise SyntaxError, "in map: " + l[index]
+ end
+ index += 1
+ end
+ return allents
+end
+
+
+
+def brushout(b)
+ yield '{'
+ b.each() do |l|
+ yield l
+ end
+ yield '}'
+end
+
+def entout(e)
+ yield '{'
+ e.each() do |k, v|
+ next if k == :brushes
+ yield '"%s" "%s"' % [k, v]
+ end
+ if e[:brushes]
+ e[:brushes].each() do |b|
+ brushout(b) do |l|
+ yield l
+ end
+ end
+ end
+ yield '}'
+end
+
+def mapout(entities)
+ entities.each() do |e|
+ entout(e) do |l|
+ yield l
+ end
+ end
+end
+
+map = mapparse(preprocess(mapstr))
+ent = mapparse(preprocess(entstr))
+
+submodels = []
+unchanged = []
+
+map.each() do |e|
+ case
+ when e['classname'] == 'light'
+ unchanged << e
+ when e['classname'] == 'func_group'
+ unchanged << e
+ when e[:brushes]
+ submodels << e[:brushes]
+ end
+end
+
+ent.each() do |e|
+ case
+ when (e['classname'] == 'worldspawn') || (e['model'] && e['model'][0, 1] == '*')
+ e.delete('model')
+ e[:brushes] = submodels.shift()
+ end
+end
+
+File.open("#{base}.map", "w") do |fh|
+ mapout(ent + unchanged) do |l|
+ fh.puts(l)
+ end
+end
+
+File.unlink("#{base}.ent")
--- /dev/null
+//bot configuration: name model skin shirt-color pants-color team-override
+//default team values (team-override): 1 = red, 2 = blue, 3 = yellow, 4 = pink
+//use -1 for shirt-color or pants-color to get random colors
+Tutor grunt 0 1 0 0