Id like to share how Im doing damage and enemy health tracking all criticism welcome!

So Ive devised a way to have a dictionary containing different weapons, that each have there own dictionary inside with their stats.
Example Weapon Dictionary:

plasmaRifle = {"objType":"Gun",
                "objName":"Gauss Plasma Rifle",
                "dmgType1":"Kinetic",
                "dmgType2":"Plasma",
                "speed":"3.5",
                "dmgMin":50,
                "dmgMax":175}

turret = {"objType":"Gun",
            "objName":"Gatling Turret",
            "dmgType1":"Kinetic",
            "dmgType2":"None",
            "speed":"0.5",
            "dmgMin":425,
            "dmgMax":850}

l.objDictionary = {"PlasmaRifle":plasmaRifle,
                    "Turret":turret}

This loop is also in the weaponDictionary Script. We just need a collision sensor on each enemy named ‘Hit’ and this loop auto detects the sensors on each enemy and knows when they get activated and acts accordingly, taking enemy health and ending the enemy if it’s health <= 0

for obj in scene.objects:
    for sensor in obj.sensors:
        if sensor.name == "Hit":
            if obj.sensors['Hit'].hitObject != None:
                Target = str(sensor.owner)
                Player = str(sensor.hitObject['owner'].parent)
                Projectile = sensor.hitObject
                curGun = sensor.hitObject['owner']
                gunName = objDict[str(sensor.hitObject['owner'])]["objName"]
                dmgType1 = objDict[str(sensor.hitObject['owner'])]["dmgType1"]
                dmgType2 = objDict[str(sensor.hitObject['owner'])]["dmgType2"]
                dmgMin = objDict[str(sensor.hitObject['owner'])]["dmgMin"]
                dmgMax = objDict[str(sensor.hitObject['owner'])]["dmgMax"]
                randDmg = random.randrange(dmgMin,dmgMax)
                
                if str(dmgType1+" Resist") in obj:
                    resist = float(obj[str(dmgType1+" Resist")])
                    resistAmt = int(randDmg*resist)
                    dmg = randDmg - resistAmt
                
                if str(dmgType2+" Resist") in obj:
                    resist = float(obj[str(dmgType2+" Resist")])
                    resistAmt = int(randDmg*resist)
                    dmg = randDmg - resistAmt
                
                if str(dmgType1+" Resist") in obj and str(dmgType2+" Resist") in obj:
                    resist = float(obj[str(dmgType1+" Resist")])
                    resist2 = float(obj[str(dmgType2+" Resist")])
                    resistAmt = int(randDmg*resist)
                    resistAmt2 = int(randDmg*resist2)
                    dmg = randDmg - resistAmt - resistAmt2
                    
                    print(Target,"is resisting", resistAmt,dmgType1,"damage and",
                        resistAmt2,dmgType2,"damage","out of",randDmg,"Total damage")
                        
                else:
                    dmg = randDmg
                    
                sensor.owner['health'] -= dmg

    if 'health' in obj:
        if obj['health'] &lt;= 0:
            obj.endObject()

Please feel free to leave any and all comments, advice, tips, and criticism. Its all welcome.
Thanks goes out to agoose77 and SolarLune for coding help and tips. And anyone else if Ive forgotten.

Attachments

Combat_Logic.blend (438 KB)

That’s pretty good. Basically, it’s better to code in modules to, say, add weaponry or different things like that, and your code does this - it’s easy to modify. Good job.

EDIT: It’s easy to hurt the enemies a random amount between dmgmin and dmgmax for each weapon, as well - you would just subtract the difference, and get a random float for that. Multiply the float by the difference, and add that to dmgmin to get any value between dmgmin and dmgmax.

You can also use the random module’s features - I believe there’s one to get a random number between two numbers. Since it’s easier, you might want to try that first.

Thats what I wanna do but I cant make heads or tails of the random api Ive found. I found this when I was searching though


import random
randNum = random.randrange(1,100) ## choose a number between the two

is this correct? Im gonna go try it now, so Ill probably answer myself lol.

Edit: Yea totally answered myself haha, it works.

Edit 2: Is it a bad thing that when the enemy “dies” console tells me “Zombie Object!”

Edit 3: Thank you Solar for mentioning the random module, Ive now added random accuracy to my firing function. here it is

shooter = cont.actuators["fire_bullet"]

fire = sens["fire"]

def Shoot():
    randXv = random.randrange(0,8)
    randZv = random.randrange(0,8)
    shooter.object = 'Bullet'
    shooter.linearVelocity = [randXv,-50,randZv]
    shooter.time = 0
    shooter.instantAddObject()
    lastObj = shooter.objectLastCreated
    lastObj['owner'] = own.parent

if fire.positive == True:
    Shoot()

Nice. If I recall, ‘Zombie Object’ references are caused when you have debug properties visible onscreen (the white text on the side), but the object for that property dies.

Hi, I am kinda new to python scripting. I understand the basics, but there are some stuff I don’t understand. What is objDict[…] and how does it work?

I believe in Argosse’s code, objDict is a dictionary reference to l.objDictionary, which contains his statistics for his weaponry. Using his code set up in this manner, he can pull specific features from each weapon, like l.objDictionary[‘PlasmaRifle’][‘speed’] would give 3.5.

@Argosse - By the way Argosse, maybe you should use actual numbers for the weapons - rather than “3.5”, you can just use the number 3.5.

Solar is correct; objDict = l.objDictionary, and good catch there, I have no idea why I used “” around the speed numbers.

Generally very clean coding, and thus congratulations!
Very similar to my system, although (and here is an idea) i used object properties.
What this means is that if you decide to make a new weapon, rather than hard coding it in, you simply create a gun, and add all the properties.
This can be used to make a customiseable, user contribution game, or for ease of development.

Thank you, SolarLune for explaining it.

Another thing… In Argosse’s way of doing this, the dictionary and the script that checks the “Hit” sensors are in the same script. Is there a way to access the dictionary from another script?

Of course, either make the dictionary a bge.logic varaible, or use modules, but option 1 is easiest.

I just realized I never showed the code that says

l = GameLogic

or

l = bge.logic

so the line that says

l.objDictionary = {plasma...} 

makes the dictionary global per se. Now you can use

bge.logic.objDictionary

just like I’ve used l.objDictionary. I also just realized the line that says

if obj.sensor['hit'].hitObject != None:

needs to be changed to if hit == “projectile”: where projectile is the name of your bullet object .

@Agoose: I don’t know how to do that. Never done it before :(…

Can you tell me how? Or maybe somebody can give a link to a tutorial…

Ok, so i’ll give you a hand.
In blender, we have access to a nifty variable storage on GameLogic or the bge.logic module.
What this means is that you can set/get properties off the module, across scenes, scripts etc.
It’s quite simple.
First, you must import either bge or GameLogic, as you would with any script in the bge.

import bge

Now, let’s say you want to save the dictionary to bge.logic so you can read it in another script:

import bge
     bge.logic.dictionary = {1:2,3:4}

That simple!
to read the dictionary, simply type

import bge
     dic = bge.logic.dictionary

Now to confuse you!
There is another, VERY similar method of storing these kind of ‘global’ variables.
If you use bge.logic.globalDict, it works in the same way as bge.logic.xxxx variables, but allows you to save and load the variables back into the game.
e.g

import bge
     bge.logic.globalDict["dictionary"] = {1:2,3:4}

Ok. Thank you very much, Agoose.

Just some stuff to think about:

1: If you’re using a collision sensor, could you not just base the damage information off of the projectile type, instead of the type of gun that fired the projectile?

2: Your code loops through every single sensor in the game every single frame. Depending on your game, that could be anywhere from a few sensors to hundreds of thousands of them. Not the best idea for scalability, and optimization.

I would suggest you use:
if “hit” in obj.sensors:

instead of:
for sensor in obj.sensors:
if sensor.name == “Hit”:

3: Is it necessary to loop through every single game object every single frame as well? Could you not assemble a list of enemies in the scene during the first frame of the game, and use that list for the rest of the time, adding any dynamically created enemies to that list when they are added to the game?

You could even add any game object with a sensor named “Hit”!

4: Your dictionary of information is fine, but there is no way for you to force each entry to contain the correct information. (say I created a gun in your code that didn’t have a speed value) This may not be a problem if you are working on your game by yourself, but you may want to invite other programmers to join you in the future. If you would like to prevent this issue from happening you can use Object Oriented Programming to force each gun to contain the correct variables.

Object Oriented Programming is extremely useful, and takes a while to get used to, but based on the scripts you have posted, you should be able to figure it out.

Good luck with you project Argosse,
-Gomer

@Argosse
I studied your script and it seems that you didn’t make it check if the objects that collides with your enemy are bullets (or other projectiles). If you just make any other object collide with the enemy, you are going to get an error.

Python script error - object 'Layer1_Hemi', controller 'Python#CONTR#1':
  Traceback (most recent call last):
    File "weapon_Dictionary.py", line 34, in &lt;module&gt;
  KeyError: 'value = gameOb[key]: KX_GameObject, key "owner" does not  exist'

This doesn’t look like it affects your game but it makes a flood of errors in your terminal. I suggest that you add something like this:

 if 'owner' in sensor.hitObject :

P.S. This is the old script so maybe the line number changed…

Edit: After testing, I found that when a random object collides with the enemy ( even with the line added) sometimes prevents it from getting health took away by the bullets. I am not sure what to do…

just make a property on the bullet object called “bullet” then add after this line:

 if obj.sensors['Hit'].hitObject != None:

this code:

if "bullet" in obj.sensors['Hit'].hitObject:

Sorry here’s the new ammoDictionary Ive made.

plasmaRound = {"type":"plasma",
               "dmgMin":15,
               "dmgMax":70}

bullets = {"type":"kinetic",
           "dmgMin":10,
           "dmgMax":20}

gaussRnd = {"type":"kinetic",
            "dmgMin":30,
            "dmgMax":50}

l.ammoDict = {"PlasmaRnd":plasmaRound,
              "KineticRnd":bullets,
              "GaussMagRnd":gaussRnd}

And this function checks if the projectile is in there


def check_ammo(object):
    if str(object) in l.ammoDict:
        return True
    else:
        return False

Here’s the usage in my main loop

for obj in scene.objects:
    for sensor in obj.sensors:
        if sensor.name == "Hit":
            #print(str(sensor.hitObject))
            if sensor.hitObject != None and check_ammo(sensor.hitObject) == True:
                Target = str(sensor.owner)
                Player = str(sensor.hitObject['owner'].parent)
                Projectile = str(sensor.hitObject)
                curGun = sensor.hitObject['owner']
                gunName = objDict[str(curGun)]["objName"]
                dmgType1 = objDict[str(curGun)]["dmgType1"]
                dmgType2 = objDict[str(curGun)]["dmgType2"]
                dmgMin = objDict[str(curGun)]["dmgMin"]
                dmgMax = objDict[str(curGun)]["dmgMax"]
                randDmg = random.randrange(dmgMin,dmgMax)
                
                ammoMinDmg = ammoDict[Projectile]['dmgMin']
                ammoMaxDmg = ammoDict[Projectile]['dmgMax']
                
                randAmmoDmg = random.randrange(ammoMinDmg,ammoMaxDmg)
                
                if str(dmgType1+" Resist") in obj:
                    resist = float(obj[str(dmgType1+" Resist")])
                    resistAmt = int(randDmg*resist)
                    dmg = randDmg - resistAmt + randAmmoDmg
                
                if str(dmgType2+" Resist") in obj:
                    resist = float(obj[str(dmgType2+" Resist")])
                    resistAmt = int(randDmg*resist)
                    dmg = randDmg - resistAmt + randAmmoDmg
                
                if str(dmgType1+" Resist") in obj and str(dmgType2+" Resist") in obj:
                    resist = float(obj[str(dmgType1+" Resist")])
                    resist2 = float(obj[str(dmgType2+" Resist")])
                    resistAmt1 = int(randDmg*resist)
                    resistAmt2 = int(randDmg*resist2)
                    ResistAmt = resistAmt1 + resistAmt2
                    dmg = int(randDmg + randAmmoDmg) - ResistAmt
                    
                    print(Target,"resisted","
",
                    resistAmt1,dmgType1,"
",
                    resistAmt2,dmgType2,"
",
                    randDmg,"Weapon Dmg
",
                    randAmmoDmg,"Ammo Dmg
")
                        
                else:
                    dmg = randDmg
                
                sensor.owner['health'] -= dmg

Here’s the firing function from my other script to show where the sensor.hitObject[‘owner’] comes from


curAmmo = "KineticRnd"
vel = -35
def Fire(ammo, curAmmo, vel):
    ammo -= 1
    own.parent['ammo'] = ammo
    randXv = random.randrange(0,4)
    randZv = random.randrange(0,4)
    created = sce.addObject(curAmmo, own)
    created.setLinearVelocity((randXv,vel,randZv),True)
    created['owner'] = own.parent

On a random note, i can’t wait to show everyone my dynamic weapon system!

argosse,
Your new script still has the issue that if two bullets hit it at the same time, only one will do damage.