extends Node const IMAGE_WIDTH := 1000 const IMAGE_HEIGHT := 2000 const SEED_TEXTURE_SIZE = 150 const COLOR_PALETTE : Array[Color] = [ Color("#78AEBA"), Color("#A7B35B"), Color("#DB6B75"), Color("#EC8E49"), Color("#F9FFCE"), ] const PLACEHOLDER_MATURE_TEXTURE: Texture = preload("res://entities/plants/assets/sprites/default/mature.png") const PLACEHOLDER_GROWING_TEXTURE: Texture = preload("res://entities/plants/assets/sprites/default/growing.png") # @export var parts_archetype_associations: Dictionary[PlantArchetype, PartArchetypeAssociation] TODO:: have the archetypes @export var bases: Array[PlantPart] @export var baby_bases: Array[PlantPart] @export var branches: Array[PlantPart] @export var n_branches: int = 2 @export var parts_mutation_associations: Dictionary[String, PartMutationAssociation] @export var seed_texture_sets: Array[SeedTextureSet] var rng := RandomNumberGenerator.new() func build_seed_texture(random_seed: int) -> Texture: rng.seed = random_seed var texture_set : SeedTextureSet = pick_random(seed_texture_sets) var image := Image.create(SEED_TEXTURE_SIZE,SEED_TEXTURE_SIZE, false, Image.FORMAT_RGBA8) for color_texture in texture_set.color_textures: var color_image = color_texture.get_image().duplicate() color_image.resize(SEED_TEXTURE_SIZE,SEED_TEXTURE_SIZE) modulate_image(color_image, pick_random(COLOR_PALETTE)) image.blend_rect( color_image, Rect2i(0,0,SEED_TEXTURE_SIZE,SEED_TEXTURE_SIZE), Vector2i.ZERO ) if texture_set.outline_texture: var outline_image = texture_set.outline_texture.get_image().duplicate() outline_image.resize(SEED_TEXTURE_SIZE,SEED_TEXTURE_SIZE) image.blend_rect(outline_image, Rect2i(0,0,SEED_TEXTURE_SIZE,SEED_TEXTURE_SIZE),Vector2i.ZERO) if rng.randi()%2 == 0: image.flip_x() return ImageTexture.create_from_image(image) func build_plant_texture(plant_data: PlantData) -> Texture: rng.seed = plant_data.random_seed var mature_texture: Texture = PLACEHOLDER_MATURE_TEXTURE var mature_image: Image = Image.create_empty(IMAGE_WIDTH, IMAGE_HEIGHT, false, Image.FORMAT_RGBA8) var mature_image_center: Vector2i = 0.5 * mature_image.get_size() var branch_parts: Array[PlantPart] var base_part: PlantPart var available_base_attaches: Array[Vector2] var available_base_bottom_attach: Array[Vector2] var branch_attaches: Array[Vector2] var branch_root: Array[Vector2] var branch_parent_attach: Array[Vector2] var parts_to_place: Array[PlantPart] match plant_data.get_state(): PlantData.State.MATURE: print("Build mature texture") # var plant_archetype := plant_data.archetype if bases.size() == 0: printerr("No base in archetype") return mature_texture # var base_part: PlantPart = pick_random(parts_archetype_associations[plant_archetype].bases) base_part = pick_random(bases) var base_image = base_part.texture.get_image() var base_image_center: Vector2i = 0.5 * base_image.get_size() mature_image.blend_rect(base_image, Rect2i(Vector2i.ZERO, base_image.get_size()), mature_image_center - base_image_center - Vector2i(base_part.root)) if branches.size() == 0: printerr("No branches in archetype") # var branch_parts: Array[PlantPart] = parts_archetype_associations[plant_archetype].branches for i in n_branches: branch_parts.append(pick_random(branches)) for m in plant_data.mutations: print("mutations: ", m.id) var association: PartMutationAssociation = parts_mutation_associations[m.id] var mutation_possible_parts := association.parts for p in association.part_amount: parts_to_place.append(pick_random(mutation_possible_parts)) PlantData.State.GROWING: print("Build growing texture") # var plant_archetype := plant_data.archetype if baby_bases.size() == 0: printerr("No baby base in archetype") return mature_texture # var base_part: PlantPart = pick_random(parts_archetype_associations[plant_archetype].baby_bases) base_part = pick_random(baby_bases) var base_image = base_part.texture.get_image() var base_image_center: Vector2i = 0.5 * base_image.get_size() mature_image.blend_rect(base_image, Rect2i(Vector2i.ZERO, base_image.get_size()), mature_image_center - base_image_center - Vector2i(base_part.root)) for m in plant_data.mutations: print("mutations: ", m.id) var association: PartMutationAssociation = parts_mutation_associations[m.id] var mutation_possible_parts := association.parts for p in ceil(0.5 * association.part_amount): parts_to_place.append(pick_random(mutation_possible_parts)) _: print("Not handled state") return null available_base_attaches = base_part.attaches.duplicate() available_base_bottom_attach = base_part.bottom_attaches.duplicate() assert(branch_parts.size() <= base_part.attaches.size(), str("More branches (", branch_parts.size(), ") than base attaches (", base_part.attaches.size(), ")")) for branch in branch_parts: if available_base_attaches.size() == 0: break var ind: int = rng.randi_range(0, available_base_attaches.size() - 1) var attach: Vector2 = available_base_attaches.pop_at(ind) var branch_image: Image = branch.texture.get_image() var branch_image_center: Vector2i = 0.5 * branch_image.get_size() mature_image.blend_rect(branch_image, Rect2i(Vector2i.ZERO, branch.texture.get_size()), mature_image_center - branch_image_center + Vector2i(attach - branch.root - base_part.root)) for branch_attach in branch.attaches: branch_attaches.append(branch_attach) branch_root.append(branch.root) branch_parent_attach.append(attach) if parts_to_place.size() > branch_attaches.size() + base_part.attaches.size() - branch_parts.size(): printerr("ERROR generating : Parts to place : ", parts_to_place.size(), "; Branch Attaches : ", branch_attaches.size(), "; Base attaches : ", base_part.attaches.size(), "; Branch parts : ", branch_parts.size()) for part: PlantPart in parts_to_place: print("create part") var attach: Vector2 var parent_root: Vector2 var chosen_attach_type: int = 0 var attachables: Array[int] if part.base_attachable && available_base_attaches.size() > 0: attachables.append(1) if part.bottom_attachable && available_base_bottom_attach.size() > 0: attachables.append(2) if part.branch_attachable && branch_attaches.size() > 0: attachables.append(3) # assert(attachables.size() > 0) if attachables.size() == 0: print("No attach available") continue chosen_attach_type = pick_random(attachables) if chosen_attach_type == 1: # base attach var ind := rng.randi_range(0, available_base_attaches.size() - 1) attach = available_base_attaches.pop_at(ind) parent_root = base_part.root elif chosen_attach_type == 2: # bottom var ind := rng.randi_range(0, available_base_bottom_attach.size() - 1) attach = available_base_bottom_attach.pop_at(ind) parent_root = base_part.root elif chosen_attach_type == 3: # branch var ind := rng.randi_range(0, branch_attaches.size() - 1) attach = branch_attaches.pop_at(ind) - branch_root.pop_at(ind) + branch_parent_attach.pop_at(ind) parent_root = base_part.root var part_image: Image = part.texture.get_image() var part_image_center: Vector2i = 0.5 * part_image.get_size() var relative_root_centered_pos: Vector2 = Vector2(mature_image_center - part_image_center) - part.root mature_image.blend_rect(part_image, Rect2i(Vector2i.ZERO, part.texture.get_size()), relative_root_centered_pos + attach - parent_root) if rng.randi() % 2 == 0: mature_image.flip_x() mature_texture = ImageTexture.create_from_image(mature_image) return mature_texture func pick_random(array: Array): return array[rng.randi_range(0, array.size() - 1)] func modulate_image(i : Image, color : Color): for x in i.get_size().x: for y in i.get_size().y: i.set_pixel(x,y, i.get_pixel(x,y)*color)