diff --git a/public/assets/css/dragon.css b/public/assets/css/dragon.css index 2ca4431..523e04b 100644 --- a/public/assets/css/dragon.css +++ b/public/assets/css/dragon.css @@ -1 +1 @@ -:root{--main-font:Cambria,Cochin,Georgia,Times,"Times New Roman",serif;font-size:16px}*{box-sizing:border-box;margin:0;padding:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.container-960{width:960px;margin:0 auto}.ui.button{cursor:pointer;font-size:1rem;font-family:var(--main-font);color:#111;text-align:center;user-select:none;-webkit-tap-highlight-color:transparent;background:#f7f8fa linear-gradient(#fff0,#0000001a);border:none;border-radius:3px;padding:.5rem 1rem;text-decoration:none;transition:opacity .1s,background-color .1s,color .1s,background .1s;display:inline-block;box-shadow:inset 0 1px 0 1px #ffffff4d,inset 0 0 0 1px #adb2bb;&:hover{color:#000c;background-color:#e0e0e0;background-image:linear-gradient(#fff0,#0000001a);box-shadow:inset 0 1px 0 1px #ffffff4d,inset 0 0 0 1px #adb2bb}&.badge{padding:.1rem .25rem;font-size:10px}&.primary{color:#111;background-color:#f4cc67;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #aa8326;border-color:#c59f43 #aa8326 #957321;box-shadow:inset 0 1px #fff3;&:hover{background-color:#fac847;border-color:#c59f43 #aa8326 #957321}}&.secondary{color:#fff;background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3;&:hover{background-color:#4e5964;border-color:#32373e #24282d #212429}}&.danger{background-color:#e57373;background-image:linear-gradient(#ffffff26,#8b00001a);border:1px solid #c62828;border-color:#d32f2f #c62828 #b71c1c;box-shadow:inset 0 1px #fff3;&:hover{background-color:#d95c5c;border-color:#b71c1c #a52727 #8e1f1f}}}.form.control{appearance:none;color:#fff;background-color:#0003;border:1px solid #0000;border-radius:4px;outline:none;width:100%;padding:.5rem;font-size:1rem;display:block;box-shadow:inset 0 1px 4px #0000001a;&::placeholder{color:#ffffffb3}&:hover{background-color:#0000004d}&:focus{background-color:#00000080;border-color:#000c}&.error{background-color:#ff2b2b33;&:hover{background-color:#ff2b2b4d}&:focus{background-color:#ff2b2b4d;border-color:#ff2b2bcc}}}.form.group{margin-bottom:1rem;&>label{margin-bottom:.5rem;display:block}&>.form.control:not(:last-child){margin-bottom:.5rem}}.character-select>.radio-block{background-color:#0003;border-radius:.15rem;display:inline-block;&:not(:last-child){margin-bottom:.25rem}&>input[type=radio]{display:none}&>label{cursor:pointer;background-image:linear-gradient(#fff0,#0000);border:1px solid #0000;border-radius:.15rem;align-items:center;width:100%;padding:.5rem;transition:color,background-color,border-color,background-image .2s;display:flex;&:hover{color:#fff;background-color:#0000004d}&>.badge{margin-left:.25rem}&>span.selected{display:none}&>.char-icon{margin-right:.25rem}}&.active>label{color:#fff;background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3;&>span.selected{display:inline-block}}&>input[type=radio]:checked+label{color:#111;background-color:#f4cc67;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #aa8326;border-color:#c59f43 #aa8326 #957321;box-shadow:inset 0 1px #fff3}&>input[type=radio]:disabled+label{cursor:default}}.character-select:not(:has(input[type=radio]:checked))>.buttons{display:none}section.profile{& header{text-align:center;margin-bottom:2rem;& h3{color:#0000004d;text-transform:uppercase;font-size:1rem}& h4{font-size:.75rem}}&>div.grid{gap:1rem;display:flex;&>section{width:50%;&>div:not(:last-child){margin-bottom:1rem}}}& div.avatar{justify-content:center;align-items:center;display:flex;& img{max-width:250px}}& h4{text-align:center;text-transform:uppercase;color:#fff;background-image:url(/assets/img/bar.jpg);background-position:bottom;margin-bottom:.5rem;padding:.5rem;font-size:.75rem}& div.stats{&>.grid{grid-template-columns:1fr 1fr;gap:.25rem;display:grid;&>div.cell{justify-content:space-between;align-items:center;padding:.25rem .5rem;display:flex;& .label{text-transform:uppercase;margin-right:.25rem;font-size:.75rem}}}}}body{font-family:var(--main-font);background-color:#bcc6cf;background-image:url(/assets/img/bg.jpg);background-position:top;background-repeat:no-repeat;background-attachment:fixed;min-width:968px;max-width:1640px;margin:0 auto}header#main-header{color:#fff;background-image:url(/assets/img/header.jpg);justify-content:space-between;align-items:center;height:76px;padding:0 1rem;display:flex;& h1{margin:0;padding:0}& .right{align-items:center;display:flex;& p{margin-right:1rem}}}main{gap:2rem;width:100%;padding:1rem;display:flex;& #center{flex:1}}aside{min-width:200px;& .box{background-color:#0003;border-radius:.15rem;padding:.5rem}}aside#left nav{&>:not(:last-child){margin-bottom:.25rem}& div.stack{background-color:#0003;border-radius:.15rem;& input[type=checkbox]{display:none;&:checked~div.list{display:block}&:checked+label{color:#fff;background-color:#00000080}}& label{color:#000;cursor:pointer;border-radius:.15rem;align-items:center;padding:.5rem 1rem;text-decoration:none;transition:color,background-color .2s;display:flex;& img{height:18px;margin-right:.25rem}& span.text{width:100%;display:block}&:hover{color:#fff;background-color:#0000004d}& span.arrow{position:relative;top:5px}}& div.list{display:none;&>a{color:#000;border-radius:.15rem;width:100%;padding:.5rem 1rem .5rem 1.35rem;text-decoration:none;transition:color,background-color .2s;display:block;&:not(:last-child):before{content:"├";margin-right:.25rem;display:inline-block}&:last-child:before{content:"└";margin-right:.25rem;display:inline-block;position:relative;top:3px}&:hover{background-color:#0000004d}&.active{color:#fff;background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3}}}}&>a{color:#000;background-color:#0003;border-radius:.15rem;width:100%;padding:.5rem 1rem;text-decoration:none;transition:color,background-color .2s;display:block;&:has(img){align-items:center;display:flex;& img{height:18px;margin-right:.25rem}}&:hover,&.active{color:#fff}&:hover{background-color:#0000004d}&.active{color:#fff;background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3}}}footer{text-align:center;color:#666;justify-content:center;align-items:center;margin:1rem 0;padding:1rem;display:flex;&>p:not(:last-child){margin-right:2rem}}#character{&>.name{align-items:center;display:flex}&>div:not(:last-child){margin-bottom:.5rem}}span.badge{color:#111;background-color:#f7f8fa;border-radius:.25rem;padding:.1rem .25rem;font-size:10px;box-shadow:inset 0 0 0 1px #0000001a;&.dark{color:#fff;background-color:#444c55}&.green{background-color:#a6e3a1}}.char-meter{background-color:#000;border-radius:.1rem;min-width:100px;height:16px;position:relative;&>div{border-radius:.1rem;height:100%;overflow:hidden;&.hp{background-color:#e57373;background-image:linear-gradient(#ffffff26,#8b00001a);border:1px solid #c62828;border-color:#d32f2f #c62828 #b71c1c;box-shadow:inset 0 1px #fff3}&.mp{background-color:#5a9bd4;background-image:linear-gradient(#ffffff26,#3c64961a);border:1px solid #3a7a9c;border-color:#4a8ab0 #3a7a9c #2a6a88;box-shadow:inset 0 1px #fff3}&.tp{background-color:#f4cc67;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #aa8326;border-color:#c59f43 #aa8326 #957321;box-shadow:inset 0 1px #fff3}}}.tooltip{color:#fff;text-align:center;background-color:#000;border:1px solid #666;border-radius:.1rem;padding:.5rem;font-size:14px;box-shadow:0 0 .5rem .1rem #0003}.tooltip-trigger{width:100%;height:100%;position:absolute;top:0;left:0}.debug-query-log{color:#666;padding:1rem;font-family:monospace;font-size:14px;&:last-child{padding-top:0}}#center>section{&:not(:last-child){border-bottom:1px solid #0000001a;margin-bottom:1rem;padding-bottom:1rem}}h1:has(.badge),h2:has(.badge),h3:has(.badge),h4:has(.badge),h5:has(.badge),h6:has(.badge){align-items:center;display:flex;&>.badge{margin-left:.5rem}}.alert{color:#000000de;background:#f8f8f9;border-radius:.285714rem;justify-content:space-between;align-items:center;min-height:1rem;margin:1rem 0;padding:.5rem 1rem;line-height:1.4285rem;transition:opacity .1s,color .1s,background .1s,box-shadow .1s;display:flex;position:relative;box-shadow:inset 0 0 0 1px #22242638,0 0 #0000;&.success{color:#2c662d;background-color:#f0f9eb;border-color:#b3dc9d}&.danger{color:#9f3a38;background-color:#f9e9eb;border-color:#e0b4b4}&.warning{color:#573a08;background-color:#fff8e1;border-color:#f9e79f}&.info{color:#2c7fba;background-color:#f0f9fb;border-color:#b3d7f9}&.dark{color:#2c2c2c;background-color:#f0f0f0;border-color:#b3b3b3}& a[alert-close]{cursor:pointer;color:inherit;font-size:2rem;text-decoration:none}}a{color:#4c0515;text-decoration:none;transition:color .2s;&:hover{color:#6c0515;text-decoration:underline}}body::-webkit-scrollbar{width:.5rem}body::-webkit-scrollbar-track{background:#0000001a}body::-webkit-scrollbar-thumb{background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3}#canvas-container{&>canvas{image-rendering:pixelated;image-rendering:crisp-edges;image-rendering:-webkit-optimize-contrast;width:100%;height:440px;display:block}}.char-icon{background-image:url(/assets/img/world/rogues.png);width:32px;height:32px;&.index-0{background-position:0 0}&.index-1{background-position:-32px 0}&.index-2{background-position:-64px 0}&.index-3{background-position:-96px 0}&.index-4{background-position:-128px 0}} \ No newline at end of file +:root{--main-font:Cambria,Cochin,Georgia,Times,"Times New Roman",serif;font-size:16px}*{box-sizing:border-box;margin:0;padding:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.container-960{width:960px;margin:0 auto}.ui.button{cursor:pointer;font-size:1rem;font-family:var(--main-font);color:#111;text-align:center;user-select:none;-webkit-tap-highlight-color:transparent;background:#f7f8fa linear-gradient(#fff0,#0000001a);border:none;border-radius:3px;padding:.5rem 1rem;text-decoration:none;transition:opacity .1s,background-color .1s,color .1s,background .1s;display:inline-block;box-shadow:inset 0 1px 0 1px #ffffff4d,inset 0 0 0 1px #adb2bb;&:hover{color:#000c;background-color:#e0e0e0;background-image:linear-gradient(#fff0,#0000001a);box-shadow:inset 0 1px 0 1px #ffffff4d,inset 0 0 0 1px #adb2bb}&.badge{padding:.1rem .25rem;font-size:10px}&.primary{color:#111;background-color:#f4cc67;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #aa8326;border-color:#c59f43 #aa8326 #957321;box-shadow:inset 0 1px #fff3;&:hover{background-color:#fac847;border-color:#c59f43 #aa8326 #957321}}&.secondary{color:#fff;background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3;&:hover{background-color:#4e5964;border-color:#32373e #24282d #212429}}&.danger{background-color:#e57373;background-image:linear-gradient(#ffffff26,#8b00001a);border:1px solid #c62828;border-color:#d32f2f #c62828 #b71c1c;box-shadow:inset 0 1px #fff3;&:hover{background-color:#d95c5c;border-color:#b71c1c #a52727 #8e1f1f}}}.form.control{appearance:none;color:#fff;background-color:#0003;border:1px solid #0000;border-radius:4px;outline:none;width:100%;padding:.5rem;font-size:1rem;display:block;box-shadow:inset 0 1px 4px #0000001a;&::placeholder{color:#ffffffb3}&:hover{background-color:#0000004d}&:focus{background-color:#00000080;border-color:#000c}&.error{background-color:#ff2b2b33;&:hover{background-color:#ff2b2b4d}&:focus{background-color:#ff2b2b4d;border-color:#ff2b2bcc}}}.form.group{margin-bottom:1rem;&>label{margin-bottom:.5rem;display:block}&>.form.control:not(:last-child){margin-bottom:.5rem}}.character-select>.radio-block{background-color:#0003;border-radius:.15rem;display:inline-block;&:not(:last-child){margin-bottom:.25rem}&>input[type=radio]{display:none}&>label{cursor:pointer;background-image:linear-gradient(#fff0,#0000);border:1px solid #0000;border-radius:.15rem;align-items:center;width:100%;padding:.5rem;transition:color,background-color,border-color,background-image .2s;display:flex;&:hover{color:#fff;background-color:#0000004d}&>.badge{margin-left:.25rem}&>span.selected{display:none}&>.char-icon{margin-right:.25rem}}&.active>label{color:#fff;background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3;&>span.selected{display:inline-block}}&>input[type=radio]:checked+label{color:#111;background-color:#f4cc67;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #aa8326;border-color:#c59f43 #aa8326 #957321;box-shadow:inset 0 1px #fff3}&>input[type=radio]:disabled+label{cursor:default}}.character-select:not(:has(input[type=radio]:checked))>.buttons{display:none}section.profile{& header{text-align:center;margin-bottom:3rem;& h3{font-size:1rem}& h5{color:#00000080;font-size:.75rem}}&>div.grid{gap:1rem;display:flex;&>section{width:50%;&>div:not(:last-child){margin-bottom:2rem}}}& div.avatar{justify-content:center;align-items:center;padding-bottom:1rem;display:flex;& img{width:185px;height:185px}& .border{width:250px;height:250px;position:absolute}}& h4{text-align:center;text-transform:uppercase;color:#fff;background-image:url(/assets/img/bar.jpg);background-position:bottom;margin-bottom:.5rem;padding:.5rem;font-size:.75rem}& div.stats{&>.grid{grid-template-columns:1fr 1fr;gap:.25rem;display:grid;&>div.cell{justify-content:space-between;align-items:center;padding:.25rem .5rem;display:flex;& .label{text-transform:uppercase;margin-right:.25rem;font-size:.75rem}}}}& #equipped-gear{flex-direction:column;justify-content:center;align-items:center;gap:.5rem;display:flex;& div.item{justify-content:center;align-items:center;display:flex;&.i-1x1{background-image:url(/assets/img/ui/1x1.png);width:30px;height:30px}&.i-2x2{background-image:url(/assets/img/ui/2x2.png);width:60px;height:60px}&.i-2x3{background-image:url(/assets/img/ui/2x3.png);width:60px;height:90px}}&>div{gap:.5rem;display:flex;&>div{justify-content:center;align-items:center;width:60px;display:flex;&.top,&.bot{width:60px;height:60px}&.mid{width:60px;height:90px}}}}}body{font-family:var(--main-font);background-color:#bcc6cf;background-image:url(/assets/img/bg.jpg);background-position:top;background-repeat:no-repeat;background-attachment:fixed;min-width:968px;max-width:1640px;margin:0 auto}header#main-header{color:#fff;background-image:url(/assets/img/header.jpg);justify-content:space-between;align-items:center;height:76px;padding:0 1rem;display:flex;& h1{margin:0;padding:0}& .right{align-items:center;display:flex;& p{margin-right:1rem}}}main{gap:2rem;width:100%;padding:1rem;display:flex;& #center{flex:1}}aside{min-width:200px;& .box{background-color:#0003;border-radius:.15rem;padding:.5rem}}aside#left nav{&>:not(:last-child){margin-bottom:.25rem}& div.stack{background-color:#0003;border-radius:.15rem;& input[type=checkbox]{display:none;&:checked~div.list{display:block}&:checked+label{color:#fff;background-color:#00000080}}& label{color:#000;cursor:pointer;border-radius:.15rem;align-items:center;padding:.5rem 1rem;text-decoration:none;transition:color,background-color .2s;display:flex;& img{height:18px;margin-right:.25rem}& span.text{width:100%;display:block}&:hover{color:#fff;background-color:#0000004d}& span.arrow{position:relative;top:5px}}& div.list{display:none;&>a{color:#000;border-radius:.15rem;width:100%;padding:.5rem 1rem .5rem 1.35rem;text-decoration:none;transition:color,background-color .2s;display:block;&:not(:last-child):before{content:"├";margin-right:.25rem;display:inline-block}&:last-child:before{content:"└";margin-right:.25rem;display:inline-block;position:relative;top:3px}&:hover{background-color:#0000004d}&.active{color:#fff;background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3}}}}&>a{color:#000;background-color:#0003;border-radius:.15rem;width:100%;padding:.5rem 1rem;text-decoration:none;transition:color,background-color .2s;display:block;&:has(img){align-items:center;display:flex;& img{height:18px;margin-right:.25rem}}&:hover,&.active{color:#fff}&:hover{background-color:#0000004d}&.active{color:#fff;background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3}}}footer{text-align:center;color:#666;justify-content:center;align-items:center;margin:1rem 0;padding:1rem;display:flex;&>p:not(:last-child){margin-right:2rem}}#character{&>.name{align-items:center;display:flex}&>div:not(:last-child){margin-bottom:.5rem}}span.badge{color:#111;background-color:#f7f8fa;border-radius:.25rem;padding:.1rem .25rem;font-size:10px;box-shadow:inset 0 0 0 1px #0000001a;&.dark{color:#fff;background-color:#444c55}&.green{background-color:#a6e3a1}}.char-meter{background-color:#000;border-radius:.1rem;min-width:100px;height:16px;position:relative;&>div{border-radius:.1rem;height:100%;overflow:hidden;&.hp{background-color:#e57373;background-image:linear-gradient(#ffffff26,#8b00001a);border:1px solid #c62828;border-color:#d32f2f #c62828 #b71c1c;box-shadow:inset 0 1px #fff3}&.mp{background-color:#5a9bd4;background-image:linear-gradient(#ffffff26,#3c64961a);border:1px solid #3a7a9c;border-color:#4a8ab0 #3a7a9c #2a6a88;box-shadow:inset 0 1px #fff3}&.tp{background-color:#f4cc67;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #aa8326;border-color:#c59f43 #aa8326 #957321;box-shadow:inset 0 1px #fff3}}}.tooltip{color:#fff;text-align:center;background-color:#000;border:1px solid #666;border-radius:.1rem;padding:.5rem;font-size:14px;position:absolute;box-shadow:0 0 .5rem .1rem #0003}.tooltip-trigger{width:100%;height:100%;position:absolute;top:0;left:0}.debug-query-log{color:#666;padding:1rem;font-family:monospace;font-size:14px;&:last-child{padding-top:0}}#center>section{&:not(:last-child){border-bottom:1px solid #0000001a;margin-bottom:1rem;padding-bottom:1rem}}h1:has(.badge),h2:has(.badge),h3:has(.badge),h4:has(.badge),h5:has(.badge),h6:has(.badge){align-items:center;display:flex;&>.badge{margin-left:.5rem}}.alert{color:#000000de;background:#f8f8f9;border-radius:.285714rem;justify-content:space-between;align-items:center;min-height:1rem;margin:1rem 0;padding:.5rem 1rem;line-height:1.4285rem;transition:opacity .1s,color .1s,background .1s,box-shadow .1s;display:flex;position:relative;box-shadow:inset 0 0 0 1px #22242638,0 0 #0000;&.success{color:#2c662d;background-color:#f0f9eb;border-color:#b3dc9d}&.danger{color:#9f3a38;background-color:#f9e9eb;border-color:#e0b4b4}&.warning{color:#573a08;background-color:#fff8e1;border-color:#f9e79f}&.info{color:#2c7fba;background-color:#f0f9fb;border-color:#b3d7f9}&.dark{color:#2c2c2c;background-color:#f0f0f0;border-color:#b3b3b3}& a[alert-close]{cursor:pointer;color:inherit;font-size:2rem;text-decoration:none}}a{color:#4c0515;text-decoration:none;transition:color .2s;&:hover{color:#6c0515;text-decoration:underline}}body::-webkit-scrollbar{width:.5rem}body::-webkit-scrollbar-track{background:#0000001a}body::-webkit-scrollbar-thumb{background-color:#444c55;background-image:linear-gradient(#ffffff26,#0000001a);border:1px solid #2f353b;border-color:#3d444c #2f353b #2c3137;box-shadow:inset 0 1px #fff3}#canvas-container{&>canvas{image-rendering:pixelated;image-rendering:crisp-edges;image-rendering:-webkit-optimize-contrast;width:100%;height:440px;display:block}}.char-icon{background-image:url(/assets/img/world/rogues.png);width:32px;height:32px;&.index-0{background-position:0 0}&.index-1{background-position:-32px 0}&.index-2{background-position:-64px 0}&.index-3{background-position:-96px 0}&.index-4{background-position:-128px 0}} \ No newline at end of file diff --git a/public/assets/css/src/main.css b/public/assets/css/src/main.css index eba40c1..b2facef 100644 --- a/public/assets/css/src/main.css +++ b/public/assets/css/src/main.css @@ -276,6 +276,7 @@ span.badge { } .tooltip { + position: absolute; background-color: black; color: white; border: 1px solid #666; diff --git a/public/assets/css/src/profile.css b/public/assets/css/src/profile.css index 06cd2d1..b8680e4 100644 --- a/public/assets/css/src/profile.css +++ b/public/assets/css/src/profile.css @@ -1,15 +1,14 @@ section.profile { header { text-align: center; - margin-bottom: 2rem; + margin-bottom: 3rem; h3 { - color: rgba(0, 0, 0, 0.3); - text-transform: uppercase; font-size: 1rem; } - h4 { + h5 { + color: rgba(0, 0, 0, 0.5); font-size: 0.75rem; } } @@ -22,7 +21,7 @@ section.profile { width: 50%; & > div:not(:last-child) { - margin-bottom: 1rem; + margin-bottom: 2rem; } } } @@ -31,8 +30,17 @@ section.profile { display: flex; align-items: center; justify-content: center; + padding-bottom: 1rem; + img { - max-width: 250px; + height: 185px; + width: 185px; + } + + .border { + width: 250px; + height: 250px; + position: absolute; } } @@ -67,4 +75,58 @@ section.profile { } } } + + #equipped-gear { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.5rem; + + div.item { + display: flex; + justify-content: center; + align-items: center; + + &.i-1x1 { + width: 30px; + height: 30px; + background-image: url('/assets/img/ui/1x1.png'); + } + + &.i-2x2 { + width: 60px; + height: 60px; + background-image: url('/assets/img/ui/2x2.png'); + } + + &.i-2x3 { + width: 60px; + height: 90px; + background-image: url('/assets/img/ui/2x3.png'); + } + } + + & > div { + display: flex; + gap: 0.5rem; + + & > div { + display: flex; + align-items: center; + justify-content: center; + width: 60px; + + &.top, &.bot { + width: 60px; + height: 60px; + } + + &.mid { + width: 60px; + height: 90px; + } + } + } + } } diff --git a/public/assets/img/ui/1x1.png b/public/assets/img/ui/1x1.png new file mode 100644 index 0000000..dd5e9f7 Binary files /dev/null and b/public/assets/img/ui/1x1.png differ diff --git a/public/assets/img/ui/2x2.png b/public/assets/img/ui/2x2.png new file mode 100644 index 0000000..7b3aedb Binary files /dev/null and b/public/assets/img/ui/2x2.png differ diff --git a/public/assets/img/ui/2x3.png b/public/assets/img/ui/2x3.png new file mode 100644 index 0000000..7c6d68f Binary files /dev/null and b/public/assets/img/ui/2x3.png differ diff --git a/public/assets/img/ui/borders/alchemist.webp b/public/assets/img/ui/borders/alchemist.webp new file mode 100644 index 0000000..34e2d82 Binary files /dev/null and b/public/assets/img/ui/borders/alchemist.webp differ diff --git a/public/assets/img/ui/borders/divine.webp b/public/assets/img/ui/borders/divine.webp new file mode 100644 index 0000000..332e2a8 Binary files /dev/null and b/public/assets/img/ui/borders/divine.webp differ diff --git a/public/assets/img/ui/borders/dragon_slaying.webp b/public/assets/img/ui/borders/dragon_slaying.webp new file mode 100644 index 0000000..f916a52 Binary files /dev/null and b/public/assets/img/ui/borders/dragon_slaying.webp differ diff --git a/public/assets/img/ui/borders/dwarven_smiths.webp b/public/assets/img/ui/borders/dwarven_smiths.webp new file mode 100644 index 0000000..5faf0c7 Binary files /dev/null and b/public/assets/img/ui/borders/dwarven_smiths.webp differ diff --git a/public/assets/img/ui/borders/earth.webp b/public/assets/img/ui/borders/earth.webp new file mode 100644 index 0000000..aae7ea6 Binary files /dev/null and b/public/assets/img/ui/borders/earth.webp differ diff --git a/public/assets/img/ui/borders/egyptian.webp b/public/assets/img/ui/borders/egyptian.webp new file mode 100644 index 0000000..61a0a99 Binary files /dev/null and b/public/assets/img/ui/borders/egyptian.webp differ diff --git a/public/assets/img/ui/borders/elven_archer.webp b/public/assets/img/ui/borders/elven_archer.webp new file mode 100644 index 0000000..f35a82c Binary files /dev/null and b/public/assets/img/ui/borders/elven_archer.webp differ diff --git a/public/assets/img/ui/borders/fire.webp b/public/assets/img/ui/borders/fire.webp new file mode 100644 index 0000000..1aa7e09 Binary files /dev/null and b/public/assets/img/ui/borders/fire.webp differ diff --git a/public/assets/img/ui/borders/gladiator.webp b/public/assets/img/ui/borders/gladiator.webp new file mode 100644 index 0000000..4c33ff5 Binary files /dev/null and b/public/assets/img/ui/borders/gladiator.webp differ diff --git a/public/assets/img/ui/borders/gold_mine.webp b/public/assets/img/ui/borders/gold_mine.webp new file mode 100644 index 0000000..04bfb15 Binary files /dev/null and b/public/assets/img/ui/borders/gold_mine.webp differ diff --git a/public/assets/img/ui/borders/golden.webp b/public/assets/img/ui/borders/golden.webp new file mode 100644 index 0000000..c55dfbf Binary files /dev/null and b/public/assets/img/ui/borders/golden.webp differ diff --git a/public/assets/img/ui/borders/ice.webp b/public/assets/img/ui/borders/ice.webp new file mode 100644 index 0000000..1bfa548 Binary files /dev/null and b/public/assets/img/ui/borders/ice.webp differ diff --git a/public/assets/img/ui/borders/knight.webp b/public/assets/img/ui/borders/knight.webp new file mode 100644 index 0000000..9911713 Binary files /dev/null and b/public/assets/img/ui/borders/knight.webp differ diff --git a/public/assets/img/ui/borders/mage.webp b/public/assets/img/ui/borders/mage.webp new file mode 100644 index 0000000..11612d5 Binary files /dev/null and b/public/assets/img/ui/borders/mage.webp differ diff --git a/public/assets/img/ui/borders/metal.webp b/public/assets/img/ui/borders/metal.webp new file mode 100644 index 0000000..2bb901d Binary files /dev/null and b/public/assets/img/ui/borders/metal.webp differ diff --git a/public/assets/img/ui/borders/necromancer.webp b/public/assets/img/ui/borders/necromancer.webp new file mode 100644 index 0000000..a24ad93 Binary files /dev/null and b/public/assets/img/ui/borders/necromancer.webp differ diff --git a/public/assets/img/ui/borders/orcs.webp b/public/assets/img/ui/borders/orcs.webp new file mode 100644 index 0000000..e27dac8 Binary files /dev/null and b/public/assets/img/ui/borders/orcs.webp differ diff --git a/public/assets/img/ui/borders/rock.webp b/public/assets/img/ui/borders/rock.webp new file mode 100644 index 0000000..8c83c05 Binary files /dev/null and b/public/assets/img/ui/borders/rock.webp differ diff --git a/public/assets/img/ui/borders/royal.webp b/public/assets/img/ui/borders/royal.webp new file mode 100644 index 0000000..c4682ad Binary files /dev/null and b/public/assets/img/ui/borders/royal.webp differ diff --git a/public/assets/img/ui/borders/rune.webp b/public/assets/img/ui/borders/rune.webp new file mode 100644 index 0000000..c84f506 Binary files /dev/null and b/public/assets/img/ui/borders/rune.webp differ diff --git a/public/assets/img/ui/borders/tavern.webp b/public/assets/img/ui/borders/tavern.webp new file mode 100644 index 0000000..291244a Binary files /dev/null and b/public/assets/img/ui/borders/tavern.webp differ diff --git a/public/assets/img/ui/borders/vikings.webp b/public/assets/img/ui/borders/vikings.webp new file mode 100644 index 0000000..b85e865 Binary files /dev/null and b/public/assets/img/ui/borders/vikings.webp differ diff --git a/public/assets/img/ui/borders/water.webp b/public/assets/img/ui/borders/water.webp new file mode 100644 index 0000000..93ea754 Binary files /dev/null and b/public/assets/img/ui/borders/water.webp differ diff --git a/public/assets/img/ui/borders/wind.webp b/public/assets/img/ui/borders/wind.webp new file mode 100644 index 0000000..fd1bd09 Binary files /dev/null and b/public/assets/img/ui/borders/wind.webp differ diff --git a/public/assets/img/ui/borders/wooden.webp b/public/assets/img/ui/borders/wooden.webp new file mode 100644 index 0000000..1ae5d9f Binary files /dev/null and b/public/assets/img/ui/borders/wooden.webp differ diff --git a/public/index.php b/public/index.php index 469e3d3..fcf7986 100644 --- a/public/index.php +++ b/public/index.php @@ -6,12 +6,12 @@ define('SRC', __DIR__ . '/../src'); require_once SRC . '/bootstrap.php'; -$r = []; +$r = new Router; /* Home */ -router_get($r, '/', function () { +$r->get('/', function () { if (user()) must_have_character(); $GLOBALS['active_nav_tab'] = 'home'; echo render('layouts/basic', ['view' => 'pages/home']); @@ -20,54 +20,54 @@ router_get($r, '/', function () { /* Auth */ -router_get($r, '/auth/register', 'auth_controller_register_get'); -router_post($r, '/auth/register', 'auth_controller_register_post'); -router_get($r, '/auth/login', 'auth_controller_login_get'); -router_post($r, '/auth/login', 'auth_controller_login_post'); -router_post($r, '/auth/logout', 'auth_controller_logout_post'); +$r->get('/auth/register', 'auth_controller_register_get'); +$r->post('/auth/register', 'auth_controller_register_post'); +$r->get('/auth/login', 'auth_controller_login_get'); +$r->post('/auth/login', 'auth_controller_login_post'); +$r->post('/auth/logout', 'auth_controller_logout_post'); /* Characters */ -router_get($r, '/characters', 'char_controller_list_get'); -router_post($r, '/characters', 'char_controller_list_post'); -router_get($r, '/character/create-first', 'char_controller_create_first_get'); -router_post($r, '/character/create', 'char_controller_create_post'); -router_post($r, '/character/delete', 'char_controller_delete_post'); +$r->get('/characters', 'char_controller_list_get'); +$r->post('/characters', 'char_controller_list_post'); +$r->get('/character/create-first', 'char_controller_create_first_get'); +$r->post('/character/create', 'char_controller_create_post'); +$r->post('/character/delete', 'char_controller_delete_post'); /* World */ -router_get($r, '/world', 'world_controller_get'); -router_post($r, '/move', 'world_controller_move_post'); +$r->get('/world', 'world_controller_get'); +$r->post('/move', 'world_controller_move_post'); /* Profile */ -router_get($r, '/profile', 'profile_controller_get'); -router_get($r, '/profile/:id', 'profile_controller_show_get'); +$r->get('/profile', 'profile_controller_get'); +$r->get('/profile/:id', 'profile_controller_show_get'); /* Settings */ -router_get($r, '/settings', 'settings_controller_get'); +$r->get('/settings', 'settings_controller_get'); /* Auctions */ -router_get($r, '/auctions', 'auctions_controller_get'); +$r->get('/auctions', 'auctions_controller_get'); /* Testing */ if (env('debug')) { - router_get($r, '/give_silver/:x', function (int $amt) { + $r->get('/give_silver/:x', function (int $amt) { auth_only_and_must_have_character(); wallet()->give(Currency::Silver, $amt); redirect('/'); }); - router_get($r, '/take_silver/:x', function (int $amt) { + $r->get('/take_silver/:x', function (int $amt) { auth_only_and_must_have_character(); wallet()->take(Currency::Silver, $amt); redirect('/'); @@ -79,11 +79,11 @@ if (env('debug')) { */ // [code, handler, params] stopwatch_start('router'); -$l = router_lookup($r, $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']); +$l = $r->lookup($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']); stopwatch_stop('router'); stopwatch_start('handler'); -if ($l['code'] !== 200) router_error($l['code']); +if ($l['code'] !== 200) error_response($l['code']); $l['handler'](...$l['params'] ?? []); stopwatch_stop('handler'); diff --git a/src/bootstrap.php b/src/bootstrap.php index e751483..ae90cde 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -25,12 +25,12 @@ require_once SRC . '/models/session.php'; require_once SRC . '/models/token.php'; // Controllers -require_once SRC . '/controller/char.php'; -require_once SRC . '/controller/auth.php'; -require_once SRC . '/controller/world.php'; -require_once SRC . '/controller/settings.php'; -require_once SRC . '/controller/auctions.php'; -require_once SRC . '/controller/profile.php'; +require_once SRC . '/controllers/char.php'; +require_once SRC . '/controllers/auth.php'; +require_once SRC . '/controllers/world.php'; +require_once SRC . '/controllers/settings.php'; +require_once SRC . '/controllers/auctions.php'; +require_once SRC . '/controllers/profile.php'; spl_autoload_register(function (string $class) { if (array_key_exists($class, CLASS_MAP)) require_once SRC . CLASS_MAP[$class]; diff --git a/src/controller/auctions.php b/src/controllers/auctions.php similarity index 100% rename from src/controller/auctions.php rename to src/controllers/auctions.php diff --git a/src/controller/auth.php b/src/controllers/auth.php similarity index 96% rename from src/controller/auth.php rename to src/controllers/auth.php index 5f6336f..4e445ce 100644 --- a/src/controller/auth.php +++ b/src/controllers/auth.php @@ -71,7 +71,7 @@ function auth_controller_register_post() exit; } - if (User::create($u, $e, $p) === false) router_error(400); + if (User::create($u, $e, $p) === false) error_response(400); $_SESSION['user'] = serialize(User::find($u)); Wallet::create(user()->id); @@ -128,7 +128,7 @@ function auth_controller_login_post() "INSERT INTO sessions (token, user_id, expires) VALUES (:t, :u, :e)", [':t' => $token, ':u' => user()->id, ':e' => $expires] ); - if (!$result) router_error(400); + if (!$result) error_response(400); set_cookie('remember_me', $token, $expires); } @@ -136,7 +136,7 @@ function auth_controller_login_post() redirect('/character/create-first'); } elseif (!change_user_character(user()->char_id)) { echo "failed to change user character (aclp)"; - router_error(999); + error_response(999); } redirect('/'); diff --git a/src/controller/char.php b/src/controllers/char.php similarity index 90% rename from src/controller/char.php rename to src/controllers/char.php index 9f0df5e..1c77a3a 100644 --- a/src/controller/char.php +++ b/src/controllers/char.php @@ -24,7 +24,7 @@ function char_controller_list_post() $action = $_POST['action'] ?? ''; // If the character ID is not a number, or the action is not a string, return a 400. - if (!is_numeric($char_id) || !is_string($action)) router_error(400); + if (!is_numeric($char_id) || !is_string($action)) error_response(400); // If the character ID is 0, return to the list. if ($char_id === 0) { @@ -33,7 +33,7 @@ function char_controller_list_post() } // If the action is not one of the allowed actions, return a 400. - if (!in_array($action, ['select', 'delete'])) router_error(400); + if (!in_array($action, ['select', 'delete'])) error_response(400); // If the action is to select a character, change the user's selected character. if ($action === 'select') { @@ -43,7 +43,7 @@ function char_controller_list_post() redirect('/characters'); } - if (!Character::belongs_to($char_id, user()->id)) router_error(999); + if (!Character::belongs_to($char_id, user()->id)) error_response(999); change_user_character($char_id); @@ -52,7 +52,7 @@ function char_controller_list_post() // If the action is to delete a character, move to the confirmation page. if ($action === 'delete') { - if (!Character::belongs_to($char_id, user()->id)) router_error(999); + if (!Character::belongs_to($char_id, user()->id)) error_response(999); echo page('chars/delete', ['char' => Character::find($char_id)]); exit; @@ -71,10 +71,10 @@ function char_controller_delete_post() $char_id = (int) ($_POST['char_id'] ?? 0); // If the character ID is not a number, return a 400. - if (!is_numeric($char_id)) router_error(400); + if (!is_numeric($char_id)) error_response(400); // Ensure the character ID is valid and belongs to the user. - if (!Character::belongs_to($char_id, user()->id)) router_error(999); + if (!Character::belongs_to($char_id, user()->id)) error_response(999); $char = Character::find($char_id); @@ -154,7 +154,7 @@ function char_controller_create_post() } } - if (($char = Character::create(user()->id, $name)) === false) router_error(400); + if (($char = Character::create(user()->id, $name)) === false) error_response(400); // Create the auxiliary tables $char->create_location(); diff --git a/src/controller/profile.php b/src/controllers/profile.php similarity index 86% rename from src/controller/profile.php rename to src/controllers/profile.php index cc3f07d..3497d13 100644 --- a/src/controller/profile.php +++ b/src/controllers/profile.php @@ -18,7 +18,7 @@ function profile_controller_show_get($id) { auth_only_and_must_have_character(); - if (($char = Character::find($id)) == false) router_error(999); + if (($char = Character::find($id)) == false) error_response(999); if (user()->char_id == $id) redirect('/profile'); echo page('profile/show', ['c' => $char]); } diff --git a/src/controller/settings.php b/src/controllers/settings.php similarity index 100% rename from src/controller/settings.php rename to src/controllers/settings.php diff --git a/src/controller/world.php b/src/controllers/world.php similarity index 98% rename from src/controller/world.php rename to src/controllers/world.php index 7567520..fb9a40d 100644 --- a/src/controller/world.php +++ b/src/controllers/world.php @@ -45,7 +45,7 @@ function world_controller_move_post() $x += directions[$d][0]; $y += directions[$d][1]; } else { - router_error(999); + error_response(999); } $r = db_query(db_live(), 'UPDATE char_locations SET x = :x, y = :y WHERE char_id = :c', [ diff --git a/src/helpers.php b/src/helpers.php index aec3f28..44119c1 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -75,7 +75,7 @@ function csrf_field() */ function csrf_ensure() { - if (!csrf_verify($_POST['csrf'] ?? '')) router_error(418); + if (!csrf_verify($_POST['csrf'] ?? '')) error_response(418); } /** @@ -121,8 +121,8 @@ function modify_user_session(string $field, mixed $value): bool function char(): Character|false { if (empty($_SESSION['user'])) return false; - if (empty($GLOBALS['char'])) $GLOBALS['char'] = serialize(user()->current_char()); - return unserialize($GLOBALS['char']); + if (empty($GLOBALS['char'])) $GLOBALS['char'] = user()->current_char(); + return $GLOBALS['char']; } /** @@ -133,7 +133,7 @@ function change_user_character(int $char_id): bool { // If the character does not exist, return false if (($char = Character::find($char_id)) === false) return false; - $GLOBALS['char'] = serialize($char); + $GLOBALS['char'] = $char; // If the character ID is different, update the session and database if (user()->char_id !== $char_id) { @@ -218,17 +218,17 @@ function stopwatch_stop($key) /** * Get the stopwatch value and format it to within 10 digits. */ -function stopwatch_get($key) +function stopwatch_get(string $key): string { - if (!env('debug', false)) return; - if (empty($GLOBALS['stopwatch'][$key])) return 0; + if (!env('debug', false)) return ''; + if (empty($GLOBALS['stopwatch'][$key])) return ''; return number_format($GLOBALS['stopwatch'][$key], 10); } /** * Conditional Echo; if the condition is true, echo the value. If the condition is false, echo the $or value. */ -function ce($condition, $value, $or = '') +function ce(bool $condition, mixed $value, mixed $or = ''): void { echo $condition ? $value : $or; } @@ -236,7 +236,7 @@ function ce($condition, $value, $or = '') /** * Get whether the request is an HTMX request. */ -function is_htmx() +function is_htmx(): bool { return isset($_SERVER['HTTP_HX_REQUEST']); } @@ -244,7 +244,7 @@ function is_htmx() /** * Get whether the request is an AJAX (fetch) request. */ -function is_ajax() +function is_ajax(): bool { return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'; } @@ -252,33 +252,50 @@ function is_ajax() /** * Limit a request to AJAX only. */ -function ajax_only() +function ajax_only(): void { - if (!is_ajax()) router_error(418); + if (!is_ajax()) error_response(418); } /** * Return a JSON response with the given data. */ -function json_response($data) +function json_response(mixed $data): void { header('Content-Type: application/json; charset=utf-8'); echo json_encode($data); exit; } +/** + * Handle an error by setting the response code and echoing an error message + */ +function error_response(int $code): void +{ + http_response_code($code); + echo match ($code) { + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 418 => 'I\'m a teapot', + 999 => 'Cheating attempt detected', + default => 'Unknown Error', + }; + exit; +} + /** * Return a title's [name, lore]. */ -function title($title_id) +function title(int $title_id): array|false { - return db_query(db_live(), 'SELECT * FROM titles WHERE id = :i', [':i' => $title_id])->fetchArray(); + return db_query(db_live(), 'SELECT * FROM titles WHERE id = ?', [$title_id])->fetchArray(); } /** * Abbreviate a number in text notation. */ -function abb_num($num) +function abb_num(int $num): string { $d = $num % 100 === 0 ? 0 : 1; return match(true) { diff --git a/src/models/character.php b/src/models/character.php index a1e60c6..ae4fc63 100644 --- a/src/models/character.php +++ b/src/models/character.php @@ -36,7 +36,15 @@ class Character public int $pre; // Precision public int $fer; // Ferocity public int $luck; // Luck + + /** + * Max number of slots this Character has in their inventory. + */ public int $inv_slots; + + /** + * Number of points available to spend on attributes. + */ public int $att_points; public string $bio; @@ -45,6 +53,11 @@ class Character */ private User $user; + /** + * The current title of the Character. + */ + private array $title; + public function __construct(array $data) { foreach ($data as $k => $v) { @@ -52,7 +65,10 @@ class Character } } - public static function find(int $id): Character|false + /** + * Find a Character by their name or ID. + */ + public static function find(int|string $id): Character|false { $q = db_query( db_live(), @@ -63,6 +79,10 @@ class Character return ($c = $q->fetchArray(SQLITE3_ASSOC)) === false ? false : new Character($c); } + /** + * Create a Character in the database using a User ID and a name. Pass optional overrides to adjust the + * defaults for other fields. + */ public static function create(int $user_id, string $name, array $overrides = []): Character|false { // Prep the data and merge in any overrides @@ -254,12 +274,14 @@ class Character */ public function title(): array|false { + if (isset($this->title)) return $this->title; + $t = title($this->title_id); $q = db_query( db_live(), - 'SELECT awarded FROM owned_titles WHERE char_id = :c AND title_id = :t LIMIT 1', - [':c' => $this->id, ':t' => $this->title_id] + 'SELECT awarded FROM owned_titles WHERE char_id = ? AND title_id = ? LIMIT 1', + [$this->id, $this->title_id] ); if ($q === false) throw new Exception('Failed to query title. (C::t)'); @@ -267,6 +289,6 @@ class Character if ($a === false) return false; $t['awarded'] = $a['awarded']; // add the awarded date to the title info - return $t; + return $this->title = $t; } } diff --git a/src/util/components.php b/src/util/components.php index 98dbd35..9b2cccc 100644 --- a/src/util/components.php +++ b/src/util/components.php @@ -3,7 +3,7 @@ /** * Render the logout button's form. */ -function c_logout_button() +function c_logout_button(): string { return render('components/logout_button'); } @@ -12,7 +12,7 @@ function c_logout_button() * Render the character bar. Relies on there being a character in the session. Without one, this will return an empty * string. */ -function c_char_bar() +function c_char_bar(): string { if (char() === false) return ''; return render('components/char_bar', ['char' => char()]); @@ -22,7 +22,7 @@ function c_char_bar() * Render the left sidebar navigation menu. Provide the active tab to highlight it. Will retrieve the current * tab from the GLOBALS. */ -function c_left_nav() +function c_left_nav(): string { return render('components/left_nav'); } @@ -30,7 +30,7 @@ function c_left_nav() /** * Render the right sidebar menu. */ -function c_right_nav() +function c_right_nav(): string { return render('components/right_nav', ['c' => char()]); } @@ -38,7 +38,7 @@ function c_right_nav() /** * Render the debug query log. */ -function c_debug_query_log() +function c_debug_query_log(): string { return render('components/debug_query_log'); } @@ -46,15 +46,15 @@ function c_debug_query_log() /** * Render the character select radio buttons. */ -function c_char_select_box($id, $char) +function c_char_select_box(int $id, array $info): string { - return render('components/char_select_box', ['id' => $id, 'char' => $char]); + return render('components/char_select_box', ['id' => $id, 'c' => $info]); } /** * Render an alert with a given type and message. */ -function c_alert($type, $message) +function c_alert(string $type, string $message): string { $a = $type !== 'danger' ? ' auto-close="5000"' : ''; return "