Jump to content
Rosso777

Randomized Damage / onHit

Recommended Posts

Hoping someone can point me in the right direction, even if it's by way of a relevant thread that can help me figure this out.

 

I've tried to many different addons and scripts for implementing a wounding system to my private dedicated Exilemod server. No dice. Nothing works correctly.

 

I am wondering if there is a way to essentially roll a virtual dice when a player gets hit. Ignore calibers/explosions, etc., just simply put: If a player gets shot, the dice rolls. Out of 100, if it rolls between 1-10, the player dies. 11-30, the player goes unconscious. 31-50, the player's health drops below 50% and goes prone until healed full. 51-100, the player's health drops to 50% with no other consequences. It makes sense that the total should equal 100 and at any point, I can adjust the measures as I see fit.

 

I articulate this all so specifically so someone can say, "No, this part won't work" or "you're better off trying it like this" and so on. I know I've seen randomized "dice" scripting somewhere in addons, at least I am pretty sure that's what I was looking at.

 

Any help is appreciated.

Share this post


Link to post
Share on other sites

@Rosso777,

Quote

I am wondering if there is a way to essentially roll a virtual dice when a player gets hit.

Yes. Here's an idea to get you started,

Spoiler

{
_x allowDamage false;
_x setVariable ["you_damage", 100];

_x addEventHandler ["HitPart", {
	(_this select 0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"];

private _hitVal = random 100;
private _damage= _target getVariable "you_damage";
	if ((_damage - _hitVal) < 0) exitWith {_target setDammage 1};
	_target setVariable ["you_damage", _damage - _hitVal];
	systemChat format ["%1, %2", _damage, _hitVal];

}];
} forEach units _grp;

check the wiki (random) for different distribution methods.


Have fun!

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, wogz187 said:

@Rosso777,

Yes. Here's an idea to get you started,

  Hide contents

check the wiki (random) for different distribution methods.


Have fun!


Thanks, Wogz! It seems a bit over my head but I love the challenge. I am going to dig into it now; I will post what I come up with for some feedback.

Edit: @wogz187 Does this seem like the direction I should follow? https://community.bistudio.com/wiki/selectRandomWeighted

 

Edit 2:  Here is my thought process:

 

In the player's onHit file,  I can use something like:

 

_scriptFile = ["unconscious.sqf", "brokenlegs.sqf", "injured.sqf", "death.sqf"] selectRandomWeighted [0.15, 0.3, 0.5, 0.05 ];

execVM _scriptFile;

 

and then script an .sqf for each instance.

 

Thoughts?

Share this post


Link to post
Share on other sites

@Rosso777,
 

Quote

Does this seem like the direction I should follow? https://community.bistudio.com/wiki/selectRandomWeighted

 

Edit 2:  Here is my thought process:

 

In the player's onHit file,  I can use something like:

 

_scriptFile = ["unconscious.sqf", "brokenlegs.sqf", "injured.sqf", "death.sqf"] selectRandomWeighted [0.15, 0.3, 0.5, 0.05 ];

execVM _scriptFile;

You have the right idea. Consider you cannot use a regular "hit" EH because damage is disabled (it won't register). The "hitPart" handler I suggested will return the part that got hit-- no need for randomization. 

In the params listed,

params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"];

"selection" will be an array of "parts" which got hit (can be multiple parts). It will return things like "head" and "rightfoot", etc.

Within the EH you could put something like,

_hitPart=_selection select 0;
[_target, _hitPart] call you_fnc_handleDamage;

You_fnc_handleDamage=
{	params ["_target", "_hitPart"];
systemChat format ["You got hit in the %1", _hitPart];
};

and then handle the pseudo damage the same way we did in the post above.

Have fun!

  • Like 1

Share this post


Link to post
Share on other sites

@wogz187 This is awesome, man, thanks for leading me in the right direction. I do love to learn! 🙂  Maybe someday I can implement my own ideas. 

  • Like 1

Share this post


Link to post
Share on other sites
7 hours ago, wogz187 said:

"Consider you cannot use a regular "hit" EH because damage is disabled (it won't register). The "hitPart" handler I suggested will return the part that got hit--"

 

I just want to make sure that I understand what you're telling me. Are you saying that I can't use the PlayerOnHit file, but I need to be doing all this inside a "PlayerOnHitPart" file? (Filenames may not be exact but hopefully you see what I mean).

Share this post


Link to post
Share on other sites

@Rosso777,

Quote

I just want to make sure that I understand what you're telling me. Are you saying that I can't use the PlayerOnHit file, but I need to be doing all this inside a "PlayerOnHitPart" file?

Not exactly.

We're talking about EventHandlers and Functions. The clever use of which can vastly reduce the amount of separate .sqf files required. So far nothing requires an external .sqf-- not even one.

This is everything so far,

Spoiler

{
_x allowDamage false;
_x setVariable ["you_damage", 100];

_x addEventHandler ["HitPart", {
	(_this select 0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"];

private _hitVal = random 100;
private _damage= _target getVariable "you_damage";
	if ((_damage - _hitVal) < 0) exitWith {_target setDammage 1};
	_target setVariable ["you_damage", _damage - _hitVal];
	_hitPart=_selection select 0;
	systemChat format ["You got hit in the %1", _hitPart];
	systemChat format ["%1, %2", _damage, _hitVal];

}];
} forEach units group this;

Calling this on your group will: disable damage for each unit, give them a pseudo health bar (you_damage var), add an EH that reports hits and generates random damage amount. You can just paste the above into the init field of any group member and you're good-to-go.


Consider the above is just an example of the logic and your job is to make it presentable. I'd suggest creating common language strings for each hitPart in a separate function. Something like,

if (_hitPart isEqualTo "spine1") then {_hitpart = "Spine"};
if (_hitPart isEqualTo "rightFoot" || _hitPart isEqualTo "leftFoot") then {_hitpart = "Foot"};

The function could take all the guts out of the EH and include the above,

Spoiler

you_fnc_handleDamage=
{ params ["_target", "_hitPart"];
private _hitVal = random 100;
private _damage= _target getVariable "you_damage";
	if ((_damage - _hitVal) < 0) exitWith {_target setDammage 1};
	_target setVariable ["you_damage", _damage - _hitVal];
if (_hitPart isEqualTo "spine1") then {_hitpart = "Spine"};
if (_hitPart isEqualTo "rightFoot" || _hitPart isEqualTo "leftFoot") then {_hitpart = "Foot"};
	systemChat format ["You got hit in the %1", _hitPart];
	systemChat format ["%1, %2", _damage, _hitVal];
};

call the function through the EH with,


[_target, _hitpart] call you_fnc_handleDamage;

 

It's a relatively complicated thing-- no need to complicate things more with superfluous .sqf files.
 
Have fun!

  • Like 1

Share this post


Link to post
Share on other sites

@wogz187 wow. Okay my first question is: are you a teacher or instructor? I am, and your way of articulation and guidance is refreshing; especially in an arena such as this.

 

my second question is: this should all be able to be done in one file, correct?
 

I very much appreciate your time and effort here; I’m going to dig into this and I’ll update you as I go. Thank you!

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

@Rosso777,

Short answer: I don't know. Slightly longer answer: If the function is loaded and the group has the EH it should work.

To make it definitely work the EH could just include the whole function.

Locality is not my thing.

Have fun!

Share this post


Link to post
Share on other sites

The way we are talking about spines and left/right feet; are these actual hit boxes on players? Is there a list somewhere that I can work off of for options?

Share this post


Link to post
Share on other sites
7 minutes ago, Rosso777 said:

The way we are talking about spines and left/right feet; are these actual hit boxes on players? Is there a list somewhere that I can work off of for options?

 

try selectionNames command (Example 2 & 3)

  • Like 1

Share this post


Link to post
Share on other sites
20 minutes ago, gc8 said:

 

try selectionNames command (Example 2 & 3)

 
Thank you. Wow, so all of those items in the examples are hit boxes already configured within Arma?

Share this post


Link to post
Share on other sites
7 minutes ago, Rosso777 said:

 
Thank you. Wow, so all of those items in the examples are hit boxes already configured within Arma?

 

they are called selections

Share this post


Link to post
Share on other sites
Just now, gc8 said:

 

they are called selections


So that means they might not all be active, but are options to be used?

Share this post


Link to post
Share on other sites
4 minutes ago, Rosso777 said:


So that means they might not all be active, but are options to be used?

 

I believe they are all active. selections means they are parts of the 3D body. you know.. "head", "spine1" etc

 

 

  • Thanks 1

Share this post


Link to post
Share on other sites
1 hour ago, gc8 said:

 

I believe they are all active. selections means they are parts of the 3D body. you know.. "head", "spine1" etc

 

 

 
Okay, I think I’m beginning to understand the system. So @wogz187, by your code logic example above, we’re turning player damage (received) OFF, then we’re essentially ‘choosing’ which hitParts we want to consider in the overall ‘rolling of the dice’. Is this right?

  • Like 1

Share this post


Link to post
Share on other sites

Still chipping away at this slowly. Whatever I come up with, I will post; even if just for future-reader knowledge. 

Share this post


Link to post
Share on other sites

@Rosso777,

This is how I tested it. Just paste all of the following into a unit's init field,

Spoiler

this allowDamage false; 
this setVariable ["you_damage", 1000]; 
 
this addEventHandler ["HitPart", { 
 (_this select 0) params ["_target", "_shooter", "_projectile", "_position", "_velocity", "_selection", "_ammo", "_vector", "_radius", "_surfaceType", "_isDirect"];
	[_target, _selection] call you_fnc_handleDamage; 
}];

you_fnc_handleDamage=
{	params ["_target", "_hitPart"];
	private _hitVal = random 100;
	private _damage= _target getVariable "you_damage";
	if ((_damage - _hitVal) < 0) exitWith {_target setDammage 1};
	_target setVariable ["you_damage", _damage - _hitVal];
	systemChat format ["You got hit in the %1", _hitpart];
	systemChat format ["%1, %2", _damage, _hitVal];
};

You can grant more or less HP with the you_damage variable or play with the distribution curve in "random".

Have fun!

Share this post


Link to post
Share on other sites

@Rosso777,
This version,
* allows handler to be added and removed whenever-- returning the unit to default damage
* allows healing the custom HP bar
* allows custom HP amount in the function call
* tracks [Current Custom HP, Total Custom HP, Original Damage Amount] on each unit
* removes EH on death

Spoiler

MMF_fnc_handleDamage=
{	params [["_load", false], ["_num", 100], ["_caller", player], ["_heal", false]];

	if (_heal) exitWith {
			private _handle= _caller getVariable "MMF_damage";
				_caller setVariable ["MMF_damage", [_handle select 1, _handle select 1, _handle select 2]];
				systemChat format ["Healed! Total HP: %1", _handle select 1];
	};

	if (_load) exitWith {
		{
			private _dam= damage _x;
			_x allowDamage false; 
			_x setVariable ["MMF_damage", [_num, _num, _dam]]; 
 
			_x addEventHandler ["HitPart", { 
 (_this select 0) params ["_target"]; 
 
			private _rNumber = random 100; 
			private _handle= _target getVariable "MMF_damage";
			private _unitHP= _handle select 0;
			if ((_unitHP - _rNumber) < 0) exitWith {_target setDammage 1; _target removeEventHandler ["hitPart", 0];}; 
				_target setVariable ["MMF_damage",[ _unitHP - _rNumber, _handle select 1, _handle select 2]]; 
				systemChat format ["Remaining HP: %1", _unitHP - _rNumber];
			}];
		}forEach units group _caller;
	};

	if !(_load) exitWith {
		{
			private _handle= _x getVariable "MMF_damage";
			_x setDamage (_handle select 2);
			_x allowDamage true;
			_x removeEventHandler ["hitPart", 0];
		} forEach units group _caller;
	};
};

call examples,


// load handler on group with 1000 HP
[true, 1000, player, false] call MMF_fnc_handleDamage;

//or load defaults
[true] call MMF_fnc_handleDamage;

// remove handler from group and return to original damage
[false, 1000, player, false] call MMF_fnc_handleDamage;

// heal unit
[false, 1000, player, true] call MMF_fnc_handleDamage;

// heal group
{[false, 1000, _x, true] call MMF_fnc_handleDamage} forEach units group player;

The system doesn't report the hitPart selection because that's annoying and janky.


Have fun!
 

  • Like 1

Share this post


Link to post
Share on other sites

@wogz187 Dude... this is awesome.  It’s so spot on and unlike EVERY bit of ridiculous code-garbage that I was trying to piece together (because I know virtually nothing, I merely conceptualize). 

 

Skimming through this, I have so many questions about what specific things mean. 
 

Oh, and after giving up on the above code we were initially discussing, I began delving into functions and eventhandlers (per your recommendation) and quickly realized I was WAY out of my league. So... I began messing around with the randomizing pseudo-damage handler idea from earlier. Now, i know,  I know... I don’t need all this sqf garbage and it overcomplicates things, but it seems to make more sense to me at this point in my learning. My thought is: learn the basic concept of HOW things work (cause and effect, links from IF to THEN), then clean it up with Functions. Then figure out how EventHandlers work into the process. Or vice verse if that’s the case. 
 

And quite frankly I’ve been afraid to admit all this because I didn’t want it to come across as me being ungrateful or stubborn— it’s just how my brain seems to process all this stuff at this point.  Here is what I've been working on:

Spoiler


// Random-Chance Damage file


private["_unit","_selectionName","_amountOfDamage","_sourceOfDamage","_typeOfProjectile"];
_unit             = _this select 0;
_selectionName    = _this select 1;
_amountOfDamage   = _this select 2;
_sourceOfDamage   = _this select 3;
_typeOfProjectile = _this select 4;


this allowDamage true;
this setVariable ["you_damage", 200];

you_fnc_handleDamage = {
params ["_target", "_damage"];
private _damage = _target getVariable "you_damage";

 

if (alive player) then
{
  if (((damage player) <= 0.80) && ((damage player) >= 0.60)) then { [] execVm "injured.sqf" };
// if not randomizing damage amounts (because now we have a 200% HP pool with the "you_damage" Variable), this may be unnecessary
 

if (((damage player) <= 0.59) && ((damage player) >= 0.39)) then { [] execVm "knockdown.sqf" };
// Player will be forced into PRONE stance for 1 second, and then allow them to get back up (use a falling animation?)

 

if (((damage player) <= 0.39) && ((damage player) >= 0.19)) then { [] execVm "brokenlegs.sqf" };
// Player will be forced into PRONE stance until being using Morphine (or being healed to full if Morphine cannot be used)
 

if (((damage player) <= 0.19) && ((damage player) >= 0.01)) then { [] execVm "unconscious.sqf" };
// Unconscious file will contain a random chance for unconscious for 5-30 seconds

 

if (damage player) < 0.01) then { [] execVm "death.sqf" };
//player death file will contain a random chance to 1) DIE or 2) Allow Revive OR 3) always allow for revive but it will have a chance to fail
};

 

So once I have these "connections" working to react accordingly with player damage received, then my next step is to ensure that the corresponding .SQF file works correctly. Once I am successful, the goal will be to rewrite one of the above sections (if-then) into a more clean version without using an SQF. 

  • Like 1

Share this post


Link to post
Share on other sites

@Rosso777,
If you don't want to deal with the hitPart selection strings, and I don't blame you, hitting is based on damage rolled.

Quote

  if (((damage player) <= 0.59) && ((damage player) >= 0.39)) then
  { [] execVm "knockdown.sqf" };   

in my function that would be something like,

Spoiler

private _rNumber = random 100;
private _chats = ["Minor Damage", "Major Damage", "Unconcious"];
if (_rNumber< 49) then {_select =0} else {if (_rNumber> 89) then {_select =2} else {_select =1}};
systemChat format ["%1", _chats select _select];
[_select] execVM "external script";

and then use the _select Var to determine what happens in the single sqf.

Have fun!

 

Spoiler

The array at the beginning of the execVM sends parameters to the script just like sending parameters to a function.
You can say,


[_caller, _hitVal] execVm "knockdown.sqf" 

for example, and transfer variables into the executing script or function.
"Spawn" is another good example of where to use that array.


[this, 3] spawn { sleep (_this select 1); systemChat format ["%1 waited %2 seconds", _this select 0, _this select 1];};

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Where do the select 0 and select 1 come in? Where is that information coming from/going to?

Share this post


Link to post
Share on other sites

_this refers to an array with the arguments passed to the script. Please refer to spawn documentation for more information on how to pass arguments to a script ran with spawn (this is what the brackets before the command are for).

 

This means that with

private _firstPassedArgument = _this select 0;

you can select the first argument that was passed to the script. Please note that you are dealing with zero-indexed arrays here.

 

So, in the code gc8 provided (last line) _this select 0 is 'this' and _this select 1 is '3'. Now, I believe you should be able to say that this (which is passed to the script) and _this (which is used inside the script) are two different variables. The first, most probably, refers to an object (in the init field of an object, for example, this refers to the object the init field "belongs" to), while the second refers to the array with the arguments that were passed to the script.

 

Finally, just to make sure everything is clear when I say "the script" I mean what is enclosed in the curly brackets after the spawn command. So, to answer your question directly, the information is coming from "you" and goes "inside the script" executed (actually it is given to the scheduler to be executed) by the spawn command.

 

Please let us know if this is not clear.

  • Like 1

Share this post


Link to post
Share on other sites

@Rosso777,

Quote

Where do the select 0 and select 1 come in? Where is that information coming from/going to?

[this, 3] spawn { sleep (_this select 1); systemChat format ["%1 waited %2 seconds", _this select 0, _this select 1];};

In the above example we are selecting from the array at the beginning of "spawn". So select 0 is "this"-- the unit and select 1 is the number (arrays start at 0).

Have fun!

  • Like 1

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×