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-429] Dungeon Settlement Levels #593

Merged
merged 13 commits into from
May 14, 2024
47 changes: 35 additions & 12 deletions apps/champions/lib/champions/users.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ defmodule Champions.Users do
"""
def register(username) do
kaline_tree_level = GameBackend.Users.get_kaline_tree_level(1)
dungeon_settlement_level = GameBackend.Users.get_dungeon_settlement_level(1)

case Users.register_user(%{
username: username,
game_id: Utils.get_game_id(:champions_of_mirra),
level: 1,
experience: 0,
kaline_tree_level_id: kaline_tree_level.id
kaline_tree_level_id: kaline_tree_level.id,
dungeon_settlement_level_id: dungeon_settlement_level.id
}) do
{:ok, user} ->
# For testing purposes, we assign some things to our user.
Expand Down Expand Up @@ -185,13 +187,18 @@ defmodule Champions.Users do
Get a user's available AFK rewards, according to their AFK reward rates and the time since their last claim.
If more than 12 hours have passed since the last claim, the user will have accumulated the maximum amount of rewards.
"""
def get_afk_rewards(user_id) do
def get_afk_rewards(user_id, type) when type in [:kaline, :dungeon] do
case Users.get_user(user_id) do
{:ok, user} ->
user.kaline_tree_level.afk_reward_rates
|> Enum.map(fn reward_rate ->
{level, last_claim} =
case type do
:kaline -> {user.kaline_tree_level, user.last_kaline_afk_reward_claim}
:dungeon -> {user.dungeon_settlement_level, user.last_dungeon_afk_reward_claim}
end

Enum.map(level.afk_reward_rates, fn reward_rate ->
currency = Currencies.get_currency(reward_rate.currency_id)
amount = calculate_afk_rewards(user, reward_rate)
amount = calculate_afk_rewards(last_claim, reward_rate)
%{currency: currency, amount: amount}
end)

Expand All @@ -200,8 +207,7 @@ defmodule Champions.Users do
end
end

defp calculate_afk_rewards(user, afk_reward_rate) do
last_claim = user.last_afk_reward_claim
defp calculate_afk_rewards(last_claim, afk_reward_rate) do
now = DateTime.utc_now()

# Cap the amount of rewards to the maximum amount of rewards that can be accumulated in 12 hours.
Expand All @@ -212,9 +218,12 @@ defmodule Champions.Users do
@doc """
Claim a user's AFK rewards, and reset their last AFK reward claim time.
"""
def claim_afk_rewards(user_id) do
afk_rewards = get_afk_rewards(user_id)
def claim_afk_rewards(user_id, type) when type in [:kaline, :dungeon] do
afk_rewards = get_afk_rewards(user_id, type)
claim_afk_rewards(user_id, afk_rewards, type)
end

defp claim_afk_rewards(user_id, afk_rewards, type) do
Multi.new()
|> Multi.run(:add_currencies, fn _, _ ->
results =
Expand All @@ -229,7 +238,7 @@ defmodule Champions.Users do
end
end)
|> Multi.run(:reset_afk_claim, fn _, _ ->
Users.reset_afk_rewards_claim(user_id)
Users.reset_afk_rewards_claim(user_id, type)
end)
|> Transaction.run()
|> case do
Expand All @@ -243,7 +252,7 @@ defmodule Champions.Users do
"""
def level_up_kaline_tree(user_id) do
with {:user, {:ok, user}} <- {:user, Users.get_user(user_id)},
level_up_costs = calculate_costs_to_level_up_kaline_tree(user),
level_up_costs = get_kaline_tree_level_up_costs(user),
{:can_afford, true} <- {:can_afford, Currencies.can_afford(user_id, level_up_costs)} do
Users.level_up_kaline_tree(user_id, level_up_costs)
else
Expand All @@ -252,9 +261,23 @@ defmodule Champions.Users do
end
end

@doc """
Level up the Dungeon Settlement of a user.
"""
def level_up_dungeon_settlement(user_id) do
with {:user, {:ok, user}} <- {:user, Users.get_user(user_id)},
level_up_costs = user.dungeon_settlement_level.level_up_costs,
{:can_afford, true} <- {:can_afford, Currencies.can_afford(user_id, level_up_costs)} do
Users.level_up_dungeon_settlement(user_id, level_up_costs)
else
{:can_afford, false} -> {:error, :cant_afford}
{:user, {:error, :not_found}} -> {:error, :user_not_found}
end
end

lotuuu marked this conversation as resolved.
Show resolved Hide resolved
# TODO: remove this after finishing CHoM-#360 (https://github.com/lambdaclass/champions_of_mirra/issues/360)
# The costs will be defined in a configuration file.
defp calculate_costs_to_level_up_kaline_tree(user),
defp get_kaline_tree_level_up_costs(user),
do: [
%CurrencyCost{
currency_id: Currencies.get_currency_by_name_and_game!("Fertilizer", Utils.get_game_id(:champions_of_mirra)).id,
Expand Down
2 changes: 1 addition & 1 deletion apps/game_backend/docs/game_backend_classes.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ defmodule GameBackend.Campaigns.Rewards.AfkRewardRate do
use GameBackend.Schema
import Ecto.Changeset

alias GameBackend.Users.KalineTreeLevel
alias GameBackend.Users.Currencies.Currency
alias GameBackend.Users.DungeonSettlementLevel
alias GameBackend.Users.KalineTreeLevel

schema "afk_reward_rates" do
belongs_to(:kaline_tree_level, KalineTreeLevel)
belongs_to(:dungeon_settlement_level, DungeonSettlementLevel)
belongs_to(:currency, Currency)
field(:rate, :float)

Expand All @@ -20,8 +22,8 @@ defmodule GameBackend.Campaigns.Rewards.AfkRewardRate do
@doc false
def changeset(afk_reward_rate, attrs) do
afk_reward_rate
|> cast(attrs, [:kaline_tree_level_id, :currency_id, :rate])
|> cast(attrs, [:kaline_tree_level_id, :dungeon_settlement_level_id, :currency_id, :rate])
|> validate_number(:rate, greater_than_or_equal_to: 0)
|> validate_required([:kaline_tree_level_id, :currency_id, :rate])
|> validate_required([:currency_id, :rate])
end
end
69 changes: 64 additions & 5 deletions apps/game_backend/lib/game_backend/users.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule GameBackend.Users do

import Ecto.Query, warn: false

alias GameBackend.Users.DungeonSettlementLevel
alias GameBackend.Users.KalineTreeLevel
alias Ecto.Multi
alias GameBackend.Repo
Expand Down Expand Up @@ -134,7 +135,6 @@ defmodule GameBackend.Users do

defp create_google_user_by_email(email) do
# TODO delete the following in a future refactor -> https://github.com/lambdaclass/mirra_backend/issues/557
kaline_tree_id = GameBackend.Users.get_kaline_tree_level(1).id
level = 1
experience = 1
amount_of_users = Repo.aggregate(GameBackend.Users.User, :count)
Expand All @@ -145,7 +145,6 @@ defmodule GameBackend.Users do
GoogleUser.changeset(%GoogleUser{}, %{
email: email,
user: %{
kaline_tree_level_id: kaline_tree_id,
game_id: game_id,
username: username,
level: level,
Expand All @@ -156,7 +155,7 @@ defmodule GameBackend.Users do
end

@doc """
Gets a Kaline Tree level by its number.
Gets a KalineTreelevel by its number.

Returns {:error, :not_found} if no level is found.

Expand All @@ -172,6 +171,23 @@ defmodule GameBackend.Users do
Repo.get_by(KalineTreeLevel, level: level_number)
end

@doc """
Gets a DungeonSettlementLevel by its number.

Returns {:error, :not_found} if no level is found.

## Examples

iex> get_dungeon_settlement_level(1)
%DungeonSettlementLevel{}

iex> get_dungeon_settlement_level(-1)
nil
"""
def get_dungeon_settlement_level(level_number) do
Repo.get_by(DungeonSettlementLevel, level: level_number)
end

@doc """
Checks whether a user exists with the given id.

Expand All @@ -194,11 +210,22 @@ defmodule GameBackend.Users do
|> User.kaline_tree_level_changeset(params)
|> Repo.update()

def reset_afk_rewards_claim(user_id) do
@doc """
Resets the AFK rewards claim time of a user, setting it to the current time.
"""
def reset_afk_rewards_claim(user_id, :kaline) do
{:ok, user} = get_user(user_id)

user
|> User.changeset(%{last_afk_reward_claim: DateTime.utc_now()})
|> User.changeset(%{last_kaline_afk_reward_claim: DateTime.utc_now()})
|> Repo.update()
end

def reset_afk_rewards_claim(user_id, :dungeon) do
{:ok, user} = get_user(user_id)

user
|> User.changeset(%{last_dungeon_afk_reward_claim: DateTime.utc_now()})
|> Repo.update()
end

Expand Down Expand Up @@ -233,11 +260,43 @@ defmodule GameBackend.Users do
end
end

@doc """
Level up the Dungeon Settlement of a user.

Returns the updated user if the operation was successful.
"""
def level_up_dungeon_settlement(user_id, level_up_costs) do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the same as level_up_kaline_tree? just changing the increment_settlement_levelright?

{:ok, _result} =
Multi.new()
|> Multi.run(:user, fn _, _ -> increment_settlement_level(user_id) end)
|> Multi.run(:user_currency, fn _, _ ->
Currencies.substract_currencies(user_id, level_up_costs)
end)
|> Repo.transaction()

get_user(user_id)
end

defp increment_settlement_level(user_id) do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

case get_user(user_id) do
{:ok, user} ->
next_level = get_dungeon_settlement_level(user.dungeon_settlement_level.level + 1)

user
|> User.changeset(%{dungeon_settlement_level_id: next_level.id})
|> Repo.update()

error ->
error
end
end

defp preload(user),
do:
Repo.preload(
user,
kaline_tree_level: [afk_reward_rates: :currency],
dungeon_settlement_level: [afk_reward_rates: :currency, level_up_costs: :currency],
super_campaign_progresses: :level,
items: :template,
units: [:character, :items],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule GameBackend.Users.DungeonSettlementLevel do
@moduledoc """
DungeonSettlementLevels indicate the level of a dungeon settlement.
Dungeon Settlements are the overarching progression mechanic of the Dungeon mode.
They define the amount of supplies a player can hold, and limit the max level of dungeons that can be accessed.
"""
use GameBackend.Schema

import Ecto.Changeset

alias GameBackend.Campaigns.Rewards.AfkRewardRate
alias GameBackend.Users.Currencies.CurrencyCost

schema "dungeon_settlement_levels" do
field(:level, :integer)

# The max dungeon level that can be accessed at this level
field(:max_dungeon, :integer)

# The max factional dungeon level that can be accessed at this level
field(:max_factional, :integer)

# The amount of Supplies currency that a player can hold at a time // TODO: [#CHoM-439]
field(:supply_limit, :integer)

has_many(:afk_reward_rates, AfkRewardRate)

embeds_many(:level_up_costs, CurrencyCost)

timestamps()
end

@doc false
def changeset(dungeon_settlement_level, attrs) do
dungeon_settlement_level
|> cast(attrs, [:level, :max_dungeon, :max_factional, :supply_limit])
|> cast_assoc(:afk_reward_rates)
|> cast_embed(:level_up_costs)
|> validate_required([:level, :max_dungeon, :max_factional, :supply_limit])
end
end
11 changes: 8 additions & 3 deletions apps/game_backend/lib/game_backend/users/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ defmodule GameBackend.Users.User do
alias GameBackend.Items.Item
alias GameBackend.Units.Unit
alias GameBackend.Users.Currencies.UserCurrency
alias GameBackend.Users.DungeonSettlementLevel
alias GameBackend.Users.KalineTreeLevel
alias GameBackend.Users.GoogleUser

Expand All @@ -17,9 +18,11 @@ defmodule GameBackend.Users.User do
field(:username, :string)
field(:level, :integer)
field(:experience, :integer)
field(:last_afk_reward_claim, :utc_datetime)
field(:last_kaline_afk_reward_claim, :utc_datetime)
field(:last_dungeon_afk_reward_claim, :utc_datetime)
field(:profile_picture, :string)

belongs_to(:dungeon_settlement_level, DungeonSettlementLevel)
belongs_to(:kaline_tree_level, KalineTreeLevel)
belongs_to(:google_user, GoogleUser)

Expand All @@ -37,7 +40,9 @@ defmodule GameBackend.Users.User do
|> cast(attrs, [
:game_id,
:username,
:last_afk_reward_claim,
:last_kaline_afk_reward_claim,
:last_dungeon_afk_reward_claim,
:dungeon_settlement_level_id,
:kaline_tree_level_id,
:level,
:experience,
Expand All @@ -46,7 +51,7 @@ defmodule GameBackend.Users.User do
])
|> unique_constraint([:game_id, :username])
|> assoc_constraint(:google_user)
|> validate_required([:game_id, :username, :kaline_tree_level_id])
|> validate_required([:game_id, :username])
end

def experience_changeset(user, attrs), do: user |> cast(attrs, [:experience, :level])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule GameBackend.Repo.Migrations.AddDungeonSettlementLevel do
use Ecto.Migration

def change do
create table(:dungeon_settlement_levels) do
add(:level, :integer)
add(:max_dungeon, :integer)
add(:max_factional, :integer)
add(:supply_limit, :integer)
add(:level_up_costs, :map)
timestamps()
end

alter table(:users) do
add(:dungeon_settlement_level_id, references(:dungeon_settlement_levels, on_delete: :nothing))
add(:last_dungeon_afk_reward_claim, :utc_datetime, default: fragment("now()"))
end

alter table(:afk_reward_rates) do
add(:dungeon_settlement_level_id, references(:dungeon_settlement_levels, on_delete: :delete_all))
end

rename table(:users), :last_afk_reward_claim, to: :last_kaline_afk_reward_claim

create unique_index(:dungeon_settlement_levels, [:level])
end
end
Loading
Loading