class_name Art extends RefCounted ## Every graphic on the playing field has an id. Art draws it in the chosen ## style, or from a picture whose file name is the id, so that an artist can ## replace a graphic without changing code: res://art/terrain/ground.png ## replaces the drawing of GROUND. ## The style changed, or pictures were loaded. Everything drawn by Art has to ## be drawn again. signal changed const GROUND := &"terrain/ground" const SOFT_ROCK := &"terrain/soft_rock" const HARD_ROCK := &"terrain/hard_rock" const BEDROCK := &"terrain/bedrock" ## The cracks of a tile that took hits, drawn over the tile. const CRACKS := &"terrain/cracks" ## The burst where a pickaxe lands. const HIT := &"effect/hit" const FIGURE := &"figure" ## The outline of the tile under the mouse. const MARKER := &"marker" ## Graphics of later milestones, shown in the showcase of the style samples. const DEAD_SOIL := &"soil/dead" const LIVING_SOIL := &"soil/living" const MOSS := &"plant/moss" const VINE := &"plant/vine" const FIBER_PLANT := &"plant/fiber" const STONE := &"item/stone" const GRAZER := &"animal/grazer" const RUIN := &"ruin" ## Every id, in the order the showcase and the checks go through them. const IDS: Array[StringName] = [ GROUND, SOFT_ROCK, HARD_ROCK, BEDROCK, CRACKS, HIT, FIGURE, MARKER, DEAD_SOIL, LIVING_SOIL, MOSS, VINE, FIBER_PLANT, STONE, GRAZER, RUIN, ] ## The folder where pictures that replace drawings are looked for. const OVERRIDE_DIR := "res://art" ## The styles to choose from, in the order the game offers them. var styles: Array[ArtStyle] = [WarmStyle.new(), PastelStyle.new(), GlowStyle.new()] ## The style everything is drawn in. var style: ArtStyle = styles[0] var _overrides: Dictionary[StringName, Texture2D] = {} ## The keys of the styles, as LaunchOptions and the settings name them. static func style_keys() -> Array[StringName]: return [WarmStyle.KEY, PastelStyle.KEY, GlowStyle.KEY] ## Draws everything in the style named `key` from now on. Returns false, and ## changes nothing, if no style has that key. func select(key: StringName) -> bool: for candidate in styles: if candidate.key() == key: if candidate != style: style = candidate changed.emit() return true return false ## Switches to the style after the current one, and from the last to the ## first. func select_next() -> void: style = styles[(styles.find(style) + 1) % styles.size()] changed.emit() ## Uses the picture `/.png` instead of the drawing, for every ## id that has one. Pictures loaded earlier are dropped. func load_overrides(directory: String) -> void: _overrides.clear() for id in IDS: var texture := _load_texture(directory.path_join(String(id) + ".png")) if texture != null: _overrides[id] = texture changed.emit() ## True if a picture replaces the drawing of `id`. func has_override(id: StringName) -> bool: return _overrides.has(id) ## Draws the graphic `id` into `rect` on `canvas`. Call it only while ## `canvas` draws, from its _draw(). `state` carries what the graphic shows, ## for example how far a plant has grown; see the styles for the entries. func draw(canvas: CanvasItem, id: StringName, rect: Rect2, state: Dictionary = {}) -> void: var texture: Texture2D = _overrides.get(id) if texture != null: canvas.draw_texture_rect(texture, rect, false) else: style.draw(canvas, id, rect, state) static func _load_texture(path: String) -> Texture2D: if path.begins_with("res://"): return load(path) as Texture2D if ResourceLoader.exists(path) else null if not FileAccess.file_exists(path): return null var image := Image.load_from_file(path) return ImageTexture.create_from_image(image) if image != null else null