sluppickc
sluppickc

Reputation: 13

Updating var value in a class from a method within it

I feel like this is not as difficult as I am making it out to be so any advice would be much appreciated.

I am just messing around in a playground trying to understand classes better. In a gaming scenario, I made a 'Player' class that has a health value and also an attack value. The attack value comes from a different class named 'Weapon', each with their own Double value. I am trying to create a function from within the 'Player' class that when called acts as if a new weapon is being picked up creating a new attack value for the player.

My function called 'weaponPickUp' always says it cannot assign value of Weapon.Type to type Double. I need any advice to make this function work as intended please!

class Player {
    var health = 100.00
    var armor = 0
    var attack: Double

    init() {
        attack = Weapon.init().fist
    }

    func weaponPickUp(weaponPickedUp: Weapon.Type){
        attack = weaponPickedUp
    }

    func attackEnemy(enemy: Enemy) {
        let weaponStrike = enemy.health - attack
        let currentEnemyHealth = weaponStrike

        if currentEnemyHealth > 0 {
            print("Enemy Hit, life remaining \(currentEnemyHealth)")
            enemy.health = currentEnemyHealth
        } else {
            print("Enemy Destroyed")
        }
    }
}


class Weapon {
    var fist = 25.0
    var machette = 35.0
    var special = Enemy.init().health / 2.0
}


class Enemy {
    var health = 100.0
    var armor = 100.0
    var attack1 = 10.0
    var specialAttack = 50.0
}

Upvotes: 1

Views: 78

Answers (2)

Rob Napier
Rob Napier

Reputation: 299663

If this were the whole program, I would definitely write this in Sweeper's style, but it relies on understanding closures, which may be a bit higher learning curve than you want to handle right away. It's also not as flexible if may want it to be if Weapons can have other attributes besides damage.

Another way to approach this problem would be with a simple Weapon protocol:

protocol Weapon {
    func damage(whenAttacking enemy: Enemy) -> Double
}

Now you can have specific weapons. If this were really all a weapon is (a set of stats), then they should probably be structs. But if you track weapons as unique items in the game, you might choose to make them classes. For now, we'll let them be structs (this means that any two Machettes, for example, are the indistinguishable).

struct Fist: Weapon {
    func damage(whenAttacking enemy: Enemy) -> Double {
        return 25
    }
}

struct Machette: Weapon {
    func damage(whenAttacking enemy: Enemy) -> Double {
        return 35
    }
}

struct SpecialWeapon: Weapon {
    func damage(whenAttacking enemy: Enemy) -> Double {
        return enemy.health / 2.0
    }
}

Now some changes to Player. I made some style changes (there's no need for init just to set an initial value), and some substantive changes (how damage is calculated).

class Player {
    var health = 100.00
    var armor = 0
    var weapon: Weapon = Fist()

    // You'd almost certainly not bother with this method, and just let people call
    //     player.weapon = Machette()
    func pickUp(weapon: Weapon){
        self.weapon = weapon
    }

    func attack(enemy: Enemy) {
        let weaponStrike = enemy.health - weapon.damage(whenAttacking: enemy)
        let currentEnemyHealth = weaponStrike

        if currentEnemyHealth > 0 {
            print("Enemy Hit, life remaining \(currentEnemyHealth)")
            enemy.health = currentEnemyHealth
        } else {
            print("Enemy Destroyed")
        }
    }
}

This would be a reasonable approach, but there are of course many, many other ways to do this. In fact, I'd personally move most of the attack code into Enemy this way:

class Enemy {
    private(set) var health = 100.0
    private var armor = 100.0
    private var attack1 = 10.0
    private var specialAttack = 50.0

    var isAlive: Bool {
        return health > 0
    }

    func receiveAttack(from weapon: Weapon) {
        health -= weapon.damage(whenAttacking: self)
        // Personally I'd let health be negative, but feel free to force it to be 0 here   
    }
}

And now, Player.attack looks like this:

func attack(enemy: Enemy) {
    enemy.receiveAttack(from: weapon)

    if enemy.isAlive {
        print("Enemy Hit, life remaining \(enemy.health)")
    } else {
        print("Enemy Destroyed")
    }
}

This puts the logic for modifying the enemy's health inside of enemy. If a new enemy had special powers that protected it against Machettes, then that could be put inside of receiveAttack without having to modify Player (or any other object).

Upvotes: 1

Sweeper
Sweeper

Reputation: 274835

Your Weapon class is not designed very well. In your Weapon class, you seem to have listed types of weapons and their attack damage. But you should not declare them as instance properties. Also, special is always going to have a damage of 50 instead of halving the enemy's health (which I guess is what you intended to do).

I redesigned your weapon class into a struct:

struct Weapon {
    var damage: (Enemy) -> Double
    static let fist = Weapon { _ in return 25.0 }
    static let machette = Weapon { _ in return 35.0 }
    static let special = Weapon { enemy in return enemy.health / 2.0 }
}

damage stores a closure that returns the damage of the weapon. I also made fist, machette and special static constants so you can access them without creating a new instance of a weapon.

Here is the changed the player class:

class Player {
    var health = 100.00
    var armor = 0
    var weapon: Weapon

    init() {
        weapon = Weapon.fist
    }

    func weaponPickUp(weaponPickedUp: Weapon){
        weapon = weaponPickedUp
    }

    func attackEnemy(enemy: Enemy) {
        let weaponStrike = enemy.health - weapon.damage(enemy)
        let currentEnemyHealth = weaponStrike

        if currentEnemyHealth > 0 {
            print("Enemy Hit, life remaining \(currentEnemyHealth)")
            enemy.health = currentEnemyHealth
        } else {
            print("Enemy Destroyed")
        }
    }
}

Since special halves the enemy's health, you can't have a constant attack value in Player independent of how much health the enemy has. So I changed it to a Weapon instance. In attackEnemy, I wrote weapon.damage(enemy) because we need to pass the enemy that we're attacking in order to figure out how much damage the weapon can deal.

Upvotes: 2

Related Questions