Log in

View Full Version : Newbie scripting question [LevyScript]



Alex Drake
04-18-2007, 09:22
I'm a fairly experienced coder whose just recently gotten into MT2TW modding and the first thing I've decided to try and cook up is a Levy Script (sortof a counterpoint to Klink's garrison script. The main roadblock I'm running into is a lack of understanding of how to get at certain info thats necessary for making the script even operate.

I've provided the pseudocode below in the hoped of getting some feedback as to which functions and grammar I'll need to accomplish the following;

;::::Start Test Levy Script;::::
declare_counter muster_isbuilt
set_counter muster_isbuilt 0
;;;;;[[Pseudo Code in question]]
;::::Check if Muster_call [building] is built in a particular settlement [London]
;::::if yes [muster_isbuilt == 1], get the settlement's population [int]
;:::: get settlement's tech level (wall) [int]
;:::: get info on settlement building types [bool + int]
;:::: get info on settlement governor [bool + int]
;:::: ??Get info on neighboring settlements?? [bool + int]
;:::: Spawn units based on this info

;::::Example Usage:
;::::[if Building A exists in London]
;:::: [settlement_pop = Settlement_pop London]
;:::: [if Building B exists in London]
;:::: [B_exists = 1]
;:::: [if Governor Z has trait1]
;:::: [Governor_has_trait1 = 1]
;:::: [if neighboring region [Sherwood] belongs to player1]
;:::: [neighboring_pop = Settlement_pop Sherwood]
;::::[London_muster_score = settlement_pop/1000 + (B_exists * 100) + (Governor_has_trait1 * 100) + (neighboring_pop/2000)
;::::[if London_muster_score > 0 && < 5000]
;:::: [create_unit London, Peasants, num 4, exp 0, arm 0, wep 0]
;::::
;::::

If anyone has any ideas as to which functions I cna use to get even a bit of this done, as well as some syntax examples, I'd be eternally greatful.

alpaca
04-18-2007, 09:37
Haha yes, you'll be frustrated by M2 scripting before long :laugh4:
Basically, something like you want to do is much, much more difficult than you'd expect with M2 scripts. Some of the problems:
- no real variables
- no counter-to-counter or counter-to-exposed comparison (only counter-to-constant)
- no functions, only commands and conditions
- conditions are boolean only
- most conditions have a game object as trigger requirements and thus only work in monitor_event
- no maths (well except for adding and subtracting and even then, only counter + constant)

Doing what you want to do requires a lot of creative thinking and compromises. I'm working on something similar and it's really hard to factor in a lot of the game state because you can't use variables.
You can design a few workarounds by using while loops, gigantic if-lists and such, but it's really not pretty.

Alex Drake
04-18-2007, 17:51
Well, I don't mind doing a lengthy workaround, I'm mainly concerned with whether its possible to get the info I want. For instance, Klink' script does something like this;

declare_ counter Dongola_lev
set_counter Dongola_lev 1
monitor_event SettlementTurnStart SettlementName Dongola
and SettlementBuildingExists = wooden_wall
set_counter Dongola_lev 2
end_monitor

Basically, where can I find a list of conditions that I can use in monitor events like SettlementBuildingExists?

I'm not afraid of fat if lists. :)

wilddog
04-18-2007, 18:06
you need to refer to the Docudemon files v 1.2

https://forums.totalwar.org/vb/showthread.php?t=83119

SigniferOne
04-18-2007, 18:16
Check the Mod Chat forum, and download Caliban's Docudemon files v1.2.

The main problem in regards to your script is that you need to check for all your conditionals all at once. In other words, you can't be like:

if building_A exists
...if building_B exists
......do something
...end_if
...if building_C exists
......do something else
...end_if
end_if

The reason for that is that almost all conditionals consume outside data, data supplied by EVENTS. If you read the events document in docudemons, you will see. Events, such as SettlementTurnStart, supply all of the content that the conditionals then consume and use. There is no:

if building_A exists in settlement ABC. It's only

if building_A exists.

How is the script going to know which settlement? Only by using this conditional in conjunction with an event that supplies the settlement name.

That's the basic framework that you have to work around. It's far from a full-fledged scripting language. Now given this limitation, you can still create some rather sophisticated programs. And also let me point out that a few conditionals don't need to be supplied with an event's data, they are entirely stand-alone. In the conditions file, you will see those conditions as ones having the I_ prefix, e.g. I_SettlementExists. Ideally, all of conditionals should have I_ formats, (poke Caliban), as that would untie our hands in unbelievable ways. But for now, we have to be satisfied with those that already exist. Hopefully I still haven't detracted you from your project, because you can still do it, just need to work with the system here. Your hands become untied if you use counters (proto-variables) cleverly, if you use if() and while() commands in clever ways.

Alex Drake
04-18-2007, 18:17
Yup, been skimming them since last night. One thing I've noticed that doesn't exist is anyway to find out the actual population of a settlement, I've found equivalents for nearly everything else. Any clues?

alpaca
04-18-2007, 22:28
Yeah it's a minor problem. You can only use the settlement level via building exists

Alex Drake
04-18-2007, 23:32
Is there a method by which one can destroy a particular building? I want the muster call "building" to be destroyed the turn after it's created.

Alex Drake
04-19-2007, 00:39
An addendum, I've found the following;

set_building_health London muster_call 0

However, would this prevent the following monitor from triggering (since the building is built, its just broken):

declare_counter muster_isbuilt
set_counter muster_isbuilt 0
monitor_event SettlementTurnStart SettlementName London
and SettlementBuildingExists = muster_call
set_counter muster_isbuilt 1
set_building_health London muster_call 0
end_monitor

ie; does repairing also qualify as building?

SigniferOne
04-19-2007, 00:44
Can't you check the building health, instead?

Alex Drake
04-19-2007, 00:53
I can't find that command. Is it check_building_health, or some such?

Alex Drake
04-19-2007, 02:12
ok, so this is what I have so far, the top bits are something of a rip of Klink's garrison code as near as I could understand what he was doing, the rest are just a couple of test conditions for the english at London.



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; Declare some local variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
declare_counter Facnr
set_counter Facnr 1
declare_counter Facstart
set_counter Facstart 1

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; Figure out if this is the player's turn and what faction he belongs to
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
monitor_event FactionTurnStart FactionIsLocal
set_counter Facstart 1
if I_LocalFaction england
set_counter Facnr 5
end_if
end_monitor

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; Find out if this is player's turn or AI's turn
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
monitor_event FactionTurnEnd FactionIsLocal
set_counter Facstart 0
end_monitor

if I_CompareCounter Facstart = 1
set_counter Grrson 0
end_if

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; Find out if the settlement belongs to this faction
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
if I_CompareCounter Facnr = 5
and I_SettlementOwner London = england
set_counter Grrson 1
end_if

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; Check if player has built the Muster Call structure and spawn an army if they have.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
if I_CompareCounter muster_isbuilt = 1
and I_CompareCounter London_lev = 1
and I_SettlementOwner London = england
and I_CompareCounter Grrson = 1
create_unit London, Peasants, num 4, exp 0, arm 0, wep 0
console_command add_money england, -400
end_if
if I_CompareCounter muster_isbuilt = 1
and I_CompareCounter London_lev = 1
and I_SettlementOwner London = england
and I_CompareCounter Grrson = 1
and I_CompareCounter gov_chivalry = 1
create_unit London, Peasants, num 8, exp 0, arm 0, wep 0
console_command add_money england, -800
end_if

;;;;; Monitor events to check if player built muster call
declare_counter muster_isbuilt
set_counter muster_isbuilt 0
monitor_event SettlementTurnStart SettlementName London
and SettlementBuildingExists = muster_call
set_counter muster_isbuilt 1
set_building_health London muster_call 0
end_monitor

;;;;;
declare_counter London_lev
set_counter London_lev 0
monitor_event SettlementTurnStart SettlementName London
and SettlementBuildingExists = wooden_walls
set_counter London_lev 1
end_monitor

declare_counter gov_chivalry
monitor_event SettlementTurnStart SettlementName London
and GovernorAttribute Chivalry >= 1
set_counter gov_chivalry 1
end_monitor


Do you think this could work?

SigniferOne
04-19-2007, 03:33
The code is abit confusing because you have events interspersed throughout the code. Try to always keep them at the top, for readability's sake. Also I don't know if you can use 'and' clauses in if statements, but I suppose it's possible. Even if not, can use nested 'if' instead, same thing.

Alright I think I get what you're trying to do in your script: have a building called 'legion', you build that building and a full legion instantly pops up, is that right?

Secondly remember, those if_statements will not be triggered once the variable acquires a new value. They will be run through once at the beginning of the script (not beginning of turn), and that's it. A better option is to use 'while' to spin loops until a variable is set as you want it.

Third, these three clauses do unnecessary work:


monitor_event FactionTurnStart FactionIsLocal
set_counter Facstart 1
if I_LocalFaction england
set_counter Facnr 5
end_if
end_monitor

---------------------------------------------------

monitor_event FactionTurnEnd FactionIsLocal
set_counter Facstart 0
end_monitor

if I_CompareCounter Facstart = 1
set_counter Grrson 0
end_if

---------------------------------------------------

if I_CompareCounter Facnr = 5
and I_SettlementOwner London = england
set_counter Grrson 1
end_if

Think of it like a state machine. You basically want to get to the state where you can say "Grrson = 1", only if local faction, england, and owning London. Simply replace the above code with:


monitor_event FactionTurnStart FactionIsLocal
and LocalFaction england

if I_SettlementOwner London = england
set_counter Grrson 1
end_if

end_monitor

Then, even better than spinning wheels with while(), just do:


monitor_condition EventCounter Grrson > 1 TrueCondition
; check counters for settings, and build an army accordingly

set_counter Grrson = 0
end_monitor


NB. I got the syntax for that monitor_condition off the top of my head, might want to tinker with the syntax to make it execute, but I think it's mostly right.

Alex Drake
04-19-2007, 03:58
maybe I'm just being obtuse, but I added;


monitor_event FactionTurnStart FactionIsLocal
and LocalFaction england

if I_SettlementOwner London = england
set_counter Grrson 1
console_command add_money england, 50000
end_if
end_monitor


to check if the event was ever triggering, but it seems that it never does. Can't for the life of me figure out why (I always start a new game with england and let a turn lapse).

Is there anyway to print directly to the command line just to see if the even is ever firing?

SigniferOne
04-19-2007, 04:06
console_command puppify_my_love (the command was created for that very purpose, as far as I know, to test if things fire).

Oh, the reason your code isn't firing is because FactionIsLocal is a condition, not an event. Change to:



monitor_event FactionTurnStart FactionType england
and FactionIsLocal TrueCondition

if I_SettlementOwner London = england
set_counter Grrson 1
console_command add_money england, 50000
end_if
end_monitor

Alex Drake
04-19-2007, 06:10
I'm working with a clean (vanilla) campaign script file and its still not firing, any clues?

SigniferOne
04-19-2007, 06:17
Apply typical code debugging tools :) delete some of the clauses, see what happens. Remove local facition thingy clause in event trigger, have it fire, then find the right syntax to add it back in. I'm 99% certain the faction clause syntax is correct.

alpaca
04-19-2007, 16:26
First of all: Enable error logging. To do this you have to add the following to your config:

[log]
to = logs/m2.log.txt
level = * error

As for your monitor: iirc console_command doesn't use a comma

wilddog
04-19-2007, 17:44
if you don't find a debug error make sure you included the wait_monitors. I hit that one before and took me ages to realise.

;
; Campaign script
;
script


declare_counter Grrson 1

monitor_event FactionTurnStart FactionType england
and FactionIsLocal TrueCondition

if I_SettlementOwner London = england
set_counter Grrson 1
console_command add_money england, 50000
end_if
end_monitor


wait_monitors


end_script

Alex Drake
04-19-2007, 18:14
Awrighty, i got it spawning peasants when I build the Muster Call structure, then dropping the structure's health to 0, unfortunately it continues spawning peasants as long as the building exists. Is there a way to check the building's health?

ie; check_building_health muster_call 0

or some such.

SigniferOne
04-19-2007, 18:26
Hold up everyone. I think you can still destroy a structure, despite the fact that there is no command to destroy it. I just realized that a player can destroy the structure, by clicking the 'destroy' button, and there is a way to invoke GUI buttons from inside the script!

I don't have time this instant to follow this idea through, but I think it could definitely be possible.

Alex Drake
04-19-2007, 20:54
Right, I remember the EB team did something like this way back during the development of .7 for RTW, of course, I have no idea how, but it definitely seems possible.

Back to my earlier question;

The monitor_event I'm using just checks if the buildign exists within the settlement, is there a way to check if it was built/repaired in the last turn instead?

alpaca
04-19-2007, 22:36
Well if you ask me, the best way to do this would be reverting the process (as there's no destroy_building command and you can't check building health):
You can trigger your script off the destruction of your building, and the player can build it again at a later time (actually I'll create the building by script after a cooldown period). The problem with that is of course that the player can kind of "store" his army because he can build it at any time and destroy it in an instant.
As for pressing buttons: This is very unstable code in general. I'm also not sure if you can actually choose a building to open the building scroll (which you'd have to do if you want to destroy it), I think I tried that back in the RTW times and couldn't get it to work.

Alex Drake
04-19-2007, 23:18
Hmm, is there anyway to count the number of turns a building has existed then?

If so, one thing you could do is have the building itself have a huge upkeep every turn its in play, then base the troop spawn on how long you've had the building before you destroy it. Players would be encouraged to only build them when necessary, since the cost of mustering a huge army then keeping it over time would be huge, and you don't have the benefit of being able to individually reshuffle/disband units as you would with an army of individual units.

Taking it a step further, The building might also add to unrest/squalor, or have some other negative penalties that force players to be fairly pragmatic about using them.

But I digress, I have no idea which conditions would work for such a thing. :)

Alex Drake
04-19-2007, 23:38
So I've had a go at the new condition, but something is still eluding me. I'm trying to have it check if a player has destroyed the building during their turn, to do this, I'm using the following events;




;;;;; Check if Muster call exists (at the beginning of the turn)
set_counter muster_exists 0
monitor_event SettlementTurnStart SettlementName London
and SettlementBuildingExists = muster_call
set_counter muster_exists 1
end_monitor

;;;;; Check of muster Call does not exist (at the end of the turn)
declare_counter muster_dne
set_counter muster_dne 0
monitor_event SettlementTurnEnd SettlementName London
and not SettlementBuildingExists = muster_call
set_counter muster_dne 1
end_monitor

;;;;; Check if Muster Call was destroyed between the start and end of the player's turn
monitor_event SettlementTurnStart SettlementName London
if I_CompareCounter muster_exists = 1
and I_CompareCounter muster_dne = 1
console_command puppify_my_love
end_if
end_monitor


I know the first monitor_event works correctly, the problem is that the second doesn't appear to be firing correctly, so as a result, I'm getting puppies when I shouldn't be getting 'em (cute as they are).

Alex Drake
04-20-2007, 00:06
Damn th elack of an edit feature;

I tried a different, equally unsuccessful vector of attack, this time no puppies. Am I misunderstanding how the BuildingDestroyed flag works?



declare_counter muster_destroyed
set_counter muster_destroyed 0
monitor_event SettlementTurnEnd SettlementName London
and BuildingDestroyed = muster_call
set_counter muster_destroyed 1
end_monitor

monitor_event FactionTurnEnd FactionType england
and I_LocalFaction england
if I_CompareCounter muster_destroyed 1
console_command puppify_my_love
end_monitor

Alex Drake
04-20-2007, 08:50
PROGRESS!

I've gotten the basic functions of th elevy script working, as of right now, you build a muster_call building, then demolish it and you get units! I'm currently coming up with a bonus/penalty scheme to make it all nice n' balanced, but here's the campaign script code as of right now:



;=============================================================
;= O Levy Script by Alex Drake =
;=============================================================

;=============================================================
; Declare some local variables
;=============================================================
declare_counter Facnr
set_counter Facnr 1
declare_counter Facstart
set_counter Facstart 0
declare_counter Grrson
set_counter Grrson 0

;=============================================================
; Figure out if this is the player's turn and what faction he belongs to
;=============================================================


monitor_event FactionTurnStart FactionType england
and FactionIsLocal TrueCondition
;console_command puppify_my_love
if I_SettlementOwner London = england
set_counter Facstart 1
set_counter Facnr 5
set_counter Grrson 1
;console_command add_money england, 50000
end_if
end_monitor

;=============================================================
;
;=============================================================

declare_counter muster_exists
set_counter muster_exists 0
monitor_event SettlementTurnStart SettlementName London
and SettlementBuildingExists = muster_call
set_counter muster_exists 1

end_monitor

declare_counter muster_dne
set_counter muster_dne 0
monitor_event SettlementTurnEnd SettlementName London
and not SettlementBuildingExists = muster_call

set_counter muster_dne 1
end_monitor

declare_counter London_lev
set_counter London_lev 0
monitor_event SettlementTurnStart SettlementName London
and SettlementBuildingExists = wooden_wall
set_counter London_lev 1
end_monitor

declare_counter gov_chivalry
monitor_event SettlementTurnStart SettlementName London
and GovernorAttribute Chivalry >= 1
set_counter gov_chivalry 1
end_monitor

;=============================================================
;;;;; Monitor events to check if player built muster call
;=============================================================

declare_counter muster_destroyed
set_counter muster_destroyed 0
monitor_event SettlementTurnEnd SettlementName London
and not SettlementBuildingExists = muster_call
set_counter muster_destroyed 1
;console_command puppify_my_love
end_monitor

monitor_event FactionTurnEnd FactionType england
and I_LocalFaction england
if I_CompareCounter muster_destroyed 1
;console_command puppify_my_love
console_command add_money england, 50000
end_if
end_monitor



monitor_event FactionTurnEnd FactionType england
and I_LocalFaction england
if I_CompareCounter muster_exists = 1
and I_CompareCounter muster_dne = 1
create_unit London, Peasants, num 4, exp 1, arm 0, wep 0
create_unit London, Town Militia, num 2, exp 6, arm 0, wep 0
if I_CompareCounter gov_chivalry = 1
create_unit London, Spear_Militia, num 1, exp 2, arm 0, wep 0
end_if
;console_command puppify_my_love
end_if
end_monitor


suggestions?

alpaca
04-20-2007, 13:14
1) Use the BuildingDestroyed event. Much more useful and works instantly :2thumbsup:
2) Keep in mind that buildings often can't give negative bonuses. I think for example that income_bonus doesn't work with negative numbers

Alex Drake
04-20-2007, 17:18
Whenever I use BuildingDestroyed, the entire statement fails to trigger. From the research I did, it seems building destroyed only works with event triggers, not monitor events in the campaign_script file (ie; when you destroy a church, you lose favor with the pope. This is an actual event).

As for negative bonuses, I was just going to do it in the campaign_script file itself, then include the info in the building description. (ie; check for the number of these buildings the player has, then subtract money based on that).

alpaca
04-20-2007, 17:46
monitor_event BuildingDestroyed TrueCondition
console_command add_money 40000
end_monitor

Works fine for me...

Alex Drake
04-23-2007, 20:30
alpaca:

when I try:

monitor_event SettlementTurnStart SettlementName London
and BuildingDestroyed = emergency_call
console_command puppify_my_love
console_command add_money england, 40000
end_monitor

I get puppies immediately whenever I start a new game, what sort of syntax can one put in the TrueCondition block? (Ie; I need to check for a specific building getting destroyed during a particular settlement's turn.

Also, is there a way to check the existence of a particular level of a building in a monitor event? I have the following buildings;

[General]
emergency_call
Level 1: Emergency Levy (City) - emergency_levy

castle_emergency_call
Level 1: Emergency Muster (Castle) - emergency_muster

[Militia Muster] (City)

city_muster
Level 1: Auxiliary Levy - auxiliary_levy
Level 2: General Levy - general_levy
Level 3: War Levy - war_levy

[Army Muster] (Castle)

castle_muster
Level 1: Auxiliary Muster - auxiliary_muster
Level 2: General Muster - general_muster
Level 3: Expeditionary Muster - expeditionary_muster
Level 4: War Muster - war_muster

But for some reason, when I do something like:

declare_counter muster_exists
set_counter muster_exists 0
monitor_event SettlementTurnStart SettlementName London
and SettlementBuildingExists = emergency_call
set_counter muster_exists 1
;console_command puppify_my_love
end_monitor

it works, whereas when I do this

declare_counter muster_exists
set_counter muster_exists 0
monitor_event SettlementTurnStart SettlementName London
and SettlementBuildingExists = emergency_levy
set_counter muster_exists 1
;console_command puppify_my_love
end_monitor

it never fires.

Am I using SettlementBuildingExists incorrectly (ie; can it only get the base building class and not building level), or is there another condition I can use instead?

Alex Drake
04-24-2007, 04:27
Nevermind, I figured it out!



monitor_event BuildingDestroyed BuildingName = war_levy
;;;;;Do some stuff
end_monitor


It works like a charm, now I just have to get the rest of the buildings together!

alpaca
04-24-2007, 14:09
Yeah the docudemon example for SettlementBuildingExists is a bit faulty.
You have to use

declare_counter muster_exists
set_counter muster_exists 0
monitor_event SettlementTurnStart SettlementName London
and SettlementBuildingExists emergency_call = emergency_level
set_counter muster_exists 1
;console_command puppify_my_love
end_monitor
As logic tokens you can use >, <, =, a combination of these and !=