SOLVED : Player damage based on game difficulty

Hello
i was working on a shooting system for a while and changed it from logic bricks and python to python only but the old system had a damage based on bullet’s properties , when it collides with the player an actuator subtracts player hp … etc
but now i use Cotaks bullet script and edited it to suit my project , the problem is i want to set multiple damages based on game difficulty .
Example :




here we have 2 bullets and a player , the red bullet ( damage 2 ) will damage the player by 20 and yellow by 30

what i am searching for is a way to change this :

some how the script will know bullet property and copy the name to player’s property and subtract the value from player’s health

that’s what i am aiming to , if you have a better method for damaging according to game difficulty it would be awesome !

Thanks in advance

oh yes and i use this script to read game difficultly and set damage properties

from bge import logic

cont = logic.getCurrentController()
own = cont.owner

if own["difficulty"] == 'Easy':
    own["damage1"] = 2
    own["damage2"] = 4
    own["damage3"] = 40

if own["difficulty"] == 'Normal':
    own["damage1"] = 5
    own["damage2"] = 6
    own["damage3"] = 100

if own["difficulty"] == 'Hard':
    own["damage1"] = 10
    own["damage2"] = 12
    own["damage3"] = 160

why such a hard method?
just change the bullet you fire.

i thought about that but if i will add new bullets means i should read the difficulty every time for each object ! ( the project is huge )

or you can simply do this and use 1 bullet

from bge import logic

# place on the bullet
# always (true pulse) -> python(module) ->scriptname.bullet_action
# make sure the script in text editor ends with .py

def bullet_action(cont):

    own = cont.owner
    
    # how fast bullet goes 
    speed = 340
    
    # damage bullet does
    difficulty = own.scene.objects['object that holds the property']['game_difficulty']
    
    if difficulty == 'easy':
        damage = 50
    elif difficulty == 'normal':
        damage = 25
    elif difficulty == 'hard':  
        damage = 10
    else:
        damage = 25 #default 
        
    # how much the bullet needs to drop
    bullet_drop = 0.0001
    # plane that represent the bullet_hole
    hole = own.scene.objects['bullet_hole']

    distance = speed/logic.getLogicTicRate()

    own.applyMovement([0,distance,-bullet_drop], 1)

    y_vec = own.worldOrientation.copy().col[1] 
    y_vec.magnitude = distance

    end_vec = own.worldPosition.copy() + y_vec
    ray = own.rayCast(end_vec, own.worldPosition.copy(), 0)

    if ray[0]:

        if 'health' in ray[0]:
            ray[0]['health'] -= damage
            
            if ray[0]['health'] <= 0:
                ray[0].endObject()
            
        bullet_hole(cont,ray,hole)
        
        
def bullet_hole(cont,ray,bullet_hole):
    
    own = cont.owner
    own.endObject()
 
    bullet_hole = own.scene.addObject(bullet_hole, ray[0], 300)
    
    offset = ray[2]
    offset.magnitude = 0.001
    
    bullet_hole.alignAxisToVect(ray[2], 2, 1)
    bullet_hole.worldPosition = ray[1] + offset
    bullet_hole.setParent(ray[0])

it would work for single bullet , but in that project i have 4 (enemy) bullets with different damages … using multiple scripts for each bullet will affect the gamplay as a single command line or what do you think ? inform me please :slightly_smiling_face:

EDIT : this is the project

if you have 4 differend bullets? then add property on the bulled called damage, then simply do

damage = own['damage']
from bge import logic

# place on the bullet
# always (true pulse) -> python(module) ->scriptname.bullet_action
# make sure the script in text editor ends with .py

def bullet_action(cont):

    own = cont.owner
    
    # how fast bullet goes 
    speed = 340
    
    # damage bullet does
    difficulty = own.scene.objects['object that holds the property']['game_difficulty']
    damage = own['damage']
    
    if difficulty == 'easy':
        damage *= 2
    elif difficulty == 'normal':
        damage = damage
    elif difficulty == 'hard':  
        damage /= 2
        
    # how much the bullet needs to drop
    bullet_drop = 0.0001
    # plane that represent the bullet_hole
    hole = own.scene.objects['bullet_hole']

    distance = speed/logic.getLogicTicRate()

    own.applyMovement([0,distance,-bullet_drop], 1)

    y_vec = own.worldOrientation.copy().col[1] 
    y_vec.magnitude = distance

    end_vec = own.worldPosition.copy() + y_vec
    ray = own.rayCast(end_vec, own.worldPosition.copy(), 0)

    if ray[0]:

        if 'health' in ray[0]:
            ray[0]['health'] -= damage
            
            if ray[0]['health'] <= 0:
                ray[0].endObject()
            
        bullet_hole(cont,ray,hole)
        
        
def bullet_hole(cont,ray,bullet_hole):
    
    own = cont.owner
    own.endObject()
 
    bullet_hole = own.scene.addObject(bullet_hole, ray[0], 300)
    
    offset = ray[2]
    offset.magnitude = 0.001
    
    bullet_hole.alignAxisToVect(ray[2], 2, 1)
    bullet_hole.worldPosition = ray[1] + offset
    bullet_hole.setParent(ray[0])

so use 1 object that holds the game difficulty and give all bullets a property damage that gonna be used as the normal damage.

for easy we do damage x2
for normal we just keep the bullet damage itself
for hard we do damage / 2

I use a simple bullet method - this is a damage dictionary and a random selection of the damage range - the same bullet can be a bullet from a pistol or it can be a bullet from a machine gun, units when shooting at a player can select the desired type and write it to the property “type_damage”

import bge
import random
from bge import logic as GL

def bullet(cont):
    own = cont.owner
    
    type_damage = own['type_damage']

    info_damage = {"low":[5, 10], "medium":[20, 30], "hard":[50, 100]}

    ray = cont.sensors['Ray']
    if ray.positive:
        obj = ray.hitObject
        if 'health' in obj:
             damage       = info_damage[own['type_damage']]
             obj['health'] -= random.randint(int(damage))
             own.endObject()
             

your script got some flaws.

  1. bullet speed will change depending on the fps
  2. your bulets will miss 25+% of the time
  3. bullets that don’t hit a target with a health property will not be killed, and will be alive for the entire game duration, over time this will greatly slowdown or stall/crash the engine.

The dict usage is indeed a handy setup. but now you have preset damage numbers for basically any weapon you want to use for example a pisol or a rpg, so separating the game_difficulty from the weapon is the best action to take. a weapon should not hold the difficulty.

in the topic the TS linked in first post, i have an other script there, it uses a dict like this:

def grab_weapons(cont):
    
    own = cont.owner
    
    own['weapons'] = {   
    
                    'pistol':{  
                                'range'                 : 20,
                                'ammo'                  : 22,
                                'spare_ammo'            : 66,
                                'max_ammo'              : 132,
                                'clip_size'             : 22,
                                'bullet'                : 'plasma_bullet',
                                'bullet_damage'         : 35,
                                'bullet_speed'          : 25,
                                'bullet_drop'           : 0.00001,
                                'bullet_spray_spread'   : 0.015, #higher = bigger spray
                                'bullet_spray_reset'    : 0.40, #higher = less accurate
                                'bullet_hole'           : 'plasma_hole',
                                'arms_animation'        : 'arms_pistol',
                                'weapon_animation'      : 'pistol_firing', 
                                'sound_firing'          : 'gun-gunshot-02.wav',
                                'sound_reload'          : 'clickclick.wav'     
                             },
                             
                    'baseballbat':{  
                                'range'                 : 2,
                                'ammo'                  : 1,
                                'spare_ammo'            : 0,
                                'max_ammo'              : 1,
                                'clip_size'             : 1,
                                'bullet'                : 'plasma_bullet',
                                'bullet_damage'         : 45,
                                'bullet_speed'          : 25,
                                'bullet_drop'           : 0.00001,
                                'bullet_spray_spread'   : 0.05,
                                'bullet_spray_reset'    : 0.20,
                                'bullet_hole'           : 'plasma_hole',
                                'arms_animation'        : 'arms_pistol',
                                'weapon_animation'      : 'pistol_firing', 
                                'sound_firing'          : 'gun-gunshot-02.wav',
                                'sound_reload'          : 'clickclick.wav'              
                             },
                             
                    'grenade':{  
                                'range'                 : 2,
                                'ammo'                  : 1,
                                'spare_ammo'            : 0,
                                'max_ammo'              : 1,
                                'clip_size'             : 1,
                                'bullet'                : 'plasma_bullet',
                                'bullet_damage'         : 45,
                                'bullet_speed'          : 25,
                                'bullet_drop'           : 0.00001,
                                'bullet_spray_spread'   : 0.05,
                                'bullet_spray_reset'    : 0.20,
                                'bullet_hole'           : 'plasma_hole',
                                'arms_animation'        : 'arms_pistol',
                                'weapon_animation'      : 'pistol_firing', 
                                'sound_firing'          : 'gun-gunshot-02.wav',
                                'sound_reload'          : 'clickclick.wav'              
                             },
                             
                    'axe':{  
                                'range'                 : 2,
                                'ammo'                  : 1,
                                'spare_ammo'            : 0,
                                'max_ammo'              : 1,
                                'clip_size'             : 1,
                                'bullet'                : 'plasma_bullet',
                                'bullet_damage'         : 45,
                                'bullet_speed'          : 25,
                                'bullet_drop'           : 0.00001,
                                'bullet_spray_spread'   : 0.05,
                                'bullet_spray_reset'    : 0.20,
                                'bullet_hole'           : 'plasma_hole',
                                'arms_animation'        : 'arms_pistol',
                                'weapon_animation'      : 'pistol_firing', 
                                'sound_firing'          : 'gun-gunshot-02.wav',
                                'sound_reload'          : 'clickclick.wav'              
                             },
                             
                    'pickaxe':{  
                                'range'                 : 2,
                                'ammo'                  : 1,
                                'spare_ammo'            : 0,
                                'max_ammo'              : 1,
                                'clip_size'             : 1,
                                'bullet'                : 'plasma_bullet',
                                'bullet_damage'         : 45,
                                'bullet_speed'          : 25,
                                'bullet_drop'           : 0.00001,
                                'bullet_spray_spread'   : 0.05,
                                'bullet_spray_reset'    : 0.20,
                                'bullet_hole'           : 'plasma_hole',
                                'arms_animation'        : 'arms_pistol',
                                'weapon_animation'      : 'pistol_firing', 
                                'sound_firing'          : 'gun-gunshot-02.wav',
                                'sound_reload'          : 'clickclick.wav'              
                             },
                             
                    'tnt':{  
                                'range'                 : 2,
                                'ammo'                  : 1,
                                'spare_ammo'            : 0,
                                'max_ammo'              : 1,
                                'clip_size'             : 1,
                                'bullet'                : 'plasma_bullet',
                                'bullet_damage'         : 45,
                                'bullet_speed'          : 25,
                                'bullet_drop'           : 0.00001,
                                'bullet_spray_spread'   : 0.05,
                                'bullet_spray_reset'    : 0.20,
                                'bullet_hole'           : 'plasma_hole',
                                'arms_animation'        : 'arms_pistol',
                                'weapon_animation'      : 'pistol_firing', 
                                'sound_firing'          : 'gun-gunshot-02.wav',
                                'sound_reload'          : 'clickclick.wav'              
                             },
                             
                    'sword':{  
                                'range'                 : 2,
                                'ammo'                  : 1,
                                'spare_ammo'            : 0,
                                'max_ammo'              : 1,
                                'clip_size'             : 1,
                                'bullet'                : 'plasma_bullet',
                                'bullet_damage'         : 45,
                                'bullet_speed'          : 25,
                                'bullet_drop'           : 0.00001,
                                'bullet_spray_spread'   : 0.05,
                                'bullet_spray_reset'    : 0.20,
                                'bullet_hole'           : 'plasma_hole',
                                'arms_animation'        : 'arms_pistol',
                                'weapon_animation'      : 'pistol_firing', 
                                'sound_firing'          : 'gun-gunshot-02.wav',
                                'sound_reload'          : 'clickclick.wav'              
                             },
                             
                    'sword_2h':{  
                                'range'                 : 2,
                                'ammo'                  : 1,
                                'spare_ammo'            : 0,
                                'max_ammo'              : 1,
                                'clip_size'             : 1,
                                'bullet'                : 'plasma_bullet',
                                'bullet_damage'         : 45,
                                'bullet_speed'          : 25,
                                'bullet_drop'           : 0.00001,
                                'bullet_spray_spread'   : 0.05,
                                'bullet_spray_reset'    : 0.20,
                                'bullet_hole'           : 'plasma_hole',
                                'arms_animation'        : 'arms_pistol',
                                'weapon_animation'      : 'pistol_firing', 
                                'sound_firing'          : 'gun-gunshot-02.wav',
                                'sound_reload'          : 'clickclick.wav'              
                             }

                    } 

As you can see it holds all the data per weapon, you can even add the difficulty damages to it, but why would you add all those things per weapon if you can do it with a simple difficulty property icw a tiny if statement. You can now even use that difficulty property to change the whole game(spawn more or tougher enemies, generate more or less loot, etc.). There is no need to go make a script/property for every single object ingame that needs to change when the difficulty changes.

Yes , i did that right after you shared the first difficulty script but missed to share … and it worked fine thank you so much !! :grin:

1 Like

I actually store the damage in the barrel of the gun, and what type of ammo each barrel uses,

this way you can ‘mod’ weapons / replace parts and change the weapons effects / damage.

to store the items in a inventory, I convert them to data and back to objects again using a little bit of py to scrape all the objects properties,

#convert game object to 2 dictionaries

                    w = {}
                    for prop in own['Target']['Equip'].getPropertyNames():
                        if prop!= 'Parts' and prop!="init" and prop!="Charge":
                            if type(own['Target']['Equip'][prop]) is Matrix:
                                w[prop] = own['Target']['Equip'][prop].copy()
                            else:
                                w[prop] = own['Target']['Equip'][prop]
                                    
                    c = {}        
                    for child in own['Target']['Equip'].childrenRecursive:
                        c[child.name] = {}
                        for prop in child.getPropertyNames():
                            if prop!="FX" and prop!="FX2":
                                if type(child[prop]) is Matrix:
                                    c[child.name][prop] = child[prop].copy()
                                else:
                                    c[child.name][prop] = child[prop]
1 Like

This looks too advanced to a python noob but it’s cool man !

yes, I agree with you in many ways, but my example is just part of my script, I also use additional parameters such as distance and adding randomness for additional damage, and I remove all semi-collided units and hit the walls :grin:

ok :D, was just looking at your example, and i know for a long time that he ain’t the best with python, thought to point it out.

Isn’t it better to use a dict with all mods? you grab a mod look it up in the dict and add it to the weapon stats, this way a mod stays what it is a visual representation. Adding or removing the stats from the weapon is easy too this way, and adjusting weapon/mmod data is way easier, you don’t have to find the right object but instead just dive into the dict and alter the data.

I don’t consider my code to be something better or outstanding, there will always be a person who will do better, I start from the simplicity of implementation and the ability to simply make changes - and dictionaries give it for a simple indie shooter, I think that’s enough

@Villi89 I think he was talking with me*

they are gathered on init when the weapon switches on, or robot(they both use the same code)

this way if you blow something into pieces, the pieces are still usable components if they still have any health / have value based on the properties inside them.

Yes and no :grin: i reporting Cotaks my last message ok :D, was just looking at your example, and i know for a long time that he ain’t the best with python, thought to point it out.

I replied to both :wink:

I understand how you do it, still i would use a dict for it and store the dict into the player to get all info on the fly. For me it’s just more practical to have that data in a dict then on every weapon part.

lucky enough there is more then 1 way to do it :slight_smile: