feat: show item renders in public database
This commit is contained in:
+4
-1
File diff suppressed because one or more lines are too long
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
<article class="database-item-card" class:talent-card={item.variant === 'talent'}>
|
<article class="database-item-card" class:talent-card={item.variant === 'talent'}>
|
||||||
<header>
|
<header>
|
||||||
|
{#if item.renderUrl}<img class="database-item-render" src={item.renderUrl} alt="" loading="lazy"/>{/if}
|
||||||
<div class="database-item-icon">
|
<div class="database-item-icon">
|
||||||
{#if item.gameIcon}<GameIcon name={item.gameIcon} decorative/>{:else}<ItemIcon src={item.iconUrl} alt=""/>{/if}
|
{#if item.gameIcon}<GameIcon name={item.gameIcon} decorative/>{:else}<ItemIcon src={item.iconUrl} alt=""/>{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export type DatabaseItem = {
|
|||||||
kicker: string;
|
kicker: string;
|
||||||
variant?: 'talent';
|
variant?: 'talent';
|
||||||
iconUrl?: string;
|
iconUrl?: string;
|
||||||
|
renderUrl?: string;
|
||||||
gameIcon?: GameIconName;
|
gameIcon?: GameIconName;
|
||||||
headerBadge?: string;
|
headerBadge?: string;
|
||||||
headerMetaIcon?: GameIconName;
|
headerMetaIcon?: GameIconName;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { DatabaseFact, DatabaseItem } from '$lib/database/items';
|
|||||||
import type { GameIconName } from '$lib/game-icons';
|
import type { GameIconName } from '$lib/game-icons';
|
||||||
import { weaponBaseRange } from '$lib/builder/loadout';
|
import { weaponBaseRange } from '$lib/builder/loadout';
|
||||||
import { gameDataIconUrl, type GameDataIconType } from '$lib/server/game-data-icons';
|
import { gameDataIconUrl, type GameDataIconType } from '$lib/server/game-data-icons';
|
||||||
|
import { managedImageUrl } from '$lib/server/managed-images';
|
||||||
import { getDatabase } from '$lib/server/database';
|
import { getDatabase } from '$lib/server/database';
|
||||||
import { getPagination, getRegistrySearch } from '$lib/server/pagination';
|
import { getPagination, getRegistrySearch } from '$lib/server/pagination';
|
||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
@@ -146,7 +147,10 @@ export const load: PageServerLoad = async ({ params, url }) => {
|
|||||||
}
|
}
|
||||||
const where = conditions.join(' AND ');
|
const where = conditions.join(' AND ');
|
||||||
countSql = `SELECT COUNT(*) AS total FROM gear item WHERE ${where}`;
|
countSql = `SELECT COUNT(*) AS total FROM gear item WHERE ${where}`;
|
||||||
rowsSql = `SELECT item.* FROM gear item WHERE ${where}
|
rowsSql = `SELECT item.*,render.id AS render_id,render.updated_at AS render_updated_at
|
||||||
|
FROM gear item
|
||||||
|
LEFT JOIN managed_images render ON render.source_entity_type='gear_render' AND render.source_entity_id=item.id
|
||||||
|
WHERE ${where}
|
||||||
ORDER BY item.slot,item.tier,item.name COLLATE NOCASE LIMIT ? OFFSET ?`;
|
ORDER BY item.slot,item.tier,item.name COLLATE NOCASE LIMIT ? OFFSET ?`;
|
||||||
filters = [
|
filters = [
|
||||||
{
|
{
|
||||||
@@ -208,13 +212,14 @@ export const load: PageServerLoad = async ({ params, url }) => {
|
|||||||
}
|
}
|
||||||
const where = conditions.join(' AND ');
|
const where = conditions.join(' AND ');
|
||||||
countSql = `SELECT COUNT(*) AS total FROM weapons item WHERE ${where}`;
|
countSql = `SELECT COUNT(*) AS total FROM weapons item WHERE ${where}`;
|
||||||
rowsSql = `SELECT item.*,
|
rowsSql = `SELECT item.*,render.id AS render_id,render.updated_at AS render_updated_at,
|
||||||
native.damage_min,native.damage_max,native.reference_level,
|
native.damage_min,native.damage_max,native.reference_level,
|
||||||
(SELECT COUNT(*) FROM weapon_talent_options options WHERE options.weapon_id=item.id AND options.talent_slot=1) AS talent_1_count,
|
(SELECT COUNT(*) FROM weapon_talent_options options WHERE options.weapon_id=item.id AND options.talent_slot=1) AS talent_1_count,
|
||||||
(SELECT COUNT(*) FROM weapon_talent_options options WHERE options.weapon_id=item.id AND options.talent_slot=2) AS talent_2_count,
|
(SELECT COUNT(*) FROM weapon_talent_options options WHERE options.weapon_id=item.id AND options.talent_slot=2) AS talent_2_count,
|
||||||
(SELECT COUNT(*) FROM weapon_category_attribute_bonuses bonuses WHERE bonuses.weapon_category=item.category) AS bonus_count
|
(SELECT COUNT(*) FROM weapon_category_attribute_bonuses bonuses WHERE bonuses.weapon_category=item.category) AS bonus_count
|
||||||
FROM weapons item
|
FROM weapons item
|
||||||
LEFT JOIN weapon_tier_ranges native ON native.weapon_id=item.id AND native.tier=item.native_tier
|
LEFT JOIN weapon_tier_ranges native ON native.weapon_id=item.id AND native.tier=item.native_tier
|
||||||
|
LEFT JOIN managed_images render ON render.source_entity_type='weapon_render' AND render.source_entity_id=item.id
|
||||||
WHERE ${where}
|
WHERE ${where}
|
||||||
ORDER BY item.is_exotic DESC,item.category,item.name COLLATE NOCASE LIMIT ? OFFSET ?`;
|
ORDER BY item.is_exotic DESC,item.category,item.name COLLATE NOCASE LIMIT ? OFFSET ?`;
|
||||||
filters = [
|
filters = [
|
||||||
@@ -481,6 +486,7 @@ export const load: PageServerLoad = async ({ params, url }) => {
|
|||||||
];
|
];
|
||||||
return {
|
return {
|
||||||
id: String(row.id), name: String(row.name), kicker: `${row.slot} // T${row.tier}`,
|
id: String(row.id), name: String(row.name), kicker: `${row.slot} // T${row.tier}`,
|
||||||
|
renderUrl: row.render_id ? managedImageUrl(String(row.render_id), row.render_updated_at) : '',
|
||||||
gameIcon: gearIcon(row.slot),
|
gameIcon: gearIcon(row.slot),
|
||||||
badges: [{ label: `T${row.tier}` }, { label: `${Number(row.tier) === 0 ? 1 : 2} MOD SLOT${Number(row.tier) === 0 ? '' : 'S'}` }],
|
badges: [{ label: `T${row.tier}` }, { label: `${Number(row.tier) === 0 ? 1 : 2} MOD SLOT${Number(row.tier) === 0 ? '' : 'S'}` }],
|
||||||
facts: [
|
facts: [
|
||||||
@@ -507,6 +513,7 @@ export const load: PageServerLoad = async ({ params, url }) => {
|
|||||||
const level50Damage = row.damage_min === null ? null : weaponBaseRange(row.damage_min, row.damage_max, 50);
|
const level50Damage = row.damage_min === null ? null : weaponBaseRange(row.damage_min, row.damage_max, 50);
|
||||||
return {
|
return {
|
||||||
id: String(row.id), name: String(row.name), kicker: `${row.category} // ${row.damage_type}`,
|
id: String(row.id), name: String(row.name), kicker: `${row.category} // ${row.damage_type}`,
|
||||||
|
renderUrl: row.render_id ? managedImageUrl(String(row.render_id), row.render_updated_at) : '',
|
||||||
gameIcon: weaponIcon(row.category),
|
gameIcon: weaponIcon(row.category),
|
||||||
headerBadge: `T${row.native_tier}`,
|
headerBadge: `T${row.native_tier}`,
|
||||||
headerMetaIcon: weaponIcon(row.damage_type),
|
headerMetaIcon: weaponIcon(row.damage_type),
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
let editing = $state<Editor | null>(null);
|
let editing = $state<Editor | null>(null);
|
||||||
let augmentEditing = $state<any | null>(null);
|
let augmentEditing = $state<any | null>(null);
|
||||||
let handledResultToken = $state('');
|
let handledResultToken = $state('');
|
||||||
|
let renderResetToken = $state(0);
|
||||||
const compatibleBrands = (ids: string[]) => ids.map((id) => data.brands.find((brand) => brand.id === id)).filter(Boolean);
|
const compatibleBrands = (ids: string[]) => ids.map((id) => data.brands.find((brand) => brand.id === id)).filter(Boolean);
|
||||||
const stat = (id: string) => data.stats.find((item) => item.id === id);
|
const stat = (id: string) => data.stats.find((item) => item.id === id);
|
||||||
const format = (value: number | string, statId: string) => {
|
const format = (value: number | string, statId: string) => {
|
||||||
@@ -63,6 +64,7 @@
|
|||||||
}
|
}
|
||||||
if (form?.gearSaved && form.resultToken && form.resultToken !== handledResultToken) {
|
if (form?.gearSaved && form.resultToken && form.resultToken !== handledResultToken) {
|
||||||
handledResultToken = form.resultToken;
|
handledResultToken = form.resultToken;
|
||||||
|
if (form.gearMode === 'add') renderResetToken += 1;
|
||||||
editing = form.gearMode === 'add' ? blank() : null;
|
editing = form.gearMode === 'add' ? blank() : null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -79,7 +81,7 @@
|
|||||||
<SegmentedChoice legend="SLOT" name="slot" options={slotOptions} bind:value={editing.slot} onchange={(values) => changeSlot(values[0])} required/>
|
<SegmentedChoice legend="SLOT" name="slot" options={slotOptions} bind:value={editing.slot} onchange={(values) => changeSlot(values[0])} required/>
|
||||||
<SegmentedChoice legend="TIER" name="tier" options={tierOptions} bind:value={editing.tier} required/>
|
<SegmentedChoice legend="TIER" name="tier" options={tierOptions} bind:value={editing.tier} required/>
|
||||||
</div>
|
</div>
|
||||||
<GameDataRenderUploadField currentUrl={editing.renderUrl} label="GEAR RENDER"/>
|
<GameDataRenderUploadField currentUrl={editing.renderUrl} label="GEAR RENDER" resetToken={renderResetToken}/>
|
||||||
<div class="gear-ranges">
|
<div class="gear-ranges">
|
||||||
<div class="effect-band range-band"><strong>HP<small>BASE HEALTH</small></strong><label class="field">MINIMUM<input name="healthMin" type="number" min="0" step="any" bind:value={editing.base_health_min} required /></label><label class="field">MAXIMUM<input name="healthMax" type="number" min="0" step="any" bind:value={editing.base_health_max} required /></label></div>
|
<div class="effect-band range-band"><strong>HP<small>BASE HEALTH</small></strong><label class="field">MINIMUM<input name="healthMin" type="number" min="0" step="any" bind:value={editing.base_health_min} required /></label><label class="field">MAXIMUM<input name="healthMax" type="number" min="0" step="any" bind:value={editing.base_health_max} required /></label></div>
|
||||||
<div class="effect-band range-band" class:unavailable={!armoredSlots.includes(editing.slot)}><strong>AR<small>BASE ARMOR</small></strong><label class="field">MINIMUM<input name="armorMin" type="number" min="0" step="any" bind:value={editing.base_armor_min} disabled={!armoredSlots.includes(editing.slot)} required={armoredSlots.includes(editing.slot)} /></label><label class="field">MAXIMUM<input name="armorMax" type="number" min="0" step="any" bind:value={editing.base_armor_max} disabled={!armoredSlots.includes(editing.slot)} required={armoredSlots.includes(editing.slot)} /></label></div>
|
<div class="effect-band range-band" class:unavailable={!armoredSlots.includes(editing.slot)}><strong>AR<small>BASE ARMOR</small></strong><label class="field">MINIMUM<input name="armorMin" type="number" min="0" step="any" bind:value={editing.base_armor_min} disabled={!armoredSlots.includes(editing.slot)} required={armoredSlots.includes(editing.slot)} /></label><label class="field">MAXIMUM<input name="armorMax" type="number" min="0" step="any" bind:value={editing.base_armor_max} disabled={!armoredSlots.includes(editing.slot)} required={armoredSlots.includes(editing.slot)} /></label></div>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
let editing = $state<any | null>(null);
|
let editing = $state<any | null>(null);
|
||||||
let bonusEditing = $state<any | null>(null);
|
let bonusEditing = $state<any | null>(null);
|
||||||
let handledResultToken = $state('');
|
let handledResultToken = $state('');
|
||||||
|
let renderResetToken = $state(0);
|
||||||
let openedRequestedWeapon = $state(false);
|
let openedRequestedWeapon = $state(false);
|
||||||
const edit = (weapon: any) => {
|
const edit = (weapon: any) => {
|
||||||
const tiers = [0, 1, 2].map((tier) => {
|
const tiers = [0, 1, 2].map((tier) => {
|
||||||
@@ -94,6 +95,7 @@
|
|||||||
}
|
}
|
||||||
if (form?.weaponSaved && form.resultToken && form.resultToken !== handledResultToken) {
|
if (form?.weaponSaved && form.resultToken && form.resultToken !== handledResultToken) {
|
||||||
handledResultToken = form.resultToken;
|
handledResultToken = form.resultToken;
|
||||||
|
if (form.weaponMode === 'add') renderResetToken += 1;
|
||||||
editing = form.weaponMode === 'add' ? blank() : null;
|
editing = form.weaponMode === 'add' ? blank() : null;
|
||||||
}
|
}
|
||||||
if (form?.weaponBonusProfileSaved && form.resultToken && form.resultToken !== handledResultToken) {
|
if (form?.weaponBonusProfileSaved && form.resultToken && form.resultToken !== handledResultToken) {
|
||||||
@@ -147,7 +149,7 @@
|
|||||||
<SegmentedChoice legend="DAMAGE TYPE" name="damageType" options={damageTypeOptions} bind:value={editing.damage_type} required/>
|
<SegmentedChoice legend="DAMAGE TYPE" name="damageType" options={damageTypeOptions} bind:value={editing.damage_type} required/>
|
||||||
<label class="field exotic-toggle">EXOTIC STATUS<span title="Exotic weapon"><input name="isExotic" type="checkbox" value="1" checked={Number(editing.is_exotic) === 1} onchange={(event) => setExotic(event.currentTarget.checked)}/><GameIcon name="exotic" decorative/><b class="sr-only">EXOTIC WEAPON</b></span></label>
|
<label class="field exotic-toggle">EXOTIC STATUS<span title="Exotic weapon"><input name="isExotic" type="checkbox" value="1" checked={Number(editing.is_exotic) === 1} onchange={(event) => setExotic(event.currentTarget.checked)}/><GameIcon name="exotic" decorative/><b class="sr-only">EXOTIC WEAPON</b></span></label>
|
||||||
</div>
|
</div>
|
||||||
<GameDataRenderUploadField currentUrl={editing.renderUrl} label="WEAPON RENDER"/>
|
<GameDataRenderUploadField currentUrl={editing.renderUrl} label="WEAPON RENDER" resetToken={renderResetToken}/>
|
||||||
{#if Number(editing.is_exotic)}
|
{#if Number(editing.is_exotic)}
|
||||||
<section class="weapon-exotic-talent">
|
<section class="weapon-exotic-talent">
|
||||||
<div class="selection-head"><div><span>DEDICATED EXOTIC TALENT</span><small>SELECT ONE TALENT FROM THE EXOTIC WEAPON REGISTRY</small></div><b>{editing.exotic_talent_id ? '1 / 1 ASSIGNED' : 'REQUIRED'}</b></div>
|
<div class="selection-head"><div><span>DEDICATED EXOTIC TALENT</span><small>SELECT ONE TALENT FROM THE EXOTIC WEAPON REGISTRY</small></div><b>{editing.exotic_talent_id ? '1 / 1 ASSIGNED' : 'REQUIRED'}</b></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user