diff options
Diffstat (limited to 'entity.rhm')
-rw-r--r-- | entity.rhm | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/entity.rhm b/entity.rhm new file mode 100644 index 0000000..07bc81b --- /dev/null +++ b/entity.rhm @@ -0,0 +1,148 @@ +#lang rhombus/static +import: + rhombus/random + "chunk.rhm" open + +export: + Entity + EntityGatherable + EntityPlayer + ActionResponse + +let rand = random.Random() + +class Entity(id:: Int, mutable x :: Int, mutable y :: Int, mutable current_chunk :: Chunk): + nonfinal + // Validate that the provided coordinates are within the max distance range of + // entities current coordinates. + method validate_dist(x :: Int, y :: Int, max_dist :: Int): + let diff_x = math.abs(this.x - x) + let diff_y = math.abs(this.y - y) + // Diagonal case is moving in the same direction (same x & y) and that amount + // is less than or equal to max distance + let diagonal = diff_x == diff_y && diff_x <= max_dist + let x_axis = diff_x <= max_dist && diff_y == 0 + let y_axis = diff_y <= max_dist && diff_x == 0 + let no_movement = diff_x == 0 && diff_y == 0 + !no_movement && (diagonal || x_axis || y_axis) + + // Validate that the x,y coordinates map to a chunk that is + // near the current coordinates of the entity + method validate_chunk(x :: Int, y :: Int) :: maybe(Chunk): + let current_chunk = this.current_chunk + let local_x = x - current_chunk.offset_x * current_chunk.width + let local_y = y - current_chunk.offset_y * current_chunk.height + let less_x = local_x < 0 + let less_y = local_y < 0 + let more_x = local_x >= current_chunk.width + let more_y = local_y >= current_chunk.height + let inside_x = !(less_x || more_x) + let inside_y = !(less_y || more_y) + let next_chunk :: maybe(Chunk) = cond + | inside_x && less_y: current_chunk.neighbours.get(Direction.north, #false) + | more_x && less_y: current_chunk.neighbours.get(Direction.north_east, #false) + | more_x && inside_y: current_chunk.neighbours.get(Direction.east, #false) + | more_x && more_y: current_chunk.neighbours.get(Direction.south_east, #false) + | inside_x && more_y: current_chunk.neighbours.get(Direction.south, #false) + | less_x && more_y: current_chunk.neighbours.get(Direction.south_west, #false) + | less_x && inside_y: current_chunk.neighbours.get(Direction.west, #false) + | less_x && less_y: current_chunk.neighbours.get(Direction.north_west, #false) + | ~else: current_chunk + + next_chunk + + // Validate that the tile at the coordinates would not block a movement of the entity + // to those coordinates from its current coordinates + method validate_move(x :: Int, y :: Int, max_dist :: Int, target_chunk :: Chunk): + let current_tile = this.current_chunk.get_tile(this.x, this.y) + let target_tile = target_chunk.get_tile(x, y) + let diff_x = x - this.x + let diff_y = y - this.y + let direction = cond + | diff_x == 0 && diff_y < 0: Direction.north + | diff_x > 0 && diff_y < 0: Direction.north_east + | diff_x > 0 && diff_y == 0: Direction.east + | diff_x > 0 && diff_y > 0: Direction.south_east + | diff_x == 0 && diff_y > 0: Direction.south + | diff_x < 0 && diff_y > 0: Direction.south_west + | diff_x < 0 && diff_y == 0: Direction.west + | diff_x < 0 && diff_y < 0: Direction.north_west + let current_block = current_tile.get_block() + let target_block = flip_block(target_tile.get_block()) + !(direction == current_block || direction == target_block) + + method + | move(x :: Int, y :: Int): move(x, y, 1) + | move(x :: Int, y :: Int, max_dist :: Int) :: Boolean: + let target_chunk = validate_chunk(x, y) + + cond + | validate_dist(x, y, max_dist) && target_chunk != #false && validate_move(x, y, max_dist, target_chunk!!): + this.current_chunk := target_chunk!! + this.x := x + this.y := y + #true + | ~else: + #false + +class EntityGatherable(quantity :: Int, odds :: Real): + extends Entity + + constructor(id :: Int, x :: Int, y :: Int, current_chunk :: Chunk): + super(id, x, y, current_chunk)(10, 1 / 10) + +enum ActionResponse: + ok + unsuccessful + invalid + cooldown + +enum Slot: + right_hand + +enum Item: + bronze_axe + +class Stack(item :: Item, quantity :: Int) + +class EntityPlayer(mutable timer :: Int): + extends Entity + + field max_timer = 4 + field inventory :: Array.now_of(maybe(Stack)) = Array.make(20, #false) + field equip :: MutableMap.now_of(Slot, Item) = MutableMap() + + constructor(id :: Int, x :: Int, y :: Int, current_chunk :: Chunk): + super(id, x, y, current_chunk)(0) + + method tick(): + // TODO this "ticks" the entity. Things like healing and other + // stuff should happen within here. In the future we probably + // want to set a state where the player is gathering and in tick + // we calculate the odds that they did gather properly + if timer == 0: + | timer := max_timer + | timer := timer - 1 + + method gather(target_entity :: Entity) :: ActionResponse: + let x = target_entity.x + let y = target_entity.y + let target_chunk = validate_chunk(x, y) + let right_type = target_entity is_a EntityGatherable + + fun validate_gather_resource(entity :: EntityGatherable) :: ActionResponse: + // TODO odds should change based on skills of player + // we should also validate player is carrying the right tool + // we should also have the timer count down every tick + // not just inside the gathering call + let roll = rand.random() + if roll < entity.odds: + | ActionResponse.ok + | ActionResponse.unsuccessful + + cond + | timer != 0: ActionResponse.cooldown + | right_type && validate_dist(x, y, 1) && target_chunk != #false: + validate_gather_resource(target_entity) + | ~else: + ActionResponse.invalid |