• 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!

Included in Build Looting Uniforms from Soldiers

Pieter Boelen

Navigation Officer
Administrator
Storm Modder
Hearts of Oak Donator
Defined thus:
Code:
void SoldierReinforcements(ref refCharacter)
{
   ref PChar = GetMainCharacter();

   if (sti(GetAttribute(refCharacter, "quest.guard_protection")) < 1)
   {
     if (GetAttribute(PChar  , "vcskip") == true)                                         return;
     if (GetAttribute(LoadedLocation, "vcskip") == true)                                         return;

     if(!HasSubStr(GetAttribute(refCharacter, "id"), "soldier"))                                     return;
     if(!HasSubStr(GetAttribute(refCharacter, "Dialog.Filename"), "Soldier_dialog.c"))                         return;

     if(!HasSubStr(GetAttribute(refCharacter, "location"), "port") && !HasSubStr(GetAttribute(refCharacter, "location"), "town"))   return;
   }

   int numGuards = 30 - LAi_numloginedcharacters;     // LDH - changed from 32 to TIH's original 30
   if (numGuards > 2 ) numGuards = 2;
   if (numGuards > 0 )
   {
     Logit(TranslateString("","The Garrison Commander sent immediate reinforcements..."));
     LAi_CreateFantomGroup("Soldiers", numGuards, LAI_GROUP_ENEMY, LAI_GROUP_NEUTRAL, GetCurrentLocationNation(), OFFIC_TYPE_GUARD, 0, true, "", "", "", "");

     if(!CheckAttribute(PChar, "locationLock"))
     {
       PChar.locationLock = true;
       StartQuestMovie(true, false, false);
     }
   }
}
(Inside spoiler tags because there's rather a lot of it.)
That seems to lock exits and send reinforcements. It doesn't seem to unlock them again after all the soldiers are dead. Where is the code for the end of the battle? That's when you'd have time to help yourself to one of the dead guards' uniforms...
Got it! The code you're looking for is in PROGRAM\Loc_ai\LAi_groups.c:
Code:
//------------------------------------------------------------------------------------------
//The response to requests
//------------------------------------------------------------------------------------------

//Update the alarm, called on every frame
#event_handler("CharacterGroup_UpdateAlarm", "LAi_group_UpdateAlarm");
void LAi_group_UpdateAlarm()
{
   ref mchr = GetMainCharacter(); // KK
   LAi_grp_playeralarm = GetEventData();
   LAi_grp_alarmactive = GetEventData();
   if(!bSeaActive)
   {
     // PB -->
     if(!LAi_grp_alarmactive && CheckAttribute(mchr, "locationLock"))
     {
       EndQuestMovie();
       DeleteAttribute(mchr,"locationLock");
     }
     // PB <--
:doff
 
That seems a bit too general, appearing to cancel "locationLock" for whatever reason it was imposed. Perhaps put the reward outfit in 'SoldierReinforcements' after all. Technically you'll receive it too soon, but either you'll survive the battle in which case it won't much matter exactly when you got the outfit, or you won't survive the battle in which case it really won't matter exactly when you got the outfit! Putting away your weapon so you can press F2 and admire your new outfit prematurely will increase the chances of the latter result. :D
 
That seems a bit too general, appearing to cancel "locationLock" for whatever reason it was imposed.
Yep, that's exactly what it is meant to do. It no longer "knows" anything about the reason for it, so it would be hard to add extra specific stuff to it.
Not impossible though, but probably not worth the trouble either.

Perhaps put the reward outfit in 'SoldierReinforcements' after all.
I'm probably a bit stupid here (damn my lack of time to pay proper attention to everything! :whipa ), but.... what reward did we want to add here again?
Some sort of "get player-character specific soldier outfit" when it happens? There aren't all that many of those; mainly just a few Nathaniel Hawk and Jack Sparrow variations.
But those are also already assigned when being promoted as "Naval Officer Nathaniel Hawk" and are used as appropriate during the Hoist the Colours storyline.

Also, 'SoldierReinforcements' only gets triggered if you deliberately kill a generic town guard.
While it is totally possible to do that, I'd be inclined to not recommend players to do that.
There tends to be a good chance of not surviving it.... :wp
 
That's the point. The chances of surviving the attack on the town guard and all the consequences are low. So if you succeed, you could get a free army uniform from the relevant nation. Maybe a special uniform not available from the tailor, though that would mean one special uniform per nation per time period; or maybe remove army officer uniforms from tailors, so you only get them by defeating the whole town guard.

But there's something wrong if it's easier to defeat the entire town's army by attacking the fort than to defeat the local guard after attacking one of them - where were these guard supermen when the town itself was being attacked? :confused:
 
That's the point. The chances of surviving the attack on the town guard and all the consequences are low. So if you succeed, you could get a free army uniform from the relevant nation. Maybe a special uniform not available from the tailor, though that would mean one special uniform per nation per time period; or maybe remove army officer uniforms from tailors, so you only get them by defeating the whole town guard.
Automatically handing out a random guard uniform or the general nation-specific soldier officer one should be relatively easy.
There is a function call you can use to find whichever uniform that happens to be for that nation and time period.

At the moment you can buy soldier uniforms from the tailors ONLY if you are a Privateer/Naval Officer have have the required promotions.
Your suggestion sounds like a viable way of getting them if you are not in a position to buy them.

Though I do wonder: Why would you want them if you're not a Privateer or Naval Officer?
Most people I know like to use those uniforms for their officers and/or crew.
It would become more relevant if they would serve as a disguise of sorts. But right now that is not a feature that exists.

But there's something wrong if it's easier to defeat the entire town's army by attacking the fort than to defeat the local guard after attacking one of them - where were these guard supermen when the town itself was being attacked? :confused:
Town Capture is handled by the ship boarding code. It is completely independent of walking around towns while ashore.
I do agree with you that it is quite weird this way.

We do still have a Planned Feature to tweak the strength of boarding crews and bring some more logic and balancing into it.
When get to tackling that one, it should be quite doable to have the "extra strength town guard code" apply to soldiers during Town Capture as well. (Excluding the "reinforcements", of course)
This might make Town Capture excessively difficult though, because there are a lot more soldiers at one time when you do that as opposed to when you're randomly walking around.

How "super" they are depends heavily on difficulty, by the way.
It is a feature I added quite a while ago, because the town guards were far too easy targets.
They have brilliantly good weapons and you could easily steal those right in the early game.

We could also disable the "extra strength Town Guards" though, because we now have the "Soldier Reinforcements" feature that serves a very similar purpose.
 
Though I do wonder: Why would you want them if you're not a Privateer or Naval Officer?
Most people I know like to use those uniforms for their officers and/or crew.
It would become more relevant if they would serve as a disguise of sorts. But right now that is not a feature that exists.
Partly as a form of trophy for having defeated the guards. And partly as another preparation in case disguising does come into play later. (There's already a perk, "Disguiser", which doesn't do much disguising due to there not being any disguising. :D)

We do still have a Planned Feature to tweak the strength of boarding crews and bring some more logic and balancing into it.
When get to tackling that one, it should be quite doable to have the "extra strength town guard code" apply to soldiers during Town Capture as well. (Excluding the "reinforcements", of course)
This might make Town Capture excessively difficult though, because there are a lot more soldiers at one time when you do that as opposed to when you're randomly walking around.
And, since this thread specifically mentions Nathaniel and the main quest, it is a good time to point out that making town capture significantly harder is going to cause Nathaniel problems when it comes to that part of the story where he captures Bridgetown. The super guards already fouled up that story, leading to the need for an excuse to weaken them again for the early mission. (Which, by the way, won't be needed if you restore regular guards to their original strength and rely on the reinforcements to dissuade players from random thuggery. Maybe use the offset to make the reinforcements a bit tougher so it's still really dangerous to get into a fight with the guards.)
 
Partly as a form of trophy for having defeated the guards.
Would just the "officer outfit" suffice for that? Or would you need to get all the individual soldier variations as well?

And, since this thread specifically mentions Nathaniel and the main quest, it is a good time to point out that making town capture significantly harder is going to cause Nathaniel problems when it comes to that part of the story where he captures Bridgetown. The super guards already fouled up that story, leading to the need for an excuse to weaken them again for the early mission. (Which, by the way, won't be needed if you restore regular guards to their original strength and rely on the reinforcements to dissuade players from random thuggery. Maybe use the offset to make the reinforcements a bit tougher so it's still really dangerous to get into a fight with the guards.)
You are quite right. It is also not actually related to the original point of this thread, so I made a new one instead:
Discussion - Regarding Town Guard/Soldier Strength | PiratesAhoy!
I'd welcome your thoughts on it there. :doff
 
Would just the "officer outfit" suffice for that? Or would you need to get all the individual soldier variations as well?
The relevant officer outfit for the nation and period would do - you've wiped out the whole unit, so if you're helping yourself to someone's uniform as a trophy, it may as well be the commander's. Or a random uniform for nation and period, as you pick the one which has least holes in it. Collecting the whole set would be more complicated - from the player's point of view as he needs to keep attacking guards to get the lot, from the programmer's point of view as the code would need to check which outfits the player already has and pick a random one not currently owned. For something that's just a little bonus for a player who actually managed to take on the guards and win, I'd recommend keeping it simple. :D
 
The relevant officer outfit for the nation and period would do - you've wiped out the whole unit, so if you're helping yourself to someone's uniform as a trophy, it may as well be the commander's.
I think adding this line would do the trick:
Code:
GiveModel2Player(GetRandomModelForTypeExSubCheck(true, "Land_Officers", "man", GetCurrentLocationNation() ), false);
You could add an extra LogIt message to indicate that this happened.

Or a random uniform for nation and period, as you pick the one which has least holes in it.
For that, use "Soldiers" instead of "Land_Officers".

Collecting the whole set would be more complicated - from the player's point of view as he needs to keep attacking guards to get the lot, from the programmer's point of view as the code would need to check which outfits the player already has and pick a random one not currently owned.
Indeed I cannot think of any simple way of accomplishing that from the programming side.
The simplest version I could think of is:
Code:
if (frnd() < 0.2) GiveModel2Player(GetRandomModelForTypeExSubCheck(true, "Land_Officers", "man", GetCurrentLocationNation() ), false); // 20% chance of an officer
else GiveModel2Player(GetRandomModelForTypeExSubCheck(true, "Soldiers", "man", GetCurrentLocationNation() ), false); // 80% chance of a regular soldier
There is no guarantee then that you'll get an outfit that you didn't already have, but it should at least do more than nothing.

Since this feature is your idea, I'll leave the final implementation up to you.
I think you should have enough details to play around with for now. :doff
 
@Grey Roger: I made this a separate feature request here so we won't forget about it. Should be easy enough to do it.

I myself have plenty of other things still to be done, so it won't be me who makes this happen.
But I'll help whoever does (you?) if necessary. :cheers
 
'SoldierReinforcements' seems to be called separately for each pair of reinforcement soldiers. That happens for each soldier you kill, starting with the one you originally attacked and also for every other guard in the area that joins in. Is there a way to check whether you've been given the reward outfit so you only get one? Otherwise, if it's picking a random uniform each time, you are likely to end up with the whole lot, along with a 'logit' message every time a new reinforcement is summoned.
 
also for every other guard in the area that joins in
Not for those, no. Only for the actual guards; not for the reinforcements.

Is there a way to check whether you've been given the reward outfit so you only get one?
You cannot have more than one of each outfit that exists (they don't work the same as items do).
You don't need more of them anyway, because you can assign the same outfit to as many characters as you want once you have it.

This is what the function I suggested does:
Code:
void GiveModel2Player(string model, bool assign)
{
   Characters[GetMainCharacterIndex()].clothes.(model) = true;
   if(assign) SetModelfromArray(GetMainCharacter(), GetModelIndex(model));
}
There is no harm in calling that multiple times with the same outfit IF you use "assign = false".
With true, you override the player model every time and I don't think you want that.
But with false, you'd just end up adding a character attribute that was already there anyway.

If you do want to avoid it, you can use something like:
Code:
string model = "Jack";
if (!CheckAttribute(PChar, "clothes." + model))
{
LogIt("Received Model: " + model);
GiveModel2Player(model, true);
}
I think that should work. It would even possible to add a default LogIt message IN the GiveModel2Player function, along with that check.
Then you just need to call the function and not need to worry anymore about the rest.

If you want to ensure that every time you get an outfit, you get a NEW one, then it becomes complicated.
It then needs to become something like this (in UNTESTED code!):
Code:
string model = "";
int i = 0;
while (model == "" || CheckAttribute(PChar, "clothes." + model) )
{
model = GetRandomModelForTypeExSubCheck(true, "Soldiers", "man", GetCurrentLocationNation() );
if (!CheckAttribute(PChar, "clothes." + model)) GiveModel2Player(model, true);
else
{
i++
if (i > 20) return;
}
}
Not impossible. But you may want to avoid that level of complexity as it is probably not needed. :cheeky
 
Not for those, no. Only for the actual guards; not for the reinforcements.
Which is why I said "for every other guard in the area that joins in", not "for every "soldier". ;)

You cannot have more than one of each outfit that exists (they don't work the same as items do).
You don't need more of them anyway, because you can assign the same outfit to as many characters as you want once you have it.
True, but 'GiveModel2Player(GetRandomModelForTypeExSubCheck(true, "Land_Officers", "man", GetCurrentLocationNation() ), false);' will assign a random uniform. If there are more than one eligible uniform then each time you kill a guard, 'SoldierReinforcements' will be called and potentially give you a different one. Whereas I'd only like to award one uniform for the whole battle.

If you want to ensure that every time you get an outfit, you get a NEW one, then it becomes complicated.
Quite the reverse. I want to ensure that, within one battle, you only get one. Getting the same one repeatedly is not a problem as you pointed out, so if it's possible to pick a random uniform at the beginning of the battle and assign that each time, it would work. But that also involves setting something at the start of the battle and remembering it throughout. On the other hand, if you kill a guard in Havana and survive the consequences, earning one Spanish uniform, and then kill a guard in Santiago and do the same, you should have the chance of getting a different one, so I don't want to merely skip the reward if you already have any uniform of the guards' nationality. (It doesn't need to keep going until it finds a uniform which you don't already have. If it picks one which you already have, tough, you're not getting another one this time. That way it won't get stuck if you have already collected the full set when you kill the next guard.)
 
Which is why I said "for every other guard in the area that joins in", not "for every "soldier". ;)
Ah, OK. The words "that joins in" threw me off there.

True, but 'GiveModel2Player(GetRandomModelForTypeExSubCheck(true, "Land_Officers", "man", GetCurrentLocationNation() ), false);' will assign a random uniform.
Technically, no it doesn't. Simply because virtually all nations have only a single model assigned to "Land_Officers".
For the random ones, it is the "Soldiers" group you need. Refer to post #9 above.

True, but 'GiveModel2Player(GetRandomModelForTypeExSubCheck(true, "Land_Officers", "man", GetCurrentLocationNation() ), false);' will assign a random uniform. If there are more than one eligible uniform then each time you kill a guard, 'SoldierReinforcements' will be called and potentially give you a different one. Whereas I'd only like to award one uniform for the whole battle.
Two things you could do:
1. Work it into LAi_group_UpdateAlarm after all.
or
2. Change the SoldierReinforcements function so that instead of 1-2 showing up for every guard you kill, a WHOLE BUNCH show up, but only for the FIRST guard you kill.
That should be simple enough; just requires checking if the "locationLock" attribute is already set and, if so, don't bother with the reinforcements.

The second one relates very much to Discussion - Regarding Town Guard/Soldier Strength | PiratesAhoy!, of course.

Quite the reverse. I want to ensure that, within one battle, you only get one. Getting the same one repeatedly is not a problem as you pointed out, so if it's possible to pick a random uniform at the beginning of the battle and assign that each time, it would work. But that also involves setting something at the start of the battle and remembering it throughout.
Then try this in SoldierReinforcements:
Code:
if (!CheckAttribute(pchar, "receivemodel") pchar.receivemodel = GetRandomModelForTypeExSubCheck(true, "Soldiers", "man", GetCurrentLocationNation() );
And this in LAi_group_UpdateAlarm:
Code:
if (CheckAttribute(mchr, "receivemodel"))
{
GiveModel2Player(mchr.receivemodel, false);
DeleteAttribute(mchr, "receivemodel")
}
Or something like that.

That may do the trick. Add the post #9 officer chance in that SoldierReinforcements section if you like that.
 
Technically, no it doesn't. Simply because virtually all nations have only a single model assigned to "Land_Officers".
For the random ones, it is the "Soldiers" group you need. Refer to post #9 above.
In that case, never mind the repeated rewards for hitting the same nation again. You get the officer uniform and if you survive the battle then you get to wear it. Getting the same uniform again doesn't cause trouble so I don't need to check for it. Which means all that is needed is 'GiveModel2Player(GetRandomModelForTypeExSubCheck(true, "Land_Officers", "man", GetCurrentLocationNation() ), false);' added to "CCCFunctions.c". See attached.

Two things you could do:
1. Work it into LAi_group_UpdateAlarm after all.
or
2. Change the SoldierReinforcements function so that instead of 1-2 showing up for every guard you kill, a WHOLE BUNCH show up, but only for the FIRST guard you kill.
That should be simple enough; just requires checking if the "locationLock" attribute is already set and, if so, don't bother with the reinforcements.
'LAi_group_UpdateAlarm' seems to be too general, I don't want to hand out uniforms for other events. And I don't want to mess with a basic mechanism just for a little bonus item. So an officer uniform for the nation whose guards you're fighting, and if you want more rewards then go and challenge someone else's army. xD
 

Attachments

  • CCCFunctions.c
    23.2 KB · Views: 220
In that case, never mind the repeated rewards for hitting the same nation again. You get the officer uniform and if you survive the battle then you get to wear it. Getting the same uniform again doesn't cause trouble so I don't need to check for it. Which means all that is needed is 'GiveModel2Player(GetRandomModelForTypeExSubCheck(true, "Land_Officers", "man", GetCurrentLocationNation() ), false);' added to "CCCFunctions.c". See attached.
Does sound like the simplest possible solution. :onya

'LAi_group_UpdateAlarm' seems to be too general, I don't want to hand out uniforms for other events. And I don't want to mess with a basic mechanism just for a little bonus item. So an officer uniform for the nation whose guards you're fighting, and if you want more rewards then go and challenge someone else's army. xD
My last suggestion does cover that. It ONLY gives a model IF the "receivemodel" attribute is set.
And that is set ONLY when Soldier Reinforcements are generated (and the location is locked).

LAi_group_UpdateAlarm then MUST unlock the location (otherwise you remain stuck) and when it does,
you both get the model AND the attribute gets removed so you won't get it again.

So I think that solution would do exactly what you described in post #13.
That being said, if you're also happy with the simpler solution, then a more complicated seems a bit superfluous. :cheeky
 
That would indeed seem to be better, partly because it allows for extra different uniforms after attacking guards of the same nation again, but mainly because it only gives the reward uniform when the fight is actually over. Try these...
 

Attachments

  • CCCFunctions.c
    23.3 KB · Views: 239
  • LAi_groups.c
    17.7 KB · Views: 236
That would indeed seem to be better, partly because it allows for extra different uniforms after attacking guards of the same nation again, but mainly because it only gives the reward uniform when the fight is actually over. Try these...
Getting there! :cheers

Three suggestions:

1. Use this so you can get the officer uniform too:
Code:
     // GR: Stealing Soldier Uniforms -->
     if (!CheckAttribute(pchar, "receivemodel"))
     {
       if (frnd() < 0.2) pchar.receivemodel = GetRandomModelForTypeExSubCheck(true, "Land_Officers", "man", GetCurrentLocationNation() ); // 20% chance of an officer
       else pchar.receivemodel = GetRandomModelForTypeExSubCheck(true, "Soldiers", "man", GetCurrentLocationNation() ); // otherwise a random soldier
     }
     // GR: Stealing Soldier Uniforms <--
2. And this to prevent seeing that LogIt message if you already got that particular model:
Code:
       // GR: Stealing Soldier Uniforms -->
       if (CheckAttribute(mchr, "receivemodel") && !CheckAttribute(mchr, "clothes." + mchr.receivemodel))
       {
         GiveModel2Player(mchr.receivemodel, false);   // if you have just defeated a group of soldiers and reinforcements,
         DeleteAttribute(mchr, "receivemodel");     // get a free uniform as set by SoldierReinforcements in CCCFunctions.c
         LogIt("You take a uniform from one of the dead soldiers.");
       }
       // GR: Stealing Soldier Uniforms <--
But since in theory someone may end up using "receivemodel" for non-soldiers too,
it may be better to not specify the "soldier" business in the LogIt message. Therefore:

3. OPTIONAL, rewrite GiveModel2Player in PROGRAM\NK.c to automatically take care of the LogIt message instead:
Code:
void GiveModel2Player(string model, bool assign)
{
   if(assign)
   {
     SetModelfromArray(GetMainCharacter(), GetModelIndex(model));
   }
   else
   {
     if (!CheckAttribute(GetMainCharacter(), "clothes." + model))   LogIt("You got the " + model + " outfit: " + Models[GetModelIndex(model)].description);
   }
   Characters[GetMainCharacterIndex()].clothes.(model) = true;
}
 
Having 'GiveModel2Player' issue a 'logit' whenever you get a new outfit might not be a good idea as it would generate potentially annoying messages whenever quests assign you a new model. "Bartolomeu" with the various pirate outfits and "Hornblower" with the various rank uniforms, not to mention any free-play naval officer who gets new rank uniforms as promotion rewards, would be affected.

For the message as part of 'LAi_group_UpdateAlarm', perhaps make it conditional:
Code:
if(!HasSubStr(mchr.receivemodel, "soldier")) LogIt("You take a uniform from one of the dead soldiers.");

Or use the same general 'logit' in 'LAi_group_UpdateAlarm' as you proposed for 'GiveModel2Player', so it doesn't say anything about soldiers, it just tells you that you got a new model.
 
Having 'GiveModel2Player' issue a 'logit' whenever you get a new outfit might not be a good idea as it would generate potentially annoying messages whenever quests assign you a new model. "Bartolomeu" with the various pirate outfits and "Hornblower" with the various rank uniforms, not to mention any free-play naval officer who gets new rank uniforms as promotion rewards, would be affected.
That is why in my suggestion, the log message shows up only if it is a NEW model you got.

And if the model is being directly assigned, the message is skipped too.
After all, you don't need to be told you got something then because you can see that yourself too.
Note that the LogIt line is in the 'else' section of the 'if(assign)'.

This second case is what happens mostly in the various storylines and for naval officer promotions too.
Model is directly assigned, so no on-screen message. I think that should be OK, no?
 
Back
Top