class TileDrawingHelper
Autotiles = [
[ [27, 28, 33, 34], [ 5, 28, 33, 34], [27, 6, 33, 34], [ 5, 6, 33, 34],
[27, 28, 33, 12], [ 5, 28, 33, 12], [27, 6, 33, 12], [ 5, 6, 33, 12] ],
[ [27, 28, 11, 34], [ 5, 28, 11, 34], [27, 6, 11, 34], [ 5, 6, 11, 34],
[27, 28, 11, 12], [ 5, 28, 11, 12], [27, 6, 11, 12], [ 5, 6, 11, 12] ],
[ [25, 26, 31, 32], [25, 6, 31, 32], [25, 26, 31, 12], [25, 6, 31, 12],
[15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ],
[ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],
[39, 40, 45, 46], [ 5, 40, 45, 46], [39, 6, 45, 46], [ 5, 6, 45, 46] ],
[ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
[17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ],
[ [37, 38, 43, 44], [37, 6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
[37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1, 2, 7, 8] ]
]
# converts neighbors returned from tableNeighbors to tile indexes
NeighborsToTiles=[
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
37, 27, 37, 27, 23, 15, 23, 13, 37, 27, 37, 27, 22, 11, 22, 9,
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
36, 26, 36, 26, 21, 7, 21, 5, 36, 26, 36, 26, 20, 3, 20, 1, 46,
44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40, 42,
32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16, 46,
44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40, 42,
32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16, 45,
38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28, 37,
25, 37, 25, 23, 14, 23, 12, 37, 25, 37, 25, 22, 10, 22, 8, 45,
38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28, 36,
24, 36, 24, 21, 6, 21, 4, 36, 24, 36, 24, 20, 2, 20, 0
]
def self.tableNeighbors(data,x,y)
return 0 if x>=data.xsize || x<0
return 0 if y>=data.ysize || y<0
t=data[x,y]
xp1=[x+1,data.xsize-1].min
yp1=[y+1,data.ysize-1].min
xm1=[x-1,0].max
ym1=[y-1,0].max
i=0
i|=0x01 if data[x ,ym1]==t # N
i|=0x02 if data[xp1,ym1]==t # NE
i|=0x04 if data[xp1,y ]==t # E
i|=0x08 if data[xp1,yp1]==t # SE
i|=0x10 if data[x ,yp1]==t # S
i|=0x20 if data[xm1,yp1]==t # SW
i|=0x40 if data[xm1,y ]==t # W
i|=0x80 if data[xm1,ym1]==t # NW
return i
end
attr_accessor :tileset
attr_accessor :autotiles
def initialize(tileset,autotiles)
@tileset=tileset
@autotiles=autotiles
end
def self.fromTileset(tileset)
bmtileset=RPG::Cache.tileset(tileset.tileset_name)
bmautotiles=[]
for i in 0...7
bmautotiles.push(RPG::Cache.autotile(tileset.autotile_names[i]))
end
return self.new(bmtileset,bmautotiles)
end
def bltSmallAutotile(bitmap,x,y,cxTile,cyTile,id,frame)
return if frame<0 || !@autotiles || id>=384
autotile=@autotiles[id/48-1]
return if !autotile || autotile.disposed?
cxTile=[cxTile/2,1].max
cyTile=[cyTile/2,1].max
if autotile.height==32
anim=frame*32
src_rect=Rect.new(anim,0,32,32)
bitmap.stretch_blt(Rect.new(x,y,cxTile*2,cyTile*2),autotile,src_rect)
else
anim=frame*96
id%=48
tiles = TileDrawingHelper::Autotiles[id>>3][id&7]
src=Rect.new(0,0,0,0)
for i in 0...4
tile_position = tiles[i] - 1
src.set(tile_position % 6 * 16 + anim,
tile_position / 6 * 16, 16, 16)
bitmap.stretch_blt(Rect.new(i%2*cxTile+x,i/2*cyTile+y,cxTile,cyTile),
autotile, src)
end
end
end
def bltSmallRegularTile(bitmap,x,y,cxTile,cyTile,id)
return if !@tileset || id<384 || @tileset.disposed?
rect=Rect.new((id - 384) % 8 * 32, (id - 384) / 8 * 32,32,32)
bitmap.stretch_blt(Rect.new(x,y,cxTile,cyTile),@tileset,rect)
end
def bltSmallTile(bitmap,x,y,cxTile,cyTile,id,frame=0)
if id>=384
bltSmallRegularTile(bitmap,x,y,cxTile,cyTile,id)
elsif id>0
bltSmallAutotile(bitmap,x,y,cxTile,cyTile,id,frame)
end
end
def bltAutotile(bitmap,x,y,id,frame)
bltSmallAutotile(bitmap,x,y,32,32,id,frame)
end
def bltRegularTile(bitmap,x,y,id)
bltSmallRegularTile(bitmap,x,y,32,32,id)
end
def bltTile(bitmap,x,y,id,frame=0)
if id>=384
bltRegularTile(bitmap,x,y,id)
elsif id>0
bltAutotile(bitmap,x,y,id,frame)
end
end
end
def floodFillInternal(table,x,y,target,fillbits,points)
w=table.xsize
xLeft=x
xRight=x
yFromOrg=y
xFromOrg=x
curpos=yFromOrg*w+xFromOrg
begin
if !fillbits[curpos]
points.push([xLeft,y])
end
fillbits[curpos]=true
curpos-=1
xLeft-=1
end while (xLeft>=0&&
table[xLeft,y]==target&&
!fillbits[curpos])
xLeft+=1
curpos=yFromOrg*w+xFromOrg
begin
if !fillbits[curpos]
points.push([xRight,y])
end
fillbits[curpos]=true
curpos+=1
xRight+=1
end while(xRight<table.xsize&&
table[xRight,y]==target&&
!fillbits[curpos])
xRight-=1
curpos=yFromOrg-w+(xLeft)
for i in xLeft..xRight
above=(yFromOrg-1)*w+(i)
below=(yFromOrg+1)*w+(i)
if(y> 0 &&table[i,y-1]==target&&!fillbits[above])
ret=floodFillInternal(table,i,y-1,target,fillbits,points)
end
if(y<(table.ysize-1)&&table[i,y+1]==target&&!fillbits[below])
ret=floodFillInternal(table,i,y+1,target,fillbits,points)
end
end
end
N=0x01
NE=0x02
E=0x04
SE=0x08
S=0x10
SW=0x20
W=0x40
NW=0x80
BADNEIGHBORS=[
0,
SE,
NW,
NE,
SW,
S|SW,
S|SE,
N|NW,
N|NE,
W|SW,
W|NW,
E|NE,
E|SE,
S|SW|SE,
N|NW|NE,
W|NW|SW,
E|NE|SE
]
def isBadNeighbor?(neighbor)
return true if BADNEIGHBORS.any?{|i| i==neighbor }
return true if (neighbor&(S|W))==(0) && (neighbor&(SW))!=0
return true if (neighbor&(N|W))==(0) && (neighbor&(NW))!=0
return true if (neighbor&(S|E))==(0) && (neighbor&(SE))!=0
return true if (neighbor&(N|E))==(0) && (neighbor&(NE))!=0
return true if (neighbor&(NW|NE))==(0) && (neighbor&(N))!=0
return true if (neighbor&(SW|SE))==(0) && (neighbor&(S))!=0
return true if (neighbor&(NW|SW))==(0) && (neighbor&(W))!=0
return true if (neighbor&(NE|SE))==(0) && (neighbor&(E))!=0
end
def pbTableMoveToNeighbor(table,pointtable,value)
1000.times do
x=rand(table.xsize)
y=rand(table.ysize)
next if table[x,y]!=0
table[x,y]=value
nb=TileDrawingHelper.tableNeighbors(table,x,y)
if isBadNeighbor?(nb)
table[x,y]=0
next
end
if nb==0 && rand(100)<50
table[x,y]=0
next
end
pointtable[x,y]=2
break
end
end
def pbTablePlaceValue(table,value,minNumber,minbunch)
return if minNumber<=0
# Phase 1: Place _minNumber_ points on the map
1000.times do
1000.times do
x=rand(table.xsize-1)
y=rand(table.ysize-1)
placepos=[]
if table[x,y]==0
table[x,y]=value
placepos.push([x,y])
end
if table[x+1,y]==0
table[x+1,y]=value
placepos.push([x+1,y])
end
if table[x,y+1]==0
table[x,y+1]=value
placepos.push([x,y+1])
end
if table[x+1,y+1]==0
table[x+1,y+1]=value
placepos.push([x+1,y+1])
end
placed=placepos.length
for pp in placepos
nb=TileDrawingHelper.tableNeighbors(table,pp[0],pp[1])
if isBadNeighbor?(nb)
table[pp[0],pp[1]]=0
placed-=1
end
end
minNumber-=placed
break
end
break if minNumber<=0
end
# Phase 2: Move all bad points
badpoints=0
begin
badpoints=0
pointtable=Table.new(table.xsize,table.ysize)
fillbits=[]
# Find all bad points with flood filling
if minbunch>0
table.xsize.times{|x|
table.ysize.times{|y|
next if table[x,y]!=value
index=y*table.xsize+x
if !fillbits[index]
points=[]
floodFillInternal(table,x,y,value,fillbits,points)
isbad=(points.length<minbunch)
havebadpoints=true if isbad
badpoints+=points.length if isbad
for p in points
pointtable[p[0],p[1]]=isbad ? 1 : 2
end
end
}
}
end
# Find all inappropriately placed points
pointtable.xsize.times{|x|
pointtable.ysize.times{|y|
next if pointtable[x,y]==1
next if table[x,y]!=value
nb=TileDrawingHelper.tableNeighbors(table,x,y)
if isBadNeighbor?(nb)
pointtable[x,y]=1
badpoints+=1
end
if nb==(0xEF) # everything but South
table[x,y+1]=value
end
if nb==(0xBF) # everything but West
table[x-1,y]=value
end
if nb==(0xFB) # everything but East
table[x+1,y]=value
end
if nb==(0xFE) # everything but North
table[x,y-1]=value
end
}
}
if badpoints>0
# Move all bad points to new locations
pointtable.xsize.times{|x|
pointtable.ysize.times{|y|
next if pointtable[x,y]!=1 # skip if not a bad point
pointtable[x,y]=0
table[x,y]=0
pbTableMoveToNeighbor(table,pointtable,value)
}
}
end
end while badpoints>0
end
def pbGenerateMap(id,width,height,generateMapParams)
# Fill table with zeros
table=Table.new(width,height)
table.xsize.times{|x|
table.ysize.times{|y|
table[x,y]=0
}
}
# Place autotiles on the table
for i in 0...7
count=generateMapParams[i*2]
sticky=generateMapParams[i*2+1]
pbTablePlaceValue(table,i+1,count,sticky)
end
# Create map and fill it with grass
map=RPG::Map.new(width,height)
map.data.xsize.times{|x|
map.data.ysize.times{|y|
map.data[x,y,0]=384
}
}
# Fill map with autotiles
neighbors=TileDrawingHelper::NeighborsToTiles
for i in 0...map.width
for j in 0...map.height
if table[i,j]!=0
nb=TileDrawingHelper.tableNeighbors(table,i,j)
tile=neighbors[nb]
map.data[i,j,1]=tile+48*table[i,j]
end
end
end
# Save map
save_data(map,sprintf("Data/Map%03d.rxdata",id))
end
#Usage:
pbGenerateMap(
24, # Map ID
30, # Map Width
30, # Map Height
[ # Map Parameters
# count, sticky
20,8, # Autotile 1
30,8, # Autotile 2
75,8, # Autotile 3
250,15, # Autotile 4
0,0, # Autotile 5
0,0, # Autotile 6
0,0 # Autotile 7
]
)