Make Scenario Triggers using PHP Script

Article written by AOHH
Published on 08-22-2014; updated on 08-22-2014
Tags: PHP, Triggers

I am working on my "PHP Genie" project which reads/edits/writes Scenarios and Saved-Games. Currently, I am starting version 2. I will however give a few snippets from version 1 which is still a beta. Version 3 will have a User-Interface. I also intend to make a mini-map viewer in version 3 or 4.

Because I am still working on reading scenario contents, I have used a method to skip to the scenario's triggers using a Magic-Keyword. For now, here are the snippets...

STEP 1:

Download PHP 5.2 and place the files into a folder in your local disc.

STEP 2:

Download and install PHP Designer

STEP 3:

Run PHP Designer and set the Preferences for debugging to the "php.exe" file.

STEP 4:

Create "Index.php" with the following contents:

<? # Extend limits for huge amounts of triggers: ini_set ( 'memory_limit' , '1024M' ) ; ini_set ( 'max_execution_time' , 300 ) ; # Set class: class Scenario { # The output variable: static $O ; # This function must be absolutely perfect. # The slightest error is not at all acceptable. # It writes all triggers into raw scenario data. # I prefer the PACK function rather than SPRINTF. function Write ( $I ) { $W = 0 ; self :: $X .= pack ( 'L1' , count ( $I ) ) ; # <TriggersCount> foreach ( $I as $T ) { $T [ 4 ] = substr ( $T [ 4 ] , 0 , 64 ) ; # Maximum length 64 $T [ 5 ] = substr ( $T [ 5 ] , 0 , 44 ) ; # Maximum length 44 self :: $X .= pack ( 'L1' , ( $T [ 0 ] ? 1 : 0 ) ) ; # <Enabled> self :: $X .= pack ( 'L1' , ( $T [ 1 ] ? 1 : 0 ) ) ; # <Looping> self :: $X .= pack ( 'C1' , 0 ) ; # <Unknown_1> self :: $X .= pack ( 'C1' , ( $T [ 2 ] ? 1 : 0 ) ) ; # <Objective> self :: $X .= pack ( 'L1' , ( isset ( $T [ 3 ] ) ? $T [ 3 ] : 0 ) ) ; # <ObjOrder> self :: $X .= pack ( 'C1' , 0 ) ; # <Unknown_2_1> self :: $X .= pack ( 'C1' , 0 ) ; # <Unknown_2_2> self :: $X .= pack ( 'C1' , 0 ) ; # <Unknown_2_3> self :: $X .= pack ( 'C1' , 0 ) ; # <Unknown_2_4> self :: $X .= pack ( 'L1' , strlen ( $T [ 4 ] ) + 1 ) ; # <DescLength> self :: $X .= pack ( 'A*' , $T [ 4 ] ) . pack ( 'C1' , 0 ) ; # <DescValue> self :: $X .= pack ( 'L1' , strlen ( $T [ 5 ] ) + 1 ) ; # <NameLength> self :: $X .= pack ( 'A*' , $T [ 5 ] ) . pack ( 'C1' , 0 ) ; # <NameValue> self :: $X .= pack ( 'L1' , count ( $T [ E ] ) ) ; # <EffectsCounts> if ( count ( $T [ E ] ) ) { foreach ( $T [ E ] as $E ) { self :: $X .= pack ( 'L1' , ( isset ( $E [ 0 ] ) ? $E [ 0 ] : 0 ) ) ; # <Type> self :: $X .= pack ( 'L1' , 23 ) ; # <Check> self :: $X .= pack ( 'L1' , ( isset ( $E [ Z ] ) ? $E [ Z ] : -1 ) ) ; # <AIGoal> self :: $X .= pack ( 'L1' , ( isset ( $E [ Q ] ) ? $E [ Q ] : -1 ) ) ; # <Amount> self :: $X .= pack ( 'L1' , ( isset ( $E [ R ] ) ? $E [ R ] : -1 ) ) ; # <Resource> self :: $X .= pack ( 'L1' , ( isset ( $E [ M ] ) ? $E [ M ] : -1 ) ) ; # <Diplomacy> self :: $X .= pack ( 'L1' , ( count ( $E [ S ] ) ? count ( $E [ S ] ) : -1 ) ) ; # <Selected> self :: $X .= pack ( 'L1' , ( isset ( $E [ O ] ) ? $E [ O ] : -1 ) ) ; # <UnitLoc> self :: $X .= pack ( 'L1' , ( isset ( $E [ U ] ) ? $E [ U ] : -1 ) ) ; # <UnitConst> self :: $X .= pack ( 'L1' , ( isset ( $E [ P ] ) ? $E [ P ] : -1 ) ) ; # <PSource> self :: $X .= pack ( 'L1' , ( isset ( $E [ E ] ) ? $E [ E ] : -1 ) ) ; # <PTarget> self :: $X .= pack ( 'L1' , ( isset ( $E [ H ] ) ? $E [ H ] : -1 ) ) ; # <Tech> self :: $X .= pack ( 'L1' , ( isset ( $E [ B ] ) ? $E [ B ] : -1 ) ) ; # <StringID> self :: $X .= pack ( 'L1' , ( isset ( $E [ K ] ) ? $E [ K ] : -1 ) ) ; # <Unknown> self :: $X .= pack ( 'L1' , ( isset ( $E [ T ] ) ? $E [ T ] : -1 ) ) ; # <Time> self :: $X .= pack ( 'L1' , ( isset ( $E [ I ] ) ? $E [ I ] : -1 ) ) ; # <Trigger> self :: $X .= pack ( 'L1' , ( isset ( $E [ L ] ) ? $E [ L ] [ 0 ] : -1 ) ) ; # <Location> [X] self :: $X .= pack ( 'L1' , ( isset ( $E [ L ] ) ? $E [ L ] [ 1 ] : -1 ) ) ; # <Location> [Y] self :: $X .= pack ( 'L1' , ( isset ( $E [ A ] ) ? $E [ A ] [ 0 ] [ 0 ] : -1 ) ) ; # <Area> [0] [X] self :: $X .= pack ( 'L1' , ( isset ( $E [ A ] ) ? $E [ A ] [ 0 ] [ 1 ] : -1 ) ) ; # <Area> [0] [Y] self :: $X .= pack ( 'L1' , ( isset ( $E [ A ] ) ? $E [ A ] [ 1 ] [ 0 ] : -1 ) ) ; # <Area> [1] [X] self :: $X .= pack ( 'L1' , ( isset ( $E [ A ] ) ? $E [ A ] [ 1 ] [ 1 ] : -1 ) ) ; # <Area> [1] [Y] self :: $X .= pack ( 'L1' , ( isset ( $E [ G ] ) ? $E [ G ] : -1 ) ) ; # <UnitGroup> self :: $X .= pack ( 'L1' , ( isset ( $E [ Y ] ) ? $E [ Y ] : -1 ) ) ; # <UnitType> self :: $X .= pack ( 'L1' , ( isset ( $E [ N ] ) ? $E [ N ] : -1 ) ) ; # <TextPanel> $Y = ( $E [ 0 ] == 3 || $E [ 0 ] == 20 || $E [ 0 ] == 26 ? 1 : 0 ) ; self :: $X .= pack ( 'L1' , strlen ( $E [ X ] ) + $Y ) ; # <TextLength> self :: $X .= $E [ X ] ; # <TextValue> if ( $Y ) self :: $X .= pack ( 'C1' , 0 ) ; self :: $X .= pack ( 'L1' , strlen ( $E [ D ] ) + $Y ) ; # <SoundLength> self :: $X .= $E [ D ] ; # <SoundValue> if ( $Y ) self :: $X .= pack ( 'C1' , 0 ) ; if ( count ( $E [ S ] ) ) { foreach ( $E [ S ] as $S ) { self :: $X .= pack ( 'L1' , $S ) ; # <UnitObj> <> } } } } if ( count ( $T [ E ] ) ) { $Z = 0 ; foreach ( $T [ E ] as $E ) { self :: $X .= pack ( 'L1' , $Z ) ; # <EffectsOrder> <> $Z ++ ; } } self :: $X .= pack ( 'L1' , count ( $T [ C ] ) ) ; # <ConditionsCount> if ( count ( $T [ C ] ) ) { foreach ( $T [ C ] as $C ) { self :: $X .= pack ( 'L1' , ( $C [ 0 ] ? $C [ 0 ] : 0 ) ) ; # <Type> self :: $X .= pack ( 'L1' , 16 ) ; # <Check> self :: $X .= pack ( 'L1' , ( isset ( $C [ Q ] ) ? $C [ Q ] : -1 ) ) ; # <Amount> self :: $X .= pack ( 'L1' , ( isset ( $C [ R ] ) ? $C [ R ] : -1 ) ) ; # <Resource> self :: $X .= pack ( 'L1' , ( isset ( $C [ S ] ) ? $C [ S ] : -1 ) ) ; # <UnitObj> self :: $X .= pack ( 'L1' , ( isset ( $C [ O ] ) ? $C [ O ] : -1 ) ) ; # <UnitLoc> self :: $X .= pack ( 'L1' , ( isset ( $C [ U ] ) ? $C [ U ] : -1 ) ) ; # <UnitConst> self :: $X .= pack ( 'L1' , ( isset ( $C [ P ] ) ? $C [ P ] : -1 ) ) ; # <Player> self :: $X .= pack ( 'L1' , ( isset ( $C [ H ] ) ? $C [ H ] : -1 ) ) ; # <Tech> self :: $X .= pack ( 'L1' , ( isset ( $C [ T ] ) ? $C [ T ] : -1 ) ) ; # <Time> self :: $X .= pack ( 'L1' , ( isset ( $C [ K ] ) ? $C [ K ] : -1 ) ) ; # <Unknown> self :: $X .= pack ( 'L1' , ( isset ( $C [ A ] ) ? $C [ A ] [ 0 ] [ 0 ] : -1 ) ) ; # <Area> [0] [X] self :: $X .= pack ( 'L1' , ( isset ( $C [ A ] ) ? $C [ A ] [ 0 ] [ 1 ] : -1 ) ) ; # <Area> [0] [Y] self :: $X .= pack ( 'L1' , ( isset ( $C [ A ] ) ? $C [ A ] [ 1 ] [ 0 ] : -1 ) ) ; # <Area> [1] [X] self :: $X .= pack ( 'L1' , ( isset ( $C [ A ] ) ? $C [ A ] [ 1 ] [ 1 ] : -1 ) ) ; # <Area> [1] [Y] self :: $X .= pack ( 'L1' , ( isset ( $C [ G ] ) ? $C [ G ] : -1 ) ) ; # <UnitGroup> self :: $X .= pack ( 'L1' , ( isset ( $C [ Y ] ) ? $C [ Y ] : -1 ) ) ; # <UnitType> self :: $X .= pack ( 'L1' , ( isset ( $C [ Z ] ) ? $C [ Z ] : -1 ) ) ; # <AISignal> } } if ( count ( $T [ C ] ) ) { $Z = 0 ; foreach ( $T [ C ] as $C ) { self :: $X .= pack ( 'L1' , $Z ) ; # <EffectsOrder> <> $Z ++ ; } } $W ++ ; } $Z = 0 ; foreach ( $I as $T ) { self :: $X .= pack ( 'L1' , $Z ) ; # <TriggersOrder> <> $Z ++ ; } # Write the end: self :: $X .= pack ( 'L1' , 0 ) ; # <FilesIncluded> self :: $X .= pack ( 'L1' , 0 ) ; # <ExtraDataFlag> } } # Scenario: $Scen = 'YourScenario.scx' ; # Keyword: $Word = 'TRIGGER_START' ; # Get scenario's data: $Cont = file_get_contents ( $Scen ) ; # Skip 4 bits: $Head [ 'Version' ] [ 1 ] = substr ( $Cont , 0 , 4 ) ; # Get length: $Head [ 'Length' ] = implode ( '' , unpack ( 'L1' , substr ( $Cont , 4 ) ) ) ; # Start of body: $Line = $Head [ 'Length' ] + 8 ; # Save head without body: file_put_contents ( 'Head.hex' , substr ( $Cont , 0 , $Line ) ) ; # GZ-Inflate the body: $Cont = gzinflate ( substr ( $Cont , $Line ) ) ; # Find start of triggers by keyword: $Line = strpos ( $Cont , $Word ) - 26 ; # Save body without triggers: file_put_contents ( 'Body.hex' , substr ( $Cont , 0 , $Line ) ) ; # Import new triggers: if ( file_exists ( 'Triggers.php' ) ) include 'Triggers.php' ; else die ( 'Triggers.php file does not exist !' ) ; # Write new triggers: Scenario :: Write_Triggers ( $I ) ; # Write the end: Scenario :: $O .= pack ( 'L1' , 0 ) ; # <FilesIncluded> Scenario :: $O .= pack ( 'L1' , 0 ) ; # <ExtraDataFlag> # Remove extention: $Scen = str_replace ( '.scn' , '' , $Scen ) ; $Scen = str_replace ( '.scx' , '' , $Scen ) ; # Loop 100 times: foreach ( range ( 1 , 100 ) as $J ) { # Set new filename: $File = $Scen . '[' . $J . '].scx' ; $J ++ ; # Check for incremented filenames: if ( ! file_exists ( $File ) ) { # Deflate body with triggers and merge head with deflation: file_put_contents ( $File , file_get_contents ( 'Head.hex' ) . gzdeflate ( file_get_contents ( 'Body.hex' ) . Scenario :: $O ) ) ; break ; } } # Write results: echo '<pre><h1>' . $File . '</h1></pre>' ; ?>

STEP 5:

Make sure to specify your scenario's name to the "$Scen" variable and Magic-Keyword to the "$Word" variable (refer to Step 6 for clarification).

STEP 6:

Make sure that the first trigger from your scenario has the Magic-Keyword at the absolute start of the Trigger-Description (a keyword such as "TRIGGER_START").

STEP 7:

Create "Triggers.php" with the following contents:

<? # Trigger 0 ... $I[0][0]=1;# <Enabled> $I[0][1]=0;# <Looping> $I[0][2]=0;# <Objective> $I[0][3]=0;# <ObjOrder> $I[0][4]="This is the trigger's description";# <DescValue> $I[0][5]="New Trigger 1";# <NameValue> $I[0][E][0][0]=20;# Display-Instructions $I[0][E][0][T]=0;# <Time> $I[0][E][0][N]=0;# <TextPanel> $I[0][E][0][X]="<WHITE>Yo yo, hello..."; $I[0][E][0][D]="Age Up"; # Or you can skip directly to trigger 1 effect without specifying trigger 1: $I[1][E][0][X]="This is the second trigger's effect text..."; ?>

STEP 8:

Open the 2 .php files in PHP Designer. From the "triggers.php", use the example triggers as a format sample to make your own triggers. After making your triggers, save "triggers.php" and Debug "index.php". This will create "YourScenario[1].scx" ... and "YourScenario[2].scx" (increment) each time you Debug.

FURTHER INFO:

  • Note that the AI Script data are contained at the end of the scenario. Any AI Script data will be delimited since I have used a series of 0000 to skip AI Scripts and end the scenario .
  • I intend to give the trigger Reader for the scenario's triggers.
  • I intend to give the XML importer/exporter for triggers. It will be the same XML format as AoK:TS for the convenience of those who wish AoK:TS XML import worked.
  • When I release version 2, web admins (such as you Leif Ericson, or Voobly.com, AoC-Zone) with a server that supports PHP will find PHP-Genie an excellent tool to read and view information about Scenarios or Saved-Games. Including a mini-map viewer (Version 3 only).

Do you want to comment on this article? Thank the author? Tribute resources for its improvement? Raze it to the ground?

Come by and visit its thread in the University Forum!