PYTHON SCRIPT/SCHEME EXCHANGE

Users who are viewing this thread

can anybody explain the error messages?
I have no idea what 'rank' refers to!  The troops file, I changed some of the upgrades, and one or two equipment items and stats, but I can't think what part of that was wrong...

Exporting strings...
Exporting sounds...
Exporting particle data...
Exporting skins...
Exporting map icons...
Exporting faction data...
Exporting item data...
Exporting scene props...
Exporting scene data...
Exporting troops data
Traceback (most recent call last):
  File "process_troops.py", line 82, in ?
    save_troops()
  File "process_troops.py", line 23, in save_troops
    for inventory_item in inventory_list:
TypeError: iteration over non-sequence
Exporting party_template data...
Exporting parties
Exporting quest data...
Exporting scripts...
WARNING: Variable name used for both local and global contexts:rank
WARNING: Variable name used for both local and global contexts:rank
Exporting mission_template data...
Exporting game menus data...
exporting simple triggers...
exporting triggers...
exporting dialogs...

______________________________

Script processing has ended.
Press any key to exit. . .
 
Amman - it means you have both :Rank and $Rank in the file somewhere, including the file variables.txt which preserves them even after they're gone from the modsys files.  Delete variables.txt (it'll get recreated), find all instances of either :Rank or $Rank and change them to be consistent throughout and/or to a new distinct name (like :RankLocal), and try it again.
 
Here is a little trick for "try_for_range". For example, you want to check all Heroes for a condition. You need a try_for_range for the first hero in module_troops.py to the last hero. Put all of the Heroes between two fake hero records ("first_npc" and "last_npc"):

Code:
["first_npc","First NPC","First NPC",tf_female|tf_hero, 0, reserved, .......
["Marnid","Marnid","Marnid", tf_hero, scn_the_happy_boar|entry(4),reserved, .......
["borcha","Borcha","Borcha",tf_hero, scn_town_13_castle|entry(6),reserved, .......
["ymira","Ymira","Ymira",tf_female|tf_hero, scn_zendar_merchant_ymira|entry(1), reserved, .......

# Add more Heroes here.

["last_npc","Last NPC","Last NPC",tf_female|tf_hero, 0, reserved, .......

Now, you can safely use in your scripts the following:
Code:
(try_for_range,":this_hero","trp_first_npc","trp_last_npc"),
	# Your code here.
(end_try,0),

No matter how many Heroes you add, just add them between trp_first_npc and trp_last_npc and your code will continue to work. You can also simplify this further by adding into header_operations.py the following:
Code:
try_for_heroes = try_for_range,":this_hero","trp_first_npc","trp_last_npc"

Then, you can simply try for a the range of Heroes like this:
Code:
(try_for_heroes),
    # Your code here.
    # The local variable ":this_hero" is still useable!
(end_try,0),

Follow the same principle anywhere you want to easily categorize your code. For example, you can make a try_for_blunts, try_for_armor, try_for_swadian_towns, etc, etc.
 
Hellequin said:
Amman - it means you have both :Rank and $Rank in the file somewhere, including the file variables.txt which preserves them even after they're gone from the modsys files.  Delete variables.txt (it'll get recreated), find all instances of either :Rank or $Rank and change them to be consistent throughout and/or to a new distinct name (like :RankLocal), and try it again.

In my python variables file, I only have '
variables =[
    "$g_move_heroes",
]


Is this normal?  I opened up the txtfile for variables and the three references to rank all seem uniform.  I assume by modsys files you refer to all the python 'module_whatever' files, and that references to &rank and :rank can be in all of them?  Or should I have a modsys file?

OK, Hellequin - thanks dude.  I just deleted the txt file variables, and re-ran build module, and there is no error message.
Now I can play my very frist python-mod!!  Whoo!
(It's nothing special, new map, some minor bits.... Every journey needs a first step...)
 
Script (v0.8xx) to match a horse to his rider during combat.

This code is for Schattenlander, so I know in advance that my party size is max 5 and that they'll all be companions.  If you're interested in checking for a different subset of characters, or just the PC or whatever, obviously those details will need to be changed.  In this case the code is being created to record whether a companion dismounted during the fight, or fought on horse; we check advancement for different skills in the two cases.  You'll see that in the <fought_mounted> slot setting on the PC.  Depending on your application a different record may need to be created.

In module_scripts.py:
Code:
("associate_PCs_with_their_horses",  # Need this to find out if they fought mounted or not.  Call within the mission template, time zero, ti_once.
	[
	(try_for_range, ":Char", 0, 5),  # Schattenlander party stacks - one per character as they're all companions.
		(party_stack_get_troop_id, ":Rider", "p_main_party", ":Char"),  # Get the actual troop IDs of the member in that spot in the party.
		(ge, ":Rider", 0),  # Break out now if there's no one in that spot in the party.

		(try_for_agents, ":CheckThisAgent"),
			(agent_get_troop_id, ":CheckThisID", ":CheckThisAgent"),
			(eq, ":Rider", ":CheckThisID"),
			(assign, ":RiderAgent", ":CheckThisAgent"),
		(try_end),
		(agent_set_slot, ":RiderAgent", 12, -1),  # Slot 12 on the agent is used to record their steed's agent-ID.
		
		(troop_set_slot, ":Rider", fought_mounted, 0),
		(try_for_range, ":Horse", horses_begin, horses_end),
			(troop_has_item_equipped, ":Rider", ":Horse"),
			(troop_set_slot, ":Rider", fought_mounted, 1),
		(try_end),
		(troop_slot_eq, ":Rider", fought_mounted, 1),

		(agent_get_position, 10, ":RiderAgent"),
		(try_for_agents, ":CheckThisHorse"),
			(neg|agent_is_human, ":CheckThisHorse"),
			(agent_get_position, 11, ":CheckThisHorse"),
			(get_distance_between_positions, ":Dist", 10, 11),
			(lt, ":Dist", 1),
			(agent_set_slot, ":RiderAgent", 12, ":CheckThisHorse"),
		(try_end),
	(try_end),
	]),
	
("check_if_PCs_are_mounted",  # Call this from a trigger within the mission template, time 5 (or thereabouts).
	[
	(try_for_range, ":Char", 0, 5),
		(party_stack_get_troop_id, ":Rider", "p_main_party", ":Char"),
		(ge, ":Rider", 0),
		(troop_slot_eq, ":Rider", fought_mounted, 1),

		(try_for_agents, ":CheckThisAgent"),
			(agent_get_troop_id, ":CheckThisID", ":CheckThisAgent"),
			(eq, ":Rider", ":CheckThisID"),
			(assign, ":RiderAgent", ":CheckThisAgent"),
		(try_end),

		(agent_get_slot, ":RidersHorse", ":RiderAgent", 12),  # Agent ID of the character's horse, from script "associate_PCs_with_their_horses"
		(try_begin),
			(ge, ":RidersHorse", 0),
			(agent_get_position, 10, ":RiderAgent"),
			(agent_get_position, 11, ":RidersHorse"),
			(get_distance_between_positions, ":Dist", 10, 11),
			(gt, ":Dist", 10),  # Nice generous 10cm margin to handle unusual cases.
			(troop_set_slot, ":Rider", fought_mounted, 0),
		(try_end),
	(try_end),
	]),

In module_constants.py you'll need values for horses_begin, horses_end, and fought_mounted.  I assume if you're able to make any use at all of this script that you'll know exactly how to set those up.  Watch for overlap between fought_mounted and any other modding of your troops' slots.

In module_mission_templates.py, in each mission form where you want to measure this (lead_charge, charge_with_allies, others as required), in the triggers section of the mission add:
Code:
(0, 0, ti_once, [],[(call_script, "script_associate_PCs_with_their_horses")]),
(5, 0, 0, [], [(call_script, "script_check_if_PCs_are_mounted")]),

The basic revelation here, from testing, is that a rider's agent position and his horse's agent position are identical - they're 0cm apart.
 
Ooh - it's a good night.

As I've commented elsewhere, unfortunately (set_show_messages) doesn't suppress all the onscreen messages I'd like it to.  If I'm secretly messing with your inventory and don't want you to know, you'll still get all sorts of "Lost 4 kettles of arsenic" or whatever, despite (set_show_messages, 0) being in place.

But I've found a poor man's version.  It takes advantage of the otherwise-annoying fact that if you overload the display_message queue, the first N or so (sixish?) messages simply don't appear at all.  Normally this is a problem, I use a lot of debug strings.  But check this out:
Code:
(operation_which_would_normally_generate_a_string_we_cannot_suppress),
(try_for_range, ":Index", 0, 20),(display_message, "str_empty_string"),(try_end),  	# Poor man's set_show_messages
Obviously, take care in where you use it.  You don't want strings you do want to see getting stuck in the queue with these guys.  But 20 is also certainly overkill; experiment to suit your palate.  Further testing would be necessary to establish how many unwanted strings you can suppress with one such cluster, if you've got a lot of them in a row.  I'm being conservative in my code, and putting them every (max) four or so unwanted outputs.

Shiny!
 
Recruiting prisoners fixed and updated.

Hmm... how about another version of the companion conversation where you can take money from them? I might have to do that...
 
Here is a way to avoid showing the tutorial question in your mod. Simply replace the "tutorial_question" menu entry with the following:

Code:
  (
    "tutorial_question",mnf_auto_enter,
    "Starting the game. Please wait.",
    "none",
    [
    ],
    [
      ("tutorial_question_no",[],"_",[(change_screen_return)]),
    ]
  ),

Programmatically,
Winter
 
I made some variants of the definition of wp(proficiency) in module_troops. Really simple stuff but it might come in handy. An example below:

Code:
def archer(x):
  n = 0
  r_1h = int( x * 0.75 )
  r_2h = int( x * 0.25 )
  r_pl = int( x * 0.50 )
  r_ar = int( x * 1.00 )
  r_cr = int( x * 0.50 )
  r_th = int( x * 0.25 )
  r = 10 + int(x/10)
  n |= wp_one_handed(r_1h + random.randrange(r))
  n |= wp_two_handed(r_2h + random.randrange(r))
  n |= wp_polearm(r_pl + random.randrange(r))
  n |= wp_archery(r_ar + random.randrange(r))
  n |= wp_crossbow(r_cr + random.randrange(r))
  n |= wp_throwing(r_th + random.randrange(r))
  return n

As you can see this definition will lower proficiency for all other weapons except bows. This can combined with a definition that calculates proficiency from character level (make a diagram in a spreadsheat if you want to see the resulting curve). Se below:

Code:
def lvl_to_skill( lvl ):
  return int( 30 * ( lvl * 7.8 / 10 ) ** 0.45 + lvl ** 1.1 )

Now its possible to write all sorts of "class"-definitions and use them like this:

Code:
  ["mercenary_crossbowman","mercenary_crossbowman","mercenary_crossbowmen",
   tf_guarantee_boots|tf_guarantee_armor|tf_guarantee_ranged,no_scene,0,fac_commoners,
   [blabla],
   def_attrib|level(14),crossbowman(lvl_to_skill(14)),
   knows_athletics_3|knows_ironflesh_3|knows_shield_1|knows_power_strike_1,
   swadian_face1,vaegir_face2],

 
Helle masters ,

I need an additional  script for the wandering townsfolk script. I need a script that will attach randomly troops  to the scene entries 10-11-12-13.
I will add 20 entries for the trrops.py. The script must get randomly from these 20 units I added.
 
Highlander:
Your formation script: I am thinking about using it to make a 'gun crew' stay together around an artillery piece: does it only work when the player equips the chosen weapon?  And, can I have more than one formation at any one time?
 
unfortunatelly the script doesn't work with ranged weapons/ riders,
the player doesn't need to use any weapon, you can use a trigger to start it as well. Several formations at any time are possable, you need to have a way to seperate the agents, though. (agent_get_troop_id should be helpfull there)
 
It works... sort of.
The 'formation' tends to be very loose and the 'gunner' troops don't really stay close to the 'gun' troop.   But they do follow it around at a distance.  I might tighten up the distance between units... 
If I get it working happily I'll post the variation up here.
this is my variation...


("guncrew_start",
[(agent_get_troop_id,reg(10),0),
(agent_get_position,1,reg(10)),
  (try_for_range,reg(20),1,100), #reset of the player dummy-slots
  (agent_set_slot,reg(10),reg(20),0),
  (try_end),
          (try_for_agents,reg(5)),
            (assign,reg(0),0),
            #guncrew
            (this_or_next|agent_get_troop_id,reg(5), "trp_vaegir_gunner"),
            #draught team
            (agent_get_troop_id,reg(5), "trp_swadian_gunhorse"),
            #If the Selected Troop is an Ally
            (agent_is_ally,reg(5)),
          (assign,reg(1),1),
          (try_begin),
            (assign,reg(20),1), #reg(20) is the agent number to use every position only once
            (agent_get_slot,reg(21),reg(10),reg(20)), #player agent used as a dummy not to make several agent use one position
            (eq,reg(21),0), #number isn't used, yet
            (assign,reg(1),reg(20)),
            (assign,reg(3),reg(1)),
            (val_mul,reg(3),100),
            (copy_position,reg(1),1),
            (position_move_x,reg(1),reg(3)), #number 1 = 100 metres right to the player agent 2= 200 etc...
            (agent_set_scripted_destination,reg(5),reg(1)), #tells the agent to run there
            (agent_set_slot,reg(5),1,reg(1)), #tells the agent which position he has - only important if you want to do somethink else with the script, it would work without it right now.
            (agent_set_slot,reg(10),reg(20),1), #tells the game that the number is used, by using the player slots.
          (else_try),
#very odd, sorry (the more call_scripts are there \/, the more agents will build a formation
(call_script,"script_formation_repeat"),(else_try),(call_script,"script_formation_repeat"),(else_try),(call_script,"script_formation_repeat"),(else_try),(call_script,"script_formation_repeat"),(else_try),
(call_script,"script_formation_repeat"),(else_try),(call_script,"script_formation_repeat"),
          (try_end),         
          (try_end),
  ]),
("formation_repeat",
[            (val_add,reg(20),1), # adds 1 to the agent number
            (agent_get_slot,reg(21),reg(10),reg(20)),
            (eq,reg(21),0), # again, is the number already used?
            (assign,reg(1),reg(20)), #just copying the number to use it somewhere else...
            (assign,reg(3),reg(1)), #again copying
            (assign,reg(4),0), #assigns the row of this agent to 0
#-#-#-#-#-#-#-#-#-#
            (try_for_range,reg(15),1,100), #this try-code assigns the right row.
            (assign,reg(17),reg(15)),
            (assign,reg(16),reg(15)),
            (val_mul,reg(15),2), #number of troops in one line
            (val_mul,reg(16),2), #has to be the same as above
            (val_add,reg(16),2), #has to be the same as above
            (gt,reg(3),reg(15)),
            (le,reg(3),reg(16)),
            (assign,reg(15),reg(17)),
            (val_mul,reg(15),3), #number of troops in one line + 1
            (val_sub,reg(3),reg(15)),
            (assign,reg(4),reg(17)),
            (try_end),
#delete these lines to get a 1 line formation
#-#-#-#-#-#-#-#-#-#
            (val_mul,reg(3),150),
            (val_mul,reg(4),200),
            (copy_position,reg(1),1),
            (position_move_x,reg(1),reg(3)),
            (position_move_y,reg(1),reg(4)),
            (agent_set_scripted_destination,reg(5),reg(1)),
            (agent_set_slot,reg(5),1,reg(1)),
            (agent_set_slot,reg(10),reg(20),1),
]),

#150 is the distance between the agents from right to left,
#200 is the distance between the agents from the front to background


#how to use:
#add [(ti_on_weapon_attack,[(call_script,"script_guncrew_start")])] to the gun-weapon that should cause the formation


 
INTEGER SQUARE ROOT:
returns the nearest integer value to square root of any positive integer.
Note that because of absence of 'while' loop I had to use recursion, so I'm not sure if it wont crash with very large numbers, it's up to you to find out.

usage: (call_script, "script_sqrt", value)                      Never set the second param!!!!
$return global variable yields the result.

MoBo syntax:
Code:
("sqrt",[#mobo#
   store_script_param_1 :var
   store_script_param_2 :flag
   {
       :flag == 0
       :var += 1
       :var /= 2
       $return = 0
       }
  {
      :var > $return
      :var -= $return
      $return += 1
      call_script "script_sqrt" :var 1
      }
   #mobo#
   ]),

Na(t)ive syntax:
Code:
("sqrt",[
      (store_script_param_1,':var'),
      (store_script_param_2,':flag'),
      (try_begin),
        (eq,':flag',0),
        (val_add,':var',1),
        (val_div,':var',2),
        (assign,'$return',0),
      (try_end),
      (try_begin),
        (gt,':var','$return'),
        (val_sub,':var','$return'),
        (val_add,'$return',1),
        (call_script,"script_sqrt",':var',1),
      (try_end),
   ]),

for those of you interested, C syntax :wink::
Code:
x = (x+1) >> 1;
for(r=0; x>r; x-=r++);
 
Leprechaun said:
EDIT: Recruiting prisoners

This code I know for a fact works like a beauty. I know it's easy to figure out, but I thought I'd share it to save you the bother of working it out for yourself.

EDIT again: I've improved it again.
This will now work much better. It stores the conversation troop's relation with the player, then generates a number between that number and good relations. The prisoner will join you if that random number is above 0, so the worse their relation with you, the wider the range for the random number, so the less likely it is to be between 0 and 3.

EDIT: It seems to be unstable. Best to leave it until I fix it.

EDIT once more: Wasn't the problem, but fisheye pointed out a slight niggle that I've now fixed. Should now work perfectly. Enjoy.

Code:
  [anyone|plyr,"prisoner_chat", [[store_conversation_troop,reg(2)],[store_troop_faction,reg(3),reg(2)],[store_relation,reg(4),reg(3),"fac_player_faction"]], "Don't try running away or trying something stupid. I will be watching you.", "prisoner_chat_2",
   [[store_random_in_range,reg(5),reg(4),4]]],
  [anyone,"prisoner_chat_2", [], "No, I swear I won't.", "prisoner_chat_3",[]],
  [anyone|plyr,"prisoner_chat_3", [], "Good. You're not stupid like the rest of these scum.", "prisoner_chat_4",[]],
  [anyone|plyr,"prisoner_chat_4", [], "[End the conversation]", "prisoner_chat_5",[]],
  [anyone|plyr,"prisoner_chat_5", [], "I hope you're looking forward to life as a slave, scum.", "close_window",[]],
  [anyone|plyr,"prisoner_chat_4", [], "[Try to recruit the prisoner]", "prisoner_chat_6",[]],
  [anyone|plyr,"prisoner_chat_6", [], "Listen, scum, you have one last chance to redeem yourself before I sell you to the slave-traders.\
  Drop all your previous allegiances and swear to fight for me and obey my every order to the letter, and you'll be paid, fed and equipped.\
   If you don't....well, let's just say that life as a slave will be seemingly unending years of agony, malnutrition and beatings.\
   I'd advise you to think very, very carefully before refusing.", "prisoner_chat_7",[]],
  [anyone,"prisoner_chat_7", [[lt,reg(5),0]], "I'll show you what I think of your offer! (The prisoner spits at your feet) There. Now get lost, I'm not interested.", "prisoner_chat_8",[]],
  [anyone|plyr,"prisoner_chat_8", [], "[Kill the prisoner]", "prisoner_chat_14",[]],
  [anyone|plyr,"prisoner_chat_8", [], "[Sell the prisoner]", "prisoner_chat_10",[]],
  [anyone|plyr,"prisoner_chat_9", [], "(You advance on the prisoner with your weapon drawn)", "prisoner_chat_11",[]],
  [anyone|plyr,"prisoner_chat_4", [], "[Kill the prisoner]", "prisoner_chat_9",[]],
  [anyone|plyr,"prisoner_chat_10", [], "We have found a roaming slave trader. It's time for us to part ways. Goodbye, it was nice not to know you.", "close_window",
   [[store_conversation_troop,reg(2)],[remove_troops_from_prisoners,reg(2),1],[troop_add_gold,"trp_player",20]]],
  [anyone,"prisoner_chat_11", [], "Please, {sir/madam}, don't kill me. I am a defenceless prisoner. Surely you're not that cruel?", "prisoner_chat_12",[]],
  [anyone|plyr,"prisoner_chat_12", [], "[Kill the prisoner]", "prisoner_chat_13",[]],
  [anyone|plyr,"prisoner_chat_13", [], "Yes, idiot. I am that cruel. Prepare to die.", "prisoner_chat_14",[]],
  [anyone|plyr,"prisoner_chat_14", [], "(The prisoner struggles against his shackles, desperate to free himself and escape you, but to no avail. You slit his throat with a knife and watch, satisfied, as his corpse sags to the floor.)", "close_window",
   [[store_conversation_troop,reg(2)],[remove_troops_from_prisoners,reg(2),1]]],
  [anyone|plyr,"prisoner_chat_12", [], "[Let him live]", "prisoner_chat_15",[]],
  [anyone|plyr,"prisoner_chat_15", [], "Very well, I'll let you live. I hope you enjoy life as a slave.", "prisoner_chat_16",[]],
  [anyone,"prisoner_chat_16", [], "Oh, thank you, {sir/madam}.", "close_window",[]],
  [anyone,"prisoner_chat_7", [[ge,reg(5),0]], "Thank you for your mercy, {sir/madam}. I promise to drop all allegiances to serve you in any way I can.","prisoner_chat_17",[]],
  [anyone|plyr,"prisoner_chat_17", [], "Good. Now, I'll make the rules clear to you. There will be no discrimination against other soldiers in my army because they were once on the opposing side or were bandits. They fight for me now, as do you. Their ties with the past are broken. Think you can handle it?", "prisoner_chat_18",[]],
  [anyone,"prisoner_chat_18", [], "Yes, {sir/madam}. I will obey the rules.", "prisoner_chat_19",[]],
  [anyone|plyr, "prisoner_chat_19", [[neg|hero_can_join]],"Oh. There isn't enough room in my party for you. I will be back when I have made room.", "close_window",
   [[store_conversation_troop,reg(0)],[troop_set_slot,reg(0),slot_prisoner_agreed,1]]],
  [anyone|plyr, "prisoner_chat_19", [[hero_can_join]], "Excellent. From now on, you fight alongside these men. Help them when they need it, and they'll do the same for you. Get equipped and fed. We'll head out as soon as you're done.", "close_window",
   [[store_conversation_troop,reg(0)],[troop_join,reg(0)],[remove_troops_from_prisoners,reg(0),1]]],
  [anyone|plyr,"prisoner_chat", [[store_conversation_troop,reg(0)],[troop_get_slot,reg(1),reg(0),slot_prisoner_agreed],[eq,reg(1),1],[hero_can_join]], "All right, I have made room for you to join my party. Welcome aboard.", "close_window",
   [[store_conversation_troop,reg(0)],[troop_join,reg(0)],[remove_troops_from_prisoners,reg(0),1]]],
  [anyone|plyr,"prisoner_chat", [[store_conversation_troop,reg(0)],[troop_get_slot,reg(1),reg(0),slot_prisoner_agreed],[neg|reg(1),1],[eq,hero_can_join]], "I am sorry, I still have no room for you. You'll have to wait a while longer, I'm afraid.", "close_window",[]],
Where am I suppose to put this?(or any of these codes for that matter)
 
The syntax for each piece of the module system is subtly different, so if you read through the official modsys docs then even where the author hasn't labeled which bits go where, you can unambiguously figure out where a given code sequence should go.

For instance, the one you quoted consists of tuples in [square brackets] with a troop name or 'anyone' at the beginning, sometimes followed by |plyr... that means they're dialogues and go in module_dialogs.py.  See the modsys documentation dealing with module_dialogs to figure out where in the file they should go.

Many others (the majority) here are scripts, recognizable by their top-level structure of ("script_name", [one big set of commands and operations within a single top-level set of square brackets]).  And so forth.

I hate to say it, but if you're having trouble recognizing where one of these schemes should go, you'll probably have a lot of other problems incorporating it into your mod, since the same kind of understanding will be necessary when debugging them.  (And if you think anything at all on this list is completely plug-and-play, think again.)  So you probably need to go back to the tutorials and docs until you're more than comfortable with recognizing the various kinds of code.
 
Back
Top Bottom