From c7936121b23a77a0b7269d2b319ec0b9f5560bb0 Mon Sep 17 00:00:00 2001 From: xangelo Date: Tue, 15 Aug 2023 14:31:53 -0400 Subject: [PATCH] feat: new UI This is a huge overhaul of the existing UI away from the temp white boxes setup to something that embodies the game a bit more. No functionality has changed, but there's been a ton of CSS updates to ensure that we keep load times short but still provide a good looking experience to players. --- public/assets/css/game.css | 203 ++++++++++++++++++++++----- public/assets/font/BreatheFire.woff | Bin 0 -> 11156 bytes public/assets/font/BreatheFire.woff2 | Bin 0 -> 8560 bytes public/assets/font/demo.html | 192 +++++++++++++++++++++++++ public/assets/font/stylesheet.css | 9 ++ public/index.html | 11 +- src/server/api.ts | 33 +++-- src/server/locations/healer/index.ts | 33 +++-- src/server/map.ts | 52 ++++--- src/server/monster.ts | 11 ++ src/server/views/fight.ts | 60 ++++++-- src/server/views/inventory.ts | 10 +- src/server/views/map.ts | 6 +- src/server/views/monster-selector.ts | 25 +++- src/server/views/player-bar.ts | 27 ++-- src/server/views/stores.ts | 12 +- src/server/views/travel.ts | 7 +- src/shared/map.ts | 7 + src/shared/travel.ts | 5 + 19 files changed, 574 insertions(+), 129 deletions(-) create mode 100644 public/assets/font/BreatheFire.woff create mode 100644 public/assets/font/BreatheFire.woff2 create mode 100644 public/assets/font/demo.html create mode 100644 public/assets/font/stylesheet.css diff --git a/public/assets/css/game.css b/public/assets/css/game.css index 8f546dc..b29a4ec 100644 --- a/public/assets/css/game.css +++ b/public/assets/css/game.css @@ -1,3 +1,12 @@ +@font-face { + font-family: 'Breathe Fire'; + src: url('/assets/font/BreatheFire.woff2') format('woff2'), + url('/assets/font/BreatheFire.woff') format('woff'); + font-weight: normal; + font-style: normal; + font-display: swap; +} + body { margin: 1rem auto 2rem; background-color: #eee; @@ -5,35 +14,48 @@ body { max-width: 724px; height: 100vh; } -#time-of-day { +.title-font { + font-family: 'Breathe Fire', monospace; +} +#title-bar { background-color: transparent; - color: invert; + margin-top: 0.5rem; + margin-bottom: 1.5rem; border: 0; - margin-bottom: 1rem; + display: flex; + justify-content: space-between; + align-items: center; +} +#title-bar a { + font-size: 3rem; + color: #8e4607; + text-decoration: none; + letter-spacing: 0.3rem; + mix-blend-mode: color-burn; + border-bottom: solid 4px; + line-height: 25px; +} +#time-of-day { text-align: right; } #time-of-day img { width: 32px; vertical-align: middle; -} -.night #time-of-day, .evening #time-of-day { - color: #fff; + mix-blend-mode: color-burn; } #view { font-size: 14px; - background-color: #fff; padding: 1rem; border: 1px solid #000; -} -:disabled { - background-color: #aaa; - cursor: not-allowed; + box-shadow: 2px 3px 20px black, 0 0 60px #8a4d0f inset; + background: #fffef0; + background-image: url(data:image/png;base64,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); } b { font-weight: bold; } a { - color: blue; + color: #a20b00; } select { padding: 0.3rem; @@ -42,11 +64,37 @@ input { border: 1px solid #000; } button { - background-color: #fff; - border: 1px solid #000; - padding: 0.3rem; cursor: pointer; - color: #000; + color: #fff; + background: url(data:image/png;base64,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), linear-gradient(to bottom, #D4AF37 0%, #C5A028 100%); + box-shadow: inset 0px 0px 1px 2px rgba(255, 255, 255, 0.3); + padding: 0.5rem 1rem; + font-weight: bold; + text-shadow: -1px -1px 0px rgba(0, 0, 0, 0.3); + border: solid 1px #6d251c; +} +button.red { + background: #a20b00; +} +button.red:hover { + background: #b20b00; +} +button.green { + background: #0a0; +} +button.green:hover { + background: #0b0; +} +button:active { + position: relative; + top: 1px; +} +button:disabled, button:disabled:hover { + background: #aaa; + cursor: not-allowed; +} +button:focus { + outline: none; } .hidden { display: none !important; @@ -58,11 +106,6 @@ p:last-child { margin-bottom: 0; } -section { - border: 1px solid #000; - background-color: #fff; -} - #announcements, #signup-prompt { padding: 1rem; line-height: 1.2rem; @@ -137,6 +180,7 @@ dialog .close-modal { } #avatar { width: 100%; + border: solid 1px #6d251c; } header { display: flex; @@ -158,7 +202,7 @@ header { } #stat-bars, #defender-stat-bars { width: 100%; - margin: 5px 5px 0 5px; + margin: 0 5px; } #stat-bars .progress-bar, #defender-stat-bars .progress-bar { margin-bottom: 2px; @@ -184,7 +228,7 @@ header { } .progress-bar { - border: solid 1px #000; + border: solid 1px #6d251c; width: 100%; font-size: 0.7rem; text-align: center; @@ -215,16 +259,14 @@ nav a.active { text-decoration: underline; } nav.filter { - margin: 0.5rem 0; + margin: 0; text-align: right; border: 0; padding: 0; position: relative; - top: 1px; + bottom: 5px; } nav.filter a { - border: solid 1px #ddd; - background-color: #ddd; border-bottom-width: 0; z-index: 1; padding: 0.6rem; @@ -232,13 +274,15 @@ nav.filter a { } nav.filter a.active { background-color: #fff; - border-color: #000; + border: solid #6d251c; + border-width: 1px 1px 0; z-index: 4; } .filter-container .listing { - border: solid 1px #000; + border: solid 1px #6d251c; z-index: 2; position: relative; + background-color: #fff; } nav.filter-result { display: none; @@ -316,25 +360,59 @@ nav.filter-result.active { } #main-nav section { min-height: 344px; - border: 0; + padding: 1rem; } #stat-breakdown th { font-weight: bold; text-align: right; - background-color: #ddd; + background-color: #6d251c; + color: #fff; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==); } #stat-breakdown th, #stat-breakdown td { - padding: 0.3rem 0.5rem; + padding: 0.5rem; } #explore { text-align: center; background-repeat: no-repeat; - background-position: bottom right; background-size: cover; - padding: 3rem 3rem 2rem; + padding: 2rem 0rem 2rem !important; line-height: 1.3rem; + border: solid 1px #6d251c; +} + +.city-title-wrapper { + filter: drop-shadow(0 0 10px black); + position: relative; + z-index: 1; +} +.city-title:before { + position: absolute; + content: ' '; + z-index: 1; + top: 2px; + left: 2px; + right: 2px; + bottom: 2px; + background: transparent; + border: solid 2px #ffa500; + clip-path: polygon(100% 0, 95% 50%, 100% 98%, 0% 100%, 5% 50%, 0 0); +} +.city-title { + font-family: 'Breathe Fire', monospace; + font-size: 1.5rem; + letter-spacing: 1rem; + display: inline-block; + padding: 0.5rem 0.5rem 0.5rem 1.5rem; + color: #fff; + border: inset 3px rgba(88, 15, 15, 0.4); + text-shadow: 1px -1px 0px #522626; + background: #bc3915 url(data:image/png;base64,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); + position: relative; + clip-path: polygon(100% 0, 95% 50%, 100% 98%, 0% 100%, 5% 50%, 0 0); + box-shadow: 0 0 4px 4px black; } #fight-container { @@ -361,10 +439,25 @@ nav.filter-result.active { background: linear-gradient(to bottom, rgba(255,255,255, 0) 0%, rgba(255, 255, 255, 0.5) 30%); } .city-details { + position: relative; + padding: 1rem 1px 2rem; + margin: 0 auto; + width: 80%; + background-image: url(data:image/png;base64,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); + background-color: #f7f4dd; + box-shadow: 0 0 10px black; + position: relative; + top: -13px; + border: solid 1px #6d251c; +} +.flex { display: flex; - justify-content: space-between; + justify-content: space-around; flex-wrap: wrap; } +.city-details.flex > div { + margin: 1rem; +} h1 { font-size: 1.5rem; font-weight: bold; @@ -381,6 +474,9 @@ h3 { font-size: 1rem; } +#travelling { + padding-top: 2rem; +} #travelling-actions { display: flex; justify-content: center; @@ -389,6 +485,28 @@ h3 { } +#explore .shop-inventory-listing { + margin: 2rem auto 1rem; + width: 90%; +} +.location-name { + display: flex; + align-items: center; + justify-content: center; +} +.location-name span { + color: #846945; + text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.7); + margin: 0 1rem; +} +.location-name:before, .location-name:after { + background-color: #846945; + height: 2px; + flex: 1; + content: ' '; + width: 4rem; + drop-shadow: 1px 1px 0px rgba(255, 255, 255, 0.7); +} .shop-inventory-listing .listing { background-color: #fff; } @@ -425,6 +543,7 @@ h3 { } .store-actions button { width: 75px; + padding: 0.3rem 0.5rem; } #inventory-page { @@ -445,7 +564,7 @@ h3 { max-height: 64px; width: 64px; height: 64px; - border: solid 1px #000; + border: solid 1px #6d251c; padding: 0; text-align: center; vertical-align: bottom; @@ -505,13 +624,16 @@ h3 { } +#chat { + border: solid 1px #6d251c; +} .chat-message { line-height: 1.2rem; margin-bottom: 0.3rem; padding: 0.3rem; } .chat-message:nth-child(even) { - background-color: #eee; + background: linear-gradient(270deg, rgba(0, 0, 0, 0) 0, rgba(196, 177, 149, 0.8) 100%); } .chat-message .from { @@ -527,8 +649,8 @@ h3 { flex-grow: 8; padding: 0.3rem; outline: none; - border-left-width: 0px; - border-bottom-width: 0px; + border-width: 1px 0 0; + background: transparent; } #chat-form input:focus { outline: none; @@ -538,6 +660,9 @@ h3 { border-bottom-width: 0px; font-weight: bold; } +#chat-form button:active { + top: 0; +} #game-footer { display: flex; diff --git a/public/assets/font/BreatheFire.woff b/public/assets/font/BreatheFire.woff new file mode 100644 index 0000000000000000000000000000000000000000..cf58d3ecdc8c88e6dd0166730a31f0807c66f801 GIT binary patch literal 11156 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
VOE!nYXDXSO}Yw^jW7 z*_3A;%hD0G0Qpkm+Go zi;Mj38g)J&fRV`UhPe;a$R`Pbpy+d78)qOy6 zsYml?;omiufrj};+kY}8L1O2O$b>AmeKYbJIAL7KyJqLTmnh-_%==!nE#$J@n(oHO zi#ohL4mSc&H{yX2^1s`A><%K2d}v-^wG(~gfECON5p>I2d6nxY)pVNTDy@s5>JFvl znI&`?6-Ts1rI{)AQjhk?al z>E7M}>jtxceWw??QG4U!mp*}cD|n0JNwN`D`ef5-NeY+wF^#Kzz3i^Y6oQ)7&hzNh z5SQrJR;Z_XCXMxo_1gzgoZlLgI8)+pVu9Xb!bt=b9<$L#pc5QW2HfcixyAreWj&a5 zhuq^WnP`HHHUr%dz@0cF!LVxt1_eVeNSPr=X}!iPlwRH?5j_+)&V`PIR#5O4v>*KN zw4`&^QkCRxYsuW_W0_6vC9X_`w`@hYWP;L%W_-+|0;tzKLt3sEq2Bl>PGz9uEWk4i zpM^*O#Ea7rD4)6TR+=xj=oT?Mb@1G^WjvtQPxx*4X>oZ65t%-#TM1_^|1zwH(A%ft zZ>28y|L?7gDOB|=d3^ceJP>T;JAxEyCwwq=D54Ma%od5s; literal 0 HcmV?d00001 diff --git a/public/assets/font/BreatheFire.woff2 b/public/assets/font/BreatheFire.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..c05796908c2fcba537b2c4e7ca9bd922c52271ff GIT binary patch literal 8560 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 literal 0 HcmV?d00001 diff --git a/public/assets/font/demo.html b/public/assets/font/demo.html new file mode 100644 index 0000000..118cce6 --- /dev/null +++ b/public/assets/font/demo.html @@ -0,0 +1,192 @@ + + + + + + + + + Transfonter demo + + + + +
+
+

Breathe Fire

+
.your-style {
+    font-family: 'Breathe Fire';
+    font-weight: normal;
+    font-style: normal;
+}
+
+<link rel="preload" href="BreatheFire.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+ + diff --git a/public/assets/font/stylesheet.css b/public/assets/font/stylesheet.css new file mode 100644 index 0000000..b4c1247 --- /dev/null +++ b/public/assets/font/stylesheet.css @@ -0,0 +1,9 @@ +@font-face { + font-family: 'Breathe Fire'; + src: url('BreatheFire.woff2') format('woff2'), + url('BreatheFire.woff') format('woff'); + font-weight: normal; + font-style: normal; + font-display: swap; +} + diff --git a/public/index.html b/public/index.html index 5657a72..e2efaa0 100644 --- a/public/index.html +++ b/public/index.html @@ -4,8 +4,8 @@ Rising Legends +Dawn icons created by Smashicons - Flaticon +--> @@ -14,8 +14,12 @@ -
+
+ Rising Legends +
+
+
@@ -76,7 +80,6 @@
...Loading
-
diff --git a/src/server/api.ts b/src/server/api.ts index f93fc3f..35ba7e3 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -12,7 +12,7 @@ import { loadPlayer, createPlayer, updatePlayer, movePlayer } from './player'; import { random, sample } from 'lodash'; import {broadcastMessage, Message} from '../shared/message'; import {expToLevel, maxHp, Player} from '../shared/player'; -import {clearFight, createFight, getMonsterList, getRandomMonster, loadMonster, loadMonsterFromFight, loadMonsterWithFaction, saveFightState} from './monster'; +import {clearFight, createFight, getMonsterList, getMonsterLocation, getRandomMonster, loadMonster, loadMonsterFromFight, loadMonsterWithFaction, saveFightState} from './monster'; import {FightRound} from '../shared/fight'; import {addInventoryItem, deleteInventoryItem, getEquippedItems, getInventory, getInventoryItem, updateAp} from './inventory'; import { getItemFromPlayer, getItemFromShop, getPlayersItems, getShopItems, givePlayerItem, updateItemCount } from './items'; @@ -35,8 +35,8 @@ import { renderMap } from './views/map'; import { renderProfilePage } from './views/profile'; import { renderSkills } from './views/skills'; import { renderInventoryPage } from './views/inventory'; -import { renderMonsterSelector } from './views/monster-selector'; -import { renderFight, renderRoundDetails } from './views/fight'; +import { renderMonsterSelector, renderOnlyMonsterSelector } from './views/monster-selector'; +import { renderFight, renderFightPreRound, renderRoundDetails } from './views/fight'; import { renderTravel, travelButton } from './views/travel'; import { renderChatMessage } from './views/chat'; @@ -547,8 +547,9 @@ app.get('/player/explore', authEndpoint, async (req: AuthRequest, res: Response) level: fight.level, fight_trigger: fight.fight_trigger }; + const location = await getMonsterLocation(fight.ref_id); - res.send(renderPlayerBar(req.player, equippedItems) + renderFight(data)); + res.send(renderPlayerBar(req.player, equippedItems) + renderFightPreRound(data, true, location)); } else { const travelPlan = await getTravelPlan(req.player.id); @@ -572,7 +573,8 @@ app.get('/player/explore', authEndpoint, async (req: AuthRequest, res: Response) things, nextAction, closestTown: closest, - walkingText: '' + walkingText: '', + travelPlan })); } else { @@ -691,7 +693,7 @@ app.get('/location/:location_id/items/:item_id/overview', authEndpoint, async (r
- +
@@ -764,7 +766,7 @@ app.get('/modal/items/:item_id', authEndpoint, async (req: AuthRequest, res: Res
- +
@@ -782,10 +784,10 @@ app.get('/city/stores/city:stores/:location_id', authEndpoint, async (req: AuthR } const [shopEquipment, shopItems] = await Promise.all([ listShopItems({location_id: location.id}), - getShopItems(location.id) + getShopItems(location.id), ]); - const html = await renderStore(shopEquipment, shopItems, req.player); + const html = await renderStore(shopEquipment, shopItems, req.player, location); res.send(html); }); @@ -799,7 +801,7 @@ app.get('/city/explore/city:explore/:location_id', authEndpoint, async (req: Aut } const monsters: Monster[] = await getMonsterList(location.id); - res.send(renderMonsterSelector(monsters)); + res.send(renderOnlyMonsterSelector(monsters, 0, location)); }); app.post('/travel', authEndpoint, async (req: AuthRequest, res: Response) => { @@ -879,6 +881,7 @@ app.post('/fight', authEndpoint, async (req: AuthRequest, res: Response) => { } const fight = await createFight(req.player.id, monster, fightTrigger); + const location = await getService(monster.location_id); const data: MonsterForFight = { @@ -890,7 +893,7 @@ app.post('/fight', authEndpoint, async (req: AuthRequest, res: Response) => { fight_trigger: fight.fight_trigger }; - res.send(renderFight(data)); + res.send(renderFightPreRound(data, true, location)); }); app.post('/travel/step', authEndpoint, async (req: AuthRequest, res: Response) => { @@ -953,7 +956,8 @@ app.post('/travel/step', authEndpoint, async (req: AuthRequest, res: Response) = things, nextAction, closestTown: closest, - walkingText: sample(walkingText) + walkingText: sample(walkingText), + travelPlan })); } @@ -973,13 +977,14 @@ app.post('/travel/:destination_id', authEndpoint, async (req: AuthRequest, res: return; } - await travel(req.player, destination.id); + const travelPlan = await travel(req.player, destination.id); res.send(renderTravel({ things: [], nextAction: 0, walkingText: '', - closestTown: req.player.city_id + closestTown: req.player.city_id, + travelPlan })); }); diff --git a/src/server/locations/healer/index.ts b/src/server/locations/healer/index.ts index 46877b2..3504d2a 100644 --- a/src/server/locations/healer/index.ts +++ b/src/server/locations/healer/index.ts @@ -8,6 +8,7 @@ import { sample } from 'lodash'; import { City, Location } from "../../../shared/map"; import { renderPlayerBar } from "../../views/player-bar"; import { getEquippedItems } from "../../inventory"; +import { EquippedItemDetails } from "../../../shared/equipped"; export const router = Router(); @@ -102,10 +103,8 @@ router.get('/city/services/city:services:healer/:location_id', authEndpoint, asy const text: string[] = []; - text.push(`

${service.name}

`); text.push(`

"${getText('intro', service, city)}"

`); - if(req.player.hp === maxHp(req.player.constitution, req.player.level)) { text.push(`

You're already at full health?

`); } @@ -120,7 +119,17 @@ router.get('/city/services/city:services:healer/:location_id', authEndpoint, asy } - res.send(`
${text.join("\n")}
`); + res.send(` +
${service.city_name}
+
+

${service.name}

+
+${text.join("\n")} +
+
+ `); + + //res.send(`
${text.join("\n")}
`); }); @@ -135,23 +144,31 @@ router.post('/city/services/city:services:healer:heal/:location_id', authEndpoin } const text: string[] = []; - text.push(`

${service.name}

`); - const cost = req.player.gold <= (healCost * 2) ? 0 : healCost; + let inventory: EquippedItemDetails[]; if(req.player.gold < cost) { text.push(`

${getText('insufficient_money', service, city)}

`) - res.send(`
${text.join("\n")}
`); } else { req.player.hp = maxHp(req.player.constitution, req.player.level); req.player.gold -= cost; await updatePlayer(req.player); - const inventory = await getEquippedItems(req.player.id); + inventory = await getEquippedItems(req.player.id); text.push(`

${getText('heal_successful', service, city)}

`); text.push('

'); - res.send(`
${text.join("\n")}
` + renderPlayerBar(req.player, inventory)); } + + res.send(` +
${service.city_name}
+
+

${service.name}

+
+${text.join("\n")} +
+
+${inventory ? renderPlayerBar(req.player, inventory) : ''} +`); }); diff --git a/src/server/map.ts b/src/server/map.ts index 54f0de8..df70a38 100644 --- a/src/server/map.ts +++ b/src/server/map.ts @@ -1,6 +1,6 @@ -import { City, Location, Path } from "../shared/map"; +import { City, Location, LocationWithCity, Path } from "../shared/map"; import type { Player } from '../shared/player'; -import type { Travel } from '../shared/travel'; +import type { Travel, TravelWithNames } from '../shared/travel'; import { db } from './lib/db'; import { random } from 'lodash'; @@ -12,10 +12,11 @@ export async function getAllServices(city_id: number): Promise { .orderBy('display_order'); } -export async function getService(location_id: number): Promise { - return db.select('*').first().from('locations').where({ - id: location_id - }); +export async function getService(location_id: number): Promise { + return db.select(['locations.*', 'cities.name as city_name']). + from('locations').join('cities', 'locations.city_id', '=', 'cities.id').where({ + 'locations.id': location_id + }).first(); } export async function getAllPaths(city_id: number): Promise { @@ -42,7 +43,7 @@ export async function getCityDetails(city_id: number): Promise { return db.first().select('*').from('cities').where({id: city_id}); } -export async function travel(player: Player, dest_id: number): Promise { +export async function travel(player: Player, dest_id: number): Promise { const city = await getCityDetails(dest_id); const path = await db.first().select('*').from('paths').where({ starting_city: player.city_id, @@ -63,25 +64,15 @@ export async function travel(player: Player, dest_id: number): Promise { source_id: player.city_id, destination_id: dest_id, total_distance: steps - }).returning('*'); - - if(rows.length !== 1) { - console.log(rows); - throw new Error('Unexpected response when creating travel'); - } - - return rows[0] as Travel; + }); + + return getTravelPlan(player.id); } -export async function stepForward(player_id: string): Promise { - const rows = await db('travel').increment('current_position').returning('*'); +export async function stepForward(player_id: string): Promise { + await db('travel').increment('current_position'); - if(rows.length !== 1) { - console.log(rows); - throw new Error('Unexpected response when moving'); - } - - return rows[0] as Travel; + return getTravelPlan(player_id); } export async function clearTravelPlan(player_id: string): Promise { @@ -98,8 +89,15 @@ export async function completeTravel(player_id: string): Promise { return rows[0] as Travel; } -export async function getTravelPlan(player_id: string): Promise { - return db.select('*').first().from('travel').where({ - player_id - }); +export async function getTravelPlan(player_id: string): Promise { + return db.select([ + 'travel.*', + 'source.name as source_city_name', + 'destination.name as destination_city_name' + ]).from('travel') + .join('cities as source', 'travel.source_id', '=', 'source.id') + .join('cities as destination', 'travel.destination_id', '=', 'destination.id') + .where({ + 'travel.player_id': player_id + }).first(); } diff --git a/src/server/monster.ts b/src/server/monster.ts index 7565146..e58c814 100644 --- a/src/server/monster.ts +++ b/src/server/monster.ts @@ -1,6 +1,7 @@ import { db } from './lib/db'; import { Fight, Monster, MonsterWithFaction, MonsterForList, FightTrigger } from '../shared/monsters'; import { TimePeriod, TimeManager } from '../shared/time'; +import { LocationWithCity } from 'shared/map'; const time = new TimeManager(); @@ -84,6 +85,16 @@ export async function createFight(playerId: string, monster: Monster, fightTrigg return res.pop(); } +export async function getMonsterLocation(monsterId: number): Promise { +return db.select(['locations.*', 'cities.name as city_name']) + .from('monsters') + .join('locations', 'monsters.location_id', '=', 'locations.id') + .join('cities', 'cities.id', '=', 'locations.city_id') + .where({ + 'monsters.id': monsterId + }).first(); +} + /** * Given a list of cities, it will return a monster that * exists in any of the exploration zones with every monster diff --git a/src/server/views/fight.ts b/src/server/views/fight.ts index b6ed6dc..1995d9a 100644 --- a/src/server/views/fight.ts +++ b/src/server/views/fight.ts @@ -1,4 +1,5 @@ import { FightRound } from "shared/fight"; +import { LocationWithCity } from "shared/map"; import { MonsterForFight } from "../../shared/monsters"; export function renderRoundDetails(roundData: FightRound): string { @@ -8,13 +9,13 @@ export function renderRoundDetails(roundData: FightRound): string { case 'player': html.push(`
You defeated the ${roundData.monster.name}!
`); if(roundData.rewards.gold) { - html.push(`
You gained ${roundData.rewards.gold} gold`); + html.push(`
You gained ${roundData.rewards.gold} gold
`); } if(roundData.rewards.exp) { - html.push(`
You gained ${roundData.rewards.exp} exp`); + html.push(`
You gained ${roundData.rewards.exp} exp
`); } if(roundData.rewards.levelIncrease) { - html.push(`
You gained a level! ${roundData.player.level}`); + html.push(`
You gained a level! ${roundData.player.level}
`); } break; case 'monster': @@ -34,7 +35,8 @@ export function renderRoundDetails(roundData: FightRound): string { export function renderFight(monster: MonsterForFight, results: string = '', displayFightActions: boolean = true) { const hpPercent = Math.floor((monster.hp / monster.maxHp) * 100); - let html = `
+ let html = ` +
@@ -53,15 +55,57 @@ export function renderFight(monster: MonsterForFight, results: string = '', disp - - + + `: ''}
-
${results}
-
`; +
+
`; + + return html; +} + +export function renderFightPreRound(monster: MonsterForFight, displayFightActions: boolean = true, location: LocationWithCity) { + const hpPercent = Math.floor((monster.hp / monster.maxHp) * 100); + + let html = ` +
+
${location.city_name}
+
+
+

${location.name}

+ +
+
+
+ +
+
+
${monster.name}
+
${hpPercent}% - ${monster.hp} / ${monster.maxHp}
+
+
+
+ ${displayFightActions ? ` +
+ + + + +
+ `: ''} +
+
+
+
`; return html; } diff --git a/src/server/views/inventory.ts b/src/server/views/inventory.ts index 3ad3e02..c027dd0 100644 --- a/src/server/views/inventory.ts +++ b/src/server/views/inventory.ts @@ -1,16 +1,16 @@ -import { EquipmentSlot, InventoryType } from "shared/inventory"; +import { EquipmentSlot } from "shared/inventory"; import { EquippedItemDetails } from "../../shared/equipped"; import { PlayerItem } from "../../shared/items"; import { capitalize } from "lodash"; function icon(icon_name?: string): string { - const icon = icon_name ? `/assets/img/icons/equipment/${icon_name}` : 'https://via.placeholder.com/64x64'; + const placeholder = 'https://placehold.co/64x64/af936c/6d5f4d'; + const icon = icon_name ? `/assets/img/icons/equipment/${icon_name}` : placeholder; return icon; } function renderEquipmentPlacementGrid(items: EquippedItemDetails[]) { - const placeholder = 'https://via.placeholder.com/64x64'; // @ts-ignore const map: Record = items.filter(item => item.is_equipped).reduce((acc, item) => { acc[item.equipment_slot] = item; @@ -94,7 +94,7 @@ function generateProgressBar(current: number, max: number, color: string, displa function renderInventoryItem(item: EquippedItemDetails , action: (item: EquippedItemDetails) => string): string { return `
- +
${item.name}
@@ -127,7 +127,7 @@ function renderInventorySection(inventory: EquippedItemDetails[]): string { return inventory.map(item => { return renderInventoryItem(item, item => { if(item.is_equipped) { - return ``; + return ``; } else { if(item.equipment_slot === 'ANY_HAND') { diff --git a/src/server/views/map.ts b/src/server/views/map.ts index 6f53fea..76a546b 100644 --- a/src/server/views/map.ts +++ b/src/server/views/map.ts @@ -17,9 +17,9 @@ export async function renderMap(data: { city: City, locations: Location[], paths }); let html = ` -
-

${data.city.name}

-
`; +
+
${data.city.name}
+
`; if(servicesParsed.SERVICES.length) { html += `

Services

${servicesParsed.SERVICES.join("
")}
` diff --git a/src/server/views/monster-selector.ts b/src/server/views/monster-selector.ts index 8acb82f..24140c6 100644 --- a/src/server/views/monster-selector.ts +++ b/src/server/views/monster-selector.ts @@ -1,13 +1,30 @@ +import { LocationWithCity } from "../../shared/map"; import { Monster, MonsterForFight } from "../../shared/monsters"; -export function renderMonsterSelector(monsters: Monster[] | MonsterForFight[], activeMonsterId: number = 0): string { - let html = `
+export function renderOnlyMonsterSelector(monsters: Monster[] | MonsterForFight[], activeMonsterId: number = 0, location?: LocationWithCity): string { + let html = ` +
+
${location?.city_name}
+
+
+

${location?.name}

+ ${renderMonsterSelector(monsters, activeMonsterId, location)} +
+`; + + return html; +} + +export function renderMonsterSelector(monsters: Monster[] | MonsterForFight[], activeMonsterId: number = 0, location?: LocationWithCity): string { + let html = ` +
`; +
+`; return html; } diff --git a/src/server/views/player-bar.ts b/src/server/views/player-bar.ts index 9ca454b..3b57789 100644 --- a/src/server/views/player-bar.ts +++ b/src/server/views/player-bar.ts @@ -23,13 +23,13 @@ function displayLoginSignupForm(): string { } -function generateProgressBar(current: number, max: number, color: string, displayPercent: boolean = true): string { +function generateProgressBar(current: number, max: number, opts: ProgressBarOptions): string { let percent = 0; if(max > 0) { percent = Math.floor((current / max) * 100); } - const display = `${displayPercent? `${percent}% - `: ''}`; - return `
${display}${current}/${max}
`; + const display = `${percent}% - `; + return `
${display}${current}/${max}
`; } function calcAp(inventoryItem: EquippedItemDetails[]): string { @@ -46,30 +46,35 @@ function calcAp(inventoryItem: EquippedItemDetails[]): string { return `
- ${generateProgressBar(ap.HEAD?.currentAp || 0, ap.HEAD?.maxAp || 0, '#7be67b')} + ${generateProgressBar(ap.HEAD?.currentAp || 0, ap.HEAD?.maxAp || 0, { startingColor: '#5ebb5e', endingColor: '#7be67b'})}
- ${generateProgressBar(ap.ARMS?.currentAp || 0, ap.ARMS?.maxAp || 0, '#7be67b')} + ${generateProgressBar(ap.ARMS?.currentAp || 0, ap.ARMS?.maxAp || 0, { startingColor: '#5ebb5e', endingColor: '#7be67b'})}
- ${generateProgressBar(ap.CHEST?.currentAp || 0, ap.CHEST?.maxAp || 0, '#7be67b')} + ${generateProgressBar(ap.CHEST?.currentAp || 0, ap.CHEST?.maxAp || 0, { startingColor: '#5ebb5e', endingColor: '#7be67b'})}
- ${generateProgressBar(ap.LEGS?.currentAp || 0, ap.LEGS?.maxAp || 0, '#7be67b')} + ${generateProgressBar(ap.LEGS?.currentAp || 0, ap.LEGS?.maxAp || 0, { startingColor: '#5ebb5e', endingColor: '#7be67b'})}
`; } -function progressBar(current: number, max: number, id: string, color: string) { +interface ProgressBarOptions { + startingColor: string; + endingColor: string; +} + +function progressBar(current: number, max: number, id: string, opts: ProgressBarOptions) { let percent = 0; if(max > 0) { percent = Math.floor((current / max) * 100); } - return `
${current}/${max} - ${percent}
`; } @@ -81,8 +86,8 @@ export function renderPlayerBar(player: Player, inventory: EquippedItemDetails[]
${player.gold.toLocaleString()}
${calcAp(inventory)}
- ${progressBar(player.hp, maxHp(player.constitution, player.level), 'hp-bar', '#ff7070')} - ${progressBar(player.exp, expToLevel(player.level + 1), 'exp-bar', '#5997f9')} + ${progressBar(player.hp, maxHp(player.constitution, player.level), 'hp-bar', { endingColor: '#ff7070', startingColor: '#d62f2f' })} + ${progressBar(player.exp, expToLevel(player.level + 1), 'exp-bar', { endingColor: '#5997f9', startingColor: '#1d64d4'})}
${player.account_type === 'session' ? displayLoginSignupForm() : ''} `; diff --git a/src/server/views/stores.ts b/src/server/views/stores.ts index 6a24e89..08745f7 100644 --- a/src/server/views/stores.ts +++ b/src/server/views/stores.ts @@ -2,6 +2,7 @@ import { ShopEquipment } from "../../shared/inventory"; import { ShopItem, Item } from "../../shared/items"; import { capitalize } from "lodash"; import { Player } from "../../shared/player"; +import { LocationWithCity } from "shared/map"; function renderStatBoost(name: string, val: number | string): string { let valSign: string = ''; @@ -78,7 +79,7 @@ function renderShopEquipment(item: ShopEquipment, action: (item: ShopEquipment) -export async function renderStore(equipment: ShopEquipment[], items: (ShopItem & Item)[], player: Player): Promise { +export async function renderStore(equipment: ShopEquipment[], items: (ShopItem & Item)[], player: Player, location: LocationWithCity): Promise { const listing: Record = {}; const listingTypes = new Set(); @@ -117,11 +118,16 @@ export async function renderStore(equipment: ShopEquipment[], items: (ShopItem & finalListing.push(`
${listing[type]}
`); }); - let html = `
+ let html = ` +
${location.city_name}
+
+

${location.name}

+
${finalListing.join("\n")}
-
`; +
+
`; return html; } diff --git a/src/server/views/travel.ts b/src/server/views/travel.ts index fc70860..cf2d7db 100644 --- a/src/server/views/travel.ts +++ b/src/server/views/travel.ts @@ -15,8 +15,9 @@ export function renderTravel(data: TravelDTO): string { } */ - let html = `
-
`; + let html = `
+ +
`; html += '
'; html += travelButton(blockTime); if(data.things.length) { @@ -25,7 +26,7 @@ export function renderTravel(data: TravelDTO): string { promptText = `You see a ${data.things[0].name}`; html += `
- +
`; } diff --git a/src/shared/map.ts b/src/shared/map.ts index 5fe8b40..c05ae8f 100644 --- a/src/shared/map.ts +++ b/src/shared/map.ts @@ -1,3 +1,5 @@ +import { TravelWithNames } from "./travel"; + export type City = { id: number; name: string; @@ -14,6 +16,10 @@ export type Location = { event_name: string; } +export type LocationWithCity = Location & { + city_name: string; +} + export type Path = { starting_city: number; ending_city: number; @@ -27,6 +33,7 @@ export type TravelDTO = { nextAction: number, walkingText: string, closestTown: number; + travelPlan: TravelWithNames } export const STEP_DELAY = 3000; diff --git a/src/shared/travel.ts b/src/shared/travel.ts index 2c5911f..babafa0 100644 --- a/src/shared/travel.ts +++ b/src/shared/travel.ts @@ -5,3 +5,8 @@ export type Travel = { total_distance: number; current_position: number; } + +export type TravelWithNames = Travel & { + destination_city_name: string; + source_city_name: string; +} -- 2.25.1