From 56796655a694c4d3c4ee6c9e50730f42ce0965d7 Mon Sep 17 00:00:00 2001 From: lotuuu Date: Fri, 7 Jun 2024 16:00:29 -0300 Subject: [PATCH 1/4] Create equip_item_and_unequip_previous --- apps/champions/lib/champions/items.ex | 4 +- apps/game_backend/lib/game_backend/items.ex | 50 +++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/apps/champions/lib/champions/items.ex b/apps/champions/lib/champions/items.ex index 7f74833f7..75f570b58 100644 --- a/apps/champions/lib/champions/items.ex +++ b/apps/champions/lib/champions/items.ex @@ -19,10 +19,10 @@ defmodule Champions.Items do def get_items(user_id), do: Items.get_items(user_id) @doc """ - Equip an item to a unit. + Equip an item to a unit. Unequips the previously eqyupped item of the same type if there is any. """ def equip_item(user_id, item_id, unit_id) do - Items.equip_item(user_id, item_id, unit_id) + Items.equip_item_and_unequip_previous(user_id, item_id, unit_id) get_item(item_id) end diff --git a/apps/game_backend/lib/game_backend/items.ex b/apps/game_backend/lib/game_backend/items.ex index 275aac461..4e1e800d1 100644 --- a/apps/game_backend/lib/game_backend/items.ex +++ b/apps/game_backend/lib/game_backend/items.ex @@ -44,6 +44,33 @@ defmodule GameBackend.Items do end end + @doc """ + Equips an item to a unit and unequips the previous item of the same type + + Returns an `{:ok, %Item{}}` tuple with the item's updated state. + """ + def equip_item_and_unequip_previous(user_id, item_id, unit_id) do + with {:ok, item} <- get_item(item_id), + true <- item.user_id == user_id, + true <- Units.unit_belongs_to_user(unit_id, user_id) do + Multi.new() + |> Multi.run(:unequip_previous, fn _, _ -> + case get_unit_item_by_type(unit_id, item.template.type) do + {:ok, previous_item} -> + unequip_item(user_id, previous_item.id) + + {:error, :not_found} -> + {:ok, nil} + end + end) + |> Multi.run(:equip_new, fn _, _ -> equip_item(user_id, item.id, unit_id) end) + |> Repo.transaction() + else + {:error, :not_found} -> {:error, :item_not_found} + false -> {:error, :item_not_owned} + end + end + @doc """ Unequips an item from its unit. Returns an `{:ok, %Item{}}` tuple with the item's updated state. @@ -176,6 +203,29 @@ defmodule GameBackend.Items do end end + @doc """ + Get a unit's item by type. Assumes only one item of each type per unit. + + ## Examples + + iex> get_unit_item_by_type(unit_id, :boots) + {:ok, %Item{}} + + iex> get_unit_item_by_type(unit_id, :socks) + {:error, :not_found} + """ + def get_unit_item_by_type(unit_id, type) do + case Repo.one( + from(item in Item, + join: t in assoc(item, :template), + where: item.unit_id == ^unit_id and t.type == ^type + ) + ) do + nil -> {:error, :not_found} + item -> {:ok, item} + end + end + @doc """ Get all items for a user. From 86ff28a92783fe8a1e1cc85722b53f3e30648f41 Mon Sep 17 00:00:00 2001 From: lotuuu Date: Fri, 7 Jun 2024 16:00:40 -0300 Subject: [PATCH 2/4] Fix missing clause in fuse --- apps/champions/lib/champions/items.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/champions/lib/champions/items.ex b/apps/champions/lib/champions/items.ex index 75f570b58..cdffb868b 100644 --- a/apps/champions/lib/champions/items.ex +++ b/apps/champions/lib/champions/items.ex @@ -76,6 +76,9 @@ defmodule Champions.Items do {:consumed_items_valid, {false, _}} -> {:error, :consumed_items_invalid} + + {:can_afford, false} -> + {:error, :cant_afford} end end From fa30f7fd14f2c2970977280977b3cf3e03397a9b Mon Sep 17 00:00:00 2001 From: lotuuu Date: Fri, 7 Jun 2024 16:12:01 -0300 Subject: [PATCH 3/4] Test automatic unequipping --- apps/gateway/test/champions_test.exs | 43 +++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/apps/gateway/test/champions_test.exs b/apps/gateway/test/champions_test.exs index be95561ce..7cc5170e9 100644 --- a/apps/gateway/test/champions_test.exs +++ b/apps/gateway/test/champions_test.exs @@ -489,7 +489,7 @@ defmodule Gateway.Test.Champions do defense_multiplier = 1.2 health_adder = 100 - {:ok, epic_item} = + {:ok, item_template} = Items.insert_item_template(%{ game_id: Utils.get_game_id(:champions_of_mirra), name: "Epic Upgrader of All Stats", @@ -515,7 +515,7 @@ defmodule Gateway.Test.Champions do ] }) - {:ok, item} = Items.insert_item(%{user_id: user.id, template_id: epic_item.id, level: 1}) + {:ok, item} = Items.insert_item(%{user_id: user.id, template_id: item_template.id, level: 1}) # EquipItem attack_before_equip = Units.get_attack(unit) @@ -524,10 +524,7 @@ defmodule Gateway.Test.Champions do :ok = SocketTester.equip_item(socket_tester, user.id, item.id, unit.id) fetch_last_message(socket_tester) - - assert_receive %WebSocketResponse{ - response_type: {:item, %Item{} = equipped_item} - } + assert_receive %WebSocketResponse{response_type: {:item, %Item{} = equipped_item}} # The item is now equipped to the unit assert equipped_item.user_id == user.id @@ -559,10 +556,7 @@ defmodule Gateway.Test.Champions do another_unit = user.units |> Enum.at(1) :ok = SocketTester.equip_item(socket_tester, user.id, item.id, another_unit.id) fetch_last_message(socket_tester) - - assert_receive %WebSocketResponse{ - response_type: {:item, %Item{} = equipped_item} - } + assert_receive %WebSocketResponse{response_type: {:item, %Item{} = equipped_item}} # The item is now equipped to the second unit assert equipped_item.user_id == user.id @@ -572,10 +566,7 @@ defmodule Gateway.Test.Champions do # UnequipItem :ok = SocketTester.unequip_item(socket_tester, user.id, item.id) fetch_last_message(socket_tester) - - assert_receive %WebSocketResponse{ - response_type: {:item, %Item{} = unequipped_item} - } + assert_receive %WebSocketResponse{response_type: {:item, %Item{} = unequipped_item}} # The item is now unequipped assert unequipped_item.user_id == user.id @@ -593,6 +584,30 @@ defmodule Gateway.Test.Champions do assert Units.get_attack(unit_without_item) == attack_before_equip assert Units.get_defense(unit_without_item) == defense_before_equip assert Units.get_health(unit_without_item) == health_before_equip + + # Equipping an item of the same type unequips the previous one. + + {:ok, same_type_item} = Items.insert_item(%{user_id: user.id, template_id: item_template.id, level: 1}) + + :ok = SocketTester.equip_item(socket_tester, user.id, item.id, unit.id) + fetch_last_message(socket_tester) + assert_receive %WebSocketResponse{response_type: {:item, %Item{} = equipped_item}} + + {:ok, equipped_item} = Items.get_item(equipped_item.id) + assert equipped_item.id == item.id + assert equipped_item.unit_id == unit.id + + :ok = SocketTester.equip_item(socket_tester, user.id, same_type_item.id, unit.id) + fetch_last_message(socket_tester) + assert_receive %WebSocketResponse{response_type: {:item, %Item{} = equipped_item}} + + {:ok, equipped_item} = Items.get_item(equipped_item.id) + assert equipped_item.id == same_type_item.id + assert equipped_item.unit_id == unit.id + + # Unequipped item has no unit_id + {:ok, equipped_item} = Items.get_item(item.id) + assert is_nil(equipped_item.unit_id) end test "tier up item", %{socket_tester: socket_tester} do From c319f5268590bd332c34ebcea89d33423db7861b Mon Sep 17 00:00:00 2001 From: Lotu Date: Wed, 12 Jun 2024 16:00:17 -0300 Subject: [PATCH 4/4] Update apps/champions/lib/champions/items.ex Co-authored-by: Agustin Escobar <106101218+agustinesco@users.noreply.github.com> --- apps/champions/lib/champions/items.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/champions/lib/champions/items.ex b/apps/champions/lib/champions/items.ex index cdffb868b..2a9e41c0e 100644 --- a/apps/champions/lib/champions/items.ex +++ b/apps/champions/lib/champions/items.ex @@ -19,7 +19,7 @@ defmodule Champions.Items do def get_items(user_id), do: Items.get_items(user_id) @doc """ - Equip an item to a unit. Unequips the previously eqyupped item of the same type if there is any. + Equip an item to a unit. Unequips the previously equipped item of the same type if there is any. """ def equip_item(user_id, item_id, unit_id) do Items.equip_item_and_unequip_previous(user_id, item_id, unit_id)