# Duane's Dungeon - a rogue-like game # Copyright (C) 2023 Duane Robertson # entity.gd - anything on the map # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . extends Node2D class_name Entity const ACT_RAD = 20.0 const SPRITE_TEXT_SHRINK = 4.0 var act_rect := Rect2i(0, 0, 0, 0) var back_cover:ColorRect var body_area:Area2D var description := 'an entity of some sort' var ename:String var icon_fg := 'ffffff' var icon_name := 'player' var is_dead := false var kills := 0 var level := 0 var map:TileMap var grid_position:Vector2i = Vector2i(10, 10) var name_label:Label var reloaded := false var species:StringName = '' var sprite:Sprite2D var sprite_sheet:SpriteSheet var sprite_text:VBoxContainer var status:RefCounted var target_pos:Vector2 var tile_size:Vector2i var uid := 0 var unliving := false var vision_area:Area2D var vision := 5 func _init(in_data=null): # uid = G.make_uid() set_species_attributes() # assert(species != 'null') status = Status.new(self) if in_data is Dictionary: pass # reloaded = true # load_data(in_data) elif in_data is Vector2: place(in_data) # Called when the node enters the scene tree for the first time. func _ready(): add_to_group('entities') if not map: map = get_parent() assert(map) sprite_sheet = G.sprite_sheet tile_size = sprite_sheet.tile_size make_sprite(icon_name, icon_fg) if not is_in_group('player'): place(grid_position) # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(_delta): pass func add_sprite_text(color := Color.GREEN, text := '') -> void: if not close_to_player(): return var lab := VanishingLabel.new(text, color, 2, \ 20, HORIZONTAL_ALIGNMENT_LEFT) sprite_text.add_child(lab) func close_to_player() -> bool: var player = get_tree().get_nodes_in_group('player')[0] return player.act_rect.has_point(grid_position) func make_sprite(icon:String, sp_color:String): # Is this used with anything other than icon_name? var spr = Sprite2D.new() var tex:ImageTexture = G.sprite_sheet.texture(icon, sp_color) back_cover = ColorRect.new() back_cover.color = Color.BLACK back_cover.visible = false add_child(back_cover) spr.texture = tex spr.centered = false sprite = spr add_child(sprite) # When loaded, status sprites end up under # the main sprite, since they're loaded first. move_child(sprite, 0) vision_area = Area2D.new() vision_area.collision_mask = 2 var coll = CollisionShape2D.new() coll.shape = RectangleShape2D.new() coll.shape.size = Vector2(tile_size * vision * 2) coll.position = Vector2(tile_size) / 2.0 vision_area.add_child(coll) add_child(vision_area) body_area = Area2D.new() body_area.collision_layer = 2 coll = CollisionShape2D.new() coll.shape = RectangleShape2D.new() coll.shape.size = Vector2(tile_size / 2) coll.position = Vector2(tile_size) / 2.0 body_area.add_child(coll) add_child(body_area) sprite_text = VBoxContainer.new() sprite_text.custom_minimum_size = tile_size * SPRITE_TEXT_SHRINK sprite_text.scale = Vector2.ONE / SPRITE_TEXT_SHRINK sprite_text.add_theme_constant_override('separation', -10) sprite_text.mouse_filter = Control.MOUSE_FILTER_IGNORE sprite.add_child(sprite_text) var nl = Label.new() nl.scale = Vector2.ONE / SPRITE_TEXT_SHRINK / 3.0 nl.mouse_filter = Control.MOUSE_FILTER_IGNORE nl.text = name nl.custom_minimum_size = Vector2(16 * SPRITE_TEXT_SHRINK * 3.0, 0) nl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT nl.add_theme_font_override('font', G.font) nl.add_theme_font_size_override('font_size', 40) sprite.add_child(nl) name_label = nl # func move_to(pos:Vector2i): # grid_position = pos # position = Vector2(grid_position * tile_size) # target_pos = position func place(pos: Vector2i, force:=false) -> bool: if not walkable(pos): return false if not force and map.entity_at(pos): return false # map.move_entity(self, pos) grid_position = pos if is_in_group('player'): # position = map.map_to_local(grid_position) position = Vector2(grid_position * tile_size) # move_map(false) # map.unshade(grid_position) else: # position = map.map_to_local(grid_position) position = Vector2(grid_position * tile_size) target_pos = position return true func release_if_dead(): if is_dead: Creature.release_name(name) map.remove_entity(self) func set_species_attributes(): pass func show_message(_text: String, _color := Color.YELLOW) -> void: if close_to_player(): G.debug('%s (%s): %s' % [species, name, _text]) func turn(): # G.debug('entity turn') pass func vary(n: int) -> int: return int(round(max(1, n * ((randi() % 100) + 51) / 100.0))) func walkable(p:Vector2i) -> bool: return map.terr_map[map.get_tile(p)].walkable