text:
#include "stdafx.h"
#include "mapsystem.h"

#include "config/config.h"
#include "util/streams.h"

#include <infrastructure/mesparser.h>
#include <infrastructure/vfs.h>
#include <infrastructure/stringutil.h>
#include <infrastructure/binaryreader.h>

#include <temple/dll.h>
#include <temple/meshes.h>

#include <tio/tio.h>

#include "animgoals/anim.h"
#include "map/sector.h"
#include "obj.h"
#include "gamesystems/legacysystems.h"
#include "gamesystems/objects/objsystem.h"
#include "gamesystems/map/gmesh.h"
#include "tig/tig_startup.h"
#include "temple/soundsystem.h"

#include "gamesystem.h"
#include "gamesystems.h"
#include "legacymapsystems.h"

#include "particlesystems.h"

#include "clipping/clipping.h"
#include "graphics/mapterrain.h"
#include "objects/objevent.h"
#include "python/python_debug.h"
#include "gamesystems/objects/objsystem.h"

struct MapListEntry {
	int id;
	int flags = 0;
	std::string name;
	std::string description;
	int worldmap = 0;
	int area = 0;
	int movie = 0;
	int startPosX = 0;
	int startPosY = 0;
	MapType type = MapType::None;

	bool IsOutdoors() const {
		return (flags & 2) == 2;
	}
	bool IsBedrest() const {
		return (flags & 8) == 8;
	}
	bool IsUnfogged() const {
		return (flags & 4) == 4;
	}
};

static struct MapSystemAddresses : public temple::AddressTable {
public:
	BOOL (*FirstNonProtoHandle)(objHndl *obj, int *it);
	BOOL (*NextNonProtoHandle)(objHndl *obj, int *it);
	BOOL (*call_ui_tutorial_isactive)();

	using UiTipCallback = void(int);
	void (*ui_show_tip)(
		const char *title,
		const char *text,
		const char *okBtnLabel,
		const char *nextTipLabel,
		const char *showTipsLabel,
		BOOL *showTips,
		UiTipCallback *closeCallback
	);

	MapSystemAddresses() {
		rebase(FirstNonProtoHandle, 0x1009CC10);
		rebase(NextNonProtoHandle, 0x1009CCB0);
		rebase(call_ui_tutorial_isactive, 0x1009AB80);
		rebase(ui_show_tip, 0x1009AC40);
	}

} addresses;

MapSystem::MapSystem(TigInitializer &tig, const GameSystemConf &config, D20System &d20System, PartySystem &partySystem)
	: mTig(tig), mD20System(d20System), mPartySystem(partySystem) {

	LoadTips();
}

MapSystem::~MapSystem() {
}

void MapSystem::LoadModule() {
	auto mapList = MesFile::ParseFile("Rules\\MapList.mes");
	auto mapNames = MesFile::ParseFile("mes\\map_names.mes");

	std::vector<gsl::cstring_span<>> parts;

	bool alwaysFog = false;
	bool alwaysUnfog = false;
	if (!_stricmp(tolower(config.fogOfWar).c_str(), "always")) {
		alwaysFog = true;
	}
	else if (!_stricmp(tolower(config.fogOfWar).c_str(), "unfogged")) {
		alwaysUnfog = true;
	}

	for (auto &line : mapList) {
		MapListEntry entry;

		parts.clear();
		split(line.second, ',', parts, true);

		entry.id = line.first;
		entry.name.assign(parts[0].begin(), parts[0].end());
		entry.startPosX = atoi(parts[1].data());
		entry.startPosY = atoi(parts[2].data());
		entry.flags = 0;
		if (alwaysUnfog)
			entry.flags |= 4;

		// The rest are key value pairs
		for (size_t i = 3; i < parts.size(); ++i) {
			auto subParts = split(parts[i], ':', true);
			auto key = subParts[0];
			auto value = subParts[1];

			if (!strncmp(key.data(), "Type", key.size())) {
				if (!strncmp(value.data(), "NONE", value.size())) {
					entry.type = MapType::None;
				} else if (!strncmp(value.data(), "START_MAP", value.size())) {
					entry.type = MapType::StartMap;
				} else if (!strncmp(value.data(), "SHOPPING_MAP", value.size())) {
					entry.type = MapType::ShoppingMap;
				} else if (!strncmp(value.data(), "TUTORIAL_MAP", value.size())) {
					entry.type = MapType::TutorialMap;
				}
			} else if (!strncmp(key.data(), "WorldMap", key.size())) {
				entry.worldmap = atoi(value.data());
			} else if (!strncmp(key.data(), "Area", key.size())) {
				entry.area = atoi(value.data());
			} else if (!strncmp(key.data(), "Movie", key.size())) {
				entry.movie = atoi(value.data());
			} else if (!strncmp(key.data(), "Flag", key.size())) {
				if (!strncmp(value.data(), "DAYNIGHT_XFER", value.size())) {
					entry.flags |= 1;
				} else if (!strncmp(value.data(), "OUTDOOR", value.size())) {
					entry.flags |= 2;
				} else if (!strncmp(value.data(), "UNFOGGED", value.size())) {
					if (!alwaysFog || entry.type == MapType::ShoppingMap)
						entry.flags |= 4;
				} else if (!strncmp(value.data(), "BEDREST", value.size())) {
					entry.flags |= 8;
				}
			}

		}
		mMaps[line.first] = entry;
	}

	for (auto &line : mapNames) {
		mMaps[line.first].description.assign( line.second  );
	}

	// get info from hardcoded map areas table (bleh)
	for (auto &entry: mMaps) {
		if (!entry.second.area) {
			auto mapArea = temple::GetRef<int(__cdecl)(int)>(0x1006EC30)(entry.second.id);
			entry.second.area = mapArea;
		}
	}

	if (config.debugObjects) {
		// Search for duplicate mob files
		std::map<std::string, int> objMapId;
		for (auto& it: mMaps) {
			auto &entry = it.second;
			auto dataDir = fmt::format("maps\\{}", entry.name);
			auto mobFiles = vfs->Search(dataDir + "\\*.mob");

			for (auto& mobFile : mobFiles) {
				auto found = objMapId.find(mobFile.filename);
				if (found != objMapId.end()) {

					auto file = tio_fopen( fmt::format("{}/{}", dataDir, mobFile.filename).c_str() , "rb");

					uint32_t header;
					if (tio_fread(&header, sizeof(header), 1, file) != 1) {
						throw TempleException("ObjSystem::LoadFromFile: Couldn't read the object header.");
					}

					if (header != 0x77) {
						throw TempleException("Expected object header 0x77, but got 0x{:x}", header);
					}

					ObjectId protoId;
					if (tio_fread(&protoId, sizeof(protoId), 1, file) != 1) {
						throw TempleException("Couldn't read the prototype id.");
					}

					logger->error("Duplicates MOB file found: {}/{} (was also on mapID = {}, now on {}). ProtoID: {}", dataDir, mobFile.filename, found->second, entry.id, protoId.GetPrototypeId());

					tio_fclose(file);
				}
				else {
					objMapId[mobFile.filename] = entry.id;
				}
			}

		}

	}

	RegisterDebugFunction("check_mobs",
		[&]() {
			std::unordered_map<std::string, int> mobObjMapId;
			std::unordered_map<std::string, int> dynObjMapId;
			auto DEBUG_GUID_STR = "G_2524B3E6_4991_4A73_A73E_E2338AF4BA3C"; // guid to look for...
			ObjectId debugGuid;
			objHndl debugGuidHandle = objHndl::null;
			auto readObject = temple::GetPointer<BOOL(objHndl*, const char*)>(0x100DE690);

			// first, read all MOB files to find the "originals" (where applicable)
			for (auto& it : mMaps) {
				auto& entry = it.second;
				auto mapId = entry.id;
				auto dataDir = fmt::format("maps\\{}", entry.name);

				// Iterate all MOB files
				auto mobFiles = vfs->Search(dataDir + "\\*.mob");
				for (auto& mobFile : mobFiles) {
					auto file = tio_fopen(fmt::format("{}/{}", dataDir, mobFile.filename).c_str(), "rb");

					uint32_t header;
					if (tio_fread(&header, sizeof(header), 1, file) != 1) {
						throw TempleException("ObjSystem::LoadFromFile: Couldn't read the object header.");
					}

					if (header != 0x77) {
						throw TempleException("Expected object header 0x77, but got 0x{:x}", header);
					}

					ObjectId protoId;
					if (tio_fread(&protoId, sizeof(protoId), 1, file) != 1) {
						throw TempleException("Couldn't read the prototype id.");
					}
					if (!protoId.IsPrototype()) {
						throw TempleException("Expected a prototype id, but got type {} instead.", (int)protoId.subtype);
					}
					ObjectId objId;
					if (tio_fread(&objId, sizeof(objId), 1, file) != 1) {
						throw TempleException("Couldn't read the object id.");
			}
					tio_fclose(file);

					// check that the object GUID matches the filename
					auto objIdString = objId.ToString();
					if (nullptr == std::strstr(mobFile.filename.c_str(), objIdString.c_str())) {
						logger->error("Mismatch between MOB filename and GUID content! Filename: {} GUID: {}", mobFile.filename, objId);
					}

					// check for dupes
					auto found = mobObjMapId.find(objIdString);
					if (found != mobObjMapId.end()) {
						logger->error("Duplicates MOB file found: {}/{} (was also on mapID = {}, now on {}). ProtoID: {}", dataDir, mobFile.filename, found->second, entry.id, protoId.GetPrototypeId());
					}
					else {
						mobObjMapId[objIdString] = entry.id;
					}

					if (nullptr != std::strstr(objIdString.c_str(), DEBUG_GUID_STR)) {
						debugGuid = objId;
					}
				}

			}

			const int MAX_MAP_ID = 5135;

			for (auto& it : mMaps) {

				auto& entry = it.second;
				auto mapId = entry.id;
				auto saveDir = fmt::format("Save\\Current\\maps\\{}", entry.name);
				auto dataDir = fmt::format("maps\\{}", entry.name);

				if (mapId >= MAX_MAP_ID) {
					continue;
		}

				OpenMap(mapId, false, true);

				// debug:
				"G_2524B3E6_4991_4A73_A73E_E2338AF4BA3C";
				auto debugHandle = objSystem->GetHandleById(debugGuid);
				if (debugHandle) {
					debugGuidHandle = debugHandle;
				}
				logger->debug("*** Finished processing map {}; Debug Handle: {}; debugGuidHandle valid: {}", mapId, debugHandle, debugGuidHandle && objSystem->IsValidHandle(debugGuidHandle));
	}

		});
}

void MapSystem::UnloadModule() {
	mMaps.clear();
}

void MapSystem::Reset() {
	ResetFleeTo();

	logger->info("Resetting map");

	gameSystems->GetAnim().InterruptAll();

	CloseMap();
	mCurrentMap = nullptr; // TODO: Redundant?

	mVisitedMaps.clear();
}

#pragma pack(push, 1)
struct MapFleeSaveData {
	int mapId;
	int padding1;
	LocAndOffsets loc;
	int enterX;
	int enterY;
	BOOL isFleeing;
	int padding2;
};
#pragma pack(pop)

template<typename T>
static void SaveIdxTable(const std::map<int32_t, T> &values, TioFile* file) {
	const uint32_t header = 0xAB1EE1BA;
	if (tio_fwrite(&header, sizeof(uint32_t), 1, file) != 1) {
		throw TempleException("Unable to write the idx table header");
	}

	uint32_t bucketCount = 1;
	if (tio_fwrite(&bucketCount, sizeof(uint32_t), 1, file) != 1) {
		throw TempleException("Unable to write the idx table capacity");
	}

	const uint32_t dataSize = sizeof(T);
	if (tio_fwrite(&dataSize, sizeof(uint32_t), 1, file) != 1) {
		throw TempleException("Unable to write the idx table item size");
	}

	// ToEE seems to distinguish between capacity and size, but we don't
	size_t nodeCount = values.size();
	if (tio_fwrite(&nodeCount, sizeof(uint32_t), 1, file) != 1) {
		throw TempleException("Unable to write the idx table size");
	}

	for (auto &entry : values) {
		if (tio_fwrite(&entry.first, sizeof(int32_t), 1, file) != 1) {
			throw TempleException("Unable to write key for idx table entry.");
		}
		if (tio_fwrite(&entry.second, sizeof(T), 1, file) != 1) {
			throw TempleException("Unable to write value for idx table entry.");
		}
	}

	const uint32_t footer = 0xE1BAAB1E;
	if (tio_fwrite(&footer, sizeof(uint32_t), 1, file) != 1) {
		throw TempleException("Unable to write the idx table footer");
	}

}

template<typename T>
static map<int32_t,T> LoadIdxTable(TioFile* file) {
	uint32_t magicNumber;
	if (tio_fread(&magicNumber, sizeof(uint32_t), 1, file) != 1
		|| magicNumber != 0xAB1EE1BA) {
		throw TempleException("Unable to read the idx table header");
	}

	uint32_t bucketCount;
	if (tio_fread(&bucketCount, sizeof(uint32_t), 1, file) != 1) {
		throw TempleException("Unable to read the idx table capacity");
	}

	uint32_t dataSize;
	if (tio_fread(&dataSize, sizeof(uint32_t), 1, file) != 1) {
		throw TempleException("Unable to write the idx table item size");
	}

	std::map<int32_t, T> result;
	for (size_t bucket = 0; bucket < bucketCount; ++bucket) {
		size_t nodeCount;
		if (tio_fread(&nodeCount, sizeof(uint32_t), 1, file) != 1) {
			throw TempleException("Unable to write the idx table size");
		}

		for (size_t i = 0; i < nodeCount; ++i) {
			int32_t key;
			if (tio_fread(&key, sizeof(int32_t), 1, file) != 1) {
				throw TempleException("Unable to read key for idx table entry.");
			}
			T value;
			if (tio_fread(&value, sizeof(T), 1, file) != 1) {
				throw TempleException("Unable to read value for idx table entry.");
			}
			result[key] = value;
		}
	}

	if (tio_fread(&magicNumber, sizeof(uint32_t), 1, file) != 1
		|| magicNumber != 0xE1BAAB1E) {
		throw TempleException("Unable to read the idx table footer");
	}

	return result;

}

bool MapSystem::SaveGame(TioFile *file) {

	FlushMap(1);

	if (tio_fputs(mSectorDataDir.c_str(), file) == -1) {
		logger->error("Couldn't write sector data directory.");
		return false;
	}
	if (tio_fputc('\n', file) == -1) {
		logger->error("Couldn't write sector directory separator.");
		return false;
	}
	if (tio_fputs(mSectorSaveDir.c_str(), file) == -1) {
		logger->error("Couldn't write sector save directory.");
		return false;
	}
	if (tio_fputc('\n', file) == -1) {
		logger->error("Couldn't write sector directory separator.");
		return false;
	}

	// Save visited maps
	std::map<int32_t, int32_t> visitedMaps;
	for (auto mapId : mVisitedMaps) {
		visitedMaps[mapId] = 1;
	}
	SaveIdxTable(visitedMaps, file);

	// Previously, the save functions were called here, but no map subsystem
	// actually had any save callbacks registered

	// Flush map will clear the dispatchers of the entire party
	// and map load postprocess will not restore them, so we
	// have to do that here
	mPartySystem.ForEachInParty([&](objHndl handle) {
		d20Sys.d20Status->D20StatusInit(handle);
	});

	// Save Flee data
	MapFleeSaveData fleeData;
	fleeData.mapId = mFleeInfo.mapId;
	fleeData.loc = mFleeInfo.location;
	fleeData.enterX = mFleeInfo.enterLocation.locx;
	fleeData.enterY = mFleeInfo.enterLocation.locy;
	fleeData.isFleeing = mFleeInfo.isFleeing ? TRUE : FALSE;

	auto mapFleeFh = vfs->Open("Save\\Current\\map_mapflee.bin", "wb");
	vfs->Write(&fleeData, sizeof(fleeData), mapFleeFh);
	vfs->Close(mapFleeFh);

	// Why does it call spell_save here directly? ... Who knows!
	gameSystems->GetSpell().Save(file);

	return true;

}

bool MapSystem::LoadGame(GameSystemSaveFile* saveFile) {
	// originally 10072C40

	char filename[260];
	if (!tio_fgets(filename, 260, saveFile->file)) {
		logger->error("Unable to load current map data directory");
		return false;
	}
	auto len = strnlen(filename, 259);
	if (len > 0 && filename[len - 1] == '\n') {
		filename[len - 1] = '\0';
	} else {
		logger->error("Map data directory not correctly terminated.");
		return false;
	}

	// Find the map entry corresponding to the map directory
	auto lastBackslash = strrchr(filename, '\\');
	const char* mapName;
	if (lastBackslash)
		mapName = lastBackslash + 1;
	else
		mapName = filename;

	MapListEntry *mapEntry = nullptr;
	for (auto& entry : mMaps) {
		if (!_stricmp(entry.second.name.c_str(), mapName)) {
			mapEntry = &entry.second;
			break;
		}
	}
	if (!mapEntry) {
		logger->error("No map was found that matches {}", filename);
		return false;
	}

	logger->info("Loading map id {}, name {}", mapEntry->id, mapEntry->name);

	if (!tio_fgets(filename, 260, saveFile->file)) {
		logger->error("Unable to load current map data directory");
		return false;
	}
	len = strnlen(filename, 259);
	if (len > 0 && filename[len - 1] == '\n') {
		filename[len - 1] = '\0';
	} else {
		logger->error("Map save directory not correctly terminated.");
		return false;
	}

	// NOTE: save dir is ignored

	auto visitedMaps = LoadIdxTable<int32_t>(saveFile->file);

	if (vfs->FileExists("Save\\Current\\map_mapflee.bin")) {
		auto mapFleeFh = vfs->Open("Save\\Current\\map_mapflee.bin", "rb");
		MapFleeSaveData fleeData;
		vfs->Read(&fleeData, sizeof(fleeData), mapFleeFh);
		mFleeInfo.mapId = fleeData.mapId;
		mFleeInfo.isFleeing = fleeData.isFleeing != 0;
		mFleeInfo.enterLocation.locx = fleeData.enterX;
		mFleeInfo.enterLocation.locy = fleeData.enterY;
		mFleeInfo.location = fleeData.loc;
		vfs->Close(mapFleeFh);
	}

	OpenMap(mapEntry);

	mVisitedMaps.clear();
	for (auto &entry : visitedMaps) {
		mVisitedMaps.insert(entry.first);
	}

	return gameSystems->GetSpell().Load(saveFile);

}

const std::string &MapSystem::GetName() const {
	static std::string name("Map");
	return name;
}

bool MapSystem::PseudoLoad(GameSystemSaveFile * saveFile, std::string saveFolder, std::vector<objHndl> & dynHandles)
{
	char filename[260];
	if (!tio_fgets(filename, 260, saveFile->file)) {
		logger->error("Unable to load current map data directory");
		return false;
	}
	auto len = strnlen(filename, 259);
	if (len > 0 && filename[len - 1] == '\n') {
		filename[len - 1] = '\0';
	}
	else {
		logger->error("Map data directory not correctly terminated.");
		return false;
	}

	// Find the map entry corresponding to the map directory
	auto lastBackslash = strrchr(filename, '\\');
	const char* mapName;
	if (lastBackslash)
		mapName = lastBackslash + 1;
	else
		mapName = filename;

	MapListEntry *mapEntry = nullptr;
	for (auto& entry : mMaps) {
		if (!_stricmp(entry.second.name.c_str(), mapName)) {
			mapEntry = &entry.second;
			break;
		}
	}
	if (!mapEntry) {
		logger->error("No map was found that matches {}", filename);
		return false;
	}

	logger->info("Loading map id {}, name {}", mapEntry->id, mapEntry->name);

	if (!tio_fgets(filename, 260, saveFile->file)) {
		logger->error("Unable to load current map data directory");
		return false;
	}
	len = strnlen(filename, 259);
	if (len > 0 && filename[len - 1] == '\n') {
		filename[len - 1] = '\0';
	}
	else {
		logger->error("Map save directory not correctly terminated.");
		return false;
	}

	// NOTE: save dir is ignored

	auto visitedMaps = LoadIdxTable<int32_t>(saveFile->file);
	if (saveFolder.size() < 2)
		return false;

	if (saveFolder[saveFolder.size() - 1] != '\\') {
		saveFolder.resize(saveFolder.size() + 1);
		saveFolder[saveFolder.size() - 1] = '\\';
	}
	std::string mapfleeFile = fmt::format("{}map_mapflee.bin", saveFolder);
	if (vfs->FileExists(mapfleeFile.c_str()) ) {
		auto mapFleeFh = vfs->Open(mapfleeFile.c_str(), "rb");
		MapFleeSaveData fleeData;
		vfs->Read(&fleeData, sizeof(fleeData), mapFleeFh);
		vfs->Close(mapFleeFh);
	}

	// Pseudo Open Map

	auto dataDir = fmt::format("maps\\{}", mapEntry->name);
	auto saveDir = fmt::format("{}maps\\{}", saveFolder, mapEntry->name);

	logger->info("Loading Map: {}", dataDir);

	if (!vfs->DirExists(dataDir)) {
		logger->error("Cannot open map '{}' because it doesn't exist.", dataDir);
		return false;
	}

	vfs->MkDir(saveDir);

	auto mdyFilename = fmt::format("{}\\mobile.mdy", saveDir);

	if (!vfs->FileExists(mdyFilename)) {
		logger->info("Skipping dynamic mobiles because {} doesn't exist.", mdyFilename);
		return false;
	}

	logger->info("Loading dynamic mobiles from {}", mdyFilename);

	auto mdyfh = tio_fopen(mdyFilename.c_str(), "rb");
	if (!mdyfh) {
		throw TempleException("Unable to open dynamic mobile file for reading: {}", mdyFilename);
	}

	dynHandles.clear();
	while (true) {
		try {
			auto handle = objSystem->LoadFromFile(mdyfh);
			auto d = description.getDisplayName(handle);
			std::string handleDesc;
			if (d)
				handleDesc = fmt::format("{}", d);
			else
				handleDesc = fmt::format("Unnamed");
			logger->debug("PseudoLoad: dynamic object {} ({})", handleDesc,	objSystem->GetObject(handle)->id.ToString());
			dynHandles.push_back(handle);
		}
		catch (TempleException &e) {
			logger->error("Unable to load object: {}", e.what());
			break;
		}

	}

	if (!tio_feof(mdyfh)) {
		tio_fclose(mdyfh);
		throw TempleException("Error while reading dynamic mobile file {}", mdyFilename);
	}

	tio_fclose(mdyfh);

	logger->info("Done reading dynamic mobiles.");

	// Post Process

	objSystem->ForEachObj([&](objHndl handle, GameObjectBody& obj) {
		if (!obj.IsStatic()) {
			obj.UnfreezeIds();
		}

		/*if (obj.HasFlag(OF_TELEPORTED)) {
			TimeEvent e;
			e.system = TimeEventType::Teleported;
			e.params[0].handle = handle;
			gameSystems->GetTimeEvent().ScheduleNow(e);
			obj.SetFlag(OF_TELEPORTED, false);
			return;
		}*/

		//// Initialize everything's D20 state
		//if (gameSystems->GetParty().IsInParty(handle)) {
		//	return; // The party is initialized elsewhere (in the Party system)
		//}

		// This logic is a bit odd really. Apparently obj_f_dispatcher will not be -1 for non-critters anyway?
		if (obj.IsNPC() || obj.GetInt32(obj_f_dispatcher) == -1) {
			d20Sys.d20Status->D20StatusInit(handle);
		}

	});

	return true;
}

void MapSystem::ResetFleeTo()
{
	mFleeInfo = MapFleeInfo();
}

void MapSystem::SetFleeInfo(int mapId, LocAndOffsets loc, locXY enterLoc)
{
	mFleeInfo.mapId = mapId;
	mFleeInfo.enterLocation = enterLoc;
	mFleeInfo.location = loc;
}

bool MapSystem::HasFleeInfo() const
{
	return mFleeInfo.mapId != 0;
}

bool MapSystem::IsFleeing() const {
	return mFleeInfo.isFleeing;
}

void MapSystem::SetFleeing(bool fleeing)
{
	mFleeInfo.isFleeing = fleeing;
}

SectorExplorationState MapSystem::GetExplorationData(SectorLoc sectorLoc, SectorExplorationData * dataOut)
{
	auto filename(fmt::format("{}\\esd{}", mSectorSaveDir, sectorLoc.raw));

	auto fh(vfs->Open(filename.c_str(), "rb"));

	if (!fh) {
		return SectorExplorationState::Unexplored;
	}

	uint8_t fullyExplored = 0;
	vfs->Read(&fullyExplored, 1, fh);

	if (fullyExplored) {
		vfs->Close(fh);
		return SectorExplorationState::AllExplored;
	}

	vfs->Read(&dataOut->explorationBitmap[0], sizeof(dataOut->explorationBitmap), fh);
	vfs->Close(fh);
	return SectorExplorationState::PartiallyExplored;
}

void MapSystem::SetExplorationData(SectorLoc sectorLoc, const SectorExplorationData & dataOut)
{
	// First determine whether the entirety of the sector has been explored
	bool allExplored = true;
	for (auto &i : dataOut.explorationBitmap) {
		if (i != 0xFF) {
			allExplored = false;
			break;
		}
	}

	auto filename(fmt::format("{}\\esd{}", mSectorSaveDir, sectorLoc.raw));

	auto fh = vfs->Open(filename.c_str(), "wb");
	if (!fh) {
		logger->error("Unable to write sector exploration data to {}", filename);
		return;
	}

	uint8_t allExploredByte = allExplored ? 1 : 0;
	vfs->Write(&allExploredByte, 1, fh);

	if (!allExplored) {
		vfs->Write(&dataOut.explorationBitmap[0], sizeof(dataOut.explorationBitmap), fh);
	}

	vfs->Close(fh);
}

void MapSystem::PreloadSectorsAround(locXY loc)
{
	// Center sector
	SectorLoc sectorLoc{ loc };
	int startX = (int) sectorLoc.x() - 1;
	int startY = (int) sectorLoc.y() - 1;

	Sector* sector;
	for (int x = 0; x < 3; ++x) {
		for (int y = 0; y < 3; ++y) {
			SectorLoc loc{ startX + x, startY + y };
			if (sectorSys.SectorLock(loc, &sector)) {
				sectorSys.SectorUnlock(loc);
			}

		}
	}

}

int MapSystem::GetMapIdByType(MapType type)
{
	for (auto &entry : mMaps) {
		if (entry.second.type == type) {
			return entry.first;
		}
	}
	if (type == MapType::ArenaMap){
		return 5119;
	}
	return 0;
}

bool MapSystem::IsRandomEncounterMap(int mapId) const
{
	auto maxRange = 5077;
	if (temple::Dll::GetInstance().IsExpandedWorldmapDll())
		maxRange = 5078;

	return mapId >= 5070 && mapId <= maxRange;
}

bool MapSystem::IsVignetteMap(int mapId) const
{
	return mapId >= 5096 && mapId <= 5104;
}

bool MapSystem::IsCurrentMapOutdoors() const
{
	if (mCurrentMap) {
		return mCurrentMap->IsOutdoors();
	}
	return false;
}

bool MapSystem::IsCurrentMapBedrest() const
{
	if (mCurrentMap) {
		return mCurrentMap->IsBedrest();
	}
	return false;
}

void MapSystem::ClearDispatchers()
{

	int it;
	objHndl handle;
	if (addresses.FirstNonProtoHandle(&handle, &it)) {
		do {
			if (!objects.IsStatic(handle)) {
				mD20System.RemoveDispatcher(handle);
			}
		} while (addresses.NextNonProtoHandle(&handle, &it));
	}

}

void MapSystem::ClearObjects()
{
	Expects(!mClearingMap);

	mClearingMap = true;

	gameSystems->GetTimeEvent().ClearForMapClose();

	auto freeRenderState = temple::GetPointer<void(objHndl)>(0x10021930);
	auto removeMapObj = temple::GetPointer<void(objHndl)>(0x100219B0);

	int it;
	objHndl handle;
	if (addresses.FirstNonProtoHandle(&handle, &it)) {
		do {
			if (!objects.IsStatic(handle)) {
				freeRenderState(handle);
				removeMapObj(handle);
			}
		} while (addresses.NextNonProtoHandle(&handle, &it));
	}

	gameSystems->GetMapSector().Clear();
	gameSystems->GetObj().CompactIndex();

	mClearingMap = false;
}

void MapSystem::ShowGameTip()
{
	if (mEnableTips)
	{
		if (!addresses.call_ui_tutorial_isactive())
		{
			auto tip = config.GetVanillaInt("startup_tip");
			if (tip >= 0) {
				mEnableTips = true;
				ShowGameTip(tip);
			}
		}
	}
}

void MapSystem::ShowGameTip(int tipId)
{
	// Roll over to the first tip
	if (tipId >= (int) mTips.size()) {
		tipId = 0;
	}

	// Have to make this static for c_str to be reliable
	static std::string sTipText = mTips[tipId];
	static MapSystem *sMapSystems = this;
	static BOOL sShowTips = TRUE;

	sTipText = mTips[tipId];
	addresses.ui_show_tip(
		mTipsDialogTitle.c_str(),
		sTipText.c_str(),
		mTipsDialogOk.c_str(),
		mTipsDialogNext.c_str(),
		mTipsDialogShowTips.c_str(),
		&sShowTips,
		[](int okButton) {
			if (okButton) {
				if (!sShowTips) {
					config.SetVanillaInt("startup_tip", -1);
				}
			} else {
				auto nextTip = config.GetVanillaInt("startup_tip");
				sMapSystems->ShowGameTip(nextTip);
			}
		});

	config.SetVanillaInt("startup_tip", tipId + 1);
}

bool MapSystem::IsValidMapId(int mapId)
{
	return mMaps.find(mapId) != mMaps.end();
}

int MapSystem::GetCurrentMapId() const
{
	if (mCurrentMap) {
		return mCurrentMap->id;
	}
	return 0;
}

int MapSystem::GetHighestMapId() const
{
	int highestId = 0;
	for (auto & entry : mMaps) {
		if (entry.second.id > highestId) {
			highestId = entry.second.id;
		}
	}
	return highestId;
}

locXY MapSystem::GetStartPos(int mapId) const
{
	locXY result{ 0, 0 };

	auto it = mMaps.find(mapId);
	if (it != mMaps.end()) {
		result.locx = it->second.startPosX;
		result.locy = it->second.startPosY;
	}

	return result;
}

const std::string & MapSystem::GetMapName(int mapId)
{
	auto it = mMaps.find(mapId);
	if (it != mMaps.end()) {
		return it->second.name;
	}

	static std::string sEmptyName;
	return sEmptyName;
}

const std::string & MapSystem::GetMapDescription(int mapId)
{
	auto it = mMaps.find(mapId);
	if (it != mMaps.end()) {
		return it->second.description;
	}

	static std::string sEmptyName;
	return sEmptyName;
}

bool MapSystem::IsMapOutdoors(int mapId)
{
	auto it = mMaps.find(mapId);
	if (it != mMaps.end()) {
		return it->second.IsOutdoors();
	}
	return false;
}

int MapSystem::GetEnterMovie(int mapId, bool ignoreVisited)
{
	if (!ignoreVisited && mVisitedMaps.find(mapId) != mVisitedMaps.end()) {
		return 0;
	}

	auto it = mMaps.find(mapId);
	if (it != mMaps.end()) {
		return it->second.movie;
	}
	return 0;
}

void MapSystem::MarkVisitedMap(objHndl obj)
{
	if (objects.GetType(obj) != obj_t_pc) {
		return;
	}

	if (!mCurrentMap) {
		return;
	}

	int mapId = mCurrentMap->id;
	if (!IsRandomEncounterMap(mapId) && !IsVignetteMap(mapId)) {
		mVisitedMaps.insert(mapId);
	}
}

bool MapSystem::IsUnfogged(int mapId)
{
	auto it = mMaps.find(mapId);
	if (it != mMaps.end()) {
		return it->second.IsUnfogged();
	}
	return false;
}

int MapSystem::GetArea(int mapId) const
{
	auto it = mMaps.find(mapId);
	if (it != mMaps.end()) {
		return it->second.area;
	}
	return 0;
}

/* 10072A90 */
bool MapSystem::OpenMap(int mapId, bool preloadSectors, bool dontSaveCurrentMap)
{
	auto it = mMaps.find(mapId);
	if (it == mMaps.end()) {
		logger->warn("Trying to open unknown map id {}", mapId);
		return false;
	}

	auto &mapEntry = it->second;

	mPartySystem.SaveCurrent();

	if (IsMapOpen() && !dontSaveCurrentMap) {
		FlushMap(0);
	}

	mTig.GetSoundSystem().ProcessEvents();

	auto validateSector = temple::GetPointer<BOOL(uint8_t)>(0x1009F550);

	if (!validateSector(1))
	{
		throw TempleException("Object system validate failed pre-load.");
	}

	OpenMap(&mapEntry);

	mStartLoc.locx = mapEntry.startPosX;
	mStartLoc.locy = mapEntry.startPosY;

	gameSystems->GetLocation().CenterOn(mapEntry.startPosX, mapEntry.startPosY);

	mTig.GetSoundSystem().ProcessEvents();

	if (preloadSectors) {
		PreloadSectorsAround(mStartLoc);
	}

	gameSystems->GetTimeEvent().LoadForCurrentMap();
	mPartySystem.RestoreCurrent();
	gameSystems->GetCritter().UpdateNpcHealingTimers();

	auto uiAfterMapload = temple::GetPointer<void()>(0x1009A540);
	uiAfterMapload();

	if (!validateSector(1)) {
		throw TempleException("Object system validate failed post-load.");
	}

	return true;
}

void MapSystem::CloseMap()
{
	if (!mMapClosed)
	{
		mMapClosed = true;

		static auto turnBasedReset = temple::GetPointer<void()>(0x10092A50);
		turnBasedReset();

		gameSystems->GetAAS().FreeAll();

		mMapOpen = false;

		for (auto mapSystem : gameSystems->GetMapCloseAwareSystems()) {
			mapSystem->CloseMap();
		}

		ClearObjects();
		gameSystems->GetParticleSys().RemoveAll();

		mSectorSaveDir = "";
		mSectorDataDir = "";
	}
}

void MapSystem::LoadTips()
{
	auto tips(MesFile::ParseFile("mes/tips.mes"));

	mTipsDialogTitle = tips[0];
	mTipsDialogOk = tips[1];
	mTipsDialogNext = tips[2];
	mTipsDialogShowTips = tips[3];

	auto count = std::stoi(tips[99]);

	for (int i = 0; i < count; ++i) {
		mTips[i] = tips[100 + i];
	}

	if (config.GetVanillaInt("startup_tip") >= 0) {
		mEnableTips = true;
	} else {
		mEnableTips = false;
	}

}

void MapSystem::FlushMap(int flags) {

	// Freeze all IDs
	objSystem->ForEachObj([&](objHndl handle, GameObjectBody& obj) {
		// The assumption seems to be that static objs are not saved here
		if (!obj.IsStatic()) {
			obj.FreezeIds();
		}
		gameSystems->GetD20().RemoveDispatcher(handle);
	});

	SaveMapMobiles();

	// Unfreeze the IDs
	MapLoadPostprocess();

	// Both are fogging related, but I have no idea how they differ?
	gameSystems->GetMapFogging().SaveEsd();

	if (mCurrentMap) {
		gameSystems->GetMapFogging().SaveExploredTileData(mCurrentMap->id);
	}

	// Previously a "map.sbf" file was saved here, which is only used
	// by the old scripting system though
	SaveSectors(flags);
	// Previously several other subsystems saved their data here if they were
	// in editor mode

	// Flushes townmap data for the current map
	static auto ui_townmap_flush = temple::GetPointer<void()>(0x100521b0);
	ui_townmap_flush();

	// flushes the ObjectEvents (which are tied to spell objects anyway and should go away)
	if (!flags){
		objEvents.FlushEvents();
	}

}

#pragma pack(push, 1)
struct MapProperties {
	// ID for terrain art
	int groundArtId;
	int y;
	uint64_t limitX;
	uint64_t limitY;
};
#pragma pack(pop)

void MapSystem::OpenMap(const MapListEntry *mapEntry)
{

	auto dataDir = fmt::format("maps\\{}", mapEntry->name);
	auto saveDir = fmt::format("Save\\Current\\maps\\{}", mapEntry->name);

	logger->info("Loading Map: {}", dataDir);

	// Close opened map
	CloseMap();

	if (!vfs->DirExists(dataDir)) {
		throw TempleException("Cannot open map '{}' because it doesn't exist.", dataDir);
	}

	vfs->MkDir(saveDir);

	mSectorSaveDir = saveDir;
	mSectorDataDir = dataDir;

	gameSystems->GetHeight().SetDataDirs(dataDir, saveDir);
	gameSystems->GetPathNode().SetDataDirs(dataDir, saveDir);

	logger->info("Reading map properties from map.prp");

	auto prpFilename = fmt::format("{}\\map.prp", dataDir);
	auto prpContent = vfs->ReadAsBinary(prpFilename);
	Expects(prpContent.size() >= 24);

	BinaryReader reader(prpContent);
	auto mapProperties = reader.Read<MapProperties>();

	// Previously startloc.txt was loaded here, but that only seems to be relevant
	// for worlded, and not for the actual game.

	ReadMapMobiles(dataDir, saveDir);

	gameSystems->GetHeight().Clear();

	gameSystems->GetAnim().ClearGoalDestinations();

	gameSystems->GetClipping().Load(dataDir);

	gameSystems->GetGMesh().Load(dataDir);

	gameSystems->GetLight().Load(dataDir);

	gameSystems->GetMapFogging().LoadFogColor(dataDir);

	gameSystems->GetPathNode().Load(dataDir, saveDir);

	MapLoadPostprocess();

	// TODO: a global sector_datadir and sector_savedir were set here
	// But only  seemed to be used from within map systems and at one spot
	// in editor mode

	gameSystems->GetTerrain().Load(mapProperties.groundArtId);

	// Previously a "map.sbf" file was loaded here, which is only used
	// by the old scripting system though

	gameSystems->GetLocation().SetLimits(mapProperties.limitX, mapProperties.limitY);

	gameSystems->GetSector().SetLimits(mapProperties.limitX / 64, mapProperties.limitY / 64);

	gameSystems->GetMapSector().SetDirectories(dataDir, saveDir);
	gameSystems->GetSectorVB().SetDirectories(dataDir, saveDir);

	auto center = gameSystems->GetLocation().GetLimitsCenter();
	gameSystems->GetLocation().CenterOn(center.locx, center.locy);

	mCurrentMap = mapEntry;

	if (mapEntry->IsUnfogged()) {
		gameSystems->GetMapFogging().Disable();
	} else {
		gameSystems->GetMapFogging().Enable();
		gameSystems->GetMapFogging().LoadExploredTileData(mapEntry->id);
	}

	gameSystems->GetLight().SetMapId(mapEntry->id);

	gameSystems->GetScroll().SetMapId(mapEntry->id);

	LoadMapInfo(dataDir);

	// Previously there was a function call here that disabled objects outside the
	// Arkanum demo bounds, which are obviously unused here.

	mMapOpen = true;
	mMapClosed = false;

	logger->info("Finished loading the map");
}

void MapSystem::LoadMapInfo(const std::string & dataDir)
{
	auto filename = fmt::format("{}\\mapinfo.txt", dataDir);

	// This file is optional
	std::string mapInfoContent;
	try {
		mapInfoContent = vfs->ReadAsString(filename);
	} catch (TempleException &e) {
		logger->info("Couldn't find or read optional file: {} ({})", filename, e.what());
		return;
	}

	for (auto& line : split(mapInfoContent, '\n', true)) {

		auto parts = split(line, ':', true);
		if (parts.size() != 2) {
			continue;
		}

		auto prop = tolower(parts[0]);
		if (prop == "lightscheme") {
			auto lightSchemeId = std::stoi(parts[1]);
			gameSystems->GetLightScheme().SetLightSchemeId(lightSchemeId);
			auto hour = gameSystems->GetLightScheme().GetHourOfDay();
			gameSystems->GetLightScheme().SetLightScheme(0, hour);
		} else if (prop == "soundscheme") {
			auto schemes = split(parts[1], ',', true);
			auto scheme1 = std::stoi(schemes[0]);
			auto scheme2 = std::stoi(schemes[1]);
			gameSystems->GetSoundGame().SetSoundSchemeIds(scheme1, scheme2);
		} else if (prop == "reverb") {
			auto reverbs = split(parts[1], ',', true);
			auto roomType = std::stoi(reverbs[0]);
			auto reverbDry = std::stoi(reverbs[1]);
			auto reverbWet = std::stoi(reverbs[2]);
			mTig.GetSoundSystem().SetReverb(roomType, reverbDry, reverbWet);
		} else if (prop == "ground") {
			auto groundId = std::stoi(parts[1]);
			gameSystems->GetTerrain().Load(groundId);
		} else {
			logger->warn("Unknown command in map extension file {}: {}", filename, line);
		}

	}

}

/* 0x100717E0 */
void MapSystem::ReadMapMobiles(const std::string &dataDir, const std::string &saveDir)
{
	auto readObject = temple::GetPointer<BOOL(objHndl*, const char*)>(0x100DE690);

	// Read all mobiles that shipped with the game files
	auto mobFiles = vfs->Search(dataDir + "\\*.mob");

	logger->info("ReadMapMobiles: Loading {} map mobiles from {}", mobFiles.size(), dataDir);

	for (auto &mobFile : mobFiles) {
		auto filename = fmt::format("{}\\{}", dataDir, mobFile.filename);
		objHndl handle;
		if (!readObject(&handle, filename.c_str())) {
			logger->warn("Unable to load mobile object {} for level {}",
				filename, dataDir);
		} else //if (config.debugMessageEnable)
		{
			// Temple+: added check for OF_DYNAMIC (fixes "Jerkstop" issue)
			auto obj = objSystem->GetObject(handle);
			logger->trace("ReadMapMobiles: \t\tLoaded MOB obj {} ({})", handle, obj->id.ToString() );
			auto flags = obj->GetFlags();
			if (flags & OF_DYNAMIC) {
				logger->error("ReadMapMobiles: \t\t\tMOB file flagged OF_DYNAMIC!!! Unsetting.");
				obj->SetFlag(OF_DYNAMIC, false);
			}
		}
	}

	logger->info("ReadMapMobiles: \t\tDone loading map mobiles");

	// Read all mobile differences that have accumulated for this map in the save dir
	auto diffFilename = fmt::format("{}\\mobile.md", saveDir);

	if (vfs->FileExists(diffFilename)) {
		logger->info("ReadMapMobiles: Loading mobile diffs from {}", diffFilename);

		auto fh = tio_fopen(diffFilename.c_str(), "rb");
		if (!fh) {
			throw TempleException("Could not read mobile diffs from {}", diffFilename);
		}

		ObjectId objId;

		while (!tio_feof(fh)) {
			auto pos = tio_ftell(fh);

			if (tio_fread(&objId, sizeof(ObjectId), 1, fh) != 1) {
				if (tio_feof(fh)) {
					break;
				}
				tio_fclose(fh);
				throw TempleException("Unable to read next object id from mobile diff file {} @ {}",
					diffFilename, pos);
			}

			// Get the active handle for the mob so we can apply diffs to it
			auto handle = gameSystems->GetObj().GetHandleById(objId);
			if (!handle) {
				tio_fclose(fh);
				throw TempleException("Could not retrieve handle for {} to apply differences to from {}",
					objId, diffFilename);
			}

			auto obj = objSystem->GetObject(handle);
			obj->LoadDiffsFromFile(handle, fh);
			logger->trace("Loaded {} ({}) from diff file.", handle, objSystem->GetObject(handle)->id.ToString());

			if (objects.GetFlags(handle) & OF_EXTINCT) {
				logger->trace("{} is extinct.", handle);
				gameSystems->GetObj().Remove(handle);
			}
		}

		tio_fclose(fh);

		logger->info("ReadMapMobiles: \t\tDone loading map mobile diffs");

	} else {
		logger->info("ReadMapMobiles: \t\tSkipping mobile diffs, because {} is missing", diffFilename);
	}

	// Destroy all mobiles that had previously been destroyed
	auto desFilename = fmt::format("{}\\mobile.des", saveDir);

	if (vfs->FileExists(desFilename)) {
		logger->info("ReadMapMobiles: Loading destroyed mobile file from {}", desFilename);

		auto desContent = vfs->ReadAsBinary(desFilename);
		BinaryReader reader(desContent);

		while (!reader.AtEnd()) {
			auto objId = reader.Read<ObjectId>();
			auto handle = gameSystems->GetObj().GetHandleById(objId);

			if (handle) {
				auto obj = objSystem->GetObject(handle);
				auto flags = obj->GetFlags();

				logger->debug("ReadMapMobiles: \t\t{} ({}) is destroyed.", handle, obj->id.ToString());
				gameSystems->GetObj().Remove(handle);
			}
		}

		logger->info("ReadMapMobiles: Done loading destroyed map mobiles");
	} else {
		logger->info("ReadMapMobiles: Skipping destroyed mobile files, because {} is missing", desFilename);
	}

	ReadDynamicMobiles(saveDir);

}

void MapSystem::ReadDynamicMobiles(const std::string & saveDir)
{
	auto filename = fmt::format("{}\\mobile.mdy", saveDir);

	if (!vfs->FileExists(filename)) {
		logger->info("ReadDynamicMobiles: Skipping dynamic mobiles because {} doesn't exist.", filename);
		return;
	}

	logger->info("ReadDynamicMobiles: Loading dynamic mobiles from {}", filename);

	auto fh = tio_fopen(filename.c_str(), "rb");
	if (!fh) {
		throw TempleException("Unable to open dynamic mobile file for reading: {}", filename);
	}

	while (true) {
		try {
			auto handle = objSystem->LoadFromFile(fh);
			logger->trace("ReadDynamicMobiles: \t\tLoaded dynamic object {} ({})", handle ,	objSystem->GetObject(handle)->id.ToString());
		} catch (TempleException &e) {
			logger->error("Unable to load object: {}", e.what());
			break;
		}
	}

	if (!tio_feof(fh)) {
		tio_fclose(fh);
		throw TempleException("Error while reading dynamic mobile file {}", filename);
	}

	tio_fclose(fh);

	logger->info("ReadDynamicMobiles: Done reading dynamic mobiles.");

}

void MapSystem::MapLoadPostprocess()
{

	gameSystems->GetD20().ResetRadialMenus();

	objSystem->ForEachObj([&](objHndl handle, GameObjectBody& obj) {

		auto flags = obj.GetFlags();
		auto isDestroyed = flags & (OF_DESTROYED | OF_OFF);
		logger->trace("MapLoadPostProcess: obj {} GUID: {} destroyed/off? {}", handle, obj.id.ToString(), isDestroyed);
		if (objects.IsEquipment(handle)) {

			auto parentId = obj.GetObjectId(obj_f_item_parent);
			auto parentHndl = objSystem->GetHandleById(parentId);
			logger->trace("  Parent GUID: {} handle: {}", parentId.ToString(), parentHndl);
			if ( (flags & OF_INVENTORY) && !parentHndl ) {
				logger->trace("    Unparented inventory item?? {}", handle);
			}
			else if (parentHndl && (flags & OF_INVENTORY) == 0) {
				logger->trace("    Parented item not flagged as inventory?? {}", handle);
			}

		}
		if (!obj.IsStatic()) {
			obj.UnfreezeIds();
		}

		// Temple+: added safeguard against nulled inventory items
		if (obj.IsCritter()) {
			obj.PruneNullInventoryItems();
		}

		if (obj.HasFlag(OF_TELEPORTED)) {
			TimeEvent e;
			e.system = TimeEventType::Teleported;
			e.params[0].handle = handle;
			gameSystems->GetTimeEvent().ScheduleNow(e);
			obj.SetFlag(OF_TELEPORTED, false);
			return;
		}

		// Initialize everything's D20 state
		if (gameSystems->GetParty().IsInParty(handle)) {
			return; // The party is initialized elsewhere (in the Party system)
		}

		// This logic is a bit odd really. Apparently obj_f_dispatcher will not be -1 for non-critters anyway?
		if (obj.IsNPC() || obj.GetInt32(obj_f_dispatcher) == -1) {
			d20Sys.d20Status->D20StatusInit(handle);
		}

	});

}

void MapSystem::SaveMapMobiles() {

	// This file will contain the differences from the mobile object stored in the sector's data files
	auto diffFilename = fmt::format("{}\\mobile.md", mSectorSaveDir);
	auto diffOut = std::make_unique<VfsOutputStream>(diffFilename);

	// This file will contain the dynamic objects that have been created on this map
	auto dynFilename = fmt::format("{}\\mobile.mdy", mSectorSaveDir);
	auto dynOut = std::make_unique<VfsOutputStream>(dynFilename);

	// This file will contain the object ids of mobile sector objects that have been destroyed
	auto destrFilename = fmt::format("{}\\mobile.des", mSectorSaveDir);
	auto destrFh = vfs->Open(destrFilename.c_str(), "ab");
	if (!destrFh) {
		throw TempleException("Unable to open {} for writing.", destrFilename);
	}

	auto prevDestroyedObjs = vfs->Length(destrFh) / sizeof(ObjectId);
	auto destroyedObjs = 0;
	auto dynamicObjs = 0;
	auto diffObjs = 0;

	objSystem->ForEachObj([&](objHndl handle, GameObjectBody& obj) {

		if (obj.IsStatic()) {
			return; // Diffs for static objects are stored in the sector files
		}

		// Dynamic objects are stored in their entirety in mobile.mdy
		if (obj.HasFlag(OF_DYNAMIC)) {
			// If a dynamic object has been destroyed, it wont be recreated on mapload
			// anyway (since there is no mob file for it)
			if (obj.HasFlag(OF_DESTROYED) || obj.HasFlag(OF_EXTINCT)) {
				//logger->debug("Skipping dynamic object {} for writing destroyed objs ({})", description.getDisplayName(handle),		objSystem->GetObject(handle)->id.ToString());
				return;
			}
			// TODO: Replace with proper VFS usage
			obj.Write(*dynOut);
			++dynamicObjs;
			return;
		}

		if (!obj.hasDifs) {
			//logger->debug("Skipping object with diffs {} for writing destroyed objs ({})", description.getDisplayName(handle),	objSystem->GetObject(handle)->id.ToString());
			return; // Object is unchanged
		}

		if (obj.HasFlag(OF_DESTROYED) || obj.HasFlag(OF_EXTINCT)) {
			if (obj.HasFlag(OF_EXTINCT))
			{
				logger->trace("SaveMapMobiles: \tWriting extinct object {} ({}) to mobile.des file.", handle,	objSystem->GetObject(handle)->id.ToString());
			} else
			{
				logger->trace("SaveMapMobiles: \tWriting destroyed object {} ({}) to mobile.des file.", handle,	objSystem->GetObject(handle)->id.ToString());
			}
			// Write the object id of the destroyed obj to mobile.des
			vfs->Write(&obj.id, sizeof(obj.id), destrFh);
			++destroyedObjs;
		} else {
			//logger->debug("Writing object {} to diff file ({})", description.getDisplayName(handle),	objSystem->GetObject(handle)->id.ToString());
			// Write the object id followed by a diff record to mobile.md
			diffOut->WriteObjectId(obj.id);

			// TODO: Replace with proper VFS usage
			obj.WriteDiffsToStream(*diffOut);
			++diffObjs;
		}

	});

	diffOut.reset();
	vfs->Close(destrFh);
	dynOut.reset();

	logger->info("Saved {} dynamic, {} destroyed ({} previously), and {} mobiles with differences in {}",
		dynamicObjs, destroyedObjs, prevDestroyedObjs, diffObjs, mSectorSaveDir);

	if (!destroyedObjs && !prevDestroyedObjs) {
		tio_remove(destrFilename.c_str());
	}
	if (!dynamicObjs) {
		tio_remove(dynFilename.c_str());
	}

}

void MapSystem::SaveSectors(int flags)
{
	auto saveStatics = temple::GetRef<int(__cdecl)(char )>(0x10082C00);
	saveStatics(flags);

}

MapFleeInfo::MapFleeInfo()
{
	isFleeing = false;
	mapId = 0;
	location.location.locx = 0;
	location.location.locy = 0;
	location.off_x = 0;
	location.off_y = 0;
	enterLocation.locx = 0;
	enterLocation.locy = 0;
}


As the trees are clustered together in this scene, begin by marking on some roughly vertical lines to indicate where some of the trees will stand. Use the B pencil for this. As a guide, and to keep the composition centred on the paper, also lightly mark on the area that leads into the grass – such an 'arrow' will point to the main part of the drawing and subconsciously attract viewers' eyes.
This is the same scene but rendered in coloured pencils. Use loose, tight, long and short marks, plus scribbled- type marks for the leaves and straighter marks for the grass. Also build up contrasting marks on the tree trunks. Intense greens are created through layers, always bearing in mind where the darkest shadows are. Some of the darkest areas of green were made with dark blue and brown layered over green. No black was used, as this can flatten the look of a picture. Because coloured pencils can sometimes be a bit too vivid and unnatural for a landscape, I also added some layers of light grey in places. Be patient; this kind of coloured pencil drawing takes time.


text:
Q: Atualizar a página(View) automaticamente no Asp.Net MVC Galera, na minha aplicação que gerencia Cursos, eu tenho uma tela de inscrição, onde o aluno clica no botão "inscrição" e se inscreve no curso, e o campo quantidade de vagas é decrementado. O problema é que minha View não atualiza após o aluno clicar no botão "inscrição". Tenho que apertar o botão de atualizar do browser para que a View seja atualizada. Como eu faço para que essa View seja atualizada automaticamente após o clique no botão "inscrição"?.
Minha View
@model IEnumerable<MeuProjeto.Models.Curso>

Catálago de Cursos

@Html.ValidationSummary(true)
@TempData["MensagemErro"]

            Curso

            Sigla

            Ementa

            Inicio

            Fim

            Turno

            Status

            Quantidade de Vagas

    @foreach (var item in Model)
    {

                @Html.DisplayFor(modelItem => item.Nome_Curso)

                @Html.DisplayFor(modelItem => item.Sigla)

                Ementa

                @Html.DisplayFor(modelItem => item.Dt_Inicio)

                @Html.DisplayFor(modelItem => item.Dt_Fim)

                @Html.DisplayFor(modelItem => item.Turno)

                <input type="text" name="Status" id="Status" value="@Html.DisplayFor(modelItem => item.Status)" readonly class="Status" />

                @Html.DisplayFor(modelItem => item.Qtd_Vagas)

                        @if (item.Qtd_Vagas > 0)
                        {
                       <input type="submit" value="Inscrição" name="detalhes" class="inscricao btn btn-success" data_toggle="modal" data_target="#modalAviso" data-inscricaoid="@item.Id"/>
                        }
                        else
                        {
                            <input type="submit" value="Não há vagas" name="detalhes" class="inscricao btn btn-default" disabled="disabled"/>
                        }

    }

    <input type="button" value="Voltar" class="btn btn-danger" />

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")

}

A: olá,
Você pode retornar um Json informando a quantidade de vagas para a sua View. Segue exemplo :
http://www.guj.com.br/27920-como-obter-objeto-json-de-retorno-da-requisicao-ajax-jquery-aspnet-mvc-
HTML :
@model IEnumerable<MeuProjeto.Models.Curso>

Catálago de Cursos

@Html.ValidationSummary(true)
@TempData["MensagemErro"]

            Curso

            Sigla

            Ementa

            Inicio

            Fim

            Turno

            Status

            Quantidade de Vagas

    @foreach (var item in Model)
    {

                @Html.DisplayFor(modelItem => item.Nome_Curso)

                @Html.DisplayFor(modelItem => item.Sigla)

                Ementa

                @Html.DisplayFor(modelItem => item.Dt_Inicio)

                @Html.DisplayFor(modelItem => item.Dt_Fim)

                @Html.DisplayFor(modelItem => item.Turno)

                <input type="text" name="Status" id="Status" value="@Html.DisplayFor(modelItem => item.Status)" readonly class="Status" />

                @Html.DisplayFor(modelItem => item.Qtd_Vagas, new { id = "qtdVagas" + "@item.Id" })

                        @if (item.Qtd_Vagas > 0)
                        {
                       <input type="submit" value="Inscrição" name="detalhes" class="inscricao btn btn-success" data_toggle="modal" data_target="#modalAviso" data-inscricaoid="@item.Id"/>
                        }
                        else
                        {
                            <input type="submit" value="Não há vagas" name="detalhes" class="inscricao btn btn-default" disabled="disabled"/>
                        }

    }

    <input type="button" value="Voltar" class="btn btn-danger" />

JavaScript:

Action MVC:
public ActionResult Inscricao(int id)
    {
        int quantidadeVagas;
        //Seu código
        ....

        //Pega a quantidade de vagas
        quantidadeVagas = new SeuRepositorio().GetQuantidadeVagas(id)

        //Retorno
        return Json(quantidadeVagas);
    }

A: Uma solução é recarregar a página com JavaScript
document.location.reload(true);

redpajama_set_name: RedPajamaStackExchange


Grilled Sirloin Mini Sandwiches with cheese and BBQ bourbon sauce will be the star of any party or a fun and easy dinner to make any night of the week.
Grill onion over medium heat until tender.
In a small bowl stir bourbon into barbecue sauce.
Season sirloin steaks with salt and pepper to taste; grill over medium-high heat to desired doneness. Allow steaks to rest 3-5 minutes before slicing thinly against the grain.
Create sandwiches by topping buns with cheese, steak, onion and barbeque sauce.


Justin Rose is making a curious equipment change in 2019, switching his irons from TaylorMade to Honma. That could derail one of the strengths of his game.
Francesco Molinari rode a hot putter to a breakthrough 2018 season, but his success might not be super sustainable.
An inevitable slide from golfers we don't see coming.
Of the players inside the top 10 of the world rankings at the end of 2017, four didn't win in 2018.
Jordan Spieth was coming into this year as the defending Open Champion, but the hot putter he's known for faded away and he fell outside of the top 15 in the world.
The buzz around Rickie Fowler was whether or not he'd finally break his major drought. Not only did he not win a major, he didn't win anywhere.
Then we had the injury bug hit Hideki Matsuyama and Henrik Stenson, who like Spieth and Fowler, were top-10 players that never found a victory during the season.
It's going to happen again in 2019, the only question is to who.
I've got a couple guesses.
There's a really good chance that when the calendar flips to 2019, Rose will be the world No. 1. He's playing one last tournament this week in Indonesia, where he's the defending champ. If he finishes inside the top 10, he'll pass Brooks Koepka for the top spot. His top 10 odds are -800 this week.
So why would I suggest a guy who might be the best player in the world is heading for a drop?
We've all heard the saying "if it ain't broke, don't fix it," but Rose isn't following that advice.
He's changing his irons from TaylorMade to Honma. The Japanese club-maker is relatively unknown outside of Japan and China. Hideto Tanihara is currently the highest-ranked player listed as part of Team Honma at No. 169 in the world.
Ball-striking isn't a problem for Rose. He's one of the best iron players in the game, so it's hard to see this change improving what he's already got.
But when a player goes off the radar with an equipment change, it can cause trouble. Look no further than when Bubba Watson changed his golf ball to Volvik.
Rose's struggles are on the green. If his ball striking was identical to this year and the putter could heat up just a little, he'd win a major.
Instead, we enter the year with an extra unknown in Rose's game.
So until I see him string together good results with the new clubs, he'll be off my board next season.
No one turned their career around quite like Molinari in 2018. The 36-year old had never won more than once in a year and had only four career victories worldwide.
He went on to win the Euro Tour's crown jewel at Wentworth, claim his first PGA Tour title and became a major champion. He then capped it off by destroying the U.S. in the Ryder Cup.
Molinari always had the approach game to compete. But in 2018, he added some distance with the driver, and more importantly, he caught fire with the putter.
And it's that putter that will be the reason I think I slides back down in 2019. Following that breakthrough at the Open Championship, Molinari returned to his normal self with the flat stick, losing strokes in four of the next five tournaments, and dropping around two shots per event on the greens.
If that putter has settled back into the same range it's been throughout his career, we can probably expect Molinari's game to return to where it was prior to 2018.


text:
Home Appeals & Appellate Arizona
Arizona Appeals & Appellate Lawyers
Find Arizona Appeals & Appellate Attorneys by City
Find Arizona Appeals & Appellate Attorneys by County
Mohave County
Apache County
Cochise County
Coconino County
Greenlee County
Navajo County
Phoenix, AZ Appeals & Appellate Attorney with 28 years experience
(602) 253-1010	3101 N Central Avenue
Free ConsultationAppeals, Criminal Defense, DUI & DWI and Domestic Violence
Golden Gate University School of Law
Former Prosecutor familiar with all courts in the Phoenix Metropolitan area.
Board Certified Criminal Law Specialist and highest rated by Martindale-Hubbell.
Born and raised in Phoenix Arizona.
Donald J Ramsell
Tucson, AZ Appeals & Appellate Lawyer with 34 years experience
(800) 342-5384	945 N. Stone Ave
Free ConsultationAppeals, Criminal Defense, DUI & DWI and Traffic Tickets
Donald J. Ramsell, author of several DUI articles and books, is the one of the few DUI defense attorneys to appear and argue on a DUI case before the United States Supreme Court in its entire 213 years of existence. In his 33 year career his firms have defended 15,000 clients.
Jared E. Holland
Flagstaff, AZ Appeals & Appellate Attorney with 11 years experience
(928) 225-2896	15 East Cherry Avenue, Suite 203
Appeals, Business, Estate Planning and Probate
Jared E. Holland is the senior partner in Holland Law Group, PLLC's Flagstaff office. A southern Arizona native, Jared moved to northern Arizona in 2007 after graduating from law school at Arizona State University. Jared is very accomplished in and has extensive experience in many areas of law, including civil litigation, probate administration and litigation, estate planning, civil, business and contract law, bankruptcy, debtor representation, and family law. Jared is also a licensed fiduciary, certified by the Arizona Supreme Court and a trained mediator. Jared is one of a handful of attorneys appointed by the Coconino County Superior Court to...
Omer Ramiro Gurion
Tempe, AZ Appeals & Appellate Lawyer with 8 years experience
(480) 456-6400	404 E Southern Ave
Free ConsultationAppeals, Criminal Defense, DUI & DWI and Juvenile
Sandra Day O'Connor College of Law, Arizona State University
Omer Gurion is a Criminal Law Specialist certified by the State Bar of Arizona, Board of Legal Specialization. Omer is a highly skilled defense attorney who brings his talent, his work ethic and his passion to each case he handles. When Omer takes on a case, his clients reap the benefit of his considerable knowledge, experience and intense personal investment in obtaining the best possible outcome in their case. He graduated from Arizona State University with a B.S. in Political Science magna cum laude and continued to law school at the Sandra Day O'Connor College of Law. After law school...
Bret Royle
Phoenix, AZ Appeals & Appellate Lawyer with 8 years experience
(602) 899-8000	2828 North Central Ave. Suite 1203
Arizona's Super Lawyers Rising Star, the National Trial Lawyers: Top 100, and one of Arizona's Outstanding Lawyers of 2014 are only several of the distinguished accolades Bret Royle has been awarded as a Phoenix based criminal defense and DUI attorney.
However, before he began practing law, Bret attended the University of Miami School of Law. After graduation from law school, Bret joined an Arizona criminal defense firm, where he gained valuable experience in numerous areas of the law. He proceeded to open his own firm in 2011, where he focused on DUI and criminal defense, gaining a reputation as one...
Marc J. Victor
Chandler, AZ Appeals & Appellate Attorney with 24 years experience
(480) 755-7110	3185 S. Price Rd.
Appeals, Criminal Defense, DUI & DWI and White Collar Crime
I realize there are many law firms competing for the privilege of representing you in your legal matter. I realize you have a difficult decision in determining which law firm would best serve your interests. I have worked hard for the past 21 years to distinguish my law firm from the others, and to build my reputation as an aggressive advocate for my clients offering honest and well reasoned legal advice. As you browse our website, you will quickly get a feel for what we call our "Commitment to Excellence." Marc J. Victor has represented clients in more than...
Michelle "Shelley" Behan
Tucson, AZ Appeals & Appellate Attorney with 5 years experience
(520) 220-5047	945 North Stone Avenue
Free ConsultationAppeals, Criminal Defense, DUI & DWI and White Collar Crime
James E. Rogers College of Law, University of Arizona
Michelle is a 2013 graduate of the University of Arizona James E. Rogers College of Law. While in law school, Michelle was an active participant in the Veteran's Advocacy Clinic, where she acted as defense counsel for indigent Veterans charged with criminal misdemeanors. Michelle's zealous advocacy on behalf of Veterans led to her selection by the Law School Faculty to present an amicus curiae brief (on behalf of the defendant) to the Federal Court of Appeals for the U.S. Armed Forces, the highest military court in the country.
Following graduation, Michelle worked as an Assistant Public Defender at the Tucson City...
Adam Studnicki
Phoenix, AZ Appeals & Appellate Lawyer with 27 years experience
(602) 845-9090	3217 E. Shea Blvd.
Free ConsultationAppeals, Business, Nursing Home and Personal Injury
Harvard Law School and Arizona State University
Adam Studnicki has over 20 years of experience in business and law. He has recovered over $100 million for clients in personal injury cases. A graduate of Harvard Law School with Honors, Adam combines legal skills and knowledge, along with real-world experience and applied wisdom, to help find creative, pragmatic and successful solutions to problems.
Donald W Hudspeth
(602) 265-7997	3200 N. Central Ave.
Appeals, Arbitration & Mediation, Business and Communications
Harvard Law School and Sandra Day O'Connor College of Law, Arizona State University
Legal Background Donald W. Hudspeth was admitted to practice law in the State of Arizona in 1988. He practiced corporate law representing financial institutions such as the Bank of Hawaii and founded The Law Offices of Donald W. Hudspeth, P.C. in January 1993. Mr. Hudspeth served as a Professor of Business Law at Arizona State University, West Campus and has conducted classes and seminars for Phoenix College, Maricopa Community Colleges Small Business Development Center, and the Arizona Small Business Association. Former Business Background Donald Hudspeth held a stock brokers license by the age of twenty-one and became a business owner by the time...
Joseph Holland
Snowflake, AZ Appeals & Appellate Attorney with 12 years experience
(928) 536-3001	970 S Main St Ste A
Snowflake, AZ 85937
Appeals, Business, Collections and Real Estate
Joseph Holland comes from a family of Arizona attorneys. After graduating from prestigious Arizona State University College of Law, he opened his practice in Mesa, AZ. In 2008, he relocated his practice to the White Mountains of Arizona, where he has settled and begun to grow his family. Currently, his practice focuses primarily on real estate law, contract disputes, and collections.
Christopher J. Stender Esq.
(602) 254-5353	141 E. Palm Lane
Appeals and Immigration
Ann M Haralambie
(520) 327-6287	1785 East Skyline Drive
Appeals, Divorce, Family and Juvenile
Ann M. Haralambie is a Certified Family Law Specialist, Arizona Board of Legal Specialization, a Certified Child Welfare Law Specialist, National Association of Counsel for Children, and a Martindale-Hubbell AV® Preeminent™ rated attorney in private practice in Tucson, Arizona since 1977. Her practice is restricted to family and (non-delinquency) juvenile cases, with an emphasis on custody and child abuse. The practice is child-focused and seeks to minimize the harm which often occurs when child custody is litigated.
Ms. Haralambie is also an author and a national and international speaker and trainer for professional groups on topics related to custody, child abuse,...
John Trebon
(928) 779-1713	308 N Agassiz St
Free ConsultationAppeals, Civil Rights, Criminal Defense and Native American
John Trebon has been practicing law in Flagstaff for more than thirty years. The focus of his practice is on personal injury, wrongful death, criminal defense and civil rights.
Our mission is to provide high quality, vigorous representation, but at an affordable, competitive price. Our motto has always been that "we will stand up for your rights." We will not only stand up for your rights in court; but also by treating you fairly with reasonable fees.
John Trebon is a certified specialist in criminal law by the State Bar of Arizona. He began his career as a state public defender in...
John Douglas Wilenchik
(602) 606-2810	2810 N. Third St.
Appeals, Arbitration & Mediation, Business and Civil Rights
John "Jack" Wilenchik is a litigation and trial lawyer who handles cases in areas including business and employment law, civil rights, law enforcement, victim's rights matters, and tax audits/deficiencies. He has been named to the national Superlawyers list of "Top Rated Attorneys" by Thomson Reuters. In 2018, he obtained the largest jury verdict in Mohave County history (at $8 million, including $1.77 million in punitive damages), which was also the largest punitive damages verdict for the state of Arizona in 2018 (and second largest verdict overall). In 2017, the White House contacted him directly with the news that President Donald...
Robert Campos
(602) 222-3440	51 E. Lexington Ave.
Appeals, Criminal Defense, DUI & DWI and Juvenile
The law firm of Robert J. Campos & Associates, P.L.C., has been proudly serving the citizens of Arizona since 1996. The Firm specializes in the defense of clients who are accused of, or charged with, a criminal offense.
Robert J. Campos founded the Firm in 1996, after serving 7 years as a Maricopa County Prosecutor and 2 years as a La Paz County prosecutor. As a former prosecutor, Mr. Campos gained extensive training and experience handling very serious cases.
In addition to his extensive experience as a former prosecutor, Mr. Campos also has several years of experience as a city court...
Daryl Manhart
(602) 234-9908	(the map shown on this page is not accurate)
702 E. Osborn Rd., Suite 200
Appeals, Business and Real Estate
Daryl Manhart is a partner (shareholder) with Burch & Cracchiolo, P.A.. His principal areas of practice are in commercial transactions and real estate, commercial litigation, and civil appeals throughout Arizona. He is the firm's principal appellate attorney. Mr. Manhart also was an adjunct professor of business law, teaching undergraduate and graduate level courses, at Grand Canyon University from 1995 - 2004, in Phoenix, Arizona. Mr. Manhart's professional engagements and experience include: * representation of buyers and sellers of both unimproved and developed Arizona real estate * representation of buyers and sellers of businesses, or with respect to sales of...
Mick Levin
(866) 707-7222	3401 North 32nd Street
Free ConsultationAppeals, Asbestos, Construction and Insurance Claims
Jonathan Collins
(602) 788-7227	3241 East Shea Blvd Suite 1
Free ConsultationAppeals, Arbitration & Mediation, Business and Family
University of Tulsa College of Law and Sandra Day O'Connor College of Law, Arizona State University
Focused on Family, Probate, Bankruptcy & Debt, Business Law. Jonathan S. Collins received his undergraduate degree from Arizona State University and then his law degree from the University of Tulsa College of Law. He was a visiting student at the Arizona State University Sandra Day O'Connor College of Law for his last semester of law school. Jonathan has been a member, in good standing, of the State Bar of Arizona since 2003. He is also licensed to practice law before all Federal Courts of the State of Arizona. Jon has been a law clerk for a...
Andrew Becke
Prescott, AZ Appeals & Appellate Attorney with 11 years experience
(928) 445-6860	325 W. Gurley Street
Prescott, AZ 86301
Free ConsultationAppeals, Insurance Claims, Insurance Defense and Personal Injury
Andrew J. Becke practices primarily in the areas of personal injury and wrongful death, appellate practice, and professional licensure and discipline. Mr. Becke received his Bachelor of Science degree from Northern Arizona University in 1999 and graduated cum laude from Arizona State University College of Law in 2007 with pro bono distinction. Mr. Becke is a member of the State Bar of Arizona. - Law Clerk to Vice Chief Justice Rebecca White Berch, Arizona Supreme Court, 2007-2008 term. - Executive Managing Editor, Arizona State Law Journal (2006-2007). - Author, Two Steps Forward, One Step Back: Arizona's Notice of Claim...
Virginia L Crews
Kingman, AZ Appeals & Appellate Attorney with 14 years experience
(866) 490-4267	2249 Hualapai Mt. Rd. #2
Kingman, AZ 86401
Criminal attorney Virginia L. Crews has practiced law for well over a decade. An experienced trial attorney and Justice of the Peace pro tem, Ms. Crews has participated in an extensive variety of criminal trials, civil trials and appellate oral arguments. Additionally, given her educational background in Biology, has utilized her scientific training in a few very complex cases. She brings experience, efficiency and expertise as a Partner to the Law Offices of Shawn B. Hamp and their clients.
Douglas Wayne Taylor
Tucson, AZ Appeals & Appellate Attorney with 16 years experience
EXCELLENT Criminal Defense Trial Attorney, handling all criminal matters, including DUI, Domestic Violence (DV), Assault, Disorderly Conduct, and everything else.
Stephen Weeks
Tucson, AZ Appeals & Appellate Attorney
(520) 318-1209	2223 E Speedway Blvd
Appeals, Business and Civil Rights
Randi Sirlin
(602) 561-8107	1747 E. MORTEN AVE., STE. 110
Appeals, Divorce, Estate Planning and Family
Tucson Association of Collaborative Divorce Law (international association), University of Phoenix and James E. Rogers College of Law, University of Arizona
Ms. Sirlin has practiced family law for almost 25 years. She has a unique perspective in that she experienced a very contentious "custody battle" as a child, which spanned many years. Ms. Sirlin primary practices family law litigation and appellate law (appeals). She is also trained as a Family Law Mediator and received her Certification of Training in Collaborative Divorce.
While almost all of the current practice is in family law, Ms. Sirlin has substantial experience in criminal defense law and also represents clients in the area of appeals for criminal law defense.
While always striving to...
Robert Andrew Mandel Esq
(602) 374-4591	2390 East Camelback Rd
Rob devotes his practice to litigating high-stakes business and other civil cases in Arizona and throughout the nation at the appellate and trial court levels. He is also called upon frequently to consult trial counsel regarding legal theories, strategy, dispositive motions, and preservation of error at the trial court level. Rob's clients include some of the largest and most sophisticated companies headquartered in Arizona and some of Arizona's most successful business owners. His advocacy skills and commitment to client service have earned Rob the admiration of peers and clients alike. He has been named to the esteemed Southwest Super Lawyers...
Aaron M. Kelly
(480) 331-9397	404 S. Mill Ave. Suite C201
Appeals, Asbestos, Business and Collections
View Blog View Lawyer Profile
Aaron M. Kelly is an AV Rated attorney at Kelly / Warner, PLLC. His practice areas include internet law, internet marketing law, defamation law, and corporate governance. Aaron is an entrepreneur as well, and owns 4 other businesses in addition to his law practice. He has been featured on techcrunch.com, the wall street journal, mashable, PC World, the washington post, and CBS and ABC affiliates in Arizona.
Surprise, AZ Appeals & Appellate Lawyer with 28 years experience
(623) 505-1609	Surprise, AZ 85374
Appeals, Arbitration & Mediation, Business and Estate Planning
Mr. Gregory is licensed to practice in Arizona and Nevada. He has more than 27 years of experience helping families and businesses avoid legal troubles. With proper planning this is possible. For families, it is necessary to put in place a detailed estate and asset protection plan. On the business side, from choice of entity to hiring employees, to tax matters, to contracts with business partners and clients, intellectual property, licensing, franchising, business succession planning, Mr. Gregory has the expertise to take a business from its infancy to an IPO and beyond. Should litigation be...
Ulises Ferragut Jr
Phoenix, AZ Appeals & Appellate Lawyer
(602) 324-5300	2 N Central Ave Suite 400
Mr Frankie Adamo Esq
(602) 644-1403	7330 N. 16th St., Suite A310
Appeals, Business, Foreclosure Defense and Real Estate
Mr. Adamo focuses on civil litigation issues, judgment collections, and appellate work. He has nine years of experience winning and settling many cases in state and federal court. Mr. Adamo represents both plaintiffs and defendants alike.
Mr. Adamo graduated cum laude from Florida A&M College of Law and earned a Bachelor of Science degree in Computer Science at the University of Central Florida. Mr. Adamo also attended Rutgers State University as part of his undergraduate studies.
Mr. Adamo worked for nearly twenty years as a computer analyst, programmer, and software trainer before becoming an attorney. Mr. Adamo approaches all of his cases...
Daniel Beeks
(602) 749-8500	7210 N. 16th Street
Appeals, Divorce and Family
Dan Beeks is an Arizona attorney with over 25 years of experience. He used his extensive knowledge in matters of complicated litigation from everything including medical malpractice to intellectual property before becoming a well known and trusted divorce attorney with Bishop Law Office in Phoenix. Dan often handles high-dollar family law cases, but also enjoys helping with smaller scale divorce cases and appeals.
Joseph Meaney
(602) 631-9100	1938 E Osborn Rd
Appeals, IP and Patents
Joe is a registered patent attorney and a licensed professional engineer in Arizona. Joe represents clients in all aspects of intellectual property, including patent, trademark, trade secret and copyright law. His practice includes patent and trademark prosecution before the United States Patent and Trademark Office, litigation of intellectual property disputes in both federal and state court, and the preparation of various documents, including licensing agreements and assignments. Joe is admitted in both the state and federal courts of Arizona. Prior to attending law school, Joe was a structural engineer for more than four years with a mid-size engineering firm in Massachusetts...
Olivier A. Beabeau
Phoenix, AZ Appeals & Appellate Attorney
(602) 955-1455	2425 East Camelback Road
Appeals, Antitrust, Business and IP
Olivier A. Beabeau is a Phoenix native and received his B.A. from Vanderbilt University in 1998, and J.D. in 2001 from The George Washington University National Law Center. Mr. Beabeau has also studied European Union law at the Tulane Center for European Union Law in Amsterdam, The Netherlands, and history and political theory at Birkbeck College, University of London. While in law school, Mr. Beabeau served as a law clerk to a large multinational law firm in Washington, D.C., where he assisted the antitrust group with issues involving international competition law. Upon graduation, Mr. Beabeau served as a legislative analyst...
Mesa, AZ Appeals & Appellate Attorney with 11 years experience
(480) 442-0489	1640 South Stapley Drive, Suite 127
Roger Williams University
After litigating hundreds of cases ranging from simple misdemeanors to homicides, I can honestly say that I love what I do. My heart lies in criminal defense work. Through hard work, I've earned the respect of the legal community here in Arizona. I've worked with hundreds of attorneys and appeared before numerous judges in city, state, and federal courts. Although most of my criminal practice is in state courts, about 30% is federal criminal defense. I've dedicated my legal career to helping those who struggle most. I first became a prosecutor because I wanted to protect the vulnerable...
Carl R Sammartino
Tucson, AZ Appeals & Appellate Lawyer
(520) 623-5466	340 N. Main Ave.
Free ConsultationAppeals, Municipal and Real Estate
CARL R. SAMMARTINO graduated in 2002 from the University of Michigan with a Bachelor of Arts in Political Science; in 2006 he received his Juris Doctor from the University of Arizona. He joined Stubbs & Schubart, P. C., in 2008.
Elizabeth McFarland
Sedona, AZ Appeals & Appellate Lawyer with 26 years experience
(928) 284-0899	60 Bell Rock Plaza
Sedona, AZ 86351
Appeals, Business, Estate Planning and Real Estate
Ms. McFarland's mission is to serve Sedona and the Verde Valley with experience and integrity. Since 2002, she has provided complete client service in the areas of estate planning, business law, real estate, and appellate practice. Ms. McFarland entered private practice in 1993. From 1993 through 2001, she worked with firms in Washington, D.C., Los Angeles, and Santa Barbara, California, primarily focusing on complex, commercial trial and appellate work in state and federal courts across the nation. Ms. McFarland's practice included disputes about employment contracts, partnership agreements, commercial leases, joint ventures, 10b(5) securities litigation, shareholder derivative actions, compliance with the Investment Company Act of 1940,...
William Cleaveland
MESA, AZ Appeals & Appellate Attorney
(480) 227-8746	1855 East Southern Avenue
Appeals, Administrative, Arbitration & Mediation and Business
Bruce R Heurlin
(520) 319-1200	1636 N Swan Rd
Appeals, Arbitration & Mediation, Business and Construction
Former Federal Prosecutor - Assistant U.S. Attorney and Criminal Division, U.S. Department of Justice (Honors Program). Mr. Heurlin now practices in the areas of complex business and real estate litigation, securities arbitration (broker fraud), federal white-collar criminal defense, and security clearance litigation. Mr. Heurlin has tried approximately 50 jury trials and argued approximately 60 appeals, many in the federal courts.
James Glenn O'Haver
(928) 776-7904	POB 282
Appeals, Animal, Criminal Defense and Divorce
Andrew Lynch
(602) 761-5900	11811 N. Tatum Blvd.
Free ConsultationAppeals, Arbitration & Mediation, Bankruptcy and Business
Andrew Lynch has been helping Arizona businesses resolve debt and other legal issues for nearly a decade. The firm has a unique approach to business debt, helping clients negotiate and settle debt without bankruptcy. We also defend & settle credit card lawsuits and debt buyer lawsuits. Mr. Lynch attended the University of Iowa and graduated from college in only 3 years with a B.A. in Philosophy and Psychology in 2000. He went directly to law school at the University of Iowa College of Law, which is one of the nationâs top ranked law schools by U.S. News &...
Maxwell Mahoney
Free ConsultationAppeals, Divorce and Family
Arizona Summit Law School
Max Mahoney practices mainly Family Law with Keith Berkshire, but also has experience in the areas of business, construction, real estate, and collections law. Before coming to Berkshire Law Office, Max practiced civil litigation at Rose Law Group and was a litigation file clerk with Cantelme & Brown, PLC and the Goldwater Institute. Max graduated cum laude from Arizona State University earning a Bachelor's Degree in Interdisciplinary Studies focusing in Anthropology, History, and Political Science. Max attended Phoenix School of Law where he received his J.D. in 2011. While at Phoenix School of Law Max was the Administrative Editor for...
Erin H. Walz
Mesa, AZ Appeals & Appellate Lawyer with 22 years experience
(480) 461-5379	1138 North Alma School Road
Appeals, Business, Education and Real Estate
I am a litigation attorney practicing since 1996. I represent schools and school districts in administrative and court proceeding, including student discipline, employee discipline and dismissal and special education hearings. I also represent business entities in a variety of civil litigation, including commercial contracts and partnership and corporate disputes and dissolutions. I am admitted to practice before the Arizona, Illinois and Michigan state courts, and the federal courts. My trial experience includes state and federal courts trials and appellate courts, as well as state supreme courts.
The OneCLE Lawyer Directory contains lawyers who have claimed their profiles and are actively seeking clients. Find more Arizona Appeals & Appellate Lawyers in the Justia Legal Services and Lawyers Directory which includes profiles of more than one million lawyers licensed to practice in the United States, in addition to profiles of legal aid, pro bono and legal service organizations.

redpajama_set_name: RedPajamaCommonCrawl


Drivers on Arriva Trains Wales are to strike on 27 December in a row over pay and conditions.
The industrial action means that no Valleys Lines services will run on this date.
Bob Crow, general secretary of The Rail Maritime and Transport (RMT) union, said 85% of members backed the industrial action and described Arriva Trains Wales pay offer as "paltry".
Arriva Trains Wales has apologised for the inconvenience to passengers.
The train operator said they planned to run a full service on all other routes on the network next Monday.
Arriva Trains Wales said drivers had been offered a pay increase of 12% taking their basic pay from £34,824 to £39,117 in two stages.
The offer also changed the obligation to work on Sundays from a voluntary to contractual basis, said the train operator.
A spokesperson said: "Arriva Trains Wales is disappointed that RMT and its driver members have chosen to announce industrial action for Monday 27 December despite the significant pay offer which has been made.
The spokesperson added that ATW train drivers were represented by two trade unions, ASLEF & RMT, and that ASLEF had recommended the latest pay offer to its members for acceptance.
RMT general secretary Bob Crow said the offer would have left union members "out of pocket".
"Our members would have ended up with inferior working conditions for a de facto pay cut.
"That is why we have balloted for industrial action and why our members have delivered this overwhelming mandate," Mr Crow added.
Last month a planned stoppage at Arriva Trains Wales by members of the drivers' union Aslef was called off.


text:
Aluko and the FA: Five things we learned from parliamentary hearing
I didn't expect it to be Eni Aluko versus the FA
After four hours of questioning, Eniola Aluko and the Football Association were released from a parliamentary inquiry on Wednesday - one vindicated, the other implicated.
Minutes before the hearing started, the FA issued a public apology to Chelsea striker Aluko and team-mate Drew Spence in light of new evidence surrounding race remarks made by ex-England boss Mark Sampson.
Here's what happened over the following 240 minutes as Aluko, team-mate Lianne Sanderson and senior figures at the FA faced the Digital, Culture, Media and Sport committee:
As it happened: FA bosses and Aluko appear in front of MPs
FA apologises for Sampson race remarks
1. Black actors and fake accent claims
In a 17-page report sent to the committee, Aluko said she understood a black actor was hired to act out bad behaviour for a role-playing exercise to teach players about "Lioness standards".
"I know that some England players were uncomfortable with this demonstration, because they believed the actress was perpetrating a negative and unfair perception of me," said Aluko.
The Nigeria-born striker also accused England goalkeeping coach Lee Kendall, appointed in 2014, of speaking to her in a fake Caribbean accent.
"I don't think he meant it maliciously," she said. "He may not have been aware of how annoying it got.
"I'm not Caribbean, I'm of African descent. I thought it was another example of an ignorant mindset and behaviour towards me."
2. 'Bordering on blackmail'
FA chief's request 'bordering on blackmail' - Aluko addresses inquiry
Aluko has 102 England caps but has not added to these since making her allegations as part of an FA inquiry into its management culture in 2016.
The 30-year-old reached a settlement of about £80,000 with the FA following her grievance claim - which the FA said was to avoid the threat of an employment tribunal disrupting the squad's preparations for Euro 2017.
She confirmed this payment was set to be paid in two instalments, but the second one had been withheld by the FA.
"Martin Glenn [FA chief executive] said if I wrote a statement saying the FA were not institutionally racist he would release the second tranche of the money. I felt that was bordering on blackmail."
Glenn then denied asking Aluko to do this. And when pushed over whether the FA would be making the final payment, he added: "We'll reflect on it."
3. Clarke's PFA criticism and 'fluff'
Greg Clarke became FA chairman in August 2016
FA chairman Greg Clarke criticised the Professional Footballers' Association, saying he had a "fundamental problem" with the governance at the top.
He said: "The PFA spends millions of pounds a year on the CEO's salary and pension and they are walking away from alcoholics, from addicted gamblers. I will never look up to their governance, though I respect their people."
The PFA denied that claim, with chief executive Gordon Taylor calling it "classic diversionary tactics".
"It's false and untrue," he said. "We've never turned anybody away, whether with problems of abuse, gambling or addiction. Why on earth couldn't he raise it with me?"
Later in the inquiry, Clarke was then criticised for referring to institutional racism as "fluff". He quickly apologised after being chastised by the committee.
"The fact you describe it as fluff speaks volumes," said Julie Elliott MP. "Language matters."
4. The apology
Glenn issued the initial apology to Aluko and Spence on behalf of the FA at the beginning of his statement in front of MPs.
When pressed on whether the FA had failed in their duty of care, he said: "There have clearly been failings. I wouldn't want to then say the organisation is failing. Clearly in this situation there have been failings.
"We have 900 members of staff. On balance I think we do a good job."
But Clarke was more resolute, adding: "That is a fundamental breach of our duty of care for that person and I feel very bad about that."
I'm not a racist - Sampson
Glenn said that Sampson, who was paid nine months' salary on his departure, may proceed with a wrongful dismissal claim.
Sampson was sacked in September following evidence of "inappropriate and unacceptable" behaviour with female players in a previous role.
Glenn and human resources director Rachel Brace confirmed the 35-year-old had received a "payment related to his contract" - nine months' salary.
But Glenn added: "We've been informed by his (Mark Sampson's) solicitor that he's considering filing a claim for wrongful dismissal."
5. 'Dragged through the mud' - reaction
As Aluko, Lianne Sanderson and the FA bosses gave their evidence, celebrities reacted on social media.
Match of the Day presenter Gary Lineker said: "Damning and damaging for FA with total vindication of Eniola Aluko."
In a series of tweets, grime artist Stormzy said: "After all you put the girl through it turns out she was telling the truth.
"Dragged her name through the mud and tarnished her and then anyone who came to her defence was demonised as well."
Following the FA apology, Aluko and Spence's Chelsea and England team-mate Claire Rafferty said: "Privilege is invisible to those who have it. #integrity."
The FA
Read more on The FA
From the section Women's Football
Read more on Mark Sampson: FA sorry over race remarks to Eniola Aluko & Drew Spence
Updates as FA bosses and Aluko appear in front of MPs
Read more on FA bosses face a parliamentary inquiry over racism allegations
FA bosses set for parliamentary inquiry
Read more on Eniola Aluko: FA bosses set for inquiry over Mark Sampson investigations
'Sampson should have gone years ago'
Read more on Mark Sampson should have been sacked years ago - FA chairman Greg Clarke
Sampson sacked as England manager
Read more on Mark Sampson sacked as England women's manager
A cliff-top green and 'Calamity Corner' - Royal Portrush pro's hole-by-hole guide
Meeting the Queen and breakfast with the kids - inside England's World Cup journey
Watch the moment England won the World Cup
The mysterious death that haunts boxing
Vote for your favourite Open Championship moment
Blood, sweat & tears - the best Wimbledon final ever?
Hamilton makes history but F1's future talents deliver the racing
Why Griezmann's transfer provides more questions than answers
'A player who was a fan, determined to extract enjoyment out of each day'
'Meme Lord' Lando Norris plays 'caption this'
Football notifications, social media and more
Get latest scores and headlines sent straight to your phone, sign-up to our newsletter and learn where to find us on online.
Today's football gossip
The latest rumours and stories from around the world of football.
Phil McNulty
Analysis and opinion from our chief football writer.
How to get into football - the most popular sport in the world, with clubs and facilities throughout the UK.


text:
Category Archives: Monuments
What's going on with the restoration works in Santiago cathedral
Traditionally, one of the highlights for any pilgrim that manages to finish the Camino is the visit to the Santiago cathedral: hugging the saint's statue, assist the Pilgrim Mass and, perhaps, if one is lucky, watch the botafumeiro fly.
However, pilgrims who walk the Camino during these months should be advised that, due to the restoration works in the cathedral, some of these experiences won't be taking place.
Since last January 28th, the masses and religious services that used to take place at the cathedral have been move to other churches in town. The Pilgrim's Mass, for example, takes place now at the St. Francis Church, close to Plaza de Obradoiro (about 300 m. further). This also means that the botafumeiro will not be working during these months.
Please note that this doesn't mean that the cathedral is closed. It still can be visited, and the pilgrims will still be able to enter it, visit the Saint's crypt and the museum and admire the Pórtico de la Gloria. The only visit that remains closed is the one of the rooftops. The entrance to the cathedral should be done through the Puerta de Platerías, in the southern facade (next to the Fonte dos Cabalos).
Cathedral authorities say that this situation is expected to last about 12 months. For further information:
Information about the masses in the Cathedral of Santiago.
PDF with schedules and locations of the religious services that have been moved.
Information about the visits to the Pórtico de la Gloria.
San Juan de Ortega and the miracle of light
There are few places along the Camino the Santiago more significant for the pilgrim than the monastery of San Juan de Ortega, in the Burgos province. Built by the saint of the same name (known in the English speaking world as Saint John the Hermit), the monument has been associated with the pilgrimage to Santiago since its foundation.
Juan de Velázquez was born in the village of Quintanaortuño in 1080, and from his youth he devoted himself to improve the Camino de Santiago, building bridges and paving roads in collaboration with Saint Dominic de la Calzada. The decision of building the monastery came from his pilgrimage to Jerusalem. According to the legend, when traveling back to Spain he suffered a shipwreck, and San Juan prayed to Saint Nicholas, promising to build a chapel in his honor if he survived. He did, so back home he started the work in the zone of the Montes de Oca, close to his hometown.
The San Juan de Ortega monument is comprised by the original chapel, a romanesque church built along it (where the saint is buried), the monastery and the pilgrims' hospice. From the artistic point of view, the church's main highlight is the capital of the Annunciation, which depicts the story of the angel Gabriel's visit to the Virgin Mary.
The miracle of light.
Speaking of this capital, one of the most interesting phenomenoms associated with the monastery takes place in it, the so-called "miracle of light". Every equinox (March 21 and September 22), at five o'clock (solar time), a ray of sunlight enters the church and illuminates the capital, going from left to right, "telling" the story of the Annunciation.
The monastery of San Martín Pinario
Once the pilgrim has reached Santiago, (s)he has a series of mandatory rituals and places to visit: the cathedral, hugging the statue of the Saint, taking the proverbial picture in the plaza de Obradoiro… But Santiago is a city with lots of hidden treasures that are worthy of discovery, and today we will talk about one of them.
The monastery of San Martín Pinario, located close to the cathedral, is the second biggest one in Spain, and it was founded in the X Century by the benedictine order. The current building is from the XVI century, and is therefore of renaissance /baroque style.
Photo by Raúl A.: https://www.flickr.com/photos/torrelodones/
One of the most spectacular elements is the church's vault, with fake coffers. Another item of interest is the main altarpiece, designed in the XVIII century by Casas y Novoa (the same architect that created the Obradoiro facade of the cathedral), which shows Saint Martin (sharing his cape with a poor) and St. Millán de la Cogolla and Santiago fighting infidels.
Photo by Diego Delso, License CC-BY-SA
An interesting anecdote is that the church's towers rise only to the same height than the facade, due to the intervention of the cathedral chapter, which didn't want towers higher than those of the cathedral itself.
Today the monastery hosts a museum of religious art, with an important collection of jewelery, sculptures and manuscripts.
The botafumeiro
We start the year in this blog talking about one of the elements most closely associated with the Compostela pilgrimage: the botafumeiro, the enormous incense burner (or thurible) in Santiago Cathedral that swings
What does "botafumeiro" mean?
The name "botafumeiro", in galician language, means "smoke expeller", though strictly speaking, a more correct expression would be "bota fume" or "fumeiro".
The botafumeiro was installed in Santiago cathedral for the first time in the XIII or XIV century, in order to alleviate the odor caused by the masses of pilgrims that spent the night at the cathedral after their long voyage. Santiago wasn't the only place with a similar device: in the past, the cathedrals of Orense, Zamora and Tuy also had their own botafumeiros. Nowadays, however, it's only used in the latter in special occasions (and in Santiago, of course).
Through history, the temple has had four botafumeiros: the first one was used until 1530, when a new one was made of silver thanks to a donation from king Louis XI of France. This one, however, was stolen by the french troops during the Peninsular Wars in 1809, and it was replaced by a new one made of iron that was in comission until 1851, when the current one was built by José Losada, the same craftsman who made the urn where the remains of the apostle Santiago are kept.
What is the size of the botafumeiro?
The botafumeiro weights 80 kg. and measures 1.60 m. of height. Every time it's used, it has to be filled with 40 kg. of charcoal and incense.
When the botafumeiro is used, seven "tirabuleiros" pull the ropes and make it swing, thanks to the elaborate system of pulleys in the cathedral. The botafumeiro swings for about 15 minutes, which is the time it takes for the priests to walk around the nave and aisles of the cathedral.
When can I see the botafumeiro?
The botafumeiro is used oficially during the opening and closing of the Holy Year of Jubilee, as well as in the most important religious celebrations. During the year 2016, these will be:
Epiphany, Jan. 6.
Easter (Resurrection Sunday).
Feast of the Ascension.
Apparition of the Apostle Santiago – Clavijo, May 23.
Pentecost.
Santiago Day, Jul. 25.
Assumption of Mary, Aug. 15.
All Saints' Day, Nov. 1.
Feast of Christ the King.
Immaculate Conception, Dec. 8.
Moving of the body of the Apostle Santiago, Dec. 30.
The legend of Eunate and Olcoz
As we mentioned when talking about the church of Santa Maria de Eunate, there is a church in the nearby village of Olcoz with an almost identical portico. This unique fact in romanesque art has caused lots of legends to sprung around these two works; here we will recount one of them.
According to it, the portico in Santa Maria de Eunate was started by a master sculptor from the templar order, who after doing a large part of the work, had to leave for unspecified reasons. The local authorities called another local sculptor to finish the work, which he managed to do in only three days (according to some versions, this second sculptor was a giant, which would explain a lot, as we'll see).
When the original sculptor came back from his voyage, he was angered by what he saw as an usurpation of his work. The local authorities challenged him then an identical portico in the same time that it took his substitute. Faced with such a challenge, the master had to resort to the black arts.
(Pictures courtesy of TaxNavarra).
It was the midsummer night (St. John's day). Following the advice of a witch that lived close to the nearby river Nekeas, the master hid himself close to the river, and waited until a huge snake appeared. The snake left a moonstone that it was carrying on its mouth on the shore. The master waited until the snake had submerged into the river, then picked up the moonstone and ran away to the unworked portico, which was already raised in front of Eunate. He put the moonstone in a cup full of the river's water, put the cup under the portico, and waited…
…And when the moon reached its apogee, the portico's stones started taking by themselves the shapes of the sculptures, columns… that existed in the original portico, only reversed. The only problem happened when the second sculptor found out. Angered by the copy that his competitor had made in just one night, he kicked the portico with such force that it flew away from Eunate to Olcoz, where it is now…
And indeed, the current portico in Olcoz is almost identical to the one in Eunate, except that its features are reversed.
San Millán de la Cogolla, origin of the spanish language
Throught the Camino de Santiago, the pilgrim can find many religious monuments (churches, monasteries…), remains of the centuries of tradition and history originated in this route. Among them, however, the Monastery of San Millán de la Cogolla is special, since it holds the honor of being considered the birthplace of the Castilian language, the most spoken language in modern Spain.
The Monastery of Suso (one of the two existing monasteries in San Millán) was, in effect, the place where the "Glosas Emilianeses", considered the first words written in Castilian (or at least a primitive version of it), were authored. These were notes written on the margin of latin codexes, in order to clarify words or expressions from the main text. Some of these comments were also written in a primitive version of the Basque language, so they can also be considered the oldest existing Basque text.
San Millán de la Cogolla was also the working place of Gonzalo de Berceo, considered the first literary writer in castillian. His main work, "Milagros de Nuestra Señora", was a compilation of miracles attributed to the Virgin Mary written around 1260, and is considered as one of the first literary works written in Castilian.
Due to these reasons, the Monasteries of San Millán were declared World Heritage Sites by UNESCO in 1997.
Santa María de Eunate
Of all the monuments that the pilgrim can find along the Camino de Santiago (St. James Way), one of the most original is probably the Church of Saint Mary of Eunate, one of the greatest achievements of spanish romanesque architecture.
How to arrive.
Eunate is located 2 km. of Muruzábal, in Navarra. If you are following the Camino Francés, once you reach said town you can take a detour towards the church, instead of going straight to Obanos. Once in the church, you don't need to backtrack to Muruzábal to go back to the Camino; you can follow the N6064 until you reach Obanos and Puente La Reina.
Why it is special.
The church of Eunate (one hundred doors, in the Basque language) is a romanesque construction from the 12th century, although there's a lot still unknown about its origins. Since it's outside the town, and archaeological excavations have found scallops (symbols of pilgrimage) in the burials alongside the church, it's been speculated that it was used as a hospice for pilgrims.
The most original feature in this building, aside of the arches that surround it, is its octagonal plan, extremely unusual in romanesque art. This plan, similar to that of other churches founded by the Knights Templar all across Europe, has caused all sort of theories and legends about its association with said military order, despite the lack of evidence.
The legend of Eunate and Olcoz.
Regarding this church and a similar one in the nearby town of Olcoz, there is an interesting legend, which we will recount at a later time…
Cemetery of Navarrete (old pilgrim's hospital)
One of the monuments that can be admired along the Camino is in the town of Navarrete (Logroño). Here we see the facade of the old San Juan de Acre's pilgrim hospital, which was founded in 1185. The hospital was named after the city of Acre, located in the Middle East and used by the Knight Hospitallers of Malta as their base of operations during the Crusades.
In the XIXth century, given its state of disrepair, the old hospital ruins were demolished, and the facade was moved to the town's cemetery, where it can be seen now.