Skip to content

Commit

Permalink
Gh 894 power ups (#899)
Browse files Browse the repository at this point in the history
* feat: add new feature to give power ups to the killer

* refactor: remove no longer used variable

* feat: add new feature to give power ups dropped by crates to the player

* refactor: create power_up_boost/3 function and move power up stat increase logic there

* fix: projectiles were not checking that the crates are alive

* fix: handle zone kill

* Fix effect not sending crate destroyed to client

* Do not deal damage to dead entities

* Delete unused code

* Delete dead code

* Delete unused alias

* Isolate crate destroy behavior

* Delete unused function

* Use entities take damage instead of specifics

* Fix projectiles giving multiples power ups

* Fix skill killfeed

---------

Co-authored-by: agustinesco <[email protected]>
  • Loading branch information
tvillegas98 and agustinesco authored Sep 9, 2024
1 parent 66aa4de commit dcab0f6
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 123 deletions.
17 changes: 15 additions & 2 deletions apps/arena/lib/arena/entities.ex
Original file line number Diff line number Diff line change
Expand Up @@ -472,8 +472,21 @@ defmodule Arena.Entities do
defp get_shape("point"), do: :point
defp get_shape(_), do: nil

def take_damage(%{category: :player} = entity, damage), do: Player.take_damage(entity, damage)
def take_damage(%{category: :crate} = entity, damage), do: Crate.take_damage(entity, damage)
def take_damage(%{category: :player} = entity, damage, damage_owner_id) do
if alive?(entity) do
Player.take_damage(entity, damage, damage_owner_id)
else
entity
end
end

def take_damage(%{category: :crate} = entity, damage, damage_owner_id) do
if alive?(entity) do
Crate.take_damage(entity, damage, damage_owner_id)
else
entity
end
end

def alive?(%{category: :player} = entity), do: Player.alive?(entity)
def alive?(%{category: :crate} = entity), do: Crate.alive?(entity)
Expand Down
11 changes: 9 additions & 2 deletions apps/arena/lib/arena/game/crate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ defmodule Arena.Game.Crate do
@moduledoc """
Module to handle crate logic
"""
def take_damage(crate, damage) do
update_in(crate, [:aditional_info, :health], fn current_health -> max(current_health - damage, 0) end)
def take_damage(crate, damage, damage_owner_id) do
crate =
update_in(crate, [:aditional_info, :health], fn current_health -> max(current_health - damage, 0) end)

unless alive?(crate) do
send(self(), {:crate_destroyed, damage_owner_id, crate.id})
end

crate
end

def alive?(crate) do
Expand Down
13 changes: 1 addition & 12 deletions apps/arena/lib/arena/game/effect.ex
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,7 @@ defmodule Arena.Game.Effect do

send(self(), {:damage_done, pool_owner.id, real_damage})

Entities.take_damage(entity, real_damage)
|> maybe_send_to_killfeed(pool_owner.id)
Entities.take_damage(entity, real_damage, pool_owner.id)
end
end

Expand Down Expand Up @@ -267,16 +266,6 @@ defmodule Arena.Game.Effect do
entity
end

defp maybe_send_to_killfeed(%{category: :player} = entity, pool_owner_id) do
unless Player.alive?(entity) do
send(self(), {:to_killfeed, pool_owner_id, entity.id})
end

entity
end

defp maybe_send_to_killfeed(entity, _pool_owner_id), do: entity

defp add_effect_to_entity(game_state, entity, effect, owner_id, start_action_removal_in_ms) do
now = System.monotonic_time(:millisecond)

Expand Down
51 changes: 41 additions & 10 deletions apps/arena/lib/arena/game/player.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ defmodule Arena.Game.Player do
end)
end

def take_damage(%{aditional_info: %{damage_immunity: true}} = player, _) do
def take_damage(%{aditional_info: %{damage_immunity: true}} = player, _, _damage_owner_id) do
player
end

def take_damage(player, damage) do
def take_damage(player, damage, damage_owner_id) do
defense_multiplier = 1 - player.aditional_info.bonus_defense
damage_taken = round(damage * defense_multiplier)

Expand All @@ -42,14 +42,21 @@ defmodule Arena.Game.Player do

send(self(), {:damage_taken, player.id, damage_taken})

Map.update!(player, :aditional_info, fn info ->
%{
info
| health: max(info.health - damage_taken, 0),
last_damage_received: System.monotonic_time(:millisecond),
mana: min(info.mana + mana_to_recover, info.max_mana)
}
end)
player =
Map.update!(player, :aditional_info, fn info ->
%{
info
| health: max(info.health - damage_taken, 0),
last_damage_received: System.monotonic_time(:millisecond),
mana: min(info.mana + mana_to_recover, info.max_mana)
}
end)

unless alive?(player) do
send(self(), {:to_killfeed, damage_owner_id, player.id})
end

player
end

def kill_player(player) do
Expand Down Expand Up @@ -427,6 +434,30 @@ defmodule Arena.Game.Player do
end)
end

def power_up_boost(player, amount_of_power_ups, game_config) do
player
|> update_in([:aditional_info, :power_ups], fn amount -> amount + amount_of_power_ups end)
|> update_in([:aditional_info], fn additional_info ->
Enum.reduce(1..amount_of_power_ups, additional_info, fn _times, additional_info ->
additional_info
|> Map.update(:health, additional_info.health, fn current_health ->
Utils.increase_value_by_base_percentage(
current_health,
additional_info.base_health,
game_config.game.power_up_health_modifier
)
end)
|> Map.update(:max_health, additional_info.max_health, fn max_health ->
Utils.increase_value_by_base_percentage(
max_health,
additional_info.base_health,
game_config.game.power_up_health_modifier
)
end)
end)
end)
end

####################
# Internal helpers #
####################
Expand Down
8 changes: 2 additions & 6 deletions apps/arena/lib/arena/game/skill.ex
Original file line number Diff line number Diff line change
Expand Up @@ -455,14 +455,10 @@ defmodule Arena.Game.Skill do

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

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

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

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

Expand All @@ -478,7 +474,7 @@ defmodule Arena.Game.Skill do

crate =
Map.get(crates_acc, crate_id)
|> Crate.take_damage(real_damage)
|> Entities.take_damage(real_damage, player.id)

Map.put(crates_acc, crate_id, crate)
end)
Expand Down
125 changes: 34 additions & 91 deletions apps/arena/lib/arena/game_updater.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ defmodule Arena.GameUpdater do
alias Arena.Serialization.PingUpdate
alias Arena.Serialization.Ping
alias Phoenix.PubSub
alias Arena.Utils
alias Arena.Game.Trap

##########################
Expand Down Expand Up @@ -267,7 +266,6 @@ defmodule Arena.GameUpdater do
|> move_players()
|> reduce_players_cooldowns(delta_time)
|> recover_mana()
|> resolve_players_collisions_with_power_ups()
|> resolve_players_collisions_with_items()
|> resolve_projectiles_effects_on_collisions(state.game_config)
|> apply_zone_damage_to_players(state.game_config.game)
Expand All @@ -282,8 +280,6 @@ defmodule Arena.GameUpdater do
|> add_pools_collisions()
|> handle_pools(state.game_config)
|> remove_expired_pools(now)
# Crates
|> handle_destroyed_crates()
|> Map.put(:server_timestamp, now)
# Traps
|> remove_activated_traps()
Expand Down Expand Up @@ -537,13 +533,13 @@ defmodule Arena.GameUpdater do
) do
entry = %{killer_id: killer_id, victim_id: victim_id}
victim = Map.get(game_state.players, victim_id)
amount_of_power_ups = get_amount_of_power_ups(victim, game_config.game.power_ups_per_kill)
killer = Map.get(game_state.players, killer_id)

game_state =
game_state
|> update_in([:killfeed], fn killfeed -> [entry | killfeed] end)
|> maybe_add_kill_to_player(killer_id)
|> spawn_power_ups(game_config, victim, amount_of_power_ups)
|> grant_power_up_to_killer(game_config, killer, victim)
|> put_player_position(victim_id)

broadcast_player_dead(state.game_state.game_id, victim_id)
Expand Down Expand Up @@ -696,6 +692,23 @@ defmodule Arena.GameUpdater do
{:noreply, state}
end

def handle_info({:crate_destroyed, player_id, crate_id}, state) do
game_state = state.game_state
crate = get_in(state.game_state, [:crates, crate_id])
player = get_in(state.game_state, [:players, player_id])

player = Player.power_up_boost(player, crate.aditional_info.amount_of_power_ups, state.game_config)

game_state =
game_state
|> put_in([:players, player_id], player)
|> put_in([:crates, crate_id, :aditional_info, :status], :DESTROYED)

state = Map.put(state, :game_state, game_state)

{:noreply, state}
end

##########################
# End callbacks
##########################
Expand Down Expand Up @@ -1176,24 +1189,6 @@ defmodule Arena.GameUpdater do
%{game_state | projectiles: moved_projectiles}
end

defp resolve_players_collisions_with_power_ups(%{players: players, power_ups: power_ups} = game_state) do
{updated_players, updated_power_ups} =
Enum.reduce(players, {players, power_ups}, fn {_player_id, player}, {players_acc, power_ups_acc} ->
case find_collided_power_up(player.collides_with, power_ups_acc) do
nil ->
{players_acc, power_ups_acc}

power_up_id ->
power_up = Map.get(power_ups_acc, power_up_id)
process_power_up(player, power_up, players_acc, power_ups_acc)
end
end)

game_state
|> Map.put(:players, updated_players)
|> Map.put(:power_ups, updated_power_ups)
end

defp resolve_players_collisions_with_items(game_state) do
{players, items} =
Enum.reduce(game_state.players, {game_state.players, game_state.items}, fn {_player_id, player},
Expand Down Expand Up @@ -1354,25 +1349,6 @@ defmodule Arena.GameUpdater do
%{game_state | players: updated_players}
end

defp handle_destroyed_crates(%{crates: crates} = game_state) do
Enum.reduce(crates, game_state, fn {crate_id, crate}, game_state ->
if Crate.alive?(crate) or crate.aditional_info.status == :DESTROYED do
game_state
else
amount_of_power_ups = crate.aditional_info.amount_of_power_ups

Process.send_after(
self(),
{:delayed_power_up_spawn, crate, amount_of_power_ups},
crate.aditional_info.power_up_spawn_delay_ms
)

game_state
|> put_in([:crates, crate_id, :aditional_info, :status], :DESTROYED)
end
end)
end

defp update_bounties_states(game_state, %{bounties_enabled?: false}) do
game_state
end
Expand Down Expand Up @@ -1459,13 +1435,7 @@ defmodule Arena.GameUpdater do

defp maybe_receive_zone_damage(player, elapse_time, zone_damage_interval, zone_damage)
when elapse_time > zone_damage_interval do
updated_player = Player.take_damage(player, zone_damage)

unless Player.alive?(updated_player) do
send(self(), {:to_killfeed, 9999, player.id})
end

updated_player
Entities.take_damage(player, zone_damage, 9999)
end

defp maybe_receive_zone_damage(player, _elaptime, _zone_damage_interval, _zone_damage),
Expand Down Expand Up @@ -1513,7 +1483,7 @@ defmodule Arena.GameUpdater do
) do
attacking_player = Map.get(players_acc, projectile.aditional_info.owner_id)
real_damage = Player.calculate_real_damage(attacking_player, projectile.aditional_info.damage)
player = Player.take_damage(player, real_damage)
player = Entities.take_damage(player, real_damage, projectile.aditional_info.owner_id)

send(
self(),
Expand All @@ -1527,10 +1497,6 @@ defmodule Arena.GameUpdater do
projectile
end

unless Player.alive?(player) do
send(self(), {:to_killfeed, projectile.aditional_info.owner_id, player.id})
end

{
Map.put(projectiles_acc, projectile.id, projectile),
Map.put(players_acc, player.id, player),
Expand All @@ -1548,7 +1514,7 @@ defmodule Arena.GameUpdater do
) do
attacking_player = Map.get(players_acc, projectile.aditional_info.owner_id)
real_damage = Player.calculate_real_damage(attacking_player, projectile.aditional_info.damage)
crate = Crate.take_damage(crate, real_damage)
crate = Entities.take_damage(crate, real_damage, attacking_player.id)

projectile =
if projectile.aditional_info.remove_on_collision do
Expand Down Expand Up @@ -1641,6 +1607,18 @@ defmodule Arena.GameUpdater do
end)
end

defp grant_power_up_to_killer(game_state, _game_config, nil = _killer, _victim), do: game_state

defp grant_power_up_to_killer(game_state, game_config, killer, victim) do
amount_of_power_ups =
get_amount_of_power_ups(victim, game_config.game.power_ups_per_kill)

updated_killer = Player.power_up_boost(killer, amount_of_power_ups, game_config)

players = Map.get(game_state, :players) |> Map.put(killer.id, updated_killer)
Map.put(game_state, :players, players)
end

defp get_amount_of_power_ups(%{aditional_info: %{power_ups: power_ups}}, power_ups_per_kill) do
Enum.sort_by(power_ups_per_kill, fn %{minimum_amount_of_power_ups: minimum} -> minimum end, :desc)
|> Enum.find(fn %{minimum_amount_of_power_ups: minimum} ->
Expand All @@ -1652,47 +1630,12 @@ defmodule Arena.GameUpdater do
end
end

defp find_collided_power_up(collides_with, power_ups) do
Enum.find(collides_with, fn collided_entity_id ->
Map.has_key?(power_ups, collided_entity_id)
end)
end

defp find_collided_item(collides_with, items) do
Enum.find_value(collides_with, fn collided_entity_id ->
Map.get(items, collided_entity_id)
end)
end

defp process_power_up(player, power_up, players_acc, power_ups_acc) do
if power_up.aditional_info.status == :AVAILABLE && Player.alive?(player) do
updated_power_up = put_in(power_up, [:aditional_info, :status], :TAKEN)

updated_player =
update_in(player, [:aditional_info, :power_ups], fn amount -> amount + 1 end)
|> update_in([:aditional_info], fn additional_info ->
Map.update(additional_info, :health, additional_info.health, fn current_health ->
Utils.increase_value_by_base_percentage(
current_health,
additional_info.base_health,
power_up.aditional_info.power_up_health_modifier
)
end)
|> Map.update(:max_health, additional_info.max_health, fn max_health ->
Utils.increase_value_by_base_percentage(
max_health,
additional_info.base_health,
power_up.aditional_info.power_up_health_modifier
)
end)
end)

{Map.put(players_acc, player.id, updated_player), Map.put(power_ups_acc, power_up.id, updated_power_up)}
else
{players_acc, power_ups_acc}
end
end

defp handle_pools(%{pools: pools, crates: crates, players: players} = game_state, game_config) do
entities = Map.merge(crates, players)

Expand Down

0 comments on commit dcab0f6

Please sign in to comment.