Great big lump o' stuff to deal with ship types and related things more cleanly.

OOShipRegistry parses shipdata.plist, shipyard.plist, demoships.plist and new shipdata-overrides.plist and shipyard-overrides.plist, resolves like_ship inheritance, does some sanity checking, filters out various sorts of invalid shipdata and shipyard entries and manages role->ship key mapping with probability weighting.

The latter is managed by OOProbabilitySet, which should probably be used by OORoleSet and OOProbabilisticTextureManager too. OOProbabilitySet is O(log n) for random selection instead of O(n) like the current linear-search code.

The next step is to start using OOShipRegistry everywhere.

git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@1607 127b21dd-08f5-0310-b4b7-95ae10353056
This commit is contained in:
Jens Ayton 2008-05-03 00:58:21 +00:00
parent edf000ec1d
commit 30bce0b115
17 changed files with 2049 additions and 95 deletions

View File

@ -26,7 +26,7 @@ endif
OBJC_PROGRAM_NAME = oolite OBJC_PROGRAM_NAME = oolite
oolite_C_FILES = legacy_random.c strlcpy.c OOTCPStreamDecoder.c oolite_C_FILES = legacy_random.c strlcpy.c OOTCPStreamDecoder.c
oolite_OBJC_FILES = OOCocoa.m Comparison.m AI.m DustEntity.m Entity.m GameController.m GuiDisplayGen.m HeadUpDisplay.m main.m MyOpenGLView.m OpenGLSprite.m ParticleEntity.m PlanetEntity.m PlayerEntityLegacyScriptEngine.m PlayerEntityContracts.m PlayerEntityControls.m PlayerEntityLoadSave.m PlayerEntitySound.m PlayerEntity.m ResourceManager.m RingEntity.m ShipEntityAI.m ShipEntity.m SkyEntity.m StationEntity.m Universe.m OOSound.m SDLMusic.m NSFileManagerOOExtensions.m JoystickHandler.m PlayerEntityStickMapper.m OOBasicSoundReferencePoint.m OOBasicSoundSource.m OOCharacter.m OOTrumble.m WormholeEntity.m NSScannerOOExtensions.m OOXMLExtensions.m NSMutableDictionaryOOExtensions.m Geometry.m Octree.m CollisionRegion.m OOColor.m OOLogging.m OOCacheManager.m OOCache.m OOStringParsing.m OOCollectionExtractors.m OOVector.m OOMatrix.m OOQuaternion.m OOVoxel.m OOTriangle.m OOPListParsing.m OOFastArithmetic.m OOTextureScaling.m OOConstToString.m OOScript.m OOJSScript.m OOJavaScriptEngine.m OOPListScript.m NSStringOOExtensions.m PlayerEntityScriptMethods.m OOWeakReference.m OOJSEntity.m EntityOOJavaScriptExtensions.m OOJSQuaternion.m OOMaterial.m OOShaderMaterial.m OOShaderProgram.m OOShaderUniform.m OOTexture.m OOTextureLoader.m OOPNGTextureLoader.m OOOpenGLExtensionManager.m OOBasicMaterial.m OOSingleTextureMaterial.m OOCPUInfo.m OOSelfDrawingEntity.m OOEntityWithDrawable.m OODrawable.m OOJSVector.m OOMesh.m OOOpenGL.m OOGraphicsResetManager.m OOProbabilisticTextureManager.m OODebugGLDrawing.m OOShaderUniformMethodType.m OOAsyncQueue.m TextureStore.m OOOXPVerifier.m OOOXPVerifierStage.m OOFileScannerVerifierStage.m OOCheckRequiresPListVerifierStage.m OOCheckDemoShipsPListVerifierStage.m OOCheckEquipmentPListVerifierStage.m OOTextureVerifierStage.m OOModelVerifierStage.m OOCheckShipDataPListVerifierStage.m OOPListSchemaVerifier.m OOJSShip.m OOJSPlayer.m OOJSCall.m OOJSStation.m OOJSSystem.m OOLegacyEventHandlerScript.m OOJSOolite.m OORoleSet.m OOJSGlobal.m OOJSMissionVariables.m OOJSMission.m OOPriorityQueue.m OOScriptTimer.m OOJSTimer.m OOJSClock.m OODebugSupport.m OODebugMonitor.m OOJSConsole.m OODebugTCPConsoleClient.m OOTCPStreamDecoderAbstractionLayer.m OOEntityFilterPredicate.m OOJSPlanet.m OOJSWorldScripts.m OOJSSun.m NSThreadOOExtensions.m OOEncodingConverter.m OOJSSound.m OOJSSoundSource.m OOMusicController.m OOLogHeader.m OOJSSpecialFunctions.m OOSpatialReference.m OOSkyDrawable.m OOFilteringEnumerator.m OOSoundSourcePool.m ShipEntityScriptMethods.m oolite_OBJC_FILES = OOCocoa.m Comparison.m AI.m DustEntity.m Entity.m GameController.m GuiDisplayGen.m HeadUpDisplay.m main.m MyOpenGLView.m OpenGLSprite.m ParticleEntity.m PlanetEntity.m PlayerEntityLegacyScriptEngine.m PlayerEntityContracts.m PlayerEntityControls.m PlayerEntityLoadSave.m PlayerEntitySound.m PlayerEntity.m ResourceManager.m RingEntity.m ShipEntityAI.m ShipEntity.m SkyEntity.m StationEntity.m Universe.m OOSound.m SDLMusic.m NSFileManagerOOExtensions.m JoystickHandler.m PlayerEntityStickMapper.m OOBasicSoundReferencePoint.m OOBasicSoundSource.m OOCharacter.m OOTrumble.m WormholeEntity.m NSScannerOOExtensions.m OOXMLExtensions.m NSMutableDictionaryOOExtensions.m Geometry.m Octree.m CollisionRegion.m OOColor.m OOLogging.m OOCacheManager.m OOCache.m OOStringParsing.m OOCollectionExtractors.m OOVector.m OOMatrix.m OOQuaternion.m OOVoxel.m OOTriangle.m OOPListParsing.m OOFastArithmetic.m OOTextureScaling.m OOConstToString.m OOScript.m OOJSScript.m OOJavaScriptEngine.m OOPListScript.m NSStringOOExtensions.m PlayerEntityScriptMethods.m OOWeakReference.m OOJSEntity.m EntityOOJavaScriptExtensions.m OOJSQuaternion.m OOMaterial.m OOShaderMaterial.m OOShaderProgram.m OOShaderUniform.m OOTexture.m OOTextureLoader.m OOPNGTextureLoader.m OOOpenGLExtensionManager.m OOBasicMaterial.m OOSingleTextureMaterial.m OOCPUInfo.m OOSelfDrawingEntity.m OOEntityWithDrawable.m OODrawable.m OOJSVector.m OOMesh.m OOOpenGL.m OOGraphicsResetManager.m OOProbabilisticTextureManager.m OODebugGLDrawing.m OOShaderUniformMethodType.m OOAsyncQueue.m TextureStore.m OOOXPVerifier.m OOOXPVerifierStage.m OOFileScannerVerifierStage.m OOCheckRequiresPListVerifierStage.m OOCheckDemoShipsPListVerifierStage.m OOCheckEquipmentPListVerifierStage.m OOTextureVerifierStage.m OOModelVerifierStage.m OOCheckShipDataPListVerifierStage.m OOPListSchemaVerifier.m OOJSShip.m OOJSPlayer.m OOJSCall.m OOJSStation.m OOJSSystem.m OOLegacyEventHandlerScript.m OOJSOolite.m OORoleSet.m OOJSGlobal.m OOJSMissionVariables.m OOJSMission.m OOPriorityQueue.m OOScriptTimer.m OOJSTimer.m OOJSClock.m OODebugSupport.m OODebugMonitor.m OOJSConsole.m OODebugTCPConsoleClient.m OOTCPStreamDecoderAbstractionLayer.m OOEntityFilterPredicate.m OOJSPlanet.m OOJSWorldScripts.m OOJSSun.m NSThreadOOExtensions.m OOEncodingConverter.m OOJSSound.m OOJSSoundSource.m OOMusicController.m OOLogHeader.m OOJSSpecialFunctions.m OOSpatialReference.m OOSkyDrawable.m OOFilteringEnumerator.m OOSoundSourcePool.m ShipEntityScriptMethods.m OOShipRegistry.m OOProbabilitySet.m NSDictionaryOOExtensions.m
include $(GNUSTEP_MAKEFILES)/objc.make include $(GNUSTEP_MAKEFILES)/objc.make
include GNUmakefile.postamble include GNUmakefile.postamble

View File

@ -33,6 +33,12 @@
1A0365890D7CA05000B5F46F /* OOSkyDrawable.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A15044A0C12C50D0032F3E8 /* OOSkyDrawable.m */; }; 1A0365890D7CA05000B5F46F /* OOSkyDrawable.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A15044A0C12C50D0032F3E8 /* OOSkyDrawable.m */; };
1A03658A0D7CA05000B5F46F /* OOSkyDrawable.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A1504490C12C50D0032F3E8 /* OOSkyDrawable.h */; }; 1A03658A0D7CA05000B5F46F /* OOSkyDrawable.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A1504490C12C50D0032F3E8 /* OOSkyDrawable.h */; };
1A03659B0D7CA0EE00B5F46F /* oolite-nebula-4.png in Copy Textures */ = {isa = PBXBuildFile; fileRef = 1A0365990D7CA0EE00B5F46F /* oolite-nebula-4.png */; }; 1A03659B0D7CA0EE00B5F46F /* oolite-nebula-4.png in Copy Textures */ = {isa = PBXBuildFile; fileRef = 1A0365990D7CA0EE00B5F46F /* oolite-nebula-4.png */; };
1A0479E90DC9F81000EE1CD0 /* OOShipRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A0479E70DC9F81000EE1CD0 /* OOShipRegistry.h */; };
1A0479EA0DC9F81000EE1CD0 /* OOShipRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0479E80DC9F81000EE1CD0 /* OOShipRegistry.m */; };
1A047A450DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A047A430DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.h */; };
1A047A460DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A047A440DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.m */; };
1A047B7E0DCB3D7500EE1CD0 /* OOProbabilitySet.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A047B7C0DCB3D7500EE1CD0 /* OOProbabilitySet.h */; };
1A047B7F0DCB3D7500EE1CD0 /* OOProbabilitySet.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A047B7D0DCB3D7500EE1CD0 /* OOProbabilitySet.m */; };
1A0DA2EE0D71D280009B0970 /* OOJSSpecialFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A0DA2EC0D71D280009B0970 /* OOJSSpecialFunctions.h */; }; 1A0DA2EE0D71D280009B0970 /* OOJSSpecialFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A0DA2EC0D71D280009B0970 /* OOJSSpecialFunctions.h */; };
1A0DA2EF0D71D280009B0970 /* OOJSSpecialFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0DA2ED0D71D280009B0970 /* OOJSSpecialFunctions.m */; }; 1A0DA2EF0D71D280009B0970 /* OOJSSpecialFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0DA2ED0D71D280009B0970 /* OOJSSpecialFunctions.m */; };
1A1502F60C1201C30032F3E8 /* oolite-unknown-ship.dat in Copy Models */ = {isa = PBXBuildFile; fileRef = 1A1502F50C1201C30032F3E8 /* oolite-unknown-ship.dat */; }; 1A1502F60C1201C30032F3E8 /* oolite-unknown-ship.dat in Copy Models */ = {isa = PBXBuildFile; fileRef = 1A1502F50C1201C30032F3E8 /* oolite-unknown-ship.dat */; };
@ -1019,6 +1025,12 @@
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; }; 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
1A020E0A0D020AFB00C3F51E /* changedScriptHandlers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = changedScriptHandlers.plist; sourceTree = "<group>"; }; 1A020E0A0D020AFB00C3F51E /* changedScriptHandlers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = changedScriptHandlers.plist; sourceTree = "<group>"; };
1A0365990D7CA0EE00B5F46F /* oolite-nebula-4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "oolite-nebula-4.png"; sourceTree = "<group>"; }; 1A0365990D7CA0EE00B5F46F /* oolite-nebula-4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "oolite-nebula-4.png"; sourceTree = "<group>"; };
1A0479E70DC9F81000EE1CD0 /* OOShipRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOShipRegistry.h; sourceTree = "<group>"; };
1A0479E80DC9F81000EE1CD0 /* OOShipRegistry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOShipRegistry.m; sourceTree = "<group>"; };
1A047A430DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSDictionaryOOExtensions.h; sourceTree = "<group>"; };
1A047A440DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSDictionaryOOExtensions.m; sourceTree = "<group>"; };
1A047B7C0DCB3D7500EE1CD0 /* OOProbabilitySet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOProbabilitySet.h; sourceTree = "<group>"; };
1A047B7D0DCB3D7500EE1CD0 /* OOProbabilitySet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOProbabilitySet.m; sourceTree = "<group>"; };
1A0517D10C7B376700BA5CCA /* debug-exports.exp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.exports; path = "debug-exports.exp"; sourceTree = "<group>"; }; 1A0517D10C7B376700BA5CCA /* debug-exports.exp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.exports; path = "debug-exports.exp"; sourceTree = "<group>"; };
1A0519340C7CCAC900BA5CCA /* DebugOXP.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = DebugOXP.xcodeproj; path = "Mac-DebugOXP/DebugOXP.xcodeproj"; sourceTree = SOURCE_ROOT; }; 1A0519340C7CCAC900BA5CCA /* DebugOXP.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = DebugOXP.xcodeproj; path = "Mac-DebugOXP/DebugOXP.xcodeproj"; sourceTree = SOURCE_ROOT; };
1A0DA2EC0D71D280009B0970 /* OOJSSpecialFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOJSSpecialFunctions.h; sourceTree = "<group>"; }; 1A0DA2EC0D71D280009B0970 /* OOJSSpecialFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOJSSpecialFunctions.h; sourceTree = "<group>"; };
@ -1035,7 +1047,7 @@
1A23154F0B9C778400EF0852 /* splash.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = splash.png; sourceTree = "<group>"; }; 1A23154F0B9C778400EF0852 /* splash.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = splash.png; sourceTree = "<group>"; };
1A2315500B9C778400EF0852 /* splashback.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = splashback.png; sourceTree = "<group>"; }; 1A2315500B9C778400EF0852 /* splashback.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = splashback.png; sourceTree = "<group>"; };
1A2315510B9C778400EF0852 /* trumblebox.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = trumblebox.png; sourceTree = "<group>"; }; 1A2315510B9C778400EF0852 /* trumblebox.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = trumblebox.png; sourceTree = "<group>"; };
1A2316DE0B9CFAD700EF0852 /* characters.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = characters.plist; sourceTree = "<group>"; }; 1A2316DE0B9CFAD700EF0852 /* characters.plist */ = {isa = PBXFileReference; explicitFileType = text.plist; fileEncoding = 4; path = characters.plist; sourceTree = "<group>"; };
1A2316DF0B9CFAD700EF0852 /* commodities.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = commodities.plist; sourceTree = "<group>"; }; 1A2316DF0B9CFAD700EF0852 /* commodities.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = commodities.plist; sourceTree = "<group>"; };
1A2316E00B9CFAD700EF0852 /* customsounds.plist */ = {isa = PBXFileReference; explicitFileType = text.plist; fileEncoding = 4; path = customsounds.plist; sourceTree = "<group>"; }; 1A2316E00B9CFAD700EF0852 /* customsounds.plist */ = {isa = PBXFileReference; explicitFileType = text.plist; fileEncoding = 4; path = customsounds.plist; sourceTree = "<group>"; };
1A2316E10B9CFAD700EF0852 /* demoships.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = demoships.plist; sourceTree = "<group>"; }; 1A2316E10B9CFAD700EF0852 /* demoships.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = demoships.plist; sourceTree = "<group>"; };
@ -1535,7 +1547,7 @@
1ACEA7270C91DF2800C7CE97 /* OOJSMissionVariables.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOJSMissionVariables.m; sourceTree = "<group>"; }; 1ACEA7270C91DF2800C7CE97 /* OOJSMissionVariables.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOJSMissionVariables.m; sourceTree = "<group>"; };
1ACEA7A80C91E32800C7CE97 /* OOJSMission.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOJSMission.h; sourceTree = "<group>"; }; 1ACEA7A80C91E32800C7CE97 /* OOJSMission.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOJSMission.h; sourceTree = "<group>"; };
1ACEA7A90C91E32800C7CE97 /* OOJSMission.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOJSMission.m; sourceTree = "<group>"; }; 1ACEA7A90C91E32800C7CE97 /* OOJSMission.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOJSMission.m; sourceTree = "<group>"; };
1AD0C32F0C463FCB0070BD23 /* autoAImap.plist */ = {isa = PBXFileReference; fileEncoding = 4; languageSpecificationIdentifier = plist; lastKnownFileType = text.xml; path = autoAImap.plist; sourceTree = "<group>"; }; 1AD0C32F0C463FCB0070BD23 /* autoAImap.plist */ = {isa = PBXFileReference; explicitFileType = text.plist; fileEncoding = 4; languageSpecificationIdentifier = plist; path = autoAImap.plist; sourceTree = "<group>"; };
1AD0C6C80C47B76F0070BD23 /* SCRDynamicShim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCRDynamicShim.m; sourceTree = "<group>"; }; 1AD0C6C80C47B76F0070BD23 /* SCRDynamicShim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCRDynamicShim.m; sourceTree = "<group>"; };
1AD0C6E30C47B82C0070BD23 /* SmartCrashReportsInstall.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = SmartCrashReportsInstall.o; sourceTree = "<group>"; }; 1AD0C6E30C47B82C0070BD23 /* SmartCrashReportsInstall.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = SmartCrashReportsInstall.o; sourceTree = "<group>"; };
1AD0C6E40C47B82C0070BD23 /* SmartCrashReportsInstall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmartCrashReportsInstall.h; sourceTree = "<group>"; }; 1AD0C6E40C47B82C0070BD23 /* SmartCrashReportsInstall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmartCrashReportsInstall.h; sourceTree = "<group>"; };
@ -2349,6 +2361,8 @@
1AB4AEB70D688AD9003076D6 /* OOLogHeader.m */, 1AB4AEB70D688AD9003076D6 /* OOLogHeader.m */,
25161107099544390037C2E1 /* OOXMLExtensions.h */, 25161107099544390037C2E1 /* OOXMLExtensions.h */,
25161102099544380037C2E1 /* OOXMLExtensions.m */, 25161102099544380037C2E1 /* OOXMLExtensions.m */,
1A047A430DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.h */,
1A047A440DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.m */,
1A8A37550B960337007D20B8 /* NSMutableDictionaryOOExtensions.h */, 1A8A37550B960337007D20B8 /* NSMutableDictionaryOOExtensions.h */,
1A8A37540B960337007D20B8 /* NSMutableDictionaryOOExtensions.m */, 1A8A37540B960337007D20B8 /* NSMutableDictionaryOOExtensions.m */,
1A8A394D0B96229C007D20B8 /* NSFileManagerOOExtensions.h */, 1A8A394D0B96229C007D20B8 /* NSFileManagerOOExtensions.h */,
@ -2390,6 +2404,8 @@
1A1616610D7DCFDC0094AE5B /* OOFilteringEnumerator.m */, 1A1616610D7DCFDC0094AE5B /* OOFilteringEnumerator.m */,
1ACBF06F0D82DF9B00CC005F /* OOSoundSourcePool.h */, 1ACBF06F0D82DF9B00CC005F /* OOSoundSourcePool.h */,
1ACBF0700D82DF9B00CC005F /* OOSoundSourcePool.m */, 1ACBF0700D82DF9B00CC005F /* OOSoundSourcePool.m */,
1A047B7C0DCB3D7500EE1CD0 /* OOProbabilitySet.h */,
1A047B7D0DCB3D7500EE1CD0 /* OOProbabilitySet.m */,
); );
name = Utilities; name = Utilities;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2592,6 +2608,8 @@
children = ( children = (
1ACEA3470C91507000C7CE97 /* OORoleSet.h */, 1ACEA3470C91507000C7CE97 /* OORoleSet.h */,
1ACEA3480C91507000C7CE97 /* OORoleSet.m */, 1ACEA3480C91507000C7CE97 /* OORoleSet.m */,
1A0479E70DC9F81000EE1CD0 /* OOShipRegistry.h */,
1A0479E80DC9F81000EE1CD0 /* OOShipRegistry.m */,
); );
name = "Ship management"; name = "Ship management";
sourceTree = "<group>"; sourceTree = "<group>";
@ -2843,6 +2861,9 @@
1A1616620D7DCFDC0094AE5B /* OOFilteringEnumerator.h in Headers */, 1A1616620D7DCFDC0094AE5B /* OOFilteringEnumerator.h in Headers */,
1ACBF0AD0D82F79600CC005F /* OOSoundSourcePool.h in Headers */, 1ACBF0AD0D82F79600CC005F /* OOSoundSourcePool.h in Headers */,
1A7BA8830D843485003C6CA3 /* ShipEntityScriptMethods.h in Headers */, 1A7BA8830D843485003C6CA3 /* ShipEntityScriptMethods.h in Headers */,
1A0479E90DC9F81000EE1CD0 /* OOShipRegistry.h in Headers */,
1A047A450DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.h in Headers */,
1A047B7E0DCB3D7500EE1CD0 /* OOProbabilitySet.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -3189,6 +3210,9 @@
1A1616630D7DCFDC0094AE5B /* OOFilteringEnumerator.m in Sources */, 1A1616630D7DCFDC0094AE5B /* OOFilteringEnumerator.m in Sources */,
1ACBF0AE0D82F79800CC005F /* OOSoundSourcePool.m in Sources */, 1ACBF0AE0D82F79800CC005F /* OOSoundSourcePool.m in Sources */,
1A7BA8840D843485003C6CA3 /* ShipEntityScriptMethods.m in Sources */, 1A7BA8840D843485003C6CA3 /* ShipEntityScriptMethods.m in Sources */,
1A0479EA0DC9F81000EE1CD0 /* OOShipRegistry.m in Sources */,
1A047A460DCA0F4F00EE1CD0 /* NSDictionaryOOExtensions.m in Sources */,
1A047B7F0DCB3D7500EE1CD0 /* OOProbabilitySet.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -1,47 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?> {
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> "constrictor-mission-thief" =
<plist version="1.0"> {
<dict> "random_seed" = "1 3 5 7 11 13"; // fix the name and details, but use the key to keep it secret
<key>constrictor-mission-thief</key> bounty = 1000; // not actually used, we do the reward in the script
<dict> origin = 7; // system number seven (Lave in Galaxy 0)
<key>random_seed</key> "script_actions" =
<string>1 3 5 7 11 13</string><!-- fix the name and details, but use the key to keep it secret --> (
<key>bounty</key> /*
<integer>1000</integer><!-- not actually used, we do the reward in the script --> check if docked
<key>origin</key> print congratulatory message from the Imperial Navy
<integer>7</integer><!-- system number seven (Lave in Galaxy 0) --> award a special bounty
<key>script_actions</key> */
<!-- {
check if docked conditions =
print congratulatory message from the Imperial Navy (
award a special bounty "status_string equal STATUS_DOCKED"
--> );
<array> do =
<dict> (
<key>conditions</key> {
<array> conditions =
<string>status_string equal STATUS_DOCKED</string> (
</array> "gui_screen_string notequal GUI_SCREEN_MISSION"
<key>do</key> );
<array> do = (
<dict> "setMissionMusic: none",
<key>conditions</key> "setMissionImage: none",
<array> "showShipModel: none",
<string>gui_screen_string notequal GUI_SCREEN_MISSION</string> "setGuiToMissionScreen"
</array> );
<key>do</key> },
<array> "awardCredits: 1000",
<string>setMissionMusic: none</string> "addMissionText: conhunt-thief-captured"
<string>setMissionImage: none</string> );
<string>showShipModel: none</string> }
<string>setGuiToMissionScreen</string> );
</array> };
</dict> }
<string>awardCredits: 1000</string>
<string>addMissionText: conhunt-thief-captured</string>
</array>
</dict>
</array>
</dict>
</dict>
</plist>

View File

@ -1,33 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?> (
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> "coriolis-station",
<plist version="1.0"> "boa-mk2",
<array> transporter,
<string>coriolis-station</string> alloy,
<string>boa-mk2</string> cobramk1,
<string>transporter</string> worm,
<string>alloy</string> viper,
<string>cobramk1</string> asteroid,
<string>worm</string> barrel,
<string>viper</string> python,
<string>asteroid</string> missile,
<string>barrel</string> tharglet,
<string>python</string> thargoid,
<string>missile</string> boa,
<string>tharglet</string> shuttle,
<string>thargoid</string> "dodecahedron-station",
<string>boa</string> sidewinder,
<string>shuttle</string> moray,
<string>dodecahedron-station</string> mamba,
<string>sidewinder</string> krait,
<string>moray</string> boulder,
<string>mamba</string> ferdelance,
<string>krait</string> gecko,
<string>boulder</string> anaconda,
<string>ferdelance</string> "escape-capsule",
<string>gecko</string> adder,
<string>anaconda</string> asp
<string>escape-capsule</string> )
<string>adder</string>
<string>asp</string>
</array>
</plist>

View File

@ -922,6 +922,10 @@ static PlayerEntity *sSharedPlayer = nil;
- (BOOL) setUpShipFromDictionary:(NSDictionary *)shipDict - (BOOL) setUpShipFromDictionary:(NSDictionary *)shipDict
{ {
// In order for default values to work and float values to not be junk,
// replace nil with empty dictionary. -- Ahruman 2008-05-01
if (shipDict == nil) shipDict = [NSDictionary dictionary];
[shipinfoDictionary release]; [shipinfoDictionary release];
shipinfoDictionary = [shipDict copy]; shipinfoDictionary = [shipDict copy];

View File

@ -0,0 +1,61 @@
/*
NSDictionaryOOExtensions.h
Extensions to NSDictionary.
Oolite
Copyright (C) 2004-2008 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
This file may also be distributed under the MIT/X11 license:
Copyright (C) 2008 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOCocoa.h"
@interface NSDictionary (OOExtensions)
// These all return self if passed nil paramters. They all return a new immutable dictionary if sent to a mutable dictionary.
- (NSDictionary *) dictionaryByAddingObject:(id)object forKey:(id)key;
- (NSDictionary *) dictionaryByRemovingObjectForKey:(id)key;
- (NSDictionary *) dictionaryByAddingEntriesFromDictionary:(NSDictionary *)dictionary;
@end

View File

@ -0,0 +1,95 @@
/*
NSDictionaryOOExtensions.m
Oolite
Copyright (C) 2004-2008 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
This file may also be distributed under the MIT/X11 license:
Copyright (C) 2008 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "NSDictionaryOOExtensions.h"
@implementation NSDictionary (OOExtensions)
- (NSDictionary *) dictionaryByAddingObject:(id)object forKey:(id)key
{
// Note: object lifetime issues aside, we need to copy and autorelease so that the right thing happens for mutable dictionaries.
if (object == nil || key == nil) return [[self copy] autorelease];
NSMutableDictionary *temp = [self mutableCopy];
[temp setObject:object forKey:key];
NSDictionary *result = [[temp copy] autorelease];
[temp release];
return result;
}
- (NSDictionary *) dictionaryByRemovingObjectForKey:(id)key
{
// Note: object lifetime issues aside, we need to copy and autorelease so that the right thing happens for mutable dictionaries.
if (key == nil) return [[self copy] autorelease];
NSMutableDictionary *temp = [self mutableCopy];
[temp removeObjectForKey:key];
NSDictionary *result = [[temp copy] autorelease];
[temp release];
return result;
}
- (NSDictionary *) dictionaryByAddingEntriesFromDictionary:(NSDictionary *)dictionary
{
// Note: object lifetime issues aside, we need to copy and autorelease so that the right thing happens for mutable dictionaries.
if (dictionary == nil) return [[self copy] autorelease];
NSMutableDictionary *temp = [self mutableCopy];
[temp addEntriesFromDictionary:dictionary];
NSDictionary *result = [[temp copy] autorelease];
[temp release];
return result;
}
@end

View File

@ -64,7 +64,7 @@ static NSString * const kCacheKeyCaches = @"_caches";
enum enum
{ {
kEndianTagValue = 0x12345678UL, kEndianTagValue = 0x12345678UL,
kFormatVersionValue = 14 kFormatVersionValue = 15
}; };

View File

@ -265,11 +265,11 @@ enum
@implementation OOMusicController (Singleton) @implementation OOMusicController (Singleton)
/* Canonical singleton boilerplate. /* Canonical singleton boilerplate.
See Cocoa Fundamentals Guide: Creating a Singleton Instance. See Cocoa Fundamentals Guide: Creating a Singleton Instance.
See also +sharedController above. See also +sharedController above.
NOTE: assumes single-threaded access. NOTE: assumes single-threaded access.
*/ */
+ (id) allocWithZone:(NSZone *)inZone + (id) allocWithZone:(NSZone *)inZone
{ {

View File

@ -0,0 +1,99 @@
/*
OOProbabilitySet.h
A collection for selecting objects randomly, with probability weighting.
Probability weights can be 0 - an object may be in the set but not selectable.
Comes in mutable and immutable variants.
Performance characteristics:
* -randomObject, the primary method, is O(log n) for immutable
OOProbabilitySets and O(n) for mutable ones.
* -containsObject: and -probabilityForObject: are O(n). This could be
optimized, but there's currently no need.
Oolite
Copyright (C) 2004-2008 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
This file may also be distributed under the MIT/X11 license:
Copyright (C) 2008 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOCocoa.h"
@interface OOProbabilitySet: NSObject <NSCopying, NSMutableCopying>
+ (id) probabilitySet;
+ (id) probabilitySetWithObjects:(id *)objects weights:(float *)weights count:(unsigned long)count;
+ (id) probabilitySetWithPropertyListRepresentation:(NSDictionary *)plist;
- (id) init;
- (id) initWithObjects:(id *)objects weights:(float *)weights count:(unsigned)count;
- (id) initWithPropertyListRepresentation:(NSDictionary *)plist;
// propertyListRepresentation is only valid if objects are property list objects.
- (NSDictionary *) propertyListRepresentation;
- (unsigned long) count;
- (id) randomObject;
- (float) weightForObject:(id)object; // Returns -1 for unknown objects.
- (float) sumOfWeights;
- (NSArray *) allObjects;
@end
@interface OOProbabilitySet (OOExtendedProbabilitySet)
- (BOOL) containsObject:(id)object;
- (NSEnumerator *) objectEnumerator;
- (float) probabilityForObject:(id)object; // Returns -1 for unknown objects, or a value from 0 to 1 inclusive for known objects.
@end
@interface OOMutableProbabilitySet: OOProbabilitySet
- (void) setWeight:(float)weight forObject:(id)object; // Adds object if needed.
- (void) removeObject:(id)object;
@end

977
src/Core/OOProbabilitySet.m Normal file
View File

@ -0,0 +1,977 @@
/*
OOProbabilitySet.m
Oolite
Copyright (C) 2004-2008 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
This file may also be distributed under the MIT/X11 license:
Copyright (C) 2008 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
IMPLEMENTATION NOTES
OOProbabilitySet is implemented as a class cluster with two abstract classes,
two special-case implementations for immutable sets with zero or one object,
and two general implementations (one mutable and one immutable). The general
implementations are the only non-trivial ones.
The general immutable implementation, OOConcreteProbabilitySet, consists of
two parallel arrays, one of objects and one of cumulative weights. The
"cumulative weight" for an entry is the sum of its weight and the cumulative
weight of the entry to the left (i.e., with a lower index), with the implicit
entry -1 having a cumulative weight of 0. Since weight cannot be negative,
this means that cumulative weights increase to the right (not strictly
increasing, though, since weights may be zero). We can thus find an object
with a given cumulative weight through a binary search.
OOConcreteMutableProbabilitySet is a naïve implementation using arrays. It
could be optimized, but isn't expected to be used much except for building
sets that will then be immutablized.
*/
#import "OOProbabilitySet.h"
#import "OOFunctionAttributes.h"
#import "OOCollectionExtractors.h"
#import "legacy_random.h"
static NSString * const kObjectsKey = @"objects";
static NSString * const kWeightsKey = @"weights";
@protocol OOProbabilitySetEnumerable <NSObject>
- (id) privObjectAtIndex:(unsigned long)index;
@end
@interface OOProbabilitySet (OOPrivate)
// Designated initializer. This must be used by subclasses, since init is overriden for public use.
- (id) initPriv;
@end
@interface OOEmptyProbabilitySet: OOProbabilitySet
+ (OOEmptyProbabilitySet *) singleton;
@end
@interface OOSingleObjectProbabilitySet: OOProbabilitySet
{
id _object;
float _weight;
}
- (id) initWithObject:(id)object weight:(float)weight;
@end
@interface OOConcreteProbabilitySet: OOProbabilitySet <OOProbabilitySetEnumerable>
{
unsigned long _count;
id *_objects;
float *_cumulativeWeights; // Each cumulative weight is weight of object at this index + weight of all objects to left.
float _sumOfWeights;
}
@end
@interface OOConcreteMutableProbabilitySet: OOMutableProbabilitySet
{
NSMutableArray *_objects;
NSMutableArray *_weights;
float _sumOfWeights;
}
@end
@interface OOProbabilitySetEnumerator: NSEnumerator
{
id _enumerable;
unsigned long _index;
}
- (id) initWithEnumerable:(id<OOProbabilitySetEnumerable>)enumerable;
@end
static void ThrowAbstractionViolationException(id obj) GCC_ATTR((noreturn));
@implementation OOProbabilitySet
// Abstract class just tosses allocations over to concrete class, and throws exception if you try to use it directly.
+ (id) probabilitySet
{
return [OOEmptyProbabilitySet singleton];
}
+ (id) probabilitySetWithObjects:(id *)objects weights:(float *)weights count:(unsigned long)count
{
return [[[self alloc] initWithObjects:objects weights:weights count:count] autorelease];
}
+ (id) probabilitySetWithPropertyListRepresentation:(NSDictionary *)plist
{
return [[[self alloc] initWithPropertyListRepresentation:plist] autorelease];
}
- (id) init
{
[self release];
return [OOEmptyProbabilitySet singleton];
}
- (id) initWithObjects:(id *)objects weights:(float *)weights count:(unsigned)count
{
NSZone *zone = [self zone];
[self release];
self = nil;
// Zero objects: return empty-set singleton.
if (count == 0) return [OOEmptyProbabilitySet singleton];
// If count is not zero and one of the paramters is nil, we've got us a programming error.
if (objects == NULL || weights == NULL)
{
[NSException raise:NSInvalidArgumentException format:@"Attempt to create %@ with non-zero count but nil objects or weights.", @"OOProbabilitySet"];
}
// Single object: simple one-object set. Expected to be quite common.
if (count == 1) return [[OOSingleObjectProbabilitySet allocWithZone:zone] initWithObject:objects[0] weight:weights[0]];
// Otherwise, use general implementation.
return [[OOConcreteProbabilitySet allocWithZone:zone] initWithObjects:objects weights:weights count:count];
}
- (id) initWithPropertyListRepresentation:(NSDictionary *)plist
{
NSArray *objects = nil;
NSArray *weights = nil;
unsigned long i = 0, count = 0;
id *rawObjects = NULL;
float *rawWeights = NULL;
objects = [plist arrayForKey:kObjectsKey];
weights = [plist arrayForKey:kWeightsKey];
// Validate
if (objects == nil || weights == nil) return nil;
count = [objects count];
if (count != [weights count]) return nil;
// Extract contents.
rawObjects = malloc(sizeof *rawObjects * count);
rawWeights = malloc(sizeof *rawWeights * count);
if (rawObjects != NULL || rawWeights != NULL)
{
// Extract objects.
[objects getObjects:rawObjects];
// Extract and convert weights.
for (i = 0; i < count; ++i)
{
rawWeights[i] = fmaxf([weights floatAtIndex:i], 0.0f);
}
self = [self initWithObjects:rawObjects weights:rawWeights count:count];
}
else
{
self = nil;
}
// Clean up.
free(rawObjects);
free(rawWeights);
return self;
}
- (id) initPriv
{
return [super init];
}
- (NSString *) descriptionComponents
{
return [NSString stringWithFormat:@"count=%lu", [self count]];
}
- (NSDictionary *) propertyListRepresentation
{
ThrowAbstractionViolationException(self);
}
- (id) randomObject
{
ThrowAbstractionViolationException(self);
}
- (float) weightForObject:(id)object
{
ThrowAbstractionViolationException(self);
}
- (float) sumOfWeights
{
ThrowAbstractionViolationException(self);
}
- (unsigned long) count
{
ThrowAbstractionViolationException(self);
}
- (NSArray *) allObjects
{
ThrowAbstractionViolationException(self);
}
- (id) copyWithZone:(NSZone *)zone
{
if (zone == [self zone])
{
return [self retain];
}
else
{
return [[OOProbabilitySet allocWithZone:zone] initWithPropertyListRepresentation:[self propertyListRepresentation]];
}
}
- (id) mutableCopyWithZone:(NSZone *)zone
{
return [[OOMutableProbabilitySet allocWithZone:zone] initWithPropertyListRepresentation:[self propertyListRepresentation]];
}
@end
@implementation OOProbabilitySet (OOExtendedProbabilitySet)
- (BOOL) containsObject:(id)object
{
return [self weightForObject:object] >= 0.0f;
}
- (NSEnumerator *) objectEnumerator
{
return [[self allObjects] objectEnumerator];
}
- (float) probabilityForObject:(id)object
{
float weight = [self weightForObject:object];
if (weight > 0) weight /= [self sumOfWeights];
return weight;
}
@end
static OOEmptyProbabilitySet *sOOEmptyProbabilitySetSingleton = nil;
@implementation OOEmptyProbabilitySet: OOProbabilitySet
+ (OOEmptyProbabilitySet *) singleton
{
if (sOOEmptyProbabilitySetSingleton == nil)
{
[[self alloc] init];
}
return sOOEmptyProbabilitySetSingleton;
}
- (NSDictionary *) propertyListRepresentation
{
NSArray *empty = [NSArray array];
return [NSDictionary dictionaryWithObjectsAndKeys:empty, kObjectsKey, empty, kWeightsKey, nil];
}
- (id) randomObject
{
return nil;
}
- (float) weightForObject:(id)object
{
return -1.0f;
}
- (float) sumOfWeights
{
return 0.0f;
}
- (unsigned long) count
{
return 0;
}
- (NSArray *) allObjects
{
return [NSArray array];
}
@end
@implementation OOEmptyProbabilitySet (Singleton)
/* Canonical singleton boilerplate.
See Cocoa Fundamentals Guide: Creating a Singleton Instance.
See also +singleton above.
NOTE: assumes single-threaded access.
*/
+ (id) allocWithZone:(NSZone *)inZone
{
if (sOOEmptyProbabilitySetSingleton == nil)
{
sOOEmptyProbabilitySetSingleton = [super allocWithZone:inZone];
return sOOEmptyProbabilitySetSingleton;
}
return nil;
}
- (id) copyWithZone:(NSZone *)inZone
{
return self;
}
- (id) retain
{
return self;
}
- (unsigned) retainCount
{
return UINT_MAX;
}
- (void) release
{}
- (id) autorelease
{
return self;
}
@end
@implementation OOSingleObjectProbabilitySet: OOProbabilitySet
- (id) initWithObject:(id)object weight:(float)weight
{
if (object == nil)
{
[self release];
return nil;
}
if ((self = [super initPriv]))
{
_object = [object retain];
_weight = fmaxf(weight, 0.0f);
}
return self;
}
- (void) dealloc
{
[_object release];
[super dealloc];
}
- (NSDictionary *) propertyListRepresentation
{
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSArray arrayWithObject:_object], kObjectsKey,
[NSArray arrayWithObject:[NSNumber numberWithFloat:_weight]], kWeightsKey,
nil];
}
- (id) randomObject
{
return _object;
}
- (float) weightForObject:(id)object
{
if ([_object isEqual:object]) return _weight;
else return -1.0f;
}
- (float) sumOfWeights
{
return _weight;
}
- (unsigned long) count
{
return 1;
}
- (NSArray *) allObjects
{
return [NSArray arrayWithObject:_object];
}
@end
@implementation OOConcreteProbabilitySet
- (id) initWithObjects:(id *)objects weights:(float *)weights count:(unsigned)count
{
unsigned long i = 0;
float cuWeight = 0.0f;
assert(count > 1 && objects != NULL && weights != NULL);
if ((self = [super initPriv]))
{
// Allocate arrays
_objects = malloc(sizeof *objects * count);
_cumulativeWeights = malloc(sizeof *_cumulativeWeights * count);
if (_objects == NULL || _cumulativeWeights == NULL)
{
[self release];
return nil;
}
// Fill in arrays, retain objects, add up weights.
for (i = 0; i != count; ++i)
{
_objects[i] = [objects[i] retain];
cuWeight += weights[i];
_cumulativeWeights[i] = cuWeight;
}
_count = count;
_sumOfWeights = cuWeight;
}
return self;
}
- (void) dealloc
{
unsigned long i = 0;
if (_objects != NULL)
{
for (i = 0; i < _count; ++i)
{
[_objects[i] release];
}
free(_objects);
_objects = NULL;
}
if (_cumulativeWeights != NULL)
{
free(_cumulativeWeights);
_cumulativeWeights = NULL;
}
[super dealloc];
}
- (NSDictionary *) propertyListRepresentation
{
NSArray *objects = nil;
NSMutableArray *weights = nil;
float cuWeight = 0.0f, sum = 0.0f;
unsigned long i = 0;
objects = [NSArray arrayWithObjects:_objects count:_count];
weights = [NSMutableArray arrayWithCapacity:_count];
for (i = 0; i < _count; ++i)
{
cuWeight = _cumulativeWeights[i];
[weights addFloat:cuWeight - sum];
sum = cuWeight;
}
return [NSDictionary dictionaryWithObjectsAndKeys:
objects, kObjectsKey,
[[weights copy] autorelease], kWeightsKey,
nil];
}
- (unsigned long) count
{
return _count;
}
- (id) privObjectForWeight:(float)target
{
/* Select an object at random. This is a binary search in the cumulative
weights array. Since weights of zero are allowed, there may be several
objects with the same cumulative weight, in which case we select the
leftmost, i.e. the one where the delta is non-zero.
*/
unsigned long low = 0, high = _count - 1, idx = 0;
float weight;
while (low < high)
{
idx = (low + high) / 2;
weight = _cumulativeWeights[idx];
if (weight > target)
{
if (EXPECT_NOT(idx == 0)) break;
high = idx - 1;
}
else if (weight < target) low = idx + 1;
else break;
}
if (weight > target)
{
while (idx > 0 && _cumulativeWeights[idx - 1] >= target) --idx;
}
else
{
while (idx < (_count - 1) && _cumulativeWeights[idx] < target) ++idx;
}
assert(idx < _count);
id result = _objects[idx];
return result;
}
- (id) randomObject
{
if (_sumOfWeights <= 0.0f) return nil;
return [self privObjectForWeight:randf() * _sumOfWeights];
}
- (float) weightForObject:(id)object
{
unsigned long i;
// Can't have nil in collection.
if (object == nil) return -1.0f;
// Perform linear search, then get weight by subtracting cumulative weight from cumulative weight to left.
for (i = 0; i < _count; ++i)
{
if ([_objects[i] isEqual:object])
{
float leftWeight = (i != 0) ? _cumulativeWeights[i - 1] : 0.0f;
return _cumulativeWeights[i] - leftWeight;
}
}
// If we got here, object not found.
return -1.0f;
}
- (float) sumOfWeights
{
return _sumOfWeights;
}
- (NSArray *) allObjects
{
return [NSArray arrayWithObjects:_objects count:_count];
}
- (NSEnumerator *) objectEnumerator
{
return [[[OOProbabilitySetEnumerator alloc] initWithEnumerable:self] autorelease];
}
- (id) privObjectAtIndex:(unsigned long)index
{
return (index < _count) ? _objects[index] : nil;
}
@end
@implementation OOMutableProbabilitySet
+ (id) probabilitySet
{
return [[[OOConcreteMutableProbabilitySet alloc] initPriv] autorelease];
}
- (id) init
{
NSZone *zone = [self zone];
[self release];
return [[OOConcreteMutableProbabilitySet allocWithZone:zone] initPriv];
}
- (id) initWithObjects:(id *)objects weights:(float *)weights count:(unsigned)count
{
NSZone *zone = [self zone];
[self release];
return [[OOConcreteMutableProbabilitySet allocWithZone:zone] initWithObjects:objects weights:weights count:count];
}
- (id) initWithPropertyListRepresentation:(NSDictionary *)plist
{
NSZone *zone = [self zone];
[self release];
return [[OOConcreteMutableProbabilitySet allocWithZone:zone] initWithPropertyListRepresentation:plist];
}
- (id) copyWithZone:(NSZone *)zone
{
return [[OOProbabilitySet allocWithZone:zone] initWithPropertyListRepresentation:[self propertyListRepresentation]];
}
- (void) setWeight:(float)weight forObject:(id)object
{
ThrowAbstractionViolationException(self);
}
- (void) removeObject:(id)object
{
ThrowAbstractionViolationException(self);
}
@end
@implementation OOConcreteMutableProbabilitySet
- (id) initPriv
{
if ((self = [super initPriv]))
{
_objects = [[NSMutableArray alloc] init];
_weights = [[NSMutableArray alloc] init];
}
return self;
}
- (id) initWithObjects:(id *)objects weights:(float *)weights count:(unsigned)count
{
unsigned long i = 0;
// Validate parameters.
if (count != 0 && (objects == NULL || weights == NULL))
{
[self release];
[NSException raise:NSInvalidArgumentException format:@"Attempt to create %@ with non-zero count but nil objects or weights.", @"OOMutableProbabilitySet"];
}
// Set up & go.
if ((self == [self initPriv]))
{
for (i = 0; i != count; ++i)
{
[self setWeight:fmaxf(weights[i], 0.0f) forObject:objects[i]];
}
}
return self;
}
- (id) initWithPropertyListRepresentation:(NSDictionary *)plist
{
BOOL OK = YES;
NSArray *objects = nil;
NSArray *weights = nil;
unsigned long i = 0, count = 0;
if (!(self = [super initPriv])) OK = NO;
if (OK)
{
objects = [plist arrayForKey:kObjectsKey];
weights = [plist arrayForKey:kWeightsKey];
// Validate
if (objects == nil || weights == nil) OK = NO;
count = [objects count];
if (count != [weights count]) OK = NO;
}
if (OK)
{
for (i = 0; i <= count; ++i)
{
[self setWeight:[weights floatAtIndex:i] forObject:[objects objectAtIndex:i]];
}
}
if (!OK)
{
[self release];
self = nil;
}
return self;
}
- (void) dealloc
{
[_objects release];
[_weights release];
[super dealloc];
}
- (NSDictionary *) propertyListRepresentation
{
return [NSDictionary dictionaryWithObjectsAndKeys:
_objects, kObjectsKey,
_weights, kWeightsKey,
nil];
}
- (unsigned long) count
{
return [_objects count];
}
- (id) randomObject
{
float target = 0.0f, sum = 0.0f;
unsigned i = 0, count = 0;
target = randf() * _sumOfWeights;
count = [_objects count];
if (count == 0 || _sumOfWeights <= 0.0f) return nil;
for (i = 0; i < count; ++i)
{
sum += [_weights floatAtIndex:i];
if (sum >= target) return [_objects objectAtIndex:i];
}
OOLog(@"probabilitySet.broken", @"%s fell off end, returning first object. Sum = %f, target = %f, count = %u. This is an internal error, please report it.", __PRETTY_FUNCTION__, _sumOfWeights, target, count);
return [_objects objectAtIndex:0];
}
- (float) weightForObject:(id)object
{
float result = -1.0f;
if (object != nil)
{
unsigned long index = [_objects indexOfObject:object];
if (index != NSNotFound)
{
result = [_weights floatAtIndex:index];
if (index != 0) result -= [_weights floatAtIndex:index - 1];
}
}
return result;
}
- (float) sumOfWeights
{
return _sumOfWeights;
}
- (NSArray *) allObjects
{
return [[_objects copy] autorelease];
}
- (NSEnumerator *) objectEnumerator
{
return [_objects objectEnumerator];
}
- (void) setWeight:(float)weight forObject:(id)object
{
if (object == nil) return;
weight = fmaxf(weight, 0.0f);
unsigned long index = [_objects indexOfObject:object];
if (index == NSNotFound)
{
[_objects addObject:object];
[_weights addFloat:weight];
_sumOfWeights += weight;
}
else
{
_sumOfWeights -= [_weights floatAtIndex:index];
_sumOfWeights += weight;
[_weights replaceObjectAtIndex:index withObject:[NSNumber numberWithFloat:weight]];
}
}
- (void) removeObject:(id)object
{
if (object != nil) return;
unsigned long index = [_objects indexOfObject:object];
if (index != NSNotFound)
{
[_objects removeObjectAtIndex:index];
_sumOfWeights -= [_weights floatAtIndex:index];
[_weights removeObjectAtIndex:index];
}
}
@end
@implementation OOProbabilitySetEnumerator
- (id) initWithEnumerable:(id<OOProbabilitySetEnumerable>)enumerable
{
if ((self = [super init]))
{
_enumerable = [enumerable retain];
}
return self;
}
- (void) dealloc
{
[_enumerable release];
[super dealloc];
}
- (id) nextObject
{
if (_index < [_enumerable count])
{
return [_enumerable privObjectAtIndex:_index++];
}
else
{
[_enumerable release];
_enumerable = nil;
return nil;
}
}
@end
static void ThrowAbstractionViolationException(id obj)
{
[NSException raise:NSGenericException format:@"Attempt to use abstract class %@ - this indicates an incorrect initialization.", [obj class]];
abort(); // unreachable
}

View File

@ -86,3 +86,7 @@ SOFTWARE.
- (id)roleSetWithRemovedRole:(NSString *)role; - (id)roleSetWithRemovedRole:(NSString *)role;
@end @end
// Returns a dictionary whose keys are roles and whose values are weights.
NSDictionary *OOParseRolesFromString(NSString *string);

View File

@ -56,7 +56,6 @@ SOFTWARE.
@interface OORoleSet (OOPrivate) @interface OORoleSet (OOPrivate)
- (id)initWithRolesAndProbabilities:(NSDictionary *)dict; - (id)initWithRolesAndProbabilities:(NSDictionary *)dict;
- (NSDictionary *)parseRolesFromString:(NSString *)string;
@end @end
@ -78,7 +77,7 @@ SOFTWARE.
{ {
NSDictionary *dict = nil; NSDictionary *dict = nil;
dict = [self parseRolesFromString:roleString]; dict = OOParseRolesFromString(roleString);
return [self initWithRolesAndProbabilities:dict]; return [self initWithRolesAndProbabilities:dict];
} }
@ -314,8 +313,10 @@ SOFTWARE.
return self; return self;
} }
@end
- (NSDictionary *)parseRolesFromString:(NSString *)string
NSDictionary *OOParseRolesFromString(NSString *string)
{ {
NSMutableDictionary *result = nil; NSMutableDictionary *result = nil;
NSArray *tokens = nil; NSArray *tokens = nil;
@ -359,5 +360,3 @@ SOFTWARE.
if ([result count] == 0) result = nil; if ([result count] == 0) result = nil;
return result; return result;
} }
@end

71
src/Core/OOShipRegistry.h Normal file
View File

@ -0,0 +1,71 @@
/*
OOShipRegistry.h
Manage the set of installed ships.
Oolite
Copyright (C) 2004-2008 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
This file may also be distributed under the MIT/X11 license:
Copyright (C) 2008 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOCocoa.h"
@interface OOShipRegistry: NSObject
{
NSDictionary *_shipData;
NSArray *_demoShips;
NSSet *_playerShips;
NSDictionary *_probabilitySets;
}
+ (OOShipRegistry *) sharedRegistry;
- (NSDictionary *) shipInfoForKey:(NSString *)key;
- (NSArray *) shipKeysWithRole:(NSString *)role;
- (NSString *) randomShipKeyForRole:(NSString *)role;
- (NSArray *) demoShipKeys;
- (NSSet *) playerShipKeys;
@end

625
src/Core/OOShipRegistry.m Normal file
View File

@ -0,0 +1,625 @@
/*
OOShipRegistry.m
Oolite
Copyright (C) 2004-2008 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
This file may also be distributed under the MIT/X11 license:
Copyright (C) 2008 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOShipRegistry.h"
#import "OOCacheManager.h"
#import "ResourceManager.h"
#import "OOCollectionExtractors.h"
#import "NSDictionaryOOExtensions.h"
#import "OOProbabilitySet.h"
#import "OORoleSet.h"
static OOShipRegistry *sSingleton = nil;
static NSString * const kShipRegistryCacheName = @"ship registry";
static NSString * const kShipDataCacheKey = @"ship data";
static NSString * const kPlayerShipsCacheKey = @"player ships";
static NSString * const kDemoShipsCacheKey = @"demo ships";
static NSString * const kRoleWeightsCacheKey = @"role weights";
static NSString * const kDefaultDemoShip = @"coriolis-station";
@interface OOShipRegistry (Loader)
- (void) loadShipData;
- (void) loadDemoShips;
- (void) loadCachedRoleProbabilitySets;
- (void) buildRoleProbabilitySets;
- (BOOL) applyLikeShips:(NSMutableDictionary *)ioData;
- (NSDictionary *) mergeShip:(NSDictionary *)child withParent:(NSDictionary *)parent;
- (BOOL) loadAndMergeShipyard:(NSMutableDictionary *)ioData;
- (BOOL) loadAndApplyShipDataOverrides:(NSMutableDictionary *)ioData;
- (BOOL) isValidShipEntry:(NSDictionary *)shipEntry name:(NSString *)name;
- (void) mergeShipRoles:(NSString *)roles forShipKey:(NSString *)shipKey intoProbabilityMap:(NSMutableDictionary *)probabilitySets;
@end
@implementation OOShipRegistry
+ (OOShipRegistry *) sharedRegistry
{
if (sSingleton == nil)
{
[[self alloc] init];
}
return sSingleton;
}
- (id) init
{
if ((self = [super init]))
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
OOCacheManager *cache = [OOCacheManager sharedCache];
_shipData = [[cache objectForKey:kShipDataCacheKey inCache:kShipRegistryCacheName] retain];
_playerShips = [[NSSet setWithArray:[cache objectForKey:kPlayerShipsCacheKey inCache:kShipRegistryCacheName]] retain];
if ([_shipData count] == 0) // Don't accept nil or empty
{
[self loadShipData];
if ([_shipData count] == 0)
{
[NSException raise:@"OOShipRegistryLoadFailure" format:@"Could not load any ship data."];
}
if ([_playerShips count] == 0)
{
[NSException raise:@"OOShipRegistryLoadFailure" format:@"Could not load any player ships."];
}
}
_demoShips = [[cache objectForKey:kDemoShipsCacheKey inCache:kShipRegistryCacheName] retain];
if ([_demoShips count] == 0)
{
[self loadDemoShips];
if ([_demoShips count] == 0)
{
[NSException raise:@"OOShipRegistryLoadFailure" format:@"Could not load or synthesize any demo ships."];
}
}
[self loadCachedRoleProbabilitySets];
if (_probabilitySets == nil)
{
[self buildRoleProbabilitySets];
if ([_probabilitySets count] == 0)
{
[NSException raise:@"OOShipRegistryLoadFailure" format:@"Could not load or synthesize role probability sets."];
}
}
[pool release];
}
return self;
}
- (void) dealloc
{
[_shipData release];
[_demoShips release];
[_playerShips release];
[_probabilitySets release];
[super dealloc];
}
- (NSDictionary *) shipInfoForKey:(NSString *)key
{
return [_shipData objectForKey:key];
}
- (NSArray *) shipKeysWithRole:(NSString *)role
{
if (role == nil) return nil;
return [[_probabilitySets objectForKey:role] allObjects];
}
- (NSString *) randomShipKeyForRole:(NSString *)role
{
if (role == nil) return nil;
return [[_probabilitySets objectForKey:role] randomObject];
}
- (NSArray *) demoShipKeys
{
return _demoShips;
}
- (NSSet *) playerShipKeys
{
return _playerShips;
}
@end
@implementation OOShipRegistry (Loader)
/* -loadShipData
Load the data for all ships. This consists of five stages:
* Load merges shipdata.plist dictionary.
* Apply all like_ship entries.
* Load shipdata-overrides.plist and apply patches.
* Load shipyard.plist, add shipyard data into ship dictionaries, and
create _playerShips array.
* Build role->ship type probability sets.
*/
- (void) loadShipData
{
NSMutableDictionary *result = nil;
NSEnumerator *enumerator = nil;
NSString *key = nil;
NSDictionary *immutableResult = nil;
[_shipData release];
_shipData = nil;
[_playerShips release];
_playerShips = nil;
// Load shipdata.plist.
result = [[[ResourceManager dictionaryFromFilesNamed:@"shipdata.plist"
inFolder:@"Config"
mergeMode:MERGE_BASIC
cache:NO] mutableCopy] autorelease];
if (result == nil) return;
// Clean out any non-dictionaries.
for (enumerator = [result keyEnumerator]; (key = [enumerator nextObject]); )
{
if (![self isValidShipEntry:[result objectForKey:key] name:key])
{
[result removeObjectForKey:key];
}
}
// Resolve like_ship entries.
if (![self applyLikeShips:result]) return;
// Apply patches.
if (![self loadAndApplyShipDataOverrides:result]) return;
// Add shipyard entries into shipdata entries.
if (![self loadAndMergeShipyard:result]) return;
immutableResult = [[result copy] autorelease];
_shipData = [immutableResult retain];
[[OOCacheManager sharedCache] setObject:_shipData forKey:kShipDataCacheKey inCache:kShipRegistryCacheName];
}
/* -loadDemoShips
Load demoships.plist, and filter out non-existent ships. If no existing
ships remain, try adding coriolis; if this fails, add any ship in
shipdata.
*/
- (void) loadDemoShips
{
NSEnumerator *enumerator = nil;
NSString *key = nil;
NSMutableArray *demoShips = nil;
[_demoShips release];
_demoShips = nil;
demoShips = [[[ResourceManager arrayFromFilesNamed:@"demoships.plist"
inFolder:@"Config"
andMerge:YES] mutableCopy] autorelease];
for (enumerator = [demoShips objectEnumerator]; (key = [enumerator nextObject]); )
{
if (![key isKindOfClass:[NSString class]] || [self shipInfoForKey:key] == nil)
{
[demoShips removeObject:key];
}
}
if ([demoShips count] == 0)
{
if ([self shipInfoForKey:kDefaultDemoShip] != nil) [demoShips addObject:kDefaultDemoShip];
else [demoShips addObject:[[_shipData allKeys] objectAtIndex:0]];
}
_demoShips = [demoShips copy];
[[OOCacheManager sharedCache] setObject:_demoShips forKey:kDemoShipsCacheKey inCache:kShipRegistryCacheName];
}
- (void) loadCachedRoleProbabilitySets
{
NSDictionary *cachedSets = nil;
NSMutableDictionary *restoredSets = nil;
NSEnumerator *roleEnum = nil;
NSString *role = nil;
cachedSets = [[OOCacheManager sharedCache] objectForKey:kRoleWeightsCacheKey inCache:kShipRegistryCacheName];
if (cachedSets == nil) return;
restoredSets = [NSMutableDictionary dictionaryWithCapacity:[cachedSets count]];
for (roleEnum = [cachedSets keyEnumerator]; (role = [roleEnum nextObject]); )
{
[restoredSets setObject:[OOProbabilitySet probabilitySetWithPropertyListRepresentation:[cachedSets objectForKey:role]] forKey:role];
}
_probabilitySets = [restoredSets copy];
}
- (void) buildRoleProbabilitySets
{
NSMutableDictionary *probabilitySets = nil;
NSEnumerator *shipEnum = nil;
NSString *shipKey = nil;
NSDictionary *shipEntry = nil;
NSString *roles = nil;
NSEnumerator *roleEnum = nil;
NSString *role = nil;
OOProbabilitySet *pset = nil;
NSMutableDictionary *cacheEntry = nil;
probabilitySets = [NSMutableDictionary dictionary];
// Build role sets
for (shipEnum = [_shipData keyEnumerator]; (shipKey = [shipEnum nextObject]); )
{
shipEntry = [_shipData objectForKey:shipKey];
roles = [shipEntry stringForKey:@"roles"];
[self mergeShipRoles:roles forShipKey:shipKey intoProbabilityMap:probabilitySets];
}
// Convert role sets to immutable form, and build cache entry.
cacheEntry = [NSMutableDictionary dictionaryWithCapacity:[probabilitySets count]];
for (roleEnum = [probabilitySets keyEnumerator]; (role = [roleEnum nextObject]); )
{
pset = [probabilitySets objectForKey:role];
pset = [[pset copy] autorelease];
[probabilitySets setObject:pset forKey:role];
[cacheEntry setObject:[pset propertyListRepresentation] forKey:role];
}
_probabilitySets = [probabilitySets copy];
[[OOCacheManager sharedCache] setObject:cacheEntry forKey:kRoleWeightsCacheKey inCache:kShipRegistryCacheName];
}
/* -applyLikeShips:
Implement like_ship by copying inherited ship and overwriting with child
ship values. Done iteratively to report recursive references of arbitrary
depth. Also removes and reports ships whose like_ship entry does not
resolve, and handles reference loops by removing all ships involved.
We start with a set of keys all ships that have a like_ships entry. In
each iteration, every ship whose like_ship entry does not refer to a ship
which itself has a like_ship entry is finalized. If the set of pending
ships does not shrink in an iteration, the remaining ships cannot be
resolved (either their like_ships do not exist, or they form reference
cycles) so we stop looping and report it.
*/
- (BOOL) applyLikeShips:(NSMutableDictionary *)ioData
{
NSMutableSet *remainingLikeShips = nil;
NSEnumerator *enumerator = nil;
NSString *key = nil;
NSString *parentKey = nil;
NSDictionary *shipEntry = nil;
NSDictionary *parentEntry = nil;
unsigned count, lastCount;
// Build set of ships with like_ship references
remainingLikeShips = [NSMutableSet set];
for (enumerator = [ioData keyEnumerator]; (key = [enumerator nextObject]); )
{
shipEntry = [ioData objectForKey:key];
if ([shipEntry stringForKey:@"like_ship"] != nil)
{
[remainingLikeShips addObject:key];
}
}
count = lastCount = [remainingLikeShips count];
while (count != 0)
{
for (enumerator = [remainingLikeShips objectEnumerator]; (key = [enumerator nextObject]); )
{
// Look up like_ship entry
shipEntry = [ioData objectForKey:key];
parentKey = [shipEntry objectForKey:@"like_ship"];
if (![remainingLikeShips containsObject:parentKey])
{
// If parent is fully resolved, we can resolve this child.
parentEntry = [ioData objectForKey:parentKey];
shipEntry = [self mergeShip:shipEntry withParent:parentEntry];
if (shipEntry != nil)
{
[remainingLikeShips removeObject:key];
[ioData setObject:shipEntry forKey:key];
}
}
}
count = [remainingLikeShips count];
if (count == lastCount)
{
// Fail: we couldn't resolve all like_ship entries.
OOLog(@"shipData.merge.failed", @"***** ERROR: one or more shipdata.plist entries have like_ship references that cannot be resolved: %@", remainingLikeShips);
break;
}
lastCount = count;
}
return YES;
}
- (NSDictionary *) mergeShip:(NSDictionary *)child withParent:(NSDictionary *)parent
{
NSMutableDictionary *result = [[parent mutableCopy] autorelease];
if (result == nil) return nil;
[result addEntriesFromDictionary:child];
[result removeObjectForKey:@"like_ship"];
return [[result copy] autorelease];
}
- (BOOL) loadAndApplyShipDataOverrides:(NSMutableDictionary *)ioData
{
NSEnumerator *enumerator = nil;
NSString *key = nil;
NSDictionary *shipEntry = nil;
NSDictionary *overrides = nil;
NSDictionary *overridesEntry = nil;
overrides = [ResourceManager dictionaryFromFilesNamed:@"shipdata-overrides.plist"
inFolder:@"Config"
mergeMode:MERGE_SMART
cache:NO];
for (enumerator = [overrides keyEnumerator]; (key = [enumerator nextObject]); )
{
shipEntry = [ioData objectForKey:key];
if (shipEntry != nil)
{
overridesEntry = [overrides objectForKey:key];
if (![overridesEntry isKindOfClass:[NSDictionary class]])
{
OOLog(@"shipData.load.error", @"***** ERROR: the shipdata-overrides.plist entry \"%@\" is not a dictionary, ignoring.", key);
}
else
{
shipEntry = [shipEntry dictionaryByAddingEntriesFromDictionary:overridesEntry];
[ioData setObject:shipEntry forKey:key];
}
}
}
return YES;
}
/* -loadAndMergeShipyard:
Load shipyard.plist, add its entries to appropriate shipyard entries as
a dictionary under the key "shipyard", and build list of player ships.
Before that, we strip out any "shipyard" entries already in shipdata, and
apply any shipyard-overrides.plist stuff to shipyard.
*/
- (BOOL) loadAndMergeShipyard:(NSMutableDictionary *)ioData
{
NSEnumerator *enumerator = nil;
NSString *key = nil;
NSDictionary *shipEntry = nil;
NSDictionary *shipyard = nil;
NSDictionary *shipyardOverrides = nil;
NSDictionary *shipyardEntry = nil;
NSDictionary *shipyardOverridesEntry = nil;
NSMutableSet *playerShips = nil;
// Strip out any shipyard stuff in shipdata (there shouldn't be any).
for (enumerator = [ioData keyEnumerator]; (key = [enumerator nextObject]); )
{
shipEntry = [ioData objectForKey:key];
if ([shipEntry objectForKey:@"shipyard"] != nil)
{
[ioData setObject:[shipEntry dictionaryByRemovingObjectForKey:@"shipyard"] forKey:key];
}
}
shipyard = [ResourceManager dictionaryFromFilesNamed:@"shipyard.plist"
inFolder:@"Config"
mergeMode:MERGE_BASIC
cache:NO];
shipyardOverrides = [ResourceManager dictionaryFromFilesNamed:@"shipyard-overrides.plist"
inFolder:@"Config"
mergeMode:MERGE_SMART
cache:NO];
playerShips = [NSMutableSet setWithCapacity:[shipyard count]];
// Insert merged shipyard and shipyardOverrides entries.
for (enumerator = [shipyard keyEnumerator]; (key = [enumerator nextObject]); )
{
shipEntry = [ioData objectForKey:key];
if (shipEntry != nil)
{
shipyardEntry = [shipyard objectForKey:key];
shipyardOverridesEntry = [shipyardOverrides objectForKey:key];
shipyardEntry = [shipyardEntry dictionaryByAddingEntriesFromDictionary:shipyardOverridesEntry];
shipEntry = [shipEntry dictionaryByAddingObject:shipyardEntry forKey:@"shipyard"];
[ioData setObject:shipEntry forKey:key];
[playerShips addObject:key];
}
// Else we have a shipyard entry with no matching shipdata entry, which we ignore.
}
_playerShips = [playerShips copy];
[[OOCacheManager sharedCache] setObject:[_playerShips allObjects] forKey:kPlayerShipsCacheKey inCache:kShipRegistryCacheName];
return YES;
}
- (BOOL) isValidShipEntry:(NSDictionary *)shipEntry name:(NSString *)name
{
// Quick checks for obvious problems. Not complete validation, just basic sanity checking.
if (![shipEntry isKindOfClass:[NSDictionary class]])
{
OOLog(@"shipData.load.badEntry", @"***** ERROR: the shipdata.plist entry \"%@\" is not a dictionary, ignoring.", name);
return NO;
}
if ([shipEntry stringForKey:@"like_ship"] == nil) // Keys may be inherited, so we only check "root" ships.
{
if ([[shipEntry stringForKey:@"roles"] length] == 0)
{
OOLog(@"shipData.load.error", @"***** ERROR: the shipdata.plist entry \"%@\" specifies no %@, ignoring.", name, @"roles");
return NO;
}
if ([[shipEntry stringForKey:@"model"] length] == 0)
{
OOLog(@"shipData.load.error", @"***** ERROR: the shipdata.plist entry \"%@\" specifies no %@, ignoring.", name, @"model");
return NO;
}
}
return YES;
}
- (void) mergeShipRoles:(NSString *)roles
forShipKey:(NSString *)shipKey
intoProbabilityMap:(NSMutableDictionary *)probabilitySets
{
NSDictionary *rolesAndWeights = nil;
NSEnumerator *roleEnum = nil;
NSString *role = nil;
OOMutableProbabilitySet *probSet = nil;
/* probabilitySets is a dictionary whose keys are roles and whose values
are mutable probability sets, whose values are ship keys.
*/
rolesAndWeights = OOParseRolesFromString(roles);
for (roleEnum = [rolesAndWeights keyEnumerator]; (role = [roleEnum nextObject]); )
{
probSet = [probabilitySets objectForKey:role];
if (probSet == nil)
{
probSet = [OOMutableProbabilitySet probabilitySet];
[probabilitySets setObject:probSet forKey:role];
}
[probSet setWeight:[rolesAndWeights floatForKey:role] forObject:shipKey];
}
}
@end
@implementation OOShipRegistry (Singleton)
/* Canonical singleton boilerplate.
See Cocoa Fundamentals Guide: Creating a Singleton Instance.
See also +sharedRegistry above.
NOTE: assumes single-threaded access.
*/
+ (id) allocWithZone:(NSZone *)inZone
{
if (sSingleton == nil)
{
sSingleton = [super allocWithZone:inZone];
return sSingleton;
}
return nil;
}
- (id) copyWithZone:(NSZone *)inZone
{
return self;
}
- (id) retain
{
return self;
}
- (unsigned) retainCount
{
return UINT_MAX;
}
- (void) release
{}
- (id) autorelease
{
return self;
}
@end

View File

@ -385,7 +385,10 @@ static JSBool TimerConstruct(JSContext *context, JSObject *inThis, uintN argc, j
function:function function:function
this:this]; this:this];
*outResult = [timer javaScriptValueInContext:context]; *outResult = [timer javaScriptValueInContext:context];
if (delay >= 0) [timer scheduleTimer]; if (delay >= 0) // Leave in stopped state if delay is negative
{
[timer scheduleTimer];
}
[timer release]; // The JS object retains the ObjC object. [timer release]; // The JS object retains the ObjC object.
return YES; return YES;

View File

@ -49,6 +49,7 @@ MA 02110-1301, USA.
#import "OOEntityFilterPredicate.h" #import "OOEntityFilterPredicate.h"
#import "OOCharacter.h" #import "OOCharacter.h"
#import "OOShipRegistry.h"
#import "PlayerEntity.h" #import "PlayerEntity.h"
#import "PlayerEntityContracts.h" #import "PlayerEntityContracts.h"
@ -162,6 +163,9 @@ static NSComparisonResult comparePrice(NSDictionary *dict1, NSDictionary *dict2,
//Jester Speech End //Jester Speech End
#endif #endif
// Load ship data
[OOShipRegistry sharedRegistry];
dumpCollisionInfo = NO; dumpCollisionInfo = NO;
next_universal_id = 100; // start arbitrarily above zero next_universal_id = 100; // start arbitrarily above zero
for (i = 0; i < MAX_ENTITY_UID; i++) for (i = 0; i < MAX_ENTITY_UID; i++)