Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GH-467] Unequip previously equipped item on equip #689

Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions apps/champions/lib/champions/items.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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.
lotuuu marked this conversation as resolved.
Show resolved Hide resolved
"""
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

Expand Down Expand Up @@ -76,6 +76,9 @@ defmodule Champions.Items do

{:consumed_items_valid, {false, _}} ->
{:error, :consumed_items_invalid}

{:can_afford, false} ->
{:error, :cant_afford}
end
end

Expand Down
50 changes: 50 additions & 0 deletions apps/game_backend/lib/game_backend/items.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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.

Expand Down
43 changes: 29 additions & 14 deletions apps/gateway/test/champions_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading