Can anybody help scripting af cover-system?

Making a modern D20 mod, where I want to implement a simple cover system via triggers, simulating being covered on 1 side. Will do it via onHit on PC/NPC armor. I think thats after dmg has been dealt, so I’ll maybe just heal for the same amount, if the cover takes effect (25-75% chance.)
Unless the attacker is in a position that nullifies the cover.

What I need is a function that checks oAttacker relative position from OBJECT_SELF.
For example if a trigger offers cover from attacks from the north. How can I do that?
ie. Is oAttacker north of OBJECT_SELF?

thanks,
/Pap

At its simplest:

int       bCover = FALSE;
vector v         = GetPosition(oAttacker) - GetPosition(OBJECT_SELF);

if (v.y > 0.0 ) bCover = TRUE;

This will provide cover whenever the attacker is north of the target, regardless of how far east or west they might be. Cover to the west will be indicated when v.x is less than zero, and so on.

If cover isn’t necessarily aligned with the axes (NW, for example) you will need to define its direction, then obtain the direction from the target to the attacker to determine whether cover applies or not. I can give you an example if it’s that complicated.

1 Like

Hi Proleric, thx for your answer, elegant and easy to modify for the other directions, thanks. I will indeed make it simple by just using the axes.

I guess since nobody (that I know of at least) has made this already, it’s probably not easy…
My next problem now is how to apply the effects of cover, since its supposed to just effect (for example) enemies north of the covered object.
My thought was to use an OnHit property on the armor, but that fires AFTER being hit and damaged,

What event can you use to make the check for cover, with a known attacker and target and apply the effect to the target for just that attack? (either EffectConcealment or an AC modifier) . I’m trying to have it work for both PC and NPCs. OnHit (either weapon or Armor) properties fire to late I guess?

The problem with EffectConcealment and AC modifiers in your case is that they are universal, applying to all attackers regardless of cover.

In theory, a frequent pseudo-heartbeat could be used to modify attack & spell casting bonuses of potential attackers. However, that becomes unduly complicated if there are multiple targets, some covered, others not (not to mention performance issues, if over-used in a PW).

I see that Bioware used EffectHeal in the OnPhysicallyAttacked event, which might be simpler (with something similar in OnSpellCastAt).

To be honest, I don’t really understand the sequence of combat events well enough.

Perhaps other people know of a way to, say, prevent or modify damage from a given attack.

1 Like

Hi Proleric
I think I solved my problem :slight_smile:
Using
SetEventScript(oPC,EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED,“pc_attacked”);

I can make a script that fires everytime the PC is attacked and check for cover and apply effect if applicable :smiley:

Just tested it and works with both melee and ranged attacks directed at the PC. It fires “pc_attacked” script which applies a Concealment effect BEFORE the attack is calculated.
Will test if making it duration 0.1 sec is enough for example. (but I only need it for a mod with small battles, so it shouldn’t be a problem)
Thanks for your help.

/Pap

2 Likes

Hello there! Would you mind sharing the system with us once you have completed it please? I would like to see it very much!

Of course. No problem :+1:

Ok most of it works now, with a couple of problems still, but minor.

make a trigger with OnEnter Script:

(I don’t know how to post code on these forums and yes, I know my code is ugly :-))

void main()
{
object oEnter = GetEnteringObject();
string cover = GetLocalString(OBJECT_SELF,"cover");
int type = GetLocalInt(OBJECT_SELF,"type");
SetLocalInt(oEnter,"cover_" + cover,TRUE);
SetLocalInt(oEnter,"type_" + cover,type);
SendMessageToPC(oEnter,"you are now in " + cover + " cover");
}

OnExit script:

void main()
{
object oEnter = GetEnteringObject();
string cover = GetLocalString(OBJECT_SELF,"cover");
SetLocalInt(oEnter,"cover_" + cover,FALSE);
SetLocalInt(oEnter,"type_" +cover,0);
SendMessageToPC(oEnter,"You are now outside cover");
}

Put these variables on the trigger:
NAME TYPE VALUE
cover string north (or south, east or west, the direction that is covered.)
type int 4 (AttackDecrease applied to attacker while you are covered.)

A creature that uses this needs the following script “pc_attacked” in OnPhysicalAttacked event:

void main()
{
object oPC = OBJECT_SELF;
object oAttacker = GetLastAttacker();

int cover_north = GetLocalInt(OBJECT_SELF,"cover_north");
int type_north = GetLocalInt(OBJECT_SELF,"type_north");
int cover_south = GetLocalInt(OBJECT_SELF,"cover_south");
int type_south = GetLocalInt(OBJECT_SELF,"type_south");
int cover_west = GetLocalInt(OBJECT_SELF,"cover_west");
int type_west = GetLocalInt(OBJECT_SELF,"type_west");
int cover_east = GetLocalInt(OBJECT_SELF,"cover_east");
int type_east = GetLocalInt(OBJECT_SELF,"type_east");

vector v         = GetPosition(oAttacker) - GetPosition(OBJECT_SELF);

if(cover_north == TRUE && v.y > 0.0)
{
 effect eDecr = EffectAttackDecrease(type_north);
 DelayCommand(4.0,ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eDecr,oAttacker,3.0));
 // DEBUG
 AssignCommand(oAttacker,SpeakString("My attack decreased!"));

}

if(cover_south == TRUE && v.y < 0.0)
{
 effect eDecr = EffectAttackDecrease(type_south);
 DelayCommand(4.0,ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eDecr,oAttacker,3.0));
 AssignCommand(oPC,SpeakString("cover south fired!"));

}
if(cover_west == TRUE && v.x < 0.0)
{
 effect eDecr = EffectAttackDecrease(type_west);
 DelayCommand(4.0,ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eDecr,oAttacker,3.0));
AssignCommand(oPC,SpeakString("cover west fired!"));

}
if(cover_east == TRUE && v.x > 0.0)
{
effect eDecr = EffectAttackDecrease(type_east);
 DelayCommand(4.0,ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eDecr,oAttacker,3.0));
 AssignCommand(oPC,SpeakString("cover east fired!"));

}
}

and finally if you want the pc to use this as well, you need to set “pc_attacked” to run on your PC too.
to test it make a trigger with this onenter script:

void main()
{

    object oPC = GetEnteringObject();
    int iDoOnce = GetLocalInt(OBJECT_SELF, "DoOnce");

    if (iDoOnce == FALSE )
    {
      AssignCommand(oPC,SpeakString("I'm ready!"));
      SetEventScript(oPC,EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED,"pc_attacked");
     }
   SetLocalInt(OBJECT_SELF,"DoOnce",TRUE);
  }

Now…problems:
The AttackDecrease doesn’t fire in time for the first attack, and will linger for 1 attack after leaving cover. Removing the delay doesn’t fix this.
Anybody have an idea to fix that?

Still need to make a check that attacker is using a ranged weapon, no cover against melee attacks
and people with many attacks per round will have problems… for my purpose this is fine though.
/Pap

To post code, use the [code] bbcode.

Regarding the timing problem, the order of events (on which I am hazy) is probably at the heart of the matter. You could test this by sending messages to the PC from each event.

For example, it may well be that OnPhysicallyAttacked only occurs after a successful attack, in which case the attack modifier will indeed apply to the next attack. If so, maybe you can tweak the outcome of the first attack.

I’m not sure whether OnDamaged occurs before or after that, or when damage is actually applied, if that’s relevant.

To check for ranged attacks, use GetWeaponRanged on GetLastWeaponUsed.

@paphjort It is actually essential to post code as a code-block if you want people to be able to copy and paste your code. If you look closely you will see that Discourse (the system that these forums work in) “helpfully” changes double quotes to the -

“”

format (which will not compile) instead of the normal coding double quotes. For fuller instructions on this, see this post that I made (part of a pinned thread all about the mechanics of using these threads) back in June 2018.

TR

@Proleric Yes, the timing is the problem, I’ve tried various solutions, but haven’t found one that can modify the first attack.
Getting the second and subseq. attacks can probably be made more precise too.

I tried doing it by making the cover as a percentage chance to avoid damage and using plot-flag, but this gives problems with multiple attackers, same with AC, so I went for modifying the attack instead.

I’ve made it work just for ranged attacks with the same functions you suggested, getting the first attack is whats missing now, but the effect is just that the benefit of cover is delayed 6 sec., but you will get them, with a risk of somebody else getting the benefit if the attacker switches target…

I would expect onDamaged to occur after OnPhysicalAttacked, but havent tested it.

@Tarot_Redhand Thx, I think the code is formatted correctly now. :slight_smile:

It does. Just not for plot objects (for which it does not fire).

Perhaps it’s possible to focus on attacker instead of defender? The easiest approach would be to employ EffectMissChance, but it’s FUBAR, so no go.

So how about this: attacker makes a roll and if it fails, it loses its round. Harsh, maybe annoying, but works and is robust with many combatants.

See the code
// creature's OnAttacked callback

void cover(object oSelf)
{
    if(!TouchAttackRanged(oSelf))
    {
        ClearAllActions(TRUE);
        ActionAttack(oSelf);
    }
}

void main()
{
    object oSelf = OBJECT_SELF;
    object oAttacker = GetLastAttacker();

    AssignCommand(oAttacker, cover(oSelf));
}

I used here TouchAttackRanged as an example. Funny enough, if you cancel the action mid-attack, the log will report 0 damage…