ajout d'un terrain infini et la possibilité de planter n'importe où

This commit is contained in:
Zacharie Guet 2025-11-30 16:53:07 +01:00
parent 72ffa0b222
commit 8917a02a7b
63 changed files with 2847 additions and 1190 deletions

View File

@ -95,6 +95,8 @@ SHOVEL,Shovel,Pelle
SHOVEL_DESC_TEXT,"Use it to [b]dig up seeds[/b] and [b]harvest mature plants[/b].","Utilise-la pour [b]déterrer les graines[/b] et pour [b]récolter les plantes mature[/b]." SHOVEL_DESC_TEXT,"Use it to [b]dig up seeds[/b] and [b]harvest mature plants[/b].","Utilise-la pour [b]déterrer les graines[/b] et pour [b]récolter les plantes mature[/b]."
TROWEL,Trowel,Truelle TROWEL,Trowel,Truelle
TROWEL_DESC_TEXT,"Use it to [b]harvest mature plants[/b]. Can grant a [b]bonus seed[/b].","Utilise-la pour [b]récolter les plantes mature[/b]. Peut donner une [b]graine supplémentaire[/b]." TROWEL_DESC_TEXT,"Use it to [b]harvest mature plants[/b]. Can grant a [b]bonus seed[/b].","Utilise-la pour [b]récolter les plantes mature[/b]. Peut donner une [b]graine supplémentaire[/b]."
PICKAXE,Pickaxe,Pioche
PICKAXE_DESC_TEXT,Can dig rock and precious materials,Peut creuser la roche et des matériaux précieux
DIG,Dig,Creuser DIG,Dig,Creuser
OPEN,Open,Ouvrir OPEN,Open,Ouvrir
%s_SEED,%s Seed,Graine de %s %s_SEED,%s Seed,Graine de %s

1 keys en fr
95 TAKE_A_SEED TAKE_THE_SHOVEL Take a seed Take the shovel Prend une graine Prend la pelle
96 PLANT_THE_SEED_IN_DECONTAMINED_ZONE DIG_UNDERGROUND_LOOT Plant the seed in the decontamined zone Dig up the buried resources Plante la graine dans la zone décontaminée Déterre les graines enterrées
97 RECHARGE_TO_PASS_DAYS TAKE_A_SEED Recharge to pass the day Take a seed Recharge-toi pour passer la journée Prend une graine
98 PLANT_THE_SEED_IN_DECONTAMINED_ZONE Plant the seed in the decontamined zone Plante la graine dans la zone décontaminée
99 RECHARGE_TO_PASS_DAYS Recharge to pass the day Recharge-toi pour passer la journée
100 HARVEST_MATURE_PLANTS_WITH_SHOVEL Harvest mature plants with the shovel Récolte les plantes matures avec la pelle
101 SCORE_%d Score : %d Score : %d
102 SOLAR_PANNEL Solar panel Panneau solaire

1
common/icons/pick.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-pick"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M13 8l-9.383 9.418a2.091 2.091 0 0 0 0 2.967a2.11 2.11 0 0 0 2.976 0l9.407 -9.385" /><path d="M9 3h4.586a1 1 0 0 1 .707 .293l6.414 6.414a1 1 0 0 1 .293 .707v4.586a2 2 0 1 1 -4 0v-3l-5 -5h-3a2 2 0 1 1 0 -4z" /></svg>

After

Width:  |  Height:  |  Size: 514 B

View File

@ -2,22 +2,24 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://bu26h0iqutnky" uid="uid://ds4m14vl7he6v"
path="res://.godot/imported/underground_loot.svg-94513f7cc11df7cda1992e530bcff786.ctex" path="res://.godot/imported/pick.svg-b8cbf14d632089bea5ad3faa09f4cc83.ctex"
metadata={ metadata={
"vram_texture": false "vram_texture": false
} }
[deps] [deps]
source_file="res://entities/underground_loot/assets/sprites/underground_loot.svg" source_file="res://common/icons/pick.svg"
dest_files=["res://.godot/imported/underground_loot.svg-94513f7cc11df7cda1992e530bcff786.ctex"] dest_files=["res://.godot/imported/pick.svg-b8cbf14d632089bea5ad3faa09f4cc83.ctex"]
[params] [params]
compress/mode=0 compress/mode=0
compress/high_quality=false compress/high_quality=false
compress/lossy_quality=0.7 compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1 compress/hdr_compression=1
compress/normal_map=0 compress/normal_map=0
compress/channel_pack=0 compress/channel_pack=0
@ -25,6 +27,10 @@ mipmaps/generate=false
mipmaps/limit=-1 mipmaps/limit=-1
roughness/mode=0 roughness/mode=0
roughness/src_normal="" roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true process/fix_alpha_border=true
process/premult_alpha=false process/premult_alpha=false
process/normal_map_invert_y=false process/normal_map_invert_y=false
@ -32,6 +38,6 @@ process/hdr_as_srgb=false
process/hdr_clamp_exposure=false process/hdr_clamp_exposure=false
process/size_limit=0 process/size_limit=0
detect_3d/compress_to=1 detect_3d/compress_to=1
svg/scale=1.0 svg/scale=2.0
editor/scale_with_editor_scale=false editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,69 @@
class_name Math
static func get_chunk_from_pos(coord) -> Vector2i:
return Vector2i(
floori(coord.x / (Planet.CHUNK_TILE_SIZE * Planet.TILE_SIZE)),
floori(coord.y / (Planet.CHUNK_TILE_SIZE * Planet.TILE_SIZE))
)
static func get_tile_from_pos(coord) -> Vector2i:
return Vector2i(
floori(coord.x / (Planet.TILE_SIZE)),
floori(coord.y / (Planet.TILE_SIZE)),
)
static func get_tiles_in_circle(center: Vector2, radius : float) -> Array[Vector2i]:
var tiles : Array[Vector2i] = []
for x in range(
floori((center.x - radius/2.) / Planet.TILE_SIZE),
ceili((center.x + radius/2.) / Planet.TILE_SIZE),
):
for y in range(
floori((center.y - radius/2.) / Planet.TILE_SIZE),
ceili((center.y + radius/2.) / Planet.TILE_SIZE),
):
if is_tile_on_circle(Vector2i(x,y), center, radius):
tiles.append(Vector2i(x,y))
return tiles
static func is_tile_on_circle(tile_coord : Vector2i, circle_center: Vector2, circle_radius : float) -> bool:
var absolute_tile_pos : Vector2 = tile_coord * Planet.TILE_SIZE
# Loop over tile corners to know if the area collide
var corners : Array[Vector2] = []
for x in [0,1]:
for y in [0,1]:
corners.append(
absolute_tile_pos
+ Vector2.RIGHT * x * Planet.TILE_SIZE
+ Vector2.DOWN * y * Planet.TILE_SIZE
)
# Check if segment touch area
for i in range(4):
var a = corners[i%4]
var b = corners[(i+1)%4]
if segment_intersect_circle(a,b,circle_center,circle_radius):
return true
return false
# Stolen here https://stackoverflow.com/questions/1073336/circle-line-segment-collision-detection-algorithm
static func segment_intersect_circle(
a : Vector2,
b : Vector2,
c : Vector2,
radius: float
) -> bool:
var a_circle = c - a
var b_a = b - a
var proj_point = proj(a_circle,b_a) + a
return proj_point.distance_to(c) < radius
static func proj(a : Vector2,b : Vector2) -> Vector2:
var k = a.dot(b) / b.dot(b)
return Vector2(k * b.x, k * b.y)

View File

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

View File

@ -0,0 +1,29 @@
shader_type canvas_item;
uniform sampler2D red_overlay_tex: repeat_enable, filter_nearest;
uniform sampler2D green_overlay_tex: repeat_enable, filter_nearest;
uniform sampler2D blue_overlay_tex: repeat_enable, filter_nearest;
uniform float scale = 0.006944444; // calculated by 1/texture size e.g. 1/144
varying vec2 world_position;
void vertex(){
// calculate the world position for use in the fragment shader
world_position = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy;
}
void fragment() {
float mix_amount = floor(COLOR.r);
// sample the overlay_tex using worldPos
vec4 red_overlay_color = texture(red_overlay_tex, world_position * scale);
vec4 green_overlay_color = texture(green_overlay_tex, world_position * scale);
vec4 blue_overlay_color = texture(blue_overlay_tex, world_position * scale);
float origin_alpha = COLOR.a;
// combine original color and overlay color together
COLOR = mix(COLOR, red_overlay_color, floor(COLOR.r));
COLOR = mix(COLOR, green_overlay_color, floor(COLOR.g));
COLOR = mix(COLOR, blue_overlay_color, floor(COLOR.b));
COLOR.a = origin_alpha;
}

View File

@ -1,21 +0,0 @@
shader_type canvas_item;
uniform sampler2D overlay_tex: repeat_enable, filter_nearest;
uniform float scale = 0.006944444; // calculated by 1/texture size e.g. 1/144
varying vec2 world_position;
void vertex(){
// calculate the world position for use in the fragment shader
world_position = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy;
}
void fragment() {
// only apply overlay_tex on the fully red parts of the original tiles
float mix_amount = floor(COLOR.r);
// sample the overlay_tex using worldPos
vec4 overlay_color = texture(overlay_tex, world_position * scale);
// combine original color and overlay color together
COLOR = mix(COLOR, overlay_color, mix_amount);
}

View File

@ -4,6 +4,6 @@ class_name TruckLadder
const TRUCK_SCENE_PATH = "res://stages/terrain/truck/truck.tscn" const TRUCK_SCENE_PATH = "res://stages/terrain/truck/truck.tscn"
func interact(p : Player): func interact(p : Player):
p.planet.save() p.planet.save()
get_tree().change_scene_to_file(TRUCK_SCENE_PATH) get_tree().change_scene_to_file(TRUCK_SCENE_PATH)
return true return true

View File

@ -2,7 +2,7 @@ extends PlantEffect
class_name DecontaminateTerrainEffect class_name DecontaminateTerrainEffect
func get_decontamination_radius(): func get_decontamination_radius():
return 50 + 50 * level return (1 + level)
func get_effect_name() -> String: func get_effect_name() -> String:
return tr("DECONTAMINATE") return tr("DECONTAMINATE")
@ -12,9 +12,9 @@ func get_effect_description() -> String:
return ret return ret
func effect(plant): func effect(plant):
var radius = get_decontamination_radius() var tiles := Math.get_tiles_in_circle(
plant.planet.garden.impact_contamination(
plant.global_position, plant.global_position,
radius get_decontamination_radius() * Planet.TILE_SIZE + Planet.TILE_SIZE/2.
) )
plant.planet.decontamination_layer.place_decontaminations(tiles, true)

View File

@ -0,0 +1,41 @@
extends Item
class_name Pickaxe
const USE_INTERVAL = 0.15
func get_item_name() -> String:
return tr("PICKAXE")
func get_description() -> String:
return tr("PICKAXE_DESC_TEXT")
func get_icon() -> Texture2D:
return preload("res://common/icons/pick.svg")
func get_energy_used() -> int:
return 1
func get_usage_zone_radius() -> int:
return 50
func use_text() -> String:
return tr("DIG")
func can_use(_player : Player, zone : Player.ActionZone) -> bool:
if zone and zone.area:
var bodies = zone.area.get_overlapping_bodies()
for body in bodies :
if body is RockLayer:
return true
return false
func use(_player : Player, zone : Player.ActionZone) -> bool:
var bodies = zone.area.get_overlapping_bodies()
var rock_layer_id = bodies.find_custom(func (b) : return b is RockLayer)
if rock_layer_id != -1:
var rock_layer : RockLayer = bodies[rock_layer_id]
return rock_layer.dig_rocks(zone.get_tiles())
return false

View File

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

View File

@ -11,192 +11,191 @@ const SCORE_ICON = preload("res://common/icons/growth.svg")
@export var plant_mutations: Array[PlantMutation] @export var plant_mutations: Array[PlantMutation]
func get_item_name() -> String: func get_item_name() -> String:
return tr("%s_SEED") % plant_type.name return tr("%s_SEED") % plant_type.name
func get_description() -> String: func get_description() -> String:
return tr("PLANT_%s_MUST_BE_USED_IN_DECONTAMINATED_ZONE") % plant_type.name return tr("PLANT_%s_MUST_BE_USED_IN_DECONTAMINATED_ZONE") % plant_type.name
func get_icon() -> Texture2D: func get_icon() -> Texture2D:
return plant_type.seed_texture return plant_type.seed_texture
func get_energy_used() -> int: func get_energy_used() -> int:
return 1 return 1
func get_usage_zone_radius() -> int: func get_usage_zone_radius() -> int:
return 35 return 35
func get_usage_object_affected(i : InspectableEntity) -> bool: func get_usage_object_affected(i : InspectableEntity) -> bool:
return i is Plant return i is Plant
func _init( func _init(
_plant_type : PlantType = null, _plant_type : PlantType = null,
_parent_mutation : Array[PlantMutation] = [] _parent_mutation : Array[PlantMutation] = []
): ):
plant_type = _plant_type plant_type = _plant_type
plant_mutations = Seed.mutate(_parent_mutation) plant_mutations = Seed.mutate(_parent_mutation)
func use_text() -> String: func use_text() -> String:
return tr("PLANT_%s") % plant_type.name return tr("PLANT_%s") % plant_type.name
func is_one_time_use(): func is_one_time_use():
return true return true
func can_use(player : Player, zone : Player.ActionZone) -> bool: func can_use(player : Player, zone : Player.ActionZone) -> bool:
if ( if (
player.planet == null player.planet == null
or not player.planet.garden.is_in_garden(zone.get_global_position()) ):
): return false
return false
var is_there_a_plant_here = false var is_there_a_plant_here = false
for area in zone.get_affected_areas(): for area in zone.get_affected_areas():
if area is Plant: if area is Plant:
is_there_a_plant_here = true is_there_a_plant_here = true
var is_there_contamination_in_zone = false var is_there_contamination_in_zone = false
for point in zone.get_points_in_zone(): for tile in zone.get_tiles():
if player.planet.garden.is_there_contamination(point): if not player.planet.decontamination_layer.is_decontamined(tile):
is_there_contamination_in_zone = true is_there_contamination_in_zone = true
return not is_there_a_plant_here and not is_there_contamination_in_zone return not is_there_a_plant_here and not is_there_contamination_in_zone
func use(player : Player, zone : Player.ActionZone) -> bool: func use(player : Player, zone : Player.ActionZone) -> bool:
if player.planet == null: if player.planet == null:
return false return false
AudioManager.play_sfx("Dig") AudioManager.play_sfx("Dig")
return player.planet.plant( return player.planet.plant(
plant_type, plant_type,
zone.get_global_position(), zone.get_global_position(),
plant_mutations plant_mutations
) )
func card_info() -> CardInfo: func card_info() -> CardInfo:
var info = CardInfo.new( var info = CardInfo.new(
get_item_name() get_item_name()
) )
info.texture = icon info.texture = icon
info.type_icon = TYPE_ICON info.type_icon = TYPE_ICON
info.stats.append( info.stats.append(
CardStatInfo.new( CardStatInfo.new(
tr("COST_%d_ENERGY") % energy_usage, tr("COST_%d_ENERGY") % energy_usage,
ENERGY_ICON ENERGY_ICON
) )
) )
if is_one_time_use(): if is_one_time_use():
info.stats.append( info.stats.append(
CardStatInfo.new( CardStatInfo.new(
tr("ONE_TIME_USE"), tr("ONE_TIME_USE"),
ONE_TIME_ICON ONE_TIME_ICON
) )
) )
var effect_section = CardSectionInfo.new( var effect_section = CardSectionInfo.new(
tr("EFFECT"), tr("EFFECT"),
get_description() get_description()
) )
effect_section.title_icon = ACTION_ICON effect_section.title_icon = ACTION_ICON
if energy_usage > 0: if energy_usage > 0:
effect_section.title_icon = ENERGY_ICON effect_section.title_icon = ENERGY_ICON
info.sections.append(effect_section) info.sections.append(effect_section)
if len(plant_mutations) != 0: if len(plant_mutations) != 0:
var rarest : int = plant_mutations.map( var rarest : int = plant_mutations.map(
func(m : PlantMutation) : return m.get_rarity() func(m : PlantMutation) : return m.get_rarity()
).max() ).max()
info.bg_color = PlantMutation.get_rarity_color(rarest) info.bg_color = PlantMutation.get_rarity_color(rarest)
for m in plant_mutations: for m in plant_mutations:
info.sections.append(m.card_section()) info.sections.append(m.card_section())
return info return info
func get_particles() -> Array[Particles.Parameters]: func get_particles() -> Array[Particles.Parameters]:
var param : Array[Particles.Parameters] = [] var param : Array[Particles.Parameters] = []
for m in plant_mutations: for m in plant_mutations:
param.append( param.append(
Particles.Parameters.new( Particles.Parameters.new(
m.get_icon(), m.get_icon(),
PlantMutation.get_rarity_color(m.get_rarity()) PlantMutation.get_rarity_color(m.get_rarity())
) )
) )
return param return param
static func mutate(parent_mutation : Array[PlantMutation] = []) -> Array[PlantMutation]: static func mutate(parent_mutation : Array[PlantMutation] = []) -> Array[PlantMutation]:
if randf() > MUTATION_PROBABILITY: if randf() > MUTATION_PROBABILITY:
return parent_mutation return parent_mutation
var possible_mutations : Array[MutationPossibility] = [ var possible_mutations : Array[MutationPossibility] = [
AddMutation.new() AddMutation.new()
] ]
if ( if (
len(parent_mutation) >= 2 len(parent_mutation) >= 2
): ):
possible_mutations = [ possible_mutations = [
UpgradeMutation.new(), UpgradeMutation.new(),
RemoveMutation.new(), RemoveMutation.new(),
] ]
elif len(parent_mutation) > 0: elif len(parent_mutation) > 0:
possible_mutations = [ possible_mutations = [
AddMutation.new(), AddMutation.new(),
UpgradeMutation.new(), UpgradeMutation.new(),
RemoveMutation.new(), RemoveMutation.new(),
] ]
var chosen_mutation = possible_mutations.pick_random() var chosen_mutation = possible_mutations.pick_random()
return chosen_mutation.mutate(parent_mutation) return chosen_mutation.mutate(parent_mutation)
class MutationPossibility: class MutationPossibility:
func mutate(_parent_mutation : Array[PlantMutation] = [])-> Array[PlantMutation]: func mutate(_parent_mutation : Array[PlantMutation] = [])-> Array[PlantMutation]:
return [] return []
class DontMutate extends MutationPossibility: class DontMutate extends MutationPossibility:
func mutate(parent_mutation : Array[PlantMutation] = [])-> Array[PlantMutation]: func mutate(parent_mutation : Array[PlantMutation] = [])-> Array[PlantMutation]:
return parent_mutation return parent_mutation
class AddMutation extends MutationPossibility: class AddMutation extends MutationPossibility:
func mutate(parent_mutation : Array[PlantMutation] = [])-> Array[PlantMutation]: func mutate(parent_mutation : Array[PlantMutation] = [])-> Array[PlantMutation]:
var new_mutations = parent_mutation.duplicate_deep() var new_mutations = parent_mutation.duplicate_deep()
var mut = PlantMutation.random_mutation(parent_mutation) var mut = PlantMutation.random_mutation(parent_mutation)
if mut: if mut:
var existing_mut_id = new_mutations.find_custom(func(m:PlantMutation): return m.get_mutation_name() == mut.get_mutation_name()) var existing_mut_id = new_mutations.find_custom(func(m:PlantMutation): return m.get_mutation_name() == mut.get_mutation_name())
if existing_mut_id >= 0: if existing_mut_id >= 0:
new_mutations[existing_mut_id].level += 1 new_mutations[existing_mut_id].level += 1
else : else :
new_mutations.append(mut) new_mutations.append(mut)
return new_mutations return new_mutations
class UpgradeMutation extends MutationPossibility: class UpgradeMutation extends MutationPossibility:
func mutate( func mutate(
parent_mutation : Array[PlantMutation] = [] parent_mutation : Array[PlantMutation] = []
) -> Array[PlantMutation]: ) -> Array[PlantMutation]:
var new_mutations = parent_mutation.duplicate_deep() var new_mutations = parent_mutation.duplicate_deep()
new_mutations.pick_random().level += 1 new_mutations.pick_random().level += 1
return new_mutations return new_mutations
class RemoveMutation extends MutationPossibility: class RemoveMutation extends MutationPossibility:
func mutate(parent_mutation : Array[PlantMutation] = [])-> Array[PlantMutation]: func mutate(parent_mutation : Array[PlantMutation] = [])-> Array[PlantMutation]:
var new_mutations :Array[PlantMutation] = parent_mutation.duplicate_deep() var new_mutations :Array[PlantMutation] = parent_mutation.duplicate_deep()
var mut_to_remove = new_mutations.pick_random() var mut_to_remove = new_mutations.pick_random()
if mut_to_remove.level > 1: if mut_to_remove.level > 1:
mut_to_remove.level -= 1 mut_to_remove.level -= 1
else: else:
new_mutations.remove_at(new_mutations.find(mut_to_remove)) new_mutations.remove_at(new_mutations.find(mut_to_remove))
return new_mutations return new_mutations

View File

@ -4,41 +4,45 @@ class_name Shovel
const SHOVEL_ZONE_RADIUS = 50 const SHOVEL_ZONE_RADIUS = 50
func get_item_name() -> String: func get_item_name() -> String:
return tr("SHOVEL") return tr("SHOVEL")
func get_description() -> String: func get_description() -> String:
return tr("SHOVEL_DESC_TEXT") return tr("SHOVEL_DESC_TEXT")
func get_icon() -> Texture2D: func get_icon() -> Texture2D:
return preload("res://common/icons/shovel.svg") return preload("res://common/icons/shovel.svg")
func get_usage_zone_radius() -> int: func get_usage_zone_radius() -> int:
return SHOVEL_ZONE_RADIUS return SHOVEL_ZONE_RADIUS
func get_usage_object_affected(i : InspectableEntity) -> bool: func get_usage_object_affected(i : InspectableEntity) -> bool:
return i is Plant or i is UndergroundLoot return i is Plant
func use_text() -> String: func use_text() -> String:
return tr("DIG") return tr("DIG")
func can_use(_player : Player, zone : Player.ActionZone) -> bool: func can_use(_player : Player, zone : Player.ActionZone) -> bool:
var areas = zone.get_affected_areas() var areas = zone.get_affected_areas()
for area in areas : var bodies = zone.area.get_overlapping_bodies()
if area is Plant or area is UndergroundLoot: for body in bodies :
return true if body is RockLayer:
return false return true
for area in areas :
if area is Plant:
return true
return false
func use(player : Player, zone : Player.ActionZone) -> bool: func use(player : Player, zone : Player.ActionZone) -> bool:
for area in zone.get_affected_areas(): for area in zone.get_affected_areas():
if area and area is Plant: if area and area is Plant:
harvest(area, player) harvest(area, player)
await player.get_tree().create_timer(USE_INTERVAL).timeout await player.get_tree().create_timer(USE_INTERVAL).timeout
if area and area is UndergroundLoot:
dig(area, player) var bodies = zone.area.get_overlapping_bodies()
await player.get_tree().create_timer(USE_INTERVAL).timeout var rock_layer_id = bodies.find_custom(func (b) : return b is RockLayer)
if rock_layer_id != -1:
return true var rock_layer : RockLayer = bodies[rock_layer_id]
func dig(u: UndergroundLoot, player: Player): rock_layer.dig_rocks(zone.get_tiles())
AudioManager.play_sfx("Dig")
u.dig() return true

View File

@ -10,320 +10,324 @@ signal upgraded
var terrain : Terrain var terrain : Terrain
var planet : Planet : var planet : Planet :
get(): return terrain if terrain is Planet else null get(): return terrain if terrain is Planet else null
@export var speed = 350 @export var speed = 350
var data : PlayerData var data : PlayerData
var last_action_area_movement_timer : float = 100. var last_action_area_movement_timer : float = 100.
var controlling_player : bool = true : var controlling_player : bool = true :
set(v): set(v):
controlling_player = v controlling_player = v
velocity = Vector2.ZERO velocity = Vector2.ZERO
var instruction : Instruction = null var instruction : Instruction = null
@onready var preview_zone : ActionZone = setup_action_zone(Vector2.ZERO, null) @onready var preview_zone : ActionZone = await setup_action_zone(Vector2.ZERO, null)
@onready var action_zone : ActionZone = setup_action_zone(Vector2.ZERO, null) @onready var action_zone : ActionZone = await setup_action_zone(Vector2.ZERO, null)
func _ready(): func _ready():
data = GameInfo.game_data.player_data data = GameInfo.game_data.player_data
data.inventory.updated.connect(_on_inventory_updated) data.inventory.updated.connect(_on_inventory_updated)
player_updated.emit(self) player_updated.emit(self)
Pointer.player = self Pointer.player = self
setup_preview_zone(data.inventory.get_item())
func _input(_event) -> void: func _input(_event) -> void:
if Input.is_action_pressed("change_item_left"): if Input.is_action_pressed("change_item_left"):
data.inventory.change_current_item(1) data.inventory.change_current_item(1)
if Input.is_action_pressed("change_item_right"): if Input.is_action_pressed("change_item_right"):
data.inventory.change_current_item(-1) data.inventory.change_current_item(-1)
for i in range(1, 10): for i in range(1, 10):
if Input.is_action_pressed("item_" + str(i)): if Input.is_action_pressed("item_" + str(i)):
data.inventory.set_current_item(i - 1) data.inventory.set_current_item(i - 1)
# Méthode déclenchée par la classe planet # Méthode déclenchée par la classe planet
func _start_pass_day(): func _start_pass_day():
controlling_player = false controlling_player = false
instruction = null instruction = null
# Méthode déclenchée par la classe planet # Méthode déclenchée par la classe planet
func _pass_day(): func _pass_day():
full_recharge() full_recharge()
# Méthode déclenchée par la classe planet # Méthode déclenchée par la classe planet
func _end_pass_day(): func _end_pass_day():
controlling_player = true controlling_player = true
func _process(delta): func _process(delta):
last_action_area_movement_timer += delta last_action_area_movement_timer += delta
if controlling_player: if controlling_player:
calculate_direction() calculate_direction()
if ( if (
last_action_area_movement_timer >= ACTION_AREA_UPDATE_TIME last_action_area_movement_timer >= ACTION_AREA_UPDATE_TIME
and instruction and instruction.can_be_done(self) and instruction and instruction.can_be_done(self)
): ):
instruction.do(self) instruction.do(self)
instruction = null instruction = null
move_preview_zone(get_global_mouse_position()) move_preview_zone(get_global_mouse_position())
else: else:
velocity = Vector2.ZERO velocity = Vector2.ZERO
move_and_slide() move_and_slide()
func _on_inventory_updated(_inventory: Inventory): func _on_inventory_updated(_inventory: Inventory):
setup_preview_zone(data.inventory.get_item()) setup_preview_zone(data.inventory.get_item())
var item : Item = data.inventory.get_item() var item : Item = data.inventory.get_item()
if item: if item:
var item_texture = item.icon var item_texture = item.icon
%ItemSprite.texture = item_texture %ItemSprite.texture = item_texture
%ItemSprite.scale = Vector2( %ItemSprite.scale = Vector2(
1./(item_texture.get_width()/HOLDING_ITEM_SPRITE_SIZE), 1./(item_texture.get_width()/HOLDING_ITEM_SPRITE_SIZE),
1./(item_texture.get_height()/HOLDING_ITEM_SPRITE_SIZE) 1./(item_texture.get_height()/HOLDING_ITEM_SPRITE_SIZE)
) )
%HideEyes.visible = item != null %HideEyes.visible = item != null
%ItemSprite.visible = item != null %ItemSprite.visible = item != null
emit_signal("player_updated", self) emit_signal("player_updated", self)
func calculate_direction(): func calculate_direction():
var input_direction: Vector2 = Input.get_vector("move_left", "move_right", "move_up", "move_down") var input_direction: Vector2 = Input.get_vector("move_left", "move_right", "move_up", "move_down")
if input_direction.length() != 0: if input_direction.length() != 0:
instruction = null instruction = null
if instruction and instruction.position.distance_to(global_position) > (MAX_REACH - 1.): if (
input_direction = self.global_position.direction_to(instruction.position) instruction
and (
instruction.position.distance_to(global_position) > (MAX_REACH - 1.)
or instruction is MoveInstruction
)
):
input_direction = self.global_position.direction_to(instruction.position)
velocity = input_direction * speed velocity = input_direction * speed
if input_direction.x: if input_direction.x:
flip_character(input_direction.x > 0) flip_character(input_direction.x > 0)
func flip_character(face_right = true): func flip_character(face_right = true):
$Sprite.flip_h = not face_right $Sprite.flip_h = not face_right
%ItemSprite.position.x = abs(%ItemSprite.position.x) * (1 if face_right else -1) %ItemSprite.position.x = abs(%ItemSprite.position.x) * (1 if face_right else -1)
%HideEyes.position.x = abs(%ItemSprite.position.x) * (1 if face_right else -1) %HideEyes.position.x = abs(%ItemSprite.position.x) * (1 if face_right else -1)
func can_interact(interactable : Interactable): func can_interact(interactable : Interactable):
return interactable.can_interact(self) return interactable.can_interact(self)
func try_interact(interactable : Interactable): func try_interact(interactable : Interactable):
if interactable: if interactable:
instruction = InteractableInstruction.new( instruction = InteractableInstruction.new(
interactable interactable
) )
func try_move(move_to : Vector2): func try_move(move_to : Vector2):
instruction = MoveInstruction.new(move_to) instruction = MoveInstruction.new(move_to)
func pick_item(item : Item) -> Item: func pick_item(item : Item) -> Item:
AudioManager.play_sfx("PickUp") AudioManager.play_sfx("PickUp")
if data.inventory.is_full(): if data.inventory.is_full():
drop_item() drop_item()
var available_slot_ind = data.inventory.get_best_available_slot_ind() var available_slot_ind = data.inventory.get_best_available_slot_ind()
if ( if (
available_slot_ind == data.inventory.current_item_ind available_slot_ind == data.inventory.current_item_ind
&& data.inventory.items[available_slot_ind] != null && data.inventory.items[available_slot_ind] != null
): ):
var current_item : Item = data.inventory.get_item() var current_item : Item = data.inventory.get_item()
data.inventory.set_item(item, available_slot_ind) data.inventory.set_item(item, available_slot_ind)
return current_item return current_item
else : else :
if data.inventory.set_item(item, available_slot_ind): if data.inventory.set_item(item, available_slot_ind):
data.inventory.set_current_item(available_slot_ind); data.inventory.set_current_item(available_slot_ind);
return null return null
func drop_item(): func drop_item():
var item_to_drop = data.inventory.pop_item() var item_to_drop = data.inventory.pop_item()
if item_to_drop: if item_to_drop:
terrain.drop_item(item_to_drop, global_position) terrain.drop_item(item_to_drop, global_position)
AudioManager.play_sfx("Drop") AudioManager.play_sfx("Drop")
func delete_item(item: Item): func delete_item(item: Item):
data.inventory.remove_item(item) data.inventory.remove_item(item)
func try_use_item(item : Item, use_position : Vector2): func try_use_item(item : Item, use_position : Vector2):
setup_action_zone(use_position, item) await setup_action_zone(use_position, item)
instruction = ItemActionInstruction.new( instruction = ItemActionInstruction.new(
use_position, use_position,
item item
) )
func preview_could_use_item(item : Item) -> bool: func preview_could_use_item(item : Item) -> bool:
return could_use_item_on_zone(item, preview_zone) return could_use_item_on_zone(item, preview_zone)
func could_use_item_on_zone(item : Item, zone: ActionZone) -> bool: func could_use_item_on_zone(item : Item, zone: ActionZone) -> bool:
return ( return (
data.inventory.has_item(item) data.inventory.has_item(item)
and item.can_use(self, zone) and item.can_use(self, zone)
) )
func can_use_item_on_zone(item : Item, zone: ActionZone) -> bool: func can_use_item_on_zone(item : Item, zone: ActionZone) -> bool:
return ( return (
could_use_item_on_zone(item, zone) could_use_item_on_zone(item, zone)
and has_energy_to_use_item(item) and has_energy_to_use_item(item)
) )
func has_energy_to_use_item(item : Item): func has_energy_to_use_item(item : Item):
return (data.energy - item.energy_usage) >= 0 return (data.energy - item.energy_usage) >= 0
func use_item(item : Item): func use_item(item : Item):
if can_use_item_on_zone(item, action_zone): if can_use_item_on_zone(item, action_zone):
var is_item_used = await item.use(self, action_zone) var is_item_used = await item.use(self, action_zone)
if is_item_used: if is_item_used:
data.energy -= item.energy_usage data.energy -= item.energy_usage
if item.is_one_time_use(): if item.is_one_time_use():
data.inventory.remove_item(item) data.inventory.remove_item(item)
func upgrade_max_energy(amount = 1): func upgrade_max_energy(amount = 1):
data.max_energy += amount data.max_energy += amount
upgraded.emit() upgraded.emit()
player_updated.emit(self) player_updated.emit(self)
func upgrade_inventory_size(amount = 1): func upgrade_inventory_size(amount = 1):
data.inventory.size += 1 data.inventory.size += amount
upgraded.emit() upgraded.emit()
player_updated.emit(self) player_updated.emit(self)
func recharge(amount : int = data.max_energy): func recharge(amount : int = data.max_energy):
data.energy += + amount data.energy += + amount
upgraded.emit() upgraded.emit()
func full_recharge(): func full_recharge():
data.energy = max(data.energy, data.max_energy) data.energy = max(data.energy, data.max_energy)
func generate_action_zone(item : Item) -> ActionZone: func generate_action_zone(item : Item) -> ActionZone:
var zone = ActionZone.new(item) var zone = ActionZone.new(item)
if zone.area: if not get_parent().is_node_ready():
get_parent().add_child(zone.area) await get_parent().ready
get_parent().add_child(zone.area)
return zone return zone
func setup_preview_zone(item : Item) -> ActionZone: func setup_preview_zone(item : Item):
if preview_zone and preview_zone.item == item: if preview_zone and preview_zone.item == item:
return preview_zone return preview_zone
elif preview_zone: elif preview_zone:
preview_zone.destroy() preview_zone.destroy()
if item: if item:
preview_zone = generate_action_zone(item) preview_zone = await generate_action_zone(item)
else: else:
preview_zone = null preview_zone = null
return preview_zone
func setup_action_zone(zone_position : Vector2, item: Item) -> ActionZone: func setup_action_zone(zone_position : Vector2, item: Item) -> ActionZone:
if action_zone: if action_zone:
action_zone.destroy() action_zone.destroy()
action_zone = generate_action_zone(item) action_zone = await generate_action_zone(item)
action_zone.move_to_position(zone_position) action_zone.move_to_position(zone_position)
last_action_area_movement_timer = 0. last_action_area_movement_timer = 0.
return action_zone return action_zone
func move_preview_zone(zone_position : Vector2): func move_preview_zone(zone_position : Vector2):
if preview_zone: if preview_zone:
preview_zone.move_to_position(zone_position) preview_zone.move_to_position(zone_position)
class Instruction: class Instruction:
var position : Vector2 var position : Vector2
func _init(_pos : Vector2): func _init(_pos : Vector2):
position = _pos position = _pos
func can_be_done(player : Player): func can_be_done(player : Player):
return player.global_position.distance_to(position) < player.MAX_REACH return player.global_position.distance_to(position) < player.MAX_REACH
func do(_player : Player): func do(_player : Player):
pass pass
class MoveInstruction extends Instruction: class MoveInstruction extends Instruction:
pass func can_be_done(player : Player):
return player.global_position.distance_to(position) < 10.
class ItemActionInstruction extends Instruction: class ItemActionInstruction extends Instruction:
var item = Item var item = Item
func _init(_pos : Vector2, _item : Item): func _init(_pos : Vector2, _item : Item):
position = _pos position = _pos
item = _item item = _item
func can_be_done(player : Player): func can_be_done(player : Player):
return ( return (
player.global_position.distance_to(position) < player.MAX_REACH player.global_position.distance_to(position) < player.MAX_REACH
) )
func do(player : Player): func do(player : Player):
player.use_item(item) player.use_item(item)
class InteractableInstruction extends Instruction: class InteractableInstruction extends Instruction:
var interactable = Interactable var interactable = Interactable
func _init(_interactable : Interactable): func _init(_interactable : Interactable):
interactable = _interactable interactable = _interactable
position = interactable.global_position position = interactable.global_position
func can_be_done(player : Player): func can_be_done(player : Player):
return player.global_position.distance_to(position) < player.MAX_REACH return player.global_position.distance_to(position) < player.MAX_REACH
func do(player : Player): func do(player : Player):
interactable.interact(player) interactable.interact(player)
class ActionZone: class ActionZone:
var item : Item = null var item : Item = null
var area : Area2D var area : Area2D
var affected_areas : Array[InspectableEntity]= [] var affected_areas : Array[InspectableEntity]= []
func _init(_i : Item): func _init(_i : Item):
item = _i item = _i
if item and item.get_usage_zone_radius() > 0: if item and item.get_usage_zone_radius() > 0:
area = Area2D.new() area = Area2D.new()
var collision_shape = CollisionShape2D.new() var collision_shape = CollisionShape2D.new()
var circle_shape = CircleShape2D.new() var circle_shape = CircleShape2D.new()
circle_shape.radius = item.get_usage_zone_radius() circle_shape.radius = item.get_usage_zone_radius()
collision_shape.shape = circle_shape collision_shape.shape = circle_shape
area.add_child(collision_shape) area.add_child(collision_shape)
func clear_preview_on_affected_area(): func clear_preview_on_affected_area():
for a in affected_areas: for a in affected_areas:
if a: if a:
a.affect_preview(false) a.affect_preview(false)
func update_preview_on_affected_area(): func update_preview_on_affected_area():
var detected_areas = get_affected_areas() var detected_areas = get_affected_areas()
clear_preview_on_affected_area() clear_preview_on_affected_area()
var new_affected_areas : Array[InspectableEntity] = [] var new_affected_areas : Array[InspectableEntity] = []
for a in detected_areas: for a in detected_areas:
if a is InspectableEntity and item.get_usage_object_affected(a): if a is InspectableEntity and item.get_usage_object_affected(a):
a.affect_preview(true) a.affect_preview(true)
new_affected_areas.append(a) new_affected_areas.append(a)
affected_areas = new_affected_areas affected_areas = new_affected_areas
func get_affected_areas() -> Array[Area2D]: func get_affected_areas() -> Array[Area2D]:
var empty_array : Array[Area2D] = [] var empty_array : Array[Area2D] = []
return empty_array if area == null else area.get_overlapping_areas() return empty_array if area == null else area.get_overlapping_areas()
func destroy(): func destroy():
clear_preview_on_affected_area() clear_preview_on_affected_area()
if area: if area:
area.queue_free() area.queue_free()
func get_global_position() -> Vector2: func get_global_position() -> Vector2:
return Vector2.ZERO if area == null else area.global_position return Vector2.ZERO if area == null else area.global_position
func move_to_position(pos : Vector2): func move_to_position(pos : Vector2):
if area: if area:
update_preview_on_affected_area() update_preview_on_affected_area()
area.global_position = pos area.global_position = pos
func get_points_in_zone(point_factor = 10) -> Array[Vector2]: func get_tiles() -> Array[Vector2i]:
var points : Array[Vector2] = [] return Math.get_tiles_in_circle(
var radius = item.get_usage_zone_radius() get_global_position(),
for x in range(-radius, radius, point_factor): item.get_usage_zone_radius()
for y in range(-radius, radius, point_factor): )
if Vector2(x, y).length() <= radius:
points.append(area.global_position + Vector2(x, y))
return points

View File

@ -3,7 +3,7 @@ class_name PlayerData
signal updated(player_data : PlayerData) signal updated(player_data : PlayerData)
const DEFAULT_MAX_ENERGY = 2 const DEFAULT_MAX_ENERGY = 3
const DEFAULT_INVENTORY_SIZE = 3 const DEFAULT_INVENTORY_SIZE = 3
@export var max_energy : int = DEFAULT_MAX_ENERGY : @export var max_energy : int = DEFAULT_MAX_ENERGY :

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="121.41377mm"
height="49.165478mm"
viewBox="0 0 121.41377 49.165478"
version="1.1"
id="svg1"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="mm" />
<defs
id="defs1" />
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-77.923929,-55.520125)">
<path
id="path1"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.264583;stroke-linecap:square"
d="M 141.47612,55.520125 A 35.11824,35.11824 0 0 0 107.53969,81.850818 26.422297,26.422297 0 0 0 104.01691,81.607939 26.422297,26.422297 0 0 0 77.923926,104.6856 H 199.239 a 28.094591,28.094591 0 0 0 0.0987,-1.00304 28.094591,28.094591 0 0 0 -26.09504,-28.015344 35.11824,35.11824 0 0 0 -31.76654,-20.147091 z" />
<path
id="path2"
style="fill:#c9c9c9;fill-opacity:1;stroke-width:0.264583;stroke-linecap:square"
d="M 149.00176,77.594747 A 22.241554,22.241554 0 0 0 127.96997,92.741626 22.241554,22.241554 0 0 0 117.22799,89.969702 22.241554,22.241554 0 0 0 96.380164,104.6856 h 95.746606 a 22.241554,22.241554 0 0 0 -21.05091,-15.385108 22.241554,22.241554 0 0 0 -2.3983,0.171566 22.241554,22.241554 0 0 0 -19.6758,-11.877311 z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,39 +0,0 @@
extends InspectableEntity
class_name UndergroundLoot
const AREA_WIDTH = 20
const LOOTED_ITEM_RANDOM_RANGE = 50
@export var item_number : int = 1
func pointer_text() -> String:
return tr("BURIED_SEEDS")
func generate_collision_shape() -> CollisionShape2D:
var collision = CollisionShape2D.new()
var shape = CircleShape2D.new()
shape.radius = AREA_WIDTH
collision.shape = shape
add_child(collision)
return collision
func generate_loot() -> Array[Item]:
var seeds : Array[Item] = []
if len(GameInfo.game_data.unlocked_plant_types):
seeds.append(
Seed.new(
GameInfo.game_data.unlocked_plant_types.pick_random()
)
)
return seeds
func dig():
for item in generate_loot():
planet.drop_item(item, global_position, LOOTED_ITEM_RANDOM_RANGE)
queue_free()
func save() -> UndergroundLootData:
return UndergroundLootData.new(self)

View File

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

View File

@ -1,15 +0,0 @@
extends EntityData
class_name UndergroundLootData
const SCENE = preload("res://entities/underground_loot/underground_loot.tscn")
@export var item_number : int
func _init(e : UndergroundLoot):
position = e.global_position
item_number = e.item_number
func load() -> Entity:
var loot : UndergroundLoot = (SCENE.instantiate() as UndergroundLoot)
loot.item_number = item_number
return loot

View File

@ -1 +0,0 @@
uid://68plwch6h4f7

View File

@ -1,21 +0,0 @@
[gd_scene load_steps=4 format=3 uid="uid://dxjud7bti0na0"]
[ext_resource type="Script" uid="uid://dfd2hh12155lo" path="res://entities/underground_loot/scripts/underground_loot.gd" id="1_knus5"]
[ext_resource type="Texture2D" uid="uid://bu26h0iqutnky" path="res://entities/underground_loot/assets/sprites/underground_loot.svg" id="2_jfggo"]
[sub_resource type="CircleShape2D" id="CircleShape2D_knus5"]
radius = 20.09975
[node name="UndergroundLootSprites" type="Area2D"]
script = ExtResource("1_knus5")
default_info_title = "BURIED_SEEDS"
default_info_desc = "BURIED_SEEDS_DESC_TEXT"
metadata/_custom_type_script = "uid://d3bk52402ylvl"
[node name="Sprite2D" type="Sprite2D" parent="."]
modulate = Color(0.286275, 0.219608, 0.313726, 1)
scale = Vector2(0.0806452, 0.0806452)
texture = ExtResource("2_jfggo")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_knus5")

View File

@ -8,177 +8,204 @@ var indicators : Array[InGameIndicator]
@export var planet : Planet @export var planet : Planet
@onready var steps : Array[Step] = [ @onready var steps : Array[Step] = [
TakeShovelStep.new(), TakeShovelStep.new(),
DigLootStep.new(), DigSeedStep.new(),
TakeSeedStep.new(), TakeSeedStep.new(),
PlantSeedStep.new(), PlantSeedStep.new(),
RechargeStep.new(), RechargeStep.new(),
# WaitMaturePlant.new(), # WaitMaturePlant.new(),
# HarvestMaturePlant.new(), # HarvestMaturePlant.new(),
] ]
var actual_step : Step = null : set = pass_to_step var actual_step : Step = null : set = pass_to_step
func _process(_d): func _process(_d):
if not GameInfo.game_data.tutorial_done: if not GameInfo.game_data.tutorial_done:
if not actual_step and planet.data.tutorial_step < len(steps): if not actual_step and planet.data.tutorial_step < len(steps):
destroy_indicators() destroy_indicators()
pass_to_step(steps[planet.data.tutorial_step]) pass_to_step(steps[planet.data.tutorial_step])
if player and actual_step and actual_step.is_step_over(player, planet): if player and actual_step and actual_step.is_step_over(player, planet):
destroy_indicators() destroy_indicators()
planet.data.tutorial_step += 1 planet.data.tutorial_step += 1
if planet.data.tutorial_step < len(steps): if planet.data.tutorial_step < len(steps):
pass_to_step(steps[planet.data.tutorial_step]) pass_to_step(steps[planet.data.tutorial_step])
else : else :
GameInfo.game_data.tutorial_done = true GameInfo.game_data.tutorial_done = true
func destroy_indicators(): func destroy_indicators():
for i in indicators: for i in indicators:
i.queue_free() i.queue_free()
indicators = [] indicators = []
func pass_to_step(new_step : Step): func pass_to_step(new_step : Step):
actual_step = new_step actual_step = new_step
indicators = new_step.generate_indicators(player, planet) indicators = new_step.generate_indicators(player, planet)
for i in indicators: for i in indicators:
add_child(i) add_child(i)
class Step: class Step:
func generate_indicator(text : String) -> InGameIndicator: func generate_indicator(text : String) -> InGameIndicator:
var new_indicator : InGameIndicator = INDICATOR_SCENE.instantiate() var new_indicator : InGameIndicator = INDICATOR_SCENE.instantiate()
new_indicator.setup( new_indicator.setup(
text text
) )
return new_indicator return new_indicator
func generate_indicators(_player : Player, _planet : Planet) -> Array[InGameIndicator]: func generate_indicators(_player : Player, _planet : Planet) -> Array[InGameIndicator]:
return [] return []
func is_step_over(_p : Player, _planet : Planet) -> bool: func is_step_over(_p : Player, _planet : Planet) -> bool:
return true return true
class TakeShovelStep extends Step: class TakeShovelStep extends Step:
func generate_indicators(_p: Player, planet : Planet) -> Array[InGameIndicator]: func generate_indicators(_p: Player, planet : Planet) -> Array[InGameIndicator]:
for entity in planet.entity_container.get_children(): for entity in planet.entity_container.get_children():
if entity is ItemObject and entity.item is Shovel: if entity is ItemObject and entity.item is Shovel:
var indicator = generate_indicator(tr("TAKE_THE_SHOVEL")) var indicator = generate_indicator(tr("TAKE_THE_SHOVEL"))
indicator.follow_entity(entity) indicator.follow_entity(entity)
return [ return [
indicator indicator
] ]
printerr("No Shovel found...") printerr("No Shovel found...")
return [] return []
func is_step_over(p : Player, _planet : Planet) -> bool: func is_step_over(p : Player, _planet : Planet) -> bool:
for item in p.data.inventory.items: for item in p.data.inventory.items:
if item is Shovel: if item is Shovel:
return true return true
return false return false
class DigLootStep extends Step: class DigSeedStep extends Step:
func generate_indicators(p: Player, planet : Planet) -> Array[InGameIndicator]: func generate_indicators(p: Player, planet : Planet) -> Array[InGameIndicator]:
var closest_loot : UndergroundLoot var closest_seed = null
var closest_distance = 10000000 var limit_distance = 1000
for entity in planet.entity_container.get_children(): var actual_distance = 100
if entity is UndergroundLoot: var player_tile = Math.get_tile_from_pos(p.global_position)
var distance = entity.global_position.distance_to(p.global_position)
if distance < closest_distance: while closest_seed == null and actual_distance < limit_distance:
closest_distance = distance print(player_tile)
closest_loot = entity for x in range(actual_distance):
for y in range(actual_distance):
if closest_loot: var coord = Vector2i(x,y) - Vector2i.ONE * floori(actual_distance/2.) + player_tile
var indicator = generate_indicator(tr("DIG_UNDERGROUND_LOOT")) if planet.rock_layer.get_tile_type(coord) == RockLayer.TileType.CRISTAL:
indicator.follow_entity(closest_loot) if closest_seed == null or player_tile.distance_to(coord) < player_tile.distance_to(closest_seed):
return [indicator] closest_seed = coord
return []
actual_distance += 100
func is_step_over(_p : Player, planet : Planet) -> bool:
for entity in planet.entity_container.get_children(): if closest_seed:
if entity is ItemObject and entity.item is Seed: var indicator = generate_indicator(tr("DIG_UNDERGROUND_LOOT"))
return true indicator.follow_game_position(closest_seed * Planet.TILE_SIZE + Vector2i.ONE * floori(Planet.TILE_SIZE/2.))
return false return [indicator]
return []
func is_step_over(_p : Player, planet : Planet) -> bool:
for entity in planet.entity_container.get_children():
if entity is ItemObject and entity.item is Seed:
return true
return false
class TakeSeedStep extends Step: class TakeSeedStep extends Step:
func generate_indicators(_p: Player, planet : Planet) -> Array[InGameIndicator]: func generate_indicators(_p: Player, planet : Planet) -> Array[InGameIndicator]:
var indicators : Array[InGameIndicator] = [] var indicators : Array[InGameIndicator] = []
for entity in planet.entity_container.get_children(): for entity in planet.entity_container.get_children():
if entity is ItemObject and entity.item is Seed: if entity is ItemObject and entity.item is Seed:
var indicator = generate_indicator(tr("TAKE_A_SEED")) var indicator = generate_indicator(tr("TAKE_A_SEED"))
indicator.follow_entity(entity) indicator.follow_entity(entity)
indicators.append( indicators.append(
indicator indicator
) )
return indicators return indicators
func is_step_over(p : Player, _planet : Planet) -> bool: func is_step_over(p : Player, _planet : Planet) -> bool:
for item in p.data.inventory.items: for item in p.data.inventory.items:
if item is Seed: if item is Seed:
return true return true
return false return false
class PlantSeedStep extends Step: class PlantSeedStep extends Step:
func generate_indicators(_p: Player, planet : Planet) -> Array[InGameIndicator]: func generate_indicators(p: Player, planet : Planet) -> Array[InGameIndicator]:
var indicator = generate_indicator(tr("PLANT_THE_SEED_IN_DECONTAMINED_ZONE"))
indicator.follow_game_position(Vector2(0,0) + planet.entity_container.global_position) var closest_decontamination = null
return [indicator] var limit_distance = 1000
func is_step_over(_p : Player, planet : Planet) -> bool: var actual_distance = 100
for entity in planet.entity_container.get_children(): var player_tile = Math.get_tile_from_pos(p.global_position)
if entity is Plant:
return true while closest_decontamination == null and actual_distance < limit_distance:
return false print(player_tile)
for x in range(actual_distance):
for y in range(actual_distance):
var coord = Vector2i(x,y) - Vector2i.ONE * floori(actual_distance/2.) + player_tile
if planet.decontamination_layer.is_decontamined(coord):
if closest_decontamination == null or player_tile.distance_to(coord) < player_tile.distance_to(closest_decontamination):
closest_decontamination = coord
actual_distance += 100
if closest_decontamination:
var indicator = generate_indicator(tr("PLANT_THE_SEED_IN_DECONTAMINED_ZONE"))
indicator.follow_game_position(closest_decontamination * Planet.TILE_SIZE + Vector2i.ONE * floori(Planet.TILE_SIZE/2.))
return [indicator]
return []
func is_step_over(_p : Player, planet : Planet) -> bool:
for entity in planet.entity_container.get_children():
if entity is Plant:
return true
return false
class RechargeStep extends Step: class RechargeStep extends Step:
func generate_indicators(_p: Player, planet : Planet) -> Array[InGameIndicator]: func generate_indicators(_p: Player, planet : Planet) -> Array[InGameIndicator]:
for entity in planet.entity_container.get_children(): for entity in planet.entity_container.get_children():
var indicator = generate_indicator(tr("RECHARGE_TO_PASS_DAYS")) var indicator = generate_indicator(tr("RECHARGE_TO_PASS_DAYS"))
indicator.follow_entity(entity) indicator.follow_entity(entity)
if entity is TruckRecharge: if entity is TruckRecharge:
return [ return [
indicator indicator
] ]
printerr("No Recharge Station found...") printerr("No Recharge Station found...")
return [] return []
func is_step_over(_p : Player, planet : Planet) -> bool: func is_step_over(_p : Player, planet : Planet) -> bool:
if planet == null : if planet == null :
return false return false
return planet.data.day > 1 return planet.data.day > 1
class WaitMaturePlant extends Step: class WaitMaturePlant extends Step:
func generate_indicators(_p: Player, _planet : Planet) -> Array[InGameIndicator]: func generate_indicators(_p: Player, _planet : Planet) -> Array[InGameIndicator]:
return [] return []
func is_step_over(_p : Player, planet : Planet) -> bool: func is_step_over(_p : Player, planet : Planet) -> bool:
if planet == null : if planet == null :
return false return false
for entity in planet.entity_container.get_children(): for entity in planet.entity_container.get_children():
if entity is Plant and entity.state == Plant.State.MATURE: if entity is Plant and entity.state == Plant.State.MATURE:
return true return true
return false return false
class HarvestMaturePlant extends Step: class HarvestMaturePlant extends Step:
var mature_plant_number : int = 0 var mature_plant_number : int = 0
func generate_indicators(_p: Player, planet : Planet) -> Array[InGameIndicator]: func generate_indicators(_p: Player, planet : Planet) -> Array[InGameIndicator]:
var indicators : Array[InGameIndicator] = [] var indicators : Array[InGameIndicator] = []
for entity in planet.entity_container.get_children(): for entity in planet.entity_container.get_children():
if entity is Plant and entity.state == Plant.State.MATURE: if entity is Plant and entity.state == Plant.State.MATURE:
var indicator = generate_indicator(tr("HARVEST_MATURE_PLANTS_WITH_SHOVEL")) var indicator = generate_indicator(tr("HARVEST_MATURE_PLANTS_WITH_SHOVEL"))
indicator.follow_entity(entity) indicator.follow_entity(entity)
indicators.append( indicators.append(
indicator indicator
) )
mature_plant_number += 1 mature_plant_number += 1
return indicators return indicators
func is_step_over(_p : Player, planet : Planet) -> bool: func is_step_over(_p : Player, planet : Planet) -> bool:
if planet == null : if planet == null :
return false return false
var actual_mature_plant_number = 0 var actual_mature_plant_number = 0
for entity in planet.entity_container.get_children(): for entity in planet.entity_container.get_children():
if entity is Plant and entity.state == Plant.State.MATURE: if entity is Plant and entity.state == Plant.State.MATURE:
actual_mature_plant_number += 1 actual_mature_plant_number += 1
return mature_plant_number != actual_mature_plant_number return mature_plant_number != actual_mature_plant_number

View File

@ -85,9 +85,9 @@ Pour ajouter un ou plusieurs fichiers ou modifier du code dans le projet, suivez
4. Créer une demande de modification dans Gitea en suivant [ce lien](https://git.zacharie-guet.fr/zacharie/seeding-planets/pulls), cliquez sur Demande d'ajout et renseignez comme suit 4. Créer une demande de modification dans Gitea en suivant [ce lien](https://git.zacharie-guet.fr/zacharie/seeding-planets/pulls), cliquez sur Demande d'ajout et renseignez comme suit
- Fusionner dans `main` - Fusionner dans `main`
- Tirer les modification depuis votre branche - Tirer les modification depuis votre branche
- Cliquez sur "Nouvelle Demande d'ajout" - Cliquez sur "Nouvelle Demande d'ajout"
- Editer le titre de la demande d'ajout ou laissez comme telle - Editer le titre de la demande d'ajout ou laissez comme telle
- Cliquez sur sur "Créer une demande d'ajout" - Cliquez sur sur "Créer une demande d'ajout"
5. Communiquez avec l'équipe sur cette demande d'ajout, et appliquer la si vous avez l'aval de tous le monde (ou du moins des concernés du changement), en cliquant sur "Rebaser et avancer rapidement" 5. Communiquez avec l'équipe sur cette demande d'ajout, et appliquer la si vous avez l'aval de tous le monde (ou du moins des concernés du changement), en cliquant sur "Rebaser et avancer rapidement"
> Pour actualiser votre dépôt local, replacez vous sur `main` et récupérer les nouvelles modifications > Pour actualiser votre dépôt local, replacez vous sur `main` et récupérer les nouvelles modifications

View File

@ -27,7 +27,7 @@ metadata/_edit_use_anchors_ = true
visible = false visible = false
[node name="Tutorial" parent="CanvasLayer" node_paths=PackedStringArray("player", "planet") instance=ExtResource("5_orelw")] [node name="Tutorial" parent="CanvasLayer" node_paths=PackedStringArray("player", "planet") instance=ExtResource("5_orelw")]
player = NodePath("../../Entities/Player") player = NodePath("../../Planet/Entities/Player")
planet = NodePath("../../Planet") planet = NodePath("../../Planet")
[node name="BaseIndicator" parent="CanvasLayer" node_paths=PackedStringArray("player") instance=ExtResource("5_gisiu")] [node name="BaseIndicator" parent="CanvasLayer" node_paths=PackedStringArray("player") instance=ExtResource("5_gisiu")]
@ -36,28 +36,28 @@ offset_bottom = 91.0
size_flags_horizontal = 4 size_flags_horizontal = 4
size_flags_vertical = 4 size_flags_vertical = 4
script = ExtResource("6_cnjsq") script = ExtResource("6_cnjsq")
player = NodePath("../../Entities/Player") player = NodePath("../../Planet/Entities/Player")
[node name="Entities" type="Node2D" parent="."] [node name="Planet" parent="." node_paths=PackedStringArray("quota_reward", "entity_container") instance=ExtResource("8_t31p7")]
y_sort_enabled = true
[node name="Player" parent="Entities" instance=ExtResource("4_g33f4")]
position = Vector2(33, -75)
[node name="TruckLadder" parent="Entities" instance=ExtResource("9_gisiu")]
position = Vector2(50, -135)
[node name="TruckRecharge" parent="Entities" instance=ExtResource("10_cnjsq")]
position = Vector2(-46, -152)
[node name="Planet" parent="." node_paths=PackedStringArray("quota_reward", "import_entities_from_node") instance=ExtResource("8_t31p7")]
loot_item_number = Array[int]([1]) loot_item_number = Array[int]([1])
quota_reward = NodePath("../Reward") quota_reward = NodePath("../Reward")
import_entities_from_node = NodePath("../Entities") entity_container = NodePath("Entities")
[node name="Entities" type="Node2D" parent="Planet"]
y_sort_enabled = true
[node name="Player" parent="Planet/Entities" instance=ExtResource("4_g33f4")]
position = Vector2(2, 0)
[node name="TruckLadder" parent="Planet/Entities" instance=ExtResource("9_gisiu")]
position = Vector2(50, -135)
[node name="TruckRecharge" parent="Planet/Entities" instance=ExtResource("10_cnjsq")]
position = Vector2(-46, -152)
[node name="Camera" parent="." node_paths=PackedStringArray("following") instance=ExtResource("16_m18ms")] [node name="Camera" parent="." node_paths=PackedStringArray("following") instance=ExtResource("16_m18ms")]
position = Vector2(2.22, 0) position = Vector2(2.22, 0)
following = NodePath("../Entities/Player") following = NodePath("../Planet/Entities/Player")
[connection signal="day_limit_exceed" from="Planet" to="CanvasLayer/Win" method="_on_planet_day_limit_exceed"] [connection signal="day_limit_exceed" from="Planet" to="CanvasLayer/Win" method="_on_planet_day_limit_exceed"]
[connection signal="pass_day_ended" from="Planet" to="RootGui" method="_on_planet_pass_day_ended"] [connection signal="pass_day_ended" from="Planet" to="RootGui" method="_on_planet_pass_day_ended"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cd62k5urdpw3i"
path="res://.godot/imported/blue_rect.png-d2bf0f89bd9a318145a3150338e6ed14.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://stages/terrain/planet/assets/textures/blue_rect.png"
dest_files=["res://.godot/imported/blue_rect.png-d2bf0f89bd9a318145a3150338e6ed14.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bvangyp301tsp"
path="res://.godot/imported/green_rect.png-2cde15567ae810adb5864776d70ef438.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://stages/terrain/planet/assets/textures/green_rect.png"
dest_files=["res://.godot/imported/green_rect.png-2cde15567ae810adb5864776d70ef438.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://yl4dg6gerykb"
path="res://.godot/imported/green_tiles.png-24cb2e8d3d77d0f127478dc375fc7791.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://stages/terrain/planet/assets/textures/green_tiles.png"
dest_files=["res://.godot/imported/green_tiles.png-24cb2e8d3d77d0f127478dc375fc7791.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bi08trir23od2"
path="res://.godot/imported/red_rect.png-2c97ffc5003cb92590914f11ff4ed41d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://stages/terrain/planet/assets/textures/red_rect.png"
dest_files=["res://.godot/imported/red_rect.png-2c97ffc5003cb92590914f11ff4ed41d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ddecvei4l62gn"
path="res://.godot/imported/red_tiles.png-0bb5056d42a3159163beb701fb8aa247.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://stages/terrain/planet/assets/textures/red_tiles.png"
dest_files=["res://.godot/imported/red_tiles.png-0bb5056d42a3159163beb701fb8aa247.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=false
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://02nuoqleo4yu"
path="res://.godot/imported/rock_cristal_texture.png-33ee9694873f9aa2e8604c502b12dee5.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://stages/terrain/planet/assets/textures/rock_cristal_texture.png"
dest_files=["res://.godot/imported/rock_cristal_texture.png-33ee9694873f9aa2e8604c502b12dee5.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bmdb63witojeg"
path="res://.godot/imported/round_red_tiles.png-7f838ac911f20be784ac93821bd5d5ba.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://stages/terrain/planet/assets/textures/round_red_tiles.png"
dest_files=["res://.godot/imported/round_red_tiles.png-7f838ac911f20be784ac93821bd5d5ba.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -1,48 +1,6 @@
[gd_scene load_steps=10 format=3 uid="uid://tsi5j1uxppa4"] [gd_scene load_steps=2 format=3 uid="uid://tsi5j1uxppa4"]
[ext_resource type="Script" uid="uid://d1mp5sguc0b6u" path="res://stages/terrain/planet/scripts/planet.gd" id="1_y7d8a"] [ext_resource type="Script" uid="uid://d1mp5sguc0b6u" path="res://stages/terrain/planet/scripts/planet.gd" id="1_y7d8a"]
[ext_resource type="Shader" uid="uid://bglep64ppn74p" path="res://common/vfx/materials/shaders/textures_data_filter.gdshader" id="3_6qoee"]
[sub_resource type="FastNoiseLite" id="FastNoiseLite_3v4ta"]
frequency = 0.04
offset = Vector3(0, 10, 0)
fractal_weighted_strength = 1.0
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_7jtco"]
width = 50
height = 50
noise = SubResource("FastNoiseLite_3v4ta")
seamless_blend_skirt = 0.0
[sub_resource type="Gradient" id="Gradient_ydx6d"]
colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 1)
[sub_resource type="GradientTexture1D" id="GradientTexture1D_6cs2h"]
gradient = SubResource("Gradient_ydx6d")
width = 1
[sub_resource type="Gradient" id="Gradient_qxify"]
offsets = PackedFloat32Array(0)
colors = PackedColorArray(0, 0, 0, 1)
[sub_resource type="GradientTexture1D" id="GradientTexture1D_sd6ll"]
gradient = SubResource("Gradient_qxify")
width = 1
[sub_resource type="ShaderMaterial" id="ShaderMaterial_hyapw"]
shader = ExtResource("3_6qoee")
shader_parameter/data_texture = SubResource("NoiseTexture2D_7jtco")
shader_parameter/data_texture_size = Vector2(120, 120)
shader_parameter/data_texture_threshold = 0.5
shader_parameter/texture_0 = SubResource("GradientTexture1D_6cs2h")
shader_parameter/texture_1 = SubResource("GradientTexture1D_sd6ll")
shader_parameter/texture_scale = 5.0
[node name="Planet" type="Node2D"] [node name="Planet" type="Node2D"]
script = ExtResource("1_y7d8a") script = ExtResource("1_y7d8a")
[node name="Polygon2D2" type="Polygon2D" parent="."]
visible = false
material = SubResource("ShaderMaterial_hyapw")
position = Vector2(-10, -10)
polygon = PackedVector2Array(10, 10, 10, 110, 110, 110, 110, 10)

View File

@ -0,0 +1,9 @@
[gd_resource type="ShaderMaterial" load_steps=3 format=3 uid="uid://b3vnia5tb6pil"]
[ext_resource type="Shader" uid="uid://q5isn3rwrir8" path="res://common/vfx/materials/shaders/texture_color_filter.gdshader" id="1_4m73r"]
[ext_resource type="Texture2D" uid="uid://bnrjnvceprxfn" path="res://stages/terrain/planet/assets/textures/garden_background_texture.png" id="2_4m73r"]
[resource]
shader = ExtResource("1_4m73r")
shader_parameter/red_overlay_tex = ExtResource("2_4m73r")
shader_parameter/scale = 0.006944444

View File

@ -0,0 +1,9 @@
[gd_resource type="ShaderMaterial" load_steps=3 format=3 uid="uid://85ap1buim1ha"]
[ext_resource type="Shader" uid="uid://q5isn3rwrir8" path="res://common/vfx/materials/shaders/texture_color_filter.gdshader" id="1_v8wor"]
[ext_resource type="Texture2D" uid="uid://c3t26nlbnkxg7" path="res://stages/terrain/planet/assets/textures/garden_decontamined_background_texture.png" id="2_v8wor"]
[resource]
shader = ExtResource("1_v8wor")
shader_parameter/red_overlay_tex = ExtResource("2_v8wor")
shader_parameter/scale = 0.006944444

View File

@ -0,0 +1,9 @@
[gd_resource type="ShaderMaterial" load_steps=3 format=3 uid="uid://dpxu8yeee4qi1"]
[ext_resource type="Shader" uid="uid://q5isn3rwrir8" path="res://common/vfx/materials/shaders/texture_color_filter.gdshader" id="1_6vcas"]
[ext_resource type="Texture2D" uid="uid://bnrjnvceprxfn" path="res://stages/terrain/planet/assets/textures/garden_background_texture.png" id="2_x0w2y"]
[resource]
shader = ExtResource("1_6vcas")
shader_parameter/red_overlay_tex = ExtResource("2_x0w2y")
shader_parameter/scale = 0.006944444

View File

@ -0,0 +1,11 @@
[gd_resource type="ShaderMaterial" load_steps=4 format=3 uid="uid://d365ovfmi3d0s"]
[ext_resource type="Shader" uid="uid://q5isn3rwrir8" path="res://common/vfx/materials/shaders/texture_color_filter.gdshader" id="1_xr5ia"]
[ext_resource type="Texture2D" uid="uid://beqx4rmgthkql" path="res://stages/terrain/planet/assets/textures/rock_background_texture.png" id="2_ieaec"]
[ext_resource type="Texture2D" uid="uid://02nuoqleo4yu" path="res://stages/terrain/planet/assets/textures/rock_cristal_texture.png" id="2_sc014"]
[resource]
shader = ExtResource("1_xr5ia")
shader_parameter/red_overlay_tex = ExtResource("2_ieaec")
shader_parameter/green_overlay_tex = ExtResource("2_sc014")
shader_parameter/scale = 0.006944444

File diff suppressed because it is too large Load Diff

View File

@ -1,137 +1,164 @@
@tool
extends Node2D extends Node2D
class_name Chunk class_name Chunk
const UNIT_PER_PIXEL = 30
var coord : Vector2i
var size : Vector2i
var planet : Planet var planet : Planet
var wall_threshold = 0.4 var planet_seed : int
var noise_image : Image var wall_threshold = 0.6
var entity_generated : bool var decontamination_threshold = 0.15
var cristal_threshold = 0.08
var rock_noise_image : Noise = null
var decontamination_noise_image : Noise = null
const CHUNK_TEXTURE_SCALE : float = 3.0 const NOISE_IMAGE_SIZE := 150
const DEFAULT_CHUNK_BACKGROUND_MATERIAL : ShaderMaterial = preload("res://stages/terrain/planet/resources/materials/default_chunk_material.tres")
const LOOT_NUMBER : Array[int] = [2,3,4] const LOOT_NUMBER : Array[int] = [2,3,4]
const LOOT_ITEM_NUMBER : Array[int] = [1,2] const LOOT_ITEM_NUMBER : Array[int] = [1,2]
var chunk_background_material const ROCK_NOISE_FREQUENCY := 0.01
const DECONTAMINATION_NOISE_FREQUENCY := 0.01
var generation_thread: Thread
@export_tool_button("Update", "Callable") var update_action = func():
planet_seed = randi()
setup()
var data : ChunkData
func _init( func _init(
_coord : Vector2i, _data : ChunkData,
_size : Vector2i, _planet : Planet = null,
_planet : Planet,
_entity_generated = false
): ):
coord = _coord planet = _planet
size = _size if planet:
planet = _planet planet_seed = planet.data.planet_seed
entity_generated = _entity_generated data = _data
func _ready(): func _ready():
noise_image = generate_noise() setup()
generate_background_sprite()
global_position = coord * size
generate_walls()
if not entity_generated:
generate_loot()
func generate_noise() -> Image: func setup():
var image_size = Vector2i( rock_noise_image = generate_noise(planet_seed + 1, ROCK_NOISE_FREQUENCY)
roundi(float(size.x) / UNIT_PER_PIXEL), decontamination_noise_image = generate_noise(planet_seed + 2, DECONTAMINATION_NOISE_FREQUENCY)
roundi(float(size.y) / UNIT_PER_PIXEL)
)
var noise: FastNoiseLite = FastNoiseLite.new()
noise.seed = planet.data.planet_seed
noise.noise_type = FastNoiseLite.TYPE_SIMPLEX_SMOOTH
noise.frequency = 0.05
noise.fractal_weighted_strength = 1.0
noise.offset = Vector3(
image_size.x * coord.x,
image_size.y * coord.y,
1
)
var image = noise.get_image( generation_thread = Thread.new()
image_size.x, generation_thread.start(
image_size.y, func ():
1.0, generate_rocks()
) generate_ground()
generate_decontamination()
)
return image global_position = data.chunk_coord * (Planet.CHUNK_TILE_SIZE * Planet.TILE_SIZE)
queue_redraw()
func generate_background_sprite() -> Polygon2D: func unload():
var sprite :Polygon2D = generate_polygon_sprite() for x in range(Planet.CHUNK_TILE_SIZE):
for y in range(Planet.CHUNK_TILE_SIZE):
var global_coord = Vector2i(x, y) + Planet.CHUNK_TILE_SIZE * data.chunk_coord
planet.rock_layer.erase_cell(global_coord)
planet.ground_layer.erase_cell(global_coord)
planet.decontamination_layer.erase_cell(global_coord)
sprite.texture = ImageTexture.create_from_image(noise_image) # Debug
# func _draw():
# draw_rect(
# Rect2(Vector2.ZERO, Vector2.ONE * Planet.CHUNK_TILE_SIZE * Planet.TILE_SIZE),
# Color.WHITE,
# false,
# 3
# )
var background_material = DEFAULT_CHUNK_BACKGROUND_MATERIAL.duplicate_deep() # for x in range(NOISE_IMAGE_SIZE):
# for y in range(NOISE_IMAGE_SIZE):
# var noise_value = rock_noise_image.get_noise_2d(
# x,
# y
# )
# draw_rect(
# Rect2(Vector2i(x,y) * Planet.CHUNK_SIZE / NOISE_IMAGE_SIZE, Vector2i.ONE * Planet.CHUNK_SIZE / NOISE_IMAGE_SIZE),
# Color.WHITE * ((noise_value+1)/2),
# true,
# )
background_material.set_shader_parameter("data_texture", ImageTexture.create_from_image(noise_image)) func generate_noise(
background_material.set_shader_parameter("data_texture_size", size) noise_seed : int,
background_material.set_shader_parameter("data_texture_threshold", wall_threshold) frequency := 0.01
background_material.set_shader_parameter("texture_scale", CHUNK_TEXTURE_SCALE) ) -> Noise:
var noise_image_size := NOISE_IMAGE_SIZE * Vector2i.ONE
var noise: FastNoiseLite = FastNoiseLite.new()
noise.seed = noise_seed
noise.noise_type = FastNoiseLite.TYPE_SIMPLEX
noise.frequency = 0.01
noise.fractal_type = FastNoiseLite.FRACTAL_NONE
noise.fractal_weighted_strength = 1.0
noise.offset = Vector3(
noise_image_size.x * data.chunk_coord.x,
noise_image_size.y * data.chunk_coord.y,
1
)
sprite.material = background_material return noise
return sprite func get_tile_value_from_noise(tile_position : Vector2i, noise : Noise) -> float:
var val = noise.get_noise_2d(
floori(float(tile_position.x * NOISE_IMAGE_SIZE) / Planet.CHUNK_TILE_SIZE),
floori(float(tile_position.y * NOISE_IMAGE_SIZE) / Planet.CHUNK_TILE_SIZE)
)
return (val + 1)/2
func generate_polygon_sprite() -> Polygon2D: func generate_rocks():
var sprite = Polygon2D.new() var cristals : Array[Vector2i] = []
sprite.polygon = PackedVector2Array([ var rocks : Array[Vector2i] = []
Vector2(0,0), for x in range(Planet.CHUNK_TILE_SIZE):
Vector2(size.x, 0), for y in range(Planet.CHUNK_TILE_SIZE):
Vector2(size.x, size.y), var tile_type := get_generated_rock_type(Vector2i(x, y))
Vector2(0, size.y), var global_coord = Vector2i(x, y) + Planet.CHUNK_TILE_SIZE * data.chunk_coord
]) if tile_type == RockLayer.TileType.CRISTAL:
cristals.append(global_coord)
elif tile_type == RockLayer.TileType.ROCK:
rocks.append(global_coord)
planet.rock_layer.mutex.lock()
planet.rock_layer.place_rocks(cristals, RockLayer.TileType.CRISTAL)
planet.rock_layer.place_rocks(rocks, RockLayer.TileType.ROCK)
planet.rock_layer.mutex.unlock()
func get_generated_rock_type(coord : Vector2i) -> RockLayer.TileType:
var tile_value : float = get_tile_value_from_noise(coord, rock_noise_image)
var saved_diff := data.get_rock_tile_diff(coord)
sprite.z_index = -100 if (
(saved_diff == ChunkData.TileDiff.PRESENT or tile_value < wall_threshold)
and saved_diff != ChunkData.TileDiff.ABSENT
):
return RockLayer.TileType.CRISTAL if tile_value < cristal_threshold else RockLayer.TileType.ROCK
return RockLayer.TileType.EMPTY
add_child(sprite) func generate_ground():
planet.ground_layer.mutex.lock()
for x in range(Planet.CHUNK_TILE_SIZE):
for y in range(Planet.CHUNK_TILE_SIZE):
planet.ground_layer.place_ground(Vector2i(x,y) + Planet.CHUNK_TILE_SIZE * data.chunk_coord)
planet.ground_layer.mutex.unlock()
return sprite func generate_decontamination():
var decontamination_tiles : Array[Vector2i] = []
for x in range(Planet.CHUNK_TILE_SIZE):
for y in range(Planet.CHUNK_TILE_SIZE):
var coord = Vector2i(x,y)
var tile_value : float = get_tile_value_from_noise(coord, decontamination_noise_image)
var saved_diff := data.get_decontamination_tile_diff(coord)
if (
(saved_diff == ChunkData.TileDiff.PRESENT or tile_value < decontamination_threshold)
and saved_diff != ChunkData.TileDiff.ABSENT
):
decontamination_tiles.append(Vector2i(x,y) + Planet.CHUNK_TILE_SIZE * data.chunk_coord)
planet.decontamination_layer.mutex.lock()
planet.decontamination_layer.place_decontaminations(decontamination_tiles)
planet.decontamination_layer.mutex.unlock()
func generate_walls(): func _exit_tree():
var static_body = StaticBody2D.new() generation_thread.wait_to_finish()
add_child(static_body)
var wall_resolution_factor = 25
for x in range(1, size.x, wall_resolution_factor):
for y in range(1, size.y, wall_resolution_factor):
if is_wall(Vector2(x, y)):
var new_collision_shape = CollisionShape2D.new()
new_collision_shape.shape = CircleShape2D.new()
new_collision_shape.shape.radius = wall_resolution_factor / 2.
static_body.add_child(new_collision_shape)
new_collision_shape.global_position = Vector2i(x, y) + coord * size + Vector2i.ONE * 5
func generate_loot(number : int = LOOT_NUMBER.pick_random()):
for i in range(number):
var loot : UndergroundLoot = (UndergroundLootData.SCENE.instantiate() as UndergroundLoot)
loot.item_number = LOOT_ITEM_NUMBER.pick_random()
var max_placement_try = 10
var valid_coord = false
while max_placement_try > 0 and not valid_coord:
var random_position = Vector2(
randf_range(0, size.x),
randf_range(0, size.y)
)
if not is_wall(random_position):
planet.add_entity(loot, random_position + Vector2(coord * size))
valid_coord = true
else :
max_placement_try -= 1
func get_pixel_point(point : Vector2) -> Vector2i:
var vec : Vector2 = Vector2(point) / UNIT_PER_PIXEL - Vector2.ONE
return Vector2i(
roundi(vec.x + 0.5),
roundi(vec.y + 0.5)
)
func is_wall(game_point : Vector2) -> bool:
var pixel_point = get_pixel_point(game_point)
return noise_image.get_pixel(pixel_point.x, pixel_point.y).r < wall_threshold

View File

@ -0,0 +1,62 @@
extends Resource
class_name ChunkData
enum TileDiff { NO_DIFF,PRESENT,ABSENT }
@export var chunk_coord : Vector2i
@export var rock_tiles_diff : Dictionary[String, TileDiff]
@export var decontamination_tiles_diff : Dictionary[String, TileDiff]
func _init(
_chunk_coord : Vector2i
):
chunk_coord = _chunk_coord
#region ------------------ Generic Tile ------------------
func get_coord_key(coord : Vector2i) -> String:
return "%d:%d" % [coord.x, coord.y]
func get_tile_diff(
coord : Vector2i,
tiles_diff : Dictionary[String, TileDiff]
) -> TileDiff:
if not has_diff(coord, tiles_diff):
return TileDiff.NO_DIFF
return tiles_diff[get_coord_key(coord)]
func update_tile_diff(
coord : Vector2i,
diff : TileDiff,
tiles_diff : Dictionary[String, TileDiff]
):
tiles_diff[get_coord_key(coord)] = diff
func has_diff(
coord : Vector2i,
tiles_diff : Dictionary[String, TileDiff]
):
return tiles_diff.has(get_coord_key(coord))
#region ------------------ Rock ------------------
func get_rock_tile_diff(coord : Vector2i) -> TileDiff:
return get_tile_diff(coord, rock_tiles_diff)
func update_rock_tile_diff(coord : Vector2i, diff : TileDiff):
update_tile_diff(coord, diff, rock_tiles_diff)
func has_rock_tile_diff(coord : Vector2i):
return has_diff(coord, rock_tiles_diff)
#region ------------------ Decontamination ------------------
func get_decontamination_tile_diff(coord : Vector2i) -> TileDiff:
return get_tile_diff(coord, decontamination_tiles_diff)
func update_decontamination_tile_diff(coord : Vector2i, diff : TileDiff):
update_tile_diff(coord, diff, decontamination_tiles_diff)
func has_decontamination_tile_diff(coord : Vector2i):
return has_diff(coord, decontamination_tiles_diff)

View File

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

View File

@ -1,48 +1,18 @@
extends Node2D extends Node2D
class_name Garden class_name Garden
const GARDEN_TEXTURE_SCALE : float = 3.0
var contamination_material : ShaderMaterial = preload("res://stages/terrain/planet/resources/materials/ground_contamination.tres")
var contamination_sprite : Polygon2D
var contamination_texture : Texture2D
var decontamination_surface : float
var plants : Array[Plant] var plants : Array[Plant]
var planet_data : PlanetData var planet_data : PlanetData
var size = PlanetData.DEFAULT_GARDEN_SIZE
func _init(_planet_data : PlanetData, _initial_plants : Array[Plant] = []): func _init(_planet_data : PlanetData, _initial_plants : Array[Plant] = []):
planet_data = _planet_data planet_data = _planet_data
plants = _initial_plants plants = _initial_plants
# update_garden_score() # update_garden_score()
func _ready(): func _ready():
contamination_sprite = generate_contamination_terrain_sprite()
decontamination_surface = planet_data.get_decontamination_surface()
for p in plants: for p in plants:
p.harvested.connect(_on_plant_harvested) p.harvested.connect(_on_plant_harvested)
p.state_changed.connect(_on_plant_state_changed) p.state_changed.connect(_on_plant_state_changed)
# update_garden_score()
func generate_contamination_terrain_sprite() -> Polygon2D:
if not planet_data.garden_contamination:
planet_data.generate_default_contamination()
var sprite :Polygon2D = generate_polygon_sprite(size, 1)
contamination_texture = ImageTexture.create_from_image(planet_data.garden_contamination.image)
contamination_material.set_shader_parameter("data_texture", contamination_texture)
contamination_material.set_shader_parameter("data_texture_size", size)
contamination_material.set_shader_parameter("texture_scale", GARDEN_TEXTURE_SCALE)
sprite.material = contamination_material
return sprite
func get_score(): func get_score():
var score = 0 var score = 0
@ -88,30 +58,3 @@ func remove_plant(p: Plant):
func update_garden_score(): func update_garden_score():
planet_data.garden_score = get_score() planet_data.garden_score = get_score()
func impact_contamination(impact_position : Vector2, impact_radius : int, contamination : bool = false):
planet_data.impact_contamination(impact_position, impact_radius, 0. if contamination else 1.)
if contamination_texture:
contamination_texture.update(planet_data.garden_contamination.image)
func generate_polygon_sprite(s : Vector2 = size, order : int = 0) -> Polygon2D:
var sprite = Polygon2D.new()
sprite.polygon = PackedVector2Array([
Vector2(0,0),
Vector2(s.x, 0),
Vector2(s.x, s.y),
Vector2(0, s.y),
])
sprite.z_index = -100 + order
add_child(sprite)
return sprite
func is_in_garden(point : Vector2) -> bool:
return planet_data.is_in_garden(point)
func is_there_contamination(point : Vector2) -> bool:
return planet_data.get_contamination(point) < 0.5

View File

@ -8,7 +8,17 @@ signal pass_day_ended(planet : Planet)
const PASS_DAY_ANIMATION_TIME : float = 1.5 const PASS_DAY_ANIMATION_TIME : float = 1.5
const DEFAULT_DAY_LIMIT : int = 7 const DEFAULT_DAY_LIMIT : int = 7
const PLANET_TEXTURE_SCALE : float = 5.0
const TILE_SET : TileSet = preload("res://stages/terrain/planet/resources/planet_tileset.tres")
const TILE_SCALE = 1
const TILE_SIZE : int = roundi(TILE_SET.tile_size.x * TILE_SCALE)
const GROUND_TILE_MAP_MATERIAL : Material = preload("res://stages/terrain/planet/resources/materials/ground_planet_tilemap.tres")
const CONTAMINATION_TILE_MAP_MATERIAL : Material = preload("res://stages/terrain/planet/resources/materials/contamination_planet_tilemap.tres")
const ORIGIN_CHUNK_HOLE_RADIUS = 5
const CHUNK_TILE_SIZE : int = 20
const CHUNK_SIZE = CHUNK_TILE_SIZE * TILE_SIZE
const CHUNK_LOAD_DISTANCE : int = 1
const CHUNK_UNLOAD_DISTANCE : int = 2
@export_group("Loot") @export_group("Loot")
@export var first_loot_number : int = 3 @export var first_loot_number : int = 3
@ -19,157 +29,178 @@ var data : PlanetData
var contamination_texture : ImageTexture var contamination_texture : ImageTexture
var day_limit = DEFAULT_DAY_LIMIT var day_limit = DEFAULT_DAY_LIMIT
var rock_layer : RockLayer
var ground_layer : GroundLayer
var decontamination_layer : DecontaminationLayer
var garden : Garden = null var garden : Garden = null
var generated_chunks_objects : Array[Vector2i] = [] var generated_chunks : Dictionary[String,Chunk] = {}
func _init():
data = GameInfo.game_data.current_planet_data
func _ready(): func _ready():
data = GameInfo.game_data.current_planet_data entity_container.position = TILE_SIZE * CHUNK_TILE_SIZE * Vector2.ONE / 2
load_entities(data.entities_saved_data)
entity_container.position = PlanetData.DEFAULT_GARDEN_SIZE/2
load_entities(data.entities_saved_data)
var plants : Array[Plant] = [] var plants : Array[Plant] = []
for e in entity_container.get_children(): for e in entity_container.get_children():
if e is Plant: if e is Plant:
plants.append(e) plants.append(e)
garden = Garden.new(data, plants) garden = Garden.new(data, plants)
add_child(garden) add_child(garden)
if len(GameInfo.game_data.unlocked_plant_types) == 0: if len(GameInfo.game_data.unlocked_plant_types) == 0:
quota_reward.trigger_reward() quota_reward.trigger_reward()
generate_first_entities() generate_first_entities()
AudioManager.enter_planet() AudioManager.enter_planet()
if player: ground_layer = GroundLayer.new(self)
generate_near_chunks(player) add_child(ground_layer)
rock_layer = RockLayer.new(self)
add_child(rock_layer)
decontamination_layer = DecontaminationLayer.new(self)
add_child(decontamination_layer)
if player:
generate_near_chunks(player)
func _process(_d): func _process(_d):
if player: if player:
generate_near_chunks(player) generate_near_chunks(player)
remove_far_chunks(player)
# queue_redraw() # queue_redraw()
# func _draw(): # func _draw():
# var factor = 20 # var factor = 20
# for x in range(terrain_size.x / factor): # for x in range(terrain_size.x / factor):
# for y in range(terrain_size.y / factor): # for y in range(terrain_size.y / factor):
# var point = Vector2(x, y) * factor # var point = Vector2(x, y) * factor
# draw_circle( # draw_circle(
# point, # point,
# factor/10, # factor/10,
# Color.BLUE if garden.is_there_contamination(point) else Color.RED, # Color.BLUE if garden.is_there_contamination(point) else Color.RED,
# true # true
# ) # )
#region ------------------ Generation ------------------ #region ------------------ Generation ------------------
func generate_first_entities(): func generate_first_entities():
if not (Vector2i.ZERO in data.generated_chunk_entities): if not (Vector2i.ZERO in data.generated_chunk_entities):
# Generate shovel # Generate shovel
drop_item(Shovel.new(), PlanetData.DEFAULT_GARDEN_SIZE/2 + Vector2(0, 100)) drop_item(Shovel.new(), entity_container.global_position + Vector2(0, 100))
# Generate first loots func get_chunk_key(coord) -> String:
generate_loot(first_loot_number) return "%d:%d" % [coord.x, coord.y]
data.generated_chunk_entities.append(Vector2i.ZERO)
func generate_near_chunks(p : Player): func generate_near_chunks(p : Player):
var player_chunk_coord = Vector2i(
floor(p.global_position.x / PlanetData.DEFAULT_GARDEN_SIZE.x),
floor(p.global_position.y / PlanetData.DEFAULT_GARDEN_SIZE.y)
)
for x in [-1, 0, 1]: var player_chunk_coord = Math.get_chunk_from_pos(p.global_position)
for y in [-1, 0, 1]:
var coord = Vector2i(x,y) + player_chunk_coord for x in range(-CHUNK_LOAD_DISTANCE, CHUNK_LOAD_DISTANCE+1):
if coord != Vector2i.ZERO and generated_chunks_objects.find(coord) == -1: for y in range(-CHUNK_LOAD_DISTANCE, CHUNK_LOAD_DISTANCE+1):
generate_chunk(coord) var coord : Vector2i = Vector2i(x,y) + player_chunk_coord
if not generated_chunks.has(get_chunk_key(coord)):
generate_chunk(coord)
func remove_far_chunks(p : Player):
var player_chunk_coord = Vector2i(
floor(p.global_position.x / (CHUNK_TILE_SIZE * TILE_SIZE)),
floor(p.global_position.y / (CHUNK_TILE_SIZE * TILE_SIZE))
)
for chunk in generated_chunks.values():
var chunk_coord = chunk.data.chunk_coord
if player_chunk_coord.distance_to(chunk_coord) > CHUNK_UNLOAD_DISTANCE:
remove_chunk(chunk)
func generate_chunk(coord : Vector2i): func generate_chunk(coord : Vector2i):
if generated_chunks_objects.find(coord) == -1: var chunk_data := data.get_or_create_chunk_data(coord)
generated_chunks_objects.append(coord) if coord == Vector2i(0,0):
var new_chunk = Chunk.new( create_hole_in_chunk(chunk_data, ORIGIN_CHUNK_HOLE_RADIUS)
coord, var chunk_key = get_chunk_key(coord)
PlanetData.DEFAULT_GARDEN_SIZE, if not generated_chunks.has(chunk_key):
self, var new_chunk = Chunk.new(
(data.generated_chunk_entities.find(coord) != -1) chunk_data,
) self
add_child(new_chunk) )
data.generated_chunk_entities.append(coord) generated_chunks[chunk_key] = new_chunk
add_child(new_chunk)
data.generated_chunk_entities.append(coord)
func create_hole_in_chunk(chunk_data : ChunkData, hole_radius : int):
var hole_center = Vector2i.ONE * floori(CHUNK_TILE_SIZE/2.)
for x in range(CHUNK_TILE_SIZE):
for y in range(CHUNK_TILE_SIZE):
var coord = Vector2i(x,y)
if coord.distance_to(hole_center) < hole_radius:
chunk_data.update_rock_tile_diff(
coord,
ChunkData.TileDiff.ABSENT
)
func remove_chunk(chunk : Chunk):
generated_chunks.erase(get_chunk_key(chunk.data.chunk_coord))
chunk.unload()
chunk.queue_free()
func save(): func save():
data.entities_saved_data = save_entities() data.entities_saved_data = save_entities()
#endregion #endregion
#region ------------------ Usage ------------------ #region ------------------ Usage ------------------
func plant( func plant(
type : PlantType, type : PlantType,
plant_position : Vector2, plant_position : Vector2,
plant_mutations : Array[PlantMutation] = [] plant_mutations : Array[PlantMutation] = []
) -> bool: ) -> bool:
if garden.is_in_garden(plant_position): var new_plant = garden.plant(type, plant_mutations)
var new_plant = garden.plant(type, plant_mutations) add_entity(new_plant, plant_position)
add_entity(new_plant, plant_position) return true
return true
return false
func pass_day(): func pass_day():
for e : Node2D in entity_container.get_children(): for e : Node2D in entity_container.get_children():
if e.has_method("_start_pass_day"): if e.has_method("_start_pass_day"):
e._start_pass_day() e._start_pass_day()
pass_day_started.emit(self) pass_day_started.emit(self)
await get_tree().create_timer(PASS_DAY_ANIMATION_TIME/2.).timeout await get_tree().create_timer(PASS_DAY_ANIMATION_TIME/2.).timeout
pass_day_proceeded.emit(self) pass_day_proceeded.emit(self)
data.day += 1 data.day += 1
data.quota_days -= 1 data.quota_days -= 1
for e : Node2D in entity_container.get_children(): for e : Node2D in entity_container.get_children():
if e.has_method("_pass_day"): if e.has_method("_pass_day"):
e._pass_day() e._pass_day()
pass_day_ended.emit(self) pass_day_ended.emit(self)
await get_tree().create_timer(PASS_DAY_ANIMATION_TIME/2.).timeout await get_tree().create_timer(PASS_DAY_ANIMATION_TIME/2.).timeout
for e : Node2D in entity_container.get_children(): for e : Node2D in entity_container.get_children():
if e.has_method("_end_pass_day"): if e.has_method("_end_pass_day"):
e._end_pass_day() e._end_pass_day()
garden.update_garden_score() garden.update_garden_score()
if data.garden_score >= data.get_quota_score(): if data.garden_score >= data.get_quota_score():
reach_quota() reach_quota()
elif data.quota_days <= 0: elif data.quota_days <= 0:
day_limit_exceed.emit(self) day_limit_exceed.emit(self)
save() save()
func generate_loot(number : int):
for i in range(number):
var loot : UndergroundLoot = (UndergroundLootData.SCENE.instantiate() as UndergroundLoot)
loot.item_number = loot_item_number.pick_random()
var loot_random_range = UndergroundLoot.LOOTED_ITEM_RANDOM_RANGE
add_entity(
loot,
Vector2(
randf_range(loot_random_range, garden.size.x),
randf_range(loot_random_range, garden.size.y)
)
)
func reach_quota(): func reach_quota():
data.quota += 1 data.quota += 1
quota_reward.trigger_reward() quota_reward.trigger_reward()
await quota_reward.reward_chosen await quota_reward.reward_chosen
garden.update_garden_score() garden.update_garden_score()
if data.garden_score >= data.get_quota_score(): if data.garden_score >= data.get_quota_score():
reach_quota() reach_quota()
data.quota_days = data.get_quota_duration() data.quota_days = data.get_quota_duration()
#endregion #endregion

View File

@ -3,97 +3,85 @@ class_name PlanetData
signal new_quota_started(planet_data : PlanetData) signal new_quota_started(planet_data : PlanetData)
signal plant_gaining_score(p : Plant, amount : int) signal plant_gaining_score(p : Plant, amount : int)
signal contamination_updated(decontamination_surface : float)
signal updated(planet_data : PlanetData) signal updated(planet_data : PlanetData)
const MAX_DEFAULT_CONTAMINATION_ZONE_SURFACE = 3000 const MAX_DEFAULT_CONTAMINATION_ZONE_SURFACE = 3000
const DEFAULT_GARDEN_SIZE = Vector2(1500,1500) const DEFAULT_GARDEN_SIZE = Vector2(1500,1500)
@export var garden_size : Vector2 = Vector2(2000,2000) @export var garden_size : Vector2 = Vector2(2000,2000)
@export var garden_contamination : TerrainData @export var planet_seed : int
@export var quota : int = 0 : @export var quota : int = 0 :
set(v): set(v):
quota = v quota = v
is_quota_announced = false is_quota_announced = false
new_quota_started.emit(self) new_quota_started.emit(self)
@export var is_quota_announced : bool = false @export var is_quota_announced : bool = false
@export var garden_score : int = 0 : @export var garden_score : int = 0 :
set(v): set(v):
garden_score = v garden_score = v
updated.emit(self) updated.emit(self)
@export var day : int = 1 @export var day : int = 1
@export var planet_seed : int
@export var quota_days : int = get_quota_duration() : @export var quota_days : int = get_quota_duration() :
set(v): set(v):
quota_days = v quota_days = v
updated.emit(self) updated.emit(self)
@export var entities_saved_data : Array[EntityData] = [] @export var entities_saved_data : Array[EntityData] = []
@export var score_by_plant : Array[int] = [] @export var score_by_plant : Array[int] = []
@export var generated_chunk_entities : Array[Vector2i] @export var generated_chunk_entities : Array[Vector2i]
@export var tutorial_step : int = 0 @export var tutorial_step : int = 0
@export var chunks_data : Dictionary[String, ChunkData]
func _init(_base_size : Vector2 = DEFAULT_GARDEN_SIZE): func _init(_base_size : Vector2 = DEFAULT_GARDEN_SIZE):
planet_seed = randi() planet_seed = randi()
garden_size = _base_size garden_size = _base_size
garden_contamination = TerrainData.new(garden_size)
garden_contamination.draw_random_zone(
MAX_DEFAULT_CONTAMINATION_ZONE_SURFACE,
garden_size/2,
planet_seed
)
contamination_updated.emit(get_decontamination_surface())
#region ------------------ Contamination ------------------ #region ------------------ Chunks ------------------
func impact_contamination(position : Vector2, impact_radius : float, to_value : float = 1.):
garden_contamination.draw_circle(
position,
impact_radius,
to_value
)
contamination_updated.emit(get_decontamination_surface())
func is_in_garden(point): func get_coord_id(coord):
return ( return "%d:%d" % [coord.x, coord.y]
point.x > 0
and point.y > 0
and point.x < garden_size.x
and point.y < garden_size.y)
func get_contamination(point : Vector2) -> float: func has_chunk_data(coord : Vector2i) -> bool:
return garden_contamination.get_value(point) return chunks_data.has(get_coord_id(coord))
func get_decontamination_coverage() -> float: func add_chunk_data(coord : Vector2i, data : ChunkData):
return garden_contamination.get_value_coverage() chunks_data[get_coord_id(coord)] = data
func get_decontamination_surface() -> float: func get_chunk_data(coord : Vector2i) -> ChunkData:
return garden_contamination.get_value_surface() return chunks_data[get_coord_id(coord)]
#endregion func get_or_create_chunk_data(coord : Vector2i) -> ChunkData:
if has_chunk_data(coord):
return get_chunk_data(coord)
else:
var new_chunk_data = ChunkData.new(coord)
add_chunk_data(coord, new_chunk_data)
return new_chunk_data
#region ------------------ Quotas ------------------ #region ------------------ Quotas ------------------
func get_quota_score(q : int = quota) -> int: func get_quota_score(q : int = quota) -> int:
var first_quotas = [ var first_quotas = [
4, 4,
10, 10,
20, 20,
40, 40,
80, 80,
] ]
if quota >= len(first_quotas): if quota >= len(first_quotas):
return pow(q, 3) return pow(q, 3)
else: else:
return first_quotas[q] return first_quotas[q]
func get_quota_duration(_q = quota) -> int: func get_quota_duration(_q = quota) -> int:
return 7 return 7
#endregion #endregion
#region ------------------ Score ------------------ #region ------------------ Score ------------------
func plant_has_gained_score(plant : Plant, amount : int): func plant_has_gained_score(plant : Plant, amount : int):
plant_gaining_score.emit(plant, amount) plant_gaining_score.emit(plant, amount)
#endregion #endregion

View File

@ -1,105 +0,0 @@
extends Resource
class_name TerrainData
const UNIT_PER_PIXEL = 30
@export var image : Image
@export var image_size : Vector2i
func _init(terrain_size : Vector2):
image_size = terrain_size / UNIT_PER_PIXEL
image = Image.create(
image_size.x,
image_size.y,
false,
Image.Format.FORMAT_L8
)
func draw_random_zone(
zone_max_surface : float,
zone_position : Vector2i,
random_seed : int,
):
var noise: Noise = FastNoiseLite.new()
noise.seed = random_seed
noise.noise_type = FastNoiseLite.TYPE_CELLULAR
noise.frequency = 0.1
var noise_image_size : Vector2i = Vector2i.ONE * (image_size)
var noise_image_center = noise_image_size / 2
var noise_image = noise.get_image(
noise_image_size.x,
noise_image_size.y,
1.0,
)
ImageTools.flatten(noise_image, 0.5)
ImageTools.draw_circle(
noise_image,
noise_image_center,
int(round(80./float(UNIT_PER_PIXEL))),
Color.WHITE,
)
var random_step = 1
var zone_radius = noise_image_size.x - random_step
while get_value_surface(noise_image) > zone_max_surface:
zone_radius -= random_step
ImageTools.draw_circle(
noise_image,
noise_image_center,
zone_radius,
Color.BLACK,
true
)
image.blit_rect(
noise_image,
Rect2i(
Vector2i.ZERO,
noise_image_size
),
Vector2i(zone_position / UNIT_PER_PIXEL) - noise_image_size/2
)
func draw_circle(position : Vector2, impact_radius : float, to_value : float = 1.):
ImageTools.draw_circle(
image,
position / UNIT_PER_PIXEL,
roundi(impact_radius / UNIT_PER_PIXEL),
Color(1., 1., 1., to_value)
)
func is_in_image(pixel_point : Vector2i):
return (
pixel_point.x > 0
and pixel_point.y > 0
and pixel_point.x < image.get_width()
and pixel_point.y < image.get_height())
func is_in_terrain(point : Vector2):
return is_in_image(get_pixel_point(point))
func get_value(point : Vector2) -> float:
var pixel_point : Vector2i = get_pixel_point(point)
if (is_in_image(pixel_point)):
return image.get_pixel(
pixel_point.x,
pixel_point.y
).r
return 0
func get_value_coverage(i : Image = image) -> float:
return ImageTools.get_color_coverage(i)
func get_value_surface(i : Image = image) -> float:
return float(ImageTools.get_color_pixel_count(i)) * UNIT_PER_PIXEL
func get_pixel_point(point : Vector2) -> Vector2i:
var vec : Vector2 = Vector2(point) / UNIT_PER_PIXEL - Vector2.ONE
return Vector2i(
roundi(vec.x + 0.5),
roundi(vec.y + 0.5)
)

View File

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

View File

@ -0,0 +1,33 @@
@tool
extends PlanetLayer
class_name DecontaminationLayer
const MATERIAL : Material = preload("res://stages/terrain/planet/resources/materials/decontamination_planet_tilemap.tres")
const DECONTAMINATION_TILE_TERRAIN_SET : int = 0
const DECONTAMINATION_TILE_TERRAIN : int = 2
func setup():
material = MATERIAL
z_index = -99
func place_decontamination(coord : Vector2i, save = false):
place_decontaminations([coord], save)
func place_decontaminations(coords : Array[Vector2i], save = false):
set_cells_terrain_connect(
coords,
DECONTAMINATION_TILE_TERRAIN_SET,
DECONTAMINATION_TILE_TERRAIN
)
if save:
for coord in coords:
var chunk_coord = Vector2i(
floori(coord.x / float(Planet.CHUNK_TILE_SIZE)),
floori(coord.y / float(Planet.CHUNK_TILE_SIZE)),
)
(planet.data
.get_chunk_data(chunk_coord)
.update_decontamination_tile_diff(coord, ChunkData.TileDiff.PRESENT))
func is_decontamined(coord : Vector2i) -> bool:
return has_cell(coord)

View File

@ -0,0 +1 @@
uid://2p41t6efxudd

View File

@ -0,0 +1,18 @@
@tool
extends PlanetLayer
class_name GroundLayer
const MATERIAL : Material = preload("res://stages/terrain/planet/resources/materials/ground_planet_tilemap.tres")
const GROUND_TILE_SOURCE_ID : int = 0
const GROUND_TILE_ATLAS_COORD : Vector2i = Vector2i.ZERO
func setup():
material = MATERIAL
z_index = -100
func place_ground(tile_position : Vector2i):
set_cell(
tile_position,
GROUND_TILE_SOURCE_ID,
GROUND_TILE_ATLAS_COORD,
)

View File

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

View File

@ -0,0 +1,36 @@
@abstract
extends TileMapLayer
class_name PlanetLayer
var planet : Planet
@onready var mutex : Mutex = Mutex.new()
func _init(
_planet : Planet = null
):
planet = _planet
func _ready():
tile_set = Planet.TILE_SET
scale = Vector2.ONE * Planet.TILE_SCALE
navigation_enabled = false
setup()
func setup():
pass
func get_all_neighbors_cell(coord : Vector2i) -> Array[Vector2i]:
var neighbors : Array[Vector2i] = []
for x in [-1, 0, 1]:
for y in [-1, 0, 1]:
var neighbor = Vector2i(
coord.x + x,
coord.y + y
)
if coord != neighbor:
neighbors.append(neighbor)
return neighbors
func has_cell(tile_position : Vector2i) -> bool:
return get_cell_source_id(tile_position) != -1

View File

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

View File

@ -0,0 +1,87 @@
@tool
extends PlanetLayer
class_name RockLayer
const MATERIAL : Material = preload("res://stages/terrain/planet/resources/materials/rock_planet_tilemap.tres")
const ROCK_TILE_TERRAIN_SET : int = 0
const ROCK_TILE_TERRAIN : int = 0
const CRISTAL_TILE_TERRAIN : int = 1
const CRISTAL_LOOT_CHANCE : float = 1
enum TileType { EMPTY,ROCK,CRISTAL }
func setup():
material = MATERIAL
z_index = 2
func place_rock(coord : Vector2i, type := TileType.ROCK):
if type != TileType.EMPTY:
set_cells_terrain_connect(
[coord],
ROCK_TILE_TERRAIN_SET,
ROCK_TILE_TERRAIN if type == TileType.ROCK else CRISTAL_TILE_TERRAIN
)
func place_rocks(coords : Array[Vector2i], type := TileType.ROCK):
if type != TileType.EMPTY:
set_cells_terrain_connect(
coords,
ROCK_TILE_TERRAIN_SET,
ROCK_TILE_TERRAIN if type == TileType.ROCK else CRISTAL_TILE_TERRAIN
)
func remove_rocks(coords : Array[Vector2i], save = false):
set_cells_terrain_connect(
coords,
ROCK_TILE_TERRAIN_SET,
-1
)
if save:
for coord in coords:
var chunk_coord = Vector2i(
floori(coord.x / float(Planet.CHUNK_TILE_SIZE)),
floori(coord.y / float(Planet.CHUNK_TILE_SIZE)),
)
var chunk_tile_coord : Vector2i = coord - chunk_coord * Planet.CHUNK_TILE_SIZE
(planet.data
.get_chunk_data(chunk_coord)
.update_rock_tile_diff(chunk_tile_coord, ChunkData.TileDiff.ABSENT))
func dig_rocks(coords : Array[Vector2i]) -> bool:
var has_rock = false
for coord in coords:
if has_tile(coord):
has_rock = true
loot_rock(coord)
if has_rock:
remove_rocks(coords, true)
return has_rock
func loot_rock(coord : Vector2i):
if get_tile_type(coord) == TileType.CRISTAL and randf() < CRISTAL_LOOT_CHANCE:
if len(GameInfo.game_data.unlocked_plant_types):
var loot = Seed.new(
GameInfo.game_data.unlocked_plant_types.pick_random()
)
planet.drop_item(
loot,
coord * Planet.TILE_SIZE + Vector2i.ONE * floori(Planet.TILE_SIZE/2.),
floor(Planet.TILE_SIZE/2.)
)
func update_cells(coords : Array[Vector2i]):
for coord in coords:
if has_tile(coord):
place_rock(coord)
func has_tile(coord : Vector2i) -> bool:
return has_cell(coord)
func get_tile_type(coord : Vector2i) -> TileType:
if has_tile(coord):
return TileType.CRISTAL if get_cell_tile_data(coord).terrain == CRISTAL_TILE_TERRAIN else TileType.ROCK
return TileType.EMPTY

View File

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

View File

@ -5,9 +5,8 @@ const BORDER_WIDTH = 100
var player : Player var player : Player
@export var import_entities_from_node : Node2D = null @export var entity_container : Node2D
@onready var entity_container : Node2D = create_entity_container()
func instantiate_entity(s: PackedScene, entity_position : Vector2): func instantiate_entity(s: PackedScene, entity_position : Vector2):
var entity = s.instantiate() as Node2D var entity = s.instantiate() as Node2D
@ -23,15 +22,18 @@ func add_entity(entity : Node2D, entity_global_position : Vector2 = Vector2.ZERO
else: else:
entity.get_parent().remove_child(entity) entity.get_parent().remove_child(entity)
enroll_entity(entity)
container.add_child(entity)
entity.global_position = entity_global_position
func enroll_entity(entity : Node2D):
if "terrain" in entity: if "terrain" in entity:
entity.terrain = self entity.terrain = self
if entity is Player: if entity is Player:
player = entity player = entity
container.add_child(entity)
entity.global_position = entity_global_position
func save_entities() -> Array[EntityData]: func save_entities() -> Array[EntityData]:
var saved_entities_data : Array[EntityData] = [] var saved_entities_data : Array[EntityData] = []
for e in entity_container.get_children(): for e in entity_container.get_children():
@ -42,6 +44,9 @@ func save_entities() -> Array[EntityData]:
return saved_entities_data return saved_entities_data
func load_entities(saved_entities_data : Array[EntityData]): func load_entities(saved_entities_data : Array[EntityData]):
for static_entity in entity_container.get_children():
enroll_entity(static_entity)
for save_data in saved_entities_data: for save_data in saved_entities_data:
var entity = save_data.load() var entity = save_data.load()
if entity: if entity:
@ -57,14 +62,6 @@ func create_entity_container() -> Node2D:
add_child(container) add_child(container)
if import_entities_from_node:
for child in import_entities_from_node.get_children():
add_entity(
child,
child.global_position + (container.global_position - import_entities_from_node.global_position),
container
)
return container return container
func drop_item(item: Item, item_position : Vector2, random_displacement_factor = 0) -> ItemObject: func drop_item(item: Item, item_position : Vector2, random_displacement_factor = 0) -> ItemObject:

View File

@ -11,16 +11,17 @@
[node name="GameGui" parent="CanvasLayer" instance=ExtResource("2_dw1sv")] [node name="GameGui" parent="CanvasLayer" instance=ExtResource("2_dw1sv")]
[node name="TruckInterior" parent="." node_paths=PackedStringArray("import_entities_from_node") instance=ExtResource("1_ycq4y")] [node name="TruckInterior" parent="." node_paths=PackedStringArray("entity_container") instance=ExtResource("1_ycq4y")]
position = Vector2(0, 0) position = Vector2(0, 0)
import_entities_from_node = NodePath("../Entities") entity_container = NodePath("Entities")
[node name="Entities" type="Node2D" parent="TruckInterior"]
y_sort_enabled = true
[node name="Player" parent="TruckInterior/Entities" instance=ExtResource("5_dw1sv")]
position = Vector2(51, 492)
[node name="Camera" type="Camera2D" parent="."] [node name="Camera" type="Camera2D" parent="."]
position = Vector2(385, 343) position = Vector2(385, 343)
script = ExtResource("2_063c3") script = ExtResource("2_063c3")
metadata/_custom_type_script = "uid://d1nsr56bh1a1y" metadata/_custom_type_script = "uid://d1nsr56bh1a1y"
[node name="Entities" type="Node2D" parent="."]
[node name="Player" parent="Entities" instance=ExtResource("5_dw1sv")]
position = Vector2(51, 492)