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
This commit is contained in:
@@ -31,11 +31,11 @@ public class Character extends PanacheEntityBase {
|
|||||||
@Column(name = "level", nullable = false)
|
@Column(name = "level", nullable = false)
|
||||||
public int level;
|
public int level;
|
||||||
|
|
||||||
@Column(name = "inventory_slots", nullable = false)
|
@Column(name = "base_weight", nullable = false)
|
||||||
public int inventorySlots = 80;
|
public int baseWeight = 115230;
|
||||||
|
|
||||||
@Column(name = "stack_limit", nullable = false)
|
@Column(name = "skill_level", nullable = false)
|
||||||
public int stackLimit = 9999;
|
public int skillLevel = 0;
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false)
|
@Column(name = "created_at", nullable = false)
|
||||||
public Instant createdAt;
|
public Instant createdAt;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public record CharacterDto(
|
|||||||
String name,
|
String name,
|
||||||
String characterClass,
|
String characterClass,
|
||||||
int level,
|
int level,
|
||||||
int inventorySlots,
|
int baseWeight,
|
||||||
int stackLimit,
|
int skillLevel,
|
||||||
Instant createdAt) {
|
Instant createdAt) {
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,8 @@ public record CharacterIn(
|
|||||||
String name,
|
String name,
|
||||||
String characterClass,
|
String characterClass,
|
||||||
int level,
|
int level,
|
||||||
Integer inventorySlots,
|
Integer baseWeight,
|
||||||
Integer stackLimit) {
|
Integer skillLevel) {
|
||||||
|
|
||||||
public CharacterIn {
|
public CharacterIn {
|
||||||
if (name == null || name.isBlank()) {
|
if (name == null || name.isBlank()) {
|
||||||
@@ -28,11 +28,12 @@ public record CharacterIn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int effectiveInventorySlots() {
|
public int effectiveBaseWeight() {
|
||||||
return inventorySlots != null && inventorySlots > 0 ? inventorySlots : 80;
|
return baseWeight != null && baseWeight > 0 ? baseWeight : 115230;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int effectiveStackLimit() {
|
public int effectiveSkillLevel() {
|
||||||
return stackLimit != null && stackLimit > 0 ? stackLimit : 9999;
|
if (skillLevel == null || skillLevel < 0 || skillLevel > 3) return 0;
|
||||||
|
return skillLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,8 +53,8 @@ public class CharacterResource {
|
|||||||
input.name(),
|
input.name(),
|
||||||
input.characterClass(),
|
input.characterClass(),
|
||||||
input.level(),
|
input.level(),
|
||||||
input.inventorySlots(),
|
input.baseWeight(),
|
||||||
input.stackLimit());
|
input.skillLevel());
|
||||||
CharacterDto created = characterService.create(userId.get(), validated);
|
CharacterDto created = characterService.create(userId.get(), validated);
|
||||||
return Response.status(201).entity(created).build();
|
return Response.status(201).entity(created).build();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@@ -87,10 +87,10 @@ public class CharacterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Path("/{id}/inventory")
|
@Path("/{id}/weight")
|
||||||
public Response updateInventory(@Context HttpHeaders headers,
|
public Response updateWeight(@Context HttpHeaders headers,
|
||||||
@PathParam("id") String idStr,
|
@PathParam("id") String idStr,
|
||||||
UpdateInventoryRequest body) {
|
UpdateWeightRequest body) {
|
||||||
Optional<UUID> userId = extractUserId(headers);
|
Optional<UUID> userId = extractUserId(headers);
|
||||||
if (userId.isEmpty()) return Response.status(401).build();
|
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();
|
return Response.status(400).entity("{\"error\":\"id inválido\"}").build();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body == null || body.inventorySlots() <= 0 || body.stackLimit() <= 0) {
|
if (body == null || body.baseWeight() <= 0 || body.skillLevel() < 0 || body.skillLevel() > 3) {
|
||||||
return Response.status(400).entity("{\"error\":\"slots y stackLimit deben ser mayores a 0\"}").build();
|
return Response.status(400).entity("{\"error\":\"baseWeight debe ser > 0 y skillLevel entre 0 y 3\"}").build();
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<CharacterDto> updated = characterService.updateInventory(userId.get(), id, body.inventorySlots(), body.stackLimit());
|
Optional<CharacterDto> updated = characterService.updateWeight(userId.get(), id, body.baseWeight(), body.skillLevel());
|
||||||
return updated.map(c -> Response.ok(c).build())
|
return updated.map(c -> Response.ok(c).build())
|
||||||
.orElse(Response.status(404).build());
|
.orElse(Response.status(404).build());
|
||||||
}
|
}
|
||||||
@@ -143,5 +143,5 @@ public class CharacterResource {
|
|||||||
|
|
||||||
public record UpdateLevelRequest(int level) {}
|
public record UpdateLevelRequest(int level) {}
|
||||||
|
|
||||||
public record UpdateInventoryRequest(int inventorySlots, int stackLimit) {}
|
public record UpdateWeightRequest(int baseWeight, int skillLevel) {}
|
||||||
}
|
}
|
||||||
@@ -33,8 +33,8 @@ public class CharacterService {
|
|||||||
entity.name = input.name();
|
entity.name = input.name();
|
||||||
entity.characterClass = input.characterClass();
|
entity.characterClass = input.characterClass();
|
||||||
entity.level = input.level();
|
entity.level = input.level();
|
||||||
entity.inventorySlots = input.effectiveInventorySlots();
|
entity.baseWeight = input.effectiveBaseWeight();
|
||||||
entity.stackLimit = input.effectiveStackLimit();
|
entity.skillLevel = input.effectiveSkillLevel();
|
||||||
entity.createdAt = Instant.now();
|
entity.createdAt = Instant.now();
|
||||||
entity.persist();
|
entity.persist();
|
||||||
return toDto(entity);
|
return toDto(entity);
|
||||||
@@ -49,11 +49,11 @@ public class CharacterService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Optional<CharacterDto> updateInventory(UUID userId, UUID id, int inventorySlots, int stackLimit) {
|
public Optional<CharacterDto> updateWeight(UUID userId, UUID id, int baseWeight, int skillLevel) {
|
||||||
Character entity = Character.find("id = ?1 AND userId = ?2", id, userId).firstResult();
|
Character entity = Character.find("id = ?1 AND userId = ?2", id, userId).firstResult();
|
||||||
if (entity == null) return Optional.empty();
|
if (entity == null) return Optional.empty();
|
||||||
if (inventorySlots > 0) entity.inventorySlots = inventorySlots;
|
if (baseWeight > 0) entity.baseWeight = baseWeight;
|
||||||
if (stackLimit > 0) entity.stackLimit = stackLimit;
|
if (skillLevel >= 0 && skillLevel <= 3) entity.skillLevel = skillLevel;
|
||||||
return Optional.of(toDto(entity));
|
return Optional.of(toDto(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,8 +78,8 @@ public class CharacterService {
|
|||||||
entity.name,
|
entity.name,
|
||||||
entity.characterClass,
|
entity.characterClass,
|
||||||
entity.level,
|
entity.level,
|
||||||
entity.inventorySlots,
|
entity.baseWeight,
|
||||||
entity.stackLimit,
|
entity.skillLevel,
|
||||||
entity.createdAt);
|
entity.createdAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,10 +143,10 @@ window.api = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
updateCharacterInventory(id, inventorySlots, stackLimit) {
|
updateCharacterWeight(id, baseWeight, skillLevel) {
|
||||||
return this.request(`/api/characters/${id}/inventory`, {
|
return this.request(`/api/characters/${id}/weight`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: JSON.stringify({ inventorySlots, stackLimit }),
|
body: JSON.stringify({ baseWeight, skillLevel }),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<h2>Agregar personaje</h2>
|
<h2>Agregar personaje</h2>
|
||||||
</header>
|
</header>
|
||||||
<form @submit.prevent="submitCharacter()">
|
<form @submit.prevent="submitCharacter()">
|
||||||
<div class="grid" style="grid-template-columns: 1fr 1fr 80px 80px 100px; gap:0.5rem">
|
<div class="grid" style="grid-template-columns: 1fr 1fr 80px 130px 130px; gap:0.5rem">
|
||||||
<label>
|
<label>
|
||||||
<span class="text-sm text-muted">Nombre</span>
|
<span class="text-sm text-muted">Nombre</span>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
@@ -47,18 +47,21 @@
|
|||||||
placeholder="85"/>
|
placeholder="85"/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span class="text-sm text-muted" title="Slots de inventario del personaje">Slots</span>
|
<span class="text-sm text-muted" title="Peso base que puede cargar el personaje">Peso base</span>
|
||||||
<input type="number"
|
<input type="number"
|
||||||
x-model.number="form.inventorySlots"
|
x-model.number="form.baseWeight"
|
||||||
min="0"
|
min="1"
|
||||||
placeholder="80"/>
|
step="1"
|
||||||
|
placeholder="115230"/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span class="text-sm text-muted" title="Stack limit por slot (config del server)">Stack</span>
|
<span class="text-sm text-muted" title="Pasivo del enano: 0=sin skill, 1=+100%, 2=+200%, 3=+300%">Skill enano</span>
|
||||||
<input type="number"
|
<select x-model.number="form.skillLevel">
|
||||||
x-model.number="form.stackLimit"
|
<option value="0">Sin pasivo</option>
|
||||||
min="0"
|
<option value="1">+100% (lv1)</option>
|
||||||
placeholder="9999"/>
|
<option value="2">+200% (lv2)</option>
|
||||||
|
<option value="3">+300% (lv3)</option>
|
||||||
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 0.75rem;">
|
<div style="margin-top: 0.75rem;">
|
||||||
@@ -94,9 +97,9 @@
|
|||||||
<th>Nombre</th>
|
<th>Nombre</th>
|
||||||
<th>Clase</th>
|
<th>Clase</th>
|
||||||
<th>Nivel</th>
|
<th>Nivel</th>
|
||||||
<th class="text-right" title="Slots de inventario">Slots</th>
|
<th class="text-right" title="Peso base del personaje">Peso base</th>
|
||||||
<th class="text-right" title="Stack limit por slot">Stack</th>
|
<th class="text-right" title="Nivel del pasivo del enano (0-3)">Skill enano</th>
|
||||||
<th class="text-right" title="Capacidad total = slots × stack">Capacidad (shots)</th>
|
<th class="text-right" title="Peso máximo = base × (1 + skill)">Peso máx</th>
|
||||||
<th>Acciones</th>
|
<th>Acciones</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -118,22 +121,25 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<input type="number"
|
<input type="number"
|
||||||
min="0"
|
min="1"
|
||||||
x-model.number="c.inventorySlots"
|
step="1"
|
||||||
@keydown.enter="updateInventory(c)"
|
x-model.number="c.baseWeight"
|
||||||
@blur="updateInventory(c)"
|
@keydown.enter="updateWeight(c)"
|
||||||
style="width: 70px; padding: 0.25rem 0.5rem; font-size: 0.875rem; text-align: right;"/>
|
@blur="updateWeight(c)"
|
||||||
|
style="width: 100px; padding: 0.25rem 0.5rem; font-size: 0.875rem; text-align: right;"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<input type="number"
|
<select x-model.number="c.skillLevel"
|
||||||
min="0"
|
@change="updateWeight(c)"
|
||||||
x-model.number="c.stackLimit"
|
style="width: 130px; padding: 0.25rem 0.5rem; font-size: 0.875rem;">
|
||||||
@keydown.enter="updateInventory(c)"
|
<option value="0">0 — sin pasivo</option>
|
||||||
@blur="updateInventory(c)"
|
<option value="1">1 — +100%</option>
|
||||||
style="width: 80px; padding: 0.25rem 0.5rem; font-size: 0.875rem; text-align: right;"/>
|
<option value="2">2 — +200%</option>
|
||||||
|
<option value="3">3 — +300%</option>
|
||||||
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right text-mono text-bold"
|
<td class="text-right text-mono text-bold"
|
||||||
x-text="fmtAdena(capacityOf(c))"></td>
|
x-text="fmtAdena(maxWeightOf(c))"></td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="secondary outline"
|
class="secondary outline"
|
||||||
@@ -151,34 +157,53 @@
|
|||||||
|
|
||||||
<article x-show="characters.length > 0">
|
<article x-show="characters.length > 0">
|
||||||
<header>
|
<header>
|
||||||
<h2>💼 Capacidad de inventario de shots</h2>
|
<h2>💼 Capacidad de inventario de shots (por peso)</h2>
|
||||||
<p class="text-sm text-muted">Total de shots que pueden cargar todos tus personajes en el inventario. Slots × Stack limit por personaje.</p>
|
<p class="text-sm text-muted">Cada card muestra cuántos shots de cada tipo×grado caben en el inventario según el pasivo del personaje. Peso máx = baseWeight × (1 + skillLevel).</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="flex-row gap-4 mb-4" style="flex-wrap:wrap;align-items:center">
|
<div class="flex-row gap-2" style="flex-wrap:wrap">
|
||||||
|
<template x-for="c in characters" :key="c.id">
|
||||||
|
<article style="flex:1; min-width:320px; max-width:560px">
|
||||||
|
<header style="flex-wrap:wrap;gap:0.5rem;align-items:center">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs text-muted mb-2">Capacidad total de la cuenta</p>
|
<p class="text-sm text-bold" x-text="c.name" style="margin:0"></p>
|
||||||
<p class="text-lg text-mono text-bold text-success"
|
<p class="text-xs text-muted" style="margin:0"
|
||||||
x-text="fmtAdena(totalCapacity()) + ' shots'"></p>
|
x-text="c.characterClass + ' · Lv ' + c.level + ' · base ' + fmtAdena(c.baseWeight) + ' · ×' + (c.skillLevel + 1)"></p>
|
||||||
<p class="text-xs text-muted"
|
|
||||||
x-text="characters.length + ' personaje' + (characters.length === 1 ? '' : 's') + ' · ' + totalSlots() + ' slots · stack máx ' + fmtAdena(maxStack())"></p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span style="margin-left:auto"
|
||||||
|
:class="c.skillLevel > 0 ? 'badge-success' : 'badge-muted'"
|
||||||
|
x-text="c.skillLevel === 0 ? 'Sin pasivo' : 'Skill +' + (c.skillLevel * 100) + '%'"></span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<p class="text-xs text-muted mt-2">Peso máx: <span class="text-bold text-mono" x-text="fmtAdena(maxWeightOf(c))"></span></p>
|
||||||
|
|
||||||
|
<div class="table-wrapper" style="margin-top:0.5rem">
|
||||||
|
<table style="font-size:0.7rem; margin:0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="cell-label text-left"></th>
|
||||||
|
<template x-for="g in GRADOS" :key="g">
|
||||||
|
<th class="cell-label text-right" x-text="g"></th>
|
||||||
|
</template>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<template x-for="t in TIPOS" :key="t">
|
||||||
|
<tr>
|
||||||
|
<td class="text-bold text-mono" x-text="tipoShort(t)"></td>
|
||||||
|
<template x-for="g in GRADOS" :key="t + '-' + g">
|
||||||
|
<td class="text-right text-mono" x-text="fmtAdena(capacityOf(c, t, g))"></td>
|
||||||
|
</template>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid-3">
|
<p class="text-xs text-muted mt-2">
|
||||||
<template x-for="c in characters" :key="c.id">
|
Total 15 tipos: <span class="text-mono text-bold" x-text="fmtAdena(totalCapacityOf(c))"></span> shots
|
||||||
<div class="metric-card">
|
</p>
|
||||||
<p class="text-sm text-bold mb-2" x-text="c.name"></p>
|
</article>
|
||||||
<p class="text-xs text-muted mb-2"
|
|
||||||
x-text="c.characterClass + ' · Lv ' + c.level"></p>
|
|
||||||
<div class="flex-row gap-2" style="font-size:0.75rem">
|
|
||||||
<span class="text-mono"><span x-text="c.inventorySlots"></span> slots</span>
|
|
||||||
<span class="text-mono">× <span x-text="c.stackLimit"></span></span>
|
|
||||||
</div>
|
|
||||||
<p class="text-lg text-mono text-bold mt-2"
|
|
||||||
x-text="fmtAdena(capacityOf(c))"></p>
|
|
||||||
<p class="text-xs text-muted">shots</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@@ -204,12 +229,20 @@
|
|||||||
submitting: false,
|
submitting: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
|
||||||
|
SHOT_WEIGHTS: {
|
||||||
|
Soulshot: { D: 3, C: 3, B: 2, A: 2, S: 2 },
|
||||||
|
Spiritshot: { D: 5, C: 3, B: 3, A: 2, S: 2 },
|
||||||
|
'Blessed Spiritshot': { D: 5, C: 3, B: 3, A: 2, S: 2 },
|
||||||
|
},
|
||||||
|
TIPOS: ['Soulshot', 'Spiritshot', 'Blessed Spiritshot'],
|
||||||
|
GRADOS: ['D', 'C', 'B', 'A', 'S'],
|
||||||
|
|
||||||
form: {
|
form: {
|
||||||
name: '',
|
name: '',
|
||||||
characterClass: '',
|
characterClass: '',
|
||||||
level: null,
|
level: null,
|
||||||
inventorySlots: 80,
|
baseWeight: 115230,
|
||||||
stackLimit: 9999,
|
skillLevel: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
@@ -243,14 +276,14 @@
|
|||||||
name: this.form.name.trim(),
|
name: this.form.name.trim(),
|
||||||
characterClass: this.form.characterClass.trim(),
|
characterClass: this.form.characterClass.trim(),
|
||||||
level: this.form.level,
|
level: this.form.level,
|
||||||
inventorySlots: this.form.inventorySlots || null,
|
baseWeight: this.form.baseWeight || null,
|
||||||
stackLimit: this.form.stackLimit || null,
|
skillLevel: this.form.skillLevel,
|
||||||
});
|
});
|
||||||
this.form.name = '';
|
this.form.name = '';
|
||||||
this.form.characterClass = '';
|
this.form.characterClass = '';
|
||||||
this.form.level = null;
|
this.form.level = null;
|
||||||
this.form.inventorySlots = 80;
|
this.form.baseWeight = 115230;
|
||||||
this.form.stackLimit = 9999;
|
this.form.skillLevel = 0;
|
||||||
await this.loadCharacters();
|
await this.loadCharacters();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.error = e.message || 'Error al guardar';
|
this.error = e.message || 'Error al guardar';
|
||||||
@@ -259,21 +292,28 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
capacityOf(c) {
|
tipoShort(tipo) {
|
||||||
return (c.inventorySlots || 0) * (c.stackLimit || 0);
|
return { 'Soulshot': 'SS', 'Spiritshot': 'SPS', 'Blessed Spiritshot': 'BSS' }[tipo] || tipo;
|
||||||
},
|
},
|
||||||
|
|
||||||
totalCapacity() {
|
maxWeightOf(c) {
|
||||||
return this.characters.reduce((sum, c) => sum + this.capacityOf(c), 0);
|
return (c.baseWeight || 115230) * (1 + (c.skillLevel || 0));
|
||||||
},
|
},
|
||||||
|
|
||||||
totalSlots() {
|
capacityOf(c, tipo, grado) {
|
||||||
return this.characters.reduce((sum, c) => sum + (c.inventorySlots || 0), 0);
|
const maxW = this.maxWeightOf(c);
|
||||||
|
const w = this.SHOT_WEIGHTS[tipo]?.[grado] || 0;
|
||||||
|
return w > 0 ? Math.floor(maxW / w) : 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
maxStack() {
|
totalCapacityOf(c) {
|
||||||
if (!this.characters.length) return 0;
|
let total = 0;
|
||||||
return this.characters.reduce((m, c) => Math.max(m, c.stackLimit || 0), 0);
|
for (const t of this.TIPOS) {
|
||||||
|
for (const g of this.GRADOS) {
|
||||||
|
total += this.capacityOf(c, t, g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateLevel(c) {
|
async updateLevel(c) {
|
||||||
@@ -287,15 +327,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateInventory(c) {
|
async updateWeight(c) {
|
||||||
const slots = Math.max(0, c.inventorySlots || 0);
|
const bw = Math.max(1, c.baseWeight || 115230);
|
||||||
const stack = Math.max(0, c.stackLimit || 0);
|
const sk = Math.max(0, Math.min(3, c.skillLevel || 0));
|
||||||
c.inventorySlots = slots;
|
c.baseWeight = bw;
|
||||||
c.stackLimit = stack;
|
c.skillLevel = sk;
|
||||||
try {
|
try {
|
||||||
await window.api.updateCharacterInventory(c.id, slots, stack);
|
await window.api.updateCharacterWeight(c.id, bw, sk);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.error = e.message || 'Error al actualizar inventario';
|
this.error = e.message || 'Error al actualizar peso/skill';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user