Skip to content

Commit

Permalink
[GH-709] implement dynamic obstacles (#715)
Browse files Browse the repository at this point in the history
* Add obstacles new fields

* Add obstacles module

* Add dynamic obstacles transition logic and active obstacles check

* Show obstacle color in the game client

* Remove unnecessary config

* Anihilate IO inspect

* Move mechanic to raised

* Add make polygon helper method

* Add player moving and mechanic trigger on obstacle activation

* Only move players if obstacle turn active

* Rename obstacle active to collisionable

* Add new polygon hit mechanic

* Fix credo complains

* Restore dynamic obstracle

* Remove unnecessary tracker pattern match

* Rename missleading atom

* Restore unnecessary variable

* Rename status to base_status

* Fix wrong obstacle

* Spikes positions (#750)

* north-eastern spike

* add south-western spikes

* fix clipping for top obstacle

* Fix base statuses for spikes

---------

Co-authored-by: tvillegas98 <tvillegas@fi.uba.ar>
Co-authored-by: agustinesco <agustinesco@outlook.es>

* Use config name instead of id name

* Fix missing obstacles names

* Move obstacle transition from using message to propper tick handling

* Use collisionable obstacles for projectiles

* Fix spike time transition not being updated

* Fix credo complain

* Allow power ups to spawn in dynamic obstacles and DESTROY recursion

* Fix obstacle transition

* fix clipping between spikes and wall on north easter obstacle

* Allow to move other entities besides player

* Limit entities to process

* Restore bot state machine

---------

Co-authored-by: Theo Katz <67983386+tkz00@users.noreply.github.com>
Co-authored-by: tvillegas98 <tvillegas@fi.uba.ar>
Co-authored-by: Theo <theokatz@gmail.com>
  • Loading branch information
4 people authored Jul 8, 2024
1 parent 9431143 commit 6b7c5b2
Show file tree
Hide file tree
Showing 16 changed files with 577 additions and 21 deletions.
48 changes: 44 additions & 4 deletions apps/arena/lib/arena/entities.ex
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,12 @@ defmodule Arena.Entities do
}
end

def new_obstacle(id, %{position: position, radius: radius, shape: shape, vertices: vertices}) do
def new_obstacle(id, %{position: position, radius: radius, shape: shape, vertices: vertices} = params) do
%{
id: id,
category: :obstacle,
shape: get_shape(shape),
name: "Obstacle" <> Integer.to_string(id),
name: params.name,
position: position,
radius: radius,
vertices: vertices,
Expand All @@ -207,7 +207,14 @@ defmodule Arena.Entities do
y: 0.0
},
is_moving: false,
aditional_info: %{}
aditional_info: %{
collisionable: obstacle_collisionable?(params),
statuses_cycle: params.statuses_cycle,
status: params.base_status,
type: params.type,
time_until_transition_start: nil,
time_until_transition: nil
}
}
end

Expand Down Expand Up @@ -313,6 +320,24 @@ defmodule Arena.Entities do
}
end

def make_polygon_area(id, positions) do
%{
id: id,
category: :obstacle,
shape: :polygon,
name: "BashDamageArea",
position: %{x: 0.0, y: 0.0},
radius: 0.0,
vertices: positions,
speed: 0.0,
direction: %{
x: 0.0,
y: 0.0
},
is_moving: false
}
end

def make_polygon(id, vertices) do
%{
id: id,
Expand Down Expand Up @@ -371,7 +396,9 @@ defmodule Arena.Entities do
def maybe_add_custom_info(entity) when entity.category == :obstacle do
{:obstacle,
%Arena.Serialization.Obstacle{
color: "red"
color: "red",
collisionable: entity.aditional_info.collisionable,
status: entity.aditional_info.status
}}
end

Expand Down Expand Up @@ -437,4 +464,17 @@ defmodule Arena.Entities do
def refresh_cooldowns(%{category: :player} = entity) do
put_in(entity, [:aditional_info, :cooldowns], %{})
end

def obstacle_collisionable?(%{type: "static"}) do
true
end

def obstacle_collisionable?(params) do
%{base_status: base_status, statuses_cycle: statuses_cycle} = params

base_status_params =
Map.get(statuses_cycle, String.to_existing_atom(base_status))

base_status_params.make_obstacle_collisionable
end
end
127 changes: 127 additions & 0 deletions apps/arena/lib/arena/game/obstacle.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
defmodule Arena.Game.Obstacle do
@moduledoc """
Module to hable obstacles logic in game updater
"""

alias Arena.Game.Skill
alias Arena.Entities
alias Arena.Game.Player

def collisionable_obstacle?(obstacle) do
obstacle.aditional_info.collisionable
end

def get_collisionable_obstacles(obstacles) do
Map.filter(obstacles, fn {_obstacle_id, obstacle} -> collisionable_obstacle?(obstacle) end)
end

def handle_transition_init(obstacle) do
now = DateTime.utc_now() |> DateTime.to_unix(:millisecond)

current_status_params =
Map.get(obstacle.aditional_info.statuses_cycle, String.to_existing_atom(obstacle.aditional_info.status))

update_in(obstacle, [:aditional_info], fn aditional_info ->
aditional_info
|> Map.put(:next_status, current_status_params.next_status)
|> Map.put(:collisionable, current_status_params.make_obstacle_collisionable)
|> Map.put(:time_until_transition_start, now + current_status_params.time_until_transition_ms)
end)
end

def update_obstacle_transition_status(game_state, %{aditional_info: %{type: "dynamic"}} = obstacle) do
now = DateTime.utc_now() |> DateTime.to_unix(:millisecond)

case obstacle.aditional_info.status do
"transitioning" ->
if obstacle.aditional_info.time_until_transition < now do
handle_transition(game_state, obstacle.id)
else
game_state
end

_status ->
if obstacle.aditional_info.time_until_transition_start < now do
start_obstacle_transition(game_state, obstacle)
else
game_state
end
end
end

def update_obstacle_transition_status(game_state, _obstacle), do: game_state

def start_obstacle_transition(game_state, obstacle) do
next_status_params =
Map.get(obstacle.aditional_info.statuses_cycle, String.to_existing_atom(obstacle.aditional_info.next_status))

now = DateTime.utc_now() |> DateTime.to_unix(:millisecond)

obstacle =
update_in(obstacle, [:aditional_info], fn aditional_info ->
aditional_info
|> Map.put(:status, "transitioning")
|> Map.put(:time_until_transition, now + next_status_params.transition_time_ms)
end)

put_in(game_state, [:obstacles, obstacle.id], obstacle)
end

def handle_transition(game_state, obstacle_id) do
obstacle = get_in(game_state, [:obstacles, obstacle_id])

next_status_params =
Map.get(obstacle.aditional_info.statuses_cycle, String.to_existing_atom(obstacle.aditional_info.next_status))

now = DateTime.utc_now() |> DateTime.to_unix(:millisecond)

obstacle =
update_in(obstacle, [:aditional_info], fn aditional_info ->
aditional_info
|> Map.put(:next_status, next_status_params.next_status)
|> Map.put(:status, obstacle.aditional_info.next_status)
|> Map.put(:collisionable, next_status_params.make_obstacle_collisionable)
|> Map.put(:time_until_transition_start, now + next_status_params.time_until_transition_ms)
end)

Enum.reduce(next_status_params.on_activation_mechanics, game_state, fn mechanic, game_state ->
Skill.do_mechanic(game_state, obstacle, mechanic, %{skill_direction: %{x: 0, y: 0}})
end)
|> put_in([:obstacles, obstacle.id], obstacle)
|> maybe_move_above_players(obstacle_id)
end

defp maybe_move_above_players(game_state, obstacle_id) do
obstacle = get_in(game_state, [:obstacles, obstacle_id])

if collisionable_obstacle?(obstacle) do
obstacle_area = Entities.make_polygon_area(obstacle.id, obstacle.vertices)

alive_players =
Player.alive_players(game_state.players)

players =
Physics.check_collisions(obstacle_area, alive_players)
|> Enum.reduce(game_state.players, fn player_id, players_acc ->
player = Map.get(players_acc, player_id)

new_position =
Physics.get_closest_available_position(
player.position,
player,
game_state.external_wall,
game_state.obstacles
)

updated_player =
Map.put(player, :position, new_position)

Map.put(players_acc, player_id, updated_player)
end)

%{game_state | players: players}
else
game_state
end
end
end
7 changes: 7 additions & 0 deletions apps/arena/lib/arena/game/player.ex
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,13 @@ defmodule Arena.Game.Player do
|> round()
end

def calculate_real_damage(
_player_damage_owner,
damage
) do
damage
end

def store_item(player, item) do
put_in(player, [:aditional_info, :inventory], item)
end
Expand Down
35 changes: 35 additions & 0 deletions apps/arena/lib/arena/game/skill.ex
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,37 @@ defmodule Arena.Game.Skill do
|> put_in([:last_id], last_id)
end

def do_mechanic(game_state, entity, {:polygon_hit, polygon_hit}, _skill_params) do
polygon_damage_area = Entities.make_polygon_area(entity.id, polygon_hit.vertices)

entity_player_owner = get_entity_player_owner(game_state, entity)

# Players
alive_players =
Player.alive_players(game_state.players)
|> Map.filter(fn {_, alive_player} -> alive_player.id != entity_player_owner.id end)

players =
Physics.check_collisions(polygon_damage_area, alive_players)
|> Enum.reduce(game_state.players, fn player_id, players_acc ->
real_damage = Player.calculate_real_damage(entity_player_owner, polygon_hit.damage)

target_player =
Map.get(players_acc, player_id)
|> Player.take_damage(real_damage)

send(self(), {:damage_done, entity_player_owner.id, polygon_hit.damage})

unless Player.alive?(target_player) do
send(self(), {:to_killfeed, entity_player_owner.id, target_player.id})
end

Map.put(players_acc, player_id, target_player)
end)

%{game_state | players: players}
end

def handle_skill_effects(game_state, player, effects, execution_duration_ms, game_config) do
effects_to_apply =
GameUpdater.get_effects_from_config(effects, game_config)
Expand Down Expand Up @@ -411,6 +442,10 @@ defmodule Arena.Game.Skill do
}),
do: get_in(game_state, [:players, owner_id])

# Default to zone id
defp get_entity_player_owner(_game_state, _),
do: %{id: 9999}

defp maybe_move_player(game_state, %{category: :player} = player, move_by)
when not is_nil(move_by) do
player_for_moving = %{player | is_moving: true, speed: move_by}
Expand Down
4 changes: 4 additions & 0 deletions apps/arena/lib/arena/game_tracker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ defmodule Arena.GameTracker do
update_in(data, [:players, player_id, :damage_taken], fn damage_taken -> damage_taken + amount end)
end

defp update_data(data, {:damage_done, 9999, _amount}) do
data
end

defp update_data(data, {:damage_done, player_id, amount}) do
update_in(data, [:players, player_id, :damage_done], fn damage_done -> damage_done + amount end)
end
Expand Down
Loading

0 comments on commit 6b7c5b2

Please sign in to comment.