Merge branch 'plant-builder'
This commit is contained in:
@@ -49,7 +49,7 @@ func generate_sprite() -> PlantSprite:
|
||||
var sprite_object : PlantSprite = SPRITE_SCENE.instantiate()
|
||||
|
||||
add_child(sprite_object)
|
||||
sprite_object.generate_mutation_effects(self)
|
||||
# sprite_object.generate_mutation_effects(self)
|
||||
|
||||
return sprite_object
|
||||
|
||||
|
||||
@@ -11,9 +11,12 @@ class_name PlantArchetype
|
||||
@export var seed_random_loose = 1
|
||||
@export var available_mutations: Array[PlantMutation] = [
|
||||
AncientMutation.new(),
|
||||
PrecociousMutation.new(),
|
||||
QualityMutation.new(),
|
||||
QuickMutation.new()
|
||||
EphemeralMutation.new(),
|
||||
FertileMutation.new(),
|
||||
PurificationMutation.new(),
|
||||
QuickMutation.new(),
|
||||
SocialMutation.new(),
|
||||
ToughMutation.new()
|
||||
]
|
||||
|
||||
static func get_all() -> Array[PlantArchetype]:
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
extends EntityData
|
||||
class_name PlantData
|
||||
|
||||
signal updated(p : PlantData)
|
||||
signal disappeared(p : PlantData)
|
||||
signal updated(p: PlantData)
|
||||
signal disappeared(p: PlantData)
|
||||
|
||||
enum State {PLANTED, GROWING, MATURE, DEAD}
|
||||
|
||||
@export var archetype: PlantArchetype
|
||||
@export var plant_name : String
|
||||
@export var mutations : Array[PlantMutation]
|
||||
@export var day : int :
|
||||
set(v):
|
||||
@export var plant_name: String
|
||||
@export var mutations: Array[PlantMutation]
|
||||
@export var day: int:
|
||||
set(v):
|
||||
day = v
|
||||
updated.emit(self)
|
||||
@export var random_seed : int
|
||||
updated.emit(self )
|
||||
@export var random_seed: int
|
||||
|
||||
@export var leafs = 0 # +1 score
|
||||
@export var roots = 0 # +1 lifetime
|
||||
@@ -21,11 +21,11 @@ enum State {PLANTED, GROWING, MATURE, DEAD}
|
||||
# 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(),
|
||||
_plant_name : String = Random.generate_random_word(),
|
||||
_mutations : Array[PlantMutation] = [],
|
||||
_day : int = 0,
|
||||
_position: Vector2 = Vector2.ZERO,
|
||||
_archetype: PlantArchetype = PlantArchetype.get_random(),
|
||||
_plant_name: String = Random.generate_random_word(),
|
||||
_mutations: Array[PlantMutation] = [],
|
||||
_day: int = 0,
|
||||
_random_seed = randi()
|
||||
):
|
||||
position = _position
|
||||
@@ -36,9 +36,9 @@ func _init(
|
||||
random_seed = _random_seed
|
||||
|
||||
for m in mutations:
|
||||
m.mutate_plant_data(self)
|
||||
m.mutate_plant_data(self )
|
||||
|
||||
static func generate_from_seed(plant_seed : Seed, plant_position : Vector2) -> PlantData:
|
||||
static func generate_from_seed(plant_seed: Seed, plant_position: Vector2) -> PlantData:
|
||||
return PlantData.new(
|
||||
plant_position,
|
||||
plant_seed.plant_archetype,
|
||||
@@ -56,7 +56,7 @@ func get_lifetime() -> int:
|
||||
var lifetime = archetype.lifetime + roots
|
||||
|
||||
for m in mutations:
|
||||
lifetime = m.mutate_lifetime(self, lifetime)
|
||||
lifetime = m.mutate_lifetime(self , lifetime)
|
||||
|
||||
return lifetime
|
||||
|
||||
@@ -64,15 +64,18 @@ func get_growing_time() -> int:
|
||||
var growing_time = archetype.growing_time
|
||||
|
||||
for m in mutations:
|
||||
growing_time = m.mutate_growing_time(self, growing_time)
|
||||
growing_time = m.mutate_growing_time(self , growing_time)
|
||||
|
||||
return growing_time
|
||||
|
||||
func get_score(state : State = get_state()) -> int:
|
||||
func get_score(state: State = get_state()) -> int:
|
||||
var score = archetype.base_score + leafs if state == State.MATURE else 0
|
||||
|
||||
var mult := 1
|
||||
|
||||
for m in mutations:
|
||||
score = m.mutate_score(self, score)
|
||||
score = m.mutate_score(self , score)
|
||||
mult = m.mutate_score_multiplier(self , mult)
|
||||
|
||||
return score
|
||||
|
||||
@@ -89,14 +92,14 @@ func get_seed_number(state = get_state()):
|
||||
var seed_number = archetype.seed_number if (state == State.MATURE or state == State.DEAD) else 0
|
||||
|
||||
for m in mutations:
|
||||
seed_number = m.mutate_seed_number(self, seed_number)
|
||||
seed_number = m.mutate_seed_number(self , seed_number)
|
||||
|
||||
return seed_number
|
||||
|
||||
func get_seed_random_loose():
|
||||
var seed_random_loose = archetype.seed_random_loose
|
||||
for m in mutations:
|
||||
seed_random_loose = m.mutate_seed_random_loose(self, seed_random_loose)
|
||||
seed_random_loose = m.mutate_seed_random_loose(self , seed_random_loose)
|
||||
|
||||
return seed_random_loose
|
||||
|
||||
@@ -107,4 +110,4 @@ func get_random_seed_income():
|
||||
)
|
||||
|
||||
func disappear():
|
||||
disappeared.emit(self)
|
||||
disappeared.emit(self )
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
extends Resource
|
||||
class_name PlantMutation
|
||||
|
||||
@export var level : int = 1
|
||||
@export var level: int = 1
|
||||
|
||||
var id : String : get = get_mutation_id
|
||||
var name : String : get = get_mutation_name
|
||||
var id: String: get = get_mutation_id
|
||||
var name: String: get = get_mutation_name
|
||||
|
||||
func _init(_level : int = 1):
|
||||
func _init(_level: int = 1):
|
||||
level = _level
|
||||
|
||||
func get_icon() -> Texture:
|
||||
@@ -27,40 +27,43 @@ func get_mutation_description() -> String:
|
||||
printerr("Classe abstraite PlantMutation appelée")
|
||||
return ""
|
||||
|
||||
func mutate_plant_data(_plant_data : PlantData):
|
||||
func mutate_plant_data(_plant_data: PlantData):
|
||||
pass
|
||||
|
||||
func mutate_score(_plant_data : PlantData, score : int) -> int:
|
||||
func mutate_score(_plant_data: PlantData, score: int) -> int:
|
||||
return score
|
||||
|
||||
func mutate_lifetime(_plant_data : PlantData, lifetime : int) -> int:
|
||||
func mutate_score_multiplier(_plant_data: PlantData, multiplier: int) -> int:
|
||||
return multiplier
|
||||
|
||||
func mutate_lifetime(_plant_data: PlantData, lifetime: int) -> int:
|
||||
return lifetime
|
||||
|
||||
func mutate_growing_time(_plant_data : PlantData, growing_time : int) -> int:
|
||||
func mutate_growing_time(_plant_data: PlantData, growing_time: int) -> int:
|
||||
return growing_time
|
||||
|
||||
func mutate_seed_number(_plant_data, seed_number):
|
||||
func mutate_seed_number(_plant_data: PlantData, seed_number: int):
|
||||
return seed_number
|
||||
|
||||
func mutate_seed_random_loose(_plant_data, seed_random_loose):
|
||||
func mutate_seed_random_loose(_plant_data: PlantData, seed_random_loose):
|
||||
return seed_random_loose
|
||||
|
||||
func _start_planted_effect(_plant : Plant):
|
||||
func _start_planted_effect(_plant: Plant):
|
||||
pass
|
||||
|
||||
func _start_day_effect(_plant : Plant):
|
||||
func _start_day_effect(_plant: Plant):
|
||||
pass
|
||||
|
||||
func _start_maturation_effect(_plant : Plant):
|
||||
func _start_maturation_effect(_plant: Plant):
|
||||
pass
|
||||
|
||||
func _start_dead_effect(_plant : Plant):
|
||||
func _start_dead_effect(_plant: Plant):
|
||||
pass
|
||||
|
||||
func _start_harvested_effect(_plant : Plant):
|
||||
func _start_harvested_effect(_plant: Plant):
|
||||
pass
|
||||
|
||||
func get_level_for_rarity(rarity : int) -> int :
|
||||
func get_level_for_rarity(rarity: int) -> int:
|
||||
return rarity - get_base_rarity() + 1
|
||||
|
||||
func get_rarity() -> int:
|
||||
@@ -78,7 +81,7 @@ func card_section() -> CardSectionInfo:
|
||||
return section
|
||||
|
||||
static func get_rarity_text(rarity) -> String:
|
||||
var rarity_text : Array[String] = [
|
||||
var rarity_text: Array[String] = [
|
||||
"COMMON",
|
||||
"RARE",
|
||||
"VERY_RARE",
|
||||
@@ -88,11 +91,11 @@ static func get_rarity_text(rarity) -> String:
|
||||
|
||||
if rarity < len(rarity_text):
|
||||
return rarity_text[rarity]
|
||||
else :
|
||||
else:
|
||||
return rarity_text[len(rarity_text) - 1]
|
||||
|
||||
static func get_rarity_color(rarity : int) -> Color:
|
||||
var rarity_colors : Array[Color] = [
|
||||
static func get_rarity_color(rarity: int) -> Color:
|
||||
var rarity_colors: Array[Color] = [
|
||||
Color("25C147"),
|
||||
Color("8B2DFF"),
|
||||
Color("FF006E"),
|
||||
|
||||
23
entities/plants/scripts/plant_mutation/ephemeral_mutation.gd
Normal file
23
entities/plants/scripts/plant_mutation/ephemeral_mutation.gd
Normal file
@@ -0,0 +1,23 @@
|
||||
extends PlantMutation
|
||||
class_name EphemeralMutation
|
||||
|
||||
func get_icon() -> Texture:
|
||||
return preload("res://common/icons/chevrons-up.svg")
|
||||
|
||||
func get_base_rarity() -> int:
|
||||
return 0
|
||||
|
||||
func get_mutation_id() -> String:
|
||||
return "EPHEMERAL"
|
||||
|
||||
func get_mutation_name() -> String:
|
||||
return tr("EPHEMERAL")
|
||||
|
||||
func get_mutation_description() -> String:
|
||||
return tr("EPHEMERAL_EFFECT_TEXT_LEVEL_%d") % level
|
||||
|
||||
func mutate_lifetime(_plant_data: PlantData, lifetime: int) -> int:
|
||||
return lifetime - ceil(0.5 * level)
|
||||
|
||||
func mutate_seed_number(_plant_data: PlantData, seed_number: int):
|
||||
return seed_number + level
|
||||
@@ -0,0 +1 @@
|
||||
uid://bmo42h1p554nv
|
||||
21
entities/plants/scripts/plant_mutation/fertile_mutation.gd
Normal file
21
entities/plants/scripts/plant_mutation/fertile_mutation.gd
Normal file
@@ -0,0 +1,21 @@
|
||||
extends PlantMutation
|
||||
class_name FertileMutation
|
||||
|
||||
func get_icon() -> Texture:
|
||||
return preload("res://common/icons/chevrons-up.svg")
|
||||
|
||||
func get_base_rarity() -> int:
|
||||
return 0
|
||||
|
||||
func get_mutation_id() -> String:
|
||||
return "FERTILE"
|
||||
|
||||
func get_mutation_name() -> String:
|
||||
return tr("FERTILE")
|
||||
|
||||
func get_mutation_description() -> String:
|
||||
return tr("FERTILE_EFFECT_TEXT_LEVEL_%d") % level
|
||||
|
||||
func mutate_lifetime(_plant_data : PlantData, lifetime : int) -> int:
|
||||
print("TODO:: Implemnt FERTILE lifetime based on plants around")
|
||||
return lifetime + level
|
||||
@@ -0,0 +1 @@
|
||||
uid://eslsw42a0ylv
|
||||
@@ -0,0 +1,21 @@
|
||||
extends PlantMutation
|
||||
class_name PurificationMutation
|
||||
|
||||
func get_icon() -> Texture:
|
||||
return preload("res://common/icons/chevrons-up.svg")
|
||||
|
||||
func get_base_rarity() -> int:
|
||||
return 0
|
||||
|
||||
func get_mutation_id() -> String:
|
||||
return "PURIFICATION"
|
||||
|
||||
func get_mutation_name() -> String:
|
||||
return tr("PURIFICATION")
|
||||
|
||||
func get_mutation_description() -> String:
|
||||
return tr("PURIFICATION_EFFECT_TEXT_LEVEL_%d") % level
|
||||
|
||||
func _start_maturation_effect(_plant : Plant):
|
||||
print("Implement purification maturation effect")
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
uid://buwy5v4yi3piv
|
||||
@@ -16,5 +16,8 @@ func get_mutation_name() -> String:
|
||||
func get_mutation_description() -> String:
|
||||
return tr("QUICK_EFFECT_TEXT_LEVEL_%d") % level
|
||||
|
||||
func mutate_score(_plant_data : PlantData, score : int) -> int:
|
||||
return score + level
|
||||
|
||||
func mutate_growing_time(_data : PlantData, grow_time : int) -> int:
|
||||
return max(grow_time - level, 0)
|
||||
return max(grow_time - level, 1)
|
||||
|
||||
21
entities/plants/scripts/plant_mutation/social_mutation.gd
Normal file
21
entities/plants/scripts/plant_mutation/social_mutation.gd
Normal file
@@ -0,0 +1,21 @@
|
||||
extends PlantMutation
|
||||
class_name SocialMutation
|
||||
|
||||
func get_icon() -> Texture:
|
||||
return preload("res://common/icons/chevrons-up.svg")
|
||||
|
||||
func get_base_rarity() -> int:
|
||||
return 0
|
||||
|
||||
func get_mutation_id() -> String:
|
||||
return "SOCIAL"
|
||||
|
||||
func get_mutation_name() -> String:
|
||||
return tr("SOCIAL")
|
||||
|
||||
func get_mutation_description() -> String:
|
||||
return tr("SOCIAL_EFFECT_TEXT_LEVEL_%d") % level
|
||||
|
||||
func mutate_score(_plant_data : PlantData, score : int) -> int:
|
||||
printerr("TODO:: implement SOCIAL score based on plants around")
|
||||
return score + level
|
||||
@@ -0,0 +1 @@
|
||||
uid://bhej2wpdfveu5
|
||||
23
entities/plants/scripts/plant_mutation/tough_mutation.gd
Normal file
23
entities/plants/scripts/plant_mutation/tough_mutation.gd
Normal file
@@ -0,0 +1,23 @@
|
||||
extends PlantMutation
|
||||
class_name ToughMutation
|
||||
|
||||
func get_icon() -> Texture:
|
||||
return preload("res://common/icons/chevrons-up.svg")
|
||||
|
||||
func get_base_rarity() -> int:
|
||||
return 0
|
||||
|
||||
func get_mutation_id() -> String:
|
||||
return "TOUGH"
|
||||
|
||||
func get_mutation_name() -> String:
|
||||
return tr("TOUGH")
|
||||
|
||||
func get_mutation_description() -> String:
|
||||
return tr("TOUGH_EFFECT_TEXT_LEVEL_%d") % level
|
||||
|
||||
func mutate_score_multiplier(_plant_data: PlantData, multiplier: int) -> int:
|
||||
return multiplier + 2
|
||||
|
||||
func mutate_growing_time(_plant_data: PlantData, growing_time: int) -> int:
|
||||
return growing_time + maxi(0, 3 - level)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bwsnyirytbnqj
|
||||
@@ -1,17 +1,72 @@
|
||||
extends Node
|
||||
|
||||
@export var n_plants_to_generate: int
|
||||
@export var n_plants_to_generate: int = 1
|
||||
@export var n_plants_per_row: int = 0
|
||||
@export var n_mutation_per_plant: int
|
||||
@export var space_between_plants: float
|
||||
@export var randomize_pos: bool
|
||||
@export var random_pos_offset: float
|
||||
|
||||
func _ready():
|
||||
%ZoomLevel.value = %Camera2D.zoom.x
|
||||
%NPlants.value = n_plants_to_generate
|
||||
%NMutationsPerPlant.value = n_mutation_per_plant
|
||||
%RandomizePos.button_pressed = randomize_pos
|
||||
%RandomizeOffset.value = random_pos_offset
|
||||
generate_plants();
|
||||
|
||||
func _input(_event) -> void:
|
||||
var input_dir = Input.get_vector("move_left", "move_right", "move_up", "move_down")
|
||||
%Camera2D.position += 1 / %Camera2D.zoom.x * 10 * input_dir
|
||||
|
||||
|
||||
func generate_plants():
|
||||
for child in %Plants.get_children():
|
||||
child.free()
|
||||
|
||||
for i in n_plants_to_generate:
|
||||
var plant_position := Vector2(i * space_between_plants, 0)
|
||||
print("Generate plant :")
|
||||
var plant_pos_x = (i % n_plants_per_row) * space_between_plants
|
||||
@warning_ignore("integer_division")
|
||||
var plant_pos_y = (i / n_plants_per_row) * space_between_plants
|
||||
var plant_position := Vector2(plant_pos_x, plant_pos_y)
|
||||
if randomize_pos:
|
||||
plant_position += randf_range(0, random_pos_offset) * Vector2.ONE.rotated(randf_range(0, 2 * PI))
|
||||
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())
|
||||
for j in n_mutation_per_plant:
|
||||
plant_data.mutations.append(plant_data.archetype.available_mutations.pick_random())
|
||||
var plant: Plant = Plant.new(plant_data)
|
||||
add_child(plant)
|
||||
print(plant.data.plant_name)
|
||||
%Plants.add_child(plant)
|
||||
plant.set_owner(self )
|
||||
plant.global_position = plant_position
|
||||
|
||||
|
||||
func _on_generate_plants_pressed() -> void:
|
||||
generate_plants()
|
||||
|
||||
|
||||
func _on_zoom_level_value_changed(value: float) -> void:
|
||||
%Camera2D.zoom = Vector2.ONE * value
|
||||
|
||||
|
||||
func _on_reset_zoom_pressed() -> void:
|
||||
%Camera2D.zoom = Vector2.ONE
|
||||
%ZoomLevel.value = 1
|
||||
|
||||
|
||||
func _on_n_plants_value_changed(value: float) -> void:
|
||||
n_plants_to_generate = int(value)
|
||||
|
||||
func _on_n_plants_per_row_value_changed(value: float) -> void:
|
||||
n_plants_per_row = int(value)
|
||||
|
||||
func _on_n_mutations_per_plant_value_changed(value: float) -> void:
|
||||
n_mutation_per_plant = int(value)
|
||||
|
||||
func _on_randomize_pos_toggled(toggled_on: bool) -> void:
|
||||
randomize_pos = toggled_on
|
||||
|
||||
func _on_randomize_offset_value_changed(value: float) -> void:
|
||||
random_pos_offset = value
|
||||
|
||||
7
entities/plants/scripts/texture_builder/plant_attach.gd
Normal file
7
entities/plants/scripts/texture_builder/plant_attach.gd
Normal file
@@ -0,0 +1,7 @@
|
||||
extends Resource
|
||||
class_name PlantAttach
|
||||
|
||||
enum AttachType {BIG_ATTACH, MEDIUM_ATTACH, FLOWER_ATTACH, SMALL_ATTACH}
|
||||
|
||||
@export var position: Vector2
|
||||
@export var attach_types: Array[AttachType]
|
||||
@@ -0,0 +1 @@
|
||||
uid://co46ywm8bgnfh
|
||||
@@ -0,0 +1,10 @@
|
||||
extends Node2D
|
||||
class_name PlantAttachBuilder
|
||||
|
||||
@export var attach_types: Array[PlantAttach.AttachType]
|
||||
|
||||
static func to_plant_attach(builder: PlantAttachBuilder) -> PlantAttach:
|
||||
var plant_attach := PlantAttach.new()
|
||||
plant_attach.position = builder.position
|
||||
plant_attach.attach_types = builder.attach_types
|
||||
return plant_attach
|
||||
@@ -0,0 +1 @@
|
||||
uid://d01kj3twwcrmu
|
||||
@@ -4,23 +4,20 @@ class_name PlantPart
|
||||
enum PartType {BASE_PART, BRANCH_PART, LEAF_PART, FLOWER_PART}
|
||||
|
||||
@export var texture: Texture
|
||||
@export var root: Vector2
|
||||
@export var attaches: Array[Vector2]
|
||||
@export var bottom_attaches: Array[Vector2]
|
||||
@export var type: PartType
|
||||
@export var root: PlantAttach
|
||||
@export var attaches: Array[PlantAttach] # array of vec2
|
||||
|
||||
@export var is_back: bool
|
||||
@export var base_attachable: bool
|
||||
@export var bottom_attachable: bool
|
||||
@export var branch_attachable: bool
|
||||
var image: Image: get = get_image
|
||||
|
||||
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, type_arg: PartType, root_arg: PlantAttach, attaches_arg: Array[PlantAttach]) -> void:
|
||||
texture = textute_arg
|
||||
image = texture.get_image()
|
||||
type = type_arg
|
||||
root = root_arg
|
||||
attaches = attaches_arg
|
||||
bottom_attaches = bottom_attaches_arg
|
||||
type = type_arg
|
||||
is_back = is_back_arg
|
||||
base_attachable = base_attachable_arg
|
||||
bottom_attachable = bottom_attachable_arg
|
||||
branch_attachable = branch_attachable_arg
|
||||
|
||||
func get_image():
|
||||
if image == null:
|
||||
image = texture.get_image()
|
||||
return image
|
||||
|
||||
@@ -1,80 +1,56 @@
|
||||
@tool
|
||||
extends Sprite2D
|
||||
class_name PlantPartScene
|
||||
class_name PlantPartBuilder
|
||||
|
||||
@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 var root: PlantAttachBuilder
|
||||
@export var attaches: Node
|
||||
|
||||
@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"
|
||||
var plant_part = ResourceLoader.load(destination)
|
||||
if plant_part is PlantPart:
|
||||
root.position = plant_part.root
|
||||
print("Loading: ", part_name, " at: ", destination)
|
||||
var plant_part: PlantPart = ResourceLoader.load(destination) as PlantPart
|
||||
if plant_part:
|
||||
texture = plant_part.texture
|
||||
type = plant_part.type
|
||||
root.position = plant_part.root.position
|
||||
root.attach_types = plant_part.root.attach_types
|
||||
|
||||
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]
|
||||
attaches_children[i].position = plant_part.attaches[i].position
|
||||
attaches_children[i].attach_types = plant_part.attaches[i].attach_types
|
||||
elif i >= attaches_children.size():
|
||||
var new_child = Node2D.new()
|
||||
var new_child = PlantAttachBuilder.new()
|
||||
new_child.name = "attach" + str(i)
|
||||
new_child.position = plant_part.attaches[i]
|
||||
new_child.position = plant_part.attaches[i].position
|
||||
new_child.attach_types = plant_part.attaches[i].attach_types
|
||||
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"
|
||||
var attaches_vec2: Array[Vector2]
|
||||
print("Saving: ", part_name, " at: ", destination)
|
||||
var attaches_vec2: Array[PlantAttach]
|
||||
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)
|
||||
attaches_vec2.append(PlantAttachBuilder.to_plant_attach(attach))
|
||||
|
||||
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)
|
||||
var err := ResourceSaver.save(plant_part, destination)
|
||||
plant_part.init(texture, type, PlantAttachBuilder.to_plant_attach(root), attaches_vec2)
|
||||
plant_part.resource_name = part_name
|
||||
var err := ResourceSaver.save(plant_part, destination, ResourceSaver.FLAG_REPLACE_SUBRESOURCE_PATHS)
|
||||
if err != OK:
|
||||
printerr("Error saving resource: ", error_string(err))
|
||||
else:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extends Node
|
||||
|
||||
const IMAGE_WIDTH := 1000
|
||||
const IMAGE_HEIGHT := 2000
|
||||
const IMAGE_WIDTH := 2048
|
||||
const IMAGE_HEIGHT := 2048
|
||||
|
||||
const SEED_TEXTURE_SIZE = 150
|
||||
|
||||
@@ -16,27 +16,73 @@ const COLOR_PALETTE : Array[Color] = [
|
||||
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")
|
||||
|
||||
enum OriginType {BRANCH_ORIGIN, MUTATION_ORIGIN, BASE_LEAF_ORIGIN}
|
||||
|
||||
# @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 base_leaves: Array[PlantPart]
|
||||
@export var parts_mutation_associations: Dictionary[String, PartMutationAssociation] # Array[PlantPart]
|
||||
|
||||
@export var chance_to_have_part := 0.75;
|
||||
|
||||
@export var origin_weights_base: Dictionary[OriginType, int] = {OriginType.BRANCH_ORIGIN: 10, OriginType.MUTATION_ORIGIN: 10, OriginType.BASE_LEAF_ORIGIN: 10}
|
||||
@export var origin_weight_loss := 3
|
||||
@export var origin_weight_gain := 1
|
||||
|
||||
@export var mutation_weight_base := 10
|
||||
@export var mutation_weight_loss := 3
|
||||
@export var mutation_weight_gain := 1
|
||||
@export var seed_texture_sets: Array[SeedTextureSet]
|
||||
|
||||
var rng := RandomNumberGenerator.new()
|
||||
var image: Image = Image.create_empty(IMAGE_WIDTH, IMAGE_HEIGHT, false, Image.FORMAT_RGBA8)
|
||||
var image_center: Vector2i = Vector2(0.5, 1) * Vector2(image.get_size())
|
||||
|
||||
func random_ind(array: Array) -> int:
|
||||
return rng.randi_range(0, array.size() - 1)
|
||||
|
||||
func pick_random(array: Array) -> Variant:
|
||||
return array[random_ind(array)]
|
||||
|
||||
func shuffle(array: Array):
|
||||
var available_ind := 0
|
||||
for i in range(array.size()):
|
||||
var temp = array[available_ind]
|
||||
var picked_ind := rng.randi_range(available_ind, array.size() - 1)
|
||||
array[available_ind] = array[picked_ind]
|
||||
array[picked_ind] = temp
|
||||
|
||||
func shuffle_weighted(array: Array, weights: Array[int]):
|
||||
assert(array.size() == weights.size(), "Suffle with weights not same size")
|
||||
|
||||
var indices := range(array.size())
|
||||
var random_values: Array[int] = []
|
||||
for i in range(array.size()):
|
||||
random_values.append(rng.randi_range(0, weights[i]))
|
||||
|
||||
indices.sort_custom(func(a, b): return random_values[a] > random_values[b])
|
||||
|
||||
var originalArray := array.duplicate()
|
||||
var originalWeights := weights.duplicate()
|
||||
|
||||
for i in range(array.size()):
|
||||
array[i] = originalArray[indices[i]]
|
||||
weights[i] = originalWeights[indices[i]]
|
||||
|
||||
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)
|
||||
var sedd_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(
|
||||
sedd_image.blend_rect(
|
||||
color_image,
|
||||
Rect2i(0,0,SEED_TEXTURE_SIZE,SEED_TEXTURE_SIZE),
|
||||
Vector2i.ZERO
|
||||
@@ -44,150 +90,117 @@ func build_seed_texture(random_seed: int) -> Texture:
|
||||
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)
|
||||
sedd_image.blend_rect(outline_image, Rect2i(0,0,SEED_TEXTURE_SIZE,SEED_TEXTURE_SIZE),Vector2i.ZERO)
|
||||
|
||||
if rng.randi()%2 == 0:
|
||||
image.flip_x()
|
||||
sedd_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 texture: Texture
|
||||
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)
|
||||
texture = PLACEHOLDER_MATURE_TEXTURE
|
||||
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)
|
||||
texture = PLACEHOLDER_GROWING_TEXTURE
|
||||
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()
|
||||
var weight_per_origin_type: Array[int] = origin_weights_base.values().duplicate()
|
||||
|
||||
assert(branch_parts.size() <= base_part.attaches.size(),
|
||||
str("More branches (", branch_parts.size(), ") than base attaches (", base_part.attaches.size(), ")"))
|
||||
var parts_to_place: Dictionary[OriginType, Array] # BRANCH_ORIGIN : Array[PlantPart], MUTATION_ORIGIN : Array[Array[PlantPart]], BASE_LEAF_ORIGIN : Array[PlantPart]
|
||||
parts_to_place[OriginType.BRANCH_ORIGIN] = branches
|
||||
parts_to_place[OriginType.MUTATION_ORIGIN] = []
|
||||
parts_to_place[OriginType.BASE_LEAF_ORIGIN] = base_leaves
|
||||
var mutation_weights: Array[int] = []
|
||||
for mutation in plant_data.mutations:
|
||||
parts_to_place[OriginType.MUTATION_ORIGIN].append(parts_mutation_associations[mutation.id].parts)
|
||||
mutation_weights.append(mutation_weight_base)
|
||||
|
||||
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 base_image_coord = blend_part(image_center, Vector2.ZERO, base_part)
|
||||
populate_part(parts_to_place, weight_per_origin_type, mutation_weights, base_part, base_image_coord)
|
||||
|
||||
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))
|
||||
texture = ImageTexture.create_from_image(image)
|
||||
image.fill(Color.TRANSPARENT)
|
||||
return texture
|
||||
|
||||
for branch_attach in branch.attaches:
|
||||
branch_attaches.append(branch_attach)
|
||||
branch_root.append(branch.root)
|
||||
branch_parent_attach.append(attach)
|
||||
## returns -1 if not found
|
||||
func find_random_matching_attach_ind(attach_to_match: PlantAttach, array: Array[PlantPart]) -> int:
|
||||
var indices := range(array.size())
|
||||
shuffle(indices)
|
||||
for i in indices:
|
||||
if array[i].root.attach_types.any(func(type): return attach_to_match.attach_types.has(type)):
|
||||
return i
|
||||
return -1
|
||||
|
||||
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())
|
||||
func populate_part(all_parts: Dictionary[OriginType, Array], weight_per_origin_type: Array[int], mutation_weights: Array[int], parent_part: PlantPart, parent_image_coord: Vector2i):
|
||||
var part_placed: Array[PlantPart] # same ind as their corresponding attach
|
||||
var part_image_coords: Array[Vector2i] # idem
|
||||
|
||||
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)
|
||||
# first find and blend parts per attach
|
||||
for attach in parent_part.attaches:
|
||||
# get part to place
|
||||
var part_to_place := get_part(all_parts, weight_per_origin_type, mutation_weights, attach)
|
||||
part_placed.append(part_to_place)
|
||||
|
||||
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
|
||||
# blend part
|
||||
if part_to_place:
|
||||
var part_image_coord := blend_part(parent_image_coord, attach.position, part_to_place)
|
||||
part_image_coords.append(part_image_coord)
|
||||
else:
|
||||
part_image_coords.append(Vector2i.ZERO)
|
||||
|
||||
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)
|
||||
# then populate them
|
||||
for i in range(part_placed.size()):
|
||||
if part_placed[i] != null:
|
||||
populate_part(all_parts, weight_per_origin_type, mutation_weights, part_placed[i], part_image_coords[i])
|
||||
|
||||
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 get_part(all_parts: Dictionary[OriginType, Array], weight_per_origin_type: Array[int], mutation_weights: Array[int], attach: PlantAttach) -> PlantPart:
|
||||
var rand := rng.randf()
|
||||
if rand <= chance_to_have_part:
|
||||
var origins: Array[int] = all_parts.keys().duplicate()
|
||||
var weight_copy: Array[int] = weight_per_origin_type.duplicate()
|
||||
shuffle_weighted(origins, weight_copy)
|
||||
for originIndInd: int in range(origins.size()):
|
||||
var originInd: int = origins[originIndInd]
|
||||
var origin := originInd as OriginType
|
||||
if origin == OriginType.BRANCH_ORIGIN || origin == OriginType.BASE_LEAF_ORIGIN:
|
||||
var ind := find_random_matching_attach_ind(attach, all_parts[origin])
|
||||
if ind >= 0:
|
||||
weight_per_origin_type[origin] -= origin_weight_loss
|
||||
weight_per_origin_type[((origin + 1) % 3) as OriginType] += origin_weight_gain
|
||||
weight_per_origin_type[((origin + 2) % 3) as OriginType] += origin_weight_gain
|
||||
return all_parts[origin][ind]
|
||||
else: # find a mutation part to place
|
||||
var parts_per_mutations: Array = all_parts[origin]
|
||||
shuffle_weighted(parts_per_mutations, mutation_weights)
|
||||
for mutation_parts_ind in range(parts_per_mutations.size()):
|
||||
var ind := find_random_matching_attach_ind(attach, parts_per_mutations[mutation_parts_ind])
|
||||
if ind >= 0:
|
||||
for i in range(mutation_weights.size()):
|
||||
mutation_weights[i] += mutation_weight_gain
|
||||
mutation_weights[mutation_parts_ind] -= mutation_weight_gain + mutation_weight_loss
|
||||
weight_per_origin_type[origin] -= origin_weight_loss
|
||||
weight_per_origin_type[(origin + 1) % 3] += origin_weight_gain
|
||||
weight_per_origin_type[(origin + 2) % 3] += origin_weight_gain
|
||||
return parts_per_mutations[mutation_parts_ind][ind]
|
||||
return null
|
||||
|
||||
func blend_part(parent_image_coord: Vector2i, attach_position: Vector2, part_to_blend: PlantPart) -> Vector2i:
|
||||
var part_image: Image = part_to_blend.image
|
||||
var part_image_center: Vector2i = 0.5 * part_image.get_size()
|
||||
var part_image_coord: Vector2i = parent_image_coord + Vector2i(attach_position - part_to_blend.root.position)
|
||||
image.blend_rect(part_image, Rect2i(Vector2i.ZERO, part_to_blend.image.get_size()), part_image_coord - part_image_center)
|
||||
return part_image_coord
|
||||
|
||||
func modulate_image(i : Image, color : Color):
|
||||
for x in i.get_size().x:
|
||||
|
||||
Reference in New Issue
Block a user