Merge branch 'master' of https://github.com/ParadiseSS13/Paradise into lavaland_megafauna

# Conflicts:
#	icons/obj/ammo.dmi
#	icons/obj/guns/projectile.dmi
#	icons/obj/projectiles.dmi
This commit is contained in:
Markolie
2017-01-23 17:36:37 +01:00
18 changed files with 945 additions and 5 deletions
+1
View File
@@ -16,6 +16,7 @@ data/
/_maps/map_files/**/*.dmm.backup
/_maps/quicksave/*
/nano/debug.html
**/__pycache__/*
*.db
stddef.dm
.atom-build.json
+6
View File
@@ -30,6 +30,12 @@ var/intercom_range_display_status = 0
src.pixel_x = -224
src.pixel_y = -224
/obj/effect/debugging/mapfix_marker
name = "map fix marker"
icon = 'icons/mob/screen_gen.dmi'
icon_state = "mapfixmarker"
desc = "I am a mappers mistake."
/obj/effect/debugging/marker
icon = 'icons/turf/areas.dmi'
icon_state = "yellow"
@@ -11,6 +11,20 @@
return
else if(istype(O,/obj/item/weapon/crowbar))
return
else if(istype(O, /obj/item/weapon/shovel))
if(seed)
to_chat(user, "You dig up and dispose of \the [seed.display_name].")
seed = null
dead = 0
sampled = 0
age = 0
lastproduce = 0
yield_mod = 0
mutation_mod = 0
check_health()
else
to_chat(user, "You clear up [src]!")
qdel(src)
else
..()
@@ -3,6 +3,12 @@
caliber = "357"
projectile_type = /obj/item/projectile/bullet
/obj/item/ammo_casing/rubber9mm
desc = "A 9mm rubber bullet casing."
caliber = "9mm"
icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/weakbullet4
/obj/item/ammo_casing/a762
desc = "A 7.62mm bullet casing."
icon_state = "762-casing"
@@ -20,6 +26,7 @@
/obj/item/ammo_casing/c38
desc = "A .38 bullet casing."
caliber = "38"
icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/weakbullet2/rubber
/obj/item/ammo_casing/c10mm
@@ -76,6 +83,12 @@
caliber = "4.6x30mm"
projectile_type = /obj/item/projectile/bullet/incendiary/firebullet
/obj/item/ammo_casing/rubber45
desc = "A .45 rubber bullet casing."
caliber = ".45"
icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/weakbullet4
/obj/item/ammo_casing/c45
desc = "A .45 bullet casing."
caliber = ".45"
@@ -120,7 +133,7 @@
/obj/item/ammo_casing/shotgun/rubbershot
name = "rubber shot"
desc = "A shotgun casing filled with densely-packed rubber balls, used to incapacitate crowds from a distance."
icon_state = "bshell"
icon_state = "cshell"
projectile_type = /obj/item/projectile/bullet/rpellet
pellets = 6
variance = 25
@@ -254,7 +267,7 @@
/obj/item/ammo_casing/shotgun/tranquilizer
name = "tranquilizer darts"
desc = "A tranquilizer round used to subdue individuals utilizing stimulants."
icon_state = "cshell"
icon_state = "nshell"
projectile_type = /obj/item/projectile/bullet/dart/syringe/tranquilizer
materials = list(MAT_METAL=250)
+13 -3
View File
@@ -35,6 +35,12 @@
ammo_type = /obj/item/ammo_casing/c45
max_ammo = 20
/obj/item/ammo_box/rubber45
name = "ammo box (.45 rubber)"
icon_state = "45box-r"
ammo_type = /obj/item/ammo_casing/rubber45
max_ammo = 16
/obj/item/ammo_box/a40mm
name = "ammo box (40mm grenades)"
icon_state = "40mm"
@@ -52,14 +58,14 @@
/obj/item/ammo_box/n762
name = "ammo box (7.62x38mmR)"
icon_state = "10mmbox"
icon_state = "riflebox"
origin_tech = "combat=2"
ammo_type = /obj/item/ammo_casing/n762
max_ammo = 14
/obj/item/ammo_box/shotgun
name = "Ammunition Box (slug)"
icon_state = "9mmbox"
icon_state = "slugbox"
origin_tech = "combat=2"
ammo_type = /obj/item/ammo_casing/shotgun
max_ammo = 7
@@ -67,26 +73,30 @@
/obj/item/ammo_box/shotgun/buck
name = "Ammunition Box (buckshot)"
icon_state = "buckshotbox"
ammo_type = /obj/item/ammo_casing/shotgun/buckshot
/obj/item/ammo_box/shotgun/stun
name = "Ammunition Box (stun shells)"
icon_state = "stunbox"
ammo_type = /obj/item/ammo_casing/shotgun/stunslug
materials = list(MAT_METAL=1750)
/obj/item/ammo_box/shotgun/beanbag
name = "Ammunition Box (beanbag shells)"
icon_state = "beanbagbox"
ammo_type = /obj/item/ammo_casing/shotgun/beanbag
materials = list(MAT_METAL=1750)
/obj/item/ammo_box/shotgun/rubbershot
name = "Ammunition Box (rubbershot shells)"
icon_state = "rubbershotbox"
ammo_type = /obj/item/ammo_casing/shotgun/rubbershot
materials = list(MAT_METAL=28000)
/obj/item/ammo_box/shotgun/tranquilizer
name = "Ammunition Box (tranquilizer darts)"
icon_state = "45box"
icon_state = "tranqbox"
ammo_type = /obj/item/ammo_casing/shotgun/tranquilizer
materials = list(MAT_METAL=1750)
@@ -209,6 +209,34 @@
caliber = ".45"
max_ammo = 8
multiple_sprites = 1
/obj/item/ammo_box/magazine/m45/enforcer45
name = "handgun magazine (.45)"
icon_state = "enforcer"
ammo_type = /obj/item/ammo_casing/rubber45
/obj/item/ammo_box/magazine/m45/enforcer45/update_icon()
..()
overlays.Cut()
var/ammo = ammo_count()
if(ammo && is_rubber())
overlays += image('icons/obj/ammo.dmi', icon_state = "enforcer-r")
/obj/item/ammo_box/magazine/m45/enforcer45/examine(mob/user, var/distance)
..()
if(distance <= 2)
to_chat(user, "It seems to be loaded with [is_rubber() ? "rubber" : "lethal"] bullets.")//only can see the topmost one.
/obj/item/ammo_box/magazine/m45/enforcer45/proc/is_rubber()//if the topmost bullet is a rubber one
var/ammo = ammo_count()
if(!ammo)
return 0
if(istype(contents[contents.len], /obj/item/ammo_casing/rubber45))
return 1
return 0
/obj/item/ammo_box/magazine/m45/enforcer45/lethal
ammo_type = /obj/item/ammo_casing/c45
/obj/item/ammo_box/magazine/wt550m9
name = "wt550 magazine (4.6x30mm)"
@@ -40,6 +40,14 @@
mag_type = /obj/item/ammo_box/magazine/m50
can_suppress = 0
/obj/item/weapon/gun/projectile/automatic/pistol/enforcer45
name = "Enforcer .45"
desc = "A pistol of modern design."
icon_state = "enforcer"
force = 10
mag_type = /obj/item/ammo_box/magazine/m45/enforcer45
can_suppress = 0
/obj/item/weapon/gun/projectile/automatic/pistol/deagle/update_icon()
..()
icon_state = "[initial(icon_state)][magazine ? "" : "-e"]"
@@ -42,6 +42,7 @@
damage = 5
weaken = 3
stamina = 60
icon_state = "bullet-r"
/obj/item/projectile/bullet/weakbullet2/rubber //detective's bullets that don't embed
embed = 0
@@ -50,6 +51,13 @@
/obj/item/projectile/bullet/weakbullet3
damage = 20
/obj/item/projectile/bullet/weakbullet4
name = "rubber bullet"
damage = 5
stamina = 30
icon_state = "bullet-r"
embed = 0
sharp = 0
/obj/item/projectile/bullet/toxinbullet
damage = 15
@@ -135,6 +143,7 @@
name = "rubber pellet"
damage = 3
stamina = 25
icon_state = "bullet-r"
/obj/item/projectile/bullet/stunshot//taser slugs for shotguns, nothing special
name = "stunshot"
+12
View File
@@ -55,6 +55,18 @@
-->
<div class="commit sansserif">
<h2 class="date">23 January 2017</h2>
<h3 class="author">FalseIncarnate updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Returns the ability to remove vacant soil with shovels and spades, and adds the ability to dig up plants in soil to remove them instantly.</li>
</ul>
<h3 class="author">Stratus updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">NT Enforcer .45 pistol.</li>
<li class="rscadd">.45 and 9mm rubber bullets.</li>
<li class="imageadd">New shotgun ammo boxes.</li>
</ul>
<h2 class="date">18 January 2017</h2>
<h3 class="author">AndriiYukhymchak updated:</h3>
<ul class="changes bgimages16">
+8
View File
@@ -3400,3 +3400,11 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
- tweak: Admin announcements are no longer sent to player in the lobby.
- tweak: Regular announcements can now only be heard if you the player is alive,
not deaf and in range of an enabled radio.
2017-01-23:
FalseIncarnate:
- rscadd: Returns the ability to remove vacant soil with shovels and spades, and
adds the ability to dig up plants in soil to remove them instantly.
Stratus:
- rscadd: NT Enforcer .45 pistol.
- rscadd: .45 and 9mm rubber bullets.
- imageadd: New shotgun ammo boxes.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

+29
View File
@@ -0,0 +1,29 @@
#Map Conflict Fixer/Helper#
The map conflict fixer is a script that can help you fix map conflicts easier and faster. Here's how it works:
###Before using###
You need git for this, of course.
Make sure your development branch is up to date before starting a map edit to ensure the script outputs a correct fix.
##Dictionary mode##
Dictionary conflicts are the easiest to fix, you simply need to create more models to accommodate your changes and everyone elses.
When you run in this mode, if the script finishes successfuly the map should be ready to be commited.
If the script fails in dictionary mode, you can run it again in full fix mode.
##Full Fix mode##
When you and someone else edit the same coordinate, there is no easy way to fix the conflict. You need to get your hands dirty.
The script will mark every tile with a marker type to help you identify what needs fixing in the map editor.
After you edit and fix a marked map, you should run it through the map merger. The .backup file should be the same you used before.
###Priorities###
In Full Fix mode, the script needs to know which map version has higher priority, yours or someone elses. This important so tiles with multiple area and turf types aren't created.
Your version has priority - In each conflicted coordinate, your floor type and your area type will be used
Their version has priority - In each conflicted coordinate, your floor type and your area type will not be used
##IMPORTANT##
This script is in a testing phase and you should not consider any output to be safe. Always verify the maps this script produced to make sure nothing is out of place.
@@ -0,0 +1,4 @@
@echo off
SET RELATIVEROOT="../../"
python map_conflict_fixer.py %1 %RELATIVEROOT% 0
pause
@@ -0,0 +1,111 @@
import map_helpers
import sys
import os
import time
# Credits to tg for this
def main(relative_root, tgm=0):
git_version = map_helpers.run_shell_command("git version")
if not git_version:
print("ERROR: Failed to run git. Make sure it is installed and in your PATH.")
return False
print("--- DISCLAIMER ---")
print("This script is in a testing phase. Verify all the results yourself to make sure you got what you expected. Make sure to read the readme to learn how to use this.")
input("Press Enter to GO\n")
file_conflicts = map_helpers.run_shell_command("git diff --name-only --diff-filter=U").split("\n")
map_conflicts = [path for path in file_conflicts if path[len(path)-3::] == "dmm"]
for i in range(0, len(map_conflicts)):
print("[{}]: {}".format(i, map_conflicts[i]))
selection = input("Choose maps you want to fix (example: 1,3-5,12):\n")
selection = selection.replace(" ", "")
selection = selection.split(",")
#shamelessly copied from mapmerger cli
valid_indices = list()
for m in selection:
index_range = m.split("-")
if len(index_range) == 1:
index = map_helpers.string_to_num(index_range[0])
if index >= 0 and index < len(map_conflicts):
valid_indices.append(index)
elif len(index_range) == 2:
index0 = map_helpers.string_to_num(index_range[0])
index1 = map_helpers.string_to_num(index_range[1])
if index0 >= 0 and index0 <= index1 and index1 < len(map_conflicts):
valid_indices.extend(range(index0, index1 + 1))
if not len(valid_indices):
print("No map selected, exiting.")
sys.exit()
print("Attempting to fix the following maps:")
for i in valid_indices:
print(map_conflicts[i])
marker = None
priority = 0
print("\nFixing modes:")
print("[{}]: Dictionary conflict fixing mode".format(map_helpers.MAP_FIX_DICTIONARY))
print("[{}]: Full map conflict fixing mode".format(map_helpers.MAP_FIX_FULL))
mode = map_helpers.string_to_num(input("Select fixing mode [Dictionary]: "))
if mode != map_helpers.MAP_FIX_FULL:
mode = map_helpers.MAP_FIX_DICTIONARY
print("DICTIONARY mode selected.")
else:
marker = input("FULL mode selected. Input a marker [/obj/effect/debugging/mapfix_marker]: ")
if not marker:
marker = "/obj/effect/debugging/mapfix_marker"
print("Marker selected: {}".format(marker))
print("\nVersion priorities:")
print("[{}]: Your version".format(map_helpers.MAP_FIX_PRIORITY_OURS))
print("[{}]: Their version".format(map_helpers.MAP_FIX_PRIORITY_THEIRS))
priority = map_helpers.string_to_num(input("Select priority [Yours]: "))
if priority != map_helpers.MAP_FIX_PRIORITY_THEIRS:
priority = map_helpers.MAP_FIX_PRIORITY_OURS
print("Your version will be prioritized.")
else:
print("Their version will be prioritized.")
ed = "FIXED" if mode == map_helpers.MAP_FIX_DICTIONARY else "MARKED"
ing = "FIXING" if mode == map_helpers.MAP_FIX_DICTIONARY else "MARKING"
print("\nMaps will be converted to TGM.")
print("Writing maps to 'file_path/file_name.fixed.dmm'. Please verify the results before commiting.")
if mode == map_helpers.MAP_FIX_FULL:
print("After editing the marked maps, run them through the map merger!")
input("Press Enter to start.")
print(".")
time.sleep(0.3)
print(".")
for i in valid_indices:
path = map_conflicts[i]
print("{}: {}".format(ing, path))
ours_map_raw_text = map_helpers.run_shell_command("git show HEAD:{}".format(path))
theirs_map_raw_text = map_helpers.run_shell_command("git show MERGE_HEAD:{}".format(path))
common_ancestor_hash = map_helpers.run_shell_command("git merge-base HEAD MERGE_HEAD").strip()
base_map_raw_text = map_helpers.run_shell_command("git show {}:{}".format(common_ancestor_hash, path))
ours_map = map_helpers.parse_map(ours_map_raw_text)
theirs_map = map_helpers.parse_map(theirs_map_raw_text)
base_map = map_helpers.parse_map(base_map_raw_text)
if map_helpers.fix_map_git_conflicts(base_map, ours_map, theirs_map, mode, marker, priority, relative_root+path, tgm):
print("{}: {}".format(ed, path))
print(".")
output_tgm = None
try:
output_tgm = int(sys.argv[2])
except ValueError:
output_tgm = 0
main(sys.argv[1], output_tgm)
+687
View File
@@ -0,0 +1,687 @@
import sys
import subprocess
tgm_header = "//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE"
try:
version = sys.version_info
if version.major < 3 or (version.major == 3 and version.minor < 5):
print("ERROR: You are running an incompatible version of Python. The current minimum version required is [3.5].\nYour version: {}".format(sys.version))
sys.exit()
except:
print("ERROR: Something went wrong, you might be running an incompatible version of Python. The current minimum version required is [3.5].\nYour version: {}".format(sys.version))
sys.exit()
import collections
error = {0:"OK", 1:"WARNING: Detected key length difference, not merging.", 2:"WARNING: Detected map size difference, not merging."}
def merge_map(newfile, backupfile, tgm):
key_length = 1
maxx = 1
maxy = 1
new_map = parse_map(get_map_raw_text(newfile))
old_map = parse_map(get_map_raw_text(backupfile))
if new_map["key_length"] != old_map["key_length"]:
if tgm:
write_dictionary_tgm(newfile, new_map["dictionary"])
write_grid_coord_small(newfile, new_map["grid"])
return 1
else:
key_length = old_map["key_length"]
if new_map["maxx"] != old_map["maxx"] or new_map["maxy"] != old_map["maxy"]:
if tgm:
write_dictionary_tgm(newfile, new_map["dictionary"])
write_grid_coord_small(newfile, new_map["grid"])
return 2
else:
maxx = old_map["maxx"]
maxy = old_map["maxy"]
new_dict = new_map["dictionary"]
new_grid = new_map["grid"]
old_dict = sort_dictionary(old_map["dictionary"]) #impose order; old_dict is used in the end as the merged dictionary
old_grid = old_map["grid"]
merged_grid = dict()
known_keys = dict()
unused_keys = list(old_dict.keys())
#both new and old dictionary in lists, for faster key lookup by tile tuple
old_dict_keys = list(old_dict.keys())
old_dict_values = list(old_dict.values())
new_dict_keys = list(new_dict.keys())
new_dict_values = list(new_dict.values())
#step one: parse the new version, compare it to the old version, merge both
for y in range(1, maxy+1):
for x in range(1, maxx+1):
new_key = new_grid[x,y]
#if this key has been processed before, it can immediately be merged
if new_key in known_keys:
merged_grid[x,y] = known_keys[new_key]
continue
old_key = old_grid[x,y]
old_tile = old_dict[old_key]
new_tile = new_dict[new_key]
if new_tile == old_tile: #this tile is the exact same as before, so the old key is used
merged_grid[x,y] = old_key
known_keys[new_key] = old_key
unused_keys.remove(old_key)
continue
#the tile is different here, but if it exists in the old dictionary, its old key can be used
newold_key = get_key(old_dict_keys, old_dict_values, new_tile)
if newold_key != None:
merged_grid[x,y] = newold_key
known_keys[new_key] = newold_key
try:
unused_keys.remove(newold_key)
except ValueError:
print("NOTICE: Correcting duplicate dictionary entry. ({})".format(new_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 get_key(new_dict_keys, new_dict_values, old_tile) == None:
merged_grid[x,y] = old_key
old_dict[old_key] = new_tile
known_keys[new_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 = generate_new_key(old_dict)
old_dict[fresh_key] = new_tile
merged_grid[x,y] = fresh_key
## #step two: clean the dictionary if it has too many unused keys
## if len(unused_keys) > min(100, (len(old_dict) * 0.5)):
## print("NOTICE: Trimming the dictionary.")
## old_dict = trim_dictionary(old_dict)
#step three: write the map to file
if tgm:
write_dictionary_tgm(newfile, old_dict)
write_grid_coord_small(newfile, merged_grid, maxx, maxy)
else:
write_dictionary(newfile, old_dict)
write_grid(newfile, merged_grid, maxx, maxy)
return 0
#######################
#write to file helpers#
def write_dictionary_tgm(filename, dictionary): #write dictionary in tgm format
with open(filename, "w") as output:
output.write("{}\n".format(tgm_header))
for key, list_ in dictionary.items():
output.write("\"{}\" = (\n".format(key))
for thing in list_:
buffer = ""
in_quote_block = False
in_varedit_block = False
for char in thing:
if in_quote_block:
if char == "\"":
in_quote_block = False
buffer = buffer + char
continue
elif char == "\"":
in_quote_block = True
buffer = buffer + char
continue
if not in_varedit_block:
if char == "{":
in_varedit_block = True
buffer = buffer + "{\n\t"
continue
else:
if char == ";":
buffer = buffer + ";\n\t"
continue
elif char == "}":
buffer = buffer + "\n\t}"
in_varedit_block = False
continue
buffer = buffer + char
if list_.index(thing) != len(list_) - 1:
buffer = buffer + ",\n"
output.write(buffer)
output.write(")\n")
def write_grid_coord_small(filename, grid, maxx, maxy): #thanks to YotaXP for finding out about this one
with open(filename, "a") as output:
output.write("\n")
for x in range(1, maxx+1):
output.write("({},{},1) = {{\"\n".format(x, 1, 1))
for y in range(1, maxy):
output.write("{}\n".format(grid[x,y]))
output.write("{}\n\"}}\n".format(grid[x,maxy]))
def write_dictionary(filename, dictionary): #writes a tile dictionary the same way Dreammaker does
with open(filename, "w") as output:
for key, value in dictionary.items():
output.write("\"{}\" = ({})\n".format(key, ",".join(value)))
def write_grid(filename, grid, maxx, maxy): #writes a map grid the same way Dreammaker does
with open(filename, "a") as output:
output.write("\n")
output.write("(1,1,1) = {\"\n")
for y in range(1, maxy+1):
for x in range(1, maxx+1):
try:
output.write(grid[x,y])
except KeyError:
print("Key error: ({},{})".format(x,y))
output.write("\n")
output.write("\"}")
output.write("\n")
####################
#dictionary helpers#
def search_key(dictionary, data):
for key, value in dictionary.items():
if value == data:
return key
return None
def get_key(keys, values, data):
try:
return keys[values.index(data)]
except:
return None
def trim_dictionary(dictionary): #rewrites dictionary into an ordered dictionary with no unused keys
trimmed_dict = collections.OrderedDict()
key_length = len(list(dictionary.keys())[0])
key = ""
for tile in dictionary.values():
key = get_next_key(key, key_length)
trimmed_dict[key] = tile
return trimmed_dict
def sort_dictionary(dictionary):
sorted_dict = collections.OrderedDict()
keys = list(dictionary.keys())
keys.sort(key=key_int_value)
for key in keys:
sorted_dict[key] = dictionary[key]
return sorted_dict
############
#map parser#
def get_map_raw_text(mapfile):
with open(mapfile, "r") as reading:
return reading.read()
def parse_map(map_raw_text): #still does not support more than one z level per file, but should parse any format
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 = collections.OrderedDict()
curr_key = ""
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
curr_x = 0
curr_y = 0
curr_z = 1
grid = dict()
for char in map_raw_text:
if not in_map_block:
if char == "\n" or char == "\t":
continue
if in_data_block:
if in_varedit_block:
if in_quote_block:
if char == "\\":
curr_datum = curr_datum + char
escaping = True
continue
if escaping:
curr_datum = curr_datum + char
escaping = False
continue
if char == "\"":
curr_datum = curr_datum + char
in_quote_block = False
continue
curr_datum = curr_datum + char
continue
if skip_whitespace and char == " ":
skip_whitespace = False
continue
skip_whitespace = False
if char == "\"":
curr_datum = curr_datum + char
in_quote_block = True
continue
if char == ";":
skip_whitespace = True
curr_datum = curr_datum + char
continue
if char == "}":
curr_datum = curr_datum + char
in_varedit_block = False
continue
curr_datum = curr_datum + char
continue
if char == "{":
curr_datum = curr_datum + char
in_varedit_block = True
continue
if char == ",":
curr_data.append(curr_datum)
curr_datum = ""
continue
if char == ")":
curr_data.append(curr_datum)
dictionary[curr_key] = tuple(curr_data)
curr_data = list()
curr_datum = ""
curr_key = ""
in_data_block = False
after_data_block = True
continue
curr_datum = curr_datum + char
continue
if in_key_block:
if char == "\"":
in_key_block = False
key_length = len(curr_key)
else:
curr_key = curr_key + char
continue
#else we're looking for a key block, a data block or the map block
if char == "\"":
in_key_block = True
after_data_block = False
continue
if char == "(":
if after_data_block:
in_map_block = True
in_coord_block = True
after_data_block = False
curr_key = ""
continue
else:
in_data_block = True
after_data_block = False
continue
else:
if in_coord_block:
if char == ",":
if reading_coord == "x":
curr_x = string_to_num(curr_num)
if curr_x > maxx:
maxx = curr_x
iter_x = 0
curr_num = ""
reading_coord = "y"
elif reading_coord == "y":
curr_y = string_to_num(curr_num)
if curr_y > maxy:
maxy = curr_y
curr_num = ""
reading_coord = "z"
else:
pass
continue
if char == ")":
in_coord_block = False
reading_coord = "x"
curr_num = ""
#read z here if needed
continue
curr_num = curr_num + char
continue
if in_map_string:
if char == "\"":
in_map_string = False
adjust_y = True
curr_y -= 1
continue
if 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
continue
curr_key = curr_key + char
if len(curr_key) == key_length:
iter_x += 1
if iter_x > 1:
curr_x += 1
grid[curr_x, curr_y] = curr_key
curr_key = ""
continue
#else look for coordinate block or a map string
if char == "(":
in_coord_block = True
continue
if char == "\"":
in_map_string = True
continue
if curr_y > maxy:
maxy = curr_y
data = dict()
data["dictionary"] = dictionary
data["grid"] = grid
data["key_length"] = key_length
data["maxx"] = maxx
data["maxy"] = maxy
return data
#############
#key helpers#
def generate_new_key(dictionary):
last_key = next(reversed(dictionary))
return get_next_key(last_key, len(last_key))
def get_next_key(key, key_length):
if key == "":
return "".join("a" for _ in range(key_length))
length = len(key)
new_key = ""
carry = 1
for char in key[::-1]:
if carry <= 0:
new_key = new_key + char
continue
if char == 'Z':
new_key = new_key + 'a'
carry += 1
length -= 1
if length <= 0:
return "OVERFLOW"
elif char == 'z':
new_key = new_key + 'A'
else:
new_key = new_key + chr(ord(char) + 1)
if carry > 0:
carry -= 1
return new_key[::-1]
def key_int_value(key):
value = 0
b = 0
for digit in reversed(key):
value += base52.index(digit) * (52 ** b)
b += 1
return value
def key_compare(keyA, keyB): #thanks byond for not respecting ascii
pos = 0
for a in keyA:
pos += 1
count = pos
for b in keyB:
if(count > 1):
count -= 1
continue
if a.islower() and b.islower():
if(a < b):
return -1
if(a > b):
return 1
break
if a.islower() and b.isupper():
return -1
if a.isupper() and b.islower():
return 1
if a.isupper() and b.isupper():
if(a < b):
return -1
if(a > b):
return 1
break
return 0
def key_difference(keyA, keyB): #subtract keyB from keyA
if len(keyA) != len(keyB):
return "you fucked up"
Ayek = keyA[::-1]
Byek = keyB[::-1]
result = 0
for i in range(0, len(keyA)):
base = 52**i
A = 26 if Ayek[i].isupper() else 0
B = 26 if Byek[i].isupper() else 0
result += ( (ord(Byek[i].lower()) + B) - (ord(Ayek[i].lower()) + A) ) * base
return result
#############
#other stuff#
#Base 52 a-z A-Z dictionary (it's a python list) for fast conversion
base52 = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
'p','q','r','s','t','u','v','w','x','y','z','A','B','C','D',
'E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S',
'T','U','V','W','X','Y','Z']
def string_to_num(s):
try:
return int(s)
except ValueError:
return -1
####################
#map conflict fixer#
MAP_FIX_DICTIONARY = 0
MAP_FIX_FULL = 1
MAP_FIX_PRIORITY_OURS = 0
MAP_FIX_PRIORITY_THEIRS = 1
def fix_map_git_conflicts(base_map, ours_map, theirs_map, mode, marker, priority, path, tgm): #attempts to fix git map conflicts automagically
#mode values:
# 0 - Dictionary mode, only fixes dictionary conflicts and fails at coordinate conflicts
# 1 - Full mode, fixes dictionary conflicts and will join both versions of a coordinate and adds a marker to each conflicted coordinate
if mode == MAP_FIX_FULL and not marker:
print("ERROR: Full fix mode selected but a marker wasn't provided.")
return False
key_length = 1
maxx = 1
maxy = 1
if ours_map["key_length"] != theirs_map["key_length"] or ours_map["key_length"] != base_map["key_length"]:
print("ERROR: Key length is different, I am not smart enough for this yet.")
return False
else:
key_length = ours_map["key_length"]
if ours_map["maxx"] != theirs_map["maxx"] or ours_map["maxy"] != theirs_map["maxy"] or ours_map["maxx"] != base_map["maxx"] or ours_map["maxy"] != base_map["maxy"]:
print("ERROR: Map size is different, I am not smart enough for this yet.")
return False
else:
maxx = ours_map["maxx"]
maxy = ours_map["maxy"]
base_dict = base_map["dictionary"]
base_grid = base_map["grid"]
ours_dict = ours_map["dictionary"]
ours_grid = ours_map["grid"]
theirs_dict = theirs_map["dictionary"]
theirs_grid = theirs_map["grid"]
merged_dict = collections.OrderedDict()
merged_grid = dict()
new_key = generate_new_key(base_dict)
grid_conflict_counter = 0
for y in range(1, maxy+1):
for x in range(1, maxx+1):
base_key = base_grid[x,y]
ours_key = ours_grid[x,y]
theirs_key = theirs_grid[x,y]
#case 1: everything is the same
if base_dict[base_key] == ours_dict[ours_key] and base_dict[base_key] == theirs_dict[theirs_key]:
merged_dict[base_key] = base_dict[base_key]
merged_grid[x,y] = base_key
continue
#case 2: ours is unchanged, theirs is different
if base_dict[base_key] == ours_dict[ours_key]:
some_key = search_key(base_dict, theirs_dict[theirs_key])
if some_key != None:
merged_dict[some_key] = theirs_dict[theirs_key]
merged_grid[x,y] = some_key
else:
merged_dict[new_key] = theirs_dict[theirs_key]
merged_grid[x,y] = new_key
new_key = get_next_key(new_key, key_length)
#case 3: ours is different, theirs is unchanged
elif base_dict[base_key] == theirs_dict[theirs_key]:
some_key = search_key(base_dict, ours_dict[ours_key])
if some_key != None:
merged_dict[some_key] = ours_dict[ours_key]
merged_grid[x,y] = some_key
else:
merged_dict[new_key] = ours_dict[ours_key]
merged_grid[x,y] = new_key
new_key = get_next_key(new_key, key_length)
#case 4: everything is different, grid conflict
else:
if mode != MAP_FIX_FULL:
print("FAILED: Map fixing failed due to grid conflicts. Try fixing in full fix mode.")
return False
else:
combined_tile = combine_tiles(ours_dict[ours_key], theirs_dict[theirs_key], priority, marker)
merged_dict[new_key] = combined_tile
merged_grid[x,y] = new_key
new_key = get_next_key(new_key, key_length)
grid_conflict_counter += 1
merged_dict = sort_dictionary(merged_dict)
if tgm:
write_dictionary_tgm(path+".fixed.dmm", merged_dict)
write_grid_coord_small(path+".fixed.dmm", merged_grid, maxx, maxy)
else:
write_dictionary(path+".fixed.dmm", merged_dict)
write_grid(path+".fixed.dmm", merged_grid, maxx, maxy)
if grid_conflict_counter > 0:
print("Counted {} conflicts.".format(grid_conflict_counter))
return True
def combine_tiles(tile_A, tile_B, priority, marker):
new_tile = list()
turf_atom = None
area_atom = None
low_tile = None
high_tile = None
if priority == MAP_FIX_PRIORITY_OURS:
high_tile = tile_A
low_tile = tile_B
else:
high_tile = tile_B
low_tile = tile_A
new_tile.append(marker)
for atom in high_tile:
if atom[0:5] == "/turf":
turf_atom = atom
elif atom[0:5] == "/area":
area_atom = atom
else:
new_tile.append(atom)
for atom in low_tile:
if atom[0:5] == "/turf" or atom[0:5] == "/area":
continue
else:
new_tile.append(atom)
new_tile.append(turf_atom)
new_tile.append(area_atom)
return tuple(new_tile)
def run_shell_command(command):
return subprocess.run(command, shell=True, stdout=subprocess.PIPE, universal_newlines=True).stdout