Jump to content
Sign in to follow this  
You need to play a total of 5 battles to post in this section.
MatroseFuchs

Unbound + ModsAPI

1 comment in this topic

Recommended Posts

Administrator
203 posts
41 battles

How to add and use Unbound technology on the ModAPI stage

As Unbound layout is processed on the Flash side, then the development of the mod will be associated mainly with ActionScript files. We need a layout handler and the main file for the Flash API similar to what we already did in the previous examples.

But first, let’s prepare a file with a simple layout code that we will experiment with, for example, draw a square in the corner.

 

test_window.xml

Create an XML file with a simple layout of a simple white square.

Spoiler

<ui>
	<block className="TestElement">
		<block>
			<style>
				<position value="absolute"/>
				<left value="10px"/>
				<top value="10px"/>
				<width value="100px"/>
				<height value="100px"/>
				<backgroundColor value="0xEEEEEEEE"/>
			</style>
		</block>
	</block>
</ui>

 

The resulting XML file shall be run through the "xml2as.bat" utility in order to compile layout expressions into a SWF file similar to what we did with the modification of the finished XML. Put the resulting SWF file in "..\res_mods\0.8.8.1\gui\unbound\flash" and add its path to "..\gui\ lash\USSExpressionsLoader.xml".

 

Movie.as

To process XML files with Unbound layout, we need to create a handling tool. To do this, open the AcrionScript editor (for example, FlashDevelop) and create a new project. The main Flash file of the mod will be called, as is customary, "Main.as", and for handling the layout we will create another one and name it, for example, "Movie.as" from which we will start.

Now we need to inherit the Movie handler class from the "ScaleformMovie" class, which we import from the external library "wows_library.swc", with the command "import lesta.dialogs.ScaleformMovie;". In this parent class, we will have almost all the necessary tools to work with the Unbound layout.

Next, we declare a variable for the layout “element” "private var unboundElement: UnboundElement;" and import this data type "import lesta.libs.unbound.UnboundElement;". We also need a controller for our element, declare it "private var unboundController: UnboundElementController;", we also import its class "import lesta.dialogs.battle_window_controllers.UnboundElementController;".

Since our handler inherited the class from the parent class, now it is necessary to correctly describe the main function of the class "public function Movie()". If we look at the parent class, we will see that its main function has three parameters, so we also need to set three parameters. When creating an instance of our class, we will pass there all the necessary parameters.

Now, in this Main() function, we add the call to the parent class (super()) by the rules and pass the parameters of the function of our class to it. Here we add a callback that will update the element stage size.

The next function that we will describe is the main function of handling the layout. So, let's declare a function, for example, call it "public function buildUnboundApplication():void", create an instance of the element "new UnboundElement()". Now our element needs to specify the path to the XML file with layout in the "config" property. This can be done by specifying a direct path in the format "C:\\Games\\World_of_Warships\\res_mods\\0.8.7.0\\PnFMods\\ubtest\\test_window.xml" (this is the path in ActionScript), or by adding a file search function "get configPath()", this is necessary due to the peculiarities of downloading files, because the folder with the mod is outside the file upload directory. After that, in the "rootElementId" element property we indicate the name of the element in the layout, which will be the main one for displaying on the stage.

Next, we need to create an instance of the controller "new UnboundElementController()", which will be responsible for loading this element. In the "clip" property you need to specify the "unboundElement" element to be loaded, via "setDataHub" method, add the "setDataHub (dataHub)" datahub instance to the controller property, which we will pass when initializing the "Movie" (this dataHub instance will not contain data but we need to add it), using the "setUbCentral"  method add an instance of  "protoCentral" (it will contain all the bindings), which is located in the parent class "ScaleformMovie", then using the "initController()" method initialize it.

Now let's set the element stage size using the "setStageSize" function and pass it the stage sizes from the main class of the Main mod, which we will describe later. In the function, create an instance of the stage from the available components "lesta.components.Stage", and set the attributes of the component "width, height, scale and evStageSizeChanged", then pass these sizes to the element controller.

Finally, add our element to the "addChild (unboundElement)" stage.

Spoiler

package 
{
	import flash.display.*;
	import lesta.components.*;
	import lesta.api.GameAPI;
	import lesta.api.constants.Calls;
	import lesta.constants.ComponentClass;
	import lesta.datahub.DataHub;
	import lesta.dialogs.ScaleformMovie;
	import lesta.dialogs.battle_window_controllers.UnboundElementController;
	import lesta.libs.unbound.UnboundElement;
	
	/**
	 * ...
	 * @author 
	 */
	public class Movie extends ScaleformMovie 
	{
		private var unboundElement:UnboundElement;
		private var unboundController:UnboundElementController;
		
		public function Movie(param0:DataHub, stage:flash.display.Stage, gameAPI:GameAPI) 
		{
			super(param0, null, stage);
			gameAPI.data.addCallBack(Calls.UPDATE_STAGE, this.setStageSize);
		}
		
		public function buildUnboundApplication():void
		{
			unboundElement = new UnboundElement();
			// unboundElement.config = "C:\\Games\\World_of_Warships\\res_mods\\0.8.7.0\\PnFMods\\ubtest\\test_window.xml";
			unboundElement.config = this.configPath;
			unboundElement.rootElementId = "TestElement";
			setupUnbound();
			unboundController = new UnboundElementController();
			unboundController.clip = unboundElement;
			unboundController.setDataHub(dataHub);
			unboundController.setUbCentral(protoCentral);
			unboundController.initController();
			setStageSize(stage.stageWidth, stage.stageHeight);
			addChild(unboundElement);
		}
		
		private function get folderPath():String 
		{
			var normalisedUrl:String = this.loaderInfo.url.split('/').join('\\');
			return normalisedUrl.substr(0, normalisedUrl.lastIndexOf('\\'));
		}

		private function get configPath():String 
		{
			return [this.folderPath, "test_window.xml"].join('\\');
		}
		
		public function setStageSize(stageW:Number, stageH:Number):void
		{
			var stage: lesta.components.Stage = this.dataHub.getSingleComponent(ComponentClass.stage) as lesta.components.Stage;
			stage.width = stageW;
			stage.height = stageH;
			stage.scale = 1.0;
			stage.evStageSizeChanged.invoke([stage]);
			unboundController.updateStageSize(stageW, stageH);
		}
	}

}

 

 

Main.as

In the main class of the mod, declare two variables - "dataHub:DataHub" and "movie:Movie" (our layout handler). In the main function of the class, create an instance of the datahub and declare a call of the main function of the parent class "ModBase" from which we inherit our "Main" (see the previous examples for this).

We override the function "init()" of the parent class "ModBase" and describe it, as it will be executed during initialization (if necessary, you can override other parent class functions). In this function, we also need to call the overridable parent class function "super.init();", now we will create an instance of the layout handler "movie" and pass to it the required parameters "new Movie (dataHub, this.stage, gameAPI)", then add the layout handler instance to the container of our mod "addChild(movie)", add to the handler a listener of the event of adding it to the stage "movie.addEventListener (Event.ADDED_TO_STAGE, onAddedToStage);" and add our mod’s link to the ModAPI stage "gameAPI.stage.addChild (this)".

Let’s describe the last function that will be called when the handler is loaded onto the "onAddedToStage(event: Event)" stage. For Scaleform to accept our movie, we need to create an entity “entity” with a stage id (id - ComponentClass.stage), a stage component “stageComponent” with the same id, add the stage component to the entity, get a collection of entities with stage components and add our entity "collection.add (entity)" to this collection. Now we can use our handler method to output the result to the stage "movie.buildUnboundApplication();" and delete the listener, because we don’t need it anymore, our element is now on the ModAPI stage.

Spoiler

package
{
	import flash.events.Event;
	import lesta.api.GameAPI;
	import lesta.api.ModBase;
	import lesta.components.Stage;
	import lesta.constants.ComponentClass;
	import lesta.datahub.DataHub;
	import lesta.datahub.Collection;
	import lesta.datahub.Entity;
	
	public class Main extends ModBase
	{
		public var dataHub:DataHub;
		private var movie:Movie;
		
		public function Main()
		{
			dataHub = new DataHub();
			super();
		}
		
		override public function init():void
		{
			super.init();
			movie = new Movie(dataHub, this.stage, gameAPI);
			addChild(movie);
			movie.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			gameAPI.stage.addChild(this);
		}
		
		public function onAddedToStage(event:Event):void
		{
			var entity:Entity = dataHub.createEntity(ComponentClass.stage);
			var stageComponent:Stage = dataHub.createComponent(ComponentClass.stage) as Stage;
			entity.addComponent(stageComponent);
			var collection:Collection = this.dataHub.getCollection(ComponentClass.stage);
			collection.add(entity);
			removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			movie.buildUnboundApplication();
		}
	}
}

 

 

Mod on unbound

We compile our project and the resulting Main.swf file (the name of the final project file can be set in the project properties) put to the mod folder. Add the Main.py file in which we write one line "API_VERSION = 'API_v1.0'", because without it, the mod will not work. Add our prepared XML file with the layout to the mod folder, and put the compiled SWF file from XML to "gui\unbound\flash" and add the path to it in "gui\flash\USSExpressionsLoader.xml". Run the client and make sure that our mod handled the file with the layout and displayed a white square in the corner of the game window.

test_window.jpg

 

Add data to the scope

Let's analyze the use of such a tool as a controller, which allows to add data to the scope and operate it, and for this we need to write our own controller.

So, we will create a new file in the project and name it "MyUbController", it will inherit from the "UbController" class. In the controller, we override the method "override public function init (param1:Vector.<IUbExpression>):void" so it is possible to pass the calculated arguments to it. Pass the arguments to the parent class "super.init(param1);" and write a line into the scope, which we then display "scope.myText ="Text";".

Spoiler

package
{
	import lesta.unbound.core.UbController;
	import lesta.unbound.expression.IUbExpression;

	public class MyUbController extends UbController 
	{
		
		public function MyUbController() 
		{
			super();
		}
		
		override public function init(param1:Vector.<IUbExpression>):void
		{
			super.init(param1);
			scope.myText = "Text";
		}
	}
}

 

Our controller is ready.

 

Add the controller to the main file "Main.as" via import if the file is in a subdirectory, or as in our case the controller is in the same directory as the other project files, then just declare the controller "MyUbController;" to initialize it and the controller data could be used (if your controller is in the same directory as Main.as, then you do not need to import it), or you can configure your editor so that when assembling the SWF file it also includes unused project files.

Spoiler

package
{
	import flash.events.Event;
	import lesta.api.GameAPI;
	import lesta.api.ModBase;
	import lesta.components.Stage;
	import lesta.constants.ComponentClass;
	import lesta.datahub.DataHub;
	import lesta.datahub.Collection;
	import lesta.datahub.Entity;
	
	public class Main extends ModBase
	{
		public var dataHub:DataHub;
		private var movie:Movie;
		
		public function Main()
		{
			dataHub = new DataHub();
			MyUbController;        // <---  initialise MyUbController
			super();
		}
		
		override public function init():void
		{
			super.init();
			movie = new Movie(dataHub, this.stage, gameAPI);
			addChild(movie);
			movie.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			gameAPI.stage.addChild(this);
		}
		
		public function onAddedToStage(... rest):void
		{
			var entity:Entity = dataHub.createEntity(ComponentClass.stage);
			var stageComponent:Stage = dataHub.createComponent(ComponentClass.stage) as Stage;
			entity.addComponent(stageComponent);
			var collection:Collection = this.dataHub.getCollection(ComponentClass.stage);
			collection.add(entity);
			removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			movie.buildUnboundApplication();
		}
	}
}

 

 

Now add the resulting controller to the XML file, mark up the text block and use the data from the scope for display. Remember that after changing the layout you need to reassemble its SWF, otherwise we will not see the change.

Spoiler

<ui>
	<block className="TestElement">
		<bind name="controller" value="MyUbController"/>
		<block>
			<style>
				<position value="absolute"/>
				<left value="10px"/>
				<top value="10px"/>
				<width value="100px"/>
				<height value="100px"/>
				<backgroundColor value="0xEEEEEEEE"/>
			</style>
			<block type="text">
				<styleClass value="$TextDefault"/>
				<bind name="text" value="myText"/>
			</block>
		</block>
	</block>
</ui>

 

 

Run the game and see the result.

test_window_and_controller.jpg

 

Our simple mod is ready.

 

PS

In order to drag and drop our rendered element and the layout does not break, you need to create the “UserPrefs” settings file in which they will be stored and updated.

 

Share this post


Link to post
Share on other sites
Sign in to follow this  

×