feat: add avatar sandbox oufits feature!
This commit is contained in:
parent
4a3ad2eb5f
commit
792375158d
2 changed files with 340 additions and 60 deletions
|
|
@ -70,6 +70,7 @@ let Order = "desc"
|
||||||
let ShowOffsale = true
|
let ShowOffsale = true
|
||||||
let TabSelected = "hat"
|
let TabSelected = "hat"
|
||||||
let RetroItems = null
|
let RetroItems = null
|
||||||
|
let Outfits = null
|
||||||
|
|
||||||
/* Customization */
|
/* Customization */
|
||||||
let SelectedBodyPart
|
let SelectedBodyPart
|
||||||
|
|
@ -78,8 +79,9 @@ let SelectedBodyPart
|
||||||
Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
|
Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
|
||||||
Utilities = Utilities.default;
|
Utilities = Utilities.default;
|
||||||
|
|
||||||
chrome.storage.sync.get(['PolyPlus_Settings'], function(result){
|
chrome.storage.sync.get(['PolyPlus_Settings', 'PolyPlus_AvatarSandboxOutfits'], function(result){
|
||||||
Settings = result.PolyPlus_Settings || Utilities.DefaultSettings;
|
Settings = result.PolyPlus_Settings || Utilities.DefaultSettings;
|
||||||
|
Outfits = result.PolyPlus_AvatarSandboxOutfits || [];
|
||||||
|
|
||||||
if (Settings.AvatarSandboxOn || 1 === 1) {
|
if (Settings.AvatarSandboxOn || 1 === 1) {
|
||||||
if (new URLSearchParams(window.location.search).has('sandbox')) {
|
if (new URLSearchParams(window.location.search).has('sandbox')) {
|
||||||
|
|
@ -284,6 +286,44 @@ async function PageLoad() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const SaveButton = document.getElementById('saveOutfit')
|
||||||
|
const OutfitCreateModal = document.getElementById('p+outfit_create')
|
||||||
|
const OutfitCreateButton = document.getElementById('p+save_outfit_confirm')
|
||||||
|
const OutfitCreateError = document.getElementById('p+outfit_create_error')
|
||||||
|
SaveButton.addEventListener('click', function(){
|
||||||
|
console.log(Outfits)
|
||||||
|
OutfitCreateModal.showModal()
|
||||||
|
})
|
||||||
|
|
||||||
|
OutfitCreateButton.addEventListener('click', function(){
|
||||||
|
let OutfitName = OutfitCreateButton.previousElementSibling.value.trim()
|
||||||
|
OutfitCreateButton.previousElementSibling.value = ''
|
||||||
|
if (OutfitName === '') {
|
||||||
|
OutfitCreateError.classList = 'text-danger';
|
||||||
|
OutfitCreateError.innerHTML = '<i class="fa-duotone fa-circle-exclamation mr-1"></i> You cannot name an outfit nothing.';
|
||||||
|
return
|
||||||
|
} else if (OutfitName.length > 25) {
|
||||||
|
OutfitName = OutfitName.substring(0, 25)
|
||||||
|
} else if (Outfits.findIndex((x) => x.name.trim() === OutfitName) !== -1) {
|
||||||
|
OutfitCreateError.classList = 'text-danger';
|
||||||
|
OutfitCreateError.innerHTML = '<i class="fa-duotone fa-circle-exclamation mr-1"></i> You already have an outfit with the name "' + OutfitName + '".';
|
||||||
|
return
|
||||||
|
}
|
||||||
|
OutfitCreateModal.close()
|
||||||
|
Outfits.push({
|
||||||
|
name: OutfitName,
|
||||||
|
createdAt: new Date().getTime(),
|
||||||
|
data: Avatar
|
||||||
|
})
|
||||||
|
if (TabSelected === 'outfit') {
|
||||||
|
LoadItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.storage.sync.set({'PolyPlus_AvatarSandboxOutfits': Outfits}, function(){
|
||||||
|
console.log('Saved outfits!')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
document.getElementById('view-cache').addEventListener('click', function(){
|
document.getElementById('view-cache').addEventListener('click', function(){
|
||||||
console.log('Cache: ', ItemCache)
|
console.log('Cache: ', ItemCache)
|
||||||
})
|
})
|
||||||
|
|
@ -486,9 +526,21 @@ async function LoadItems() {
|
||||||
document.getElementById('inventory').innerHTML = ''
|
document.getElementById('inventory').innerHTML = ''
|
||||||
|
|
||||||
let Items;
|
let Items;
|
||||||
if (TabSelected !== 'retro') {
|
if (['retro', 'outfit'].indexOf(TabSelected) === -1) {
|
||||||
Items = (await (await fetch('https://api.polytoria.com/v1/store?limit=12&order=' + Order + '&sort=' + Sort + '&showOffsale=' + ShowOffsale + '&types[]='+ TabSelected +'&search=' + Search + '&page=' + Page)).json())
|
Items = (await (await fetch('https://api.polytoria.com/v1/store?limit=12&order=' + Order + '&sort=' + Sort + '&showOffsale=' + ShowOffsale + '&types[]='+ TabSelected +'&search=' + Search + '&page=' + Page)).json())
|
||||||
} else {
|
} else if (TabSelected === 'outfit') {
|
||||||
|
const OutfitsClone = structuredClone(Outfits)
|
||||||
|
let Groups = []
|
||||||
|
while (OutfitsClone.length > 0) {
|
||||||
|
Groups.push(OutfitsClone.splice(0, 12));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(Groups, OutfitsClone)
|
||||||
|
Items = {
|
||||||
|
assets: Groups[Page - 1],
|
||||||
|
pages: Groups.length
|
||||||
|
}
|
||||||
|
} else if (TabSelected === 'retro') {
|
||||||
if (RetroItems === null) {
|
if (RetroItems === null) {
|
||||||
Items = (await (await fetch('https://poly-upd-archival.pages.dev/data.json')).json())
|
Items = (await (await fetch('https://poly-upd-archival.pages.dev/data.json')).json())
|
||||||
Object.values(Items).forEach((item, index) => {
|
Object.values(Items).forEach((item, index) => {
|
||||||
|
|
@ -538,6 +590,11 @@ async function LoadItems() {
|
||||||
document.getElementById('pagination-first').classList.add('disabled');
|
document.getElementById('pagination-first').classList.add('disabled');
|
||||||
}
|
}
|
||||||
document.getElementById('pagination-current').innerText = Page
|
document.getElementById('pagination-current').innerText = Page
|
||||||
|
|
||||||
|
if (Items.assets === undefined) { Items.assets = [] }
|
||||||
|
if (Items.assets.length > 0) {
|
||||||
|
document.getElementById('inventory').classList.add('itemgrid')
|
||||||
|
if (TabSelected !== 'outfit') {
|
||||||
Items.assets.forEach(item => {
|
Items.assets.forEach(item => {
|
||||||
const ItemColumn = document.createElement('div')
|
const ItemColumn = document.createElement('div')
|
||||||
ItemColumn.classList = 'col-auto'
|
ItemColumn.classList = 'col-auto'
|
||||||
|
|
@ -597,6 +654,175 @@ async function LoadItems() {
|
||||||
WearAsset(item, item.id)
|
WearAsset(item, item.id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
Items.assets.forEach((outfit, index) => {
|
||||||
|
const ItemColumn = document.createElement('div')
|
||||||
|
ItemColumn.classList = 'col-auto'
|
||||||
|
ItemColumn.innerHTML = `
|
||||||
|
<div style="max-width: 150px;">
|
||||||
|
<div class="card mb-2">
|
||||||
|
<div class="p-2 text-center">
|
||||||
|
<div class="mb-1">
|
||||||
|
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.headColor}; padding: 15px;"></button>
|
||||||
|
</div>
|
||||||
|
<div class="mb-1">
|
||||||
|
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.leftArmColor}; padding: 10px; padding-top: 20px; padding-bottom: 20px;"></button>
|
||||||
|
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.torsoColor}; padding: 20px;"></button>
|
||||||
|
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.rightArmColor}; padding: 20px; padding: 10px; padding-top: 20px; padding-bottom: 20px;"></button>
|
||||||
|
</div>
|
||||||
|
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.leftLegColor}; padding: 10px; padding-top: 20px; padding-bottom: 20px;"></button>
|
||||||
|
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.rightLegColor}; padding: 10px; padding-top: 20px; padding-bottom: 20px;"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h6 class="text-truncate mb-0 text-reset text-center mb-2">${outfit.name}</h6>
|
||||||
|
<div class="btn-group w-100">
|
||||||
|
<button class="btn btn-primary btn-sm p+outfit_wear_button">Wear</button>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-warning dropdown-toggle btn-sm" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="fa-duotone fa-wrench"></i>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item text-primary p+outfit_rename_button" href="#">
|
||||||
|
<i class="fa-solid fa-signature"></i>
|
||||||
|
Rename
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span class="p+outfit_overwrite_button dropdown-item text-warning">
|
||||||
|
<i class="fa-solid fa-wand-magic-sparkles"></i>
|
||||||
|
<span>Overwrite</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<hr class="dropdown-divider">
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span class="p+outfit_delete_button dropdown-item text-danger">
|
||||||
|
<i class="fa-duotone fa-trash"></i>
|
||||||
|
<span>Delete</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
document.getElementById('inventory').appendChild(ItemColumn)
|
||||||
|
Utilities.InjectResource("registerTooltips")
|
||||||
|
|
||||||
|
ItemColumn.getElementsByClassName('p+outfit_wear_button')[0].addEventListener('click', function(){
|
||||||
|
if (Avatar === outfit.data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('Equipped Outfit: ', outfit)
|
||||||
|
Avatar = outfit.data
|
||||||
|
UpdateAvatar()
|
||||||
|
})
|
||||||
|
|
||||||
|
const OutfitRenameModal = document.getElementById('p+outfit_rename')
|
||||||
|
const OutfitRenameButton = document.getElementById('p+rename_outfit_confirm')
|
||||||
|
const OutfitRenameError = document.getElementById('p+outfit_rename_error')
|
||||||
|
ItemColumn.getElementsByClassName('p+outfit_rename_button')[0].addEventListener('click', function(){
|
||||||
|
OutfitRenameModal.showModal()
|
||||||
|
document.getElementById('p+outfit_rename_name').innerText = outfit.name
|
||||||
|
})
|
||||||
|
|
||||||
|
OutfitRenameButton.addEventListener('click', function(){
|
||||||
|
let OutfitName = OutfitRenameButton.previousElementSibling.value.trim()
|
||||||
|
OutfitRenameButton.previousElementSibling.value = ''
|
||||||
|
if (OutfitName === '') {
|
||||||
|
OutfitRenameError.classList = 'text-danger';
|
||||||
|
OutfitRenameError.innerHTML = '<i class="fa-duotone fa-circle-exclamation mr-1"></i> You cannot name an outfit nothing.';
|
||||||
|
return
|
||||||
|
} else if (OutfitName.length > 25) {
|
||||||
|
OutfitName = OutfitName.substring(0, 25)
|
||||||
|
} else if (Outfits.findIndex((x) => x.name.trim() === OutfitName) !== -1) {
|
||||||
|
OutfitRenameError.classList = 'text-danger';
|
||||||
|
OutfitRenameError.innerHTML = '<i class="fa-duotone fa-circle-exclamation mr-1"></i> You already have an outfit with the name "' + OutfitName + '".';
|
||||||
|
return
|
||||||
|
}
|
||||||
|
OutfitRenameModal.close()
|
||||||
|
Outfits[index].name = OutfitName
|
||||||
|
if (TabSelected === 'outfit') {
|
||||||
|
LoadItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.storage.sync.set({'PolyPlus_AvatarSandboxOutfits': Outfits}, function(){
|
||||||
|
console.log('Saved outfits!')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let OverwritePending = false
|
||||||
|
const OutfitOverwriteButton = ItemColumn.getElementsByClassName('p+outfit_overwrite_button')[0]
|
||||||
|
OutfitOverwriteButton.addEventListener('click', function(e){
|
||||||
|
e.stopPropagation()
|
||||||
|
if (OverwritePending === false) {
|
||||||
|
OverwritePending = true
|
||||||
|
OutfitOverwriteButton.children[1].innerText = 'Are you sure?'
|
||||||
|
setTimeout(function (){
|
||||||
|
if (OverwritePending === true) {
|
||||||
|
OutfitOverwriteButton.children[1].innerText = 'Overwrite'
|
||||||
|
OverwritePending = false
|
||||||
|
}
|
||||||
|
}, 3000)
|
||||||
|
} else {
|
||||||
|
OverwritePending = false
|
||||||
|
console.log('Overwrite Outfit (outfit, avatar): ', outfit, Avatar)
|
||||||
|
|
||||||
|
Outfits[index].data = Avatar
|
||||||
|
if (TabSelected === 'outfit') {
|
||||||
|
LoadItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.storage.sync.set({'PolyPlus_AvatarSandboxOutfits': Outfits}, function(){
|
||||||
|
console.log('Saved outfits!')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let DeletePending = false
|
||||||
|
const OutfitDeleteButton = ItemColumn.getElementsByClassName('p+outfit_delete_button')[0]
|
||||||
|
OutfitDeleteButton.addEventListener('click', function(e){
|
||||||
|
e.stopPropagation()
|
||||||
|
if (DeletePending === false) {
|
||||||
|
DeletePending = true
|
||||||
|
OutfitDeleteButton.children[1].innerText = 'Are you sure?'
|
||||||
|
setTimeout(function (){
|
||||||
|
if (DeletePending === true) {
|
||||||
|
OutfitDeleteButton.children[1].innerText = 'Delete'
|
||||||
|
DeletePending = false
|
||||||
|
}
|
||||||
|
}, 3000)
|
||||||
|
} else {
|
||||||
|
DeletePending = false
|
||||||
|
console.log('Deleted Outfit: ', outfit)
|
||||||
|
|
||||||
|
Outfits.splice(index, 1)
|
||||||
|
if (TabSelected === 'outfit') {
|
||||||
|
LoadItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.storage.sync.set({'PolyPlus_AvatarSandboxOutfits': Outfits}, function(){
|
||||||
|
console.log('Saved outfits!')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById('inventory').classList.remove('itemgrid')
|
||||||
|
document.getElementById('inventory').innerHTML = `
|
||||||
|
<div class="text-muted" style="padding: 37px 30px;">
|
||||||
|
<h1 class="display-3">
|
||||||
|
<i class="fas fa-box-open"></i>
|
||||||
|
</h1>
|
||||||
|
<h6 class="mb-0">
|
||||||
|
You do not have any items matching this type or search query. Find new items in the <a href="/store">store</a>!
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function LoadWearing() {
|
function LoadWearing() {
|
||||||
|
|
@ -710,5 +936,13 @@ function FormatPrice(price) {
|
||||||
} else {
|
} else {
|
||||||
return 'text-muted">???</small>'
|
return 'text-muted">???</small>'
|
||||||
}
|
}
|
||||||
return "what"
|
return '">how did this happen</small>'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chrome.storage.onChanged.addListener(function (changes, namespace) {
|
||||||
|
if ('PolyPlus_AvatarSandboxOutfits' in changes) {
|
||||||
|
chrome.storage.sync.get(['PolyPlus_AvatarSandboxOutfits'], function (result) {
|
||||||
|
Outfits = result.PolyPlus_AvatarSandboxOutfits || [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -34,6 +34,10 @@
|
||||||
#options *:not(input):not(:nth-child(2)) {
|
#options *:not(input):not(:nth-child(2)) {
|
||||||
margin-bottom: 3.5px;
|
margin-bottom: 3.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[class^="p+outfit_overwrite_button"]:hover, [class^="p+outfit_delete_button"]:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<dialog id="p+body_colors" class="polyplus-modal" style="width: 375px; border: 1px solid #484848; background-color: #181818; border-radius: 20px; overflow: hidden;">
|
<dialog id="p+body_colors" class="polyplus-modal" style="width: 375px; border: 1px solid #484848; background-color: #181818; border-radius: 20px; overflow: hidden;">
|
||||||
<div class="row text-muted mb-4" style="font-size: 0.8rem;">
|
<div class="row text-muted mb-4" style="font-size: 0.8rem;">
|
||||||
|
|
@ -112,6 +116,42 @@
|
||||||
<div class="colorpicker-color" style="background-color: #e8bac8"></div>
|
<div class="colorpicker-color" style="background-color: #e8bac8"></div>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
<dialog id="p+outfit_create" class="polyplus-modal" style="width: 375px; border: 1px solid #484848; background-color: #181818; border-radius: 20px; overflow: hidden;">
|
||||||
|
<div class="row text-muted mb-4" style="font-size: 0.8rem;">
|
||||||
|
<div class="col">
|
||||||
|
<h5 class="mb-0" style="color: #fff;">Create Outfit</h5>
|
||||||
|
Save this avatar for later!
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<button class="btn btn-info w-100 mx-auto" onclick="this.parentElement.parentElement.parentElement.close();">X</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="input-group mb-2">
|
||||||
|
<input type="text" class="form-control" placeholder="Outfit Name...">
|
||||||
|
<button id="p+save_outfit_confirm" class="btn btn-success">Save</button>
|
||||||
|
</div>
|
||||||
|
<b id="p+outfit_create_error" class="text-muted" style="font-size: 0.85rem;"><i class="fa-duotone fa-square-question mr-1"></i> ...</b>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
<dialog id="p+outfit_rename" class="polyplus-modal" style="width: 375px; border: 1px solid #484848; background-color: #181818; border-radius: 20px; overflow: hidden;">
|
||||||
|
<div class="row text-muted mb-4" style="font-size: 0.8rem;">
|
||||||
|
<div class="col">
|
||||||
|
<h5 class="mb-0" style="color: #fff;">Rename Outfit</h5>
|
||||||
|
Renaming Outfit "<i id="p+outfit_rename_name">none</i>"
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<button class="btn btn-info w-100 mx-auto" onclick="this.parentElement.parentElement.parentElement.close();">X</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="input-group mb-2">
|
||||||
|
<input type="text" class="form-control" placeholder="New Outfit Name...">
|
||||||
|
<button id="p+rename_outfit_confirm" class="btn btn-success">Save</button>
|
||||||
|
</div>
|
||||||
|
<b id="p+outfit_rename_error" class="text-muted" style="font-size: 0.85rem;"><i class="fa-duotone fa-square-question mr-1"></i> ...</b>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
<span class="badge bg-warning mb-2" id="view-cache">Poly+</span>
|
<span class="badge bg-warning mb-2" id="view-cache">Poly+</span>
|
||||||
<h4 class="d-none d-lg-block">
|
<h4 class="d-none d-lg-block">
|
||||||
Avatar Sandbox
|
Avatar Sandbox
|
||||||
|
|
@ -260,6 +300,12 @@
|
||||||
<span class="pilltitle">Retro</span>
|
<span class="pilltitle">Retro</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
|
||||||
|
<a class="nav-link" data-tab="outfit">
|
||||||
|
<i class="fa-solid fa-tv-retro"></i>
|
||||||
|
<span class="pilltitle">Outfits</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="card px-2 pt-2 pb-2 mb-2" style="background: transparent; border-color: transparent; border-top-left-radius: 20px; border-top-right-radius: 20px;">
|
<div class="card px-2 pt-2 pb-2 mb-2" style="background: transparent; border-color: transparent; border-top-left-radius: 20px; border-top-right-radius: 20px;">
|
||||||
<div class="row mb-1">
|
<div class="row mb-1">
|
||||||
|
|
|
||||||
Reference in a new issue