Allow equipment items to "provide" functionality of basis equipment items

Only works for items which are either passive or instantly active but
reusable. Single-use items need some more thought (e.g. if player has
gal drive + another item which provides gal drive, and makes a gal jump,
which one gets removed?) so aren't supported yet.
This commit is contained in:
cim 2014-12-09 20:40:25 +00:00
parent 4c7b5aa465
commit c982a7082c
10 changed files with 84 additions and 28 deletions

View File

@ -111,7 +111,7 @@ MA 02110-1301, USA.
return;
}
if (![PLAYER hasEquipmentItem:@"EQ_ADVANCED_COMPASS"])
if (![PLAYER hasEquipmentItemProviding:@"EQ_ADVANCED_COMPASS"])
{
return;
}

View File

@ -1143,7 +1143,7 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
[self setFastEquipmentA:[dict oo_stringForKey:@"primed_equipment_a" defaultValue:@"EQ_CLOAKING_DEVICE"]];
[self setFastEquipmentB:[dict oo_stringForKey:@"primed_equipment_b" defaultValue:@"EQ_ENERGY_BOMB"]]; // even though there isn't one, for compatibility.
if ([self hasEquipmentItem:@"EQ_ADVANCED_COMPASS"]) compassMode = COMPASS_MODE_PLANET;
if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_COMPASS"]) compassMode = COMPASS_MODE_PLANET;
else compassMode = COMPASS_MODE_BASIC;
DESTROY(compassTarget);
@ -3347,6 +3347,9 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
witchspaceCountdown = fdim(witchspaceCountdown, delta_t);
// damaged gal drive? abort!
/* TODO: this check should possibly be hasEquipmentItemProviding:,
* but if it was we'd need to know which item was actually doing
* the providing so it could be removed. */
if (EXPECT_NOT(galactic_witchjump && ![self hasEquipmentItem:@"EQ_GAL_DRIVE"]))
{
galactic_witchjump = NO;
@ -3410,7 +3413,7 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
{
[UNIVERSE addMessage:[NSString stringWithFormat:@" %@. ",[UNIVERSE getSystemName:system_id]] forCount:3.0];
// and reset the compass
if ([self hasEquipmentItem:@"EQ_ADVANCED_COMPASS"])
if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_COMPASS"])
compassMode = COMPASS_MODE_PLANET;
else
compassMode = COMPASS_MODE_BASIC;
@ -3513,7 +3516,7 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
// If target is an unexpired wormhole and the player has bought the Wormhole Scanner and we're in ID mode
if ([target isWormhole] && [target scanClass] != CLASS_NO_DRAW &&
[self hasEquipmentItem:@"EQ_WORMHOLE_SCANNER"] && ident_engaged)
[self hasEquipmentItemProviding:@"EQ_WORMHOLE_SCANNER"] && ident_engaged)
{
return YES;
}
@ -5061,7 +5064,7 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
if (missile_entity[next_missile])
{
// If we don't have the multi-targeting module installed, clear the active missiles' target
if( ![self hasEquipmentItem:@"EQ_MULTI_TARGET"] && [missile_entity[activeMissile] isMissile] )
if( ![self hasEquipmentItemProviding:@"EQ_MULTI_TARGET"] && [missile_entity[activeMissile] isMissile] )
{
[missile_entity[activeMissile] removeTarget:nil];
}
@ -5076,7 +5079,7 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
// If the newly active pylon contains a missile then work out its target, if any
if( [missile_entity[activeMissile] isMissile] )
{
if( [self hasEquipmentItem:@"EQ_MULTI_TARGET"] &&
if( [self hasEquipmentItemProviding:@"EQ_MULTI_TARGET"] &&
([missile_entity[next_missile] primaryTarget] != nil))
{
// copy the missile's target
@ -5089,7 +5092,7 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
/* CIM: seems okay to do this when launching a
* missile to stop multi-target being a bit
* irritating in a fight - 20/8/2014 */
if([self hasEquipmentItem:@"EQ_MULTI_TARGET"] && !launchingMissile)
if([self hasEquipmentItemProviding:@"EQ_MULTI_TARGET"] && !launchingMissile)
{
[self noteLostTarget];
DESTROY(_primaryTarget);
@ -5991,7 +5994,7 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
[self setVelocity:launchVector];
/* TODO: doing it this way prevents 'providing' on this item */
//remove escape pod
[self removeEquipmentItem:@"EQ_ESCAPE_POD"];
@ -6317,6 +6320,8 @@ NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
[UNIVERSE addMessage:[NSString stringWithFormat:DESC(@"@-damaged"), system_name] forCount:4.5];
}
/* TODO: the nature of this check means that providing
* EQ_DOCK_COMP is difficult. */
// if Docking Computers have been selected to take damage and they happen to be on, switch them off
if ([system_key isEqualToString:@"EQ_DOCK_COMP"] && autopilot_engaged)
{
@ -11064,11 +11069,11 @@ static NSString *last_outfitting_key=nil;
if ([targetEntity isWormhole])
{
assert ([self hasEquipmentItem:@"EQ_WORMHOLE_SCANNER"]);
assert ([self hasEquipmentItemProviding:@"EQ_WORMHOLE_SCANNER"]);
[self addScannedWormhole:(WormholeEntity*)targetEntity];
}
// wormholes don't go in target memory
else if ([self hasEquipmentItem:@"EQ_TARGET_MEMORY"] && targetEntity != nil)
else if ([self hasEquipmentItemProviding:@"EQ_TARGET_MEMORY"] && targetEntity != nil)
{
OOWeakReference *targetRef = [targetEntity weakSelf];
NSUInteger i = [target_memory indexOfObject:targetRef];
@ -11389,7 +11394,7 @@ static NSString *last_outfitting_key=nil;
}
else if ([special isEqualToString:@"LONG_RANGE_CHART_SHORTEST"])
{
if ([self hasEquipmentItem:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
{
_missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_LONG_ANA_SHORTEST;
}
@ -11400,7 +11405,7 @@ static NSString *last_outfitting_key=nil;
}
else if ([special isEqualToString:@"LONG_RANGE_CHART_QUICKEST"])
{
if ([self hasEquipmentItem:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
{
_missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_LONG_ANA_QUICKEST;
}

View File

@ -1024,7 +1024,7 @@ static NSTimeInterval time_last_frame;
// '+' // next target
if ([gameView isDown:key_next_target] || joyButtonState[BUTTON_NEXTTARGET])
{
if ((!next_target_pressed)&&([self hasEquipmentItem:@"EQ_TARGET_MEMORY"]))
if ((!next_target_pressed)&&([self hasEquipmentItemProviding:@"EQ_TARGET_MEMORY"]))
{
[self moveTargetMemoryBy:+1];
}
@ -1036,7 +1036,7 @@ static NSTimeInterval time_last_frame;
// '-' // previous target
if ([gameView isDown:key_previous_target] || joyButtonState[BUTTON_PREVTARGET])
{
if ((!previous_target_pressed)&&([self hasEquipmentItem:@"EQ_TARGET_MEMORY"]))
if ((!previous_target_pressed)&&([self hasEquipmentItemProviding:@"EQ_TARGET_MEMORY"]))
{
[self moveTargetMemoryBy:-1];
}
@ -1349,6 +1349,7 @@ static NSTimeInterval time_last_frame;
exceptionContext = @"galactic hyperspace";
// Galactic hyperspace 'g'
/* TODO: Should be Providing check, but GAL_DRIVE is tricky, see comments in PlayerEntity */
if (([gameView isDown:key_galactic_hyperspace] || joyButtonState[BUTTON_GALACTICDRIVE]) &&
([self hasEquipmentItem:@"EQ_GAL_DRIVE"]))// look for the 'g' key
{
@ -1685,7 +1686,7 @@ static NSTimeInterval time_last_frame;
case GUI_SCREEN_SHORT_RANGE_CHART:
if ([self hasEquipmentItem:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
{
if ([gameView isDown:key_advanced_nav_array]) // '^' key
{

View File

@ -36,7 +36,7 @@ MA 02110-1301, USA.
[self setDialForwardShield:1.0f];
[self setDialAftShield:1.0f];
[self setDialFuelScoopStatus:[self hasScoop] ? SCOOP_STATUS_OKAY : SCOOP_STATUS_NOT_INSTALLED];
[self setCompassMode:[self hasEquipmentItem:@"EQ_ADVANCED_COMPASS"] ? COMPASS_MODE_PLANET : COMPASS_MODE_BASIC];
[self setCompassMode:[self hasEquipmentItemProviding:@"EQ_ADVANCED_COMPASS"] ? COMPASS_MODE_PLANET : COMPASS_MODE_BASIC];
[self setTradeInFactor:95];
}

View File

@ -581,6 +581,7 @@ typedef enum
- (OOWeaponFacingSet) weaponFacings;
- (BOOL) hasEquipmentItem:(id)equipmentKeys includeWeapons:(BOOL)includeWeapons whileLoading:(BOOL)loading; // This can take a string or an set or array of strings. If a collection, returns YES if ship has _any_ of the specified equipment. If includeWeapons is NO, missiles and primary weapons are not checked.
- (BOOL) hasEquipmentItem:(id)equipmentKeys; // Short for hasEquipmentItem:foo includeWeapons:NO whileLoading:NO
- (BOOL) hasEquipmentItemProviding:(NSString *)equipmentType;
- (BOOL) hasAllEquipment:(id)equipmentKeys includeWeapons:(BOOL)includeWeapons whileLoading:(BOOL)loading; // Like hasEquipmentItem:includeWeapons:, but requires _all_ elements in collection.
- (BOOL) hasAllEquipment:(id)equipmentKeys; // Short for hasAllEquipment:foo includeWeapons:NO
- (BOOL) setWeaponMount:(OOWeaponFacing)facing toWeapon:(NSString *)eqKey;

View File

@ -2946,6 +2946,30 @@ ShipEntity* doOctreesCollide(ShipEntity* prime, ShipEntity* other)
}
/* allows OXP equipment to provide core functions (or indeed OXP
* functions, potentially) */
- (BOOL) hasEquipmentItemProviding:(NSString *)equipmentType
{
NSString *key = nil;
foreach (key, _equipment) {
if ([key isEqualToString:equipmentType])
{
// equipment always provides itself
return YES;
}
else
{
OOEquipmentType *et = [OOEquipmentType equipmentTypeWithIdentifier:key];
if (et != nil && [et provides:equipmentType])
{
return YES;
}
}
}
return NO;
}
- (BOOL) hasAllEquipment:(id)equipmentKeys includeWeapons:(BOOL)includeWeapons whileLoading:(BOOL)loading
{
NSEnumerator *keyEnum = nil;
@ -3661,18 +3685,19 @@ ShipEntity* doOctreesCollide(ShipEntity* prime, ShipEntity* other)
- (BOOL) hasScoop
{
return [self hasEquipmentItem:@"EQ_FUEL_SCOOPS"];
return [self hasEquipmentItemProviding:@"EQ_FUEL_SCOOPS"];
}
- (BOOL) hasECM
{
return [self hasEquipmentItem:@"EQ_ECM"];
return [self hasEquipmentItemProviding:@"EQ_ECM"];
}
- (BOOL) hasCloakingDevice
{
/* TODO: Checks above stop this being 'providing'. */
return [self hasEquipmentItem:@"EQ_CLOAKING_DEVICE"];
}
@ -3680,7 +3705,7 @@ ShipEntity* doOctreesCollide(ShipEntity* prime, ShipEntity* other)
- (BOOL) hasMilitaryScannerFilter
{
#if USEMASC
return [self hasEquipmentItem:@"EQ_MILITARY_SCANNER_FILTER"];
return [self hasEquipmentItemProviding:@"EQ_MILITARY_SCANNER_FILTER"];
#else
return NO;
#endif
@ -3690,7 +3715,7 @@ ShipEntity* doOctreesCollide(ShipEntity* prime, ShipEntity* other)
- (BOOL) hasMilitaryJammer
{
#if USEMASC
return [self hasEquipmentItem:@"EQ_MILITARY_JAMMER"];
return [self hasEquipmentItemProviding:@"EQ_MILITARY_JAMMER"];
#else
return NO;
#endif
@ -3699,54 +3724,63 @@ ShipEntity* doOctreesCollide(ShipEntity* prime, ShipEntity* other)
- (BOOL) hasExpandedCargoBay
{
/* Not 'providing' - too many consequences */
return [self hasEquipmentItem:@"EQ_CARGO_BAY"];
}
- (BOOL) hasShieldBooster
{
/* Not 'providing' - too many consequences */
return [self hasEquipmentItem:@"EQ_SHIELD_BOOSTER"];
}
- (BOOL) hasMilitaryShieldEnhancer
{
/* Not 'providing' - too many consequences */
return [self hasEquipmentItem:@"EQ_NAVAL_SHIELD_BOOSTER"];
}
- (BOOL) hasHeatShield
{
return [self hasEquipmentItem:@"EQ_HEAT_SHIELD"];
return [self hasEquipmentItemProviding:@"EQ_HEAT_SHIELD"];
}
- (BOOL) hasFuelInjection
{
return [self hasEquipmentItem:@"EQ_FUEL_INJECTION"];
return [self hasEquipmentItemProviding:@"EQ_FUEL_INJECTION"];
}
- (BOOL) hasCascadeMine
{
/* TODO: this could be providing since theoretically OXP
* deployable mines could also do cascade effects, but there are
* probably better ways to manage OXP pylon AI */
return [self hasEquipmentItem:@"EQ_QC_MINE" includeWeapons:YES whileLoading:NO];
}
- (BOOL) hasEscapePod
{
// TODO: can't be Providing, see comments elsewhere and in PlayerEntity
return [self hasEquipmentItem:@"EQ_ESCAPE_POD"];
}
- (BOOL) hasDockingComputer
{
// TODO: can't be Providing - see comments in PlayerEntity
return [self hasEquipmentItem:@"EQ_DOCK_COMP"];
}
- (BOOL) hasGalacticHyperdrive
{
// TODO: can't be Providing - see comments in PlayerEntity
return [self hasEquipmentItem:@"EQ_GAL_DRIVE"];
}
@ -12741,6 +12775,7 @@ Vector positionOffsetForShipInRotationToAlignment(ShipEntity* ship, Quaternion q
if (![self isPlayer])
{
OK = YES;
/* TODO: prevents 'providing' this function */
[self removeEquipmentItem:@"EQ_ESCAPE_POD"];
[self setAITo:@"nullAI.plist"];
behaviour = BEHAVIOUR_IDLE;

View File

@ -1757,7 +1757,7 @@ static OOTextureSprite *NewTextureSpriteWithDescriptor(NSDictionary *descriptor)
OOSystemID savedDestNumber = 0;
static NSDictionary *routeInfo = nil;
if (advancedNavArrayMode != OPTIMIZED_BY_NONE && [player hasEquipmentItem:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
if (advancedNavArrayMode != OPTIMIZED_BY_NONE && [player hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
{
OOSystemID planetNumber = [UNIVERSE findSystemNumberAtCoords:galaxy_coordinates withGalaxy:galaxy_id];
OOSystemID destNumber = [UNIVERSE findSystemNumberAtCoords:cursor_coordinates withGalaxy:galaxy_id];
@ -2235,7 +2235,7 @@ static OOTextureSprite *NewTextureSpriteWithDescriptor(NSDictionary *descriptor)
advancedNavArrayMode = OPTIMIZED_BY_TIME;
}
if (advancedNavArrayMode != OPTIMIZED_BY_NONE && [player hasEquipmentItem:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
if (advancedNavArrayMode != OPTIMIZED_BY_NONE && [player hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
{
OOSystemID planetNumber = [UNIVERSE findSystemNumberAtCoords:galaxy_coordinates withGalaxy:galaxy_id];
OOSystemID destNumber = [UNIVERSE findSystemNumberAtCoords:cursor_coordinates withGalaxy:galaxy_id];

View File

@ -933,7 +933,7 @@ OOINLINE void GLColorWithOverallAlpha(const GLfloat *color, GLfloat alpha)
{
// check if equipment is required
NSString *equipmentRequired = [info oo_stringForKey:EQUIPMENT_REQUIRED_KEY];
if (equipmentRequired != nil && ![PLAYER hasEquipmentItem:equipmentRequired])
if (equipmentRequired != nil && ![PLAYER hasEquipmentItemProviding:equipmentRequired])
{
return;
}
@ -1013,7 +1013,7 @@ OOINLINE void GLColorWithOverallAlpha(const GLfloat *color, GLfloat alpha)
{
NSString *equipment = [info oo_stringForKey:EQUIPMENT_REQUIRED_KEY];
if (equipment != nil && ![PLAYER hasEquipmentItem:equipment])
if (equipment != nil && ![PLAYER hasEquipmentItemProviding:equipment])
{
return;
}
@ -2536,7 +2536,7 @@ static OOPolygonSprite *IconForMissileRole(NSString *role)
[self drawDirectionCue:info];
}
// extra feature if extra equipment installed
if ([PLAYER hasEquipmentItem:@"EQ_INTEGRATED_TARGETING_SYSTEM"])
if ([PLAYER hasEquipmentItemProviding:@"EQ_INTEGRATED_TARGETING_SYSTEM"])
{
[self drawSecondaryTargetReticle:info];
}
@ -2548,7 +2548,7 @@ static OOPolygonSprite *IconForMissileRole(NSString *role)
GLfloat alpha = [info oo_nonNegativeFloatForKey:ALPHA_KEY defaultValue:1.0f] * overallAlpha * 0.4;
PlayerEntity *player = PLAYER;
if ([player hasEquipmentItem:@"EQ_TARGET_MEMORY"])
if ([player hasEquipmentItemProviding:@"EQ_TARGET_MEMORY"])
{
// needs target memory to be working in addition to any other equipment
// this item may be bound to

View File

@ -65,6 +65,7 @@ SOFTWARE.
NSSet *_requiresAnyEquipment;
NSSet *_incompatibleEquipment;
NSArray *_conditions;
NSArray *_provides;
NSDictionary *_scriptInfo;
NSDictionary *_weaponInfo;
NSString *_script;
@ -131,6 +132,9 @@ SOFTWARE.
- (NSUInteger) installTime;
- (NSUInteger) repairTime;
- (BOOL) provides:(NSString *)key;
// weapon properties
- (BOOL) isTurretLaser;
- (BOOL) isMiningLaser;
- (GLfloat) weaponRange;

View File

@ -230,6 +230,7 @@ static NSDictionary *sMissilesRegistry = nil;
_installTime = [extra oo_unsignedIntForKey:@"installation_time" defaultValue:0];
_repairTime = [extra oo_unsignedIntForKey:@"repair_time" defaultValue:0];
_provides = [[extra oo_arrayForKey:@"provides" defaultValue:[NSArray array]] retain];
_weaponInfo = [[extra oo_dictionaryForKey:@"weapon_info" defaultValue:[NSDictionary dictionary]] retain];
@ -327,6 +328,7 @@ static NSDictionary *sMissilesRegistry = nil;
DESTROY(_incompatibleEquipment);
DESTROY(_conditions);
DESTROY(_condition_script);
DESTROY(_provides);
DESTROY(_weaponInfo);
DESTROY(_scriptInfo);
DESTROY(_script);
@ -585,6 +587,14 @@ static NSDictionary *sMissilesRegistry = nil;
}
- (BOOL) provides:(NSString *)key
{
return [_provides containsObject:key];
}
// weapon properties follow
- (BOOL) isTurretLaser
{
return [_weaponInfo oo_boolForKey:@"is_turret_laser" defaultValue:NO];