-
Notifications
You must be signed in to change notification settings - Fork 0
/
worldgen.pas
204 lines (168 loc) · 6.6 KB
/
worldgen.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
unit WorldGen;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, allegro, PerlinNoiseUnit,boolUtils,ChunkUtils,Tilesets;
type
{ TWorldGenerator }
TWorldGenerator = class
public
constructor Create(seed: Integer);
destructor Destroy();override;
function GenerateChunkData(ChunkDataX,ChunkDataY, ChunkDatasize:Integer):TChunkData;
private
PerlinGenerator: TPerlinNoise;
cx,cy,lastcarvey:Integer;
procedure _genStepTerrain(var ChunkData:TChunkData);
procedure _genStepGround(var ChunkData:TChunkData);
procedure _genStepCarve(var ChunkData:TChunkData);
procedure _genStepTraps(var ChunkData:TChunkData);
procedure _genStepBlocks(var ChunkData:TChunkData);
end;
implementation
const
Octaves=4;
Persistance=6.5;
Frequency=2;
TrapChance=18; //there is 14 percent of chance that spike will spawn on ground or block.
{ TWorldGenerator }
constructor TWorldGenerator.Create(seed: Integer);
begin
inherited Create;
PerlinGenerator:= TPerlinNoise.Create(seed);
lastcarvey:=0;
end;
destructor TWorldGenerator.Destroy;
begin
inherited;
PerlinGenerator.Destroy;
end;
function TWorldGenerator.GenerateChunkData(ChunkDataX, ChunkDataY, ChunkDatasize: Integer
): TChunkData;
var
ChunkData:TChunkData;
begin
cx:=ChunkDatax;
cy:=ChunkDatay;
//setting dimensions of ChunkData
SetLength(ChunkData,ChunkDatasize,ChunkDatasize);
_genStepTerrain(ChunkData);
_genStepCarve(ChunkData);
_genStepBlocks(ChunkData);
_genStepGround(ChunkData);
_genStepTraps(ChunkData);
result:=ChunkData;
ChunkData:=nil;
end;
procedure TWorldGenerator._genStepTerrain(var ChunkData: TChunkData);
var i,perlinresult:Byte;
x,y,temp,ChunkDatasize:Integer;
neighbors:array[0..7]of byte;
begin
//getting length of ChunkData
ChunkDatasize:=Length(ChunkData);
//generation
for x:=0 to ChunkDatasize-1 do
begin
for y:=0 to ChunkDatasize-1 do
begin
//getting perlin for specified point
perlinresult:=Trunc(PerlinGenerator.PerlinNoise2d((cx*ChunkDatasize)+x,(cy*ChunkDatasize)+y,Persistance,Frequency,Octaves)*255);
//smoothing it so it won't look awful
neighbors[0]:=Trunc(PerlinGenerator.PerlinNoise2d(((cx*ChunkDatasize)+x)-1,(cy*ChunkDatasize)+y,Persistance,Frequency,Octaves)*255);
neighbors[1]:=Trunc(PerlinGenerator.PerlinNoise2d(((cx*ChunkDatasize)+x)+1,(cy*ChunkDatasize)+y,Persistance,Frequency,Octaves)*255);
neighbors[2]:=Trunc(PerlinGenerator.PerlinNoise2d((cx*ChunkDatasize)+x,((cy*ChunkDatasize)+y)-1,Persistance,Frequency,Octaves)*255);
neighbors[3]:=Trunc(PerlinGenerator.PerlinNoise2d((cx*ChunkDatasize)+x,((cy*ChunkDatasize)+y)+1,Persistance,Frequency,Octaves)*255);
neighbors[4]:=Trunc(PerlinGenerator.PerlinNoise2d(((cx*ChunkDatasize)+x)-1,((cy*ChunkDatasize)+y)-1,Persistance,Frequency,Octaves)*255);
neighbors[5]:=Trunc(PerlinGenerator.PerlinNoise2d(((cx*ChunkDatasize)+x)+1,((cy*ChunkDatasize)+y)-1,Persistance,Frequency,Octaves)*255);
neighbors[6]:=Trunc(PerlinGenerator.PerlinNoise2d(((cx*ChunkDatasize)+x)+1,((cy*ChunkDatasize)+y)+1,Persistance,Frequency,Octaves)*255);
neighbors[7]:=Trunc(PerlinGenerator.PerlinNoise2d(((cx*ChunkDatasize)+x)-1,((cy*ChunkDatasize)+y)+1,Persistance,Frequency,Octaves)*255);
temp:= perlinresult div 2;
for i:=0 to 7 do begin
temp:= temp + (neighbors[i] div 12);
end;
if temp>255 then temp:=255;
perlinresult:=temp;
//standard ground
if Between(perlinresult,90,256) then ChunkData[x][y]:=GROUNDID
else ChunkData[x][y]:= AIRID;
end;
end;
end;
procedure TWorldGenerator._genStepGround(var ChunkData: TChunkData);
var ChunkDatasize,x,y:Integer;
begin
ChunkDatasize:=Length(ChunkData);
for x:=0 to ChunkDatasize-1 do begin
for y:=1 to ChunkDatasize-2 do begin
//we start at Y=1 and end one index before end of ChunkData as we need to test block before that for being
//air and if there is something under.
if ((ChunkData[x][y]=AIRID) and (ChunkData[x][y-1]=AIRID) and (ChunkData[x][y+1]=GROUNDID))
then ChunkData[x][y]:=INVINCIBLEBLOCKID;
end;
end;
end;
procedure TWorldGenerator._genStepCarve(var ChunkData: TChunkData);
var x,ChunkDatasize:Integer;
begin
// writeln;
//writeln('-------NEW ChunkData---------');
{
This function uses what I call Worm Algorithm, Dunno if it was made previously
by someone else (probably was) and how it is proffessionally called. I made it
on my own.
Reason for name of it is that it carves cave in a way worm would do it. This
ensures continous play area without some impenetrable walls that would stop
player.
}
ChunkDatasize:=Length(ChunkData);
if lastcarvey=0 then lastcarvey:=ChunkDatasize div 2;
for x:=0 to ChunkDatasize do begin
ChunkDataCircle(ChunkData,x,lastcarvey,3,AIRID,true);
if lastcarvey > ChunkDatasize div 2 then lastcarvey:=lastcarvey-Random(4) else
if lastcarvey < (ChunkDatasize div 2) then lastcarvey:=lastcarvey+Random(2) else
lastcarvey:=lastcarvey-Random(6)+6;
if lastcarvey>ChunkDatasize then lastcarvey :=ChunkDatasize-5;
//write(lastcarvey,'|');
end;
end;
procedure TWorldGenerator._genStepTraps(var ChunkData: TChunkData);
var ChunkDatasize,x,y:Integer;
begin
ChunkDatasize:=Length(ChunkData);
for x:=0 to ChunkDatasize-1 do begin
for y:=1 to ChunkDatasize-2 do begin
//we start at Y=1 and end one index before end of ChunkData as we need to test block before that for being
//air and if there is something under.
if ((ChunkData[x][y]=AIRID) and (ChunkData[x][y-1]=AIRID) and ((ChunkData[x][y+1]=GROUNDID) or
(ChunkData[x][y+1]=INVINCIBLEBLOCKID)) and (Random(101)<TrapChance)) then ChunkData[x][y]:=SPIKEID;
//randomizing above is done so whole ground won't be in traps.
end;
end;
end;
procedure TWorldGenerator._genStepBlocks(var ChunkData: TChunkData);
var x1,y1,x2,y2,i,chunkdatasize:Integer;
begin
ChunkDatasize:=Length(ChunkData);
for i:=1 to 3 do //3 blocks per chunk is reasonable amount
begin
x1:=0;
y1:=0;
x2:=0;
y2:=0;
//we need to get x1,y1 and x2,y2 in some empty area of map
while chunkdata[x1][y1]<>AIRID do
begin
x1:=random(chunkdatasize);
y1:=random(chunkdatasize);
end;
while chunkdata[x2][y2]<>AIRID do
begin
x2:=random(chunkdatasize);
y2:=random(chunkdatasize);
end;
if abs(x1-x2)>3 then x2:=x1+3; //block of blocks can't be wider than 3 blocks. It can however be as high as it can.
ChunkDataRect(ChunkData,x1,y1,x2,y2,BLOCKID, true); //making rectangle in chunk.
end;
end;
end.