Map Database  •  FAQ  •  RSS  •  Login

Dynamic Script Usage Questions

<<

Ben

User avatar

Former Site Admin

Posts: 3814

Joined: 08 Jan 2009, 23:00

Location: California - Pacific Time (UTC -8/-7 Summer Time)

Post 14 Apr 2013, 18:08

Re: Dynamic Script Usage

I guess that my question got lost:
One more thing: My tower-limiting script doesn't actually work because players can still place as many tower plans as they want before the towers are actually built. However ,there is no state that detects how many houseplans are laid out, so I can't figure out a way around this...
Is there a way to fix this?
I used to spam this forum so much...
<<

Siegfried

User avatar

Knight

Posts: 494

Joined: 24 Jul 2009, 22:00

Post 14 Apr 2013, 19:29

Re: Dynamic Script Usage

It would be cool to support classes, maybe one day ;) Right now we haven't seen any suggestions that really need classes... but as people's ideas get more and more complicated maybe we will.
Mhm ... I will write you an email ;)
<<

Krom

User avatar

Knights Province Developer

Posts: 3280

Joined: 09 May 2006, 22:00

KaM Skill Level: Fair

Location: Russia

Post 15 Apr 2013, 04:58

Re: Dynamic Script Usage

I guess that my question got lost:
One more thing: My tower-limiting script doesn't actually work because players can still place as many tower plans as they want before the towers are actually built. However ,there is no state that detects how many houseplans are laid out, so I can't figure out a way around this...
Is there a way to fix this?
Added to our todo list. Hopefully it will be included in next RC version.
Knights Province at: http://www.knightsprovince.com
KaM Remake at: http://www.kamremake.com
Original MBWR/WR2/AFC/FVR tools at: http://krom.reveur.de
<<

Tef

User avatar

Lance Carrier

Posts: 64

Joined: 15 Apr 2013, 15:12

KaM Skill Level: Skilled

Post 15 Apr 2013, 16:10

Re: Dynamic Script Usage

Another one: ObjectInArea. Whether it is a unit or building, I'd like to be able to have a state that checks if something is in a certain spot.
(By Ben from the ideas topic)

I was also interested in this and made a script that checks the presence of any (of your own) military units in a defined square. Currently, this script is only usable for one player (single player) but could easily be extended. Just plop this function on top of your script
  Code:
function MilitaryUnitZoneTrigger(left: Integer; top: Integer; right: Integer; bottom: Integer): Boolean; var // define local constants ACTIVE_PLAYER,LOW_UNIT_NR,HIGH_UNIT_NR: Integer; // define local variables MilitaryUnitsAlive, MilitaryUnitsCounted, UnitsCounter, MilitaryType: Integer; begin // initialize local constants ACTIVE_PLAYER := 0; LOW_UNIT_NR := 14; // from militia up to... HIGH_UNIT_NR := 27; // ...vagabond //initialize local variables MilitaryUnitsAlive := 0; MilitaryUnitsCounted := 0; // calculate the number of living soldiers for human player for MilitaryType := LOW_UNIT_NR to HIGH_UNIT_NR do begin MilitaryUnitsAlive := MilitaryUnitsAlive + States.StatUnitTypeCount(ACTIVE_PLAYER,MilitaryType); end; for UnitsCounter := 0 to 999 do begin //escape loop if the number of evaluated units equals all units if MilitaryUnitsCounted = MilitaryUnitsAlive then break; // only add as counted unit if it is military and from human player // otherwise, go to next iteration if (States.UnitOwner(UnitsCounter) = ACTIVE_PLAYER) AND (States.UnitType(UnitsCounter) >= LOW_UNIT_NR) AND (States.UnitType(UnitsCounter) <= HIGH_UNIT_NR) then MilitaryUnitsCounted := MilitaryUnitsCounted + 1 else continue; // if unit is human and military, perform this check: // if unit is in zone (left, top, right, bottom) then return TRUE if (States.UnitPositionX(UnitsCounter) >= left) AND (States.UnitPositionX(UnitsCounter) <= right) AND (States.UnitPositionY(UnitsCounter) >= top) AND (States.UnitPositionY(UnitsCounter) <= bottom) then Result := true; end; end;
So far the function that checks for soldiers. To actually use this function, let's say once every second for soldiers you could use the script below. It also makes something happen once if a military unit has been found. Note that the zone coordinates as left-top-right-bottom are 10-15-20-25:
  Code:
var MilitaryUnitHasEnteredZone: boolean; procedure OnTick; begin if (States.GameTime mod 10 = 0) AND (MilitaryUnitHasEnteredZone = false) then begin if MilitaryUnitZoneTrigger(10,15,20,25) then begin action 1 action 2 action n MilitaryUnitHasEnteredZone := true; end; end; end;
I'm sure this script can be made better / more efficient. I welcome any improvements!
<<

Lewin

User avatar

KaM Remake Developer

Posts: 3822

Joined: 16 Sep 2007, 22:00

KaM Skill Level: Skilled

ICQ: 269127056

Website: http://lewin.hodgman.id.au

Yahoo Messenger: lewinlewinhodgman

Location: Australia

Post 15 Apr 2013, 16:47

Re: Dynamic Script Usage

@Tef: It's great that you wrote that script, we're happy to see people writing and sharing useful procedures/functions. However there are some issues with it. It's bad practice to rely on something like:
for UnitsCounter := 0 to 999 do begin
It's true that unit/house IDs are assigned sequentially, but it's quite likely that there will be more than 999 in big games, since IDs are never reused so every new unit or house that is created takes up another ID. Sure you could increase it to 9999 or 99999 but that will start to become noticeable performance wise.

We realise that there is currently no correct way to gain access to all of the players units/houses/groups, which is something that you may want to do in certain scripts such as yours. Because of this we are planned to add functions which give you an array of IDs of every unit which is alive. It could have a parameter for player (-1 for any player) and another which is a set of what unit types you are interested in (which is good because we need to introduce people about the beauty of sets in Pascal somehow :P). We'd also make versions of these for houses/groups. So that part of your script could change to something like this:
  Code:
var AllTheUnits: array of Integer; ... MilitaryUnits := States.GetUnits(ACTIVE_PLAYER, [14..27]); for I := 0 to Length(MilitaryUnits)-1 do begin if (States.UnitPositionX(MilitaryUnits[I]) >= left) AND ...
Note that this is just the plan and it might change, but we certainly want to provide an alternative to iterating from 0 to 999 and hoping the game doesn't last long enough to exceed 999 units/houses created.

I also have some other feedback which might help you:
You can replace "(MilitaryUnitHasEnteredZone = false)" with "not MilitaryUnitHasEnteredZone". If you're more comfortable with the first version there's nothing wrong with that, I just wanted to let you know that both options exist.

Constants: Use "const" instead of "var", like this:
  Code:
function MilitaryUnitZoneTrigger(left: Integer; top: Integer; right: Integer; bottom: Integer): Boolean; const // define local constants ACTIVE_PLAYER = 0; LOW_UNIT_NR = 14; HIGH_UNIT_NR = 27; // define local variables var MilitaryUnitsAlive, MilitaryUnitsCounted, UnitsCounter, MilitaryType: Integer; begin
There's also another way you could implement your script Run States.UnitAt() on every tile within the defined area, using for loops like this:
  Code:
for X:=Left to Right do for Y:=Top to Bottom do begin U := States.UnitAt(X,Y); ... end;
This will work quite well for small areas, that's how I did the king of the hill style maps. States.UnitAt is actually quite efficient because we have a reverse lookup to tell us which unit is standing on a certain tile (so it doesn't need to check whether every single unit in the game to see if it's standing there) On large areas the performance will be pretty bad because the number of tiles to be checked goes up exponentially (e.g. on a 100 by 100 area there's 100,000 tiles to be checked, not good).

I hope this helps, and thanks for posting your script here to help others :)
<<

Tef

User avatar

Lance Carrier

Posts: 64

Joined: 15 Apr 2013, 15:12

KaM Skill Level: Skilled

Post 15 Apr 2013, 17:06

Re: Dynamic Script Usage

@ Lewin: Actually, I cannot get const to work within a function, because something like this...
  Code:
function Something (somevar: integer) : boolean; const SOME_CONSTANT = 0; var SomeVariable: Integer begin end;
...gives me an error in Kam Remake at the line in which const is placed. The error: 'BEGIN expected'. Const only seems to work for global constants, i.e. if used outside of functions? But thanks for the feedback and nice to hear that there will come something to avoid those nasty long iterations!
<<

The Dark Lord

User avatar

King Karolus Servant

Posts: 2154

Joined: 29 Aug 2007, 22:00

KaM Skill Level: Veteran

Location: In his dark thunderstormy castle

Post 17 Apr 2013, 01:03

Re: Dynamic Script Usage

Hello, I'm having problems again. I'm trying to display a countdown to show players when the next attack will be.

I'm using these variables:
  Code:
TimeNextAttack: Integer; ShowTime: Boolean;
These are the settings OnStart:
  Code:
TimeNextAttack := 2400 ShowTime:= True
And this the procedure to make it work:
  Code:
procedure ShowTimeUntilNextAttack; var I, Minutes, Seconds: Integer; begin if (ShowTime = True) then begin ShowTime := False; Minutes := TimeNextAttack div 600; Seconds := (TimeNextAttack div 10) mod 60; for I := 0 to 3 do Actions.SetOverlayText(I, States.TextFormatted(20, [Minutes, Seconds])); end; end;
After OnTick, I placed this:
  Code:
ShowTimeUntilNextAttack;
And at the same time when the first attack is launched:
  Code:
TimeNextAttack := 4200 ShowTime:= True ShowTimeUntilNextAttack;
As you can see I, ehm, made grateful use of Annie's Hill's script. :P Anyway, The text does appear on my screen ("The next attack will arrive in 04:00"), but it doesn't count down, it just stays at 04:00. After four minutes, it changes to 07:00 as supposed to but it doesn't count down either (not that I expected it to). My guess would be that my condition is faulty and it somehow loops the script, thereby showing 04:00 or 07:00 the whole time? Or is something missing?

Then two more things to complicate things:
- I'd like to have the countdown in white, just like normal text; until the last minute, where '01:00' and onwards should be red. How can I do this? Would something like
  Code:
if (Time = 600) then begin Actions.SetOverlayText(I, States.TextFormatted(21, [Minutes, Seconds])); end;
work?
- And a larger problem: This countdown interferes with my previous 'Player has defeated the first attack wave' messages. Is it even possible to have both working correctly?
<<

Lewin

User avatar

KaM Remake Developer

Posts: 3822

Joined: 16 Sep 2007, 22:00

KaM Skill Level: Skilled

ICQ: 269127056

Website: http://lewin.hodgman.id.au

Yahoo Messenger: lewinlewinhodgman

Location: Australia

Post 17 Apr 2013, 01:47

Re: Dynamic Script Usage

@tef: Sorry about that, it must be a limitation of the script compiler. In Delphi/Lazarus you can have local constants, that's why I suggested it.

@TDL: The reason why it doesn't count down is because you set TimeNextAttack := 4200, then every tick you display TimeNextAttack to the player. Where is the countdown code? You need to decrease TimeNextAttack every tick otherwise it will always stay at 4200. Here's the code I would use:
  Code:
procedure ShowTimeUntilNextAttack; var I, Minutes, Seconds, MessageID: Integer; begin //Convert to minutes and seconds Minutes := TimeNextAttack div 600; Seconds := (TimeNextAttack div 10) mod 60; //Decide on the colour if TimeNextAttack < 600 then MessageID := 21 //Red for the last 60 seconds else MessageID := 20; //Otherwise white //Show to the players for I := 0 to 3 do Actions.SetOverlayText(I, States.TextFormatted(MessageID, [Minutes, Seconds])); end;
And in OnTick put this:
  Code:
if TimeNextAttack > 0 then begin ShowTimeUntilNextAttack; TimeNextAttack := TimeNextAttack - 1; end;
Then you don't need ShowTime at all. When you want to start the timer you set:
TimeNextAttack := 2400
And it will countdown, stopping when it reaches 0.

Now to the bigger problem, showing multiple things on the overlay text. I'd do something like this:
1. Change ShowTimeUntilNextAttack to be a function which returns AnsiString:
  Code:
function ShowTimeUntilNextAttack: AnsiString; var I, Minutes, Seconds, MessageID: Integer; begin //Convert to minutes and seconds Minutes := TimeNextAttack div 600; Seconds := (TimeNextAttack div 10) mod 60; //Decide on the colour if TimeNextAttack < 600 then MessageID := 21 //Red for the last 60 seconds else MessageID := 20; //Otherwise white //Return the message Result := States.TextFormatted(MessageID, [Minutes, Seconds]); end;
2. Your message for showing when someone has defeated a wave should also be a function returning AnsiString, something like this:
  Code:
var PopupShowTime, PopupPlayer: Integer; procedure GetOverlayPopup: AnsiString; begin //If we haven't reached the time to hide the popup yet, show it if PopupHideTime > States.GameTime then Result := States.TextFormatted(29, [States.PlayerName(PopupPlayer)])) else Result := ''; //No popup message end;
To start a popup message use this:
  Code:
PopupHideTime := States.GameTime+DELAY; //Time when the popup will get hidden (now + delay) PopupPlayer := 2; //Will show player 2's name in the popup
3. To combine these things, put something like this in OnTick:
  Code:
procedure OnTick; var OverlayMessage: AnsiString; begin OverlayMessage := ''; if TimeNextAttack > 0 then begin OverlayMessage := OverlayMessage + ShowTimeUntilNextAttack; TimeNextAttack := TimeNextAttack - 1; end; OverlayMessage := OverlayMessage + '|' + GetOverlayPopup; for I:=0 to 3 do Actions.SetOverlayText(I, OverlayMessage); end;
Does that all make sense? I hope you can understand my code. Basically it generates each message separately (countdown + popup) then combines them in OnTick and displays them to the player. Let me know if you have any questions.
Cheers,
Lewin.
<<

The Dark Lord

User avatar

King Karolus Servant

Posts: 2154

Joined: 29 Aug 2007, 22:00

KaM Skill Level: Veteran

Location: In his dark thunderstormy castle

Post 17 Apr 2013, 12:32

Re: Dynamic Script Usage

I understand most of it; what I don't really understand is this part:
  Code:
procedure OnTick; begin OverlayMessage := OverlayMessage + ShowTimeUntilNextAttack; TimeNextAttack := TimeNextAttack - 1; end;
Meanwhile I have 2 new questions:

1. I have no idea how to make text red. I thought it wouldn't be really hard but that Delphi page about Format doesn't really explain it.
2. I was thinking it would actually be better to have a recruit in the tower and provide 5 stones at the start, so you lose a bit less quickly. However, once the recruit gets hungry he will be useless. Can I make his condition 'infinite', so he never gets hungry? :P
Edit: never mind, I found out about UnitHungerSet. :)
Edit 2: my script didn't work (the game froze and crashed) but I fixed it already. :) Everything now works as it should.
<<

Lewin

User avatar

KaM Remake Developer

Posts: 3822

Joined: 16 Sep 2007, 22:00

KaM Skill Level: Skilled

ICQ: 269127056

Website: http://lewin.hodgman.id.au

Yahoo Messenger: lewinlewinhodgman

Location: Australia

Post 17 Apr 2013, 15:13

Re: Dynamic Script Usage

I understand most of it; what I don't really understand is this part:
  Code:
procedure OnTick; begin OverlayMessage := OverlayMessage + ShowTimeUntilNextAttack; TimeNextAttack := TimeNextAttack - 1; end;
OverlayMessage is a local string (text) variable. "+" is used here to join two strings together. The first string is whatever we currently have in OverlayMessage (blank), and the second is the result of the function ShowTimeUntilNextAttack. So basically we get the result of that function and place it on the end of our variable OverlayMessage.

The second line decreases TimeNextAttack by 1. So if it was equal to 100, it will be equal to 99 after that line. This means the countdown timer actually counts down (decreases every tick)
1. I have no idea how to make text red. I thought it wouldn't be really hard but that Delphi page about Format doesn't really explain it.
We have our own markup for text colour:
[$0000FF]This text is red![] This text is not.
So put [$0000FF] around the bit of text that needs to be red. You can even do it in the code, something like this:
'[$0000FF]'+States.Text(1)+'[]'
Once again "+" is used to join strings.
<<

The Dark Lord

User avatar

King Karolus Servant

Posts: 2154

Joined: 29 Aug 2007, 22:00

KaM Skill Level: Veteran

Location: In his dark thunderstormy castle

Post 17 Apr 2013, 15:39

Re: Dynamic Script Usage

Ah, now I understand it. :D
And the colouring works beautifully, thanks. I'm getting more and more excited about this. :P
<<

Tef

User avatar

Lance Carrier

Posts: 64

Joined: 15 Apr 2013, 15:12

KaM Skill Level: Skilled

Post 18 Apr 2013, 16:11

Re: Dynamic Script Usage

Suppose I have this:

FancyArray: Array [1..10] of integer;

I know how to fill FancyArray one by one, or with a for loop. I also know how to access values from it. But I'd like to know what I can do more with arrays in dynamic scripts for Kam Remake. Could any expert here tell me how to do the following, if possible? (preferably with a built-in funcion!)
1 - is there a built-in function to get the lowest, or highest value out of FancyArray?
2 - is there a built-in function to sort FancyArray, ascending or descending?
3 - can I read a value at place n and then move all values higher than n to n-1. Example: read FancyArray[5] and move FancyArray[6] up to FancyArray[10] to position FancyArray[5] up to FancyArray[9]
4 - can I dynamically increase the length of FancyArray? Example: let FancyArray become an Array [1..20].
5 - can I dynamically create FancyArray2 (so not declaring beforehand under a 'var' section)
6 - any other useful things I could do with FancyArray that you know of?

I'm asking because documentation on the Internet isn't really helpful for me regarding array manipulation. Thanks in advance!
<<

Krom

User avatar

Knights Province Developer

Posts: 3280

Joined: 09 May 2006, 22:00

KaM Skill Level: Fair

Location: Russia

Post 18 Apr 2013, 16:20

Re: Dynamic Script Usage

Suppose I have this:

FancyArray: Array [1..10] of integer;

I know how to fill FancyArray one by one, or with a for loop. I also know how to access values from it. But I'd like to know what I can do more with arrays in dynamic scripts for Kam Remake. Could any expert here tell me how to do the following, if possible? (preferably with a built-in funcion!)
1 - is there a built-in function to get the lowest, or highest value out of FancyArray?
2 - is there a built-in function to sort FancyArray, ascending or descending?
3 - can I read a value at place n and then move all values higher than n to n-1. Example: read FancyArray[5] and move FancyArray[6] up to FancyArray[10] to position FancyArray[5] up to FancyArray[9]
4 - can I dynamically increase the length of FancyArray? Example: let FancyArray become an Array [1..20].
5 - can I dynamically create FancyArray2 (so not declaring beforehand under a 'var' section)
6 - any other useful things I could do with FancyArray that you know of?

I'm asking because documentation on the Internet isn't really helpful for me regarding array manipulation. Thanks in advance!

Hi there,
I'll reply from Delphi standpoint. PascalScript may be a little more limiting:

1. Low(Arr) and High(Arr). Length(Arr) returns length
2. no, only manually
3. no, only manually
4. SetLength(Arr, new_length) or SetLength(Arr, Length(Arr) + 32) will grow or shrink the array; new elements are set to 0
5. yes, but it will have to start from 0, where arrays declared in VAR can start from any number. SetLength(Arr, new_length);
6. e.g. you can declare in VAR array [boolean] of Integer; arrays can start from any number. array [8..16] of string; maybe something else I forgot
Knights Province at: http://www.knightsprovince.com
KaM Remake at: http://www.kamremake.com
Original MBWR/WR2/AFC/FVR tools at: http://krom.reveur.de
<<

Lewin

User avatar

KaM Remake Developer

Posts: 3822

Joined: 16 Sep 2007, 22:00

KaM Skill Level: Skilled

ICQ: 269127056

Website: http://lewin.hodgman.id.au

Yahoo Messenger: lewinlewinhodgman

Location: Australia

Post 18 Apr 2013, 16:32

Re: Dynamic Script Usage

The functions Krom suggested Low(Arr) and High(Arr) get the first and last elements of the array (so with FancyArray they will give 1 and 10). I think you might have meant the maximum and minimum values, that's something you have to write yourself.

Something you might not be aware of: Pascal has two types of arrays, dynamic and static:
Static array:
- Declaration: FancyArray: array[1..10] of Integer;
- Changing size is not possible
- Slightly more efficient for the compiler

Dynamic:
- Declaration: FancyArray: array of Integer;
- Change size with SetLength(FancyArray, NewLength);
- First element is always 0, last is Length(FancyArray)-1

Currently there are some issues with using arrays in global variables, only static arrays are supported. I've recently improved the saving/loading of global variables so it now supports dynamic arrays and some other cool stuff like multidimensional arrays, sets, enums and records.

Do you know about multidimensional arrays? FancyArray: array[1..10] of array[1..10] of Integer. This gives you a 2 dimensional array (a 10x10 grid or matrix). You can access an element with FancyArray[1][2]. These also cannot be used in global variables at the moment, but I've fixed them. You can also have dynamic multidimensional arrays.
<<

Ben

User avatar

Former Site Admin

Posts: 3814

Joined: 08 Jan 2009, 23:00

Location: California - Pacific Time (UTC -8/-7 Summer Time)

Post 19 Apr 2013, 00:59

Re: Dynamic Script Usage

I'm having another problem:

I'm trying to set the hunger for a soldier using the UnitHungerSet command. However, I'm having trouble getting the command to work. I think it is because I am not getting the correct ID for the soldier. I tried using a variable to set the soldier with, and using it later as the ID, but this isn't working.
  Code:
var Spawner: Integer; procedure OnMissionStart; begin Spawner := Actions.GiveGroup(0, 22, 15, 45, 4, 1, 1); end; procedure OnTick; begin if (States.GameTime mod 30 = 0) then Actions.UnitHungerSet(Spawner, 60); end;
I used to spam this forum so much...

Return to “Dynamic Scripting”

Who is online

Users browsing this forum: No registered users and 11 guests