Subentity definitions can now be dictionaries, and this is now used as the internal format. Ship registry now uses mutable dictionaries for entries while working on them for simplicity, and uniques strings and numbers once done for efficiency (in the same way binary plist generation does for the cache... I really should double-check that GNUstep does that). Also simplified handling of external views, fixing a crashing bug (triggered the third time the player died) in the process.

git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@2051 127b21dd-08f5-0310-b4b7-95ae10353056
This commit is contained in:
Jens Ayton 2009-02-11 17:37:20 +00:00
parent f8b348be25
commit 557a353cc4
16 changed files with 1117 additions and 294 deletions

View File

@ -207,7 +207,8 @@ OO_UTILITY_FILES = \
OOShipGroup.m \ OOShipGroup.m \
OOStringParsing.m \ OOStringParsing.m \
OOWeakReference.m \ OOWeakReference.m \
OOXMLExtensions.m OOXMLExtensions.m \
OODeepCopy.m
OOLITE_MISC_FILES = \ OOLITE_MISC_FILES = \
AI.m \ AI.m \

View File

@ -577,6 +577,8 @@
1AD1F4FF0CD9E83700EAE520 /* NSThreadOOExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AD1F4C80CD9E42A00EAE520 /* NSThreadOOExtensions.m */; }; 1AD1F4FF0CD9E83700EAE520 /* NSThreadOOExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AD1F4C80CD9E42A00EAE520 /* NSThreadOOExtensions.m */; };
1AD1F5000CD9E83800EAE520 /* NSThreadOOExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1AD1F4C70CD9E42A00EAE520 /* NSThreadOOExtensions.h */; }; 1AD1F5000CD9E83800EAE520 /* NSThreadOOExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1AD1F4C70CD9E42A00EAE520 /* NSThreadOOExtensions.h */; };
1AD267650C83058C00B4BFD1 /* Debug.oxp in Copy Debug OXP */ = {isa = PBXBuildFile; fileRef = 1A0519390C7CCAC900BA5CCA /* Debug.oxp */; }; 1AD267650C83058C00B4BFD1 /* Debug.oxp in Copy Debug OXP */ = {isa = PBXBuildFile; fileRef = 1A0519390C7CCAC900BA5CCA /* Debug.oxp */; };
1ADA8AB30F42DBA80001BEC9 /* OODeepCopy.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ADA8AB10F42DBA80001BEC9 /* OODeepCopy.h */; };
1ADA8AB40F42DBA80001BEC9 /* OODeepCopy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ADA8AB20F42DBA80001BEC9 /* OODeepCopy.m */; };
1ADBA5500BD0F173008FC99C /* OOBasicMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ADBA54E0BD0F173008FC99C /* OOBasicMaterial.h */; }; 1ADBA5500BD0F173008FC99C /* OOBasicMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ADBA54E0BD0F173008FC99C /* OOBasicMaterial.h */; };
1ADBA5510BD0F173008FC99C /* OOBasicMaterial.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ADBA54F0BD0F173008FC99C /* OOBasicMaterial.m */; }; 1ADBA5510BD0F173008FC99C /* OOBasicMaterial.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ADBA54F0BD0F173008FC99C /* OOBasicMaterial.m */; };
1ADF5CEC0B9DF59A00FDB2A3 /* OOCacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A231A170B9D8B1B00EF0852 /* OOCacheManager.m */; }; 1ADF5CEC0B9DF59A00FDB2A3 /* OOCacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A231A170B9D8B1B00EF0852 /* OOCacheManager.m */; };
@ -1630,6 +1632,8 @@
1AD1F4C70CD9E42A00EAE520 /* NSThreadOOExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSThreadOOExtensions.h; sourceTree = "<group>"; }; 1AD1F4C70CD9E42A00EAE520 /* NSThreadOOExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSThreadOOExtensions.h; sourceTree = "<group>"; };
1AD1F4C80CD9E42A00EAE520 /* NSThreadOOExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSThreadOOExtensions.m; sourceTree = "<group>"; }; 1AD1F4C80CD9E42A00EAE520 /* NSThreadOOExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSThreadOOExtensions.m; sourceTree = "<group>"; };
1AD6B3280E3BB55E001C42D9 /* debug-exports-64.exp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.exports; path = "debug-exports-64.exp"; sourceTree = "<group>"; }; 1AD6B3280E3BB55E001C42D9 /* debug-exports-64.exp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.exports; path = "debug-exports-64.exp"; sourceTree = "<group>"; };
1ADA8AB10F42DBA80001BEC9 /* OODeepCopy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OODeepCopy.h; sourceTree = "<group>"; };
1ADA8AB20F42DBA80001BEC9 /* OODeepCopy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OODeepCopy.m; sourceTree = "<group>"; };
1ADBA54E0BD0F173008FC99C /* OOBasicMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOBasicMaterial.h; sourceTree = "<group>"; }; 1ADBA54E0BD0F173008FC99C /* OOBasicMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOBasicMaterial.h; sourceTree = "<group>"; };
1ADBA54F0BD0F173008FC99C /* OOBasicMaterial.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOBasicMaterial.m; sourceTree = "<group>"; }; 1ADBA54F0BD0F173008FC99C /* OOBasicMaterial.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOBasicMaterial.m; sourceTree = "<group>"; };
1ADF5F110B9E374B00FDB2A3 /* JoystickHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JoystickHandler.h; sourceTree = "<group>"; }; 1ADF5F110B9E374B00FDB2A3 /* JoystickHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JoystickHandler.h; sourceTree = "<group>"; };
@ -2510,6 +2514,8 @@
1A1616610D7DCFDC0094AE5B /* OOFilteringEnumerator.m */, 1A1616610D7DCFDC0094AE5B /* OOFilteringEnumerator.m */,
1A047B7C0DCB3D7500EE1CD0 /* OOProbabilitySet.h */, 1A047B7C0DCB3D7500EE1CD0 /* OOProbabilitySet.h */,
1A047B7D0DCB3D7500EE1CD0 /* OOProbabilitySet.m */, 1A047B7D0DCB3D7500EE1CD0 /* OOProbabilitySet.m */,
1ADA8AB10F42DBA80001BEC9 /* OODeepCopy.h */,
1ADA8AB20F42DBA80001BEC9 /* OODeepCopy.m */,
); );
name = Utilities; name = Utilities;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3000,6 +3006,7 @@
1ABAD7320F350B3400FD2CBF /* OOShipGroup.h in Headers */, 1ABAD7320F350B3400FD2CBF /* OOShipGroup.h in Headers */,
1A11F84B0F35F60C001C886C /* OOJSShipGroup.h in Headers */, 1A11F84B0F35F60C001C886C /* OOJSShipGroup.h in Headers */,
1A20F7060F36EE0500156DE9 /* OOExcludeObjectEnumerator.h in Headers */, 1A20F7060F36EE0500156DE9 /* OOExcludeObjectEnumerator.h in Headers */,
1ADA8AB30F42DBA80001BEC9 /* OODeepCopy.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -3364,6 +3371,7 @@
1ABAD7310F350B3400FD2CBF /* OOShipGroup.m in Sources */, 1ABAD7310F350B3400FD2CBF /* OOShipGroup.m in Sources */,
1A11F84A0F35F60C001C886C /* OOJSShipGroup.m in Sources */, 1A11F84A0F35F60C001C886C /* OOJSShipGroup.m in Sources */,
1A20F7070F36EE0500156DE9 /* OOExcludeObjectEnumerator.m in Sources */, 1A20F7070F36EE0500156DE9 /* OOExcludeObjectEnumerator.m in Sources */,
1ADA8AB40F42DBA80001BEC9 /* OODeepCopy.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -319,6 +319,7 @@
shipData.load.done = no; shipData.load.done = no;
shipData.load.error = $error; shipData.load.error = $error;
shipData.load.warning = $error; shipData.load.warning = $error;
shipData.translateSubentity = no;
shipRegistry.selection.profile = no; shipRegistry.selection.profile = no;

View File

@ -67,9 +67,6 @@
// "setBackgroundFromDescriptionsKey:", // Is this ever used from a script? It seems a bad match for future plans of mine. -- Ahruman // "setBackgroundFromDescriptionsKey:", // Is this ever used from a script? It seems a bad match for future plans of mine. -- Ahruman
"setGalacticHyperspaceBehaviourTo:", "setGalacticHyperspaceBehaviourTo:",
"setGalacticHyperspaceFixedCoordsTo:", "setGalacticHyperspaceFixedCoordsTo:",
// ShipEntity.m
"initialiseTurret"
); );
ai_methods = ai_methods =
( (
@ -362,6 +359,7 @@
action_method_aliases = action_method_aliases =
{ {
"setGuiToStatusScreen" = "doNothing"; "setGuiToStatusScreen" = "doNothing";
"initialiseTurret" = "doNothing";
}; };
query_method_aliases = query_method_aliases =

View File

@ -281,10 +281,10 @@
type = "dictionary"; type = "dictionary";
schema = schema =
{ {
view_description = "string"; "view_description" = "string";
view_position = "vector"; "view_position" = "vector";
view_orientation = "quaternion"; "view_orientation" = "quaternion";
weapon_facing = "weapon_facing" =
{ {
type = "enumeration"; type = "enumeration";
filter = "lowerCase"; filter = "lowerCase";
@ -373,9 +373,55 @@
}; };
$subEntitySpecifier = $subEntitySpecifier =
{ {
type = "delegatedType"; type = "oneOf";
baseType = "string"; options =
key = "subEntitySpecifier"; (
{
type = "delegatedType";
baseType = "string";
key = "subEntitySpecifier";
},
{
type = "dictionary";
requiredKeys = ( "type", "subentity_key" );
schema =
{
"type" =
{
type = "enumeration";
values = ( "standard", "ball_turret" );
};
subentity_key = "$shipKey";
position = "vector";
orientation = "quaternion";
is_dock = "boolean";
};
},
{
type = "dictionary";
requiredKeys = ( "type" );
schema =
{
"type" =
{
type = "enumeration";
values = ( "flasher" );
};
color = "$colorSpecifier";
colors =
{
type = array;
valueType = "$colorSpecifier";
};
position = "vector";
orientation = "quaternion";
size = "positiveFloat";
frequency = "float";
phase = "float";
initially_on = "boolean";
};
}
);
}; };
$scriptActions = $scriptActions =
{ {

View File

@ -412,7 +412,6 @@ typedef enum
int target_memory_index; int target_memory_index;
// custom view points // custom view points
NSMutableArray *custom_views;
Quaternion customViewQuaternion; Quaternion customViewQuaternion;
OOMatrix customViewMatrix; OOMatrix customViewMatrix;
Vector customViewOffset, customViewForwardVector, customViewUpVector, customViewRightVector; Vector customViewOffset, customViewForwardVector, customViewUpVector, customViewRightVector;
@ -472,6 +471,10 @@ waitingForStickCallback: 1;
OOGalacticHyperspaceBehaviour galacticHyperspaceBehaviour; OOGalacticHyperspaceBehaviour galacticHyperspaceBehaviour;
NSPoint galacticHyperspaceFixedCoords; NSPoint galacticHyperspaceFixedCoords;
@private
NSArray *_customViews;
OOUInteger _customViewIndex;
#ifdef DOCKING_CLEARANCE_ENABLED #ifdef DOCKING_CLEARANCE_ENABLED
OODockingClearanceStatus dockingClearanceStatus; OODockingClearanceStatus dockingClearanceStatus;
#endif #endif

View File

@ -828,8 +828,9 @@ static PlayerEntity *sSharedPlayer = nil;
isSpeechOn = NO; isSpeechOn = NO;
[custom_views release]; [_customViews release];
custom_views = nil; _customViews = nil;
_customViewIndex = 0;
mouse_control_on = NO; mouse_control_on = NO;
@ -1067,8 +1068,9 @@ static PlayerEntity *sSharedPlayer = nil;
NSArray *customViews = [shipDict arrayForKey:@"custom_views"]; NSArray *customViews = [shipDict arrayForKey:@"custom_views"];
if (customViews != nil) if (customViews != nil)
{ {
[custom_views release]; [_customViews release];
custom_views = [customViews mutableCopy]; // FIXME: This is mutable because it's used as a queue rather than using an index. Silly, fix. -- Ahruman _customViews = [customViews retain];
_customViewIndex = 0;
} }
// set weapon offsets // set weapon offsets
@ -1132,7 +1134,7 @@ static PlayerEntity *sSharedPlayer = nil;
[save_path release]; [save_path release];
[custom_views release]; [_customViews release];
[dockingReport release]; [dockingReport release];
@ -6541,12 +6543,6 @@ static int last_outfitting_index;
} }
- (void) initialiseTurret
{
OOLog(@"script.invalid", @"SCRIPT ERROR: attempt to call initialiseTurret for player.");
}
#ifdef DOCKING_CLEARANCE_ENABLED #ifdef DOCKING_CLEARANCE_ENABLED
- (BOOL)clearedToDock - (BOOL)clearedToDock
{ {

View File

@ -2189,16 +2189,15 @@ static NSTimeInterval time_last_frame;
if ([gameView isDown:key_custom_view]) if ([gameView isDown:key_custom_view])
{ {
if ((!customView_pressed)&&(custom_views)&&(![UNIVERSE displayCursor])) if (!customView_pressed && [_customViews count] != 0 && ![UNIVERSE displayCursor])
{ {
if ([UNIVERSE viewDirection] == VIEW_CUSTOM) // already in custom view mode if ([UNIVERSE viewDirection] == VIEW_CUSTOM) // already in custom view mode
{ {
// rotate the custom views // rotate the custom views
[custom_views addObject:[custom_views objectAtIndex:0]]; _customViewIndex = (_customViewIndex + 1) % [_customViews count];
[custom_views removeObjectAtIndex:0];
} }
[self setCustomViewDataFromDictionary:(NSDictionary*)[custom_views objectAtIndex:0]]; [self setCustomViewDataFromDictionary:[_customViews dictionaryAtIndex:_customViewIndex]];
if ([UNIVERSE displayGUI]) if ([UNIVERSE displayGUI])
[self switchToMainView]; [self switchToMainView];

View File

@ -211,7 +211,7 @@ MA 02110-1301, USA.
NSMutableDictionary *previousCondition; // restored after collision avoidance NSMutableDictionary *previousCondition; // restored after collision avoidance
// derived variables // derived variables
double weapon_recharge_rate; // time between shots float weapon_recharge_rate; // time between shots
int shot_counter; // number of shots fired int shot_counter; // number of shots fired
double cargo_dump_time; // time cargo was last dumped double cargo_dump_time; // time cargo was last dumped
@ -503,7 +503,9 @@ MA 02110-1301, USA.
- (GLfloat) weaponRange; - (GLfloat) weaponRange;
- (void) setWeaponRange:(GLfloat) value; - (void) setWeaponRange:(GLfloat) value;
- (void) setWeaponDataFromType:(int) weapon_type; - (void) setWeaponDataFromType:(OOWeaponType)weapon_type;
- (float) weaponRechargeRate;
- (void) setWeaponRechargeRate:(float)value;
- (GLfloat) scannerRange; - (GLfloat) scannerRange;
- (void) setScannerRange: (GLfloat) value; - (void) setScannerRange: (GLfloat) value;

View File

@ -90,6 +90,11 @@ static NSString * const kOOLogEntityBehaviourChanged = @"entity.behaviour.change
- (void) rescaleBy:(GLfloat)factor; - (void) rescaleBy:(GLfloat)factor;
- (BOOL) setUpOneSubentity:(NSDictionary *) subentDict;
- (BOOL) setUpOneFlasher:(NSDictionary *) subentDict;
- (BOOL) setUpOneStandardSubentity:(NSDictionary *) subentDict asTurret:(BOOL)asTurret;
@end @end
@ -143,88 +148,6 @@ static NSString * const kOOLogEntityBehaviourChanged = @"entity.behaviour.change
} }
- (BOOL) setUpSubEntities: (NSDictionary *) shipDict
{
unsigned int i;
NSArray *plumes = [shipDict arrayForKey:@"exhaust"];
for (i = 0; i < [plumes count]; i++)
{
ParticleEntity *exhaust = [[ParticleEntity alloc] initExhaustFromShip:self details:[plumes objectAtIndex:i]];
[self addExhaust:exhaust];
[exhaust release];
}
NSArray *subs = [shipDict arrayForKey:@"subentities"];
for (i = 0; i < [subs count]; i++)
{
NSArray *details = ScanTokensFromString([subs objectAtIndex:i]);
if ([details count] == 8)
{
Vector sub_pos;
sub_pos.x = [details floatAtIndex:1];
sub_pos.y = [details floatAtIndex:2];
sub_pos.z = [details floatAtIndex:3];
Quaternion sub_q;
sub_q.w = [details floatAtIndex:4];
sub_q.x = [details floatAtIndex:5];
sub_q.y = [details floatAtIndex:6];
sub_q.z = [details floatAtIndex:7];
NSString* subdesc = [details stringAtIndex:0];
if ([subdesc isEqual:@"*FLASHER*"])
{
ParticleEntity *flasher;
flasher = [[ParticleEntity alloc]
initFlasherWithSize:sub_q.z
frequency:sub_q.x
phase:2.0 * sub_q.y];
[flasher setColor:[OOColor colorWithCalibratedHue:sub_q.w/360.0
saturation:1.0
brightness:1.0
alpha:1.0]];
[flasher setPosition:sub_pos];
[self addFlasher:flasher];
[flasher release];
}
else
{
quaternion_normalize(&sub_q);
ShipEntity* subent = [UNIVERSE newShipWithName:subdesc]; // retained
if (subent == nil)
{
// Failing to find a subentity could result in a partial ship, which'd be, y'know, weird.
OOLog(@"ship.sanityCheck.failed", @"Ship %@ generated with missing subentity %@!", self, subdesc);
return NO;
}
if (self->isStation && [subdesc rangeOfString:@"dock"].location != NSNotFound)
{
[(StationEntity*)self setDockingPortModel:subent :sub_pos :sub_q];
}
[subent setStatus:STATUS_INACTIVE];
Vector ref = vector_forward_from_quaternion(sub_q); // VECTOR FORWARD
[subent setReference: ref];
[subent setPosition: sub_pos];
[subent setOrientation: sub_q];
[self addSolidSubentityToCollisionRadius:subent];
[self addSubEntity:subent];
[subent release];
}
}
}
return YES;
}
- (BOOL) setUpShipFromDictionary:(NSDictionary *) dict - (BOOL) setUpShipFromDictionary:(NSDictionary *) dict
{ {
NSDictionary *shipDict = dict; NSDictionary *shipDict = dict;
@ -483,6 +406,110 @@ static NSString * const kOOLogEntityBehaviourChanged = @"entity.behaviour.change
} }
- (BOOL) setUpSubEntities: (NSDictionary *) shipDict
{
unsigned int i;
NSArray *plumes = [shipDict arrayForKey:@"exhaust"];
for (i = 0; i < [plumes count]; i++)
{
ParticleEntity *exhaust = [[ParticleEntity alloc] initExhaustFromShip:self details:[plumes objectAtIndex:i]];
[self addExhaust:exhaust];
[exhaust release];
}
NSArray *subs = [shipDict arrayForKey:@"subentities"];
for (i = 0; i < [subs count]; i++)
{
[self setUpOneSubentity:[subs dictionaryAtIndex:i]];
}
return YES;
}
- (BOOL) setUpOneSubentity:(NSDictionary *) subentDict
{
NSString *type = nil;
type = [subentDict stringForKey:@"type"];
if ([type isEqualToString:@"flasher"])
{
return [self setUpOneFlasher:subentDict];
}
else
{
return [self setUpOneStandardSubentity:subentDict asTurret:[type isEqualToString:@"ball_turret"]];
}
}
- (BOOL) setUpOneFlasher:(NSDictionary *) subentDict
{
ParticleEntity *flasher = nil;
float size, frequency, phase;
size = [subentDict floatForKey:@"size"];
frequency = [subentDict floatForKey:@"frequency"];
phase = [subentDict floatForKey:@"phase"];
flasher = [[ParticleEntity alloc] initFlasherWithSize:size frequency:frequency phase:phase];
[flasher setColor:[OOColor colorWithDescription:[[subentDict arrayForKey:@"colors"] objectAtIndex:0]]];
[flasher setPosition:[subentDict vectorForKey:@"position"]];
if ([subentDict boolForKey:@"initially_on"]) [flasher setStatus:STATUS_EFFECT];
[self addFlasher:flasher];
[flasher release];
return YES;
}
- (BOOL) setUpOneStandardSubentity:(NSDictionary *) subentDict asTurret:(BOOL)asTurret
{
ShipEntity *subentity = nil;
NSString *subentKey = nil;
Vector subPosition;
Quaternion subOrientation;
subentKey = [subentDict stringForKey:@"subentity_key"];
if (subentKey == nil) return NO;
subentity = [UNIVERSE newShipWithName:subentKey];
if (subentity == nil) return NO;
subPosition = [subentDict vectorForKey:@"position"];
subOrientation = [subentDict quaternionForKey:@"orientation"];
if (!asTurret && [self isStation] && [subentDict boolForKey:@"is_dock"])
{
[(StationEntity *)self setDockingPortModel:subentity :subPosition :subOrientation];
}
[subentity setPosition:subPosition];
[subentity setOrientation:subOrientation];
[subentity setReference:vector_forward_from_quaternion(subOrientation)];
if (asTurret)
{
[subentity setBehaviour:BEHAVIOUR_TRACK_AS_TURRET];
[subentity setWeaponRechargeRate:[subentDict floatForKey:@"fire_rate"]];
[subentity setStatus: STATUS_ACTIVE];
}
else
{
[subentity setStatus:STATUS_INACTIVE];
}
[self addSolidSubentityToCollisionRadius:subentity];
[self addSubEntity:subentity];
[subentity release];
return YES;
}
- (void) dealloc - (void) dealloc
{ {
[self setTrackCloseContacts:NO]; // deallocs tracking dictionary [self setTrackCloseContacts:NO]; // deallocs tracking dictionary
@ -3559,7 +3586,7 @@ static GLfloat mascem_color2[4] = { 0.4, 0.1, 0.4, 1.0}; // purple
} }
- (void) setWeaponDataFromType: (int) weapon_type - (void) setWeaponDataFromType: (OOWeaponType) weapon_type
{ {
switch (weapon_type) switch (weapon_type)
{ {
@ -3602,6 +3629,18 @@ static GLfloat mascem_color2[4] = { 0.4, 0.1, 0.4, 1.0}; // purple
} }
- (float) weaponRechargeRate
{
return weapon_recharge_rate;
}
- (void) setWeaponRechargeRate:(float)value
{
weapon_recharge_rate = value;
}
- (GLfloat) scannerRange - (GLfloat) scannerRange
{ {
return scannerRange; return scannerRange;
@ -4215,15 +4254,6 @@ NSComparisonResult planetSort(id i1, id i2, void* context)
} }
// Exposed to script actions
- (void) initialiseTurret
{
[self setBehaviour: BEHAVIOUR_TRACK_AS_TURRET];
weapon_recharge_rate = 0.5; // test
[self setStatus: STATUS_ACTIVE];
}
// Exposed to AI // Exposed to AI
- (void) dealEnergyDamageWithinDesiredRange - (void) dealEnergyDamageWithinDesiredRange
{ {

View File

@ -710,48 +710,14 @@ static NSDictionary* instructions(int station_id, Vector coords, float speed, fl
- (BOOL) setUpShipFromDictionary:(NSDictionary *) dict - (BOOL) setUpShipFromDictionary:(NSDictionary *) dict
{ {
unsigned int i;
isShip = YES; isShip = YES;
isStation = YES; isStation = YES;
alertLevel = STATION_ALERT_LEVEL_GREEN; alertLevel = STATION_ALERT_LEVEL_GREEN;
// ** Set up a the docking port double port_radius = [dict nonNegativeDoubleForKey:@"port_radius" defaultValue:500.0];
// Look for subentity specifying position port_position = make_vector(0, 0, port_radius);
/* NOTE: all this is overriden by -setDockingPortModel:::, called from port_orientation = kIdentityQuaternion;
-setUpSubEntities:, if any subentity key contains "dock" (not just
prefixed with "dock" as here). port_radius and port_dimensions should
be considered obsolete.
-- Ahruman 20090108
*/
NSArray *subs = [dict arrayForKey:@"subentities"];
NSArray *dockSubEntity = nil;
for (i = 0; i < [subs count]; i++)
{
NSArray* details = ScanTokensFromString([subs objectAtIndex:i]);
if (([details count] == 8) && ([[details objectAtIndex:0] hasPrefix:@"dock"])) dockSubEntity = details;
}
if (dockSubEntity != nil)
{
port_position.x = [(NSString *)[dockSubEntity objectAtIndex:1] floatValue];
port_position.y = [(NSString *)[dockSubEntity objectAtIndex:2] floatValue];
port_position.z = [(NSString *)[dockSubEntity objectAtIndex:3] floatValue];
port_orientation.w = [(NSString *)[dockSubEntity objectAtIndex:4] floatValue];
port_orientation.x = [(NSString *)[dockSubEntity objectAtIndex:5] floatValue];
port_orientation.y = [(NSString *)[dockSubEntity objectAtIndex:6] floatValue];
port_orientation.z = [(NSString *)[dockSubEntity objectAtIndex:7] floatValue];
quaternion_normalize(&port_orientation);
}
else
{
// No dock* subentity found, use defaults.
double port_radius = [dict nonNegativeDoubleForKey:@"port_radius" defaultValue:500.0];
port_position = make_vector(0, 0, port_radius);
port_orientation = kIdentityQuaternion;
}
// port_dimensions can be set for rock-hermits and other specials // port_dimensions can be set for rock-hermits and other specials
port_dimensions = make_vector(69, 69, 250); port_dimensions = make_vector(69, 69, 250);

79
src/Core/OODeepCopy.h Normal file
View File

@ -0,0 +1,79 @@
/*
OODeepCopy.h
Informal protocol and utility function for making efficient deep copies of
immutable collections.
It is implemented in such a way that all objects can be deep copied. Objects
that implement the NSCopying protocol are automatically copied, while others
are retained. The following special cases exist:
* NSStrings and NSValues (including NSNumbers) are uniqued - that is, the
resulting collection will only include one (immutable) copy of any string
or number.
* Arrays, sets and dictionaries deep copy their contents.
For objects where the mutable/immutable distinction exists, the result should
be expected to be immutable.
This self-optimizing behaviour is similar to that performed by binary plist
export.
NOTE: in accordance with Cocoa coding conventions, methods and functions with
Copy in the name return objects owned by the receiver.
Oolite
Copyright (C) 2004-2009 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) 2009 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"
id OODeepCopy(id object);
@interface NSObject (OODeepCopy)
- (id) ooDeepCopyWithSharedObjects:(NSMutableSet *)objects;
@end

337
src/Core/OODeepCopy.m Normal file
View File

@ -0,0 +1,337 @@
/*
OODeepCopy.m
Oolite
Copyright (C) 2004-2009 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) 2009 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 "OODeepCopy.h"
id OODeepCopy(id object)
{
NSAutoreleasePool *pool = nil;
NSMutableSet *objects = nil;
if (object == nil) return nil;
pool = [[NSAutoreleasePool alloc] init];
objects = [NSMutableSet set];
object = [object ooDeepCopyWithSharedObjects:objects];
[pool release];
return object;
}
@implementation NSObject (OODeepCopy)
- (id) ooDeepCopyWithSharedObjects:(NSMutableSet *)objects
{
if ([self conformsToProtocol:@protocol(NSCopying)])
{
return [self copy];
}
else
{
return [self retain];
}
}
@end
@implementation NSString (OODeepCopy)
- (id) ooDeepCopyWithSharedObjects:(NSMutableSet *)objects
{
if ([self length] == 0) return [[NSString string] retain];
id object = [objects member:self];
if (object != nil && [object isKindOfClass:[NSString class]])
{
return [object retain];
}
else
{
object = [self copy];
[objects addObject:object];
return object;
}
}
@end
@implementation NSValue (OODeepCopy) // Includes NSNumber
- (id) ooDeepCopyWithSharedObjects:(NSMutableSet *)objects
{
id object = [objects member:self];
if (object != nil && [object isKindOfClass:[NSValue class]])
{
return [object retain];
}
else
{
object = [self copy];
[objects addObject:object];
return object;
}
}
@end
@implementation NSArray (OODeepCopy)
- (id) ooDeepCopyWithSharedObjects:(NSMutableSet *)objects
{
OOUInteger i, count;
id *members = NULL;
NSArray *result = nil;
BOOL tempObjects = NO;
count = [self count];
if (count == 0) return [[NSArray array] retain];
members = malloc(sizeof *members * count);
if (members == NULL)
{
[NSException raise:NSMallocException format:@"Failed to allocate space for %lu objects in %s.", (unsigned long)count, __PRETTY_FUNCTION__];
}
// Ensure there's an objects set even if passed nil.
if (objects == nil)
{
objects = [[NSMutableSet alloc] init];
tempObjects = YES;
}
[self getObjects:members];
NS_DURING
// Deep copy members.
for (i = 0; i < count; i++)
{
members[i] = [members[i] ooDeepCopyWithSharedObjects:objects];
}
NS_HANDLER
// Clean up and rethrow.
free(members);
if (tempObjects) [objects release];
[localException raise];
NS_ENDHANDLER
#if !OOLITE_MAC_OS_X
// Make NSArray of results.
result = [[NSArray alloc] initWithObjects:members count:count];
// Release objects.
for (i = 0; i < count; i++)
{
[members[i] release];
}
#else
// Use CF to avoid redundant retain and release.
CFArrayCallBacks arrayCB = kCFTypeArrayCallBacks;
arrayCB.version = 0;
arrayCB.retain = NULL;
result = (NSArray *)CFArrayCreate(kCFAllocatorDefault, (const void **)members, count, &arrayCB);
#endif
free(members);
if (tempObjects) [objects release];
// Collections are not reused because comparing them is arbitrarily slow.
return result;
}
@end
@implementation NSSet (OODeepCopy)
- (id) ooDeepCopyWithSharedObjects:(NSMutableSet *)objects
{
OOUInteger i, count;
id *members = NULL;
NSSet *result = nil;
BOOL tempObjects = NO;
NSEnumerator *setEnum = nil;
count = [self count];
if (count == 0) return [[NSSet set] retain];
members = malloc(sizeof *members * count);
if (members == NULL)
{
[NSException raise:NSMallocException format:@"Failed to allocate space for %lu objects in %s.", (unsigned long)count, __PRETTY_FUNCTION__];
}
// Ensure there's an objects set even if passed nil.
if (objects == nil)
{
objects = [[NSMutableSet alloc] init];
tempObjects = YES;
}
setEnum = [self objectEnumerator];
NS_DURING
// Deep copy members.
for (i = 0; i < count; i++)
{
members[i] = [[setEnum nextObject] ooDeepCopyWithSharedObjects:objects];
}
NS_HANDLER
// Clean up and rethrow.
free(members);
if (tempObjects) [objects release];
[localException raise];
NS_ENDHANDLER
#if !OOLITE_MAC_OS_X
// Make NSArray of results.
result = [[NSSet alloc] initWithObjects:members count:count];
// Release objects.
for (i = 0; i < count; i++)
{
[members[i] release];
}
#else
// Use CF to avoid redundant retain and release.
CFSetCallBacks setCB = kCFTypeSetCallBacks;
setCB.version = 0;
setCB.retain = NULL;
result = (NSSet *)CFSetCreate(kCFAllocatorDefault, (const void **)members, count, &setCB);
#endif
free(members);
if (tempObjects) [objects release];
// Collections are not reused because comparing them is arbitrarily slow.
return result;
}
@end
@implementation NSDictionary (OODeepCopy)
- (id) ooDeepCopyWithSharedObjects:(NSMutableSet *)objects
{
OOUInteger i, count;
id *keys = NULL;
id *values = NULL;
NSDictionary *result = nil;
BOOL tempObjects = NO;
NSEnumerator *keyEnum = nil;
count = [self count];
if (count == 0) return [[NSSet set] retain];
keys = malloc(sizeof *keys * count);
values = malloc(sizeof *values * count);
if (keys == NULL || values == NULL)
{
free(keys);
free(values);
[NSException raise:NSMallocException format:@"Failed to allocate space for %lu objects in %s.", (unsigned long)count, __PRETTY_FUNCTION__];
}
// Ensure there's an objects set even if passed nil.
if (objects == nil)
{
objects = [[NSMutableSet alloc] init];
tempObjects = YES;
}
keyEnum = [self keyEnumerator];
NS_DURING
// Deep copy members.
for (i = 0; i < count; i++)
{
keys[i] = [[keyEnum nextObject] ooDeepCopyWithSharedObjects:objects];
values[i] = [[self objectForKey:keys[i]] ooDeepCopyWithSharedObjects:objects];
}
NS_HANDLER
// Clean up and rethrow.
free(keys);
free(values);
if (tempObjects) [objects release];
[localException raise];
NS_ENDHANDLER
#if !OOLITE_MAC_OS_X
// Make NSArray of results.
result = [[NSDictionary alloc] initWithObjects:values forKeys:keys count:count];
// Release objects.
for (i = 0; i < count; i++)
{
[keys[i] release];
[values[i] release];
}
#else
// Use CF to avoid redundant retain and release.
CFDictionaryKeyCallBacks keyCB = kCFTypeDictionaryKeyCallBacks;
CFDictionaryValueCallBacks valueCB = kCFTypeDictionaryValueCallBacks;
keyCB.version = 0;
keyCB.retain = NULL;
valueCB.version = 0;
valueCB.retain = NULL;
result = (NSDictionary *)CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, count, &keyCB, &valueCB);
#endif
free(keys);
free(values);
if (tempObjects) [objects release];
// Collections are not reused because comparing them is arbitrarily slow.
return result;
}
@end

View File

@ -6,7 +6,7 @@ Manage the set of installed ships.
Oolite Oolite
Copyright (C) 2004-2008 Giles C Williams and contributors Copyright (C) 2004-2009 Giles C Williams and contributors
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License
@ -26,7 +26,7 @@ MA 02110-1301, USA.
This file may also be distributed under the MIT/X11 license: This file may also be distributed under the MIT/X11 license:
Copyright (C) 2008 Jens Ayton Copyright (C) 2008-2009 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -4,7 +4,7 @@ OOShipRegistry.m
Oolite Oolite
Copyright (C) 2004-2008 Giles C Williams and contributors Copyright (C) 2004-2009 Giles C Williams and contributors
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License
@ -24,7 +24,7 @@ MA 02110-1301, USA.
This file may also be distributed under the MIT/X11 license: This file may also be distributed under the MIT/X11 license:
Copyright (C) 2008 Jens Ayton Copyright (C) 2008-2009 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -57,6 +57,7 @@ SOFTWARE.
#import "OOMesh.h" #import "OOMesh.h"
#import "GameController.h" #import "GameController.h"
#import "OOLegacyScriptWhitelist.h" #import "OOLegacyScriptWhitelist.h"
#import "OODeepCopy.h"
#define PRELOAD 0 #define PRELOAD 0
@ -81,8 +82,10 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
- (BOOL) applyLikeShips:(NSMutableDictionary *)ioData; - (BOOL) applyLikeShips:(NSMutableDictionary *)ioData;
- (BOOL) loadAndMergeShipyard:(NSMutableDictionary *)ioData; - (BOOL) loadAndMergeShipyard:(NSMutableDictionary *)ioData;
- (BOOL) stripPrivateKeys:(NSMutableDictionary *)ioData;
- (BOOL) makeShipEntriesMutable:(NSMutableDictionary *)ioData;
- (BOOL) loadAndApplyShipDataOverrides:(NSMutableDictionary *)ioData; - (BOOL) loadAndApplyShipDataOverrides:(NSMutableDictionary *)ioData;
- (BOOL) tagSubEntities:(NSMutableDictionary *)ioData; - (BOOL) canonicalizeAndTagSubentities:(NSMutableDictionary *)ioData;
- (BOOL) removeUnusableEntries:(NSMutableDictionary *)ioData; - (BOOL) removeUnusableEntries:(NSMutableDictionary *)ioData;
- (BOOL) sanitizeConditions:(NSMutableDictionary *)ioData; - (BOOL) sanitizeConditions:(NSMutableDictionary *)ioData;
@ -90,9 +93,36 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
- (BOOL) preloadShipMeshes:(NSMutableDictionary *)ioData; - (BOOL) preloadShipMeshes:(NSMutableDictionary *)ioData;
#endif #endif
- (NSDictionary *) mergeShip:(NSDictionary *)child withParent:(NSDictionary *)parent; - (NSMutableDictionary *) mergeShip:(NSDictionary *)child withParent:(NSDictionary *)parent;
- (void) mergeShipRoles:(NSString *)roles forShipKey:(NSString *)shipKey intoProbabilityMap:(NSMutableDictionary *)probabilitySets; - (void) mergeShipRoles:(NSString *)roles forShipKey:(NSString *)shipKey intoProbabilityMap:(NSMutableDictionary *)probabilitySets;
- (NSDictionary *) canonicalizeSubentityDeclaration:(id)declaration
forShip:(NSString *)shipKey
shipData:(NSDictionary *)shipData
fatalError:(BOOL *)outFatalError;
- (NSDictionary *) translateOldStyleSubentityDeclaration:(NSString *)declaration
forShip:(NSString *)shipKey
shipData:(NSDictionary *)shipData
fatalError:(BOOL *)outFatalError;
- (NSDictionary *) translateOldStyleFlasherDeclaration:(NSArray *)tokens
forShip:(NSString *)shipKey
fatalError:(BOOL *)outFatalError;
- (NSDictionary *) translateOldStandardBasicSubentityDeclaration:(NSArray *)tokens
forShip:(NSString *)shipKey
shipData:(NSDictionary *)shipData
fatalError:(BOOL *)outFatalError;
- (NSDictionary *) validateNewStyleSubentityDeclaration:(NSDictionary *)declaration
forShip:(NSString *)shipKey
fatalError:(BOOL *)outFatalError;
- (NSDictionary *) validateNewStyleFlasherDeclaration:(NSDictionary *)declaration
forShip:(NSString *)shipKey
fatalError:(BOOL *)outFatalError;
- (NSDictionary *) validateNewStyleStandardSubentityDeclaration:(NSDictionary *)declaration
forShip:(NSString *)shipKey
fatalError:(BOOL *)outFatalError;
- (BOOL) shipIsBallTurretForKey:(NSString *)shipKey inShipData:(NSDictionary *)shipData;
@end @end
@ -176,7 +206,7 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
- (NSDictionary *) shipyardInfoForKey:(NSString *)key - (NSDictionary *) shipyardInfoForKey:(NSString *)key
{ {
return [[self shipInfoForKey:key] objectForKey:@"shipyard"]; return [[self shipInfoForKey:key] objectForKey:@"_oo_shipyard"];
} }
@ -232,9 +262,6 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
- (void) loadShipData - (void) loadShipData
{ {
NSMutableDictionary *result = nil; NSMutableDictionary *result = nil;
NSEnumerator *enumerator = nil;
NSString *key = nil;
NSDictionary *immutableResult = nil;
OOLog(@"shipData.load.begin", @"Loading ship data..."); OOLog(@"shipData.load.begin", @"Loading ship data...");
OOLogIndentIf(@"shipData.load.begin"); OOLogIndentIf(@"shipData.load.begin");
@ -251,21 +278,17 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
cache:NO] mutableCopy] autorelease]; cache:NO] mutableCopy] autorelease];
if (result == nil) return; if (result == nil) return;
// Clean out any non-dictionaries. (Iterates over a copy of keys since it mutates the dictionary.) // Make each entry mutable to simplify later stages. Also removes any entries that aren't dictionaries.
for (enumerator = [[result allKeys] objectEnumerator]; (key = [enumerator nextObject]); ) if (![self makeShipEntriesMutable:result]) return;
{
if (![[result objectForKey:key] isKindOfClass:[NSDictionary class]])
{
OOLog(@"shipData.load.badEntry", @"***** ERROR: the shipdata.plist entry \"%@\" is not a dictionary.", key);
[result removeObjectForKey:key];
}
}
// Apply patches. // Apply patches.
if (![self loadAndApplyShipDataOverrides:result]) return; if (![self loadAndApplyShipDataOverrides:result]) return;
// Tag subentities so they won't be pruned. // Strip private keys (anything starting with _oo_).
if (![self tagSubEntities:result]) return; if (![self stripPrivateKeys:result]) return;
// Clean up subentity declarations and tag subentities so they won't be pruned.
if (![self canonicalizeAndTagSubentities:result]) return;
// Resolve like_ship entries. // Resolve like_ship entries.
if (![self applyLikeShips:result]) return; if (![self applyLikeShips:result]) return;
@ -284,8 +307,7 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
if (![self preloadShipMeshes:result]) return; if (![self preloadShipMeshes:result]) return;
#endif #endif
immutableResult = [[result copy] autorelease]; _shipData = OODeepCopy(result);
_shipData = [immutableResult retain];
[[OOCacheManager sharedCache] setObject:_shipData forKey:kShipDataCacheKey inCache:kShipRegistryCacheName]; [[OOCacheManager sharedCache] setObject:_shipData forKey:kShipDataCacheKey inCache:kShipRegistryCacheName];
OOLogOutdentIf(@"shipData.load.begin"); OOLogOutdentIf(@"shipData.load.begin");
@ -480,7 +502,7 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
} }
- (NSDictionary *) mergeShip:(NSDictionary *)child withParent:(NSDictionary *)parent - (NSMutableDictionary *) mergeShip:(NSDictionary *)child withParent:(NSDictionary *)parent
{ {
NSMutableDictionary *result = [[parent mutableCopy] autorelease]; NSMutableDictionary *result = [[parent mutableCopy] autorelease];
if (result == nil) return nil; if (result == nil) return nil;
@ -492,15 +514,42 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
if ([child stringForKey:@"display_name"] == nil) [result removeObjectForKey:@"display_name"]; if ([child stringForKey:@"display_name"] == nil) [result removeObjectForKey:@"display_name"];
if ([child stringForKey:@"is_template"] == nil) [result removeObjectForKey:@"is_template"]; if ([child stringForKey:@"is_template"] == nil) [result removeObjectForKey:@"is_template"];
return [[result copy] autorelease]; return result;
}
- (BOOL) makeShipEntriesMutable:(NSMutableDictionary *)ioData
{
NSEnumerator *shipKeyEnum = nil;
NSString *shipKey = nil;
NSDictionary *shipEntry = nil;
for (shipKeyEnum = [[ioData allKeys] objectEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
{
shipEntry = [ioData objectForKey:shipKey];
if (![shipEntry isKindOfClass:[NSDictionary class]])
{
OOLog(@"shipData.load.badEntry", @"***** ERROR: the shipdata.plist entry \"%@\" is not a dictionary.", shipKey);
[ioData removeObjectForKey:shipKey];
}
else
{
shipEntry = [shipEntry mutableCopy];
[ioData setObject:shipEntry forKey:shipKey];
[shipEntry release];
}
}
return YES;
} }
- (BOOL) loadAndApplyShipDataOverrides:(NSMutableDictionary *)ioData - (BOOL) loadAndApplyShipDataOverrides:(NSMutableDictionary *)ioData
{ {
NSEnumerator *enumerator = nil; NSEnumerator *shipKeyEnum = nil;
NSString *key = nil; NSString *shipKey = nil;
NSDictionary *shipEntry = nil; NSMutableDictionary *shipEntry = nil;
NSDictionary *overrides = nil; NSDictionary *overrides = nil;
NSDictionary *overridesEntry = nil; NSDictionary *overridesEntry = nil;
@ -509,20 +558,44 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
mergeMode:MERGE_SMART mergeMode:MERGE_SMART
cache:NO]; cache:NO];
for (enumerator = [overrides keyEnumerator]; (key = [enumerator nextObject]); ) for (shipKeyEnum = [overrides keyEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
{ {
shipEntry = [ioData objectForKey:key]; shipEntry = [ioData objectForKey:shipKey];
if (shipEntry != nil) if (shipEntry != nil)
{ {
overridesEntry = [overrides objectForKey:key]; overridesEntry = [overrides objectForKey:shipKey];
if (![overridesEntry isKindOfClass:[NSDictionary class]]) if (![overridesEntry isKindOfClass:[NSDictionary class]])
{ {
OOLog(@"shipData.load.error", @"***** ERROR: the shipdata-overrides.plist entry \"%@\" is not a dictionary.", key); OOLog(@"shipData.load.error", @"***** ERROR: the shipdata-overrides.plist entry \"%@\" is not a dictionary.", shipKey);
} }
else else
{ {
shipEntry = [shipEntry dictionaryByAddingEntriesFromDictionary:overridesEntry]; [shipEntry addEntriesFromDictionary:overridesEntry];
[ioData setObject:shipEntry forKey:key]; }
}
}
return YES;
}
- (BOOL) stripPrivateKeys:(NSMutableDictionary *)ioData
{
NSEnumerator *shipKeyEnum = nil;
NSString *shipKey = nil;
NSMutableDictionary *shipEntry = nil;
NSEnumerator *attrKeyEnum = nil;
NSString *attrKey = nil;
for (shipKeyEnum = [ioData keyEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
{
shipEntry = [ioData objectForKey:shipKey];
for (attrKeyEnum = [shipEntry keyEnumerator]; (attrKey = [attrKeyEnum nextObject]); )
{
if ([attrKey hasPrefix:@"_oo_"])
{
[shipEntry removeObjectForKey:attrKey];
} }
} }
} }
@ -540,9 +613,9 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
*/ */
- (BOOL) loadAndMergeShipyard:(NSMutableDictionary *)ioData - (BOOL) loadAndMergeShipyard:(NSMutableDictionary *)ioData
{ {
NSEnumerator *enumerator = nil; NSEnumerator *shipKeyEnum = nil;
NSString *key = nil; NSString *shipKey = nil;
NSDictionary *shipEntry = nil; NSMutableDictionary *shipEntry = nil;
NSDictionary *shipyard = nil; NSDictionary *shipyard = nil;
NSDictionary *shipyardOverrides = nil; NSDictionary *shipyardOverrides = nil;
NSDictionary *shipyardEntry = nil; NSDictionary *shipyardEntry = nil;
@ -550,12 +623,12 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
NSMutableSet *playerShips = nil; NSMutableSet *playerShips = nil;
// Strip out any shipyard stuff in shipdata (there shouldn't be any). // Strip out any shipyard stuff in shipdata (there shouldn't be any).
for (enumerator = [ioData keyEnumerator]; (key = [enumerator nextObject]); ) for (shipKeyEnum = [ioData keyEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
{ {
shipEntry = [ioData objectForKey:key]; shipEntry = [ioData objectForKey:shipKey];
if ([shipEntry objectForKey:@"shipyard"] != nil) if ([shipEntry objectForKey:@"_oo_shipyard"] != nil)
{ {
[ioData setObject:[shipEntry dictionaryByRemovingObjectForKey:@"shipyard"] forKey:key]; [shipEntry removeObjectForKey:@"_oo_shipyard"];
} }
} }
@ -571,23 +644,22 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
playerShips = [NSMutableArray arrayWithCapacity:[shipyard count]]; playerShips = [NSMutableArray arrayWithCapacity:[shipyard count]];
// Insert merged shipyard and shipyardOverrides entries. // Insert merged shipyard and shipyardOverrides entries.
for (enumerator = [shipyard keyEnumerator]; (key = [enumerator nextObject]); ) for (shipKeyEnum = [shipyard keyEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
{ {
shipEntry = [ioData objectForKey:key]; shipEntry = [ioData objectForKey:shipKey];
if (shipEntry != nil) if (shipEntry != nil)
{ {
shipyardEntry = [shipyard objectForKey:key]; shipyardEntry = [shipyard objectForKey:shipKey];
shipyardOverridesEntry = [shipyardOverrides objectForKey:key]; shipyardOverridesEntry = [shipyardOverrides objectForKey:shipKey];
shipyardEntry = [shipyardEntry dictionaryByAddingEntriesFromDictionary:shipyardOverridesEntry]; shipyardEntry = [shipyardEntry dictionaryByAddingEntriesFromDictionary:shipyardOverridesEntry];
shipEntry = [shipEntry dictionaryByAddingObject:shipyardEntry forKey:@"shipyard"]; [shipEntry setObject:shipyardEntry forKey:@"_oo_shipyard"];
[ioData setObject:shipEntry forKey:key];
[playerShips addObject:key]; [playerShips addObject:shipKey];
} }
else else
{ {
OOLog(@"shipData.load.shipyard.unknown", @"WARNING: the shipyard.plist entry \"%@\" does not have a corresponding shipdata.plist entry, ignoring.", key); OOLog(@"shipData.load.shipyard.unknown", @"WARNING: the shipyard.plist entry \"%@\" does not have a corresponding shipdata.plist entry, ignoring.", shipKey);
} }
} }
@ -598,110 +670,95 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
} }
- (BOOL) tagSubEntities:(NSMutableDictionary *)ioData - (BOOL) canonicalizeAndTagSubentities:(NSMutableDictionary *)ioData
{ {
NSEnumerator *shipKeyEnum = nil; NSEnumerator *shipKeyEnum = nil;
NSString *shipKey = nil; NSString *shipKey = nil;
NSDictionary *shipEntry = nil; NSMutableDictionary *shipEntry = nil;
NSArray *subEntityDeclarations = nil; NSArray *subentityDeclarations = nil;
NSEnumerator *subEntityEnum = nil; NSEnumerator *subentityEnum = nil;
NSString *subEntityKey = nil; id subentityDecl = nil;
NSArray *subEntityDef = nil; NSDictionary *subentityDict = nil;
NSDictionary *subEntityShipEntry = nil; NSString *subentityKey = nil;
BOOL remove; NSMutableDictionary *subentityShipEntry = nil;
NSMutableSet *badSubEntities = nil; NSMutableSet *badSubentities = nil;
NSString *badSubEntitiesList = nil; NSString *badSubentitiesList = nil;
BOOL isFlasher; NSMutableArray *okSubentities = nil;
NSMutableArray *okSubEntities = nil; BOOL remove, fatal;
// Convert all subentity declarations to dictionaries and add
// _oo_is_subentity=YES to all entries used as subentities.
// Add _oo_is_subentity=YES to all entries used as subentities.
// Iterate over all ships. (Iterates over a copy of keys since it mutates the dictionary.) // Iterate over all ships. (Iterates over a copy of keys since it mutates the dictionary.)
for (shipKeyEnum = [[ioData allKeys] objectEnumerator]; (shipKey = [shipKeyEnum nextObject]); ) for (shipKeyEnum = [[ioData allKeys] objectEnumerator]; (shipKey = [shipKeyEnum nextObject]); )
{ {
shipEntry = [ioData objectForKey:shipKey]; shipEntry = [ioData objectForKey:shipKey];
remove = NO; remove = NO;
badSubEntities = nil; badSubentities = nil;
// Iterate over each subentity declaration of each ship // Iterate over each subentity declaration of each ship
subEntityDeclarations = [shipEntry arrayForKey:@"subentities"]; subentityDeclarations = [shipEntry arrayForKey:@"subentities"];
if (subEntityDeclarations != nil) if (subentityDeclarations != nil)
{ {
okSubEntities = [NSMutableArray arrayWithCapacity:[subEntityDeclarations count]]; okSubentities = [NSMutableArray arrayWithCapacity:[subentityDeclarations count]];
for (subEntityEnum = [subEntityDeclarations objectEnumerator]; (subEntityKey = [subEntityEnum nextObject]); ) for (subentityEnum = [subentityDeclarations objectEnumerator]; (subentityDecl = [subentityEnum nextObject]); )
{ {
subEntityDef = ScanTokensFromString(subEntityKey); subentityDict = [self canonicalizeSubentityDeclaration:subentityDecl forShip:shipKey shipData:ioData fatalError:&fatal];
if([subEntityDef count] != 0) subEntityKey = [subEntityDef stringAtIndex:0];
else subEntityKey = nil;
isFlasher = [subEntityKey isEqualToString:@"*FLASHER*"];
// While we're at it, do basic sanity checking on subentity defs. // If entry is broken, we need to kill this ship.
if ([subEntityDef count] != 8) if (fatal)
{ {
if (!isFlasher) remove = YES;
{
OOLog(@"shipData.load.error.badSubEntity", @"***** ERROR: the shipdata.plist entry \"%@\" has a broken subentity definition \"%@\" (should have 8 tokens, has %u).", shipKey, subEntityKey, [subEntityDef count]);
if (![shipEntry boolForKey:@"frangible"]) remove = YES;
}
else
{
OOLog(@"shipData.load.error.badFlasher", @"----- WARNING: the shipdata.plist entry \"%@\" has a broken flasher definition \"%@\" (should have 8 tokens, has %u). This flasher will be ignored.", shipKey, subEntityKey, [subEntityDef count]);
}
} }
else else
{ {
[okSubEntities addObject:subEntityKey]; [okSubentities addObject:subentityDict];
if (!isFlasher) // Tag subentities.
if (![[subentityDict stringForKey:@"type"] isEqualToString:@"flasher"])
{ {
subEntityShipEntry = [ioData objectForKey:subEntityKey]; subentityKey = [subentityDict stringForKey:@"subentity_key"];
if (subEntityShipEntry == nil) subentityShipEntry = [ioData objectForKey:subentityKey];
if (subentityKey == nil)
{ {
// Oops, reference to non-existent subent // Oops, reference to non-existent subent.
if (badSubEntities == nil) badSubEntities = [NSMutableSet set]; if (badSubentities == nil) badSubentities = [NSMutableSet set];
[badSubEntities addObject:subEntityKey]; [badSubentities addObject:subentityKey];
} }
else if (![subEntityShipEntry boolForKey:@"_oo_is_subentity"]) else
{ {
// Subent exists, add _oo_is_subentity so roles aren't required // Subent exists, add _oo_is_subentity so roles aren't required.
subEntityShipEntry = [subEntityShipEntry dictionaryByAddingObject:[NSNumber numberWithBool:YES] forKey:@"_oo_is_subentity"]; [subentityShipEntry setBool:YES forKey:@"_oo_is_subentity"];
[ioData setObject:subEntityShipEntry forKey:subEntityKey];
} }
} }
} }
} }
// If subentities have been excluded (i.e. there are bad flashers, // Set updated subentity list.
// or there are bad subentities but the ship is frangible), copy if ([okSubentities count] != 0)
// in new shortened list.
if ([okSubEntities count] != [subEntityDeclarations count] && !remove)
{ {
shipEntry = [[shipEntry mutableCopy] autorelease]; [shipEntry setObject:okSubentities forKey:@"subentities"];
if ([okSubEntities count] == 0)
{
[(NSMutableDictionary *)shipEntry removeObjectForKey:@"subentities"];
}
else
{
[(NSMutableDictionary *)shipEntry setObject:[[okSubEntities copy] autorelease] forKey:@"subentities"];
}
} }
} else
if (badSubEntities != nil)
{
if (![shipEntry boolForKey:@"is_external_dependency"])
{ {
badSubEntitiesList = [[[badSubEntities allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] componentsJoinedByString:@", "]; [shipEntry removeObjectForKey:@"subentities"];
OOLog(@"shipData.load.error", @"***** ERROR: the shipdata.plist entry \"%@\" has unresolved subentit%@ %@.", shipKey, ([badSubEntities count] == 1) ? @"y" : @"ies", badSubEntitiesList); }
if (badSubentities != nil)
{
if (![shipEntry boolForKey:@"is_external_dependency"])
{
badSubentitiesList = [[[badSubentities allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] componentsJoinedByString:@", "];
OOLog(@"shipData.load.error", @"***** ERROR: the shipdata.plist entry \"%@\" has unresolved subentit%@ %@.", shipKey, ([badSubentities count] == 1) ? @"y" : @"ies", badSubentitiesList);
}
remove = YES;
}
if (remove)
{
// Removal is deferred to avoid bogus "entry doesn't exist" errors.
[shipEntry setBool:YES forKey:@"_oo_deferred_remove"];
} }
remove = YES;
}
if (remove)
{
// Removal is deferred to avoid bogus "entry doesn't exist" errors.
shipEntry = [shipEntry dictionaryByAddingObject:[NSNumber numberWithBool:YES] forKey:@"_oo_deferred_remove"];
[ioData setObject:shipEntry forKey:shipKey];
} }
} }
@ -713,7 +770,7 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
{ {
NSEnumerator *shipKeyEnum = nil; NSEnumerator *shipKeyEnum = nil;
NSString *shipKey = nil; NSString *shipKey = nil;
NSDictionary *shipEntry = nil; NSMutableDictionary *shipEntry = nil;
BOOL remove; BOOL remove;
NSString *modelName = nil; NSString *modelName = nil;
@ -757,8 +814,7 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
{ {
NSEnumerator *shipKeyEnum = nil; NSEnumerator *shipKeyEnum = nil;
NSString *shipKey = nil; NSString *shipKey = nil;
NSDictionary *shipEntry = nil; NSMutableDictionary *shipEntry = nil;
NSMutableDictionary *mutableEntry = nil;
NSMutableDictionary *mutableShipyard = nil; NSMutableDictionary *mutableShipyard = nil;
NSArray *conditions = nil; NSArray *conditions = nil;
NSArray *hasShipyard = nil; NSArray *hasShipyard = nil;
@ -770,12 +826,10 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
conditions = [shipEntry objectForKey:@"conditions"]; conditions = [shipEntry objectForKey:@"conditions"];
hasShipyard = [shipEntry objectForKey:@"hasShipyard"]; hasShipyard = [shipEntry objectForKey:@"hasShipyard"];
if (![hasShipyard isKindOfClass:[NSArray class]]) hasShipyard = nil; // May also be fuzzy boolean if (![hasShipyard isKindOfClass:[NSArray class]]) hasShipyard = nil; // May also be fuzzy boolean
shipyardConditions = [[shipEntry dictionaryForKey:@"shipyard"] objectForKey:@"conditions"]; shipyardConditions = [[shipEntry dictionaryForKey:@"_oo_shipyard"] objectForKey:@"conditions"];
if (conditions == nil && hasShipyard && shipyardConditions == nil) continue; if (conditions == nil && hasShipyard && shipyardConditions == nil) continue;
mutableEntry = [[shipEntry mutableCopy] autorelease];
if (conditions != nil) if (conditions != nil)
{ {
if ([conditions isKindOfClass:[NSArray class]]) if ([conditions isKindOfClass:[NSArray class]])
@ -790,11 +844,11 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
if (conditions != nil) if (conditions != nil)
{ {
[mutableEntry setObject:conditions forKey:@"conditions"]; [shipEntry setObject:conditions forKey:@"conditions"];
} }
else else
{ {
[mutableEntry removeObjectForKey:@"conditions"]; [shipEntry removeObjectForKey:@"conditions"];
} }
} }
@ -804,17 +858,17 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
if (hasShipyard != nil) if (hasShipyard != nil)
{ {
[mutableEntry setObject:hasShipyard forKey:@"hasShipyard"]; [shipEntry setObject:hasShipyard forKey:@"hasShipyard"];
} }
else else
{ {
[mutableEntry removeObjectForKey:@"hasShipyard"]; [shipEntry removeObjectForKey:@"hasShipyard"];
} }
} }
if (shipyardConditions != nil) if (shipyardConditions != nil)
{ {
mutableShipyard = [[[shipEntry dictionaryForKey:@"shipyard"] mutableCopy] autorelease]; mutableShipyard = [[[shipEntry dictionaryForKey:@"_oo_shipyard"] mutableCopy] autorelease];
if ([shipyardConditions isKindOfClass:[NSArray class]]) if ([shipyardConditions isKindOfClass:[NSArray class]])
{ {
@ -835,10 +889,8 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
[mutableShipyard removeObjectForKey:@"conditions"]; [mutableShipyard removeObjectForKey:@"conditions"];
} }
[mutableEntry setObject:mutableShipyard forKey:@"shipyard"]; [shipEntry setObject:mutableShipyard forKey:@"_oo_shipyard"];
} }
[ioData setObject:[[mutableEntry copy] autorelease] forKey:shipKey];
} }
return YES; return YES;
@ -850,7 +902,7 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
{ {
NSEnumerator *shipKeyEnum = nil; NSEnumerator *shipKeyEnum = nil;
NSString *shipKey = nil; NSString *shipKey = nil;
NSDictionary *shipEntry = nil; NSMutableDictionary *shipEntry = nil;
BOOL remove; BOOL remove;
NSString *modelName = nil; NSString *modelName = nil;
OOMesh *mesh = nil; OOMesh *mesh = nil;
@ -921,6 +973,306 @@ static NSString * const kDefaultDemoShip = @"coriolis-station";
} }
} }
- (NSDictionary *) canonicalizeSubentityDeclaration:(id)declaration
forShip:(NSString *)shipKey
shipData:(NSDictionary *)shipData
fatalError:(BOOL *)outFatalError
{
NSDictionary *result = nil;
assert(outFatalError != NULL);
*outFatalError = NO;
if ([declaration isKindOfClass:[NSString class]])
{
// Update old-style string-based declaration.
result = [self translateOldStyleSubentityDeclaration:declaration
forShip:shipKey
shipData:shipData
fatalError:outFatalError];
// TODO: remove
result = [self validateNewStyleSubentityDeclaration:result
forShip:shipKey
fatalError:outFatalError];
}
else if ([declaration isKindOfClass:[NSDictionary class]])
{
// Validate dictionary-based declaration.
result = [self validateNewStyleSubentityDeclaration:declaration
forShip:shipKey
fatalError:outFatalError];
}
else
{
OOLog(@"shipData.load.error.badSubentity", @"***** ERROR: subentity declaration for ship %@ should be string or dictionary, found %@.", shipKey, [declaration class]);
*outFatalError = YES;
}
// For frangible ships, bad subentities are non-fatal.
if (*outFatalError && [[shipData dictionaryForKey:shipKey] boolForKey:@"frangible"]) *outFatalError = NO;
return result;
}
- (NSDictionary *) translateOldStyleSubentityDeclaration:(NSString *)declaration
forShip:(NSString *)shipKey
shipData:(NSDictionary *)shipData
fatalError:(BOOL *)outFatalError
{
NSArray *tokens = nil;
NSString *subentityKey = nil;
BOOL isFlasher;
tokens = ScanTokensFromString(declaration);
subentityKey = [tokens objectAtIndex:0];
isFlasher = [subentityKey isEqualToString:@"*FLASHER*"];
// Sanity check: require eight tokens.
if ([tokens count] != 8)
{
if (!isFlasher)
{
OOLog(@"shipData.load.error.badSubentity", @"***** ERROR: the shipdata.plist entry \"%@\" has a broken subentity definition \"%@\" (should have 8 tokens, has %u).", shipKey, subentityKey, [tokens count]);
*outFatalError = YES;
}
else
{
OOLog(@"shipData.load.error.badFlasher", @"----- WARNING: the shipdata.plist entry \"%@\" has a broken flasher definition \"%@\" (should have 8 tokens, has %u). This flasher will be ignored.", shipKey, subentityKey, [tokens count]);
}
return nil;
}
if (isFlasher)
{
return [self translateOldStyleFlasherDeclaration:tokens
forShip:shipKey
fatalError:outFatalError];
}
else
{
return [self translateOldStandardBasicSubentityDeclaration:tokens
forShip:shipKey
shipData:shipData
fatalError:outFatalError];
}
}
- (NSDictionary *) translateOldStyleFlasherDeclaration:(NSArray *)tokens
forShip:(NSString *)shipKey
fatalError:(BOOL *)outFatalError
{
Vector position;
float size, frequency, phase, hue;
NSDictionary *colorDict = nil;
NSDictionary *result = nil;
position.x = [tokens floatAtIndex:1];
position.y = [tokens floatAtIndex:2];
position.z = [tokens floatAtIndex:3];
hue = [tokens floatAtIndex:4];
frequency = [tokens floatAtIndex:5];
phase = [tokens floatAtIndex:6] * 2.0;
size = [tokens floatAtIndex:7];
colorDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:hue] forKey:@"hue"];
result = [NSDictionary dictionaryWithObjectsAndKeys:
@"flasher", @"type",
OOPropertyListFromVector(position), @"position",
[NSArray arrayWithObject:colorDict], @"colors",
[NSNumber numberWithFloat:frequency], @"frequency",
[NSNumber numberWithFloat:phase], @"phase",
[NSNumber numberWithFloat:size], @"size",
nil];
OOLog(@"shipData.translateSubentity.flasher", @"Translated flasher declaration \"%@\" to %@", [tokens componentsJoinedByString:@" "], result);
return result;
}
- (NSDictionary *) translateOldStandardBasicSubentityDeclaration:(NSArray *)tokens
forShip:(NSString *)shipKey
shipData:(NSDictionary *)shipData
fatalError:(BOOL *)outFatalError
{
NSString *subentityKey = nil;
Vector position;
Quaternion orientation;
NSMutableDictionary *result = nil;
BOOL isTurret, isDock = NO;
subentityKey = [tokens stringAtIndex:0];
isTurret = [self shipIsBallTurretForKey:subentityKey inShipData:shipData];
position.x = [tokens floatAtIndex:1];
position.y = [tokens floatAtIndex:2];
position.z = [tokens floatAtIndex:3];
orientation.w = [tokens floatAtIndex:4];
orientation.x = [tokens floatAtIndex:5];
orientation.y = [tokens floatAtIndex:6];
orientation.z = [tokens floatAtIndex:7];
quaternion_normalize(&orientation);
if (!isTurret) isDock = [shipKey rangeOfString:@"dock"].location != NSNotFound;
result = [NSMutableDictionary dictionaryWithCapacity:5];
[result setObject:isTurret ? @"ball_turret" : @"standard" forKey:@"type"];
[result setObject:subentityKey forKey:@"subentity_key"];
[result setVector:position forKey:@"position"];
[result setQuaternion:orientation forKey:@"orientation"];
if (isDock) [result setBool:YES forKey:@"is_dock"];
OOLog(@"shipData.translateSubentity.standard", @"Translated subentity declaration \"%@\" to %@", [tokens componentsJoinedByString:@" "], result);
return [[result copy] autorelease];
}
- (NSDictionary *) validateNewStyleSubentityDeclaration:(NSDictionary *)declaration
forShip:(NSString *)shipKey
fatalError:(BOOL *)outFatalError
{
NSString *type = nil;
type = [declaration stringForKey:@"type"];
if ([type isEqualToString:@"flasher"])
{
return [self validateNewStyleFlasherDeclaration:declaration forShip:shipKey fatalError:outFatalError];
}
else if ([type isEqualToString:@"standard"] || [type isEqualToString:@"ball_turret"])
{
return [self validateNewStyleStandardSubentityDeclaration:declaration forShip:shipKey fatalError:outFatalError];
}
else
{
OOLog(@"shipData.load.error.badSubentity", @"***** ERROR: subentity declaration for ship %@ does not declare a valid type (must be standard, flasher or ball_turret).", shipKey);
*outFatalError = YES;
return nil;
}
}
- (NSDictionary *) validateNewStyleFlasherDeclaration:(NSDictionary *)declaration
forShip:(NSString *)shipKey
fatalError:(BOOL *)outFatalError
{
NSMutableDictionary *result = nil;
Vector position = kZeroVector;
NSArray *colors = nil;
id colorDesc = nil;
float size, frequency, phase;
BOOL initiallyOn;
// "Validate" is really "clean up", since all values have defaults.
colors = [result arrayForKey:@"colors"];
if (colors == nil)
{
colorDesc = [result objectForKey:@"color"];
if (colorDesc == nil) colorDesc = @"redColor";
colors = [NSArray arrayWithObject:colorDesc];
}
position = [declaration vectorForKey:@"position"];
size = [declaration floatForKey:@"size" defaultValue:8.0];
if (size <= 0)
{
OOLog(@"shipData.load.error.badSubentity", @"----- WARNING: skipping flasher of invalid size %g for ship %@.", size, shipKey);
return nil;
}
frequency = [declaration floatForKey:@"frequency" defaultValue:2.0];
phase = [declaration floatForKey:@"phase" defaultValue:0.0];
initiallyOn = [declaration boolForKey:@"initially_on" defaultValue:NO];
result = [NSMutableDictionary dictionaryWithCapacity:7];
[result setObject:@"flasher" forKey:@"type"];
[result setObject:colors forKey:@"colors"];
[result setVector:position forKey:@"position"];
[result setObject:[NSNumber numberWithFloat:size] forKey:@"size"];
[result setObject:[NSNumber numberWithFloat:frequency] forKey:@"frequency"];
if (phase != 0) [result setObject:[NSNumber numberWithFloat:phase] forKey:@"phase"];
if (initiallyOn) [result setObject:[NSNumber numberWithBool:YES] forKey:@"initially_on"];
return [[result copy] autorelease];
}
- (NSDictionary *) validateNewStyleStandardSubentityDeclaration:(NSDictionary *)declaration
forShip:(NSString *)shipKey
fatalError:(BOOL *)outFatalError
{
NSMutableDictionary *result = nil;
NSString *subentityKey = nil;
Vector position = kZeroVector;
Quaternion orientation = kIdentityQuaternion;
BOOL isTurret;
BOOL isDock = NO;
float fireRate;
subentityKey = [declaration objectForKey:@"subentity_key"];
if (subentityKey == nil)
{
OOLog(@"shipData.load.error.badSubentity", @"***** ERROR: subentity declaration for ship %@ specifies no subentity_key.", shipKey);
*outFatalError = YES;
return nil;
}
isTurret = [[declaration stringForKey:@"type"] isEqualToString:@"ball_turret"];
if (isTurret)
{
fireRate = [declaration floatForKey:@"fire_rate" defaultValue:0.5];
}
else
{
isDock = [declaration boolForKey:@"is_dock"];
}
position = [declaration vectorForKey:@"position"];
orientation = [declaration quaternionForKey:@"orientation"];
quaternion_normalize(&orientation);
result = [NSMutableDictionary dictionaryWithCapacity:5];
[result setObject:isTurret ? @"ball_turret" : @"standard" forKey:@"type"];
[result setObject:subentityKey forKey:@"subentity_key"];
[result setVector:position forKey:@"position"];
[result setQuaternion:orientation forKey:@"orientation"];
if (isDock) [result setBool:YES forKey:@"is_dock"];
if (isTurret) [result setFloat:fireRate forKey:@"fire_rate"];
return [[result copy] autorelease];
}
- (BOOL) shipIsBallTurretForKey:(NSString *)shipKey inShipData:(NSDictionary *)shipData
{
// Test for presence of setup_actions containing initialiseTurret.
NSArray *setupActions = nil;
NSEnumerator *actionEnum = nil;
NSString *action = nil;
setupActions = [[shipData dictionaryForKey:shipKey] arrayForKey:@"setup_actions"];
for (actionEnum = [setupActions objectEnumerator]; (action = [actionEnum nextObject]); )
{
if ([[ScanTokensFromString(action) objectAtIndex:0] isEqualToString:@"initialiseTurret"]) return YES;
}
return NO;
}
@end @end

View File

@ -327,6 +327,11 @@ static NSArray *SanitizeActionStatement(NSString *statement, NSString *context)
selectorString = rawSelectorString; selectorString = rawSelectorString;
} }
if ([selectorString isEqualToString:@"doNothing"])
{
return nil;
}
if ([selectorString hasSuffix:@":"]) if ([selectorString hasSuffix:@":"])
{ {
// Expects an argument // Expects an argument