Initial commit

This commit is contained in:
Fam 2023-07-09 17:48:48 +01:00
parent 5d081d4067
commit 289c833d34
1779 changed files with 32260 additions and 132 deletions

139
.gitignore vendored
View File

@ -1,130 +1,11 @@
# Logs
logs
.DS_Store
node_modules
.yo-rc.json
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
gulptasks/upload.js
downloads/*
.eslintignore
.eslintrc
.tern-project
dist/*
dist

101
README.md
View File

@ -1,4 +1,99 @@
# blackdesert-somethinglovely-map
Famme's BDO Tools - somethinglovely.net
For source, check back tomorrow!
# Famme's BDO Tools
### somethinglovely.net by Famme (Discord: fffam)
An interactive map for Black Desert Online. The website has been sunset as a result of not being in a position to stay current with game updates.
Horses, crates & tradepack tools are just embeds for the following jsfiddle pages:
* **Horse Calculator:** https://jsfiddle.net/fffam/k5z99cus/embedded/result/
* **Crate Calculator:** https://jsfiddle.net/fffam/8Ldoz5wz/embedded/result/
* **Tradepack Reference:** https://jsfiddle.net/fffam/pL6us4kd/embedded/result/
All code & original assets are to be considered MIT-licensed (images within `horses` & `icons` are copyright Pearl Abyss Corp). You may use any of the code, or rehost the website in its entirety.
<hr>
## Installation
* Download the map tiles https://mega.nz/file/ThJA3BRD#KD-6R8Lm8d9MRZsl21BSe2HMrlpi72B9iMIKcRsoTs8 (100MB)
* Host map tiles somewhere.
* Clone repo
* `npm install`
* Search project for somethinglovely.net and replace with your own path to where your map tiles are being served from
* If serving to the public, put it behind a cache (CloudFlare free tier works fine).
To run locally:
* `gulp`
To build (into dist folder):
* `gulp build`
## Updating data
The data is all in [data.json](src/data.json). Nodes are listed in custom object format, all other layers are in standard GeoJSON FeatureCollection format. Should all be pretty obvious once prettified.
## Updating map tiles
https://mega.nz/file/7t5yBTpb#kG8wK4gp6JKZf4W-qreqPQ8l9m0RdaowxDHBLk0rV8o (3.9GB) and https://mega.nz/file/b4pz3CBD#Fr9EWgL77kpCe2Iteai0lZhuS36YTJKaUt1NLw5f0BI (58KB).
#### Time estimates:
* 5 mins work
* 1 hour wait to extract tiles
* 10 mins work (more if you need to install WSL/bash)
* 6 hour wait to convert to giant singular map png
* 30 mins to combine in photoshop
* 2 hour to split into leaflet tiles
#### Steps:
1. Download latest PAZ files (i.e. just patch game)
2. Get paz_browser from BDO Data/PazExtractor and put it in BDO paz folder
3. Run it and Search > mapdata_realexplore then extract mapdata_realexplore.xml and mapdata_realexplore2.xml
4. Run it and extract `rader*.dds` (search `rader*.dds`, then press A to select all)
5. Wait for extraction to finish (ETA: 60 minutes, set an alert)
6. Put all of the dds map tiles into a folder called 'rader'
7. Copy the 5 radar script files (radar-combine.sh, radar-combine-fast.sh, radar-convert.sh, radar-getbounds.js and radar-sort.js) into the folder above 'rader'
8. In linux (probably WSL - https://docs.microsoft.com/en-us/windows/wsl/install-win10), make sure imagemagick is installed (sudo apt-get update followed by sudo apt-get install imagemagick)
9. Increase the width/height/disk/memory limits in the ImageMagick policy.xml (probably in /etc/ImageMagick-6/). Change width/height from 16KP to 256KP. Change Memory from 256MiB to 16GiB. Change Disk to 16GiB.
10. Create the empty folders alongside rader:
* rader-sorted
* rader-slices
11. Put the blank.png in the same folder as radar-sort.sh
12. In bash, run the scripts in the following order (or chain execute them with &&):
`radar-convert.sh` (converts the dds texture files to png. ETA: 20m)
`radar-sort.sh` (copies the png tiles to a folder with new names that are 0-indexed.) ETA: 30m)
`radar-combine-fast.sh` (uses imagemagick's montage to stitch the tiles together. ETA: 4 hours, run it overnight)
**NOTES:**
radar-getbounds.js is just to check the map grid bounds
radar-combine-fast.sh is slow, but its just faster than the other version because it combines the tiles in strips as an intermediate step
13. Open Map (Combined).psb in photoshop
14. With the `map_{DATE}` layer selected, go Layer > Smart Objects > Relink to File and select the new map.png that was created from step 11. The original linked image path will probably be broken by default.
15. Adjust the layer mask for the `map` layer, and for the Ocean Overlay layer to make the map look good. Then save the file (Ctrl-S), then also export the file as a 100% JPEG (File > Save a Copy... > Select JPEG) and save as map.jpg
16. Run the slicer script to convert to leaflet map tiles:
`./magick-slicer.sh map.jpg`
17. [OPTIONAL BUT ADVISED] Once sliced, optimise the filesizes of the tiles (using ImageOptim on OSX or an equivalent tool on Linux/Win)
`find ./Map_files -type f -iname \*jpg -print0 | xargs -0 -t -n 100 /Applications/ImageOptim\ 2.app/Contents/MacOS/ImageOptim`
18. compress the files into a tarball
`mv ./Map_files tiles-new`
`tar -zcvf tiles-new.tar.gz tiles-new`
19. Upload tarbarll to server, then ssh into server and unpack the tarball
`scp tiles-new.tar.gz username@somethinglovely.net:/home/www/somethinglovely.net/bdo`
`tar xzvf tiles-new.tar.gz`
20. swap the old tiles for the new ones
`mv tiles2 tiles2-old && mv tiles-new tiles2`

121
crates/index.html Normal file
View File

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>📦 Crate Calculator — Famme's BDO Tools</title>
<meta name="description" content="Black Desert Online crate calculator">
<link rel="icon" type="image/png" href="../map/static/favicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<style>
@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700|Open+Sans+Condensed:300,700);
html {
font-family: 'Open Sans', sans-serif;
font-weight: 400;
font-size: 11px;
}
html, body { margin: 0; padding: 0 }
#menu {
background: #b71c1c;
color: #FFF;
padding: 0;
position: absolute;
top: 0;
left: 0;
right: 0;
height: 54px;
display: flex;
}
h1,h2 {
display: inline-block;
font-size: 1.2rem;
margin: 0rem 0.5rem 0;
padding: 0;
font-weight: 400;
padding-left: 0.5rem;
}
h1 {
font-weight: 700;
position: absolute;
top: 9px;
}
h2 {
position: absolute;
top: 25px;
font-size: 1.1rem;
color: rgba(255, 255, 255, 0.7);
}
.back {
border: 1px solid rgba(255, 255, 255, 0.2);
margin: 0;
display: inline-block;
padding: 0.5rem 1rem;
font-size: 1rem;
font-weight: normal;
color: rgba(255, 255, 255, 0.6);
text-decoration: none;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
white-space: nowrap;
border-radius: 2px;
}
.back:hover {
border-color: rgba(255, 255, 255, 0.85);
color: rgba(255, 255, 255, 0.85);
}
.intro {
flex: 0 1 280px;
padding: 1.1rem;
}
.links {
flex: 1 1 50%;
background: #263238;
padding: 1.1rem 2rem;
}
.link {
border: 1px solid rgba(255, 255, 255, 0.2);
margin: 0 1rem 0 0;
display: inline-block;
padding: 0.5rem 1rem;
font-size: 1rem;
font-weight: normal;
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
white-space: nowrap;
border-radius: 2px;
}
.link:hover {
border: 1px solid rgba(255, 255, 255, 0.4);
color: rgba(255, 255, 255, 0.9);
}
.link.selected {
font-weight: 700;
border-color: #FFF;
color: #263238;
background: #FFF;
}
</style>
</head>
<body>
<div id="app">
<iframe width="100%" height="1200" src="//jsfiddle.net/fffam/8Ldoz5wz/embedded/result/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<div id="menu">
<div class="intro">
<a class="back" href="/bdo/"><span>Return to Map</span></a>
<h1>Famme's BDO Tools</h1>
<h2>Trading Tools</h2>
</div>
<div class="links">
<a href="/bdo/crates" class="link selected">Crate Calculator</a>
<a href="/bdo/tradepacks" class="link">Trade Pack Reference</a>
</div>
</div>
</div>
</body>
</html>

149
gulpfile.js Normal file
View File

@ -0,0 +1,149 @@
var gulp = require('gulp');
var runSequence = require('run-sequence');
var webpack = require('webpack');
var notify = require('gulp-notify');
var rm = require('rimraf');
var imagemin = require('gulp-imagemin');
var scsslint = require('gulp-scss-lint');
//http://www.browsersync.cn/docs/recipes/
var browserSync = require('browser-sync').create();
var reload = browserSync.reload;
var src = './src/';
var dest = './dist/';
var homepage = 'index.html';
var config = {
src: src,
dest: dest,
webServer: {
server: './dist',
index: homepage,
port: 3000,
logLevel: 'debug',
logPrefix: 'JHW',
open: true,
files: [dest + '/*.js', './index.html']
},
scss: {
src: src + '**/*.scss'
},
script: {
entry: {
'entry': src + 'main.js'
},
output: {
path: dest, //js
filename: 'bundle.js'
},
sourceMap: true,
watch: src + '**/*.js'
},
html: {
watchHome: homepage,
watchAll: src + '**/*.html'
}
}
var webpackConfig = require('./webpack.config')(config);
gulp.task('webpack', function(cb) {
webpack(webpackConfig, function(err, stats) {
if (err) {
handleErrors();
console.error(stats);
}
if (stats.compilation.errors.length) {
console.error(stats.compilation.errors[0].toString());
}
cb();
});
});
gulp.task('img:dev', ['clean'], function() {
return gulp.src([src + '/images/**'])
.pipe(watch())
.pipe(reload());
});
gulp.task('img', ['clean'], function() {
return gulp.src([src + '/images/**'])
.pipe(imagemin())
.pipe(gulp.dest(dest + '/images'));
});
function handleErrors() {
var args = Array.prototype.slice.call(arguments);
notify.onError({
title: 'compile error',
message: '<%= error.message %>'
}).apply(this, args);
this.emit('end');
}
gulp.task('web-server', ['build'],function() {
browserSync.init(config.webServer);
});
gulp.task('watch', ['web-server'], function() {
gulp.watch(config.script.watch, ['webpack']).on('change', reload);
gulp.watch(config.scss.src, ['webpack']).on('change', reload);
gulp.watch(config.src + '/**/*.vue', ['webpack']).on('change', reload);
gulp.watch(config.html.watchHome, ['html']).on('change', reload);
gulp.watch(config.html.watchAll, ['html']).on('change', reload);
});
gulp.task('scss-lint', function() {
return gulp.src(src+'**/*.scss')
.pipe(scsslint({
'config': 'scsslint.yml',
}));
});
gulp.task('static', function() {
return gulp.src([src + 'static/**'])
.pipe(gulp.dest(dest + 'static'));
});
gulp.task('datajson', function() {
return gulp.src([src + 'data.json'])
.pipe(gulp.dest(dest));
});
gulp.task('gatheringdata', function() {
return gulp.src([src + 'gathering/**'])
.pipe(gulp.dest(dest + 'gathering'));
});
gulp.task('icons', function() {
return gulp.src([src + 'icons/**'])
.pipe(gulp.dest(dest + 'icons'));
});
gulp.task('html', function() {
return gulp.src([src + '**/*.html'])
.pipe(gulp.dest(dest));
});
gulp.task('clean', function(next) {
rm(dest, function() {
next();
});
});
gulp.task('default', ['watch']);
gulp.task('run', ['watch']);
gulp.task('build', function(callback) {
runSequence('clean', 'img', 'webpack', 'static', 'datajson', 'gatheringdata', 'icons', 'html',callback);
});
gulp.task( 'download-images', function() {
var data = require('./scripts/download-images');
});
// Require extra gulp tasks
try {
require('./gulptasks/upload.js')(gulp);
} catch(err) {
// Don't error if we don't have extra tasks, they're only for deploy right now
}

BIN
horses/img/1A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
horses/img/1B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
horses/img/2A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
horses/img/2B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
horses/img/2C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
horses/img/2D.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
horses/img/3A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
horses/img/3B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
horses/img/3C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
horses/img/3D.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/3E.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/3F.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/4A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
horses/img/4B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
horses/img/4C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
horses/img/4D.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/4E.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
horses/img/4F.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/4G.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/4H.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/4I.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
horses/img/4J.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/4K.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/4L.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
horses/img/4M.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
horses/img/4N.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/4O.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/4P.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/4Q.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/5A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/5B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/5C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/5D.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/5E.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/5F.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
horses/img/5G.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
horses/img/5H.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/5I.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/5J.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
horses/img/5K.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
horses/img/5L.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
horses/img/5M.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
horses/img/5N.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
horses/img/5O.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/6A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
horses/img/6B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
horses/img/6C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
horses/img/6D.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
horses/img/6E.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/6F.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/6G.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
horses/img/6H.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/6I.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/6J.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/6K.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/6L.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/6M.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
horses/img/6N.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/6O.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
horses/img/6P.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/6Q.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/6R.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/6S.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/6T.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
horses/img/6U.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
horses/img/7A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
horses/img/7B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/7C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/7D.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/7E.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
horses/img/7G.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
horses/img/7H.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
horses/img/7I.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
horses/img/7J.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/8A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/8B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/8C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/8D.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
horses/img/8E.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
horses/img/9A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
horses/img/9A2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
horses/img/9B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
horses/img/9C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

117
horses/index.html Normal file
View File

@ -0,0 +1,117 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>🐎 Horse Calculator — Famme's BDO Tools</title>
<meta name="description" content="Black Desert Online horse breeding calculator">
<link rel="icon" type="image/png" href="../map/static/favicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<style>
@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700|Open+Sans+Condensed:300,700);
html {
font-family: 'Open Sans', sans-serif;
font-weight: 400;
font-size: 11px;
}
html, body { margin: 0; padding: 0 }
#menu {
background: #b71c1c;
color: #FFF;
padding: 0;
position: absolute;
top: 0;
left: 0;
right: 0;
height: 54px;
display: flex;
}
h1,h2 {
display: inline-block;
font-size: 1.2rem;
margin: 0rem 0.5rem 0;
padding: 0;
font-weight: 400;
padding-left: 0.5rem;
}
h1 {
font-weight: 700;
position: absolute;
top: 9px;
}
h2 {
position: absolute;
top: 25px;
font-size: 1.1rem;
color: rgba(255, 255, 255, 0.7);
}
.back {
border: 1px solid rgba(255, 255, 255, 0.2);
margin: 0;
display: inline-block;
padding: 0.5rem 1rem;
font-size: 1rem;
font-weight: normal;
color: rgba(255, 255, 255, 0.6);
text-decoration: none;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
white-space: nowrap;
border-radius: 2px;
}
.back:hover {
border-color: rgba(255, 255, 255, 0.85);
color: rgba(255, 255, 255, 0.85);
}
.intro {
flex: 0 1 280px;
padding: 1.1rem;
}
.links {
flex: 1 1 50%;
background: #263238;
padding: 1.1rem 2rem;
}
.link {
border: 1px solid rgba(255, 255, 255, 0.2);
margin: 0 1rem 0 0;
display: inline-block;
padding: 0.5rem 1rem;
font-size: 1rem;
font-weight: normal;
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
white-space: nowrap;
border-radius: 2px;
}
.link:hover {
border: 1px solid rgba(255, 255, 255, 0.4);
color: rgba(255, 255, 255, 0.9);
}
.link.selected {
font-weight: 700;
border-color: #FFF;
color: #263238;
background: #FFF;
}
</style>
</head>
<body>
<div id="app">
<iframe width="100%" height="1200" src="//jsfiddle.net/fffam/k5z99cus/embedded/result/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<div id="menu">
<div class="intro">
<a class="back" href="/bdo/"><span>Return to Map</span></a>
<h1>Famme's BDO Tools</h1>
<h2>Horse Calculator</h2>
</div>
</div>
</div>
</body>
</html>

15591
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

80
package.json Normal file
View File

@ -0,0 +1,80 @@
{
"name": "mwBlog",
"version": "0.0.1",
"description": "",
"main": "bundle.js",
"scripts": {
"test": "echo \\\"Error: no test specified\\\" && exit 1",
"start": "export NODE_ENV=\"dev\" ; gulp watch",
"clean": "export NODE_ENV=\"dev\" ; gulp clean",
"webpack": "export NODE_ENV=\"production\" ; gulp webpack",
"build": "export NODE_ENV=\"production\" ; gulp build",
"deploy": "export NODE_ENV=\"production\" ; gulp deploy",
"download-images": "export NODE_ENV=\"dev\" ; gulp download-images"
},
"dependencies": {
"clipboard-js": "^0.3.6",
"d3": "^3.5.17",
"eventemitter3": "^2.0.3",
"js-cookie": "^2.2.0",
"js-cookies": "^1.0.4",
"leaflet": "^1.2.0",
"riot-route": "^2.5.0",
"vue": "^1.0.28",
"vue-resource": "^0.7.0",
"vue-router": "^0.7.10",
"vue-simple-store": "^1.0.0",
"whatwg-fetch": "^1.1.1"
},
"devDependencies": {
"async": "^2.6.0",
"autoprefixer": "^6.7.7",
"babel-core": "^6.26.0",
"babel-eslint": "^6.0.4",
"babel-loader": "^6.4.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-runtime": "^6.26.0",
"browser-sync": "^2.23.1",
"cheerio": "^0.20.0",
"concat": "^1.0.3",
"css-loader": "^0.23.1",
"csswring": "^4.0.0",
"eslint": "~2.2.0",
"eslint-loader": "^1.9.0",
"eslint-plugin-html": "^1.7.0",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.5",
"firebase": "^3.9.0",
"gulp": "^3.9.0",
"gulp-imagemin": "~2.4.0",
"gulp-jshint": "^2.1.0",
"gulp-load-plugins": "^1.5.0",
"gulp-notify": "^2.2.0",
"gulp-scss-lint": "^0.4.0",
"html-loader": "^0.4.5",
"html-webpack-plugin": "^2.30.1",
"jquery": "^2.1.4",
"jshint": "^2.9.5",
"jshint-loader": "^0.8.4",
"map-stream": "0.0.6",
"node-libs-browser": "^1.1.1",
"node-sass": "^3.13.1",
"postcss-loader": "^0.8.0",
"rimraf": "~2.5.0",
"run-sequence": "^1.2.1",
"sass-loader": "^3.1.2",
"source-map-loader": "^0.1.6",
"style-loader": "^0.13.2",
"url-loader": "^0.5.9",
"vinyl-ftp": "^0.6.1",
"vue-html-loader": "^1.2.4",
"vue-loader": "^8.7.0",
"vue-style-loader": "^1.0.0",
"webpack": "^1.15.0",
"webpack-dev-server": "^1.16.5",
"webpack-stream": "^3.2.0",
"webpack-subresource-integrity": "^1.1.0-rc.2"
}
}

253
scsslint.yml Normal file
View File

@ -0,0 +1,253 @@
# Default application configuration that all configurations inherit from.
scss_files: "**/*.scss"
plugin_directories: ['.scss-linters']
# List of gem names to load custom linters from (make sure they are already
# installed)
plugin_gems: []
# Default severity of all linters.
severity: warning
linters:
BangFormat:
enabled: true
space_before_bang: true
space_after_bang: false
BemDepth:
enabled: false
max_elements: 1
BorderZero:
enabled: true
convention: none # or `zero`
ChainedClasses:
enabled: false
ColorKeyword:
enabled: true
ColorVariable:
enabled: false
Comment:
enabled: true
style: silent
DebugStatement:
enabled: true
DeclarationOrder:
enabled: true
DisableLinterReason:
enabled: false
DuplicateProperty:
enabled: true
ElsePlacement:
enabled: true
style: same_line # or 'new_line'
EmptyLineBetweenBlocks:
enabled: false
ignore_single_line_blocks: true
EmptyRule:
enabled: true
ExtendDirective:
enabled: false
FinalNewline:
enabled: true
present: true
HexLength:
enabled: true
style: short # or 'long'
HexNotation:
enabled: true
style: uppercase # or 'uppercase'
HexValidation:
enabled: true
IdSelector:
enabled: false
ImportantRule:
enabled: false
ImportPath:
enabled: true
leading_underscore: false
filename_extension: false
Indentation:
enabled: true
allow_non_nested_indentation: false
character: 'tab' # or 'tab'
width: 1
LeadingZero:
enabled: false
style: exclude_zero # or 'include_zero'
MergeableSelector:
enabled: true
force_nesting: true
NameFormat:
enabled: true
allow_leading_underscore: true
convention: hyphenated_lowercase # or 'camel_case', or 'snake_case', or a regex pattern
NestingDepth:
enabled: false
max_depth: 3
ignore_parent_selectors: false
PlaceholderInExtend:
enabled: true
PrivateNamingConvention:
enabled: false
prefix: _
PropertyCount:
enabled: false
include_nested: false
max_properties: 10
PropertySortOrder:
enabled: false
ignore_unspecified: false
min_properties: 2
separate_groups: false
PropertySpelling:
enabled: true
extra_properties: []
disabled_properties: []
PropertyUnits:
enabled: true
global: [
'ch', 'em', 'ex', 'rem', # Font-relative lengths
'cm', 'in', 'mm', 'pc', 'pt', 'px', 'q', # Absolute lengths
'vh', 'vw', 'vmin', 'vmax', # Viewport-percentage lengths
'deg', 'grad', 'rad', 'turn', # Angle
'ms', 's', # Duration
'Hz', 'kHz', # Frequency
'dpi', 'dpcm', 'dppx', # Resolution
'%'] # Other
properties: {}
PseudoElement:
enabled: true
QualifyingElement:
enabled: true
allow_element_with_attribute: true
allow_element_with_class: true
allow_element_with_id: false
SelectorDepth:
enabled: false
max_depth: 3
SelectorFormat:
enabled: false
convention: hyphenated_lowercase # or 'strict_BEM', or 'hyphenated_BEM', or 'snake_case', or 'camel_case', or a regex pattern
Shorthand:
enabled: true
allowed_shorthands: [1, 2, 3, 4]
SingleLinePerProperty:
enabled: true
allow_single_line_rule_sets: true
SingleLinePerSelector:
enabled: false
SpaceAfterComma:
enabled: false
style: one_space # or 'no_space', or 'at_least_one_space'
SpaceAfterPropertyColon:
enabled: true
style: one_space # or 'no_space', or 'at_least_one_space', or 'aligned'
SpaceAfterPropertyName:
enabled: true
SpaceAfterVariableColon:
enabled: false
style: one_space # or 'no_space', 'at_least_one_space' or 'one_space_or_newline'
SpaceAfterVariableName:
enabled: true
SpaceAroundOperator:
enabled: true
style: one_space # or 'at_least_one_space', or 'no_space'
SpaceBeforeBrace:
enabled: true
style: space # or 'new_line'
allow_single_line_padding: false
SpaceBetweenParens:
enabled: true
spaces: 0
StringQuotes:
enabled: false
style: single_quotes # or double_quotes
TrailingSemicolon:
enabled: true
TrailingWhitespace:
enabled: true
TrailingZero:
enabled: false
TransitionAll:
enabled: false
UnnecessaryMantissa:
enabled: true
UnnecessaryParentReference:
enabled: false
UrlFormat:
enabled: true
UrlQuotes:
enabled: true
VariableForProperty:
enabled: false
properties: []
VendorPrefix:
enabled: true
identifier_list: base
additional_identifiers: []
excluded_identifiers: []
ZeroUnit:
enabled: true
Compass::*:
enabled: false

71
src/App.scss Normal file
View File

@ -0,0 +1,71 @@
@import "./common";
@import "./MapIcons";
*, *:before, *:after {
box-sizing: border-box;
}
html {
font-size: 12px;
}
body {
@include font-normal;
font-style: normal;
font-size: 12px;
background: #222;
-webkit-overflow-scrolling: auto;
}
a {
color: $color-text-link;
}
#map.leaflet-container {
font-family: 'Open Sans', sans-serif;
}
body,html,#app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#panels {
position: absolute;
top: 0;
left: 0;
bottom: 0;
z-index: 100000;
pointer-events: none;
width: 24rem;
flex-flow: column;
display: flex;
transition: margin-left 0.5s;
margin-left: 0;
&>* {
pointer-events: auto;
}
.panel-group {
flex: 1;
flex-flow: column;
display: flex;
pointer-events: none;
&>* {
pointer-events: auto;
}
}
&.minimized {
margin-left: -24rem;
}
}
#panes {
display: flex;
position: absolute;
top: 0;
bottom: 0;
flex-flow: row;
}

440
src/App.vue Normal file
View File

@ -0,0 +1,440 @@
<template>
<div>
<map-view v-ref:map :is-node-planner-active="isNodePlannerActive"></map-view>
<div id="panes">
<menu-pane :active="selectedPane == 'menu'" v-ref:menupane></menu-pane>
<node-planner-pane v-ref:nodeplannerpane v-if="isNodePlannerActive && isNodePlannerEnabled" :active="selectedPane == 'nodeplanner'"></node-planner-pane>
<component v-ref:detailpane :is="currentDetailPane" keep-alive :active="selectedPane == 'detail'" :item-id="currentDetailItemId"></component>
</div>
</div>
</template>
<script>
import Vue from 'vue';
import router from 'riot-route';
import NodePathCalculator from './misc/NodePathCalculator.js';
import NodePlanner from './misc/NodePlanner.js';
import MapView from './components/MapView.vue';
import MenuPane from './components/panes/MenuPane.vue';
import InfoPane from './components/panes/InfoPane.vue';
import NodePlannerPane from './components/panes/NodePlannerPane.vue';
import NodeInfoPane from './components/panes/NodeInfoPane.vue';
import LevellingAreaInfoPane from './components/panes/LevellingAreaInfoPane.vue';
import VillaInfoPane from './components/panes/VillaInfoPane.vue';
import WorldBossInfoPane from './components/panes/WorldBossInfoPane.vue';
import FishingHotspotInfoPane from './components/panes/FishingHotspotInfoPane.vue';
import SeaRegionInfoPane from './components/panes/SeaRegionInfoPane.vue';
import GoldenChestInfoPane from './components/panes/GoldenChestInfoPane.vue';
export default {
name: 'App',
data: function() {
return {
appTitle: 'Map — Famme\'s BDO Tools',
selectedPane: 'menu',
currentDetailPane: '',
currentDetailItemId: null,
isNodePlannerEnabled: false,
isNodePlannerActive: false,
isReady: false
};
},
components: {
MapView,
MenuPane,
InfoPane,
NodeInfoPane,
NodePlannerPane,
LevellingAreaInfoPane,
VillaInfoPane,
WorldBossInfoPane,
FishingHotspotInfoPane,
SeaRegionInfoPane,
GoldenChestInfoPane
},
ready: function() {
this.$action('mapdata:load');
this.$action('filters:loadStateFromBrowserStorage');
let devmode = window.location && window.location.hash && (window.location.hash.indexOf('devmode') > -1);
if( devmode ) {
this.isNodePlannerEnabled = true;
}
this.router = router;
// Determine if the query string contains the show parameter
// for which nodes to display
this.setStateFromQueryString( this.router.query() );
// this.route('/node/'+id, '', true])
this.router('/node/*', (nodeid) => {
if( this.isReady ) return; // Only use routes on load
let matchedNode = this.getNodeFromUrlPath(nodeid);
matchedNode && this.selectNode(matchedNode);
});
// Route for linking directly to layers
this.router('/layer/(.*)', (layernames) => {
var layers = layernames.split(',');
for( var i in this.state.filters ) {
if( i !== 'gathering' ) {
this.state.filters[i].active = false;
}
}
for( var i in layers ) {
// Is this a gathering filter?
if( layers[i].indexOf(':') && layers[i].indexOf('gathering') === 0 ) {
var layerparts = layers[i].split(':');
if(layerparts.length !== 3) {
continue;
}
this.state.filters['gathering'][layerparts[1]][layerparts[2]].active = true;
// Normal filters
} else if( this.state.filters[layers[i]] ) {
this.state.filters[layers[i]].active = true;
}
}
});
// Match data layers
this.router('/*/*', (layername,id) => {
if( this.isReady ) return; // Only use routes on load
let matchedFeature = this.getDataLayerFeatureFromUrlPath(layername,id);
matchedFeature && this.selectDataLayerFeature(layername,matchedFeature);
});
this.$watch('state.mapdata.ready', (c) => {
this.nodePlanner = new NodePlanner(this.state.mapdata.nodes);
this.router.exec();
this.router.stop();
this.isReady = true;
this.nodePathCalculator = new NodePathCalculator(this.state.mapdata.nodes);
this.nodePlanner = new NodePlanner(this.state.mapdata.nodes);
this.nodePlanner.on('plan-changed', () => {
this.$broadcast('nodeplanner-plan-changed', {plan:this.nodePlanner.plan, planner: this.nodePlanner} );
this.isNodePlannerActive = true;
});
this.$broadcast('node-data-ready');
});
// Link horses filter to elephants filter
this.$watch('state.filters.horses.active', (c) => {
this.state.filters.elephants.active = c;
});
// Link all of the hunting filters
this.$watch('state.filters.hunting.active', (c) => {
this.state.filters.whales.active = c;
this.state.filters.bluewhales.active = c;
this.state.filters.crocodiles.active = c;
this.state.filters.khalks.active = c;
});
// Did we just activate the node planner?
// If so, make it the focussed panel
this.$watch('isNodePlannerActive', (c) => {
if( this.isNodePlannerActive ) {
this.selectedPane = 'nodeplanner';
}
});
},
events: {
'marker-clicked': function (msg,a) {
switch(msg.type) {
case 'node':
this.selectNode(msg.id,true,false);
break;
case 'feature':
this.selectDataLayerFeature(msg.layer,msg.id,true,false);
break;
default:
// Not setup for anything other than nodes yet
}
this.$broadcast('marker-clicked',msg);
return true;
},
'search-suggestion-clicked': function (msg) {
if( msg.isDataLayer || msg.isDataLayerOutput ) {
this.selectDataLayerFeature(msg.type,msg.id,true,true);
} else {
this.selectNode(msg == 'node' ? msg.id : msg.node.id,true,true);
}
return true;
},
'search-suggestion-hovered': function (msg) {
if( msg.isDataLayer || msg.isDataLayerOutput ) {
this.pingDataLayerFeature(msg.type,msg.id);
} else {
this.pingNode(msg.type == 'node' ? msg.id : msg.node.id);
}
return true;
},
'node-link-clicked': function (id) {
this.selectNode(id,true,true);
return true;
},
'zoom-map-to-coordinates': function (coords) {
this.zoomMapToCoords(coords);
return true;
},
'detail-pane-closed': function() {
this.currentDetailPane = null;
this.selectedPane = 'menu';
this.$refs.menupane.activatePane();
return true;
}
},
methods: {
isBeta() {
let document = window.document;
if( document && document.location && document.location.href ) {
return ((document.location.pathname.indexOf('bdobeta')>-1) || (document.location.hostname == 'localhost'));
}
return false;
},
setStateFromQueryString: function(query) {
// Should we turn off all layers by default?
if( query.hidedefaults ) {
for( var i in this.state.filters ) {
this.state.filters[i].active = false;
}
}
// Which layers should we turn on?
if( query.filters ) {
try {
let queryParts = query.filters.split('|');
for( var i in queryParts ) {
if( this.state.filters[queryParts[i]] ) {
this.state.filters[queryParts[i]].active = true;
} else {
// Search through the gathering filters to see if one matches
for( var gatheringCategory in this.state.filters['gathering'] ) {
// It matches the category
if( this.state.filters['gathering'][gatheringCategory].slug == queryParts[i] ) {
for( var j in this.state.filters['gathering'][gatheringCategory].items ) {
this.state.filters['gathering'][gatheringCategory].items[j].active = true;
}
} else {
for( var j in this.state.filters['gathering'][gatheringCategory].items ) {
if( this.state.filters['gathering'][gatheringCategory].items[j].slug == queryParts[i] ) {
this.state.filters['gathering'][gatheringCategory].items[j].active = true;
}
}
}
}
}
}
} catch(e) {
// Just use default filters
}
}
if( query.latlng ) {
this.$refs.map.zoomMapToCoords( query.latlng.split('|').map( (a) => a * 1 ) );
}
},
routeTo: function(title, ...urlparts) {
if( urlparts && urlparts.length ) {
if( title ) {
this.router('/'+urlparts.join('/'),title + ' — ' + this.appTitle,true);
} else {
this.router('/'+urlparts.join('/'),this.appTitle,true);
}
} else {
this.router('/',this.appTitle,true);
}
},
getNodeFromUrlPath: function(urlPathPart) {
if( this.state.mapdata.nodes[urlPathPart] ) {
return this.state.mapdata.nodes[urlPathPart];
} else {
// Find by slug
for( let id in this.state.mapdata.nodes ) {
if( this.state.mapdata.nodes[id].slug && this.state.mapdata.nodes[id].slug == urlPathPart ) {
return this.state.mapdata.nodes[urlPathPart];
}
}
}
return false;
},
getLinkInfoForNode: function(id) {
if( this.state.mapdata.nodes[id] ) {
return {
id: id,
name: this.state.mapdata.nodes[id].name,
slug: this.state.mapdata.nodes[id].slug || id
};
} else {
return false;
}
},
getDataLayerFeatureFromUrlPath: function(layerName,urlPathPart) {
if( this.state.mapdata.layers[layerName] && this.state.mapdata.layers[layerName].features ) {
let features = this.state.mapdata.layers[layerName].features;
for( var i in features ) {
if(
(features[i].id && features[i].id == urlPathPart) ||
(features[i].properties && features[i].properties.slug && features[i].properties.slug == urlPathPart)
) {
return features[i];
}
}
}
return false;
},
getLinkInfoForDataLayerFeature: function(layerName,id) {
if( this.state.mapdata.layers[layerName] && this.state.mapdata.layers[layerName].features ) {
let features = this.state.mapdata.layers[layerName].features;
for( var i in features ) {
if( features[i].id && features[i].id == id ) {
let title = (features[i].properties && features[i].properties.name) ? features[i].properties.name : null;
return {
id: id,
name: title,
slug: (features[i].properties && features[i].properties.slug) ? features[i].properties.slug : features[i].id
};
}
}
}
},
selectNode: function(idOrNodeObj,ping=false,zoom=true) {
// Get the ID
if( idOrNodeObj.id ) {
idOrNodeObj = idOrNodeObj.id;
}
let linkInfo = this.getLinkInfoForNode(idOrNodeObj);
if( linkInfo ) {
this.routeTo( linkInfo.name, 'node', linkInfo.slug );
} else {
// Node doesn't exist
return;
}
this.$refs.map.selectNode(idOrNodeObj,ping,zoom);
this.currentDetailItemId = idOrNodeObj;
this.currentDetailPane = 'NodeInfoPane';
this.selectedPane = 'detail';
},
selectDataLayerFeature: function(type,id,ping=false,zoom=true) {
console.log('[App]','selectDataLayerFeature',type,id);
if( id.id ) {
id = id.id;
}
// If we are selecting the elephant layer, ensure horses filter is enabled too
if( type == 'elephants' ) {
this.state.filters.horses.active = true;
}
// If we are selecting a hunting feature, toggle on the hunting layer
if( type == 'whales' || type == 'bluewhales' || type == 'crocodiles' || type == 'khalks' ) {
this.state.filters.hunting.active = true;
}
let linkInfo = this.getLinkInfoForDataLayerFeature(type,id);
if( linkInfo ) {
this.$refs.map.selectDataLayerFeature(type,id,ping,zoom);
this.routeTo( linkInfo.name, type, linkInfo.slug );
} else {
// Data layer feature doesn't exist
return;
}
// Is this a data layer that has a detail panel?
switch( type ) {
case 'levellingareas':
this.currentDetailPane = 'LevellingAreaInfoPane';
this.currentDetailItemId = id;
this.selectedPane = 'detail';
break;
case 'villas':
this.currentDetailPane = 'VillaInfoPane';
this.currentDetailItemId = id;
this.selectedPane = 'detail';
break;
case 'worldbosses':
this.currentDetailPane = 'WorldBossInfoPane';
this.currentDetailItemId = id;
this.selectedPane = 'detail';
break;
case 'fishinghotspots':
this.currentDetailPane = 'FishingHotspotInfoPane';
this.currentDetailItemId = id;
this.selectedPane = 'detail';
break;
case 'searegions':
this.currentDetailPane = 'SeaRegionInfoPane';
this.currentDetailItemId = id;
this.selectedPane = 'detail';
break;
case 'goldenchests':
this.currentDetailPane = 'GoldenChestInfoPane';
this.currentDetailItemId = id;
this.selectedPane = 'detail';
break;
default:
// No detail pane
}
},
registerCustomDataLayer: function( url, name, properties ) {
console.log('[App] Registering custom data layer:', name, url);
Vue.set(
this.state.filters.custom,
url,
{ url: url, active: true, loaded: true, name: name, properties: properties }
);
},
pingNode: function(idOrNodeObj) {
let id = idOrNodeObj;
if( idOrNodeObj.id ) {
idOrNodeObj = idOrNodeObj.id;
}
this.$refs.map.pingNode(idOrNodeObj);
},
pingDataLayerFeature: function(type,id) {
this.$refs.map.pingDataLayerFeature(type,id);
},
zoomMapToCoords: function(coords) {
this.$refs.map.zoomMapToCoords(coords);
setTimeout( () => {
this.$refs.map.pingCoords(coords);
}, 1500);
}
}
};
</script>
<style src="./App.scss" lang="sass"></style>

212
src/MapIcons.scss Normal file

File diff suppressed because one or more lines are too long

5
src/VueFilters.js Normal file
View File

@ -0,0 +1,5 @@
import Vue from 'vue';
Vue.filter('to-css-class', function (value) {
return value.replace(/\s/g,'-');
});

91
src/common.scss Normal file
View File

@ -0,0 +1,91 @@
//@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700|Open+Sans+Condensed:300,700);
@import url(https://fonts.googleapis.com/css?family=Roboto+Condensed:300,700|Roboto:400,700);
$color-red-50: #FFEBEE;
$color-red-100: #FFCDD2;
$color-red-200: #EF9A9A;
$color-red-300: #E57373;
$color-red-400: #EF5350;
$color-red-500: #F44336;
$color-red-600: #E53935;
$color-red-700: #D32F2F;
$color-red-800: #C62828;
$color-red-900: #B71C1C;
$color-primary: $color-red-500;
$color-primary-700: $color-red-700;
$color-accent: #00E676;
$color-text: rgba(0,0,0,0.87);
$color-text-2: rgba(0,0,0,0.54);
$color-text-3: rgba(0,0,0,0.38);
$color-text-4: rgba(0,0,0,0.18);
$color-text-on-primary: #FFF;
$color-text-on-accent: #FFF;
$color-text-link: #2196F3;
$color-divider: rgba(0,0,0,0.12);
$color-grey-50: #FAFAFA;
$color-grey-100: #F5F5F5;
$color-grey-200: #EEE;
$color-grey-300: #E0E0E0;
$color-grey-400: #DBDBDB;
$color-grey-500: #9E9E9E;
$color-grey-600: #757575;
$color-grey-700: #616161;
$color-grey-800: #424242;
$color-grey-900: #212121;
$color-message-background: #263238;
$color-message-text: rgba(255,255,255,0.54);
$color-message-text-link: rgba(255,255,255,0.87);
$menu-padding: 1rem;
$pane-width: 25rem;
@mixin font-normal() {
font-family: 'Roboto', sans-serif;
font-weight: 400;
}
@mixin font-bold() {
font-family: 'Roboto', sans-serif;
font-weight: 700;
}
@mixin font-condensed() {
font-family: 'Roboto Condensed', sans-serif;
font-weight: 300;
}
@mixin font-condensed-bold() {
font-family: 'Roboto Condensed', sans-serif;
font-weight: 700;
}
.ui-button {
//border: 1px solid rgba(0,0,0,1);
border: none;
display: inline-block;
padding: 0.5rem 1rem;
background: $color-red-700;
border-radius: 3px;
color: $color-text-on-primary;
cursor: pointer;
&:hover {
color: $color-text-on-primary;
//border-color: $color-red-500;
background-color: $color-red-500;
}
&:focus {
outline: none;
//border-color: $color-red-500;
background-color: $color-red-700;
}
&:active {
outline: none;
color: $color-text-on-primary;
//border-color: $color-red-500;
background-color: $color-red-800;
}
}

485
src/components/MapView.scss Normal file
View File

@ -0,0 +1,485 @@
@import "../common";
@import "../MapIcons";
@import "../vendor/leaflet.rrose.css";
circle {
stroke: none;
fill: #1E88E5;
}
.leaflet-interactive {
pointer-events: auto;
}
.leaflet-right {
//right: 26rem !important;
}
@mixin map-icon($name, $width:36px, $height:47px, $shape:'circle', $offsety:8px) {
.icon {
width: $width;
height: $height;
background-size: $width $height;
margin-top: -$height + 6px + $offsety;
margin-left: -$width/2 + 6;
}
&.Bank,&.Forest,&.Mushrooms,&.Mine,&.Fishing,&.Farm,&.Specialties {
h3 {
display: none !important;
}
}
&:hover h3 {
//display: block !important;
}
.z1 &,.z2 &,.z3 &,.z4 & {
h3 {
@if $name == 'City' {
display: block;
} @else {
display: none;
}
}
.icon {
width: $width*0.5;
height: $height*0.5;
background-size: $width*0.5 $height*0.5;
margin-top: -$height*0.5 + 6px + ($offsety*0.5);
margin-left: -($width*0.5)/2 + 6;
}
}
.z5 & {
.icon {
width: $width*0.75;
height: $height*0.75;
background-size: $width*0.75 $height*0.75;
margin-top: -$height*0.75 + 6px + ($offsety*0.75);
margin-left: -($width*0.75)/2 + 6;
}
}
}
#map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.leaflet-container {
background: #222;
border: none;
}
.leaflet-marker-icon {
// If the marker is part of node network, don't show it grayscale
&.marker--nodeplanner-unselected {
.icon {
filter: grayscale(100%);
}
}
&.marker--nodeplanner-selected {
.icon {
filter: grayscale(0);
}
.contrib {
background: #ffe605;
}
}
// Text label
&.label {
pointer-events: none;
span {
margin-left: -5rem;
width: 10rem;
text-align: center;
display: block;
font-weight: bold;
color: #FFF;
font-size: 1.25rem;
text-shadow: rgba(0,0,0,1) 0 0 2px,rgba(0,0,0,1) 0 0 2px,rgba(0,0,0,1) 0 0 2px,rgba(0,0,0,1) 0 0 2px;
}
&.label-for-data-layer-feaure {
span {
font-size: 0.7rem;
line-height: 1.3;
font-weight: normal;
.z5 &,.z6 &,.z7 & {
font-size: 1.1rem;
}
}
}
&.label-on-data-layer-searegions {
span {
pointer-events: none;
margin-left: -7.5rem;
width: 15rem;
text-shadow: none;
opacity:0.85;
color: #E3F2FD;
font-size: 1rem;
.z5 &,.z6 &,.z7 & {
font-size: 1.5rem;
}
}
}
&.label-next-area {
span {
color: $color-primary;
margin-left: -5rem;
width: 10rem;
font-size: 0.8rem;
.z5 &,.z6 &,.z7 & {
margin-left: -10rem;
width: 20rem;
font-size: 1.3rem;
}
}
}
}
h3 {
@include font-normal;
margin: 0;
padding: 0;
margin-left: -4rem;
text-align: center;
font-size: 0.9rem;
line-height: 1rem;
width: 9rem;
color: #FFF;
text-shadow: rgba(0,0,0,1) 0 0 3px,rgba(0,0,0,1) 0 0 3px,rgba(0,0,0,1) 0 0 3px,rgba(0,0,0,1) 0 0 3px;
}
.contrib {
text-align: center;
color: #000;
background: #FFF;
width: 16px;
height: 16px;
text-overflow: clip;
display: block;
position: absolute;
top: -6px;
left: -6px;
border-radius: 16px;
font-size: 0.7rem;
font-weight: bold;
font-style: normal;
border: 2px solid rgba(0,0,0,0.9);
padding-bottom: 4px;
box-shadow: rgba(0,0,0,0.2) 0 1px 2px;
}
.detail {
color: #FFF;
width: 7rem;
margin-left: -3.5rem;
font-size: 0.8rem;
text-align: left;
text-shadow: rgba(0,0,0,1) 0 0 2px,rgba(0,0,0,1) 0 0 2px,rgba(0,0,0,1) 0 0 2px,rgba(0,0,0,1) 0 0 2px;
b {
display: block;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
.secondary {
font-weight: 400;
}
}
img {
width: 16px;
height: 16px;
margin-right: 4px;
margin-bottom: -5px;
background: #222;
border: 1px solid #000;
}
}
// Hide contribution when zoomed out far
.z1 &,.z2 &,.z3 &,.z3 & {
&:hover {
i.contrib {
display: block;
}
}
i.contrib {
display: none;
}
.detail {
display: none;
}
}
// Hide titles when zoomed out
.z1 &,.z2 &,.z3 &,.z4 & {
h3 {
display: none;
}
}
&.City {
@include map-icon('City', 42px, 53px, 'square');
h3 {
font-size: 1.1rem;
}
.icon {
@include map-icon-city();
}
}
&.Town {
@include map-icon('Town', 42px, 53px, 'square');
.icon {
@include map-icon-town();
}
}
&.Gateway {
@include map-icon('Gateway', 36px, 47px, 'square');
.icon {
@include map-icon-gateway();
}
}
&.Trading-Post {
@include map-icon('Trading-Post', 42px, 53px);
.icon {
@include map-icon-trading-post();
}
}
&.Connection {
@include map-icon('Connection', 42px, 43px);
.icon {
@include map-icon-connection();
}
}
&.Dangerous {
@include map-icon('Dangerous', 45px, 50px, 'triangle');
.icon {
@include map-icon-dangerous();
}
}
&.Bank {
@include map-icon('Bank');
color: #F0AF00;
.icon {
@include map-icon-bank();
}
}
&.Forest {
@include map-icon('Forest');
color: #81C784;
.icon {
@include map-icon-forest();
}
}
&.Mushrooms {
@include map-icon('Mushrooms');
color: #CE93D8;
.icon {
@include map-icon-mushrooms();
}
}
&.Mine {
@include map-icon('Mine');
color: #90A4AE;
.icon {
@include map-icon-mine();
}
}
&.Fishing {
@include map-icon('Fishing');
color: #4FC3F7;
.icon {
@include map-icon-fishing();
}
}
&.Farm {
@include map-icon('Farm');
color: #FFF176;
.icon {
@include map-icon-farm();
}
}
&.Specialty {
@include map-icon('Specialty');
color: #4CAF50;
.icon {
@include map-icon-specialty();
}
}
&.marker-trader, &.marker-trader-imperial, &.marker-trader-smuggler {
.icon {
width: 17px;
height: 17px;
margin-left: -8px;
margin-top: -8px;
.z1 &,.z2 &,.z3 &,.z4 & {
display: none;
}
}
}
&.marker-trader .icon {
@include map-icon-trader;
}
&.marker-trader-imperial .icon {
@include map-icon-trader-yellow;
}
&.marker-trader-smuggler .icon {
@include map-icon-trader-red;
}
&.marker-npc {
.icon {
@include map-icon-tiny-person;
width: 14px;
height: 14px;
margin-left: -7px;
margin-top: -7px;
.z1 &,.z2 &,.z3 &,.z4 & {
display: none;
}
}
}
}
#currentLatLng {
background: rgba(0,0,0,0.7);
color: #FFF;
font-size: 0.8rem;
padding: 0.25rem 0.5rem;
position: absolute;
bottom: 0;
right: 0;
z-index: 400;
transition: background-color 300ms ease-in;
cursor: pointer;
&.copied {
transition: background-color 0ms ease-in;
background-color: rgb(43, 198, 101);
}
}
.leaflet-rrose {
pointer-events: none;
.leaflet-rrose-content {
min-width: 12rem;
background: #FFF;
margin: 0;
padding: 0;
}
.leaflet-rrose-content-wrapper {
border-radius: none;
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.5);
}
.leaflet-rrose-content-wrapper,
.leaflet-rrose-tip {
background: #FFF;
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.5);
}
.header {
padding: 0.5rem 1rem;
h2 { margin: 0; font-size: 1rem; line-height: 1.2rem; }
h3 { margin: 0; font-size: 0.9rem; color: $color-text-2; }
.cp {
line-height: 1.2rem;
color: #888;
float: right;
margin: 0 0 0 0.5rem;
b {
color: #555;
}
}
}
.body {
padding: 0.5rem 1rem;
background: $color-grey-100;
p { margin: 0; font-size: 0.9rem; }
ul, li {
margin: 0;
padding: 0;
display: block;
}
h5 {
font-weight: normal;
color: $color-text-2;
margin: 0.25rem 0 0.5rem;
}
ul {
font-size: 0.8rem;
li {
img {
width: 16px;
margin: 0 0.25rem -4px 0;
}
b {
font-weight: normal;
}
&.primary b {
font-weight: bold;
}
}
& + h5 {
border-top: 1px solid $color-divider;
margin-top: 0.5rem;
padding-top: 0.5rem;
}
}
}
// .footer {
//
// }
.has-header-content {
.body {
border-top: 1px solid $color-divider;
}
}
.map-tooltip {
.originnode {
border-bottom: 1px solid $color-divider;
padding-bottom: 0.25rem;
margin-bottom: 0.25rem;
}
.drops {
font-size: 0.8rem;
}
.drop--rarity-green {
color: rgb(15, 133, 45);
}
.drop--rarity-blue {
color: rgb(0, 79, 233);
}
.drop--rarity-yellow {
color: rgb(242, 174, 0);
}
.drop--rarity-orange {
color: rgb(255, 69, 0);
}
}
}

1498
src/components/MapView.vue Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
<template>
<img v-bind:src="fishIconSrc" class="icon datalayer small icon-{{type}}">
</template>
<script>
import markerIcons from '../../markers';
import fishlist from '../../fishlist';
export default {
name: 'DataLayerFishIcon',
props: {
type: {
type: String,
required: true
},
fishType: {
type: String,
required: false
}
},
computed: {
fishIconSrc: function() {
if( !this.fishType ) {
return '';
}
var fishIdx = 16;
for( var i in fishlist ) {
if( fishlist[i] == this.fishType ) {
fishIdx = i;
break;
}
}
var marker = markerIcons[this.type+'-'+fishIdx];
return marker.options.iconRetinaUrl;
}
}
};
</script>

View File

@ -0,0 +1,4 @@
@import "../../common";
@import "../../MapIcons";
@include small-icon-classes;

Some files were not shown because too many files have changed in this diff Show More