Spectator reactions based on another character health% or death

oldyz

Well-known member
i am trying to find a way for spectator entities to react based on a "check health" of another entity.

the opponent character is generated at random - 1 out of 40 different characters -
Problem 1: if any of these 40 characters is killed with a throw, there is no death animation, so i cannot rely on a spawn0, summon frame or spawn frame command
Problem 2 - also i would like to avoid editing every single character in the game to include those line commands. - it is easier to edit the spectators entity with the 40 enemies in mind

to keep it simple:
i have 9 possible characters spawned by script in a certain coordinate

spawn Sinister9 ## spawns 1 out 9 enemies at random
coords  35 200
at  0

the spectators are supossed to boo if the enemy characters Dorian, Smith, Dundee, Duck & Poncho die -
the spectators are supposed to cheer if enemy characters  Juan, Ringo, Pedro, Adolf die


 
I found 2 threads that might help figure out how to make the script -
one is a script that checks for health percentage & it changes the animation accordingly:

void lifeCheck by %
https://www.chronocrash.com/forum/index.php?topic=3621.0

it has what i am looking for, but i need to adapt it so the entity "spcetator" can read the health of multiple entities that may appear & react acordingly

another piece of the puzzle might be found on this other script improved by Bloodbane
Code:
void main()
{// Equalize health with mate's health
  void self = getlocalvar("self"); //Get calling entity
  void Mate = getentityvar(self, 0); //Find Mate
  void MateX = getentityproperty(Mate, "exists");
  int Health = getentityproperty(self,"health");

  if(Mate!=NULL() && MateX){
    if(Health > 0){
      changeentityproperty(Mate, "health", Health);
    } else {
      setentityvar(self, 0, NULL());
      setentityvar(Mate, 0, NULL());
      damageentity(Mate, self, 1000, 1, openborconstant("ATK_NORMAL"));
    }
  }
}

so in this case - what i need to do is for "spectator" to consider entities Dorian, Smith, Dundee, Duck & Poncho as "MatesX"
and play animspecial12 animations when their health is low & animspecial13 when any of them die
& also at the same time  "spectator" has to consider entities Juan, Ringo, Pedro, Adolf as "Mates"
and play animspecial14 animations when their health is low & animspecial15 when any of them die
 
Can't you create ondeathscript for these enemies?
They are pretty specific so it'd best if they have ondeathscript setting their death status in global variable.
Spectator simply checks each death status and react based on it.
 
Thanks Bloodbane - i will search and read about ondeathscript to see if i can understand it better -
i wasted a bunch of time trying to use killentity - turns out i still cant figure out ho to use it to kill an specific enemy -
if i can figure it out , then the old trick of "item" drops in based on the level.txt file can work again.....
 
I think forcing enemies is just quicker than having all enemies check constantly in their animations if variable is changed.

You pretty much force enemies into animations from death anim itself based on their name and animation theyre playing (walk + idle + grounded attack etc)and decide if they will be sad or cheer.
But i dont like to help without posting actual script cause thats almost no help at all so...
I edited this post a bit, thats the script i think, should work:
Code:
@script
    void self = getlocalvar("self");
    float x = getentityproperty(self, "x");
    float z = getentityproperty(self, "z");

    void vEntity;                                       //Target entity placeholder.
    int  iEntity;                                       //Entity enumeration holder.
    int  iName;                                         //Entity name.
    int  iMax      = openborvariant("ent_max");         //Entity count.
    int animID;
    if(frame == 1){
      //Enumerate and loop through entity collection.
      for(iEntity=0; iEntity<iMax; iEntity++){
        vEntity = getentity(iEntity);                 //Get target entity from current loop.
        iName   = getentityproperty(vEntity, "name"); //Get target name
        animID = getentityproperty(vEntity, "animationid");
        //Sign?
        if(( animID == openborconstant( "ANI_WALK") || animID == openborconstant( "ANI_IDLE") || animID == openborconstant( "ANI_ATTACK1" ) )&& (iName == "Ene1" || iName == "Sign" || iName == "Ene2" )&& vEntity!=self){
    performattack(vEntity, openborconstant("ANI_FOLLOW1"));

        }
      }
    }
@end_script

change names to whatever you want

You need to check animations of enemies so they dont suddenly cheer in fall anim.


Also  because enemies will cheer all at the same time - will be loud, unless you make them stall for tiny bit before playin cheer sample. one thing i recommend is to use different delays for each cheering enemy in their cheering animaions so not all cheer anims are the same, or use random script to skip some cloned frames to add some delay

This way is nicer IMO because you dont have to edit multiple enemies death and also walks and idles to insert checks etc. you do most stuff in deaths only.

Heres stall script in rise animation so not all enemies stand up at the same time after fall:

Code:
anim	rise
	loop	0
	offset	164 196
	bbox	0 0 0 0 0
	delay	25
      @script
    void self = getlocalvar("self");
    if( frame == 0){
      int r = rand()%30;
      if( r > 11){
        changeentityproperty(self, "animpos", 1);
      } else if( r < -10){
        changeentityproperty(self, "animpos", 2);
      } else if( r < 0){
        changeentityproperty(self, "animpos", 0);
      } else if( r > 0){
        changeentityproperty(self, "animpos", 3);
      }
    }
	@end_script	
	frame	data/chars/zboj/fall6.gif
	frame	data/chars/zboj/fall6.gif
	frame	data/chars/zboj/fall6.gif
	frame	data/chars/zboj/fall6.gif
	offset	164 200
	delay	8
	frame	data/chars/zboj/rise1.gif
	frame	data/chars/zboj/rise2.gif
	frame	data/chars/zboj/rise3.gif
	delay	18
	frame	data/chars/zboj/rise4.gif
	delay	8
	frame	data/chars/zboj/rise5.gif
	offset	158 200
	frame	data/chars/zboj/rise6.gif
	frame	data/chars/zboj/rise7.gif
 
bWWd,

Great post. That's probably the best way, all except for this:

use anim ID's you can get ID;s by using textobject in walk or idle, they have numerical values like 2,4,5,7 etc.I just dont remember them, dont check for actual :anim idle or anim walk... it didnt worked as nice as IDs in some cases for me.
Yeah i think someone could collect all ID's of all animations and slap into manual

That is 100% incorrect, and if someone does put a list of IDs in the manual I will remove them on the spot. I repeat: Never, never, NEVER put engine values directly into your scripts. Those IDs change regularly by design. This is specifically why we have openborcosntant().

If you want the ID of an animation, use openborconstant() to get it. That's the only right way, and it's a 100% free function when used correctly.

Example (checking to see if we are playing Idle animation):

Code:
void acting_entity = getlocalvar("self");
int animation = 0;

animation = getentityproperty(acting_entity, "animationID");

if(animation == openborconstant("ANI_IDLE"))
{
     /* In animation idle! Let's do some stuff! */
}
else
{
     /* NOT in animation idle. Do something else! */
}


it didnt worked as nice as IDs in some cases for me

I'd really like to see what you mean by that - maybe a code sample. I'm not trying to be harsh, but again, the method you suggested to check animations is wrong in every way. There has to be something else in your code that caused you problems before and maybe we can help.  :)

DC
 
Oh in that case i need to edit some of my stuff.
You suggest using actual animation names or not ? I had issues of scritps not working at all when using animation names, only worked when i used ids so now i dont even bother with names.I wasted half day fighting with it , then changed to ids and worked.
Getting constants will take some time before i edit post
---
Code:
 ani_id = ANI_SELECT;
		ani_id = ANI_SELECTIN;
		ani_id = ANI_SELECTOUT;
        newanim->sync = ANI_WALK;
        ani_id = ANI_SLEEP;
        ani_id = ANI_RUN;
        ani_id = ANI_BACKRUN;

        newanim->sync = ANI_WALK;
        newanim->sync = ANI_WALK;
        newanim->sync = ANI_WALK;
        ani_id = ANI_JUMP;
        ani_id = ANI_DUCK;
        ani_id = ANI_LAND;
            ani_id = ANI_PAIN;
            ani_id = ANI_PAIN2;
            ani_id = ANI_PAIN3;
            ani_id = ANI_PAIN4;
            ani_id = ANI_PAIN5;
            ani_id = ANI_PAIN6;
            ani_id = ANI_PAIN7;
            ani_id = ANI_PAIN8;
            ani_id = ANI_PAIN9;
            ani_id = ANI_PAIN10;
            ani_id = ANI_BACKPAIN;
            ani_id = ANI_BACKPAIN2;
            ani_id = ANI_BACKPAIN3;
            ani_id = ANI_BACKPAIN4;
            ani_id = ANI_BACKPAIN5;
            ani_id = ANI_BACKPAIN6;
            ani_id = ANI_BACKPAIN7;
            ani_id = ANI_BACKPAIN8;
            ani_id = ANI_BACKPAIN9;
            ani_id = ANI_BACKPAIN10;
        ani_id = ANI_SHOCKPAIN;
        ani_id = ANI_BURNPAIN;
        ani_id = ANI_BACKSHOCKPAIN;
        ani_id = ANI_BACKBURNPAIN;
            ani_id = ANI_FALL;
            ani_id = ANI_FALL2;
            ani_id = ANI_FALL3;
            ani_id = ANI_FALL4;
            ani_id = ANI_FALL5;
            ani_id = ANI_FALL6;
            ani_id = ANI_FALL7;
            ani_id = ANI_FALL8;
            ani_id = ANI_FALL9;
            ani_id = ANI_FALL10;
            ani_id = ANI_BACKFALL;
            ani_id = ANI_BACKFALL2;
            ani_id = ANI_BACKFALL3;
            ani_id = ANI_BACKFALL4;
            ani_id = ANI_BACKFALL5;
            ani_id = ANI_BACKFALL6;
            ani_id = ANI_BACKFALL7;
            ani_id = ANI_BACKFALL8;
            ani_id = ANI_BACKFALL9;
            ani_id = ANI_BACKFALL10;
        ani_id = ANI_SHOCK;
        ani_id = ANI_BACKSHOCK;
        ani_id = ANI_BURN;
        ani_id = ANI_BACKBURN;
            ani_id = ANI_DIE;
            ani_id = ANI_DIE2;
            ani_id = ANI_DIE3;
            ani_id = ANI_DIE4;
            ani_id = ANI_DIE5;
            ani_id = ANI_DIE6;
            ani_id = ANI_DIE7;
            ani_id = ANI_DIE8;
            ani_id = ANI_DIE9;
            ani_id = ANI_DIE10;
            ani_id = ANI_BACKDIE;
            ani_id = ANI_BACKDIE2;
            ani_id = ANI_BACKDIE3;
            ani_id = ANI_BACKDIE4;
            ani_id = ANI_BACKDIE5;
            ani_id = ANI_BACKDIE6;
            ani_id = ANI_BACKDIE7;
            ani_id = ANI_BACKDIE8;
            ani_id = ANI_BACKDIE9;
            ani_id = ANI_BACKDIE10;
        ani_id = ANI_SHOCKDIE;
        ani_id = ANI_BURNDIE;
        ani_id = ANI_BACKSHOCKDIE;
        ani_id = ANI_BACKBURNDIE;
        ani_id = ANI_CHIPDEATH;
        ani_id = ANI_GUARDBREAK;
        ani_id = ANI_RISEB;
        ani_id = ANI_BACKRISEB;
        ani_id = ANI_RISES;
        ani_id = ANI_BACKRISES;
            ani_id = ANI_RISE;
            ani_id = ANI_RISE2;
            ani_id = ANI_RISE3;
            ani_id = ANI_RISE4;
            ani_id = ANI_RISE5;
            ani_id = ANI_RISE6;
            ani_id = ANI_RISE7;
            ani_id = ANI_RISE8;
            ani_id = ANI_RISE9;
            ani_id = ANI_RISE10;
            ani_id = ANI_BACKRISE;
            ani_id = ANI_BACKRISE2;
            ani_id = ANI_BACKRISE3;
            ani_id = ANI_BACKRISE4;
            ani_id = ANI_BACKRISE5;
            ani_id = ANI_BACKRISE6;
            ani_id = ANI_BACKRISE7;
            ani_id = ANI_BACKRISE8;
            ani_id = ANI_BACKRISE9;
            ani_id = ANI_BACKRISE10;
        ani_id = ANI_RISEATTACKB;
        ani_id = ANI_BACKRISEATTACKB;
        ani_id = ANI_RISEATTACKS;
        ani_id = ANI_BACKRISEATTACKS;
            ani_id = ANI_RISEATTACK;
            ani_id = ANI_RISEATTACK2;
            ani_id = ANI_RISEATTACK3;
            ani_id = ANI_RISEATTACK4;
            ani_id = ANI_RISEATTACK5;
            ani_id = ANI_RISEATTACK6;
            ani_id = ANI_RISEATTACK7;
            ani_id = ANI_RISEATTACK8;
            ani_id = ANI_RISEATTACK9;
            ani_id = ANI_RISEATTACK10;
            ani_id = ANI_BACKRISEATTACK;
            ani_id = ANI_BACKRISEATTACK2;
            ani_id = ANI_BACKRISEATTACK3;
            ani_id = ANI_BACKRISEATTACK4;
            ani_id = ANI_BACKRISEATTACK5;
            ani_id = ANI_BACKRISEATTACK6;
            ani_id = ANI_BACKRISEATTACK7;
            ani_id = ANI_BACKRISEATTACK8;
            ani_id = ANI_BACKRISEATTACK9;
            ani_id = ANI_BACKRISEATTACK10;
        ani_id = ANI_PICK;
        ani_id = ANI_THROWATTACK;
        ani_id = ANI_UPPER;
        ani_id = ANI_CANT;
        ani_id = ANI_JUMPCANT;
        ani_id = ANI_CHARGE;
        ani_id = ANI_FAINT;
        ani_id = ANI_DODGE;
        ani_id = ANI_SPECIAL;
        ani_id = ANI_SPECIAL2;
        ani_id = ANI_JUMPSPECIAL;
        ani_id = ANI_JUMPATTACK;
        ani_id = ANI_JUMPATTACK2;
        ani_id = ANI_JUMPATTACK3;
        ani_id = ANI_JUMPFORWARD;
        ani_id = ANI_RUNJUMPATTACK;
        ani_id = ANI_RUNATTACK;    // New attack for when a player is running
        ani_id = ANI_ATTACKUP;    // New attack for when a player presses u u
        ani_id = ANI_ATTACKDOWN;    // New attack for when a player presses d d
        ani_id = ANI_ATTACKFORWARD;    // New attack for when a player presses f f
        ani_id = ANI_ATTACKBACKWARD;    // New attack for when a player presses b a
        ani_id = ANI_ATTACKBOTH;
        ani_id = ANI_GET;
        ani_id = ANI_GRAB;
        ani_id = ANI_GRABWALK;
         ani_id = ANI_GRABWALKUP;
        ani_id = ANI_GRABWALKDOWN;
         ani_id = ANI_GRABBACKWALK;
        ani_id = ANI_GRABTURN;
        ani_id = ANI_GRABBED;
        ani_id = ANI_GRABBEDWALK;
        ani_id = ANI_GRABWALKUP;
        ani_id = ANI_GRABWALKDOWN;
        ani_id = ANI_GRABBEDBACKWALK;
        ani_id = ANI_GRABBEDTURN;
        ani_id = ANI_GRABATTACK;
        ani_id = ANI_GRABATTACK2;
        ani_id = ANI_GRABFORWARD;
        ani_id = ANI_GRABFORWARD2;
           ani_id = ANI_GRABBACKWARD;
        ani_id = ANI_GRABBACKWARD2;
         ani_id = ANI_GRABUP;
        ani_id = ANI_GRABUP2;
        ani_id = ANI_GRABDOWN;
        ani_id = ANI_GRABDOWN2;
        ani_id = ANI_SPAWN;
        ani_id = ANI_RESPAWN;
        ani_id = ANI_THROW;
        ani_id = ANI_BLOCK;
		ani_id = ANI_BLOCKRELEASE;
		ani_id = ANI_BLOCKSTART;
        ani_id = ANI_CHARGEATTACK;
        ani_id = ANI_TURN;
        ani_id = ANI_FORWARDJUMP;
        ani_id = ANI_RUNJUMP;
        ani_id = ANI_JUMPLAND;
        ani_id = ANI_JUMPDELAY;
        ani_id = ANI_HITOBSTACLE;
        ani_id = ANI_HITPLATFORM;
        ani_id = ANI_HITWALL;
        ani_id = ANI_SLIDE;
        ani_id = ANI_RUNSLIDE;
        ani_id = ANI_BLOCKPAINB;
        ani_id = ANI_BLOCKPAINS;
            ani_id = ANI_BLOCKPAIN;
            ani_id = ANI_BLOCKPAIN2;
            ani_id = ANI_BLOCKPAIN3;
            ani_id = ANI_BLOCKPAIN4;
            ani_id = ANI_BLOCKPAIN5;
            ani_id = ANI_BLOCKPAIN6;
            ani_id = ANI_BLOCKPAIN7;
            ani_id = ANI_BLOCKPAIN8;
            ani_id = ANI_BLOCKPAIN9;
            ani_id = ANI_BLOCKPAIN10;
        ani_id = ANI_BACKBLOCKPAINB;
        ani_id = ANI_BACKBLOCKPAINS;
            ani_id = ANI_BACKBLOCKPAIN;
             ani_id = ANI_BACKBLOCKPAIN2;
            ani_id = ANI_BACKBLOCKPAIN3;
             ani_id = ANI_BACKBLOCKPAIN4;
            ani_id = ANI_BACKBLOCKPAIN5;
            ani_id = ANI_BACKBLOCKPAIN6;
              ani_id = ANI_BACKBLOCKPAIN7;
              ani_id = ANI_BACKBLOCKPAIN8;
            ani_id = ANI_BACKBLOCKPAIN9;
             ani_id = ANI_BACKBLOCKPAIN10;
        ani_id = ANI_DUCKATTACK;
        ani_id = ANI_WALKOFF;
        ani_id = ANI_EDGE;
        ani_id = ANI_BACKEDGE;
        ani_id = ANI_DUCKING;
        ani_id = ANI_DUCKRISE;
        ani_id = ANI_VICTORY;
        ani_id = ANI_LOSE;
 
bWWd,

This might be a miscommunication. It's not that you don't want to use ID's... you do, that part is right. It's that you never want to stick them directly into code because they can change in the engine and then your code breaks. They can cause a bunch of other problems too, but we won't get into that right now.

You always use openborconstant() to get them.

I just modified my post with a sample, maybe that will explain better.

DC
 
Yeah i edited as well at the same time, that should work better now, thanks, maybe somethinh was off when i was doing it over year ago when decided to use simply numerical ids, if you say itll work , then thats great.
 
bWWd said:
---
Code:
 ani_id = ANI_SELECT;
		ani_id = ANI_SELECTIN;
		ani_id = ANI_SELECTOUT;
...

I think I see the problem now. That's not not how you get the constants in script. Again, it's through openborconstant(). Like this:

Code:
ani_id = openborcosntant("ANI_SELECT");
ani_id = openborcosntant("ANI_SELECTIN");
...

And here's a example of it in use from above:

Code:
void acting_entity = getlocalvar("self");
int animation = 0;

/* Get current animation ID. */
animation = getentityproperty(acting_entity, "animationID");

/* 
* Compare entity's current animation ID to the animation ID 
* for Idle. Are they the same? Then we're in idle animation. 
*/

if(animation == openborconstant("ANI_IDLE"))
{
     /* In animation idle! Let's do some stuff! */
}
else
{
     /* NOT in animation idle. Do something else! */
}

HTH,
DC
 
So i the one i just posted should work ? I edited it and added stall method as well.
I didnt really tested it with animations and all, i just checked if its syntax isnt crashing the engine.
--
OK tested it in game to change named enemies to burn anim when enemy dies, works fine
Pretty cool to have this kind of interaction and enemies reacting to whats happening.
 
bWWd
i will test the rise code right away - it is the easiest thing to test a the moment -
it will give all the zombies & monsters a more organic feel, the only pain in the butt is that  have to edit all 100 variations i already had to include it.

i believe you corrected the code right? the stuff Damon mentioned (in green) and the name of the entities that is targetted should be this (in red)

@script
    void self = getlocalvar("self");
    float x = getentityproperty(self, "x");
    float z = getentityproperty(self, "z");

    void vEntity;                                      //Target entity placeholder.
    int  iEntity;                                      //Entity enumeration holder.
    int  iName;                                        //Entity name.
    int  iMax      = openborvariant("ent_max");        //Entity count.
    int animID;
    if(frame == 1){
      //Enumerate and loop through entity collection.
      for(iEntity=0; iEntity<iMax; iEntity++){
        vEntity = getentity(iEntity);                //Get target entity from current loop.
        iName  = getentityproperty(vEntity, "name"); //Get target name
        animID = getentityproperty(vEntity, "animationid");
        //Sign?
        if(( animID == openborconstant( "ANI_WALK") || animID == openborconstant( "ANI_IDLE") || animID == openborconstant( "ANI_ATTACK1" ) )&& (iName == "Ene1" || iName == "Sign" || iName == "Ene2" )&& vEntity!=self){
    performattack(vEntity, openborconstant("ANI_FOLLOW1"));

        }
      }
    }
@end_script

- also, this script has to be posted 5 times for 5 different characters in between frames or i replace the "name" with "dorian, robert, adolf" ?
 
it worked as is, yes it needs to be in all deaths you want reactions from so death is controlling enemies
, that skips the need to put checks in other anims, fastest way to do it that i can think of.well besides ondeathscript. but its still similar, you still have to give link to ondeathscript in header of enemy, but since its copy paste - similar outcome
 
bWWd
i tested it & still have a bit of trouble, but i will make sure to use it.
i will try to make a small demo.
I am going to continue editing NS - i have found a method that restores the item drops to their former function with "randomizers" at least for the bosses & characters that don't need palettes -

a bit off topic, would you happen to know how to make a Binded entity shift from left to right depending on the Z axis?
it would be sort of like the scale thing you showcased a while back.
 
ondrawscript.c  :


Code:
void main()
{
    void self = getlocalvar("self");
    int z = getentityproperty(self, "z");
		void sp = getentityproperty(self,"speed");
    int ypos = openborvariant("ypos");
    int zoom = -50 + (z - 240 ) *1.2 -10;
	    int zspeed = 0.5 + (z / 250-1600/500/2 ) *2.5 ;
		drawstring(110,380,0,"SPEED_-_" + sp/100000);
	drawstring(110,410,0,"ZOOM_-_" + zoom/100000);
	    if(zoom < 0) {zoom = 0;}
    setdrawmethod(self, 1, zoom , zoom , 0, 0, 0, -1);
	changeentityproperty(self, "speed", zspeed );  
}

int zoom is the formula , but it would really be best to just describe what you want to do, maybe theres just easier way than shifting bindentity.
IF you want to shift it at 60fps , it needs to be updatescript.
 
Back
Top Bottom