branches and one texture

This commit is contained in:
Altaezio
2026-02-13 11:18:34 +01:00
parent ab5089ad6c
commit e6d34a50a5
76 changed files with 359 additions and 1386 deletions

View File

@@ -1,9 +1,7 @@
@tool
extends Resource
class_name PlantArchetype
@export var archetype_name = Random.generate_random_word()
@export var texture_builder = TextureBuilder.new()
@export var archetype_name := Random.generate_random_word()
@export var plant_area_radius = 20
@export var plant_influence_radius = 100
@export var growing_time = 2
@@ -17,13 +15,9 @@ class_name PlantArchetype
QualityMutation.new(),
QuickMutation.new()
]
@export var bases: Array[PlantPart]
@export var branches: Array[PlantPart]
static func get_all() -> Array[PlantArchetype]:
return [
PlantArchetype.new()
]
return [PlantArchetype.new()]
static func get_random() -> PlantArchetype:
return get_all().pick_random()

View File

@@ -18,6 +18,8 @@ enum State {PLANTED, GROWING, MATURE, DEAD}
@export var leafs = 0 # +1 score
@export var roots = 0 # +1 lifetime
# var texture_builder: TextureBuilder = preload("res://entities/plants/scripts/texture_builder/texture_builder.tres")
func _init(
_position : Vector2 = Vector2.ZERO,
_archetype : PlantArchetype = PlantArchetype.get_random(),
@@ -83,12 +85,6 @@ func get_state() -> State:
return State.GROWING
return State.MATURE
func get_plant_texture() -> Texture:
return archetype.texture_builder.build_plant_texture(self)
func get_seed_texture():
return archetype.texture_builder.build_seed_texture(random_seed)
func get_seed_number(state = get_state()):
var seed_number = archetype.seed_number if (state == State.MATURE or state == State.DEAD) else 0
@@ -111,4 +107,4 @@ func get_random_seed_income():
)
func disappear():
disappeared.emit(self)
disappeared.emit(self)

View File

@@ -2,9 +2,8 @@ extends Resource
class_name PlantMutation
@export var level : int = 1
@export var possible_parts: Array[PlantPart]
@export var part_amount: int = 1
var id : String : get = get_mutation_id
var name : String : get = get_mutation_name
func _init(_level : int = 1):
@@ -16,6 +15,10 @@ func get_icon() -> Texture:
func get_base_rarity() -> int:
return 0
func get_mutation_id() -> String:
printerr("Classe abstraite PlantMutation appelée")
return ""
func get_mutation_name() -> String:
printerr("Classe abstraite PlantMutation appelée")
return ""

View File

@@ -9,6 +9,9 @@ func get_icon() -> Texture:
func get_base_rarity() -> int:
return 0
func get_mutation_id() -> String:
return "ANCIENT"
func get_mutation_name() -> String:
return tr("ANCIENT")

View File

@@ -7,6 +7,9 @@ func get_icon() -> Texture:
func get_base_rarity() -> int:
return 0
func get_mutation_id() -> String:
return "PRECOCIOUS"
func get_mutation_name() -> String:
return tr("PRECOCIOUS")

View File

@@ -7,6 +7,9 @@ func get_icon() -> Texture:
func get_base_rarity() -> int:
return 0
func get_mutation_id() -> String:
return "QUALITY"
func get_mutation_name() -> String:
return tr("QUALITY")

View File

@@ -7,6 +7,9 @@ func get_icon() -> Texture:
func get_base_rarity() -> int:
return 0
func get_mutation_id() -> String:
return "QUICK"
func get_mutation_name() -> String:
return tr("QUICK")

View File

@@ -16,9 +16,9 @@ func update_plant_sprite(plant_data : PlantData, with_animation = false):
await %AnimationPlayer.animation_finished
%Sprite.flip_h = true if plant_data.random_seed%2 == 0 else false
%Sprite.texture = plant_data.get_plant_texture()
%Sprite.texture = %TextureBuilder.build_plant_texture(plant_data)
%PlantedSeed.texture = plant_data.get_seed_texture()
%PlantedSeed.texture = %TextureBuilder.build_seed_texture(plant_data.random_seed)
%PlantedSeed.visible = plant_data.get_state() == PlantData.State.PLANTED
# %PlantedSeed.region_rect = Rect2(

View File

@@ -0,0 +1,6 @@
extends Resource
class_name PartArchetypeAssociation
@export var bases: Array[PlantPart]
@export var branches: Array[PlantPart]
@export var branches_amount: int = 1

View File

@@ -0,0 +1 @@
uid://c8wmu8a3gyg55

View File

@@ -1,47 +1,18 @@
extends Node
@export var n_plants_to_generate: int
@export var space_between_plants: int
@export var base_mutations: Array[PlantMutation]
@export var all_mutations: Array[PlantMutation]
@export var max_n_mutations := 2
# @export var base_parts: Array[PackedScene]
# @export var leaves_parts: Array[PackedScene]
# @export var branches_parts: Array[PackedScene]
# @export var flowers_parts: Array[PackedScene]
@export var space_between_plants: float
func _ready():
generate_plants();
func generate_plants():
var fail_safe := 0;
for i in n_plants_to_generate:
# func populate_part(parent: PlantPart, parent_node: Node2D, depth := 1):
# if parent.attaches.size() == 0:
# return
# if depth >= max_depth:
# return
# var n_sub_parts: int = randi() % parent.attaches.size();
# var available_parent_attaches: Array = range(parent.attaches.size());
# for i in n_sub_parts:
# var attach_ind_ind: int = randi() % available_parent_attaches.size();
# var parent_attach_ind: int = available_parent_attaches[attach_ind_ind];
# available_parent_attaches.pop_at(attach_ind_ind);
# var attach := parent.attaches[parent_attach_ind];
# var next_packed_scene: PackedScene;
# if parent.type == PlantPart.PartType.BASE_PART:
# next_packed_scene =
# elif parent.type == PlantPart.PartType.BRANCH_PART:
# var selected_root: Vector2 = next_part.roots.pick_random().position;
# var sprite := Sprite2D.new();
# sprite.texture = next_part.texture;
# sprite.position = attach.position - selected_root;
# parent_node.add_child(sprite);
# populate_part(next_part, sprite, depth + 1);
print("Generate plant")
var plant_position := Vector2(i * space_between_plants, 0)
var plant_data: PlantData = PlantData.new(plant_position)
plant_data.day = plant_data.get_growing_time()
plant_data.mutations.append(plant_data.archetype.available_mutations.pick_random())
var plant: Plant = Plant.new(plant_data)
add_child(plant)
plant.global_position = plant_position

View File

@@ -0,0 +1,5 @@
extends Resource
class_name PartMutationAssociation
@export var parts: Array[PlantPart]
@export var part_amount: int = 1

View File

@@ -0,0 +1 @@
uid://cfjd8jelpm8dt

View File

@@ -14,7 +14,7 @@ enum PartType {BASE_PART, BRANCH_PART, LEAF_PART, FLOWER_PART}
@export var bottom_attachable: bool
@export var branch_attachable: bool
func _init(textute_arg: Texture, root_arg: Vector2, attaches_arg: Array[Vector2], bottom_attaches_arg: Array[Vector2], type_arg: PartType, is_back_arg: bool, base_attachable_arg: bool, bottom_attachable_arg: bool, branch_attachable_arg: bool) -> void:
func init(textute_arg: Texture, root_arg: Vector2, attaches_arg: Array[Vector2], bottom_attaches_arg: Array[Vector2], type_arg: PartType, is_back_arg: bool, base_attachable_arg: bool, bottom_attachable_arg: bool, branch_attachable_arg: bool) -> void:
texture = textute_arg
root = root_arg
attaches = attaches_arg

View File

@@ -0,0 +1,82 @@
@tool
extends Sprite2D
class_name PlantPartScene
@export var part_name: String
@export_tool_button("Load resource") var load_resource_button = load_resource
@export var root: Node2D
@export var attaches: Node
@export var bottom_attaches: Node
@export var type: PlantPart.PartType
@export var is_back: bool
@export var base_attachable: bool
@export var bottom_attachable: bool
@export var branch_attachable: bool
@export_tool_button("Save as resource") var save_as_resource_button = save_as_resource
func load_resource():
var destination := "res://entities/plants/resources/plant_parts/" + part_name + ".tres"
print("Loading: ", part_name, " at: ", destination)
var plant_part = ResourceLoader.load(destination)
if plant_part is PlantPart:
root.position = plant_part.root
texture = plant_part.texture
var attaches_children := attaches.get_children()
for i in maxi(attaches_children.size(), plant_part.attaches.size()):
if i < attaches_children.size() && i < plant_part.attaches.size():
attaches_children[i].position = plant_part.attaches[i]
elif i >= attaches_children.size():
var new_child = Node2D.new()
new_child.name = "attach" + str(i)
new_child.position = plant_part.attaches[i]
attaches.add_child(new_child)
new_child.set_owner(self )
elif i >= plant_part.attaches.size():
attaches_children[i].free()
else:
printerr("Invalid code path")
var bottom_attaches_children := bottom_attaches.get_children()
for i in maxi(bottom_attaches_children.size(), plant_part.bottom_attaches.size()):
if i < bottom_attaches_children.size() && i < plant_part.bottom_attaches.size():
bottom_attaches_children[i].position = plant_part.bottom_attaches[i]
elif i >= bottom_attaches_children.size():
var new_child = Node2D.new()
new_child.name = "bottom_attach" + str(i)
new_child.position = plant_part.bottom_attaches[i]
bottom_attaches.add_child(new_child)
new_child.set_owner(self )
elif i >= plant_part.bottom_attaches.size():
bottom_attaches_children[i].free()
else:
printerr("Invalid code path")
type = plant_part.type
is_back = plant_part.is_back
base_attachable = plant_part.base_attachable
bottom_attachable = plant_part.bottom_attachable
branch_attachable = plant_part.branch_attachable
else:
printerr("Error loading ", part_name)
func save_as_resource():
var destination := "res://entities/plants/resources/plant_parts/" + part_name + ".tres"
print("Saving: ", part_name, " at: ", destination)
var attaches_vec2: Array[Vector2]
for attach in attaches.get_children():
attaches_vec2.append(attach.position)
var bottom_attaches_vec2: Array[Vector2]
for bottom_attach in bottom_attaches.get_children():
bottom_attaches_vec2.append(bottom_attach.position)
var plant_part = PlantPart.new()
plant_part.init(texture, root.position, attaches_vec2, bottom_attaches_vec2, type, is_back, base_attachable, bottom_attachable, branch_attachable)
plant_part.take_over_path(destination)
var err := ResourceSaver.save(plant_part, destination)
if err != OK:
printerr("Error saving resource: ", error_string(err))

View File

@@ -1,30 +0,0 @@
@tool
extends Sprite2D
class_name PlantPartScene
@export var root: Node2D
@export var attaches: Array[Node2D]
@export var bottom_attaches: Array[Node2D]
@export var type: PlantPart.PartType
@export var is_back: bool
@export var base_attachable: bool
@export var bottom_attachable: bool
@export var branch_attachable: bool
@export_tool_button("Save as resource") var save_as_resource_button = save_as_resource
func save_as_resource():
var destination := "res://entities/plants/resources/plant_parts/" + name + ".tres"
print("Saving: ", name, " at: ", destination)
var attaches_vec2: Array[Vector2]
for attach in attaches:
attaches_vec2.append(attach.position)
var bottom_attaches_vec2: Array[Vector2]
for bottom_attach in bottom_attaches:
bottom_attaches_vec2.append(bottom_attach.position)
var plant_part = PlantPart.new(texture, root.position, attaches_vec2, bottom_attaches_vec2, type, is_back, base_attachable, bottom_attachable, branch_attachable)
var err := ResourceSaver.save(plant_part, destination)
if err != OK:
printerr("Error saving resource: ", error_string(err))

View File

@@ -1,10 +1,18 @@
extends Resource
extends Node
class_name TextureBuilder
const IMAGE_WIDTH := 1000
const IMAGE_HEIGHT := 2000
const PLACEHOLDER_SEED_TEXTURE: Texture = preload("res://entities/plants/assets/sprites/default/seed.png")
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 branches: Array[PlantPart]
@export var n_branches: int = 2
@export var parts_mutation_associations: Dictionary[String, PartMutationAssociation]
func build_seed_texture(_random_seed: int) -> Texture:
return PLACEHOLDER_SEED_TEXTURE
@@ -14,78 +22,105 @@ func build_plant_texture(plant_data: PlantData) -> Texture:
PlantData.State.MATURE:
var mature_texture: Texture = PLACEHOLDER_MATURE_TEXTURE
var mature_image: Image
# var plant_archetype := plant_data.archetype
var base_part: PlantPart = plant_data.archetype.bases.pick_random();
if bases.size() == 0:
printerr("No base in archetype")
return mature_texture
# var base_part: PlantPart = parts_archetype_associations[plant_archetype].bases.pick_random()
var base_part: PlantPart = bases.pick_random()
mature_image = base_part.texture.get_image()
var base_image = base_part.texture.get_image()
mature_image = Image.create_empty(IMAGE_WIDTH, IMAGE_HEIGHT, false, Image.FORMAT_RGBA8)
var base_image_center: Vector2i = 0.5 * base_image.get_size()
var mature_image_center: Vector2i = 0.5 * mature_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))
var branch_parts: Array[PlantPart] = plant_data.archetype.branches
if branches.size() == 0:
printerr("No branches in archetype")
# var branch_parts: Array[PlantPart] = parts_archetype_associations[plant_archetype].branches
var branch_parts: Array[PlantPart]
for i in n_branches:
branch_parts.append(branches.pick_random())
var parts_to_place: Array[PlantPart];
var parts_to_place: Array[PlantPart]
for m in plant_data.mutations:
var mutation := m;
var mutation_possible_parts := mutation.possible_parts;
for p in mutation.part_amount:
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(mutation_possible_parts.pick_random())
var available_base_attaches: Array[Vector2] = base_part.attaches.duplicate();
var available_base_attaches: Array[Vector2] = base_part.attaches.duplicate()
var available_base_bottom_attach: Array[Vector2] = base_part.bottom_attaches.duplicate();
var available_base_bottom_attach: Array[Vector2] = base_part.bottom_attaches.duplicate()
var branch_attaches: Array[Vector2];
var branch_attach_parent: Array[PlantPart];
var branch_attaches: Array[Vector2]
var branch_root: Array[Vector2]
var branch_parent_attach: Array[Vector2]
assert(branch_parts.size() <= base_part.attaches.size());
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 = randi_range(0, available_base_attaches.size() - 1);
var attach: Vector2 = available_base_attaches.pop_at(ind);
var ind: int = 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()
mature_image.blend_rect(branch_image, Rect2i(Vector2.ZERO, branch.texture.get_size()), attach)
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_attach_parent.append(branch);
branch_attaches.append(branch_attach)
branch_root.append(branch.root)
branch_parent_attach.append(attach)
assert(parts_to_place.size() <= branch_attaches.size() + base_part.attaches.size() - branch_parts.size());
assert(parts_to_place.size() <= branch_attaches.size() + base_part.attaches.size() - branch_parts.size(),
str("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:
var attach: Vector2;
var parent_part: PlantPart;
var chosen_attach_type: int = 0;
var attachables: Array[int];
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);
attachables.append(1)
if part.bottom_attachable && available_base_bottom_attach.size() > 0:
attachables.append(2);
attachables.append(2)
if part.branch_attachable && branch_attaches.size() > 0:
attachables.append(3);
# assert(attachables.size() > 0);
attachables.append(3)
# assert(attachables.size() > 0)
if attachables.size() == 0:
print("No attach available")
continue
chosen_attach_type = attachables.pick_random();
chosen_attach_type = attachables.pick_random()
if chosen_attach_type == 1: # base attach
var ind := randi_range(0, available_base_attaches.size() - 1);
attach = available_base_attaches.pop_at(ind);
parent_part = base_part;
var ind := 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 := randi_range(0, available_base_bottom_attach.size() - 1);
attach = available_base_bottom_attach.pop_at(ind);
parent_part = base_part;
var ind := 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 := randi_range(0, branch_attaches.size() - 1);
attach = branch_attaches.pop_at(ind);
parent_part = branch_attach_parent.pop_at(ind);
var ind := 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()
mature_image.blend_rect(part_image, Rect2i(Vector2.ZERO, part.texture.get_size()), attach)
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 plant_data.random_seed % 2 == 0:
mature_image.flip_x()
mature_texture = ImageTexture.create_from_image(mature_image)
@@ -93,6 +128,8 @@ func build_plant_texture(plant_data: PlantData) -> Texture:
PlantData.State.GROWING:
print("Build growing texture")
return PLACEHOLDER_GROWING_TEXTURE
_:
return null
print("Not handled state")
return null