Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions DeepLevel/CityMapGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,16 @@ final class CityMapGenerator: DungeonGenerating {
tiles[idx].kind = actualTileKind
tiles[idx].scale = scaleFactor

// Add soil properties for park tiles
if districtType == .park {
tiles[idx].soilProperties = SoilProperties()
}

if districtType == .park && Double.random(in: 0..<1, using: &rng) < 0.5 {
tiles[idx].kind = .hidingArea
tiles[idx].scale = 1.0
// Hiding areas in parks should also have soil properties
tiles[idx].soilProperties = SoilProperties()
}
}
}
Expand Down
53 changes: 51 additions & 2 deletions DeepLevel/Entity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ final class Charmed: Entity {
final class Player: Entity {
var inventory: [StoredItem] = []
private var lastHealTime: TimeInterval = 0
var soilTestResults: [SoilTestResult] = []

init(gridX: Int, gridY: Int, tileSize: CGFloat, scale: CGFloat = 1.0) {
super.init(kind: .player,
Expand All @@ -146,9 +147,57 @@ final class Player: Entity {
return CACurrentMediaTime() - lastHealTime < 10.0 // 10 seconds
}

/// Check if player has any soil testing equipment
func hasSoilTestingEquipment() -> Bool {
return inventory.contains { item in
["pH Test Kit", "Moisture Meter", "Soil Thermometer", "Soil Probe", "NPK Test Kit"].contains(item.title)
}
}

/// Get available soil testing equipment
func availableSoilTestingEquipment() -> [StoredItem] {
return inventory.filter { item in
Player.soilTestingEquipmentNames.contains(item.title)
}
}

/// Get available soil testing equipment
func availableSoilTestingEquipment() -> [StoredItem] {
return inventory.filter { item in
Player.soilTestingEquipmentNames.contains(item.title)
}
}

/// Perform a soil test using the first available equipment
func performSoilTest(at location: (Int, Int), soilProperties: SoilProperties) -> SoilTestResult? {
guard let equipment = availableSoilTestingEquipment().first else {
return nil
}

let result = SoilTestResult(location: location, equipment: equipment.title, soilProperties: soilProperties)
soilTestResults.append(result)
return result
}

/// Perform a soil test using specific equipment
func performSoilTest(at location: (Int, Int), soilProperties: SoilProperties, using equipmentName: String) -> SoilTestResult? {
guard inventory.contains(where: { $0.title == equipmentName }) else {
return nil
}

let result = SoilTestResult(location: location, equipment: equipmentName, soilProperties: soilProperties)
soilTestResults.append(result)
return result
}

private func initializeStartingInventory(tileSize: CGFloat) {
let startingItemDefs = ItemDatabase.randomItems(count: 3)
for itemDef in startingItemDefs {
// Start with 2 random items and 1 soil testing equipment
let randomItems = ItemDatabase.randomItems(count: 2)
let soilTestingItems = ItemDatabase.randomItems(from: .soilTesting, count: 1)

let allStartingItems = randomItems + soilTestingItems

for itemDef in allStartingItems {
let item = StoredItem(name: itemDef.name,
description: itemDef.description,
gridX: gridX, // Items start at player position conceptually
Expand Down
73 changes: 72 additions & 1 deletion DeepLevel/GameScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -894,10 +894,18 @@ final class GameScene: SKScene {
y: CGFloat(player.gridY) * tileSize + tileSize/2
)

// Check if tapping on or very close to the player - if so, stop movement
// Check if tapping on or very close to the player - if so, stop movement or perform soil test
let distanceToPlayer = hypot(location.x - playerScreenPos.x, location.y - playerScreenPos.y)
if distanceToPlayer < tileSize {
continuousMovementDir = (0, 0)

// Check if player can perform soil test at current location
if let map = map {
let currentTile = map.tiles[map.index(x: player.gridX, y: player.gridY)]
if currentTile.canTestSoil && player.hasSoilTestingEquipment() {
performSoilTest(player: player, tile: currentTile, location: (player.gridX, player.gridY))
}
}
return
}

Expand Down Expand Up @@ -932,6 +940,69 @@ final class GameScene: SKScene {
camera.run(zoomAction)
}

// MARK: - Soil Testing

/// Perform a soil test at the player's current location
private func performSoilTest(player: Player, tile: Tile, location: (Int, Int)) {
guard let soilProperties = tile.soilProperties else { return }

// Perform the soil test using the first available equipment
if let result = player.performSoilTest(at: location, soilProperties: soilProperties) {
// Mark the soil as tested
if let map = map {
Copy link

Copilot AI Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The tile mutation logic is complex and updates the map directly. Consider moving this logic to a dedicated method or making the tile's soilProperties mutable through a cleaner interface.

Copilot uses AI. Check for mistakes.
var mutableTile = map.tiles[map.index(x: location.0, y: location.1)]
if var properties = mutableTile.soilProperties {
properties.hasBeenTested = true
mutableTile.soilProperties = properties
// Update the tile in the map
map.tiles[map.index(x: location.0, y: location.1)] = mutableTile
}
}

// Display the soil test result to the player
showSoilTestResult(result)

if debugLogging {
print("[GameScene] Soil test performed at (\(location.0), \(location.1)) using \(result.equipmentUsed)")
print("[GameScene] Result: \(result.assessment)")
}
}
}

/// Display soil test results to the player
private func showSoilTestResult(_ result: SoilTestResult) {
// Create a temporary label to show the test result
let resultLabel = SKLabelNode(fontNamed: "Arial")
resultLabel.text = result.assessment
resultLabel.fontSize = 16
resultLabel.fontColor = .white
resultLabel.position = CGPoint(x: 0, y: 100) // Position above player
resultLabel.zPosition = 1000 // Ensure it's on top

// Add background for better readability
let background = SKShapeNode(rectOf: CGSize(width: resultLabel.frame.width + 20, height: 30))
background.fillColor = SKColor.black.withAlphaComponent(0.8)
background.strokeColor = .clear
background.position = resultLabel.position
background.zPosition = 999

// Add to camera node so it stays in view
if let camera = camNode {
camera.addChild(background)
camera.addChild(resultLabel)

// Animate the label appearance and removal
let fadeIn = SKAction.fadeAlpha(to: 1.0, duration: 0.3)
let wait = SKAction.wait(forDuration: 3.0)
let fadeOut = SKAction.fadeAlpha(to: 0.0, duration: 0.5)
let remove = SKAction.removeFromParent()
let sequence = SKAction.sequence([fadeIn, wait, fadeOut, remove])

background.run(sequence)
resultLabel.run(sequence)
}
}

// .,
}

Expand Down
11 changes: 10 additions & 1 deletion DeepLevel/ItemDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct ItemDatabase {
case useful
case food
case curious
case soilTesting
}

/// Definition of an item with its properties.
Expand Down Expand Up @@ -70,7 +71,15 @@ struct ItemDatabase {
ItemDefinition(name: "Marble", description: "A single glass marble, green with swirls.", category: .curious, value: 8),
ItemDefinition(name: "Lottery Ticket", description: "A scratched lottery ticket. Looks like a loser.", category: .curious, value: 1),
ItemDefinition(name: "USB Drive", description: "A tiny USB drive. Wonder what's on it?", category: .curious, value: 20),
ItemDefinition(name: "Toy Soldier", description: "A small plastic soldier missing his rifle.", category: .curious, value: 7)
ItemDefinition(name: "Toy Soldier", description: "A small plastic soldier missing his rifle.", category: .curious, value: 7),

// Soil Testing Equipment
ItemDefinition(name: "pH Test Kit", description: "A portable soil pH testing kit with color strips.", category: .soilTesting, value: 25),
ItemDefinition(name: "Soil Probe", description: "A metal probe for testing soil compaction and depth.", category: .soilTesting, value: 30),
ItemDefinition(name: "Moisture Meter", description: "Digital device for measuring soil moisture content.", category: .soilTesting, value: 35),
ItemDefinition(name: "Soil Thermometer", description: "Specialized thermometer for measuring soil temperature.", category: .soilTesting, value: 20),
ItemDefinition(name: "NPK Test Kit", description: "Tests for nitrogen, phosphorus, and potassium levels.", category: .soilTesting, value: 40),
ItemDefinition(name: "Soil Sample Bag", description: "Sterile bag for collecting soil samples for analysis.", category: .soilTesting, value: 5)
]

/// Get a random item from the database.
Expand Down
Loading