• New Horizons on Maelstrom
    Maelstrom New Horizons


    Visit our website www.piratehorizons.com to quickly find download links for the newest versions of our New Horizons mods Beyond New Horizons and Maelstrom New Horizons!

Feature Request Remove rep loss when an enemy's sword breaks and he runs

Pieter Boelen

Navigation Officer
Administrator
Storm Modder
Hearts of Oak Donator
What does the "disarm" attribute on swords do?
There is the DISARM_MODE toggle at the bottom of PROGRAM\InternalSettings.h that triggers this section in PROGRAM\Loc_ai\LAi_events.c:
Code:
       // CCC and PB: New attribute to weapons to allow for disarming opponents
       bool tbool = CheckAttribute(enemy, "equip.blade");
       if(tbool) tbool = GetCharacterEquipByGroup(enemy,BLADE_ITEM_TYPE) != "bladeX4";
       if( DISARM_MODE && CheckAttribute(weapon, "disarm" ) &&  LAi_IsFightMode(enemy) && !CheckAttribute(enemy, "nodisarm" ) && tbool)
       {
           int disarmbonus = 0;
           if(CheckAttribute(attack,"perks.list.SwordplayProfessional")) {disarmbonus = 20;}
           // if attacker has professional fencing skill, increase disarm chance with 20%

           int disarmpenalty = 0;
           if(CheckAttribute(enemy,"perks.list.SwordplayProfessional")) {disarmpenalty = 10;}
           // if attacked victim has professional fencing skill, decrease disarm chance with 10%

           if(sti(rand(100)+enemy.skill.Fencing) < sti(weapon.disarm)+sti(attack.skill.Fencing) + disarmbonus - disarmpenalty)
           {
               LAi_CharacterPlaySound(enemy,"OBJECTS\duel\sword_fallen.wav");
               string enemy_blade = GetCharacterEquipByGroup(enemy,BLADE_ITEM_TYPE);
               RemoveCharacterEquip(enemy,  BLADE_ITEM_TYPE );
               GiveItem2Character(attack, enemy_blade);

               if ( enemy.chr_ai.group !=  LAI_GROUP_PLAYER && !bAbordageStarted )
               // no disarming opponents during boarding
               {
                   LAi_SetCitizenTypeNoGroup(enemy);
                   Log_SetStringToLog(TranslateString("","Opponent disarmed !"));
               }
               else
               {
                   TakeItemFromCharacter(enemy, enemy_blade);
                   if ( sti(enemy.index) != GetMainCharacterIndex() && !bAbordageStarted ) // if NOT player
                   // officers safe from being disarmed boarding; player NOT safe from being disarmed during boarding
                   {
                       RemoveCharacterEquip(attack, BLADE_ITEM_TYPE );           // makes enemy use your blade against your officer
                       EquipCharacterByItem(attack, enemy_blade);           // and enables you to get it back by killing him/her
                       Log_SetStringToLog(TranslateString("","One of your officers was disarmed!"));
                       LAi_tmpl_afraid_SetAfraidCharacter(enemy, attack, true);   // makes officer flee
                       LAi_SetAfraidDead(enemy);
                   }
                   else
                   {
                       RemoveCharacterEquip(attack, BLADE_ITEM_TYPE );           // makes enemy use your blade against you
                       EquipCharacterByItem(attack, enemy_blade);           // and enables you to get it back by killing him/her
                       EquipCharacterByItem(enemy, "bladeX4");
                       Log_SetStringToLog(TranslateString("","You were disarmed!"));
                   }
               }
           }
       }
We experimented with that, but it ended up being set OFF by default.
I don't think it serves any real purpose anymore, since the bladedamage mod kind-of took over from it.

There's certainly a problem with enemies who attack you with a worn weapon, it breaks, you don't notice it, and you lose reputation for continuing to attack.
Either the enemy continues to fight with his fists (in which case: no problem), OR he gives up and tries to run away.
Only in the second case you would get the reputation loss. (And this second case never happens in boardings.)

I don't think fighting, let alone killing, someone who is running away is a very respectable thing to do; so the reputation loss makes sense.
I can see the problem though when it just happened and you haven't had the chance to notice it.

At sea, a similar situation applies to ships that surrender in the middle of a cannon volley.
This is why there is a 'grace period' coded in per ship so that any hits right after a surrender don't immediately trigger the reputation loss.
This doesn't happen on shore.

Adding this could be done here (also in PROGRAM\Loc_ai\LAi_events.c:
Code:
           if(BladeBreak)
           {
               if(sti(enemy.index) == GetMainCharacterIndex()){
                   Continue_Bladedamage = false;
                   LogIt("Your blade decreased in quality!"); }
               if(CheckAttribute(enemy, "nodisarm")) Continue_Bladedamage = false;
               if(enemy.chr_ai.group == LAI_GROUP_PLAYER) Continue_Bladedamage = false;
               if(bAbordageStarted) Continue_Bladedamage = false;

               if(Continue_Bladedamage && rand(1) > 0)
               {
                   LAi_SetCitizenTypeNoGroup(enemy);
               }
               else
               {
                   if(CheckCharacterItem(enemy, FindCharacterItemByGroup(enemy, BLADE_ITEM_TYPE)) && GetItemQualityByID(FindCharacterItemByGroup(enemy, BLADE_ITEM_TYPE)) != 0 && BLADEDAMAGE_USEOTHERBLADE)
                   // If character has another blade and the other blade is not broken and USEOTHERBLADE is on
                   {
                       // Then use the other blade
                       EquipCharacterByItem(enemy, FindCharacterItemByGroup(enemy, BLADE_ITEM_TYPE));
                   }
                   else
                   {
                       // Else use the default blade
                       if(!CheckCharacterItem(enemy, BLADEDAMAGE_DEFAULTBLADE))
                       {GiveItem2Character(enemy, BLADEDAMAGE_DEFAULTBLADE);}
                       // To prevent characters being given another defaultblade if they already have one
                       EquipCharacterByItem(enemy, BLADEDAMAGE_DEFAULTBLADE);
                   }
               }
           }
Would have to be next to the 'LAi_SetCitizenTypeNoGroup' line.
And here is an example of how to code a delayed execution of a custom function from PROGRAM\NK.c:
Code:
    PostEvent("KrakenAttackFinished", delay, "i", rCharacter);
}

#event_handler("KrakenAttackFinished", "FinishKrakenAttack");
void FinishKrakenAttack()
{
   ref pchar = GetMainCharacter();
   aref rCharacter = GetEventData();
   LogIt("Captain, the Kraken has finished its attack on the " + rCharacter.ship.name + "!");
   if(CheckAttribute(pchar, "KrakenAttack"))   PostEvent("EnableKraken", 5*60*1000);
}

Then again in LAi_events.c, this is where the reputation loss for unarmed victims is applied:
Code:
       if(!isSetBalde)
       {
           // ccc mar05 REPLOSS tweak added
           if(enemy.chr_ai.group != LAi_monsters_group)
           {
               if(!CheckAttribute(enemy,"corpse")) enemy.corpse = false; //Fix by levis
               if(enemy.corpse==false) //Levis: fix so you won't get reploss from hitting corpses
               {
                   if (!CheckAttribute(enemy,"pickgold") || GetCharacterEquipByGroup(attack, BLADE_ITEM_TYPE) != "bladeX3") // GR: no reploss if he robbed you and you use a thief's knife
                   {
                       if(GetAttribute(attack, "index") && sti(attack.index) == GetMainCharacterIndex()) LogIt(TranslateString("","CHANGE REP for player:") + " " + -REPLOSS + " - " + TranslateString("","undrawn blade 2"));    // LDH 19Dec08
                       LAi_ChangeReputation(attack, - REPLOSS); // NK tempfix for un-drawn blades 04-17
                   }
               }
           }
           //exp = 0.0;
       }
(You knew that already; I see your initials there. :cheeky )

On a side-note... I think this code has never functioned as intended:
Code:
bool isSetBalde = (SendMessage(enemy, "ls", MSG_CHARACTER_EX_MSG, "IsSetBalde") != 0);
It should return whether a character has drawn his/her blade.
But as far as I know, it only returns 'true' for characters who don't have a blade equipped at all.
Which is stupid, because we can check that easily on the PROGRAM side; without a need to ask the engine itself using 'SendMessage' .

I wonder if that was ever fixed in later releases like CoAS or TEHO...
 
I don't think fighting, let alone killing, someone who is running away is a very respectable thing to do; so the reputation loss makes sense.
I can see the problem though when it just happened and you haven't had the chance to notice it.

At sea, a similar situation applies to ships that surrender in the middle of a cannon volley.
This is why there is a 'grace period' coded in per ship so that any hits right after a surrender don't immediately trigger the reputation loss.
This doesn't happen on shore.
Snag 1: a 'grace' period would not help. If you hit someone while he's trying to run away, he stops. (So do you if you're the one trying to run away and an enemy is attacking you.) In the heat of combat, you don't notice that he's stopped fighting (*), you just keep attacking. So he starts to turn to run away, stops because you've hit him, and the only difference is that the reputation loss kicks in a bit later. (Unless you manage to finish him off before the 'grace' period ends.)

(*) If you watch after every strike to see whether he's still fighting, most of the time the answer is yes, he's still fighting, and he hit you while you were looking to see if he could still hit you.

Snag 2: even if you've noticed that the enemy has lost his weapon and is trying to run away, your officers don't. They will pursue and kill him anyway.

Snag 3: I disagree about it being unrespectable. If someone has just tried to kill you, you're entitled to kill him. Likewise, if you're at sea and an enemy ship is trying to run away, you're perfectly entitled to chase it and finish it off. If the ship surrenders then continuing to fire on it is certainly naughty - and that's where the analogy ends, as land enemies can't surrender.

On a side-note... I think this code has never functioned as intended:
Code:
bool isSetBalde = (SendMessage(enemy, "ls", MSG_CHARACTER_EX_MSG, "IsSetBalde") != 0);
It should return whether a character has drawn his/her blade.
But as far as I know, it only returns 'true' for characters who don't have a blade equipped at all.
Which is stupid, because we can check that easily on the PROGRAM side; without a need to ask the engine itself using 'SendMessage' .

I wonder if that was ever fixed in later releases like CoAS or TEHO...
Having downloaded the GoF mod for CoAS with the aim of borrowing character, weapon and ship models, I can answer part of that. CoAS (or at least, GoF) has this in "LAi_fightparams.c":
Code:
bool isSetBalde = (CheckAttribute(enemy, "equip.blade") == true);//(SendMessage(enemy, "ls", MSG_CHARACTER_EX_MSG, "IsSetBalde") != 0);
But I don't think that will make much difference because NPC's who have weapons auto-equip them. (Apart from street traders, but attacking those brings trouble of a different kind!) Is there a condition to check if a blade is drawn, not just equipped?
 
Snag 1: a 'grace' period would not help. If you hit someone while he's trying to run away, he stops.
That's a snag indeed...

Snag 2: even if you've noticed that the enemy has lost his weapon and is trying to run away, your officers don't. They will pursue and kill him anyway.
They do?
Then perhaps the runner needs to be moved to another AI group too.
Thought that function might've already done that; but apparently not then...

If the ship surrenders then continuing to fire on it is certainly naughty - and that's where the analogy ends, as land enemies can't surrender.
The running away is supposed to be the shore equivalent of surrendering.

If you don't like it, simplest solution is to change that 50% chance so this never happens anymore.
No running = no problem.

CoAS (or at least, GoF) has this in "LAi_fightparams.c":
Code:
bool isSetBalde = (CheckAttribute(enemy, "equip.blade") == true);//(SendMessage(enemy, "ls", MSG_CHARACTER_EX_MSG, "IsSetBalde") != 0);
I think that's indeed functionally the same.
It isn't meant to be; but it is...

Is there a condition to check if a blade is drawn, not just equipped?
Pretty sure that SendMessage is meant to do exactly that.
It just doesn't.

And so it might as well be recoded to match GoF.
Then at least it's clearer what it really does. :facepalm
 
They do?
Then perhaps the runner needs to be moved to another AI group too.
Thought that function might've already done that; but apparently not then...
Don't count on AI groups to handle this. We already know from other situations that they don't always do what you want. The officer might continue chasing the enemy anyway; or other NPC's might start attacking him. Besides, which AI group - the local citizen AI group? So a group of random thugs attacks you in town, one of them breaks his sword and tries to run, your officer pursues, then all local citizens join in on the thug's side. :facepalm

What I'm going to try is this:
Code:
               if(Continue_Bladedamage && rand(1) > 0)
               {
                   LAi_SetCitizenTypeNoGroup(enemy);
                   if (sti(GetAttribute(attack, "index")) == GetMainCharacterIndex()) logit(TranslateString("", "Enemy lost weapon, trying to run!"));
                   enemy.attacked_you = true;
               }
You get a warning that the enemy is trying to run. He gets an attribute. Then, when checking for reploss (actually earlier in "LAi_events.c")...
Code:
if(!isSetBalde && !CheckAttribute(enemy, "attacked_you"))
... it checks this attribute as well as the enemy not having a weapon. But that's only part of the story. There are similar checks in "LAi_fightparams.c", for damage by blade and damage by gun, and for each of them a check for killing the enemy as well as a check for damage. I'm only adding the check for "attacked_you" in the checks for damage. So you get a warning that he's trying to run. You have a chance to spot it while you're still in battle rage and don't lose reputation. But you still get the reputation loss for killing him. (I might move the warning to the damage checks so that you get it repeatedly. If you still kill him after that, you can't complain that you weren't warned!)
 
Initial testing is looking good. I made a test version of "LAi_events.c" which forces enemies to break their swords, then went looking for a rumble in the jungle. Though I did indeed put the warning in "LAi_fightparams.c" in the damage check, the warning is not repeated because "logit" does not repeat a message which is already on screen. I'll leave it anyway, partly because I'm lazy and can't be bothered to change it back; and partly because it's just possible that you're using a low damage weapon, the enemy has still not died and still not managed to run away by the time the message disappears, and then it will be repeated. There's a separate section for damage by gun and I did not put a check on "attacked_you" there. This is supposed to stop you losing reputation for continuing to hit him with your sword during the heat of battle. If you have time to pull out a gun and shoot, you don't have that excuse!
 
Though I did indeed put the warning in "LAi_fightparams.c" in the damage check, the warning is not repeated because "logit" does not repeat a message which is already on screen.
Clever LogIt!

How often would it be triggered?
And how many alternate things could happen in the meantime that also generate log messages?
The only log message that isn't repeated is the most recent one.
 
How often would it be triggered?
And how many alternate things could happen in the meantime that also generate log messages?
The only log message that isn't repeated is the most recent one.
In theory the message should be shown every time you hit the would-be runner.

In a simple jungle fight against highwaymen or random thugs, not much else should happen unless quest things are happening at the same time. In a more complex setting you might hit the runner, then some innocent civilian wanders through the middle of the fight and gets hit, triggering the reputation loss message, then you go back to hitting the runner - if he hasn't taken advantage of the distraction to get away. Still, long-term experience of the game plus the occasional weird bug report on the forum shows that million-to-one chances occur nine times out of ten...
 
Still, long-term experience of the game plus the occasional weird bug report on the forum shows that million-to-one chances occur nine times out of ten...
:rofl :rofl :rofl

On the other hand... As long as people don't complain about it, apparently it's not a problem. :cheeky
 
Back
Top