From e8e5ab41c7393a2477e1e16f09c6db63475dbd59 Mon Sep 17 00:00:00 2001 From: Daniel Arroyo Date: Sun, 23 Aug 2026 12:34:47 -0400 Subject: [PATCH] feat(personajes): capacity by weight (L2 dwarf model) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reemplaza el modelo slots × stack por el modelo real de L2 para vendedores enanos: capacidad basada en el límite de peso del personaje con multiplicador por el pasivo 'Weight Limit Increase' del enano. Modelo: - maxWeight = baseWeight × (1 + skillLevel) - skillLevel 0 → ×1 (sin pasivo) - skillLevel 1 → ×2 (+100%, lv1) - skillLevel 2 → ×3 (+200%, lv2) - skillLevel 3 → ×4 (+300%, lv3) - capacity(tipo, grado) = floor(maxWeight / shotWeight[tipo][grado]) Backend (V5 migration): - DROP COLUMN inventory_slots, stack_limit - ADD COLUMN base_weight DEFAULT 115230 - ADD COLUMN skill_level DEFAULT 0 - Character/CharacterDto/CharacterIn con los nuevos campos - PUT /api/characters/{id}/weight endpoint (reemplaza /inventory) - Validación: baseWeight > 0, skillLevel 0..3 Frontend: - Form: Base weight (default 115230) + Skill level select (0..3) - Tabla: Peso base + Skill enano (select editable) + Peso máx calculado - Sección '💼 Capacidad de inventario de shots (por peso)': - Por personaje: header con nombre, lvl, base, multiplicador, badge skill - Tabla 3×5 con las 15 capacidades (3 tipos × 5 grados) - Sub-info: Peso máx = base × (1+skill) - Total 15 tipos por personaje - Shot weights constants en Alpine (SHOT_WEIGHTS, TIPOS, GRADOS) - Métodos: maxWeightOf(c), capacityOf(c, tipo, grado), totalCapacityOf(c), updateWeight(c), tipoShort(tipo) Pesos de shots (hardcoded): - SS: D=3, C=3, B=2, A=2, S=2 - SPS: D=5, C=3, B=3, A=2, S=2 - BSS: D=5, C=3, B=3, A=2, S=2 Verificado en navegador: - Migration V5 aplicada limpiamente (5 migrations total) - Personajes existentes preservados con default baseWeight=115230 skillLevel=0 - Math correcta: skillLevel=3, baseWeight=115230 → maxWeight=460.920 → SS D=153.640, BSS S=230.460 - Edición inline de skill en la tabla persiste via PUT /weight - Visualización 3×5 por personaje con badge dinámico --- .../com/l2/shots/characters/Character.java | 8 +- .../com/l2/shots/characters/CharacterDto.java | 4 +- .../com/l2/shots/characters/CharacterIn.java | 13 +- .../shots/characters/CharacterResource.java | 22 +-- .../l2/shots/characters/CharacterService.java | 14 +- .../META-INF/resources/static/js/api.js | 6 +- .../db/migration/V5__character_weight.sql | 4 + .../templates/PageResource/personajes.html | 182 +++++++++++------- 8 files changed, 149 insertions(+), 104 deletions(-) create mode 100644 src/main/resources/db/migration/V5__character_weight.sql diff --git a/src/main/java/com/l2/shots/characters/Character.java b/src/main/java/com/l2/shots/characters/Character.java index 57434a0..58720d0 100644 --- a/src/main/java/com/l2/shots/characters/Character.java +++ b/src/main/java/com/l2/shots/characters/Character.java @@ -31,11 +31,11 @@ public class Character extends PanacheEntityBase { @Column(name = "level", nullable = false) public int level; - @Column(name = "inventory_slots", nullable = false) - public int inventorySlots = 80; + @Column(name = "base_weight", nullable = false) + public int baseWeight = 115230; - @Column(name = "stack_limit", nullable = false) - public int stackLimit = 9999; + @Column(name = "skill_level", nullable = false) + public int skillLevel = 0; @Column(name = "created_at", nullable = false) public Instant createdAt; diff --git a/src/main/java/com/l2/shots/characters/CharacterDto.java b/src/main/java/com/l2/shots/characters/CharacterDto.java index 2588992..7f847f8 100644 --- a/src/main/java/com/l2/shots/characters/CharacterDto.java +++ b/src/main/java/com/l2/shots/characters/CharacterDto.java @@ -11,7 +11,7 @@ public record CharacterDto( String name, String characterClass, int level, - int inventorySlots, - int stackLimit, + int baseWeight, + int skillLevel, Instant createdAt) { } \ No newline at end of file diff --git a/src/main/java/com/l2/shots/characters/CharacterIn.java b/src/main/java/com/l2/shots/characters/CharacterIn.java index 1936b61..626836c 100644 --- a/src/main/java/com/l2/shots/characters/CharacterIn.java +++ b/src/main/java/com/l2/shots/characters/CharacterIn.java @@ -7,8 +7,8 @@ public record CharacterIn( String name, String characterClass, int level, - Integer inventorySlots, - Integer stackLimit) { + Integer baseWeight, + Integer skillLevel) { public CharacterIn { if (name == null || name.isBlank()) { @@ -28,11 +28,12 @@ public record CharacterIn( } } - public int effectiveInventorySlots() { - return inventorySlots != null && inventorySlots > 0 ? inventorySlots : 80; + public int effectiveBaseWeight() { + return baseWeight != null && baseWeight > 0 ? baseWeight : 115230; } - public int effectiveStackLimit() { - return stackLimit != null && stackLimit > 0 ? stackLimit : 9999; + public int effectiveSkillLevel() { + if (skillLevel == null || skillLevel < 0 || skillLevel > 3) return 0; + return skillLevel; } } \ No newline at end of file diff --git a/src/main/java/com/l2/shots/characters/CharacterResource.java b/src/main/java/com/l2/shots/characters/CharacterResource.java index 0e21c5d..2f0a523 100644 --- a/src/main/java/com/l2/shots/characters/CharacterResource.java +++ b/src/main/java/com/l2/shots/characters/CharacterResource.java @@ -53,8 +53,8 @@ public class CharacterResource { input.name(), input.characterClass(), input.level(), - input.inventorySlots(), - input.stackLimit()); + input.baseWeight(), + input.skillLevel()); CharacterDto created = characterService.create(userId.get(), validated); return Response.status(201).entity(created).build(); } catch (IllegalArgumentException e) { @@ -87,10 +87,10 @@ public class CharacterResource { } @PUT - @Path("/{id}/inventory") - public Response updateInventory(@Context HttpHeaders headers, - @PathParam("id") String idStr, - UpdateInventoryRequest body) { + @Path("/{id}/weight") + public Response updateWeight(@Context HttpHeaders headers, + @PathParam("id") String idStr, + UpdateWeightRequest body) { Optional userId = extractUserId(headers); if (userId.isEmpty()) return Response.status(401).build(); @@ -101,11 +101,11 @@ public class CharacterResource { return Response.status(400).entity("{\"error\":\"id inválido\"}").build(); } - if (body == null || body.inventorySlots() <= 0 || body.stackLimit() <= 0) { - return Response.status(400).entity("{\"error\":\"slots y stackLimit deben ser mayores a 0\"}").build(); + if (body == null || body.baseWeight() <= 0 || body.skillLevel() < 0 || body.skillLevel() > 3) { + return Response.status(400).entity("{\"error\":\"baseWeight debe ser > 0 y skillLevel entre 0 y 3\"}").build(); } - Optional updated = characterService.updateInventory(userId.get(), id, body.inventorySlots(), body.stackLimit()); + Optional updated = characterService.updateWeight(userId.get(), id, body.baseWeight(), body.skillLevel()); return updated.map(c -> Response.ok(c).build()) .orElse(Response.status(404).build()); } @@ -143,5 +143,5 @@ public class CharacterResource { public record UpdateLevelRequest(int level) {} - public record UpdateInventoryRequest(int inventorySlots, int stackLimit) {} -} + public record UpdateWeightRequest(int baseWeight, int skillLevel) {} +} \ No newline at end of file diff --git a/src/main/java/com/l2/shots/characters/CharacterService.java b/src/main/java/com/l2/shots/characters/CharacterService.java index c6d47c1..556c06c 100644 --- a/src/main/java/com/l2/shots/characters/CharacterService.java +++ b/src/main/java/com/l2/shots/characters/CharacterService.java @@ -33,8 +33,8 @@ public class CharacterService { entity.name = input.name(); entity.characterClass = input.characterClass(); entity.level = input.level(); - entity.inventorySlots = input.effectiveInventorySlots(); - entity.stackLimit = input.effectiveStackLimit(); + entity.baseWeight = input.effectiveBaseWeight(); + entity.skillLevel = input.effectiveSkillLevel(); entity.createdAt = Instant.now(); entity.persist(); return toDto(entity); @@ -49,11 +49,11 @@ public class CharacterService { } @Transactional - public Optional updateInventory(UUID userId, UUID id, int inventorySlots, int stackLimit) { + public Optional updateWeight(UUID userId, UUID id, int baseWeight, int skillLevel) { Character entity = Character.find("id = ?1 AND userId = ?2", id, userId).firstResult(); if (entity == null) return Optional.empty(); - if (inventorySlots > 0) entity.inventorySlots = inventorySlots; - if (stackLimit > 0) entity.stackLimit = stackLimit; + if (baseWeight > 0) entity.baseWeight = baseWeight; + if (skillLevel >= 0 && skillLevel <= 3) entity.skillLevel = skillLevel; return Optional.of(toDto(entity)); } @@ -78,8 +78,8 @@ public class CharacterService { entity.name, entity.characterClass, entity.level, - entity.inventorySlots, - entity.stackLimit, + entity.baseWeight, + entity.skillLevel, entity.createdAt); } } \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/static/js/api.js b/src/main/resources/META-INF/resources/static/js/api.js index ac0bda2..a899593 100644 --- a/src/main/resources/META-INF/resources/static/js/api.js +++ b/src/main/resources/META-INF/resources/static/js/api.js @@ -143,10 +143,10 @@ window.api = { }); }, - updateCharacterInventory(id, inventorySlots, stackLimit) { - return this.request(`/api/characters/${id}/inventory`, { + updateCharacterWeight(id, baseWeight, skillLevel) { + return this.request(`/api/characters/${id}/weight`, { method: 'PUT', - body: JSON.stringify({ inventorySlots, stackLimit }), + body: JSON.stringify({ baseWeight, skillLevel }), }); }, diff --git a/src/main/resources/db/migration/V5__character_weight.sql b/src/main/resources/db/migration/V5__character_weight.sql new file mode 100644 index 0000000..d1d63fb --- /dev/null +++ b/src/main/resources/db/migration/V5__character_weight.sql @@ -0,0 +1,4 @@ +ALTER TABLE characters DROP COLUMN inventory_slots; +ALTER TABLE characters DROP COLUMN stack_limit; +ALTER TABLE characters ADD COLUMN base_weight INT NOT NULL DEFAULT 115230; +ALTER TABLE characters ADD COLUMN skill_level INT NOT NULL DEFAULT 0; \ No newline at end of file diff --git a/src/main/resources/templates/PageResource/personajes.html b/src/main/resources/templates/PageResource/personajes.html index 2413374..66e7cc7 100644 --- a/src/main/resources/templates/PageResource/personajes.html +++ b/src/main/resources/templates/PageResource/personajes.html @@ -20,7 +20,7 @@

Agregar personaje

-
+
@@ -94,9 +97,9 @@ Nombre Clase Nivel - Slots - Stack - Capacidad (shots) + Peso base + Skill enano + Peso máx Acciones @@ -118,22 +121,25 @@ + min="1" + step="1" + x-model.number="c.baseWeight" + @keydown.enter="updateWeight(c)" + @blur="updateWeight(c)" + style="width: 100px; padding: 0.25rem 0.5rem; font-size: 0.875rem; text-align: right;"/> - + + x-text="fmtAdena(maxWeightOf(c))">