diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 9c978d71..1519faef 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1727,6 +1727,10 @@ Fix viewport loading Database warning handling In memory database support Fix test utils creating characters +Fix server simulation starting prior to database connection +Scaffolding macro character simulation +Fix block generation thread filtering repairable structures +Catch errors in pathfinding threads diff --git a/src/main/java/electrosphere/server/ai/nodes/macro/MacroCharacterGoalNode.java b/src/main/java/electrosphere/server/ai/nodes/macro/MacroCharacterGoalNode.java index e099cf73..1625e203 100644 --- a/src/main/java/electrosphere/server/ai/nodes/macro/MacroCharacterGoalNode.java +++ b/src/main/java/electrosphere/server/ai/nodes/macro/MacroCharacterGoalNode.java @@ -73,14 +73,14 @@ public class MacroCharacterGoalNode implements AITreeNode { case BUILD_STRUCTURE: { Object targetRaw = goal.getTarget(); if(!(targetRaw instanceof Structure)){ - throw new Error("Target is not a structure " + targetRaw); + return AITreeNodeResult.FAILURE; } BeginStructureNode.setStructureTarget(blackboard, (Structure)goal.getTarget()); } break; case ACQUIRE_ITEM: { Object targetRaw = goal.getTarget(); if(!(targetRaw instanceof String)){ - throw new Error("Target is not a string " + targetRaw); + return AITreeNodeResult.FAILURE; } blackboard.put(BlackboardKeys.GOAL_ITEM_ACQUISITION_TARGET, targetRaw); } break; diff --git a/src/main/java/electrosphere/server/ai/services/PathfindingService.java b/src/main/java/electrosphere/server/ai/services/PathfindingService.java index abe26355..0cbaa35b 100644 --- a/src/main/java/electrosphere/server/ai/services/PathfindingService.java +++ b/src/main/java/electrosphere/server/ai/services/PathfindingService.java @@ -32,10 +32,16 @@ public class PathfindingService implements AIService { public PathingProgressiveData queuePathfinding(Vector3d start, Vector3d end, VoxelPathfinder pathfinder, VoxelCellManager voxelCellManager){ PathingProgressiveData rVal = new PathingProgressiveData(end); executorService.submit(() -> { - List points = pathfinder.findPath(voxelCellManager, start, end, VoxelPathfinder.DEFAULT_MAX_COST); - points.add(end); - rVal.setPoints(points); - rVal.setReady(true); + try { + List points = pathfinder.findPath(voxelCellManager, start, end, VoxelPathfinder.DEFAULT_MAX_COST); + points.add(end); + rVal.setPoints(points); + rVal.setReady(true); + } catch(Error e){ + e.printStackTrace(); + } catch(Exception e){ + e.printStackTrace(); + } }); return rVal; } diff --git a/src/main/java/electrosphere/server/datacell/Realm.java b/src/main/java/electrosphere/server/datacell/Realm.java index ce0963ca..678790f0 100644 --- a/src/main/java/electrosphere/server/datacell/Realm.java +++ b/src/main/java/electrosphere/server/datacell/Realm.java @@ -257,7 +257,7 @@ public class Realm { // //macro data simulation - if(this.macroData != null){ + if(this.macroData != null && Globals.dbController != null && Globals.dbController.isConnected()){ MacroSimulation.simulate(this); } diff --git a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java index 47b3e721..c280035b 100644 --- a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java @@ -1233,4 +1233,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager return this.groundDataCells.values().contains(cell); } + @Override + public boolean hasBlocksAtPosition(Vector3i worldPosition) { + return this.serverWorldData.getServerBlockManager().hasChunk(worldPosition.x, worldPosition.y, worldPosition.z); + } + } diff --git a/src/main/java/electrosphere/server/datacell/interfaces/VoxelCellManager.java b/src/main/java/electrosphere/server/datacell/interfaces/VoxelCellManager.java index c2d434f6..8918bf45 100644 --- a/src/main/java/electrosphere/server/datacell/interfaces/VoxelCellManager.java +++ b/src/main/java/electrosphere/server/datacell/interfaces/VoxelCellManager.java @@ -59,6 +59,13 @@ public interface VoxelCellManager { */ public BlockChunkData getBlocksAtPosition(Vector3i worldPosition); + /** + * Checks if the manager has already-generated blocks at a given position + * @param worldPosition The position + * @return true if there are blocks at the position, false otherwise + */ + public boolean hasBlocksAtPosition(Vector3i worldPosition); + /** * Edits a single block voxel * @param worldPosition The world position of the block to edit diff --git a/src/main/java/electrosphere/server/macro/character/data/CharacterDataSerializer.java b/src/main/java/electrosphere/server/macro/character/data/CharacterDataSerializer.java index 462d31cd..c6e98af7 100644 --- a/src/main/java/electrosphere/server/macro/character/data/CharacterDataSerializer.java +++ b/src/main/java/electrosphere/server/macro/character/data/CharacterDataSerializer.java @@ -10,6 +10,7 @@ import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import electrosphere.server.macro.character.diety.Diety; +import electrosphere.server.macro.character.goal.CharacterGoal; import electrosphere.server.macro.race.Race; import electrosphere.server.macro.town.Town; @@ -34,10 +35,16 @@ public class CharacterDataSerializer implements JsonDeserializer, } //a structure - case CharacterDataStrings.STRUCTURE_ID: { + case CharacterDataStrings.STRUCTURE_ID: + case CharacterDataStrings.SHELTER: { return context.deserialize(json, CharacterAssociatedId.class); } + //goal + case CharacterDataStrings.ENTITY_GOAL: { + return context.deserialize(json, CharacterGoal.class); + } + //a town case CharacterDataStrings.TOWN: { return context.deserialize(json, Town.class); @@ -64,11 +71,7 @@ public class CharacterDataSerializer implements JsonDeserializer, } //a structure - case CharacterDataStrings.STRUCTURE_ID: { - return context.serialize((CharacterAssociatedId)src); - } - - //a structure + case CharacterDataStrings.STRUCTURE_ID: case CharacterDataStrings.SHELTER: { return context.serialize((CharacterAssociatedId)src); } @@ -78,6 +81,11 @@ public class CharacterDataSerializer implements JsonDeserializer, return context.serialize((Town)src); } + //goal + case CharacterDataStrings.ENTITY_GOAL: { + return context.serialize((CharacterGoal)src); + } + default: { throw new Error("Failed to serialize datatype: " + src.getDataType()); } diff --git a/src/main/java/electrosphere/server/macro/utils/StructureRepairUtils.java b/src/main/java/electrosphere/server/macro/utils/StructureRepairUtils.java index 96884512..8fa646a3 100644 --- a/src/main/java/electrosphere/server/macro/utils/StructureRepairUtils.java +++ b/src/main/java/electrosphere/server/macro/utils/StructureRepairUtils.java @@ -64,6 +64,10 @@ public class StructureRepairUtils { public static String getNextRepairMat(Realm realm, Structure struct){ Vector3i repairPos = StructureRepairUtils.getRepairablePosition(realm, struct); + if(repairPos == null){ + return null; + } + //get the id of item entity type for the block we need BlockFab fab = struct.getFab(); short blockTypeId = fab.getType(repairPos.x, repairPos.y, repairPos.z); @@ -131,6 +135,8 @@ public class StructureRepairUtils { ); Vector3i chunkPos = ServerWorldData.convertRealToChunkSpace(offsetPos); Vector3i blockPos = ServerWorldData.convertRealToLocalBlockSpace(offsetPos); + + //check existing blocks BlockChunkData blockChunkData = griddedDataCellManager.getBlocksAtPosition(chunkPos); short existingBlockType = blockChunkData.getType(blockPos.x, blockPos.y, blockPos.z); short desiredType = fab.getType(x, y, z); diff --git a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java index 37a71d22..680f6ef2 100644 --- a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java +++ b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java @@ -164,7 +164,7 @@ public class ServerBlockChunkGenerationThread implements Runnable { } //check if this chunk intersects any macro data AABBd localAABB = new AABBd(ServerWorldData.convertChunkToRealSpace(worldX,worldY,worldZ),ServerWorldData.convertChunkToRealSpace(worldX+1,worldY+1,worldZ+1)); - List filtered = macroData.getStructures().stream().filter((Structure struct) -> {return struct.isRepairable() || struct.getAABB().testAABB(localAABB);}).collect(Collectors.toList()); + List filtered = macroData.getStructures().stream().filter((Structure struct) -> {return !struct.isRepairable() && struct.getAABB().testAABB(localAABB);}).collect(Collectors.toList()); if(filtered.size() > 0){ Vector3i chunkPos = new Vector3i(worldX, worldY, worldZ); Vector3i blockPos = new Vector3i(0,0,0); diff --git a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java index 353b1c0a..58a4fe79 100644 --- a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java +++ b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java @@ -147,6 +147,17 @@ public class ServerBlockManager { Globals.profiler.endCpuSample(); } + /** + * Checks if there is an already-generated chunk at the position + * @param worldX The world x coordinate + * @param worldY The world y coordinate + * @param worldZ The world z coordinate + * @return true if the chunk exists, false otherwise + */ + public boolean hasChunk(int worldX, int worldY, int worldZ){ + return chunkDiskMap.containsBlocksAtPosition(worldX, worldY, worldZ) || chunkCache.containsChunk(worldX, worldY, worldZ, BlockChunkData.LOD_FULL_RES); + } + /** * Saves a given position's chunk to disk. * Uses the current global save name diff --git a/src/main/java/electrosphere/server/saves/SaveUtils.java b/src/main/java/electrosphere/server/saves/SaveUtils.java index 9653712a..04befd98 100644 --- a/src/main/java/electrosphere/server/saves/SaveUtils.java +++ b/src/main/java/electrosphere/server/saves/SaveUtils.java @@ -165,6 +165,9 @@ public class SaveUtils { //write server structures Globals.realmManager.save(saveName); + //store character service + Globals.characterService.saveAll(); + LoggerInterface.loggerEngine.WARNING("Finished saving " + saveName); } diff --git a/src/main/java/electrosphere/server/service/CharacterService.java b/src/main/java/electrosphere/server/service/CharacterService.java index d3529b5d..f0547a00 100644 --- a/src/main/java/electrosphere/server/service/CharacterService.java +++ b/src/main/java/electrosphere/server/service/CharacterService.java @@ -248,4 +248,31 @@ public class CharacterService extends SignalServiceImpl { lock.unlock(); } + /** + * Saves the character service + */ + public void saveAll(){ + lock.lock(); + for(Character chara : this.loadedCharacterMap.values()){ + CreatureTemplate template = chara.getCreatureTemplate(); + if(this.characterEntityMap.containsKey(chara)){ + Entity characterEntity = this.characterEntityMap.get(chara); + template = CreatureUtils.getCreatureTemplate(characterEntity); + chara.setCreatureTemplate(template); + chara.setPos(EntityUtils.getPosition(characterEntity)); + } + + //serialize + String toStore = SerializationUtils.serialize(chara); + + //store a serialization to associate with the character + Globals.dbController.executePreparedStatement( + "UPDATE charaData SET dataVal=? WHERE id=?;", + toStore, + chara.getId() + ); + } + lock.unlock(); + } + } diff --git a/src/main/java/electrosphere/server/simulation/MacroSimulation.java b/src/main/java/electrosphere/server/simulation/MacroSimulation.java index c1878125..f7a744cb 100644 --- a/src/main/java/electrosphere/server/simulation/MacroSimulation.java +++ b/src/main/java/electrosphere/server/simulation/MacroSimulation.java @@ -2,24 +2,11 @@ package electrosphere.server.simulation; import java.util.List; -import org.joml.Vector3d; - import electrosphere.engine.Globals; -import electrosphere.game.data.block.BlockFab; -import electrosphere.game.data.struct.StructureData; import electrosphere.server.datacell.Realm; -import electrosphere.server.macro.MacroData; import electrosphere.server.macro.character.Character; -import electrosphere.server.macro.character.CharacterUtils; -import electrosphere.server.macro.character.data.CharacterDataStrings; -import electrosphere.server.macro.character.goal.CharacterGoal; -import electrosphere.server.macro.character.goal.CharacterGoal.CharacterGoalType; -import electrosphere.server.macro.structure.Structure; -import electrosphere.server.macro.utils.StructurePlacementUtils; -import electrosphere.server.macro.utils.StructureRepairUtils; import electrosphere.server.service.CharacterService; -import electrosphere.server.simulation.chara.CharaInventoryUtils; -import electrosphere.util.FileUtils; +import electrosphere.server.simulation.chara.CharaSimulation; /** * Performs the macro-level (ie virtual, non-physics based) simulation @@ -41,9 +28,12 @@ public class MacroSimulation { if(character.getPlayerId() != CharacterService.NO_PLAYER){ continue; } - //do something - MacroSimulation.checkForShelter(realm, character); - MacroSimulation.checkTownMembership(character); + //update the goal of the character + CharaSimulation.setGoal(realm, character); + //if the character doesn't have an entity, simulate it at the macro level + if(Globals.characterService.getEntity(character) == null){ + CharaSimulation.performGoal(realm, character); + } } } } @@ -63,147 +53,5 @@ public class MacroSimulation { public boolean isReady(){ return isReady; } - - /** - * Maximum attempts to place a structure - */ - static final int MAX_PLACE_ATTEMPTS = 10; - - protected static void checkForShelter(Realm realm, Character chara){ - MacroData macroData = realm.getMacroData(); -// for(Character chara : Globals.macroData.getAliveCharacters()){ - /* - If doesn’t have shelter, check if in town - If in town, - check if there’s an inn/church/friendly family - if so, try to stay there - if can’t find place to stay, fashion makeshift shelter - If no town - fashion makeshift shelter - */ - if(CharacterUtils.getShelter(macroData,chara) != null){ - Structure shelter = CharacterUtils.getShelter(macroData,chara); - if(shelter.isRepairable()){ - if(StructureRepairUtils.validateRepairable(realm, shelter)){ - String repairMat = StructureRepairUtils.getNextRepairMat(realm, shelter); - if(CharaInventoryUtils.containsItem(chara, repairMat)){ - CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.BUILD_STRUCTURE, shelter)); - } else { - CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.ACQUIRE_ITEM, repairMat)); - } - } else { - shelter.setRepairable(false); - } - } - // Vector2i charPos = CharacterUtils.getDiscretePosition(chara); - // Town nearbyTown = Town.getTownAtPosition(charPos.x,charPos.y); - // if(nearbyTown != null){ - // //if town has a place to stay - // if(false){ - // } else { - // //try to find a place to put down a structure - - // } - } else { - Vector3d position = chara.getPos(); - StructureData structureData = Globals.gameConfigCurrent.getStructureData().getTypes().iterator().next(); - - //solve where to place - Vector3d placementPos = StructurePlacementUtils.getPlacementPosition(macroData, structureData, position); - - //add to macro data - Structure struct = Structure.createStructure(macroData, structureData, placementPos); - struct.setRepairable(true); - struct.setFab(BlockFab.read(FileUtils.getAssetFile(struct.getFabPath()))); - CharacterUtils.addShelter(chara, struct); - - //target the struct - CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.BUILD_STRUCTURE, struct)); - // //cry - // //TODO: Get building type to place - // String buildingTypeToPlace = "building1"; - // //try to find a place to put down a structure - // // int dynamicInterpRatio = Globals.serverTerrainManager.getDynamicInterpolationRatio(); - // // Vector2f placementPos = new Vector2f( - // // (float)(charPos.x * dynamicInterpRatio + Math.random() * dynamicInterpRatio), - // // (float)(charPos.y * dynamicInterpRatio + Math.random() * dynamicInterpRatio) - // // ); - // // int attempts = 0; - // // while(!VirtualStructureUtils.validStructurePlacementPosition(placementPos.x, placementPos.y, buildingTypeToPlace)){ - // // placementPos = new Vector2f( - // // (float)(charPos.x * dynamicInterpRatio + Math.random() * dynamicInterpRatio), - // // (float)(charPos.y * dynamicInterpRatio + Math.random() * dynamicInterpRatio) - // // ); - // // attempts++; - // // if(attempts > MAX_PLACE_ATTEMPTS){ - // // placementPos = null; - // // break; - // // } - // // } - // // if(placementPos != null){ - // // // Structure placedStructure = VirtualStructureUtils.placeStructureAtPoint(placementPos.x, placementPos.y, buildingTypeToPlace); - // // // CharacterUtils.addShelter(chara, placedStructure); - // // // VirtualStructureUtils.addResident(placedStructure, chara); - // // } - // } - } -// } - } - - protected static void checkTownMembership(Character chara){ - //TODO: eventually exclude people who shouldn't belong to a town (traders, bandits, etc) -// for(Character chara : Globals.macroData.getAliveCharacters()){ - boolean hasHometown = chara.containsKey(CharacterDataStrings.HOMETOWN); - boolean hasShelter = chara.containsKey(CharacterDataStrings.SHELTER); - //if has structure & no hometown - if(!hasHometown && hasShelter){ - // Structure shelter = CharacterUtils.getShelter(chara); - //if there's at least one other structure nearby - // Vector2i shelterDiscretePos = new Vector2i(shelter.getWorldX(),shelter.getWorldY()); - // List nearbyPopulatedStructures = new LinkedList(); - // for(Structure currentStruct : Globals.macroData.getStructures()){ - // if(currentStruct.getWorldX() == shelterDiscretePos.x && currentStruct.getWorldY() == shelterDiscretePos.y && currentStruct != shelter){ - // //if has a resident - // if(shelter.getDataKeys().contains(StructureDataStrings.RESIDENTS) && VirtualStructureUtils.getResidents(shelter).size() > 0){ - // boolean noTown = true; - // for(Town town : Globals.macroData.getTowns()){ - // if(town.getStructures().contains(currentStruct)){ - // noTown = false; - // } - // } - // if(noTown){ - // nearbyPopulatedStructures.add(currentStruct); - // } - // } - // } - // } - // if(nearbyPopulatedStructures.size() > 0){ - // int numStructures = 0; - // int numResidents = 0; - // //form town - // Town newTown = Town.createTown(shelterDiscretePos.x, shelterDiscretePos.y); - // for(Structure structure : nearbyPopulatedStructures){ - // numStructures++; - // newTown.addStructure(structure); - // for(Character resident : VirtualStructureUtils.getResidents(structure)){ - // numResidents++; - // newTown.addResident(resident); - // CharacterUtils.addHometown(resident, newTown); - // } - // } - // newTown.addStructure(shelter); - // newTown.addResident(chara); - // CharacterUtils.addHometown(chara, newTown); - // System.out.println("Formed town with " + numStructures + " structures and " + numResidents + " residents"); - // } - } -// } - } - - // private static void checkInitCombat(){ - // for(Character chara : Globals.macroData.getAliveCharacters()){ - // // Vector2i position = CharacterUtils.getDiscretePosition(chara); - // } - // } } diff --git a/src/main/java/electrosphere/server/simulation/chara/CharaSimulation.java b/src/main/java/electrosphere/server/simulation/chara/CharaSimulation.java new file mode 100644 index 00000000..6b3d1a37 --- /dev/null +++ b/src/main/java/electrosphere/server/simulation/chara/CharaSimulation.java @@ -0,0 +1,193 @@ +package electrosphere.server.simulation.chara; + +import org.joml.Vector3d; + +import electrosphere.engine.Globals; +import electrosphere.game.data.block.BlockFab; +import electrosphere.game.data.struct.StructureData; +import electrosphere.server.datacell.Realm; +import electrosphere.server.macro.MacroData; +import electrosphere.server.macro.character.Character; +import electrosphere.server.macro.character.CharacterUtils; +import electrosphere.server.macro.character.data.CharacterDataStrings; +import electrosphere.server.macro.character.goal.CharacterGoal; +import electrosphere.server.macro.character.goal.CharacterGoal.CharacterGoalType; +import electrosphere.server.macro.structure.Structure; +import electrosphere.server.macro.utils.StructurePlacementUtils; +import electrosphere.server.macro.utils.StructureRepairUtils; +import electrosphere.util.FileUtils; + +/** + * Methods for simulating characters + */ +public class CharaSimulation { + + + /** + * Sets the goal of the character + * @param realm The realm + * @param chara The character + */ + public static void setGoal(Realm realm, Character chara){ + CharaSimulation.checkForShelter(realm, chara); + } + + /** + * Maximum attempts to place a structure + */ + static final int MAX_PLACE_ATTEMPTS = 10; + + /** + * Checks if the character has shelter + * @param realm The realm + * @param chara The character + */ + protected static void checkForShelter(Realm realm, Character chara){ + MacroData macroData = realm.getMacroData(); +// for(Character chara : Globals.macroData.getAliveCharacters()){ + /* + If doesn’t have shelter, check if in town + If in town, + check if there’s an inn/church/friendly family + if so, try to stay there + if can’t find place to stay, fashion makeshift shelter + If no town + fashion makeshift shelter + */ + if(CharacterUtils.getShelter(macroData,chara) != null){ + Structure shelter = CharacterUtils.getShelter(macroData,chara); + if(shelter.isRepairable()){ + if(StructureRepairUtils.validateRepairable(realm, shelter)){ + String repairMat = StructureRepairUtils.getNextRepairMat(realm, shelter); + if(CharaInventoryUtils.containsItem(chara, repairMat)){ + CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.BUILD_STRUCTURE, shelter)); + } else { + CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.ACQUIRE_ITEM, repairMat)); + } + } else { + shelter.setRepairable(false); + } + } + // Vector2i charPos = CharacterUtils.getDiscretePosition(chara); + // Town nearbyTown = Town.getTownAtPosition(charPos.x,charPos.y); + // if(nearbyTown != null){ + // //if town has a place to stay + // if(false){ + + // } else { + // //try to find a place to put down a structure + + // } + } else { + Vector3d position = chara.getPos(); + StructureData structureData = Globals.gameConfigCurrent.getStructureData().getTypes().iterator().next(); + + //solve where to place + Vector3d placementPos = StructurePlacementUtils.getPlacementPosition(macroData, structureData, position); + + //add to macro data + Structure struct = Structure.createStructure(macroData, structureData, placementPos); + struct.setRepairable(true); + struct.setFab(BlockFab.read(FileUtils.getAssetFile(struct.getFabPath()))); + CharacterUtils.addShelter(chara, struct); + + //target the struct + CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.BUILD_STRUCTURE, struct)); + // //cry + // //TODO: Get building type to place + // String buildingTypeToPlace = "building1"; + // //try to find a place to put down a structure + // // int dynamicInterpRatio = Globals.serverTerrainManager.getDynamicInterpolationRatio(); + // // Vector2f placementPos = new Vector2f( + // // (float)(charPos.x * dynamicInterpRatio + Math.random() * dynamicInterpRatio), + // // (float)(charPos.y * dynamicInterpRatio + Math.random() * dynamicInterpRatio) + // // ); + // // int attempts = 0; + // // while(!VirtualStructureUtils.validStructurePlacementPosition(placementPos.x, placementPos.y, buildingTypeToPlace)){ + // // placementPos = new Vector2f( + // // (float)(charPos.x * dynamicInterpRatio + Math.random() * dynamicInterpRatio), + // // (float)(charPos.y * dynamicInterpRatio + Math.random() * dynamicInterpRatio) + // // ); + // // attempts++; + // // if(attempts > MAX_PLACE_ATTEMPTS){ + // // placementPos = null; + // // break; + // // } + // // } + // // if(placementPos != null){ + // // // Structure placedStructure = VirtualStructureUtils.placeStructureAtPoint(placementPos.x, placementPos.y, buildingTypeToPlace); + // // // CharacterUtils.addShelter(chara, placedStructure); + // // // VirtualStructureUtils.addResident(placedStructure, chara); + // // } + // } + } +// } + } + + protected static void checkTownMembership(Character chara){ + //TODO: eventually exclude people who shouldn't belong to a town (traders, bandits, etc) +// for(Character chara : Globals.macroData.getAliveCharacters()){ + boolean hasHometown = chara.containsKey(CharacterDataStrings.HOMETOWN); + boolean hasShelter = chara.containsKey(CharacterDataStrings.SHELTER); + //if has structure & no hometown + if(!hasHometown && hasShelter){ + // Structure shelter = CharacterUtils.getShelter(chara); + //if there's at least one other structure nearby + // Vector2i shelterDiscretePos = new Vector2i(shelter.getWorldX(),shelter.getWorldY()); + // List nearbyPopulatedStructures = new LinkedList(); + // for(Structure currentStruct : Globals.macroData.getStructures()){ + // if(currentStruct.getWorldX() == shelterDiscretePos.x && currentStruct.getWorldY() == shelterDiscretePos.y && currentStruct != shelter){ + // //if has a resident + // if(shelter.getDataKeys().contains(StructureDataStrings.RESIDENTS) && VirtualStructureUtils.getResidents(shelter).size() > 0){ + // boolean noTown = true; + // for(Town town : Globals.macroData.getTowns()){ + // if(town.getStructures().contains(currentStruct)){ + // noTown = false; + // } + // } + // if(noTown){ + // nearbyPopulatedStructures.add(currentStruct); + // } + // } + // } + // } + // if(nearbyPopulatedStructures.size() > 0){ + // int numStructures = 0; + // int numResidents = 0; + // //form town + // Town newTown = Town.createTown(shelterDiscretePos.x, shelterDiscretePos.y); + // for(Structure structure : nearbyPopulatedStructures){ + // numStructures++; + // newTown.addStructure(structure); + // for(Character resident : VirtualStructureUtils.getResidents(structure)){ + // numResidents++; + // newTown.addResident(resident); + // CharacterUtils.addHometown(resident, newTown); + // } + // } + // newTown.addStructure(shelter); + // newTown.addResident(chara); + // CharacterUtils.addHometown(chara, newTown); + // System.out.println("Formed town with " + numStructures + " structures and " + numResidents + " residents"); + // } + } +// } + } + + // private static void checkInitCombat(){ + // for(Character chara : Globals.macroData.getAliveCharacters()){ + // // Vector2i position = CharacterUtils.getDiscretePosition(chara); + // } + // } + + + /** + * Performs whatever the current goal of the character is + * @param realm The realm + * @param chara The character + */ + public static void performGoal(Realm realm, Character chara){ + //todo -- acquire item logic + } + +}