minijam-166/scripts/terrain.gd
2024-09-04 20:04:20 +02:00

226 lines
5.3 KiB
GDScript

extends Node2D
enum Stats {WATER, FERTILITY, PRESENCE}
const TERRAIN_SIZE = Vector2i(300, 300)
const LEVELS_NUMBER : int = 20
const MAP_RATIO = 10;
@onready var image := Image.create_empty(
TERRAIN_SIZE.x,
TERRAIN_SIZE.y,
true,
Image.Format.FORMAT_RGBF
)
@onready var texture : ImageTexture = ImageTexture.create_from_image(image)
var sum := 0.0
func _ready():
setup_texture(Vector3i(0, 0, -5))
func map_to_pixel(
pos : Vector2
) -> Vector2i :
return Vector2i(
int(pos.x / MAP_RATIO),
int(pos.y / MAP_RATIO)
)
func is_on_map_real(pos: Vector2) -> bool:
return pos.x >= 0 and pos.x < TERRAIN_SIZE.x * MAP_RATIO and pos.y >= 0 and pos.y < TERRAIN_SIZE.y * MAP_RATIO
func is_on_map_image(pos: Vector2) -> bool:
return pos.x >= 0 and pos.x < TERRAIN_SIZE.x and pos.y >= 0 and pos.y < TERRAIN_SIZE.y
func color_value_to_level(
color_value : float
) -> int:
return roundi(color_value*LEVELS_NUMBER) - float(LEVELS_NUMBER)/2
func color_to_levels(
color: Color
) -> Vector3i :
return Vector3i(
color_value_to_level(color.r),
color_value_to_level(color.g),
color_value_to_level(color.b),
)
func level_to_color_value(
level : int
) -> float:
var limited_level = max(
min(
level,
float(LEVELS_NUMBER)/2
),
-float(LEVELS_NUMBER)/2
)
return float(limited_level+float(LEVELS_NUMBER)/2)/LEVELS_NUMBER
func levels_to_color(
levels: Vector3i
) -> Color :
return Color(
level_to_color_value(levels.x),
level_to_color_value(levels.y),
level_to_color_value(levels.z)
)
func modification_to_levels(
stat: Stats,
modification: int
) -> Vector3i :
var levels = Vector3i()
match stat:
Stats.WATER:
levels.x = modification
Stats.FERTILITY:
levels.y = modification
Stats.PRESENCE:
levels.z = modification
return levels
func modify_pixel(
pixel_pos: Vector2i,
stat: Stats,
modification: int,
):
if not is_on_map_image(pixel_pos):
return
var actual_levels = color_to_levels(image.get_pixelv(pixel_pos))
var modification_levels = modification_to_levels(stat, modification)
var calculated_levels = actual_levels + modification_levels
set_pixel(pixel_pos, calculated_levels)
func set_pixel(
pixel_pos: Vector2i,
level: Vector3i,
):
if not is_on_map_image(pixel_pos):
return
image.set_pixelv(pixel_pos, levels_to_color(level))
func modify_zone(
center: Vector2,
radius: float,
stat : Stats,
modification: int
):
var pixel_center = map_to_pixel(center)
var pixel_radius = int(radius / MAP_RATIO)
for x in range(pixel_center.x - pixel_radius, pixel_center.x + pixel_radius + 1) :
for y in range(pixel_center.y - pixel_radius, pixel_center.y + pixel_radius + 1):
if not is_on_map_image(Vector2i(x, y)):
continue
if pow(x - pixel_center.x,2) + pow(y - pixel_center.y,2) <= pow(pixel_radius,2):
modify_pixel(
Vector2i(x, y),
stat,
modification
)
update_texture()
func modify_zone_falloff(
center: Vector2,
radius: float,
falloff_extraradius: float,
stat : Stats,
modification: int
):
var pixel_center := map_to_pixel(center)
var pixel_radius := int(radius / MAP_RATIO)
var pixel_falloff_radius := int(falloff_extraradius / MAP_RATIO)
for x in range(pixel_center.x - pixel_radius - pixel_falloff_radius, pixel_center.x + pixel_radius + pixel_falloff_radius + 1) :
for y in range(pixel_center.y - pixel_radius - pixel_falloff_radius, pixel_center.y + pixel_radius + pixel_falloff_radius + 1):
if not is_on_map_image(Vector2i(x, y)):
continue
var pos := Vector2i(x, y)
var dist_to_center := pos.distance_to(pixel_center)
if dist_to_center <= pixel_radius:
modify_pixel(pos, stat, modification)
elif modification > 1 and dist_to_center <= pixel_radius + falloff_extraradius:
var new_modif = lerp(modification, 1, (dist_to_center - pixel_radius) / pixel_falloff_radius)
modify_pixel(pos, stat, new_modif)
update_texture()
func modify_rect(
pos: Vector2,
size: Vector2,
stat : Stats,
modification: int
):
var pixel_pos = map_to_pixel(pos)
var pixel_size = map_to_pixel(size)
for x in range(pixel_pos.x, pixel_pos.x+pixel_size.x) :
for y in range(pixel_pos.y, pixel_pos.y+pixel_size.y):
modify_pixel(
Vector2i(x, y),
stat,
modification
)
update_texture()
func get_color(
pos: Vector2
) -> Color:
var pixel_pos = map_to_pixel(pos)
return image.get_pixelv(pixel_pos)
func get_levels(
pos: Vector2,
) -> Vector3i:
if not is_on_map_real(pos):
return Vector3i()
var pixel_pos = map_to_pixel(pos)
return color_to_levels(image.get_pixelv(pixel_pos))
func get_stat(
pos: Vector2,
stat : Stats
) -> int:
var levels = get_levels(pos)
match stat:
Stats.WATER:
return levels.x
Stats.FERTILITY:
return levels.y
Stats.PRESENCE:
return levels.z
_:
return 0
func setup_texture(
levels : Vector3i
):
var water_noise := generate_noise()
var fertility_noise := generate_noise()
for x in range(0, TERRAIN_SIZE.x) :
for y in range(0, TERRAIN_SIZE.y):
set_pixel(
Vector2i(x,y),
Vector3i(
color_value_to_level(water_noise.get_noise_2d(x, y)),
color_value_to_level(fertility_noise.get_noise_2d(x, y)/2),
-LEVELS_NUMBER/2
)
)
update_texture()
func generate_noise() -> Noise:
var noise := FastNoiseLite.new()
noise.seed = randi()
noise.fractal_lacunarity = 1
noise.frequency = 0.005
noise.fractal_gain = 1000
return noise
func update_texture():
texture.update(image)
func get_texture():
return texture