-
Newbie scripting question [LevyScript]
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.
-
Re: Newbie scripting question [LevyScript]
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.
-
Re: Newbie scripting question [LevyScript]
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. :)
-
Re: Newbie scripting question [LevyScript]
-
Re: Newbie scripting question [LevyScript]
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.
-
Re: Newbie scripting question [LevyScript]
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?
-
Re: Newbie scripting question [LevyScript]
Yeah it's a minor problem. You can only use the settlement level via building exists
-
Re: Newbie scripting question [LevyScript]
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.
-
Re: Newbie scripting question [LevyScript]
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?
-
Re: Newbie scripting question [LevyScript]
Can't you check the building health, instead?
-
Re: Newbie scripting question [LevyScript]
I can't find that command. Is it check_building_health, or some such?
-
Re: Newbie scripting question [LevyScript]
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.
Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; 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?
-
Re: Newbie scripting question [LevyScript]
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:
Code:
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:
Code:
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:
Code:
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.
-
Re: Newbie scripting question [LevyScript]
maybe I'm just being obtuse, but I added;
Code:
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?
-
Re: Newbie scripting question [LevyScript]
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:
Code:
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
-
Re: Newbie scripting question [LevyScript]
I'm working with a clean (vanilla) campaign script file and its still not firing, any clues?
-
Re: Newbie scripting question [LevyScript]
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.
-
Re: Newbie scripting question [LevyScript]
First of all: Enable error logging. To do this you have to add the following to your config:
Code:
[log]
to = logs/m2.log.txt
level = * error
As for your monitor: iirc console_command doesn't use a comma
-
Re: Newbie scripting question [LevyScript]
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
-
Re: Newbie scripting question [LevyScript]
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.
-
Re: Newbie scripting question [LevyScript]
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.
-
Re: Newbie scripting question [LevyScript]
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?
-
Re: Newbie scripting question [LevyScript]
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.
-
Re: Newbie scripting question [LevyScript]
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. :)
-
Re: Newbie scripting question [LevyScript]
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;
Code:
;;;;; 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).
-
Re: Newbie scripting question [LevyScript]
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?
Code:
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
-
Re: Newbie scripting question [LevyScript]
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:
Code:
;=============================================================
;= 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?
-
Re: Newbie scripting question [LevyScript]
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
-
Re: Newbie scripting question [LevyScript]
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).
-
Re: Newbie scripting question [LevyScript]
monitor_event BuildingDestroyed TrueCondition
console_command add_money 40000
end_monitor
Works fine for me...