From 66d46a2e7c072060b4baa022ae2c148b622b63fa Mon Sep 17 00:00:00 2001 From: BurgerLUA Date: Wed, 30 May 2018 14:44:46 -0700 Subject: [PATCH 1/7] Small Disposal Fixes (#4826) Fixes disposal outlet items piling up on one tile, instead of the space around it. This should reduce lag when examining tiles in the crusher. Fixes #4824 --- code/modules/recycling/disposal.dm | 13 +- html/changelogs/burgerbb-disposalsfix.yml | 38 ++++++ maps/aurora/aurora-4_mainlevel.dmm | 147 ++++++++++++---------- 3 files changed, 130 insertions(+), 68 deletions(-) create mode 100644 html/changelogs/burgerbb-disposalsfix.yml diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 13a42ccca75..e7d1b095e57 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -1412,9 +1412,13 @@ var/turf/target // this will be where the output objects are 'thrown' to. var/mode = 0 + var/spread = 0 + var/spread_point = 10 + + /obj/structure/disposaloutlet/Initialize() . = ..() - target = get_ranged_target_turf(src, dir, 10) + target = get_ranged_target_turf(src, dir, spread_point) var/obj/structure/disposalpipe/trunk/trunk = locate() in src.loc if(trunk) @@ -1437,7 +1441,12 @@ AM.pipe_eject(dir) if(!istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z spawn(5) - AM.throw_at(target, 3, 1) + if(spread) + var/turf/new_turf_target = get_step(target,turn(src.dir, rand(-spread,spread))) + AM.throw_at(new_turf_target, 3, 1) + else + AM.throw_at(target, 3, 1) + H.vent_gas(src.loc) qdel(H) diff --git a/html/changelogs/burgerbb-disposalsfix.yml b/html/changelogs/burgerbb-disposalsfix.yml new file mode 100644 index 00000000000..d8a579a0c3e --- /dev/null +++ b/html/changelogs/burgerbb-disposalsfix.yml @@ -0,0 +1,38 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +# balance +################################# + +# Your name. +author: BurgerBB + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - bugfix: "Added vents to the cargo warehouse connector to prevent underpressure from disposal inlets." + - bugfix: "Disposal outlets now spread out items so all the trash doesn't pile up on one tile." diff --git a/maps/aurora/aurora-4_mainlevel.dmm b/maps/aurora/aurora-4_mainlevel.dmm index 1a2f700fa7f..f7a757137de 100644 --- a/maps/aurora/aurora-4_mainlevel.dmm +++ b/maps/aurora/aurora-4_mainlevel.dmm @@ -2375,6 +2375,19 @@ }, /turf/simulated/floor/tiled, /area/security/brig) +"aeG" = ( +/obj/effect/floor_decal/corner/brown{ + icon_state = "corner_white"; + dir = 5 + }, +/obj/effect/floor_decal/industrial/loading{ + icon_state = "loadingarea"; + dir = 1 + }, +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/atmospherics/unary/vent_pump/on, +/turf/simulated/floor/tiled, +/area/quartermaster/office) "aeH" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -2765,6 +2778,38 @@ /obj/machinery/seed_extractor, /turf/simulated/floor/tiled, /area/hydroponics) +"afz" = ( +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, +/turf/simulated/floor/tiled, +/area/quartermaster/office) +"afA" = ( +/obj/machinery/camera/network/supply{ + c_tag = "Cargo - Warehouse Entrence"; + dir = 1 + }, +/obj/effect/floor_decal/corner/brown{ + dir = 10 + }, +/obj/machinery/light/small, +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 1 + }, +/turf/simulated/floor/tiled, +/area/quartermaster/storage) +"afB" = ( +/obj/structure/sign/drop{ + pixel_y = 32 + }, +/turf/simulated/open, +/area/maintenance/disposal) "afC" = ( /obj/structure/closet{ name = "Evidence Closet" @@ -2851,6 +2896,33 @@ }, /turf/simulated/floor/tiled, /area/security/brig) +"afO" = ( +/obj/machinery/light/small/emergency, +/turf/simulated/open, +/area/maintenance/disposal) +"afP" = ( +/obj/structure/disposaloutlet{ + spread = 180; + spread_point = 1 + }, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/turf/simulated/floor/tiled/airless{ + icon_state = "asteroidfloor" + }, +/area/maintenance/disposal) +"afQ" = ( +/obj/structure/disposalpipe/trunk{ + dir = 4 + }, +/obj/structure/disposaloutlet{ + dir = 8; + spread = 360; + spread_point = 2 + }, +/turf/simulated/floor/plating, +/area/maintenance/disposal) "afX" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, /turf/simulated/floor/tiled/dark{ @@ -57431,18 +57503,6 @@ /obj/item/weapon/wrapping_paper, /turf/simulated/floor/tiled, /area/quartermaster/office) -"cad" = ( -/obj/effect/floor_decal/corner/brown{ - icon_state = "corner_white"; - dir = 5 - }, -/obj/effect/floor_decal/industrial/loading{ - icon_state = "loadingarea"; - dir = 1 - }, -/obj/effect/floor_decal/industrial/outline/yellow, -/turf/simulated/floor/tiled, -/area/quartermaster/office) "cae" = ( /obj/effect/floor_decal/corner/brown/full{ icon_state = "corner_white_full"; @@ -57756,17 +57816,6 @@ /obj/effect/large_stock_marker, /turf/simulated/floor/tiled, /area/quartermaster/office) -"caM" = ( -/obj/machinery/camera/network/supply{ - c_tag = "Cargo - Warehouse Entrence"; - dir = 1 - }, -/obj/effect/floor_decal/corner/brown{ - dir = 10 - }, -/obj/machinery/light/small, -/turf/simulated/floor/tiled, -/area/quartermaster/storage) "caN" = ( /obj/effect/floor_decal/corner/brown/full{ icon_state = "corner_white_full"; @@ -58325,17 +58374,6 @@ }, /turf/simulated/floor/tiled, /area/maintenance/disposal) -"ccd" = ( -/obj/structure/lattice/catwalk, -/turf/simulated/open, -/area/maintenance/disposal) -"cce" = ( -/obj/structure/lattice/catwalk, -/obj/structure/sign/drop{ - pixel_y = 32 - }, -/turf/simulated/open, -/area/maintenance/disposal) "ccf" = ( /turf/simulated/open, /area/maintenance/disposal) @@ -58532,15 +58570,6 @@ }, /turf/simulated/floor/tiled, /area/maintenance/disposal) -"ccA" = ( -/obj/structure/disposalpipe/trunk{ - dir = 4 - }, -/obj/structure/disposaloutlet{ - dir = 8 - }, -/turf/simulated/floor/plating, -/area/maintenance/disposal) "ccB" = ( /obj/structure/cable{ d1 = 1; @@ -58767,11 +58796,6 @@ }, /turf/simulated/floor/tiled, /area/maintenance/disposal) -"ccW" = ( -/obj/machinery/light/small/emergency, -/obj/structure/lattice/catwalk, -/turf/simulated/open, -/area/maintenance/disposal) "ccX" = ( /obj/machinery/light/small/emergency, /obj/structure/closet/crate, @@ -58843,15 +58867,6 @@ }, /turf/simulated/wall/r_wall, /area/maintenance/disposal) -"cdi" = ( -/obj/structure/disposaloutlet, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/turf/simulated/floor/tiled/airless{ - icon_state = "asteroidfloor" - }, -/area/maintenance/disposal) "cdj" = ( /obj/machinery/light/small/emergency{ dir = 8 @@ -89280,9 +89295,9 @@ bWF bWm cih cim -cad -bXT -caM +aeG +afz +afA bWm cbs cbM @@ -94425,9 +94440,9 @@ bUz bPO bPK cbD -ccd ccf -ccd +ccf +ccf cdh aaa aab @@ -94682,9 +94697,9 @@ bUz caW bPL cbE -cce +afB ccf -ccW +afO cdh aab aab @@ -95197,7 +95212,7 @@ bPO bPL cbE cbE -ccA +afQ cbE cdh cdt @@ -95456,7 +95471,7 @@ cbF ccg cbD cbE -cdi +afP cdu cdE aab From 5397e798c65bf58ff33fd5a3d9535e1f44e5fc95 Mon Sep 17 00:00:00 2001 From: Mykhailo Bykhovtsev Date: Fri, 1 Jun 2018 10:51:31 -0700 Subject: [PATCH 2/7] Hostile AI fix (#4839) When I made an update to hostile AI, I have not seen this stupid bug. This one variable makes them ignore people with full health. I urge to merge it ASAP as carps and spiders won't attack people with 100 health. fixes #4836 Also this makes hostile mobs to destroy surroundings if there is no-one to attack. --- code/modules/mob/living/simple_animal/hostile/hostile.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 21bce1f0d1b..10dcac507f3 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -33,7 +33,7 @@ return null var/atom/T = null - var/lowest_health = 100 + var/lowest_health = INFINITY // Max you can get stop_automated_movement = 0 for(var/atom/A in targets) @@ -192,6 +192,8 @@ mob/living/simple_animal/hostile/hitby(atom/movable/AM as mob|obj,var/speed = TH if(HOSTILE_STANCE_IDLE) targets = ListTargets(10) target_mob = FindTarget() + if(isnull(target_mob)) + DestroySurroundings() if(HOSTILE_STANCE_ATTACK) if(destroy_surroundings) From 1d335dd9e988d87518c5ec503f1eea306d12e518 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Fri, 1 Jun 2018 15:54:51 -0500 Subject: [PATCH 3/7] Add mapmerge 2 (#4645) Adds TG's mapmerge2, a new TGM-compatible merger that handles things like dictionary expansion much better than the old tool, as well as supporting a git merge hook for map files. This PR does not remove the old merger as this one is more annoying to setup. --- tools/hooks/README.md | 36 ++ tools/hooks/install.bat | 14 + tools/hooks/install.sh | 12 + tools/hooks/pre-commit.hook | 2 + tools/hooks/python.sh | 17 + tools/mapmerge2/Prepare Maps - Aurora.bat | 12 + tools/mapmerge2/Prepare Maps - Exodus.bat | 12 + tools/mapmerge2/Prepare Maps - Runtime.bat | 12 + tools/mapmerge2/README.md | 52 +++ tools/mapmerge2/convert.py | 8 + tools/mapmerge2/dmm.py | 488 +++++++++++++++++++++ tools/mapmerge2/dmm2tgm.bat | 5 + tools/mapmerge2/frontend.py | 127 ++++++ tools/mapmerge2/mapmerge.bat | 5 + tools/mapmerge2/mapmerge.py | 94 ++++ tools/mapmerge2/precommit.py | 50 +++ tools/mapmerge2/requirements-install.bat | 3 + tools/mapmerge2/requirements.txt | 2 + tools/mapmerge2/tgm2dmm.bat | 5 + 19 files changed, 956 insertions(+) create mode 100644 tools/hooks/README.md create mode 100644 tools/hooks/install.bat create mode 100644 tools/hooks/install.sh create mode 100644 tools/hooks/pre-commit.hook create mode 100644 tools/hooks/python.sh create mode 100644 tools/mapmerge2/Prepare Maps - Aurora.bat create mode 100644 tools/mapmerge2/Prepare Maps - Exodus.bat create mode 100644 tools/mapmerge2/Prepare Maps - Runtime.bat create mode 100644 tools/mapmerge2/README.md create mode 100644 tools/mapmerge2/convert.py create mode 100644 tools/mapmerge2/dmm.py create mode 100644 tools/mapmerge2/dmm2tgm.bat create mode 100644 tools/mapmerge2/frontend.py create mode 100644 tools/mapmerge2/mapmerge.bat create mode 100644 tools/mapmerge2/mapmerge.py create mode 100644 tools/mapmerge2/precommit.py create mode 100644 tools/mapmerge2/requirements-install.bat create mode 100644 tools/mapmerge2/requirements.txt create mode 100644 tools/mapmerge2/tgm2dmm.bat diff --git a/tools/hooks/README.md b/tools/hooks/README.md new file mode 100644 index 00000000000..844f3a3952f --- /dev/null +++ b/tools/hooks/README.md @@ -0,0 +1,36 @@ +# Git Integration Hooks + +This folder contains installable scripts for [Git hooks] and [merge drivers]. +Use of these hooks and drivers is optional and they must be installed +explicitly before they take effect. + +To install the current set of hooks, or update if new hooks are added, run +`install.bat` (Windows) or `install.sh` (Unix-like) as appropriate. + +Hooks expect a Unix-like environment on the backend. Usually this is handled +automatically by GUI tools like TortoiseGit and GitHub for Windows, but +[Git for Windows] is an option if you prefer to use a CLI even on Windows. + +## Current Hooks + +* **Pre-commit**: Runs [mapmerge2] on changed maps, if any. + +## Adding New Hooks + +New [Git hooks] may be added by creating a file named `.hook` in +this directory. Git determines what hooks are available and what their names +are. The install script copies the `.hook` file into `.git/hooks`, so editing +the `.hook` file will require a reinstall. + +New [merge drivers] may be added by adding a shell script named `.merge` +and updating `.gitattributes` in the root of the repository to include the line +`*. merge=`. The install script will set up the merge driver to point +to the `.merge` file directly, and editing it will not require a reinstall. + +`tools/hooks/python.sh` may be used as a trampoline to ensure that the correct +version of Python is found. + +[Git hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks +[merge drivers]: https://git-scm.com/docs/gitattributes#_performing_a_three_way_merge +[Git for Windows]: https://gitforwindows.org/ +[mapmerge2]: ../mapmerge2/README.md diff --git a/tools/hooks/install.bat b/tools/hooks/install.bat new file mode 100644 index 00000000000..c4f864b5c68 --- /dev/null +++ b/tools/hooks/install.bat @@ -0,0 +1,14 @@ +@echo off +cd %~dp0 +for %%f in (*.hook) do ( + echo Installing hook: %%~nf + copy %%f ..\..\.git\hooks\%%~nf >nul +) +for %%f in (*.merge) do ( + echo Installing merge driver: %%~nf + echo [merge "%%~nf"]^ + + driver = tools/hooks/%%f %%P %%O %%A %%B %%L >> ..\..\.git\config +) +echo Done +pause diff --git a/tools/hooks/install.sh b/tools/hooks/install.sh new file mode 100644 index 00000000000..32183a7ce89 --- /dev/null +++ b/tools/hooks/install.sh @@ -0,0 +1,12 @@ +#!/bin/bash +shopt -s nullglob +cd "$(dirname "$0")" +for f in *.hook; do + echo Installing hook: ${f%.hook} + cp $f ../../.git/hooks/${f%.hook} +done +for f in *.merge; do + echo Installing merge driver: ${f%.merge} + git config --replace-all merge.${f%.merge}.driver "tools/hooks/$f %P %O %A %B %L" +done +echo "Done" diff --git a/tools/hooks/pre-commit.hook b/tools/hooks/pre-commit.hook new file mode 100644 index 00000000000..7eccda6f58d --- /dev/null +++ b/tools/hooks/pre-commit.hook @@ -0,0 +1,2 @@ +#!/bin/bash +exec tools/hooks/python.sh -m precommit diff --git a/tools/hooks/python.sh b/tools/hooks/python.sh new file mode 100644 index 00000000000..32557070f48 --- /dev/null +++ b/tools/hooks/python.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e +if command -v python3 >/dev/null 2>&1; then + PY=python3 +else + PY=python +fi +PATHSEP=$($PY - <<'EOF' +import sys, os +if sys.version_info.major != 3 or sys.version_info.minor < 6: + sys.stderr.write("Python 3.6+ is required: " + sys.version + "\n") + exit(1) +print(os.pathsep) +EOF +) +export PYTHONPATH=tools/mapmerge2/${PATHSEP}${PYTHONPATH} +$PY "$@" diff --git a/tools/mapmerge2/Prepare Maps - Aurora.bat b/tools/mapmerge2/Prepare Maps - Aurora.bat new file mode 100644 index 00000000000..5cc98794819 --- /dev/null +++ b/tools/mapmerge2/Prepare Maps - Aurora.bat @@ -0,0 +1,12 @@ +@echo off +cd ../../maps/aurora + +for /R %%f in (*.dmm) do copy "%%f" "%%f.backup" + +cls +echo All dmm files in the maps/aurora directory have been backed up. +echo Now you can make your changes... +echo --- +echo Remember to run mapmerge.bat just before you commit your changes! +echo --- +pause diff --git a/tools/mapmerge2/Prepare Maps - Exodus.bat b/tools/mapmerge2/Prepare Maps - Exodus.bat new file mode 100644 index 00000000000..bc3e0179f87 --- /dev/null +++ b/tools/mapmerge2/Prepare Maps - Exodus.bat @@ -0,0 +1,12 @@ +@echo off +cd ../../maps/exodus + +for /R %%f in (*.dmm) do copy "%%f" "%%f.backup" + +cls +echo All dmm files in the maps/exodus directory have been backed up. +echo Now you can make your changes... +echo --- +echo Remember to run mapmerge.bat just before you commit your changes! +echo --- +pause diff --git a/tools/mapmerge2/Prepare Maps - Runtime.bat b/tools/mapmerge2/Prepare Maps - Runtime.bat new file mode 100644 index 00000000000..2c3e15a68d4 --- /dev/null +++ b/tools/mapmerge2/Prepare Maps - Runtime.bat @@ -0,0 +1,12 @@ +@echo off +cd ../../maps/runtime + +for /R %%f in (*.dmm) do copy "%%f" "%%f.backup" + +cls +echo All dmm files in the maps/runtime directory have been backed up. +echo Now you can make your changes... +echo --- +echo Remember to run mapmerge.bat just before you commit your changes! +echo --- +pause diff --git a/tools/mapmerge2/README.md b/tools/mapmerge2/README.md new file mode 100644 index 00000000000..0ff4d21ac2e --- /dev/null +++ b/tools/mapmerge2/README.md @@ -0,0 +1,52 @@ +# Map Merge 2 + +**Map Merge 2** is an improvement over previous map merging scripts, with +better merge-conflict prevention, multi-Z support, and automatic handling of +key overflow. For up-to-date tips and tricks, also visit the [Map Merger] wiki article. + +## What Map Merging Is + +The "map merge" operation describes the process of rewriting a map file written +by the DreamMaker map editor to A) use a format more amenable to Git's conflict +resolution and B) differ in the least amount textually from the previous +version of the map while maintaining all the actual changes. It requires an old +version of the map to use as a reference and a new version of the map which +contains the desired changes. + +## Installation + +To install Python dependencies, run `requirements-install.bat`, or run +`python -m pip install -r requirements.txt` directly. See the [Git hooks] +documentation to install the Git pre-commit hook which runs the map merger +automatically, or use `tools/mapmerge/Prepare Maps.bat` to save backups before +running `mapmerge.bat`. + +For up-to-date installation and detailed troubleshooting instructions, visit +the [Map Merger] wiki article. + +## Code Structure + +Frontend scripts are meant to be run directly. They obey the environment +variables `TGM` to set whether files are saved in TGM (1) or DMM (0) format, +and `MAPROOT` to determine where maps are kept. By default, TGM is used and +the map root is autodetected. Each script may either prompt for the desired map +or be run with command-line parameters indicating which maps to act on. The +scripts include: + +* `convert.py` for converting maps to and from the TGM format. Used by + `tgm2dmm.bat` and `dmm2tgm.bat`. +* `mapmerge.py` for running the map merge on map backups saved by + `Prepare Maps.bat`. Used by `mapmerge.bat` + +Implementation modules: + +* `dmm.py` includes the map reader and writer. +* `mapmerge.py` includes the implementation of the map merge operation. +* `frontend.py` includes the common code for the frontend scripts. + +`precommit.py` is run by the [Git hooks] if installed, and merges the new +version of any map saved in the index (`git add`ed) with the old version stored +in Git when run. + +[Map Merger]: https://tgstation13.org/wiki/Map_Merger +[Git hooks]: ../hooks/README.md diff --git a/tools/mapmerge2/convert.py b/tools/mapmerge2/convert.py new file mode 100644 index 00000000000..35e5dda4433 --- /dev/null +++ b/tools/mapmerge2/convert.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +import frontend +import dmm + +if __name__ == '__main__': + settings = frontend.read_settings() + for fname in frontend.process(settings, "convert"): + dmm.DMM.from_file(fname).to_file(fname, settings.tgm) diff --git a/tools/mapmerge2/dmm.py b/tools/mapmerge2/dmm.py new file mode 100644 index 00000000000..d76f07e32c6 --- /dev/null +++ b/tools/mapmerge2/dmm.py @@ -0,0 +1,488 @@ +# Tools for working with DreamMaker maps + +import io +import bidict +import random +from collections import namedtuple + +TGM_HEADER = "//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE" +ENCODING = 'utf-8' + +Coordinate = namedtuple('Coordinate', ['x', 'y', 'z']) + +class DMM: + __slots__ = ['key_length', 'size', 'dictionary', 'grid', 'header'] + + def __init__(self, key_length, size): + self.key_length = key_length + self.size = size + self.dictionary = bidict.bidict() + self.grid = {} + self.header = None + + @staticmethod + def from_file(fname): + # stream the file rather than forcing all its contents to memory + with open(fname, 'r', encoding=ENCODING) as f: + return _parse(iter(lambda: f.read(1), '')) + + @staticmethod + def from_bytes(bytes): + return _parse(bytes.decode(ENCODING)) + + def to_file(self, fname, tgm = True): + self._presave_checks() + with open(fname, 'w', newline='\n', encoding=ENCODING) as f: + (save_tgm if tgm else save_dmm)(self, f) + + def to_bytes(self, tgm = True): + self._presave_checks() + bio = io.BytesIO() + with io.TextIOWrapper(bio, newline='\n', encoding=ENCODING) as f: + (save_tgm if tgm else save_dmm)(self, f) + f.flush() + return bio.getvalue() + + def generate_new_key(self): + free_keys = self._ensure_free_keys(1) + # choose one of the free keys at random + key = 0 + while free_keys: + if key not in self.dictionary: + # this construction is used to avoid needing to construct the + # full set in order to random.choice() from it + if random.random() < 1 / free_keys: + return key + free_keys -= 1 + key += 1 + + raise RuntimeError("ran out of keys, this shouldn't happen") + + def _presave_checks(self): + # last-second handling of bogus keys to help prevent and fix broken maps + self._ensure_free_keys(0) + max_key = max_key_for(self.key_length) + bad_keys = {key: 0 for key in self.dictionary.keys() if key > max_key} + if bad_keys: + print(f"Warning: fixing {len(bad_keys)} overflowing keys") + for k in bad_keys: + # create a new non-bogus key and transfer that value to it + new_key = bad_keys[k] = self.generate_new_key() + self.dictionary.forceput(new_key, self.dictionary[k]) + print(f" {num_to_key(k, self.key_length, True)} -> {num_to_key(new_key, self.key_length)}") + for k, v in self.grid.items(): + # reassign the grid entries which used the old key + self.grid[k] = bad_keys.get(v, v) + + def _ensure_free_keys(self, desired): + # ensure that free keys exist by increasing the key length if necessary + free_keys = max_key_for(self.key_length) - len(self.dictionary) + while free_keys < desired: + if self.key_length >= MAX_KEY_LENGTH: + raise KeyTooLarge(f"can't expand beyond key length {MAX_KEY_LENGTH} ({len(self.dictionary)} keys)") + self.key_length += 1 + free_keys = max_key_for(self.key_length) - len(self.dictionary) + return free_keys + + @property + def coords_zyx(self): + for z in range(1, self.size.z + 1): + for y in range(1, self.size.y + 1): + for x in range(1, self.size.x + 1): + yield (z, y, x) + + @property + def coords_z(self): + return range(1, self.size.z + 1) + + @property + def coords_yx(self): + for y in range(1, self.size.y + 1): + for x in range(1, self.size.x + 1): + yield (y, x) + +# ---------- +# key handling + +# Base 52 a-z A-Z dictionary for fast conversion +MAX_KEY_LENGTH = 3 # things will get ugly fast if you exceed this +BASE = 52 +base52 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +base52_r = {x: i for i, x in enumerate(base52)} +assert len(base52) == BASE and len(base52_r) == BASE + +def key_to_num(key): + num = 0 + for ch in key: + num = BASE * num + base52_r[ch] + return num + +def num_to_key(num, key_length, allow_overflow=False): + if num >= (BASE ** key_length if allow_overflow else max_key_for(key_length)): + raise KeyTooLarge(f"num={num} does not fit in key_length={key_length}") + + result = '' + while num: + result = base52[num % BASE] + result + num //= BASE + + assert len(result) <= key_length + return base52[0] * (key_length - len(result)) + result + +def max_key_for(key_length): + # keys only go up to "ymo" = 65534, under-estimated just in case + # https://secure.byond.com/forum/?post=2340796#comment23770802 + return min(65530, BASE ** key_length) + +class KeyTooLarge(Exception): + pass + +# ---------- +# An actual atom parser + +def parse_map_atom(atom): + try: + i = atom.index('{') + except ValueError: + return atom, {} + + path, rest = atom[:i], atom[i+1:] + vars = {} + + in_string = False + in_name = False + escaping = False + current_name = '' + current = '' + for ch in rest: + if escaping: + escaping = False + current += ch + elif ch == '\\': + escaping = True + elif ch == '"': + in_string = not in_string + current += ch + elif in_string: + current += ch + elif ch == ';': + vars[current_name.strip()] = current.strip() + current_name = current = '' + elif ch == '=': + current_name = current + current = '' + elif ch == '}': + vars[current_name.strip()] = current.strip() + break + elif ch not in ' ': + current += ch + + return path, vars + +# ---------- +# TGM writer + +def save_tgm(dmm, output): + output.write(f"{TGM_HEADER}\n") + if dmm.header: + output.write(f"{dmm.header}\n") + + # write dictionary in tgm format + for key, value in sorted(dmm.dictionary.items()): + output.write(f'"{num_to_key(key, dmm.key_length)}" = (\n') + for idx, thing in enumerate(value): + in_quote_block = False + in_varedit_block = False + for char in thing: + if in_quote_block: + if char == '"': + in_quote_block = False + output.write(char) + elif char == '"': + in_quote_block = True + output.write(char) + elif not in_varedit_block: + if char == "{": + in_varedit_block = True + output.write("{\n\t") + else: + output.write(char) + elif char == ";": + output.write(";\n\t") + elif char == "}": + output.write("\n\t}") + in_varedit_block = False + else: + output.write(char) + if idx < len(value) - 1: + output.write(",\n") + output.write(")\n") + + # thanks to YotaXP for finding out about this one + max_x, max_y, max_z = dmm.size + for z in range(1, max_z + 1): + output.write("\n") + for x in range(1, max_x + 1): + output.write(f"({x},{1},{z}) = {{\"\n") + for y in range(1, max_y + 1): + output.write(f"{num_to_key(dmm.grid[x, y, z], dmm.key_length)}\n") + output.write("\"}\n") + +# ---------- +# DMM writer + +def save_dmm(dmm, output): + if dmm.header: + output.write(f"{dmm.header}\n") + + # writes a tile dictionary the same way Dreammaker does + for key, value in sorted(dmm.dictionary.items()): + output.write(f'"{num_to_key(key, dmm.key_length)}" = ({",".join(value)})\n') + + output.write("\n") + + # writes a map grid the same way Dreammaker does + max_x, max_y, max_z = dmm.size + for z in range(1, max_z + 1): + output.write(f"(1,1,{z}) = {{\"\n") + + for y in range(1, max_y + 1): + for x in range(1, max_x + 1): + try: + output.write(num_to_key(dmm.grid[x, y, z], dmm.key_length)) + except KeyError: + print(f"Key error: ({x}, {y}, {z})") + output.write("\n") + output.write("\"}\n") + +# ---------- +# Parser + +def _parse(map_raw_text): + in_comment_line = False + comment_trigger = False + + in_quote_block = False + in_key_block = False + in_data_block = False + in_varedit_block = False + after_data_block = False + escaping = False + skip_whitespace = False + + dictionary = bidict.bidict() + duplicate_keys = {} + curr_key_len = 0 + curr_key = 0 + curr_datum = "" + curr_data = list() + + in_map_block = False + in_coord_block = False + in_map_string = False + iter_x = 0 + adjust_y = True + + curr_num = "" + reading_coord = "x" + + key_length = 0 + + maxx = 0 + maxy = 0 + maxz = 0 + + curr_x = 0 + curr_y = 0 + curr_z = 0 + grid = dict() + + it = iter(map_raw_text) + + # map block + for char in it: + if char == "\n": + in_comment_line = False + comment_trigger = False + continue + elif in_comment_line: + continue + elif char == "\t": + continue + + if char == "/" and not in_quote_block: + if comment_trigger: + in_comment_line = True + continue + else: + comment_trigger = True + else: + comment_trigger = False + + if in_data_block: + + if in_varedit_block: + + if in_quote_block: + if char == "\\": + curr_datum = curr_datum + char + escaping = True + + elif escaping: + curr_datum = curr_datum + char + escaping = False + + elif char == "\"": + curr_datum = curr_datum + char + in_quote_block = False + + else: + curr_datum = curr_datum + char + + else: + if skip_whitespace and char == " ": + skip_whitespace = False + continue + skip_whitespace = False + + if char == "\"": + curr_datum = curr_datum + char + in_quote_block = True + + elif char == ";": + skip_whitespace = True + curr_datum = curr_datum + char + + elif char == "}": + curr_datum = curr_datum + char + in_varedit_block = False + + else: + curr_datum = curr_datum + char + + elif char == "{": + curr_datum = curr_datum + char + in_varedit_block = True + + elif char == ",": + curr_data.append(curr_datum) + curr_datum = "" + + elif char == ")": + curr_data.append(curr_datum) + curr_data = tuple(curr_data) + try: + dictionary[curr_key] = curr_data + except bidict.ValueDuplicationError: + # if the map has duplicate values, eliminate them now + duplicate_keys[curr_key] = dictionary.inv[curr_data] + curr_data = list() + curr_datum = "" + curr_key = 0 + curr_key_len = 0 + in_data_block = False + after_data_block = True + + else: + curr_datum = curr_datum + char + + elif in_key_block: + if char == "\"": + in_key_block = False + if key_length == 0: + key_length = curr_key_len + else: + assert key_length == curr_key_len + else: + curr_key = BASE * curr_key + base52_r[char] + curr_key_len += 1 + + # else we're looking for a key block, a data block or the map block + elif char == "\"": + in_key_block = True + after_data_block = False + + elif char == "(": + if after_data_block: + in_coord_block = True + after_data_block = False + curr_key = 0 + curr_key_len = 0 + break + else: + in_data_block = True + after_data_block = False + + # grid block + for char in it: + if in_coord_block: + if char == ",": + if reading_coord == "x": + curr_x = int(curr_num) + if curr_x > maxx: + maxx = curr_x + iter_x = 0 + curr_num = "" + reading_coord = "y" + elif reading_coord == "y": + curr_y = int(curr_num) + if curr_y > maxy: + maxy = curr_y + curr_num = "" + reading_coord = "z" + else: + raise ValueError("too many dimensions") + + elif char == ")": + curr_z = int(curr_num) + if curr_z > maxz: + maxz = curr_z + in_coord_block = False + reading_coord = "x" + curr_num = "" + + else: + curr_num = curr_num + char + + elif in_map_string: + if char == "\"": + in_map_string = False + adjust_y = True + curr_y -= 1 + + elif char == "\n": + if adjust_y: + adjust_y = False + else: + curr_y += 1 + if curr_x > maxx: + maxx = curr_x + if iter_x > 1: + curr_x = 1 + iter_x = 0 + + else: + curr_key = BASE * curr_key + base52_r[char] + curr_key_len += 1 + if curr_key_len == key_length: + iter_x += 1 + if iter_x > 1: + curr_x += 1 + + grid[curr_x, curr_y, curr_z] = duplicate_keys.get(curr_key, curr_key) + curr_key = 0 + curr_key_len = 0 + + # else look for coordinate block or a map string + elif char == "(": + in_coord_block = True + elif char == "\"": + in_map_string = True + + if curr_y > maxy: + maxy = curr_y + + data = DMM(key_length, Coordinate(maxx, maxy, maxz)) + data.dictionary = dictionary + data.grid = grid + return data diff --git a/tools/mapmerge2/dmm2tgm.bat b/tools/mapmerge2/dmm2tgm.bat new file mode 100644 index 00000000000..b17b03afa08 --- /dev/null +++ b/tools/mapmerge2/dmm2tgm.bat @@ -0,0 +1,5 @@ +@echo off +set MAPROOT=../../maps/ +set TGM=1 +python convert.py +pause diff --git a/tools/mapmerge2/frontend.py b/tools/mapmerge2/frontend.py new file mode 100644 index 00000000000..4d44eab951e --- /dev/null +++ b/tools/mapmerge2/frontend.py @@ -0,0 +1,127 @@ +# Common code for the frontend interface of map tools +import sys +import os +import pathlib +import shutil +from collections import namedtuple + +Settings = namedtuple('Settings', ['map_folder', 'tgm']) +MapsToRun = namedtuple('MapsToRun', ['files', 'indices']) + +def string_to_num(s): + try: + return int(s) + except ValueError: + return -1 + +def read_settings(): + # discover map folder if needed + try: + map_folder = os.environ['MAPROOT'] + except KeyError: + map_folder = '_maps/' + for _ in range(8): + if os.path.exists(map_folder): + break + map_folder = os.path.join('..', map_folder) + else: + map_folder = None + + # assume TGM is True by default + tgm = os.environ.get('TGM', "1") == "1" + + return Settings(map_folder, tgm) + +def pretty_path(settings, path_str): + if settings.map_folder: + return path_str[len(os.path.commonpath([settings.map_folder, path_str]))+1:] + else: + return path_str + +def prompt_maps(settings, verb): + if not settings.map_folder: + print("Could not autodetect the _maps folder, set MAPROOT") + exit(1) + + list_of_files = list() + for root, directories, filenames in os.walk(settings.map_folder): + for filename in [f for f in filenames if f.endswith(".dmm")]: + list_of_files.append(pathlib.Path(root, filename)) + + last_dir = "" + for i, this_file in enumerate(list_of_files): + this_dir = this_file.parent + if last_dir != this_dir: + print("--------------------------------") + last_dir = this_dir + print("[{}]: {}".format(i, pretty_path(settings, str(this_file)))) + + print("--------------------------------") + in_list = input("List the maps you want to " + verb + " (example: 1,3-5,12):\n") + in_list = in_list.replace(" ", "") + in_list = in_list.split(",") + + valid_indices = list() + for m in in_list: + index_range = m.split("-") + if len(index_range) == 1: + index = string_to_num(index_range[0]) + if index >= 0 and index < len(list_of_files): + valid_indices.append(index) + elif len(index_range) == 2: + index0 = string_to_num(index_range[0]) + index1 = string_to_num(index_range[1]) + if index0 >= 0 and index0 <= index1 and index1 < len(list_of_files): + valid_indices.extend(range(index0, index1 + 1)) + + return MapsToRun(list_of_files, valid_indices) + +def process(settings, verb, *, modify=True, backup=None): + if backup is None: + backup = modify # by default, backup when we modify + assert modify or not backup # doesn't make sense to backup when not modifying + + if len(sys.argv) > 1: + maps = sys.argv[1:] + else: + maps = prompt_maps(settings, verb) + maps = [str(maps.files[i]) for i in maps.indices] + print() + + if not maps: + print("No maps selected.") + return + + if modify: + print(f"Maps WILL{'' if settings.tgm else ' NOT'} be converted to tgm.") + if backup: + print("Backups will be created with a \".before\" extension.") + else: + print("Warning: backups are NOT being taken.") + + print(f"\nWill {verb} these maps:") + for path_str in maps: + print(pretty_path(settings, path_str)) + + try: + confirm = input(f"\nPress Enter to {verb}...\n") + except KeyboardInterrupt: + confirm = "^C" + if confirm != "": + print(f"\nAborted.") + return + + for path_str in maps: + print(f' - {pretty_path(settings, path_str)}') + + if backup: + shutil.copyfile(path_str, path_str + ".before") + + try: + yield path_str + except Exception as e: + print(f"Error: {e}") + else: + print("Succeeded.") + + print("\nFinished.") diff --git a/tools/mapmerge2/mapmerge.bat b/tools/mapmerge2/mapmerge.bat new file mode 100644 index 00000000000..e12c500bc83 --- /dev/null +++ b/tools/mapmerge2/mapmerge.bat @@ -0,0 +1,5 @@ +@echo off +set MAPROOT=../../maps/ +set TGM=1 +python mapmerge.py +pause diff --git a/tools/mapmerge2/mapmerge.py b/tools/mapmerge2/mapmerge.py new file mode 100644 index 00000000000..f449bd948f0 --- /dev/null +++ b/tools/mapmerge2/mapmerge.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +import frontend +import shutil +from dmm import * +from collections import defaultdict + +def merge_map(new_map, old_map, delete_unused=False): + if new_map.key_length != old_map.key_length: + print("Warning: Key lengths differ, taking new map") + print(f" Old: {old_map.key_length}") + print(f" New: {new_map.key_length}") + return new_map + + if new_map.size != old_map.size: + print("Warning: Map dimensions differ, taking new map") + print(f" Old: {old_map.size}") + print(f" New: {new_map.size}") + return new_map + + key_length, size = old_map.key_length, old_map.size + merged = DMM(key_length, size) + merged.dictionary = old_map.dictionary.copy() + + known_keys = dict() # mapping fron 'new' key to 'merged' key + unused_keys = set(old_map.dictionary.keys()) # keys going unused + + # step one: parse the new version, compare it to the old version, merge both + for z, y, x in new_map.coords_zyx: + new_key = new_map.grid[x, y, z] + # if this key has been processed before, it can immediately be merged + try: + merged.grid[x, y, z] = known_keys[new_key] + continue + except KeyError: + pass + + def select_key(assigned): + merged.grid[x, y, z] = known_keys[new_key] = assigned + + old_key = old_map.grid[x, y, z] + old_tile = old_map.dictionary[old_key] + new_tile = new_map.dictionary[new_key] + + # this tile is the exact same as before, so the old key is used + if new_tile == old_tile: + select_key(old_key) + unused_keys.remove(old_key) + + # the tile is different here, but if it exists in the merged dictionary, that key can be used + elif new_tile in merged.dictionary.inv: + newold_key = merged.dictionary.inv[new_tile] + select_key(newold_key) + unused_keys.remove(newold_key) + + # the tile is brand new and it needs a new key, but if the old key isn't being used any longer it can be used instead + elif old_tile not in new_map.dictionary.inv and old_key in unused_keys: + merged.dictionary[old_key] = new_tile + select_key(old_key) + unused_keys.remove(old_key) + + # all other options ruled out, a brand new key is generated for the brand new tile + else: + fresh_key = merged.generate_new_key() + merged.dictionary[fresh_key] = new_tile + select_key(fresh_key) + + # step two: delete unused keys + if unused_keys: + print(f"Notice: Trimming {len(unused_keys)} unused dictionary keys.") + for key in unused_keys: + del merged.dictionary[key] + + # sanity check: that the merged map equals the new map + for z, y, x in new_map.coords_zyx: + new_tile = new_map.dictionary[new_map.grid[x, y, z]] + merged_tile = merged.dictionary[merged.grid[x, y, z]] + if new_tile != merged_tile: + print(f"Error: the map has been mangled! This is a mapmerge bug!") + print(f"At {x},{y},{z}.") + print(f"Should be {new_tile}") + print(f"Instead is {merged_tile}") + raise RuntimeError() + + return merged + +def main(settings): + for fname in frontend.process(settings, "merge", backup=True): + shutil.copyfile(fname, fname + ".before") + old_map = DMM.from_file(fname + ".backup") + new_map = DMM.from_file(fname) + merge_map(new_map, old_map).to_file(fname, settings.tgm) + +if __name__ == '__main__': + main(frontend.read_settings()) diff --git a/tools/mapmerge2/precommit.py b/tools/mapmerge2/precommit.py new file mode 100644 index 00000000000..381f1ea8a3e --- /dev/null +++ b/tools/mapmerge2/precommit.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +import os +import pygit2 +import dmm +from mapmerge import merge_map + +def main(repo): + if repo.index.conflicts: + print("You need to resolve merge conflicts first.") + return 1 + + changed = 0 + for path, status in repo.status().items(): + if path.endswith(".dmm") and (status & (pygit2.GIT_STATUS_INDEX_MODIFIED | pygit2.GIT_STATUS_INDEX_NEW)): + # read the index + index_entry = repo.index[path] + index_map = dmm.DMM.from_bytes(repo[index_entry.id].read_raw()) + + try: + head_blob = repo[repo[repo.head.target].tree[path].id] + except KeyError: + # New map, no entry in HEAD + print(f"Converting new map: {path}") + assert (status & pygit2.GIT_STATUS_INDEX_NEW) + merged_map = index_map + else: + # Entry in HEAD, merge the index over it + print(f"Merging map: {path}") + assert not (status & pygit2.GIT_STATUS_INDEX_NEW) + head_map = dmm.DMM.from_bytes(head_blob.read_raw()) + merged_map = merge_map(index_map, head_map) + + # write to the index + blob_id = repo.create_blob(merged_map.to_bytes()) + repo.index.add(pygit2.IndexEntry(path, blob_id, index_entry.mode)) + changed += 1 + + # write to the working directory if that's clean + if status & (pygit2.GIT_STATUS_WT_DELETED | pygit2.GIT_STATUS_WT_MODIFIED): + print(f"Warning: {path} has unindexed changes, not overwriting them") + else: + merged_map.to_file(os.path.join(repo.workdir, path)) + + if changed: + repo.index.write() + print(f"Merged {changed} maps.") + return 0 + +if __name__ == '__main__': + exit(main(pygit2.Repository(pygit2.discover_repository(os.getcwd())))) diff --git a/tools/mapmerge2/requirements-install.bat b/tools/mapmerge2/requirements-install.bat new file mode 100644 index 00000000000..71341af8792 --- /dev/null +++ b/tools/mapmerge2/requirements-install.bat @@ -0,0 +1,3 @@ +@echo off +python -m pip install -r requirements.txt +pause diff --git a/tools/mapmerge2/requirements.txt b/tools/mapmerge2/requirements.txt new file mode 100644 index 00000000000..d01a2c6ccff --- /dev/null +++ b/tools/mapmerge2/requirements.txt @@ -0,0 +1,2 @@ +pygit2==0.26.0 +bidict==0.13.1 diff --git a/tools/mapmerge2/tgm2dmm.bat b/tools/mapmerge2/tgm2dmm.bat new file mode 100644 index 00000000000..5bf5677453c --- /dev/null +++ b/tools/mapmerge2/tgm2dmm.bat @@ -0,0 +1,5 @@ +@echo off +set MAPROOT=../../maps/ +set TGM=0 +python convert.py +pause From aa61badaf7b5df3ef08def0acf853961d23b5b2c Mon Sep 17 00:00:00 2001 From: Alberyk Date: Sat, 2 Jun 2018 12:16:34 -0300 Subject: [PATCH 4/7] Removes genetics for real now (#4831) -removes all genetics things from the map/rd -fixes #4829 -fully removes the geneticist job --- code/game/jobs/job/science.dm | 30 ------------------- code/game/jobs/jobs.dm | 16 +++++----- .../research/designs/circuit_designs.dm | 6 ---- maps/aurora/aurora-4_mainlevel.dmm | 29 ------------------ 4 files changed, 7 insertions(+), 74 deletions(-) diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index eaa1c4b7936..1f69a5ee14d 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -133,33 +133,3 @@ H.equip_to_slot_or_del(new /obj/item/weapon/storage/belt/utility/full(H), slot_belt) H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/labcoat(H), slot_wear_suit) return TRUE - -/datum/job/geneticist - title = "Geneticist" - flag = GENETICIST - department = "Science" - department_flag = MEDSCI - faction = "Station" - total_positions = 0 - spawn_positions = 0 - supervisors = "the research director" - selection_color = "#ffeeff" - economic_modifier = 7 - access = list(access_genetics, access_tox, access_tox_storage, access_morgue, access_research) - minimal_access = list(access_morgue, access_genetics, access_research) - - bag_type = /obj/item/weapon/storage/backpack/genetics - satchel_type = /obj/item/weapon/storage/backpack/satchel_gen - duffel_type = /obj/item/weapon/storage/backpack/duffel/gen - messenger_bag_type = /obj/item/weapon/storage/backpack/messenger/gen - - equip(var/mob/living/carbon/human/H) - if(!H) - return FALSE - H.equip_to_slot_or_del(new /obj/item/device/radio/headset/headset_sci(H), slot_l_ear) - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/geneticist(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/white(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/device/pda/geneticist(H), slot_belt) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/labcoat/genetics(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/device/flashlight/pen(H), slot_s_store) - return TRUE diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index 8736fa16874..c7c84264c75 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -23,14 +23,13 @@ var/const/SCIENTIST =(1<<1) var/const/CHEMIST =(1<<2) var/const/CMO =(1<<3) var/const/DOCTOR =(1<<4) -var/const/GENETICIST =(1<<5) -var/const/VIROLOGIST =(1<<6) -var/const/PSYCHIATRIST =(1<<7) -var/const/ROBOTICIST =(1<<8) -var/const/XENOBIOLOGIST =(1<<9) -var/const/PARAMEDIC =(1<<10) -var/const/INTERN_MED =(1<<11) -var/const/INTERN_SCI =(1<<12) +var/const/VIROLOGIST =(1<<5) +var/const/PSYCHIATRIST =(1<<6) +var/const/ROBOTICIST =(1<<7) +var/const/XENOBIOLOGIST =(1<<8) +var/const/PARAMEDIC =(1<<9) +var/const/INTERN_MED =(1<<10) +var/const/INTERN_SCI =(1<<11) var/const/CIVILIAN =(1<<2) @@ -85,7 +84,6 @@ var/list/medical_positions = list( var/list/science_positions = list( "Research Director", "Scientist", - "Geneticist", //Part of both medical and science "Roboticist", "Xenobiologist", "Lab Assistant" diff --git a/code/modules/research/designs/circuit_designs.dm b/code/modules/research/designs/circuit_designs.dm index 0a76c264fe4..b898445ba99 100644 --- a/code/modules/research/designs/circuit_designs.dm +++ b/code/modules/research/designs/circuit_designs.dm @@ -79,12 +79,6 @@ build_path = /obj/item/weapon/circuitboard/pandemic sort_string = "FAEAA" -/datum/design/circuit/scan_console - name = "DNA machine" - id = "scan_console" - build_path = /obj/item/weapon/circuitboard/scan_consolenew - sort_string = "FAGAA" - /datum/design/circuit/clonecontrol name = "cloning control console" id = "clonecontrol" diff --git a/maps/aurora/aurora-4_mainlevel.dmm b/maps/aurora/aurora-4_mainlevel.dmm index f7a757137de..e2a22d61e2c 100644 --- a/maps/aurora/aurora-4_mainlevel.dmm +++ b/maps/aurora/aurora-4_mainlevel.dmm @@ -7241,22 +7241,6 @@ dir = 1 }, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/machinery/door/window/southleft{ - dir = 4; - icon_state = "left"; - name = "Atmospheric Hardsuits"; - req_access = list(24) - }, -/obj/structure/table/rack, -/obj/item/clothing/suit/space/void/atmos, -/obj/item/clothing/head/helmet/space/void/atmos, -/obj/item/clothing/shoes/magboots, -/obj/item/clothing/mask/breath, -/obj/structure/window/reinforced{ - icon_state = "rwindow"; - dir = 1 - }, -/obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/door/window/southleft{ dir = 4; icon_state = "left"; @@ -7834,11 +7818,9 @@ /turf/simulated/floor/plating, /area/engineering/storage) "aqH" = ( -/obj/structure/table/rack, /obj/item/clothing/suit/space/void/atmos, /obj/item/clothing/head/helmet/space/void/atmos, /obj/item/clothing/shoes/magboots, -/obj/item/clothing/mask/breath, /obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/door/window/southleft{ dir = 4; @@ -7847,17 +7829,7 @@ req_access = list(24) }, /obj/structure/table/rack, -/obj/item/clothing/suit/space/void/atmos, -/obj/item/clothing/head/helmet/space/void/atmos, -/obj/item/clothing/shoes/magboots, /obj/item/clothing/mask/breath, -/obj/effect/floor_decal/industrial/outline/yellow, -/obj/machinery/door/window/southleft{ - dir = 4; - icon_state = "left"; - name = "Atmospheric Hardsuits"; - req_access = list(24) - }, /turf/simulated/floor/tiled, /area/engineering/atmos/storage) "aqI" = ( @@ -32802,7 +32774,6 @@ }, /obj/item/weapon/circuitboard/clonescanner, /obj/item/weapon/circuitboard/clonepod, -/obj/item/weapon/circuitboard/scan_consolenew, /obj/item/weapon/circuitboard/med_data{ pixel_x = 3; pixel_y = -3 From c22698ce74d5937cc0185ee03694adc023538404 Mon Sep 17 00:00:00 2001 From: Erki Date: Sun, 3 Jun 2018 12:31:10 +0300 Subject: [PATCH 5/7] Fixes a thing because aaaaaa. (#4840) --- code/modules/mob/living/simple_animal/hostile/hostile.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 10dcac507f3..bc75fdc929b 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -192,7 +192,7 @@ mob/living/simple_animal/hostile/hitby(atom/movable/AM as mob|obj,var/speed = TH if(HOSTILE_STANCE_IDLE) targets = ListTargets(10) target_mob = FindTarget() - if(isnull(target_mob)) + if(destroy_surroundings && isnull(target_mob)) DestroySurroundings() if(HOSTILE_STANCE_ATTACK) From 5c108a1ed7d6c9e9e0714f38d3c7afd8a288204f Mon Sep 17 00:00:00 2001 From: Werner Date: Sun, 3 Jun 2018 11:55:33 +0200 Subject: [PATCH 6/7] Fixes #3769 (#4849) Caused by a div by 0 runtime. --- code/modules/power/smes.dm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index f1d3fd386b2..d565ec72e94 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -333,7 +333,10 @@ // this is the data which will be sent to the ui var/data[0] data["nameTag"] = name_tag - data["storedCapacity"] = round(100.0*charge/capacity, 0.1) + if(capacity) + data["storedCapacity"] = round(100.0*charge/capacity, 0.1) + else + data["storedCapacity"] = 0 data["charging"] = inputting data["chargeMode"] = input_attempt data["chargeLevel"] = input_level From 598e7e94c0c4aaeb9d5034f9d40908ccaec24aec Mon Sep 17 00:00:00 2001 From: skull132 Date: Sun, 3 Jun 2018 12:58:56 +0300 Subject: [PATCH 7/7] Changelogs, 03JUN2018 --- html/changelog.html | 7 +++++ html/changelogs/.all_changelog.yml | 6 ++++ html/changelogs/burgerbb-disposalsfix.yml | 38 ----------------------- 3 files changed, 13 insertions(+), 38 deletions(-) delete mode 100644 html/changelogs/burgerbb-disposalsfix.yml diff --git a/html/changelog.html b/html/changelog.html index 8c4868a4f59..fe3aabae566 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,13 @@ -->
+

03 June 2018

+

BurgerBB updated:

+
    +
  • Added vents to the cargo warehouse connector to prevent underpressure from disposal inlets.
  • +
  • Disposal outlets now spread out items so all the trash doesn't pile up on one tile.
  • +
+

29 May 2018

Kaedwuff updated:

    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 26008dbe624..7e46944753a 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -5523,3 +5523,9 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2018-05-29: Kaedwuff: - bugfix: You can no longer slice fruit (or anything else) with a syringe. +2018-06-03: + BurgerBB: + - bugfix: Added vents to the cargo warehouse connector to prevent underpressure + from disposal inlets. + - bugfix: Disposal outlets now spread out items so all the trash doesn't pile up + on one tile. diff --git a/html/changelogs/burgerbb-disposalsfix.yml b/html/changelogs/burgerbb-disposalsfix.yml deleted file mode 100644 index d8a579a0c3e..00000000000 --- a/html/changelogs/burgerbb-disposalsfix.yml +++ /dev/null @@ -1,38 +0,0 @@ -################################ -# Example Changelog File -# -# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. -# -# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) -# When it is, any changes listed below will disappear. -# -# Valid Prefixes: -# bugfix -# wip (For works in progress) -# tweak -# soundadd -# sounddel -# rscadd (general adding of nice things) -# rscdel (general deleting of nice things) -# imageadd -# imagedel -# maptweak -# spellcheck (typo fixes) -# experiment -# balance -################################# - -# Your name. -author: BurgerBB - -# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. -delete-after: True - -# Any changes you've made. See valid prefix list above. -# INDENT WITH TWO SPACES. NOT TABS. SPACES. -# SCREW THIS UP AND IT WON'T WORK. -# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. -# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. -changes: - - bugfix: "Added vents to the cargo warehouse connector to prevent underpressure from disposal inlets." - - bugfix: "Disposal outlets now spread out items so all the trash doesn't pile up on one tile."