diff --git a/.github/workflows/render_nanomaps.yml b/.github/workflows/render_nanomaps.yml new file mode 100644 index 0000000000000..532cfa7f169c7 --- /dev/null +++ b/.github/workflows/render_nanomaps.yml @@ -0,0 +1,46 @@ +# GitHub action to autorender nanomaps outside the game +# This kills off the awful verb we have that takes a full 50 seconds and hangs the whole server +# The file names and locations are VERY important here +# DO NOT EDIT THIS UNLESS YOU KNOW WHAT YOU ARE DOING +# -aa +name: 'Render Nanomaps' +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + generate_maps: + name: 'Generate NanoMaps' + runs-on: ubuntu-22.04 + steps: + - id: create_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.PRIVATE_KEY }} + + - run: echo "GH_TOKEN=${{ steps.create_token.outputs.token }}" >> "$GITHUB_ENV" + + - name: 'Update Branch' + uses: actions/checkout@v4 + with: + token: ${{ steps.create_token.outputs.token }} + + - name: Branch + run: | + git branch -f nanomap-render + git checkout nanomap-render + git reset --hard origin/master + + - name: 'Generate Maps' + run: './tools/nanomap_renderer/nanomap-renderer-invoker.sh' + + - name: 'Commit Maps and open PR' + run: | + git config --local user.email "action@github.com" + git config --local user.name "NanoMap Generation" + git pull origin master + git commit -m "NanoMap Auto-Update (`date`)" -a || true + git push -f -u origin nanomap-render + gh pr create -t "Automatic NanoMap Update" -b "This pull request updates the server NanoMaps. Please review the diff images before merging." -l "NanoMaps" -H "nanomap-render" -B "master" diff --git a/tgui/packages/tgui/components/NanoMap.js b/tgui/packages/tgui/components/NanoMap.js new file mode 100644 index 0000000000000..f06141047238b --- /dev/null +++ b/tgui/packages/tgui/components/NanoMap.js @@ -0,0 +1,224 @@ +// BANDASTATION ADDITION + +import { resolveAsset } from '../assets'; +import { useBackend } from '../backend'; +import { Box, Button, Icon, Tooltip } from '.'; +import { LabeledList } from './LabeledList'; +import { Slider } from './Slider'; + +const pauseEvent = (e) => { + if (e.stopPropagation) { + e.stopPropagation(); + } + if (e.preventDefault) { + e.preventDefault(); + } + e.cancelBubble = true; + e.returnValue = false; + return false; +}; + +export class NanoMap extends Component { + constructor(props) { + super(props); + + // Auto center based on window size + const Xcenter = window.innerWidth / 2 - 256; + const Ycenter = window.innerHeight / 2 - 256; + + this.state = { + offsetX: 128, + offsetY: 48, + transform: 'none', + dragging: false, + originX: null, + originY: null, + zoom: 1, + }; + + // Dragging + this.handleDragStart = (e) => { + this.ref = e.target; + this.setState({ + dragging: false, + originX: e.screenX, + originY: e.screenY, + }); + document.addEventListener('mousemove', this.handleDragMove); + document.addEventListener('mouseup', this.handleDragEnd); + pauseEvent(e); + }; + + this.handleDragMove = (e) => { + this.setState((prevState) => { + const state = { ...prevState }; + const newOffsetX = e.screenX - state.originX; + const newOffsetY = e.screenY - state.originY; + if (prevState.dragging) { + state.offsetX += newOffsetX; + state.offsetY += newOffsetY; + state.originX = e.screenX; + state.originY = e.screenY; + } else { + state.dragging = true; + } + return state; + }); + pauseEvent(e); + }; + + this.handleDragEnd = (e) => { + this.setState({ + dragging: false, + originX: null, + originY: null, + }); + document.removeEventListener('mousemove', this.handleDragMove); + document.removeEventListener('mouseup', this.handleDragEnd); + pauseEvent(e); + }; + + this.handleZoom = (_e, value) => { + this.setState((state) => { + const newZoom = Math.min(Math.max(value, 1), 8); + let zoomDiff = (newZoom - state.zoom) * 1.5; + state.zoom = newZoom; + state.offsetX = state.offsetX - 262 * zoomDiff; + state.offsetY = state.offsetY - 256 * zoomDiff; + if (props.onZoom) { + props.onZoom(state.zoom); + } + return state; + }); + }; + } + + render() { + const { config } = useBackend(this.context); + const { dragging, offsetX, offsetY, zoom = 1 } = this.state; + const { children } = this.props; + + const mapUrl = config.map + '_nanomap_z1.png'; + const mapSize = 510 * zoom + 'px'; + const newStyle = { + width: mapSize, + height: mapSize, + 'margin-top': offsetY + 'px', + 'margin-left': offsetX + 'px', + overflow: 'hidden', + position: 'relative', + 'background-size': 'cover', + 'background-repeat': 'no-repeat', + 'text-align': 'center', + cursor: dragging ? 'move' : 'auto', + }; + const mapStyle = { + width: '100%', + height: '100%', + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + '-ms-interpolation-mode': 'nearest-neighbor', + }; + + return ( + + + + {children} + + + + ); + } +} + +const NanoMapMarker = (props, context) => { + const { x, y, zoom = 1, icon, tooltip, color } = props; + const rx = x * 2 * zoom - zoom - 3; + const ry = y * 2 * zoom - zoom - 3; + return ( +
+ + + + + +
+ ); +}; + +NanoMap.Marker = NanoMapMarker; + +const NanoMapZoomer = (props, context) => { + return ( + + + + v + 'x'} + value={props.zoom} + onDrag={(e, v) => props.onZoom(e, v)} + /> + + + + ); +}; + +NanoMap.Zoomer = NanoMapZoomer; + +let ActiveButton; +class NanoButton extends Component { + constructor(props) { + super(props); + const { act } = useBackend(this.props.context); + this.state = { + color: this.props.color, + }; + this.handleClick = (e) => { + if (ActiveButton !== undefined) { + ActiveButton.setState({ + color: 'blue', + }); + } + act('switch_camera', { + name: this.props.name, + }); + ActiveButton = this; + this.setState({ + color: 'green', + }); + }; + } + render() { + let rx = this.props.x * 2 * this.props.zoom - this.props.zoom - 3; + let ry = this.props.y * 2 * this.props.zoom - this.props.zoom - 3; + return ( + + ); + } +} +NanoMap.NanoButton = NanoButton; diff --git a/tgui/packages/tgui/components/index.ts b/tgui/packages/tgui/components/index.ts index 1a5f477d256b5..e591dc4cc9238 100644 --- a/tgui/packages/tgui/components/index.ts +++ b/tgui/packages/tgui/components/index.ts @@ -49,3 +49,6 @@ export { TimeDisplay } from './TimeDisplay'; export { Tooltip } from './Tooltip'; export { TrackOutsideClicks } from './TrackOutsideClicks'; export { VirtualList } from './VirtualList'; + +// BANDASTATION EDIT ADDITION +export { NanoMap } from './NanoMap'; diff --git a/tgui/packages/tgui/styles/components/NanoMap.scss b/tgui/packages/tgui/styles/components/NanoMap.scss new file mode 100644 index 0000000000000..acdb43ffb03a6 --- /dev/null +++ b/tgui/packages/tgui/styles/components/NanoMap.scss @@ -0,0 +1,35 @@ +// BANDASTATION ADDITION + +$background-color: rgba(0, 0, 0, 0.33) !default; + +.NanoMap__container { + overflow: hidden; + width: 100%; + z-index: 1; +} + +.NanoMap__marker { + z-index: 10; + padding: 0px; + margin: 0px; +} + +.NanoMap__button { + padding: 3px 3px; + font-size: 12px; + border: 2px solid black; +} + +.NanoMap__button:hover { + background-color: greenyellow; +} + +.NanoMap__zoomer { + z-index: 20; + background-color: $background-color; + position: absolute; + top: 30px; + left: 0; + padding: 0.5rem; + width: 20%; +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 91712be44d0da..c3bad8883a0e1 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -93,3 +93,6 @@ background-position: center; background-repeat: no-repeat; } + +// BANDASTATION EDIT ADDITION +@include meta.load-css('./components/NanoMap.scss'); diff --git a/tools/nanomap_renderer/README.MD b/tools/nanomap_renderer/README.MD new file mode 100644 index 0000000000000..74b31f5bdd699 --- /dev/null +++ b/tools/nanomap_renderer/README.MD @@ -0,0 +1,6 @@ +# GitHub Actions Scripts + +This folder contains all the script and tools required for GitHub actions. If you add something to this directory, **PLEASE** document it in here + +- `nanomap-renderer` - A linux application to render NanoMap images of the ingame maps automatically. Based off of SpacemanDMM (Modified source [here](https://github.com/AffectedArc07/ParaSpacemanDMM), original source [here](https://github.com/Spacemaniac/SpacemanDMM)) +- `nanomap-renderer-invoker.sh` - A script which invokes the render tool and clones the maps to the correct directory diff --git a/tools/nanomap_renderer/nanomap-renderer b/tools/nanomap_renderer/nanomap-renderer new file mode 100644 index 0000000000000..bd655ccb1f743 Binary files /dev/null and b/tools/nanomap_renderer/nanomap-renderer differ diff --git a/tools/nanomap_renderer/nanomap-renderer-invoker.sh b/tools/nanomap_renderer/nanomap-renderer-invoker.sh new file mode 100644 index 0000000000000..3129f2a1ccbce --- /dev/null +++ b/tools/nanomap_renderer/nanomap-renderer-invoker.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e +# Generate maps +tools/github-actions/nanomap-renderer minimap -w 2040 -h 2040 "./_maps/map_files/Birdshot/birdshot.dmm" +tools/github-actions/nanomap-renderer minimap -w 2040 -h 2040 "./_maps/map_files/Deltastation/DeltaStation2.dmm" +tools/github-actions/nanomap-renderer minimap -w 2040 -h 2040 "./_maps/map_files/IceBoxStation/IceBoxStation.dmm" +tools/github-actions/nanomap-renderer minimap -w 2040 -h 2040 "./_maps/map_files/MetaStation/MetaStation.dmm" +tools/github-actions/nanomap-renderer minimap -w 2040 -h 2040 "./_maps/map_files/Northstar/north_star.dmm" +tools/github-actions/nanomap-renderer minimap -w 2040 -h 2040 "./_maps/map_files/tramstation/tramstation.dmm" +# Move and rename files so the game understands them +cd "data/nanomaps" +mv "birdshot_nanomap_z1.png" "Birdshot Station_nanomap_z1.png" +mv "DeltaStation2_nanomap_z1.png" "Delta Station_nanomap_z1.png" +mv "IceBoxStation_nanomap_z1.png" "Ice Box Station_nanomap_z1.png" +mv "MetaStation_nanomap_z1.png" "MetaStation_nanomap_z1.png" +mv "north_star_nanomap_z1.png" "NorthStar_nanomap_z1.png" +mv "tramstation_nanomap_z1.png" "Tramstation_nanomap_z1.png" +cd "../../" +cp "data/nanomaps/Birdshot Station_nanomap_z1.png" "icons/_nanomaps" +cp "data/nanomaps/Delta Station_nanomap_z1.png" "icons/_nanomaps" +cp "data/nanomaps/Ice Box Station_nanomap_z1.png" "icons/_nanomaps" +cp "data/nanomaps/MetaStation_nanomap_z1.png" "icons/_nanomaps" +cp "data/nanomaps/NorthStar_nanomap_z1.png" "icons/_nanomaps" +cp "data/nanomaps/Tramstation_nanomap_z1.png" "icons/_nanomaps"