Skip to content

Commit

Permalink
Add new silence item (#1047)
Browse files Browse the repository at this point in the history
* Add silence_effect from item behavior

* Add silence_item in seeds

* Add item.mechanic_radius field to send it to the client

* Upgrade arena version

* Add new status and owner_id for Item proto

* Update items status according to user actions

* Restore wrongly deleted code in merge

* Remove pickup time if item got picked up

* Move all items functions to its section

* Upgrade arena version
  • Loading branch information
Nico-Sanchez authored Jan 23, 2025
1 parent 06e19d5 commit ec2dec6
Show file tree
Hide file tree
Showing 18 changed files with 461 additions and 51 deletions.
21 changes: 19 additions & 2 deletions apps/arena/lib/arena/entities.ex
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ defmodule Arena.Entities do
bounty_completed: false,
current_basic_animation: 0,
item_effects_expires_at: now,
position: nil
position: nil,
blocked_actions: false
},
collides_with: []
}
Expand Down Expand Up @@ -244,6 +245,9 @@ defmodule Arena.Entities do
is_moving: false,
aditional_info: %{
name: config.name,
mechanic_radius: get_range_from_mechanic(config.mechanics),
status: :ITEM_STATUS_UNDEFINED,
owner_id: nil,
effect: config.effect,
mechanics: config.mechanics,
pick_up_time_elapsed: %{},
Expand All @@ -254,6 +258,9 @@ defmodule Arena.Entities do
}
end

defp get_range_from_mechanic([]), do: nil
defp get_range_from_mechanic([mechanic | _]), do: mechanic.range

def new_obstacle(id, %{position: position, radius: radius, shape: shape, vertices: vertices} = params) do
%{
id: id,
Expand Down Expand Up @@ -441,7 +448,8 @@ defmodule Arena.Entities do
mana: get_in(entity, [:aditional_info, :mana]),
current_basic_animation: get_in(entity, [:aditional_info, :current_basic_animation]),
match_position: get_in(entity, [:aditional_info, :match_position]),
team: get_in(entity, [:aditional_info, :team])
team: get_in(entity, [:aditional_info, :team]),
blocked_actions: get_in(entity, [:aditional_info, :blocked_actions])
}}
end

Expand Down Expand Up @@ -487,6 +495,9 @@ defmodule Arena.Entities do
{:item,
%Arena.Serialization.Item{
name: get_in(entity, [:aditional_info, :name]),
mechanic_radius: get_in(entity, [:aditional_info, :mechanic_radius]),
status: get_in(entity, [:aditional_info, :status]),
owner_id: get_in(entity, [:aditional_info, :owner_id]),
pick_up_time_elapsed: get_in(entity, [:aditional_info, :pick_up_time_elapsed])
}}
end
Expand Down Expand Up @@ -583,6 +594,12 @@ defmodule Arena.Entities do
put_in(entity, [:aditional_info, :cooldowns], %{})
end

def block_actions_for_duration(%{category: :player} = entity, duration) do
Process.send(self(), {:block_actions, entity.id, true}, [])
Process.send_after(self(), {:block_actions, entity.id, false}, duration)
put_in(entity, [:aditional_info, :blocked_actions], true)
end

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

Expand Down
4 changes: 4 additions & 0 deletions apps/arena/lib/arena/game/effect.ex
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ defmodule Arena.Game.Effect do
Entities.refresh_cooldowns(entity)
end

defp do_effect_mechanics(_game_state, entity, effect, %{name: "silence"} = _silence_mechanic) do
Entities.block_actions_for_duration(entity, effect.duration_ms)
end

## Sink for mechanics that don't do anything
defp do_effect_mechanics(_game_state, entity, _effect, _mechanic) do
entity
Expand Down
5 changes: 5 additions & 0 deletions apps/arena/lib/arena/game/item.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Arena.Game.Item do

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

@doc """
Apply all item mechanics to an entity
Expand Down Expand Up @@ -38,6 +39,10 @@ defmodule Arena.Game.Item do
put_in(game_state, [:players, player.id], player)
end

def do_mechanic(game_state, entity, %{type: "circle_hit"} = item_params) do
Skill.do_mechanic(game_state, entity, item_params, %{})
end

def do_mechanic(game_state, _entity, _mechanic) do
game_state
end
Expand Down
2 changes: 2 additions & 0 deletions apps/arena/lib/arena/game/player.ex
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@ defmodule Arena.Game.Player do
|> maybe_update_player_item_effects_expires_at(player, item.effect)

Item.do_mechanics(game_state, player, item.mechanics)
|> put_in([:items, item.id, :aditional_info, :status], :ITEM_USED)
|> put_in([:items, item.id, :aditional_info, :owner_id], player.id)
|> put_in(
[:players, player.id, :aditional_info, :inventory],
Map.delete(player.aditional_info.inventory, item_position)
Expand Down
28 changes: 28 additions & 0 deletions apps/arena/lib/arena/game/skill.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ defmodule Arena.Game.Skill do
|> maybe_move_player(entity, circle_hit[:move_by])
end

defp execute_mechanic(
game_state,
entity,
%{type: "circle_hit"} = circle_hit,
_skill_params
) do
circle_center_position = get_position_with_offset(entity.position, nil, circle_hit.offset)
circular_damage_area = Entities.make_circular_area(entity.id, circle_center_position, circle_hit.range)

entity_player_owner = get_entity_player_owner(game_state, entity)

apply_damage_and_effects_to_entities(
game_state,
entity_player_owner,
circular_damage_area,
circle_hit.damage,
circle_hit.effect
)
|> maybe_move_player(entity, circle_hit[:move_by])
end

defp execute_mechanic(
game_state,
entity,
Expand Down Expand Up @@ -403,6 +424,13 @@ defmodule Arena.Game.Skill do
Enum.concat([add_side, middle, sub_side])
end

defp get_position_with_offset(position, nil, offset) do
real_position_x = position.x + offset
real_position_y = position.y + offset

%{x: real_position_x, y: real_position_y}
end

defp get_position_with_offset(position, direction, offset) do
real_position_x = position.x + offset * direction.x
real_position_y = position.y + offset * direction.y
Expand Down
118 changes: 81 additions & 37 deletions apps/arena/lib/arena/game_updater.ex
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ defmodule Arena.GameUpdater do
|> Effect.apply_effect_mechanic_to_entities()
# Players
|> move_players()
|> update_pickup_time_for_items()
|> reduce_players_cooldowns(delta_time)
|> recover_mana()
|> resolve_projectiles_effects_on_collisions()
Expand Down Expand Up @@ -276,6 +275,8 @@ defmodule Arena.GameUpdater do
|> add_players_to_respawn_queue(state.game_config)
|> respawn_players(state.game_config)
# Items
|> update_pickup_time_for_items()
|> update_items_status()
|> remove_pickup_time_for_items()

{:ok, state_diff} = diff(state.last_broadcasted_game_state, game_state)
Expand Down Expand Up @@ -679,6 +680,7 @@ defmodule Arena.GameUpdater do
end

def handle_info({:block_actions, player_id, value}, state) do
state = put_in(state, [:game_state, :players, player_id, :aditional_info, :blocked_actions], value)
broadcast_player_block_actions(state.game_state.game_id, player_id, value)
{:noreply, state}
end
Expand Down Expand Up @@ -1317,6 +1319,29 @@ defmodule Arena.GameUpdater do
end)
end

# Mark used items as active
# If items were active already, expire them
defp update_items_status(%{items: items} = game_state) do
updated_items =
Enum.reduce(items, items, fn {item_id, item}, acc ->
case item.aditional_info.status do
:ITEM_EXPIRED ->
Map.delete(acc, item_id)

:ITEM_USED ->
Map.put(acc, item_id, put_in(item, [:aditional_info, :status], :ITEM_ACTIVE))

:ITEM_ACTIVE ->
Map.put(acc, item_id, put_in(item, [:aditional_info, :status], :ITEM_EXPIRED))

_ ->
acc
end
end)

%{game_state | items: updated_items}
end

defp apply_zone_damage_to_players(%{zone: %{enabled: false}} = game_state, _zone_params), do: game_state

defp apply_zone_damage_to_players(%{zone: %{enabled: true}} = game_state, zone_params) do
Expand Down Expand Up @@ -1988,60 +2013,79 @@ defmodule Arena.GameUpdater do

defp update_pickup_time_for_items(game_state) do
{players, items} =
Enum.reduce(game_state.players, {game_state.players, game_state.items}, fn {player_id, player},
{players_acc, items_acc} ->
case find_collided_item(player.collides_with, items_acc) do
nil ->
{players_acc, items_acc}
Enum.reduce(
game_state.players,
{game_state.players,
game_state.items |> Map.filter(fn {_item_id, item} -> item.aditional_info.status == :ITEM_STATUS_UNDEFINED end)},
fn {player_id, player}, {players_acc, items_acc} ->
case find_collided_item(player.collides_with, items_acc) do
nil ->
{players_acc, items_acc}

item ->
now = System.monotonic_time(:millisecond)
item ->
now = System.monotonic_time(:millisecond)

cond do
not Map.has_key?(item.aditional_info.pick_up_time_elapsed, player_id) ->
item =
put_in(item, [:aditional_info, :pick_up_time_elapsed, player_id], 0)
|> put_in([:aditional_info, :pick_up_time_initial_timestamp, player_id], now)
cond do
not Map.has_key?(item.aditional_info.pick_up_time_elapsed, player_id) ->
item =
put_in(item, [:aditional_info, :pick_up_time_elapsed, player_id], 0)
|> put_in([:aditional_info, :pick_up_time_initial_timestamp, player_id], now)

{players_acc, Map.put(items_acc, item.id, item)}
{players_acc, Map.put(items_acc, item.id, item)}

item.aditional_info.pick_up_time_elapsed[player_id] < @standing_time ->
elapsed_time = min(now - item.aditional_info.pick_up_time_initial_timestamp[player_id], @standing_time)
item.aditional_info.pick_up_time_elapsed[player_id] < @standing_time ->
elapsed_time =
min(now - item.aditional_info.pick_up_time_initial_timestamp[player_id], @standing_time)

item = put_in(item, [:aditional_info, :pick_up_time_elapsed, player_id], elapsed_time)
item = put_in(item, [:aditional_info, :pick_up_time_elapsed, player_id], elapsed_time)

{players_acc, Map.put(items_acc, item.id, item)}
{players_acc, Map.put(items_acc, item.id, item)}

not Player.inventory_full?(player) ->
player = Player.store_item(player, item.aditional_info)
{Map.put(players_acc, player.id, player), Map.delete(items_acc, item.id)}
not Player.inventory_full?(player) ->
player = Player.store_item(player, item.aditional_info |> Map.put(:id, item.id))

true ->
{players_acc, items_acc}
end
{Map.put(players_acc, player.id, player),
Map.put(items_acc, item.id, put_in(item, [:aditional_info, :status], :ITEM_PICKED_UP))}

true ->
{players_acc, items_acc}
end
end
end
end)
)

game_state
|> Map.put(:players, players)
|> Map.put(:items, items)
|> Map.put(:items, Map.merge(game_state.items, items))
end

defp remove_pickup_time_for_items(game_state) do
items =
Enum.reduce(game_state.items, %{}, fn {item_id, item}, acc ->
players_colliding = Physics.check_collisions(item, game_state.players)

item =
put_in(
item,
[:aditional_info, :pick_up_time_elapsed],
Map.take(item.aditional_info.pick_up_time_elapsed, players_colliding)
)
|> put_in(
[:aditional_info, :pick_up_time_initial_timestamp],
Map.take(item.aditional_info.pick_up_time_initial_timestamp, players_colliding)
)
if item.aditional_info.status != :ITEM_STATUS_UNDEFINED do
put_in(
item,
[:aditional_info, :pick_up_time_elapsed],
%{}
)
|> put_in(
[:aditional_info, :pick_up_time_initial_timestamp],
nil
)
else
players_colliding = Physics.check_collisions(item, game_state.players)

put_in(
item,
[:aditional_info, :pick_up_time_elapsed],
Map.take(item.aditional_info.pick_up_time_elapsed, players_colliding)
)
|> put_in(
[:aditional_info, :pick_up_time_initial_timestamp],
Map.take(item.aditional_info.pick_up_time_initial_timestamp, players_colliding)
)
end

Map.put(acc, item_id, item)
end)
Expand Down
17 changes: 17 additions & 0 deletions apps/arena/lib/arena/serialization/messages.pb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ defmodule Arena.Serialization.GameStatus do
field(:SELECTING_BOUNTY, 3)
end

defmodule Arena.Serialization.ItemStatus do
@moduledoc false

use Protobuf, enum: true, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:ITEM_STATUS_UNDEFINED, 0)
field(:ITEM_PICKED_UP, 1)
field(:ITEM_USED, 2)
field(:ITEM_ACTIVE, 3)
field(:ITEM_EXPIRED, 4)
end

defmodule Arena.Serialization.ProjectileStatus do
@moduledoc false

Expand Down Expand Up @@ -577,6 +589,7 @@ defmodule Arena.Serialization.Player do
field(:team, 19, proto3_optional: true, type: :uint32)
field(:max_health, 20, proto3_optional: true, type: :uint64, json_name: "maxHealth")
field(:inventory, 21, repeated: true, type: Arena.Serialization.Player.InventoryEntry, map: true)
field(:blocked_actions, 22, proto3_optional: true, type: :bool, json_name: "blockedActions")
end

defmodule Arena.Serialization.Effect do
Expand Down Expand Up @@ -611,6 +624,10 @@ defmodule Arena.Serialization.Item do
json_name: "pickUpTimeElapsed",
map: true
)

field(:mechanic_radius, 4, proto3_optional: true, type: :float, json_name: "mechanicRadius")
field(:status, 5, type: Arena.Serialization.ItemStatus, enum: true)
field(:owner_id, 6, proto3_optional: true, type: :uint64, json_name: "ownerId")
end

defmodule Arena.Serialization.Projectile do
Expand Down
2 changes: 1 addition & 1 deletion apps/arena/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Arena.MixProject do
def project do
[
app: :arena,
version: "0.15.1",
version: "0.16.1",
build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
Expand Down
Loading

0 comments on commit ec2dec6

Please sign in to comment.