Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* prototype save support * make proximity a mating factor again * move TankSettings struct into structs file * ensure food nodes load from saves full-sized * limit tank scene to 60 fps * create core data models * create NSManagedObject subclasses * recreate data model * saving/loading tanks to/from core data works so far * whoops, forgot to save tank settings to data store * previous save is loaded automatically on app launch * add a fade-in on initial load of saved tank * turn off scene debug display * reorganize files * ensure only one copy of a tank is saved to storage. at a time * rename some methods * set up timer to auto-save tank every 10 seconds
- Loading branch information
Showing
77 changed files
with
2,233 additions
and
2,758 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
197 changes: 197 additions & 0 deletions
197
Aeon Garden Shared/Models/Core Data/CoreDataStore.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
// | ||
// CoreDataStore.swift | ||
// Aeon Garden | ||
// | ||
// Created by Brad Root on 10/4/19. | ||
// Copyright © 2019 Brad Root. All rights reserved. | ||
// | ||
|
||
import CoreData | ||
import Foundation | ||
|
||
class CoreDataStore { | ||
static let standard: CoreDataStore = CoreDataStore() | ||
|
||
var mainManagedObjectContext: NSManagedObjectContext | ||
var persistentContainer: NSPersistentContainer | ||
|
||
init() { | ||
persistentContainer = { | ||
let container = NSPersistentContainer(name: "AeonGarden") | ||
container.loadPersistentStores(completionHandler: { _, error in | ||
if let error = error as NSError? { | ||
fatalError("Unresolved error \(error), \(error.userInfo)") | ||
} | ||
}) | ||
return container | ||
}() | ||
mainManagedObjectContext = persistentContainer.viewContext | ||
} | ||
|
||
deinit { | ||
self.saveContext() | ||
} | ||
|
||
func saveContext() { | ||
if mainManagedObjectContext.hasChanges { | ||
do { | ||
try mainManagedObjectContext.save() | ||
} catch { | ||
// Replace this implementation with code to handle the error appropriately. | ||
// fatalError() causes the application to generate a crash log and terminate. | ||
// You should not use this function in a shipping application, although it | ||
// may be useful during development. | ||
let nserror = error as NSError | ||
fatalError("Unresolved error \(nserror), \(nserror.userInfo)") | ||
} | ||
} | ||
} | ||
} | ||
|
||
extension CoreDataStore: DataStoreProtocol { | ||
|
||
// MARK: - Creature Favorites | ||
func saveCreature(_ creature: Creature) { | ||
fatalError() | ||
} | ||
|
||
func deleteCreature(_ creature: Creature) { | ||
fatalError() | ||
} | ||
|
||
func getCreatures(completion: @escaping ([Creature]) -> Void) { | ||
fatalError() | ||
} | ||
|
||
// MARK: - Tanks | ||
|
||
func getTanks(completion: @escaping ([Tank]) -> Void) { | ||
mainManagedObjectContext.perform { | ||
do { | ||
let fetchRequest: NSFetchRequest<ManagedTank> = ManagedTank.fetchRequest() | ||
let managedTanks = try self.mainManagedObjectContext.fetch(fetchRequest) as [ManagedTank] | ||
var tanks: [Tank] = [] | ||
for managedTank in managedTanks { | ||
tanks.append(managedTank.toStruct()) | ||
} | ||
completion(tanks) | ||
} catch { | ||
completion([]) | ||
} | ||
} | ||
} | ||
|
||
func saveTank(_ tank: Tank) { | ||
mainManagedObjectContext.perform { | ||
do { | ||
// Check for tank in storage by UUID | ||
var storedTank: ManagedTank? | ||
let fetchRequest: NSFetchRequest<ManagedTank> = ManagedTank.fetchRequest() | ||
fetchRequest.predicate = NSPredicate(format: "uuid == %@", tank.uuid.uuidString) | ||
if let tank = try? self.mainManagedObjectContext.fetch(fetchRequest).first { | ||
storedTank = tank | ||
} else { | ||
storedTank = ManagedTank(context: self.mainManagedObjectContext) | ||
} | ||
|
||
guard let managedTank = storedTank else { | ||
Log.error("Could not load or create new managed tank object!") | ||
return | ||
} | ||
|
||
managedTank.timestamp = Date() | ||
managedTank.birthCount = Int16(tank.birthCount) | ||
managedTank.deathCount = Int16(tank.deathCount) | ||
managedTank.tankTime = tank.tankTime | ||
managedTank.uuid = tank.uuid | ||
|
||
var managedCreatures: [ManagedTankCreature] = [] | ||
for creature in tank.creatures { | ||
let managedTankCreature = ManagedTankCreature(context: self.mainManagedObjectContext) | ||
managedTankCreature.uuid = creature.uuid | ||
managedTankCreature.firstName = creature.firstName | ||
managedTankCreature.lastName = creature.lastName | ||
|
||
managedTankCreature.currentHealth = creature.currentHealth | ||
managedTankCreature.lifeTime = creature.lifeTime | ||
|
||
managedTankCreature.isFavorite = creature.isFavorite | ||
|
||
managedTankCreature.movementSpeed = creature.movementSpeed | ||
managedTankCreature.turnSpeed = creature.turnSpeed | ||
managedTankCreature.sizeModifier = creature.sizeModifier | ||
managedTankCreature.primaryHue = creature.primaryHue | ||
|
||
managedTankCreature.positionX = creature.positionX | ||
managedTankCreature.positionY = creature.positionY | ||
|
||
var managedLimbs: [ManagedLimb] = [] | ||
for limb in creature.limbs { | ||
let managedLimb = ManagedLimb(context: self.mainManagedObjectContext) | ||
managedLimb.shape = limb.shape.rawValue | ||
managedLimb.hue = limb.hue | ||
managedLimb.blend = limb.blend | ||
managedLimb.brightness = limb.brightness | ||
managedLimb.saturation = limb.saturation | ||
managedLimb.limbWidth = Int16(limb.limbWidth) | ||
|
||
managedLimb.wiggleFactor = limb.wiggleFactor | ||
managedLimb.wiggleMoveFactor = limb.wiggleMoveFactor | ||
managedLimb.wiggleMoveBackFactor = limb.wiggleMoveBackFactor | ||
managedLimb.wiggleActionDuration = limb.wiggleActionDuration | ||
managedLimb.wiggleActionBackDuration = limb.wiggleActionBackDuration | ||
managedLimb.wiggleActionMovementDuration = limb.wiggleActionMovementDuration | ||
managedLimb.wiggleActionMovementBackDuration = limb.wiggleActionMovementBackDuration | ||
|
||
managedLimb.limbzRotation = limb.limbzRotation | ||
|
||
managedLimb.positionX = limb.positionX | ||
managedLimb.positionY = limb.positionY | ||
|
||
managedLimbs.append(managedLimb) | ||
} | ||
managedTankCreature.limbs = NSSet(array: managedLimbs) | ||
|
||
managedCreatures.append(managedTankCreature) | ||
} | ||
managedTank.creatures = NSSet(array: managedCreatures) | ||
|
||
var managedBubbles: [ManagedBubble] = [] | ||
for bubble in tank.bubbles { | ||
let managedBubble = ManagedBubble(context: self.mainManagedObjectContext) | ||
managedBubble.positionY = bubble.positionY | ||
managedBubble.positionX = bubble.positionX | ||
managedBubbles.append(managedBubble) | ||
} | ||
managedTank.bubbles = NSSet(array: managedBubbles) | ||
|
||
var managedFoods: [ManagedFood] = [] | ||
for food in tank.food { | ||
let managedFood = ManagedFood(context: self.mainManagedObjectContext) | ||
managedFood.positionY = food.positionY | ||
managedFood.positionX = food.positionX | ||
managedFoods.append(managedFood) | ||
} | ||
managedTank.food = NSSet(array: managedFoods) | ||
|
||
let managedTankSettings = ManagedTankSettings(context: self.mainManagedObjectContext) | ||
managedTankSettings.foodMaxAmount = Int16(tank.tankSettings.foodMaxAmount) | ||
managedTankSettings.foodHealthRestorationBaseValue = tank.tankSettings.foodHealthRestorationBaseValue | ||
managedTankSettings.foodSpawnRate = Int16(tank.tankSettings.foodSpawnRate) | ||
managedTankSettings.creatureInitialAmount = Int16(tank.tankSettings.creatureInitialAmount) | ||
managedTankSettings.creatureMinimumAmount = Int16(tank.tankSettings.creatureMinimumAmount) | ||
managedTankSettings.creatureSpawnRate = Int16(tank.tankSettings.creatureSpawnRate) | ||
managedTankSettings.creatureBirthSuccessRate = tank.tankSettings.creatureBirthSuccessRate | ||
managedTankSettings.backgroundParticleBirthrate = Int16(tank.tankSettings.backgroundParticleBirthrate) | ||
managedTankSettings.backgroundParticleLifetime = Int16(tank.tankSettings.backgroundParticleLifetime) | ||
|
||
managedTank.tankSettings = managedTankSettings | ||
|
||
try self.mainManagedObjectContext.save() | ||
|
||
} catch { | ||
Log.error("Tank failed to save to storage.") | ||
} | ||
} | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
...en Shared/Models/Core Data/Models/AeonGarden.xcdatamodeld/AeonGarden.xcdatamodel/contents
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14903" systemVersion="18G95" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> | ||
<entity name="ManagedBubble" representedClassName="ManagedBubble" syncable="YES"> | ||
<attribute name="positionX" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="positionY" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<relationship name="tank" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ManagedTank" inverseName="bubbles" inverseEntity="ManagedTank"/> | ||
</entity> | ||
<entity name="ManagedCreature" representedClassName="ManagedCreature" syncable="YES"> | ||
<attribute name="firstName" attributeType="String"/> | ||
<attribute name="lastName" attributeType="String"/> | ||
<attribute name="movementSpeed" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="primaryHue" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="sizeModifier" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="turnSpeed" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="uuid" attributeType="UUID" usesScalarValueType="NO"/> | ||
<relationship name="limbs" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="ManagedLimb" inverseName="creature" inverseEntity="ManagedLimb"/> | ||
</entity> | ||
<entity name="ManagedFood" representedClassName="ManagedFood" syncable="YES"> | ||
<attribute name="positionX" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="positionY" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<relationship name="tank" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ManagedTank" inverseName="food" inverseEntity="ManagedTank"/> | ||
</entity> | ||
<entity name="ManagedLimb" representedClassName="ManagedLimb" syncable="YES"> | ||
<attribute name="blend" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="brightness" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="hue" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="limbWidth" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<attribute name="limbzRotation" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="positionX" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="positionY" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="saturation" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="shape" attributeType="String"/> | ||
<attribute name="wiggleActionBackDuration" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="wiggleActionDuration" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="wiggleActionMovementBackDuration" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="wiggleActionMovementDuration" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="wiggleFactor" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="wiggleMoveBackFactor" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="wiggleMoveFactor" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<relationship name="creature" maxCount="1" deletionRule="Nullify" destinationEntity="ManagedCreature" inverseName="limbs" inverseEntity="ManagedCreature"/> | ||
</entity> | ||
<entity name="ManagedTank" representedClassName="ManagedTank" syncable="YES"> | ||
<attribute name="birthCount" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<attribute name="deathCount" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<attribute name="tankTime" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="timestamp" attributeType="Date" usesScalarValueType="NO"/> | ||
<attribute name="uuid" optional="YES" attributeType="UUID" usesScalarValueType="NO"/> | ||
<relationship name="bubbles" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="ManagedBubble" inverseName="tank" inverseEntity="ManagedBubble"/> | ||
<relationship name="creatures" toMany="YES" deletionRule="Cascade" destinationEntity="ManagedTankCreature" inverseName="tank" inverseEntity="ManagedTankCreature"/> | ||
<relationship name="food" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="ManagedFood" inverseName="tank" inverseEntity="ManagedFood"/> | ||
<relationship name="tankSettings" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ManagedTankSettings" inverseName="tank" inverseEntity="ManagedTankSettings"/> | ||
</entity> | ||
<entity name="ManagedTankCreature" representedClassName="ManagedTankCreature" parentEntity="ManagedCreature" syncable="YES"> | ||
<attribute name="currentHealth" attributeType="Float" defaultValueString="0.0"/> | ||
<attribute name="isFavorite" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> | ||
<attribute name="lifeTime" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="positionX" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="positionY" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<relationship name="tank" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ManagedTank" inverseName="creatures" inverseEntity="ManagedTank"/> | ||
</entity> | ||
<entity name="ManagedTankSettings" representedClassName="ManagedTankSettings" syncable="YES"> | ||
<attribute name="backgroundParticleBirthrate" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<attribute name="backgroundParticleLifetime" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<attribute name="creatureBirthSuccessRate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="creatureInitialAmount" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<attribute name="creatureMinimumAmount" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<attribute name="creatureSpawnRate" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<attribute name="foodHealthRestorationBaseValue" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/> | ||
<attribute name="foodMaxAmount" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<attribute name="foodSpawnRate" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> | ||
<relationship name="tank" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ManagedTank" inverseName="tankSettings" inverseEntity="ManagedTank"/> | ||
</entity> | ||
<elements> | ||
<element name="ManagedBubble" positionX="368.70703125" positionY="111.85546875" width="128" height="88"/> | ||
<element name="ManagedCreature" positionX="-26.3359375" positionY="-118.1875" width="128" height="163"/> | ||
<element name="ManagedFood" positionX="391.83203125" positionY="6.43359375" width="128" height="88"/> | ||
<element name="ManagedTank" positionX="-200.9921875" positionY="48.17578125" width="128" height="178"/> | ||
<element name="ManagedTankSettings" positionX="156.62890625" positionY="194.0703125" width="128" height="193"/> | ||
<element name="ManagedTankCreature" positionX="149.62890625" positionY="-8.6640625" width="128" height="133"/> | ||
<element name="ManagedLimb" positionX="9" positionY="36" width="128" height="298"/> | ||
</elements> | ||
</model> |
16 changes: 16 additions & 0 deletions
16
Aeon Garden Shared/Models/Core Data/Models/ManagedBubble+CoreDataClass.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// ManagedBubble+CoreDataClass.swift | ||
// Aeon Garden | ||
// | ||
// Created by Brad Root on 10/6/19. | ||
// Copyright © 2019 Brad Root. All rights reserved. | ||
// | ||
// | ||
|
||
import Foundation | ||
import CoreData | ||
|
||
@objc(ManagedBubble) | ||
public class ManagedBubble: NSManagedObject { | ||
|
||
} |
30 changes: 30 additions & 0 deletions
30
Aeon Garden Shared/Models/Core Data/Models/ManagedBubble+CoreDataProperties.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// ManagedBubble+CoreDataProperties.swift | ||
// Aeon Garden | ||
// | ||
// Created by Brad Root on 10/6/19. | ||
// Copyright © 2019 Brad Root. All rights reserved. | ||
// | ||
// | ||
|
||
import Foundation | ||
import CoreData | ||
|
||
|
||
extension ManagedBubble { | ||
|
||
@nonobjc public class func fetchRequest() -> NSFetchRequest<ManagedBubble> { | ||
return NSFetchRequest<ManagedBubble>(entityName: "ManagedBubble") | ||
} | ||
|
||
@NSManaged public var positionX: Float | ||
@NSManaged public var positionY: Float | ||
@NSManaged public var tank: ManagedTank | ||
|
||
} | ||
|
||
extension ManagedBubble { | ||
func toStruct() -> Bubble { | ||
return Bubble(positionX: self.positionX, positionY: self.positionY) | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
Aeon Garden Shared/Models/Core Data/Models/ManagedCreature+CoreDataClass.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// ManagedCreature+CoreDataClass.swift | ||
// Aeon Garden | ||
// | ||
// Created by Brad Root on 10/6/19. | ||
// Copyright © 2019 Brad Root. All rights reserved. | ||
// | ||
// | ||
|
||
import Foundation | ||
import CoreData | ||
|
||
@objc(ManagedCreature) | ||
public class ManagedCreature: NSManagedObject { | ||
|
||
} |
Oops, something went wrong.