Modding Q&A [For Quick Questions and Answers]

Users who are viewing this thread

Status
Not open for further replies.
Cozur said:
I'm trying to improve the performance of ACOK on the world map, as it has a tendency to stutter a bit much, while at the same time also having some weird pathfinding for the player character.

What are the most common reasons for the two above?

1)  As Karlahan mentioned, be afraid of try_for_parties loops in triggers, especially as the party count climbs.
  A fairly common old OSP code snippet in module_triggers.py creates kingdom parties such as patrols, scouts, foragers and caravans.  This seems simple enough, but watch how it was written for native and see what happens when the factions, party count, etc, grows:

  (try_for_range, ":cur_kingdom", kingdoms_begin, kingdoms_end),
      .
      (call_script, "script_create_kingdom_party_if_below_limit", ":cur_kingdom", spt_forager),

  and that looks pretty safe... but if you dig a little deeper, script  create_kingdom_party_if_below_limit has within it a "try_for_parties" loop in it.

    For native, where you might have 6 factions and perhaps 600 parties, the math is bad enough but manageable:
  [6 factions * (600 parties per time script _create_kingdom_party_if_below_limit gets called)], and it might get called once per party template  spt_forager, spt_patrol, spt_forager, and spt_caravan.  I might get away with a small stutter on the world map every time this trigger occurs, but I might shrug and say "slow PCs, nothing can be done".  I might... but I might notice I just caused 1 single trigger to run a code snippet (6*600*4) times == 14400 times!! as 1 trigger!!!

    Now for Perisno or Phantasy 2018 (or worse, Warsword Conquest), the number climbs due to both more factions and more total party count.  Having some experience with each, taking Phantasy 2018 as a middle case I have 13 factions and 1100 parties total, so if this was called in a single trigger, I now have some code snippet running
  (13 * 1100 * 4) times == 57,200 times!  If that were just left as-is, I should expect a stutter on the world map lasting about 4 times longer than the same code executing at the mod it was first written for.  Instead, I had to break it into 4 different triggers.  But... that leads to issue #2

2)  Consider the following triggers:  one hourly, one at 2 hours, one at 4 hours, one at 8 hours, one at 24 hours, one weekly.
the next week, at the same time I'm processing the weekly trigger, also firing at that exact instant is the 24 hour, the 8 hour, the 4 hour, the two hour, and every single 1 hour trigger -- ALL AT ONCE.

    The artful solution is to move as many triggers to prime numbers to slightly different timing so they don't fire at the same time, spreading the hit on performance out across time.  If I had a trigger that fired every 24 hours, but could have gotten away with 23 or 25 hours, I now moved that little bit of performance spike to a new location, if I was graphing CPU time consumed versus time of day within game. If the above triggers for kingdom creation used to be say every 3 hours but now are at 13, 17, 19, and 23 hours, they hardly ever overlap, and the perceived performance hit is smoothed.  If I really do need them to happen close together, I could still cheat a bit by setting the time to say 17.3 hours till next repeat for one, and 17.7 hours for the next one and 18.3 for the next one and 18.7 for the last one, although at hour 18 would also be firing all your 1 hour triggers, all your 2 hour triggers, all your 3 hour triggers, etc -- and all at once, so I hate burdening one of my mods with triggers firing at even numbers of hours. I prefer prime numbers or fractions of an hour, although there is a limit to how small an hour you can choose according to this line at module.ini  (I think, and perhaps I'm wrong):
  time_multiplier = 0.25      # I will guess this is the granularity of hourly timers, so if I have a timer set to 11.1 and another at 11.2 and a third at 11.25  and a fourth at 11.26 and another at 11.49 the first 3 fire together and the last two fire at the same time as 11.49  -- but I'm not entirely sure.

  How to guesstimate the worst performance in your campaign map?
  in python editor, find in files      try_for_parties  and just see how many triggers, simple_triggers, and scripts called from within triggers and simple triggers there are.  Split those up first, so that say in the case of my example #1, HALF the factions were processed, then in a different trigger called on a different period (else there was no value in splitting it to a new trigger, unless there were the ability to run multiple threads, and if so be afraid of using reg values for any reason so that one thread didn't walk on regs used by another thread, ever).

  I can't say you will instantly solve lag, but the difference in Perisno 0.771 (no optimization) and 0.773 (both optimizations above) was about 35%.  I'll guess that even if my 0.7 series was partly discarded, the later Perisno kept at least those optimizations.  I can say something similar occurred between Warsword Conquest 2016 (not optimized bandit spawns, same issue) and my purely experimental Warsword Rigale (optimized bandit spawns) build the FOLLOWING MONTH.  Just changes at a few triggers!  Removing icon checks for all the parties over water trimmed another 10% or so.  There were certain triggers that were performance hogs compared to all other triggers.

By Phantasy 2018 I had cut the number of total parties and aggressively policed EVERY try_for_parties and try_for_agents use to groom the code.  I think you'll need to review you code likewise.

  Good luck with that.  We both may be unpopular with other users, but I've seen you over the years and have great respect for your intelligence.  I know how frustrating it is to mod with no one willing to rescue you, and users telling you how disappointed they are etc.  When you break it down and start looking for places where the code really is calling something too many times you might find ways to optimize that didn't seem important before.  I did it by hand instead of a fancy tool like Karlahan mentioned exists, but I am certain you can handle your code with just a few tips.  Sometimes it just needed a little heads up to get the break through you were praying for (or however you handle tech support requests).  It sure beats beating your head against the keyboard.

  Good luck and 'God Speed'.

  - GS

 
Lord Engineer said:
How do you edit NPC relation to one another?, like how would I make Lord Irya always be the brother of Lord Aedin?.

Is there any way I can edit already existing relationships?, like making Klagrus no longer the father of Beranz.

start by checking your own thread and add any other info (like if you tried this, what you did not understand, and so on). You can keep posting here, but you should update your own thread to make that clear (close it or add a note about it).

https://forums.taleworlds.com/index.php/topic,377937.msg9001643.html#msg9001643
 
Hello all, how can I assign to one unit in NW two or more firearms simultaneously? And btw how can I add mare than 4 items to one unit?

And more general question - how can I make outer terrain, which is behind the scene, have shadows like they are generated on the scene? I'm trying to make extension of my scene by placing trenches behind the scene on my new outer terrain mesh, but without shadows they look very blury and bleak, but if I assign same shader as uniform has, whole meshes just are dark shaded.
 
gsanders said:
2)  Consider the following triggers:  one hourly, one at 2 hours, one at 4 hours, one at 8 hours, one at 24 hours, one weekly.
the next week, at the same time I'm processing the weekly trigger, also firing at that exact instant is the 24 hour, the 8 hour, the 4 hour, the two hour, and every single 1 hour trigger -- ALL AT ONCE.

    The artful solution is to move as many triggers to prime numbers to slightly different timing so they don't fire at the same time, spreading the hit on performance out across time. 

NO.
This is a bad idea.

I've seen your optimization in Perisno and I thought 'wow, that's a cool solution', and followed like a sheep doing this to more triggers, but months later I tested it and we both underestimated the intelligence of TW devs.

Counters of all triggers of the same frequency start at different times, they're given different offsets at the start of the game.
I.e., you have 10 24-hour triggers, but they will trigger at different time, not at once.

Whole numbers (like 24, 6, 4) are actually almost a guarantee that triggers will never be triggered at the same time, because they started running at different time and there is no change in their "pace".
Fractional triggers, however, have different "pace", so sooner or later they will intersect with each other, having "compensated" that offset that was given to them at the start of the game by the engine.

You can easily test it by creating multiple triggers of the same frequency or frequency that is 'part' of the largest one (sorry, my math English vocabulary is non-existent), like 2, 4, 6, 8, 24 and adding display_message operations like "Trigger 1/2/3/4 was called!" to each.
You won't receive these messages at the same time.
______________________________________________________

So this is not a way to optimize triggers.
The best ways are indeed getting rid of big try_for_parties triggers and diving large triggers into parts, like that enormous faction AI trigger that does a lot of calculations for all factions at once:
Code:
   (0,
   [
     (eq, "$g_recalculate_ais", 1),
     (assign, "$g_recalculate_ais", 0),
     (call_script, "script_recalculate_ais"),
   ]),

Code:
   (0.1, 
   [
	 (try_begin),
		(is_between, "$g_recalculate_ais_cur_fac", kingdoms_begin, kingdoms_end),
		(try_begin),
			(faction_slot_eq, "$g_recalculate_ais_cur_fac", slot_faction_state, sfs_active),
			(call_script, "script_recalculate_ais_for_faction", "$g_recalculate_ais_cur_fac"),
		(else_try),
			(assign, ":cycl_end", kingdoms_end),
			(assign, ":cycl_beg", "$g_recalculate_ais_cur_fac"),
			(try_for_range, ":faction_no", ":cycl_beg", ":cycl_end"),
				(faction_slot_eq, ":faction_no", slot_faction_state, sfs_active),
				(assign, "$g_recalculate_ais_cur_fac", ":faction_no"),
				(call_script, "script_recalculate_ais_for_faction", "$g_recalculate_ais_cur_fac"),
				(assign, ":cycl_end", 0),
			(try_end),
		(try_end),
	 (else_try),
		(assign, "$g_recalculate_ais_cur_fac", kingdoms_begin),
		(call_script, "script_recalculate_ais_for_faction", "$g_recalculate_ais_cur_fac"),
	 (try_end),
	 (val_add, "$g_recalculate_ais_cur_fac", 1),
   ]),
 
Leonion said:
we both underestimated the intelligence of TW devs

just because it is a feature that lacks documentation (like everything else in teh modsys), it doesnt make it a bad thingy  :razz:. This is a great engine mechanic, as you don't need to worry about distribution of schedule triggers that dont have a performance impact (think Native small numbers).

First occurance is not important in a new game. Player is just starting out, player has no fiefs, diplomacy is already decided for that time period, and all that. So it doesnt matter if a weekly trigger fires on day 2, 9, 11, 18, ..., while other weekly trigger fires at 5, 12, 19, 26, ...

if you do have a instance where you need to control the schedule then use the recharge timer, and not the waiting period, as your control. That is why we have 3 timers on full triggers.

My reply above on how VC solved this problem goes around this mechanic, as does yours, with a look at performance. VC uses a random distribution for big numbers (to understand it you can look at @moto thread for the math).
 
kalarhan said:
just because it is a feature that lacks documentation (like everything else in teh modsys), it doesnt make it a bad thingy  :razz:.
Never said it was.
Underestimated, not overestimated.
They took into account something we assumed they didn't. We wasted our time fixing a problem that never existed.
 
Leonion said:
We wasted our time fixing a problem that never existed.

actually we wasted our time solving the wrong issues  :razz:. A good example is when a player reports ocassional lag spikes that cant be reproduced (as the time slot is unique to that savegame). I learned about triggers being tricky because of that lol (fun times).

it happens often when we just assume stuff on a closed box like the Warband engine. It often surprises us, and not always in a good way  :mrgreen:
 
Hey

I've got a number of smaller questions this time:
1. Is there any way to edit the native formations or are they hardcoded? If it is possible, where is the code for them stored?
2. I've been trying to find a tutorial on how presentations work. I can't seem to find any tho. Does anyone have any suggestions on where to find presentation tutorials?
3. Do order positions have rotations, and if so do the rotations indicate the direction that troops will face and line up perpendicularly to? (by order positions, I mean the position that is received by using the team_get_order_position operation)
4. There seems to be a difference between the hold position command where you use the orders menu and the hold position command where you hold down F1 to place a flag. What is the order code for the hold F1 command?
 
Oh and one more bigger question:

I'm trying to make a small formation tweak where when a division gets a hold position order, it forms up on that position in three rows, rather than the regular one (I just think it looks better and is more realistic). The method I've decided to use is I'm going to split the division into three smaller divisions and just have it so that when all of the divisions get a hold position order, the first division goes to the original order position, the second division goes a little ways behind it, and the third goes a little ways behind that one. The big problem I've been getting is that whenever I try this, all three divisions just end up going to the same place, despite me changing the order position for divisions 2 and 3. Is there something I'm missing when it comes to moving order positions around? Here's the script for it:
("form_ranks",
[
  (store_script_param_1, ":eek:rder_code"),
  (store_script_param_2, ":captain_agent"),
  (agent_get_team, ":captains_team", ":captain_agent"),
  (team_get_order_position, pos35, ":captains_team", 0), #get the original order position

  (copy_position, pos37, pos36), #create hold location for div 2 (1)
#   (position_copy_rotation, pos37, pos36), #copy the rotation of the original order pos
  (position_move_y, pos37, -200, 0), #move the pos back two meters behind first row
  (copy_position, pos38, pos36), #create hold location of div 3 (2)
#   (position_copy_rotation, pos38, pos36),
  (position_move_y, pos38, -400, 0), #move the pos back four meters behind first row
  (team_set_order_listener, ":captains_team", -1, 0), #reset order listener to no one
 
  (team_set_order_listener, ":captains_team", 1, 0),
  (team_set_order_position, ":captains_team", 1, pos36),
  (team_give_order, ":captains_team", 1, 0),
  (team_set_order_listener, ":captains_team", -1, 0),
 
  (team_set_order_listener, ":captains_team", 2, 0),
  (team_set_order_position, ":captains_team", 2, pos37),
  (team_give_order, ":captains_team", 2, 0),
  (team_set_order_listener, ":captains_team", -1, 0),
 
  (team_set_order_listener, ":captains_team", 0, 0),
 
  ]),
 
Guitarma said:
1. Is there any way to edit the native formations or are they hardcoded? If it is possible, where is the code for them stored?
2. I've been trying to find a tutorial on how presentations work. I can't seem to find any tho. Does anyone have any suggestions on where to find presentation tutorials?
3. Do order positions have rotations, and if so do the rotations indicate the direction that troops will face and line up perpendicularly to? (by order positions, I mean the position that is received by using the team_get_order_position operation)
4. There seems to be a difference between the hold position command where you use the orders menu and the hold position command where you hold down F1 to place a flag. What is the order code for the hold F1 command?

1. Yes, but you need to override it with your own code. See OSP section for FormationsOSP (or VC code, same creator)
2. Get some OSP with presentations and study how they work. Also use Lav's header_operations.py and read on all operations for presentations. You can always ask here once you have specific questions.
3. See (1)
4. F1 command lets you choose the spot. Command is on your location.

use edit button to add more stuff (instead of double posting)
 
Is there any difference between
(display_message, "@Blablahblah"),
(display_message, "@{!}Blablahblah"),
?
I've seen both formats in scripts, but messages look identical.
 
Somebody said:
The first one will generate a translation entry in the csv files, the second one won't.
Thanks.
__________________________________________________________________

BTW, speaking of optimization again, I had an issue of my own with global map performance and I finally solved it.
Maybe others will find the solution useful too.
The problem was with HUGE, literally unbearable lags when ctrl+t mode (revealed map) was enabled.
The issue, as turned out, was with game_get_skill_modifier_for_troop script, to which a bunch of extra code was added by me before.
In general the game managed to handle this new code well, but in ctrl+t mode the game called this script for tracking skill 14 times more often (550458 vs 38250 for the same time period).
It is also the most frequently 'used' skill of all.
h-344.jpg
That's how often the game_get_skill_modifier_for_troop script is called (usually from the engine) for different skills during roughly 1-day travel on the global map for player and some of his companions (left) or for everyone (right).
Random mod, random time, random numbers (of lords, parties and everything), but RELATIVELY this should give you some rough picture of how frequently different skills are called from the engine, and therefore which ones of them should be less "loaded" in game_get_skill_modifier_for_troop script (which, for example, is used for item bonuses or item set bonuses).
Both screenshots were taken in normal mode, without revealed map.
Also the mod where it was tested doesn't have any special "tracking/pathfinding enhancement" features (spotting may be called more often due to the use of game_check_party_sees_party script) so at least 98-99% of what you see for these skills are hardcoded engine calls.
 
If I set an item as abundance 0 why is it still showing up in loot?
["skeleton_armor", "Skeleton Armor", [("barf_skeleton_armor_full",0)],  itp_type_body_armor  |itp_covers_legs ,0, 1320 , weight(19)|abundance(0)|head_armor(0)|body_armor(40)|leg_armor(12)|difficulty(7) ,imodbits_armor ],
 
cwr said:
If I set an item as abundance 0 why is it still showing up in loot?
["skeleton_armor", "Skeleton Armor", [("barf_skeleton_armor_full",0)],  itp_type_body_armor  |itp_covers_legs ,0, 1320 , weight(19)|abundance(0)|head_armor(0)|body_armor(40)|leg_armor(12)|difficulty(7) ,imodbits_armor ],

I believe abundance just relates to shop merchandising and you have to flag the item as unique to stop it being looted.
 
Hello, I'm trying to make it so that when the player falls in battle he controls another agent and this is what I have so far (I'm using lua) :

function ChangePlayerAgent()
local player = game.get_player_agent_no(0)
local deadAgent = game.store_trigger_param(0, 1)

if deadAgent == player then
local newPlayerAgent

for agent in game.agentsI() do
if game.agent_is_ally(agent) and game.agent_is_human(agent) and game.agent_is_alive(agent) then
newPlayerAgent = agent
break
end
end

if newPlayerAgent ~= nil then
local playerID = game.agent_get_player_id(0, player)
game.player_control_agent(playerID, newPlayerAgent)
game.display_message("Set new player agent")
else
game.display_message("Couldn't find agent for player")
end
end
end

game.addTrigger("mst_lead_charge", game.const.ti_on_agent_killed_or_wounded, 0, 0, returnTrue, ChangePlayerAgent)

I'm almost certain that the cause of the problem is that I'm using "player_control_agent" which is for multiplayer but I can't find an equivalent for singleplayer. Does anybody know how to make this work?
 
Wendepoz said:
Hello, I'm trying to make it so that when the player falls in battle he controls another agent and this is what I have so far (I'm using lua) :

function ChangePlayerAgent()
local player = game.get_player_agent_no(0)
local deadAgent = game.store_trigger_param(0, 1)

if deadAgent == player then
local newPlayerAgent

for agent in game.agentsI() do
if game.agent_is_ally(agent) and game.agent_is_human(agent) and game.agent_is_alive(agent) then
newPlayerAgent = agent
break
end
end

if newPlayerAgent ~= nil then
local playerID = game.agent_get_player_id(0, player)
game.player_control_agent(playerID, newPlayerAgent)
game.display_message("Set new player agent")
else
game.display_message("Couldn't find agent for player")
end
end
end

game.addTrigger("mst_lead_charge", game.const.ti_on_agent_killed_or_wounded, 0, 0, returnTrue, ChangePlayerAgent)

I'm almost certain that the cause of the problem is that I'm using "player_control_agent" which is for multiplayer but I can't find an equivalent for singleplayer. Does anybody know how to make this work?

I can’t help with lua, but the Twilight of the Sun King mod’s source code is OSP & includes body sliding for singleplayer - look at their auxilliary triggers in mission_templates.py.
 
Status
Not open for further replies.
Back
Top Bottom