diff --git a/.gitignore b/.gitignore index 7b8d793..7172a55 100644 --- a/.gitignore +++ b/.gitignore @@ -86,4 +86,9 @@ render_test.py test logs/ -*.log* \ No newline at end of file +*.log* + +dev/ + +tests/reference_images +tests/reference_saves \ No newline at end of file diff --git a/README.md b/README.md index fd47b13..d5bd3a8 100644 --- a/README.md +++ b/README.md @@ -64,4 +64,4 @@ Assets for image generation go in `sdv\assets\[subfolder]`. Assets used as-is go ## Acknowledgements -Thanks to Leonardo Francisco (icantbewrong@outlook.com) for contributing the Brazilian Portuguese translation, and thanks to Yiming Wang (yimingw@umich.edu) for contributing the Chinese translation. \ No newline at end of file +Thanks to Leonardo Francisco (icantbewrong@outlook.com) for contributing the Brazilian Portuguese translation, thanks to Yiming Wang (yimingw@umich.edu) for contributing the Chinese translation, and thanks to Jhordi Rodriguez (jhordi.rs@gmail.com) for contributing and improving the Spanish translation. diff --git a/sdv/__init__.py b/sdv/__init__.py index f7c2fef..f81183f 100644 --- a/sdv/__init__.py +++ b/sdv/__init__.py @@ -1518,6 +1518,8 @@ def operate_on_url(url,instruction): return _op_toggle_boolean_param(url,'private',True) g.error = _("Unknown or insufficient credentials") return render_template("error.html", **page_args()) + elif request.method == 'GET' and url == '.well-known': + return redirect(url_for('static',filename='.well-known/{}'.format(instruction))) else: return redirect(url_for('display_data',url=url)) @@ -2279,5 +2281,6 @@ def get_votes(url): else: return None + if __name__ == "__main__": app.run() \ No newline at end of file diff --git a/sdv/farmInfo.py b/sdv/farmInfo.py index 98c9045..08f77e0 100644 --- a/sdv/farmInfo.py +++ b/sdv/farmInfo.py @@ -10,6 +10,7 @@ 'Combat' ] + # Check adj. tiles for all tiles on map to determine orientation. Uses bit mask to select correct tile from spritesheet def checkSurrounding(tiles): floor_map = [[None for a in range(80)] for b in range(65)] @@ -41,7 +42,8 @@ def checkSurrounding(tiles): current_tile = None try: if floor_map[y + dy][x + dx] is not None: - if tile.name == 'Flooring' or (tile.name == 'Fence' and not tile.growth): + if tile.name == 'Flooring' or ( + tile.name == 'Fence' and not tile.growth): if floor_map[y + dy][x + dx].type == tile.type: a += b else: @@ -71,11 +73,10 @@ def getFarmInfo(saveFile): root = saveFile.getRoot() - # Farm Objects s = [] - farm_location = get_location(root,'Farm') + farm_location = get_location(root, 'Farm') for item in farm_location.find('objects').iter("item"): f = False obj = item.find('value').find('Object') @@ -129,7 +130,7 @@ def getFarmInfo(saveFile): s = None loc = None f = False - name = item.find('value').find('TerrainFeature').get(ns+'type') + name = item.find('value').find('TerrainFeature').get(ns + 'type') if name == 'Tree': t = int(item.find('value').find('TerrainFeature').find('treeType').text) s = int(item.find('value').find('TerrainFeature').find('growthStage').text) @@ -164,11 +165,12 @@ def getFarmInfo(saveFile): crop_dead = False if crop.find('dead').text == 'true': crop_dead = True - crops.append(sprite('HoeDirtCrop', crop_x, crop_y, 1, 1, crop_dead, crop_location, crop_phase, crop_flip, o)) + crops.append(sprite('HoeDirtCrop', crop_x, crop_y, 1, 1, crop_dead, crop_location, + crop_phase, crop_flip, o)) if name == "FruitTree": t = int(item.find('value').find('TerrainFeature').find('treeType').text) s = int(item.find('value').find('TerrainFeature').find('growthStage').text) - if item.find('value').find('TerrainFeature').find('flipped').text == 'true': f= True + if item.find('value').find('TerrainFeature').find('flipped').text == 'true': f = True if name == "Grass": t = int(item.find('value').find('TerrainFeature').find('grassType').text) s = int(item.find('value').find('TerrainFeature').find('numberOfWeeds').text) @@ -192,11 +194,28 @@ def getFarmInfo(saveFile): except: pass + # Large Terrain Features + + large_terrain_features = [] + for ltf in farm_location.find('largeTerrainFeatures'): + name = ltf.get(ns + 'type') + flipped = ltf.find('flipped').text == 'true' + size = int(ltf.find('size').text) + x = int(ltf.find('tilePosition').find('X').text) + y = int(ltf.find('tilePosition').find('Y').text) + tile_sheet_offset = int(ltf.find('tileSheetOffset').text) + + large_terrain_features.append( + sprite(name, x, y, 1, 1, tile_sheet_offset, None, size, flipped, None) + ) + + farm['largeTerrainFeatures'] = large_terrain_features + # Resource Clumps s = [] for item in farm_location.find('resourceClumps').iter('ResourceClump'): - name = item.get(ns+'type') + name = item.get(ns + 'type') if name is None: name = 'ResourceClump' t = int(item.find('parentSheetIndex').text) @@ -230,7 +249,7 @@ def getFarmInfo(saveFile): hasGreenhouse = False try: - community_center = get_location(root,'CommunityCenter') + community_center = get_location(root, 'CommunityCenter') cats = community_center.find('areasComplete').findall('boolean') if cats[0].text == 'true': hasGreenhouse = True @@ -264,7 +283,7 @@ def colourBox(x, y, colour, pixels, scale=8): for i in range(scale): for j in range(scale): try: - pixels[x*scale + i, y*scale + j] = colour + pixels[x * scale + i, y * scale + j] = colour except IndexError: pass return pixels @@ -337,21 +356,15 @@ def generateImage(data): elif tile.type == 600: for i in range(tile[3]): for j in range(tile[3]): - colourBox(tile.x+i, tile.y + j, (75, 75, 75), pixels) + colourBox(tile.x + i, tile.y + j, (75, 75, 75), pixels) return image def regenerateFarmInfo(json_from_db): - sprite = namedtuple('Sprite', ['name', 'x', 'y', 'w', 'h', 'index', 'type', 'growth', 'flipped', 'orientation']) + sprite = namedtuple('Sprite', ['name', 'x', 'y', 'w', 'h', 'index', 'type', 'growth', 'flipped', + 'orientation']) for key in json_from_db['data'].keys(): for i, item in enumerate(json_from_db['data'][key]): json_from_db['data'][key][i] = sprite(*item) return json_from_db - - -def main(): - generateImage(getFarmInfo('./saves/Crono_116230451')).save('farm.png') - -if __name__ == '__main__': - main() diff --git a/sdv/imagegeneration/assets.py b/sdv/imagegeneration/assets.py index 1de0659..e9c8946 100644 --- a/sdv/imagegeneration/assets.py +++ b/sdv/imagegeneration/assets.py @@ -17,6 +17,7 @@ def open_nicely(filename): im.load() return im + def load_overlays(season, base): overlays = dict() @@ -24,113 +25,139 @@ def load_overlays(season, base): overlays[layer] = list() overlay_path = os.path.join(asset_dir, 'base', base, season, layer) for i in range(65): - overlays[layer].append(open_nicely(os.path.join(overlay_path, '{}-{}.png'.format(layer, i)))) + overlays[layer].append( + open_nicely(os.path.join(overlay_path, '{}-{}.png'.format(layer, i)))) return overlays def loadFarmAssets(season='spring', base='Default'): assets = { - 'base' : { + 'base': { base: {season: Image.open(os.path.join(asset_dir, 'base', base, season, 'Back.png'))} }, - 'overlays' : { - base : {season: load_overlays(season, base)} + 'overlays': { + base: {season: load_overlays(season, base)} }, - 'objects' : Image.open(os.path.join(asset_dir, 'farm', 'tileSheets', 'springobjects.png')), + 'objects': Image.open(os.path.join(asset_dir, 'farm', 'tileSheets', 'springobjects.png')), 'craftables': Image.open(os.path.join(asset_dir, 'farm', 'tileSheets', 'Craftables.png')), - 'flooring' : Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'flooring.png')), - 'hoe dirt' : { + 'flooring': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'flooring.png')), + 'hoe dirt': { 'normal': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'hoeDirt.png')), - 'winter': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'hoeDirtsnow.png')) + 'winter': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'hoeDirtsnow.png')) }, - 'crops' : Image.open(os.path.join(asset_dir, 'farm', 'tileSheets', 'crops.png')), - 'fences' : { - 'wood' : Image.open(os.path.join(asset_dir, 'farm', 'looseSprites', 'Fence1.png')), - 'stone' : Image.open(os.path.join(asset_dir, 'farm', 'looseSprites', 'Fence2.png')), - 'iron' : Image.open(os.path.join(asset_dir, 'farm', 'looseSprites', 'Fence3.png')), + 'crops': Image.open(os.path.join(asset_dir, 'farm', 'tileSheets', 'crops.png')), + 'fences': { + 'wood': Image.open(os.path.join(asset_dir, 'farm', 'looseSprites', 'Fence1.png')), + 'stone': Image.open(os.path.join(asset_dir, 'farm', 'looseSprites', 'Fence2.png')), + 'iron': Image.open(os.path.join(asset_dir, 'farm', 'looseSprites', 'Fence3.png')), 'hardwood': Image.open(os.path.join(asset_dir, 'farm', 'looseSprites', 'Fence5.png')) }, - 'trees' : { - 'oak' : { - 'spring': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree1_spring.png')), - 'summer': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree1_summer.png')), - 'fall' : Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree1_fall.png')), - 'winter': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree1_winter.png')) + 'bushes': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'bushes.png')), + 'trees': { + 'oak': { + 'spring': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree1_spring.png')), + 'summer': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree1_summer.png')), + 'fall': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree1_fall.png')), + 'winter': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree1_winter.png')) }, - 'maple' : { - 'spring': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree2_spring.png')), - 'summer': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree2_summer.png')), - 'fall' : Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree2_fall.png')), - 'winter': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree2_winter.png')) + 'maple': { + 'spring': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree2_spring.png')), + 'summer': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree2_summer.png')), + 'fall': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree2_fall.png')), + 'winter': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree2_winter.png')) }, - 'pine' : { - 'spring': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree3_spring.png')), - 'summer': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree3_spring.png')), - 'fall' : Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree3_fall.png')), - 'winter': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree3_winter.png')) + 'pine': { + 'spring': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree3_spring.png')), + 'summer': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree3_spring.png')), + 'fall': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree3_fall.png')), + 'winter': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'tree3_winter.png')) }, - 'mushroom': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'mushroom_tree.png')), - 'fruit' : Image.open(os.path.join(asset_dir, 'farm', 'tileSheets', 'fruitTrees.png')) + 'mushroom': Image.open( + os.path.join(asset_dir, 'farm', 'terrainFeatures', 'mushroom_tree.png')), + 'fruit': Image.open(os.path.join(asset_dir, 'farm', 'tileSheets', 'fruitTrees.png')) }, - 'grass' : Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'grass.png')), - 'buildings' : { - 'barn' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Barn.png')), - 'big barn' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Big Barn.png')), - 'deluxe barn' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Deluxe Barn.png')), - 'coop' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Coop.png')), - 'big coop' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Big Coop.png')), - 'deluxe coop' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Deluxe Coop.png')), - 'greenhouse' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'houses.png')) + 'grass': Image.open(os.path.join(asset_dir, 'farm', 'terrainFeatures', 'grass.png')), + 'buildings': { + 'barn': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Barn.png')), + 'big barn': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Big Barn.png')), + 'deluxe barn': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Deluxe Barn.png')), + 'coop': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Coop.png')), + 'big coop': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Big Coop.png')), + 'deluxe coop': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Deluxe Coop.png')), + 'greenhouse': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'houses.png')) .crop((160, 0, 160 + 112, 144 * 3)), - 'house' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'houses.png')) + 'house': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'houses.png')) .crop((0, 0, 160, 144 * 3)), - 'silo' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Silo.png')), - 'slime hutch' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Slime Hutch.png')), - 'stable' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Stable.png')), - 'well' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Well.png')), - 'earth obelisk': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Earth Obelisk.png')), - 'gold clock' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Gold Clock.png')), - 'junimo hut' : { - 'spring': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Junimo Hut.png')).crop( + 'silo': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Silo.png')), + 'slime hutch': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Slime Hutch.png')), + 'stable': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Stable.png')), + 'well': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Well.png')), + 'earth obelisk': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Earth Obelisk.png')), + 'gold clock': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Gold Clock.png')), + 'junimo hut': { + 'spring': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Junimo Hut.png')).crop( (0, 0, 48, 64)), - 'summer': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Junimo Hut.png')).crop( + 'summer': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Junimo Hut.png')).crop( (48, 0, 48 * 2, 64)), - 'fall' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Junimo Hut.png')).crop( + 'fall': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Junimo Hut.png')).crop( (48 * 2, 0, 48 * 3, 64)), - 'winter': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Junimo Hut.png')).crop( + 'winter': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Junimo Hut.png')).crop( (48 * 3, 0, 48 * 4, 64)) }, - 'mill' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Mill.png')), - 'shed' : Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Shed.png')), - 'water obelisk': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Water Obelisk.png')) + 'mill': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Mill.png')), + 'shed': Image.open(os.path.join(asset_dir, 'farm', 'buildings', 'Shed.png')), + 'water obelisk': Image.open( + os.path.join(asset_dir, 'farm', 'buildings', 'Water Obelisk.png')) }, - 'binLid' : Image.open(os.path.join(asset_dir, 'farm', 'looseSprites', 'binLid.png')) + 'binLid': Image.open(os.path.join(asset_dir, 'farm', 'looseSprites', 'binLid.png')) } return assets def loadAvatarAssets(): assets = { - 'base' : { - 'male' : Image.open(os.path.join(asset_dir, 'player', 'male', 'male_base.png')), + 'base': { + 'male': Image.open(os.path.join(asset_dir, 'player', 'male', 'male_base.png')), 'female': Image.open(os.path.join(asset_dir, 'player', 'female', 'female_base.png')) }, - 'boots' : { - 'male' : Image.open(os.path.join(asset_dir, 'player', 'male', 'male_boots.png')), + 'boots': { + 'male': Image.open(os.path.join(asset_dir, 'player', 'male', 'male_boots.png')), 'female': Image.open(os.path.join(asset_dir, 'player', 'female', 'female_boots.png')) }, - 'legs' : { - 'male' : Image.open(os.path.join(asset_dir, 'player', 'male', 'male_legs.png')), + 'legs': { + 'male': Image.open(os.path.join(asset_dir, 'player', 'male', 'male_legs.png')), 'female': Image.open(os.path.join(asset_dir, 'player', 'female', 'female_legs.png')) }, - 'arms' : { - 'male' : Image.open(os.path.join(asset_dir, 'player', 'male', 'male_arms.png')), + 'arms': { + 'male': Image.open(os.path.join(asset_dir, 'player', 'male', 'male_arms.png')), 'female': Image.open(os.path.join(asset_dir, 'player', 'female', 'female_arms.png')) }, - 'hair' : Image.open(os.path.join(asset_dir, 'player', 'misc', 'hairstyles.png')), + 'hair': Image.open(os.path.join(asset_dir, 'player', 'misc', 'hairstyles.png')), 'accessories': Image.open(os.path.join(asset_dir, 'player', 'misc', 'accessories.png')), - 'shirts' : Image.open(os.path.join(asset_dir, 'player', 'misc', 'shirts.png')), + 'shirts': Image.open(os.path.join(asset_dir, 'player', 'misc', 'shirts.png')), 'skin colors': Image.open(os.path.join(asset_dir, 'player', 'misc', 'skinColors.png')) } - return assets \ No newline at end of file + return assets diff --git a/sdv/imagegeneration/farm.py b/sdv/imagegeneration/farm.py index 69d0718..983c24f 100644 --- a/sdv/imagegeneration/farm.py +++ b/sdv/imagegeneration/farm.py @@ -138,6 +138,9 @@ def generateFarm(season, data, assets=None): overlay = cropImg(assets['craftables'], 176, (16, 32), (16, 32)) obj_img.paste(overlay, box=(0, 0), mask=overlay) + + if item.flipped: + obj_img = obj_img.transpose(Image.FLIP_LEFT_RIGHT) farm_base.paste(obj_img, (item.x * 16, item.y * 16 - offset), obj_img) if item.name == 'Fence': @@ -170,6 +173,49 @@ def generateFarm(season, data, assets=None): except Exception as e: print(e) + if item.name == 'Bush': + + sprite_sizes = [(16, 32), (32, 48), (48, 48)] + + sprite_size = sprite_sizes[item.growth] + season_offset = 0 + if item.growth == 0: + if season == 'spring': + season_offset = 0 + elif season == 'summer': + season_offset = 2 + elif season == 'fall': + season_offset = 4 + elif season == 'winter': + season_offset = 6 + elif item.growth == 1: + if season == 'spring': + season_offset = 0 + elif season == 'summer': + season_offset = 4 + item.index * 2 + elif season == 'fall': + season_offset = 8*3 + elif season == 'winter': + season_offset = 8*3 + 4 + elif item.growth == 2: + if season == 'spring': + season_offset = 0 + elif season == 'summer': + season_offset = 0 + elif season == 'fall': + season_offset = 3 + elif season == 'winter': + season_offset = 8*3 + + sprite_index = [8*14, 8*0, 8*8][item.growth] + season_offset + + obj_img = cropImg(assets['bushes'], sprite_index, objectSize=sprite_size) + + if item.flipped: + obj_img = obj_img.transpose(Image.FLIP_LEFT_RIGHT) + + farm_base.paste(obj_img, (item.x * 16, item.y * 16 - (sprite_size[1])+16), obj_img) + if item.name == 'ResourceClump': obj_img = cropImg(assets['objects'], item.type, objectSize=(32, 32)) farm_base.paste(obj_img, (item.x * 16, item.y * 16), obj_img) @@ -264,8 +310,9 @@ def generateFarm(season, data, assets=None): print(e) if item.name == "House": + house_index = 2 if item.index == 3 else item.index try: - house_img = cropImg(assets['buildings']['house'], item.index, + house_img = cropImg(assets['buildings']['house'], house_index, defaultSize=(160, 144), objectSize=(160, 144)) farm_base.paste(house_img, (item.x * 16, item.y * 16 - 16 * item.h), house_img) except Exception as e: diff --git a/sdv/static/css/languages/pt_br.png b/sdv/static/css/languages/pt_BR.png similarity index 100% rename from sdv/static/css/languages/pt_br.png rename to sdv/static/css/languages/pt_BR.png