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

webapi: remove attribute point gain after level cutoff #372

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
5 changes: 3 additions & 2 deletions data/constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@
"tournamentLevel": 30,
"newUserStartingCharacterLevel": 30,
"experienceForLevelCoefs": [13, 200],
"highLevelCutoff": 30,
"defaultStrength": 3,
"defaultAgility": 3,
"defaultHealthPoints": 60, /* A 33 STR, 3 AGI character with 11 IF would reach 137 HP. */
"defaultGeneration": 0,
"defaultAttributePoints": 0,
"defaultAttributePoints": 1,
"attributePointsPerLevel": 1,
"defaultSkillPoints": 2,
"defaultSkillPoints": 3,
"skillPointsPerLevel": 1,
"healthPointsForStrength": 1,
"healthPointsForIronFlesh": 4,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Crpg.Application.Common.Interfaces;
using Crpg.Application.Common.Mediator;
using Crpg.Application.Common.Results;
using Crpg.Application.Common.Services;
using Microsoft.EntityFrameworkCore;

namespace Crpg.Application.Characters.Commands;

public record RespecializeAllCharactersCommand : IMediatorRequest
{
internal class Handler : IMediatorRequestHandler<RespecializeAllCharactersCommand>
{
private readonly ICrpgDbContext _db;
private readonly ICharacterService _characterService;

public Handler(ICrpgDbContext db, ICharacterService characterService)
{
_db = db;
_characterService = characterService;
}

public async Task<Result> Handle(RespecializeAllCharactersCommand req, CancellationToken cancellationToken)
{
var characters = await _db.Characters.ToArrayAsync(cancellationToken: cancellationToken);

foreach (var character in characters)
{
_characterService.ResetCharacterCharacteristics(character, true);
// Trick to avoid UpdatedAt to be updated.
character.UpdatedAt = character.UpdatedAt;
}

await _db.SaveChangesAsync(cancellationToken);
return Result.NoErrors;
}
}
}
1 change: 1 addition & 0 deletions src/Application/Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class Constants
public int TournamentLevel { get; set; }
public int NewUserStartingCharacterLevel { get; set; }
public float[] ExperienceForLevelCoefs { get; set; } = Array.Empty<float>();
public int HighLevelCutoff { get; set; }
public int DefaultStrength { get; set; }
public int DefaultAgility { get; set; }
public int DefaultHealthPoints { get; set; }
Expand Down
25 changes: 23 additions & 2 deletions src/Application/Common/Services/ICharacterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,25 @@ public void SetDefaultValuesForCharacter(Character character)
/// <inheritdoc />
public void ResetCharacterCharacteristics(Character character, bool respecialization = false)
{
int CalculateAttributePoints(int level)
{
int points = 0;
for (int i = 1; i < level; i++)
{
if (i < _constants.HighLevelCutoff)
{
points += _constants.AttributePointsPerLevel;
}
}

return points;
}

character.Characteristics = new CharacterCharacteristics
{
Attributes = new CharacterAttributes
{
Points = _constants.DefaultAttributePoints + (respecialization ? (character.Level - 1) * _constants.AttributePointsPerLevel : 0),
Points = _constants.DefaultAttributePoints + (respecialization ? CalculateAttributePoints(character.Level) : 0),
Strength = _constants.DefaultStrength,
Agility = _constants.DefaultAgility,
},
Expand Down Expand Up @@ -166,7 +180,14 @@ public void GiveExperience(Character character, int experience, bool useExperien
int levelDiff = newLevel - character.Level;
if (levelDiff != 0) // if character leveled up
{
character.Characteristics.Attributes.Points += levelDiff * _constants.AttributePointsPerLevel;
for (int i = character.Level; i < newLevel; i++)
{
if (i < _constants.HighLevelCutoff) // reward attribute points for lower levels
{
character.Characteristics.Attributes.Points += _constants.AttributePointsPerLevel;
}
}

character.Characteristics.Skills.Points += levelDiff * _constants.SkillPointsPerLevel;
character.Characteristics.WeaponProficiencies.Points += WeaponProficiencyPointsForLevel(newLevel) - WeaponProficiencyPointsForLevel(character.Level);
character.Level = newLevel;
Expand Down
1 change: 1 addition & 0 deletions src/Module.Server/Common/CrpgConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal class CrpgConstants
public int TournamentLevel { get; set; }
public int NewUserStartingCharacterLevel { get; set; }
public float[] ExperienceForLevelCoefs { get; set; } = Array.Empty<float>();
public int HighLevelCutoff { get; set; }
public int DefaultStrength { get; set; }
public int DefaultAgility { get; set; }
public int DefaultHealthPoints { get; set; }
Expand Down
13 changes: 13 additions & 0 deletions src/WebApi/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,19 @@ public Task UpdateEveryCharacterCompetitiveRating()
return ResultToActionAsync(Mediator.Send(cmd));
}

/// <summary>
/// Respecializes every character.
/// </summary>>
/// <response code="200">Updated.</response>
/// <response code="400">Bad Request.</response>
[Authorize(AdminPolicy)]
[HttpPut("characters/respecialize")]
public Task RespecializeAllCharacters()
{
RespecializeAllCharactersCommand cmd = new();
return ResultToActionAsync(Mediator.Send(cmd));
}

/// <summary>
/// Updates character characteristics for the current user.
/// </summary>
Expand Down
5 changes: 3 additions & 2 deletions src/WebUI/src/__mocks__/constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@
"maximumLevel": 38,
"tournamentLevel": 30,
"experienceForLevelCoefs": [13, 200],
"highLevelCutoff": 30,
"defaultStrength": 3,
"defaultAgility": 3,
"defaultHealthPoints": 60,
"defaultGeneration": 0,
"defaultAttributePoints": 0,
"defaultAttributePoints": 1,
"attributePointsPerLevel": 1,
"defaultSkillPoints": 2,
"defaultSkillPoints": 3,
"skillPointsPerLevel": 1,
"healthPointsForStrength": 1,
"healthPointsForIronFlesh": 4,
Expand Down
22 changes: 12 additions & 10 deletions src/WebUI/src/services/characters-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,21 +177,23 @@ it.each([
});

it.each([
[0, 0],
[1, 0],
[2, 1],
[10, 9],
[38, 37],
[0, 1],
[1, 1],
[2, 2],
[10, 10],
[30, 30],
[38, 30],
])('attributePointsForLevel - level: %s', (level, expectation) => {
expect(attributePointsForLevel(level)).toEqual(expectation);
});

it.each([
[0, 2],
[1, 2],
[2, 3],
[10, 11],
[38, 39],
[0, 3],
[1, 3],
[2, 4],
[10, 12],
[30, 32],
[38, 40],
])('skillPointsForLevel - level: %s', (level, expectation) => {
expect(skillPointsForLevel(level)).toEqual(expectation);
});
Expand Down
13 changes: 11 additions & 2 deletions src/WebUI/src/services/characters-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
weaponProficiencyPointsForAgility,
weaponProficiencyPointsForWeaponMasterCoefs,
experienceForLevelCoefs,
highLevelCutoff,
defaultStrength,
defaultAgility,
defaultHealthPoints,
Expand Down Expand Up @@ -60,7 +61,6 @@ import { range, groupBy, getIndexToIns } from '@/utils/array';

import { GameMode } from '@/models/game-mode';


export const getCharacters = () => get<Character[]>('/users/self/characters');

export const getCharactersByUserId = (userId: number) =>
Expand Down Expand Up @@ -262,7 +262,16 @@ export const getMaximumExperience = () => getExperienceForLevel(maximumLevel);

export const attributePointsForLevel = (level: number): number => {
if (level <= 0) level = minimumLevel;
return defaultAttributePoints + (level - 1) * attributePointsPerLevel;

let points = defaultAttributePoints;

for (let i = 1; i < level; i++) {
if (i < highLevelCutoff) {
points += attributePointsPerLevel;
}
}

return points;
};

export const skillPointsForLevel = (level: number): number => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class CharacterServiceTest
ExperienceMultiplierByGeneration = 0.03f,
MaxExperienceMultiplierForGeneration = 1.48f,
ExperienceForLevelCoefs = new[] { 2f, 0 },
HighLevelCutoff = 30,
DefaultAttributePoints = 0,
AttributePointsPerLevel = 1,
DefaultSkillPoints = 2,
Expand Down
Loading