From: xangelo Date: Tue, 15 Aug 2023 18:31:53 +0000 (-0400) Subject: feat: new UI X-Git-Tag: v0.2.9~1 X-Git-Url: https://git.xangelo.ca/?a=commitdiff_plain;h=c7936121b23a77a0b7269d2b319ec0b9f5560bb0;p=risinglegends.git 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. --- 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,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); } #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 0000000..cf58d3e Binary files /dev/null and b/public/assets/font/BreatheFire.woff differ diff --git a/public/assets/font/BreatheFire.woff2 b/public/assets/font/BreatheFire.woff2 new file mode 100644 index 0000000..c057969 Binary files /dev/null and b/public/assets/font/BreatheFire.woff2 differ 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; +}