Skip to content

Commit

Permalink
polish alts ui, add unique constraint on user_data
Browse files Browse the repository at this point in the history
  • Loading branch information
serprex committed Jan 28, 2024
1 parent d5d7be7 commit b67da92
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 78 deletions.
3 changes: 2 additions & 1 deletion scripts/initdb.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ create table user_data (
user_id bigint not null references users(id),
type_id int not null,
name text not null,
data json not null
data json not null,
unique (user_id, type_id, name)
);
create table user_role (
user_id bigint not null references users(id),
Expand Down
300 changes: 223 additions & 77 deletions src/views/Alts.jsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,241 @@
import { createSignal, createMemo } from 'solid-js';

import { eleNames } from '../ui.js';
import { userEmit } from '../sock.jsx';
import ExitBtn from '../Components/ExitBtn.jsx';
import * as store from '../store.jsx';

export default function Alts() {
const rx = store.useRx();
const flagNames = [
'no-oracle',
'no-quest',
'no-shop',
'no-up-pillar',
'no-up-merge',
'no-trade',
'hardcore',
];

const flagNames = [
'no-oracle',
'no-quest',
'no-shop',
'no-up-pillar',
'no-up-merge',
'no-trade',
'hardcore',
];
const flags = Object.create(null);
let altname, ele;
function AltCreator() {
const [showCustom, setShowCustom] = createSignal(false);
const [name, setName] = createSignal('');
const [checked, setChecked] = createSignal({ 'no-trade': true });

const Preset = props => {
const selected = createMemo(() => {
const chk = checked();
for (const key of flagNames) {
if (!!chk[key] !== props.flags.includes(key)) {
return false;
}
}
return true;
});
const chked = {};
for (const key of props.flags) {
chked[key] = true;
}
return (
<>
<input
type="button"
value={props.name}
class={selected() ? 'selectedbutton' : ''}
onClick={() => setChecked(chked)}
/>
{props.description && selected() && (
<div style="position:absolute;top:72px;width:900px;white-space:pre-wrap">
{props.description}
</div>
)}
</>
);
};

return (
<>
<ExitBtn x={12} y={12} />
<div style="margin-top:72px">
{flagNames.map(name => (
<div
class="bgbox"
style="z-index:1;position:absolute;top:48px;width:900px;height:552px">
<div style="display:flex;justify-content:space-between">
<input
type="input"
placeholder="Alt Name"
value={name()}
onInput={e => setName(e.target.value)}
/>
<Preset
name="Spins-only"
flags={['no-shop', 'no-up-pillar']}
description={
!showCustom() &&
'Spending money is not allowed other than battle entry fees.\nThat means no buying packs, & no upgrading pillars!\nTrading is allowed, but only with other Spins-Only alts.'
}
/>
<Preset
name="Spins-only Self-found"
flags={['no-shop', 'no-up-pillar', 'no-trade']}
description={
!showCustom() &&
"Just like Spins-only, but trading is also disabled.\nYou're truly limited to what you can win from the AI."
}
/>
<Preset
name="Spins-only Hardcore"
flags={['no-shop', 'no-up-pillar', 'hardcore']}
description={
!showCustom() &&
'Just like Spins-only, but everytime you lose, you permanently lost a card form your deck.\nTrading is allowed, but only with other Spins-only Hardcore alts.'
}
/>
<Preset
name="Spins-only Self-found Hardcore"
flags={['no-shop', 'no-up-pillar', 'no-trade', 'hardcore']}
description={
!showCustom() &&
'Just like Spins-only Self-found, but everytime you lose, you permanently lose a card from your deck.\nRoguelike mode!'
}
/>
{!showCustom() && (
<input
type="button"
value="Custom"
onClick={() => setShowCustom(true)}
/>
)}
</div>
{showCustom() &&
flagNames.map(name => (
<label style="display:block">
<input
type="checkbox"
ref={flags[name]}
checked={name === 'no-trade'}
checked={!!checked()[name]}
onChange={e => {
setChecked(checked => ({
...checked,
[name]: e.currentTarget.checked,
}));
}}
/>{' '}
{name}
</label>
))}
<input type="number" value="1" ref={ele} min="1" max="13" /> element
<input type="input" placeholder="Alt Name" ref={altname} />
<input
type="button"
value="Create"
onClick={() => {
if (!altname.value) return;
const uflags = [];
for (const key in flags) {
if (flags[key].checked) {
uflags.push(key);
}
}
userEmit('altcreate', {
name: altname.value,
flags: uflags,
e: ele.value | 0,
});
}}
/>
</div>
<div style="margin-top:36px">
<input
type="button"
value="Main"
onClick={() => {
store.setAlt(null);
store.doNav(import('../views/MainMenu.jsx'));
}}
/>
</div>
<For each={Object.keys(rx.alts)}>
{name => {
if (!name) return null;
return (
<div style={`color:#${name === rx.uname ? 'ed8' : 'ccb'}`}>
{name}
<input
type="button"
value="Select"
onClick={() => {
store.setAlt(name);
store.doNav(import('../views/MainMenu.jsx'));
}}
/>
{rx.alts[name].flags.join(' ')}
<input
type="button"
value="Delete"
style="float:right"
onClick={() => {
store.rmAlt(name);
userEmit('altdelete', { name });
}}
/>
</div>
);
{!!name() &&
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14].map((i, idx) => (
<span
class={`imgb ico e${
i === 14 ? 13
: i === 13 ? 14
: i
}`}
style={`position:absolute;left:${100 + (idx & 1) * 450}px;top:${
200 + (idx >> 1) * 40
}px`}
onClick={() => {
if (!name()) return;
const uflags = flagNames.filter(key => checked()[key]);
userEmit('altcreate', {
name: name(),
flags: uflags,
e: i === 14 ? (Math.random() * 12 + 1) | 0 : i,
});
}}>
<span style="position:absolute;left:48px;top:6px;width:144px">
{eleNames[i]}
</span>
</span>
))}
</div>
);
}

export default function Alts() {
const rx = store.useRx();
let yesdelete;
const [viewCreator, setViewCreator] = createSignal(false);
const [viewDeletor, setViewDeletor] = createSignal(null);

return (
<>
<ExitBtn x={12} y={12} />
<input
type="button"
value={viewCreator() ? 'Cancel' : 'Create'}
style="position:absolute;left:144px;top:12px"
onClick={() => setViewCreator(x => !x)}
/>
<input
type="button"
value="Main"
style="position:absolute;left:300px;top:12px"
onClick={() => {
store.setAlt(null);
store.doNav(import('../views/MainMenu.jsx'));
}}
</For>
/>
{viewCreator() && <AltCreator />}
<div style="position:absolute;top:72px;height:528px;width:900px;overflow-y:auto">
<table style="width:100%">
<For each={Object.keys(rx.alts)}>
{name => {
if (!name) return null;
return (
<tr>
<td style={`color:#${name === rx.uname ? 'ed8' : 'ccb'}`}>
{name}
</td>
<td>
<input
type="button"
value="Select"
onClick={() => {
store.setAlt(name);
store.doNav(import('../views/MainMenu.jsx'));
}}
/>
</td>
<td>{rx.alts[name].flags.join(' ')}</td>
<td>
<input
type="button"
value="Delete"
style="float:right"
onClick={() => {
setViewDeletor(name);
}}
/>
</td>
</tr>
);
}}
</For>
</table>
</div>
{viewDeletor() && (
<div
class="bgbox"
style="z-index:2;position:absolute;left:200px;width:500px;top:100px;height:400px;display:flex;flex-direction:column;justify-content:space-evenly">
<label style="align-self:center">
<input type="checkbox" ref={yesdelete} /> Yes, delete{' '}
{viewDeletor()}
</label>
<input
type="button"
value="Cancel"
style="align-self:center"
onClick={() => setViewDeletor(null)}
/>
<input
type="button"
value="Delete"
style="align-self:center"
onClick={() => {
if (yesdelete.checked) {
store.rmAlt(viewDeletor());
userEmit('altdelete', { name: viewDeletor() });
}
}}
/>
</div>
)}
</>
);
}

0 comments on commit b67da92

Please sign in to comment.