Add sources
2
.gitignore
vendored
@@ -16,3 +16,5 @@ bin-release/
|
|||||||
# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
|
# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
|
||||||
# should NOT be excluded as they contain compiler settings and other important
|
# should NOT be excluded as they contain compiler settings and other important
|
||||||
# information for Eclipse / Flash Builder.
|
# information for Eclipse / Flash Builder.
|
||||||
|
|
||||||
|
.svn/
|
||||||
BIN
libs/Alternativa3D.swc
Normal file
BIN
libs/AlternativaTypes.swc
Normal file
BIN
libs/AlternativaUtils.swc
Normal file
BIN
libs/PropsLib.swc
Normal file
135
src/AlternativaEditor-app.xml
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<application xmlns="http://ns.adobe.com/air/application/1.5">
|
||||||
|
|
||||||
|
<!-- Adobe AIR Application Descriptor File Template.
|
||||||
|
|
||||||
|
Specifies parameters for identifying, installing, and launching AIR applications.
|
||||||
|
|
||||||
|
xmlns - The Adobe AIR namespace: http://ns.adobe.com/air/application/1.1
|
||||||
|
The last segment of the namespace specifies the version
|
||||||
|
of the AIR runtime required for this application to run.
|
||||||
|
|
||||||
|
minimumPatchLevel - The minimum patch level of the AIR runtime required to run
|
||||||
|
the application. Optional.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- The application identifier string, unique to this application. Required. -->
|
||||||
|
<id>AlternativaEditor</id>
|
||||||
|
|
||||||
|
<!-- Used as the filename for the application. Required. -->
|
||||||
|
<filename>AlternativaEditor</filename>
|
||||||
|
|
||||||
|
<!-- The name that is displayed in the AIR application installer.
|
||||||
|
May have multiple values for each language. See samples or xsd schema file. Optional. -->
|
||||||
|
<name>AlternativaEditor</name>
|
||||||
|
|
||||||
|
<!-- An application version designator (such as "v1", "2.5", or "Alpha 1"). Required. -->
|
||||||
|
<version>v1</version>
|
||||||
|
|
||||||
|
<!-- Description, displayed in the AIR application installer.
|
||||||
|
May have multiple values for each language. See samples or xsd schema file. Optional. -->
|
||||||
|
<!-- <description></description> -->
|
||||||
|
|
||||||
|
<!-- Copyright information. Optional -->
|
||||||
|
<!-- <copyright></copyright> -->
|
||||||
|
|
||||||
|
<!-- Settings for the application's initial window. Required. -->
|
||||||
|
<initialWindow>
|
||||||
|
<!-- The main SWF or HTML file of the application. Required. -->
|
||||||
|
<!-- Note: In Flex Builder, the SWF reference is set automatically. -->
|
||||||
|
<content>[This value will be overwritten by Flex Builder in the output app.xml]</content>
|
||||||
|
|
||||||
|
<!-- The title of the main window. Optional. -->
|
||||||
|
<!-- <title></title> -->
|
||||||
|
|
||||||
|
<!-- The type of system chrome to use (either "standard" or "none"). Optional. Default standard. -->
|
||||||
|
<!-- <systemChrome></systemChrome> -->
|
||||||
|
|
||||||
|
<!-- Whether the window is transparent. Only applicable when systemChrome is none. Optional. Default false. -->
|
||||||
|
<!-- <transparent></transparent> -->
|
||||||
|
|
||||||
|
<!-- Whether the window is initially visible. Optional. Default false. -->
|
||||||
|
<!-- <visible></visible> -->
|
||||||
|
|
||||||
|
<!-- Whether the user can minimize the window. Optional. Default true. -->
|
||||||
|
<!-- <minimizable></minimizable> -->
|
||||||
|
|
||||||
|
<!-- Whether the user can maximize the window. Optional. Default true. -->
|
||||||
|
<!-- <maximizable></maximizable> -->
|
||||||
|
|
||||||
|
<!-- Whether the user can resize the window. Optional. Default true. -->
|
||||||
|
<!-- <resizable></resizable> -->
|
||||||
|
|
||||||
|
<!-- The window's initial width. Optional. -->
|
||||||
|
<!-- <width></width> -->
|
||||||
|
|
||||||
|
<!-- The window's initial height. Optional. -->
|
||||||
|
<!-- <height></height> -->
|
||||||
|
|
||||||
|
<!-- The window's initial x position. Optional. -->
|
||||||
|
<!-- <x></x> -->
|
||||||
|
|
||||||
|
<!-- The window's initial y position. Optional. -->
|
||||||
|
<!-- <y></y> -->
|
||||||
|
|
||||||
|
<!-- The window's minimum size, specified as a width/height pair, such as "400 200". Optional. -->
|
||||||
|
<!-- <minSize></minSize> -->
|
||||||
|
|
||||||
|
<!-- The window's initial maximum size, specified as a width/height pair, such as "1600 1200". Optional. -->
|
||||||
|
<!-- <maxSize></maxSize> -->
|
||||||
|
</initialWindow>
|
||||||
|
|
||||||
|
<!-- The subpath of the standard default installation location to use. Optional. -->
|
||||||
|
<!-- <installFolder></installFolder> -->
|
||||||
|
|
||||||
|
<!-- The subpath of the Windows Start/Programs menu to use. Optional. -->
|
||||||
|
<!-- <programMenuFolder></programMenuFolder> -->
|
||||||
|
|
||||||
|
<!-- The icon the system uses for the application. For at least one resolution,
|
||||||
|
specify the path to a PNG file included in the AIR package. Optional. -->
|
||||||
|
<!-- <icon>
|
||||||
|
<image16x16></image16x16>
|
||||||
|
<image32x32></image32x32>
|
||||||
|
<image48x48></image48x48>
|
||||||
|
<image128x128></image128x128>
|
||||||
|
</icon> -->
|
||||||
|
|
||||||
|
<!-- Whether the application handles the update when a user double-clicks an update version
|
||||||
|
of the AIR file (true), or the default AIR application installer handles the update (false).
|
||||||
|
Optional. Default false. -->
|
||||||
|
<!-- <customUpdateUI></customUpdateUI> -->
|
||||||
|
|
||||||
|
<!-- Whether the application can be launched when the user clicks a link in a web browser.
|
||||||
|
Optional. Default false. -->
|
||||||
|
<!-- <allowBrowserInvocation></allowBrowserInvocation> -->
|
||||||
|
|
||||||
|
<!-- Listing of file types for which the application can register. Optional. -->
|
||||||
|
<!-- <fileTypes> -->
|
||||||
|
|
||||||
|
<!-- Defines one file type. Optional. -->
|
||||||
|
<!-- <fileType> -->
|
||||||
|
|
||||||
|
<!-- The name that the system displays for the registered file type. Required. -->
|
||||||
|
<!-- <name></name> -->
|
||||||
|
|
||||||
|
<!-- The extension to register. Required. -->
|
||||||
|
<!-- <extension></extension> -->
|
||||||
|
|
||||||
|
<!-- The description of the file type. Optional. -->
|
||||||
|
<!-- <description></description> -->
|
||||||
|
|
||||||
|
<!-- The MIME type. Optional. -->
|
||||||
|
<!-- <contentType></contentType> -->
|
||||||
|
|
||||||
|
<!-- The icon to display for the file type. Optional. -->
|
||||||
|
<!-- <icon>
|
||||||
|
<image16x16></image16x16>
|
||||||
|
<image32x32></image32x32>
|
||||||
|
<image48x48></image48x48>
|
||||||
|
<image128x128></image128x128>
|
||||||
|
</icon> -->
|
||||||
|
|
||||||
|
<!-- </fileType> -->
|
||||||
|
<!-- </fileTypes> -->
|
||||||
|
|
||||||
|
</application>
|
||||||
784
src/AlternativaEditor.mxml
Normal file
@@ -0,0 +1,784 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*"
|
||||||
|
xmlns:local1="alternativa.editor.*" applicationComplete="onApplicationComplete()">
|
||||||
|
<mx:Script>
|
||||||
|
<![CDATA[
|
||||||
|
import alternativa.editor.events.LevelLoaded;
|
||||||
|
import alternativa.editor.importLevel.BinaryImporter;
|
||||||
|
import alternativa.editor.export.BinaryExporter;
|
||||||
|
import alternativa.editor.importLevel.TanksXmlImporter;
|
||||||
|
import alternativa.editor.prop.Bonus;
|
||||||
|
import alternativa.editor.SceneContainer;
|
||||||
|
import alternativa.editor.prop.Tile;
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.Preview;
|
||||||
|
import alternativa.editor.scene.MainScene;
|
||||||
|
import alternativa.editor.LibraryManager;
|
||||||
|
import alternativa.editor.scene.CursorScene;
|
||||||
|
import mx.events.ResizeEvent;
|
||||||
|
import __AS3__.vec.Vector;
|
||||||
|
import mx.preloaders.DownloadProgressBar;
|
||||||
|
import mx.controls.Text;
|
||||||
|
import mx.controls.Label;
|
||||||
|
import mx.core.Window;
|
||||||
|
import mx.controls.ProgressBar;
|
||||||
|
import mx.core.windowClasses.StatusBar;
|
||||||
|
import mx.events.FlexNativeMenuEvent;
|
||||||
|
import mx.controls.FlexNativeMenu;
|
||||||
|
import alternativa.editor.propslib.PropMesh;
|
||||||
|
import alternativa.editor.propslib.PropObject;
|
||||||
|
import alternativa.editor.propslib.PropData;
|
||||||
|
import alternativa.editor.propslib.PropGroup;
|
||||||
|
import alternativa.editor.propslib.PropsLibrary;
|
||||||
|
import alternativa.engine3d.materials.FillMaterial;
|
||||||
|
import alternativa.engine3d.materials.SpriteTextureMaterial;
|
||||||
|
import alternativa.engine3d.materials.TextureMaterial;
|
||||||
|
import alternativa.types.Texture;
|
||||||
|
import alternativa.engine3d.core.Sprite3D;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import flash.utils.setTimeout;
|
||||||
|
import alternativa.engine3d.core.Vertex;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.engine3d.core.Surface;
|
||||||
|
import gui.events.PropListEvent;
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import mx.events.CloseEvent;
|
||||||
|
import alternativa.utils.KeyboardUtils;
|
||||||
|
import alternativa.engine3d.loaders.Loader3DS;
|
||||||
|
import alternativa.types.Map;
|
||||||
|
|
||||||
|
|
||||||
|
// Превью
|
||||||
|
public static var preview:Preview;
|
||||||
|
// Контейнер, содержащий главную и курсорные сцены
|
||||||
|
// private var sceneContainer:SceneContainer;
|
||||||
|
// Главная сцена
|
||||||
|
private var mainScene:MainScene;
|
||||||
|
// Курсорная сцена
|
||||||
|
private var cursorScene:CursorScene;
|
||||||
|
// Открывающийся файл
|
||||||
|
private var file:File = new File();
|
||||||
|
// Файл, в который сохраняем
|
||||||
|
private var fileForSave:File = new File();
|
||||||
|
// Файл, в который сохраняем xml
|
||||||
|
private var fileForSaveXML:File = new File();
|
||||||
|
// Фильтр
|
||||||
|
private var fileFilter:FileFilter;
|
||||||
|
private var xmlFilter:FileFilter;
|
||||||
|
// Файловый поток
|
||||||
|
private var fileStream:FileStream;
|
||||||
|
// Функция, вызвавшая текущую
|
||||||
|
private var func:Function;
|
||||||
|
// Индикатор несохраненного уровня
|
||||||
|
private var emptyPath:Boolean = true;
|
||||||
|
// Индикатор сохранения в xml
|
||||||
|
private var xmlLevel:Boolean = false;
|
||||||
|
private var progressBar:ProgressBar;
|
||||||
|
// Некоторые элементы меню
|
||||||
|
private var normalItem:NativeMenuItem;
|
||||||
|
private var collisionItem:NativeMenuItem;
|
||||||
|
private var lockItem:NativeMenuItem;
|
||||||
|
private var lockGroupItem:NativeMenuItem;
|
||||||
|
private var unlockItem:NativeMenuItem;
|
||||||
|
private var snapItem:NativeMenuItem;
|
||||||
|
private var showGridItem:NativeMenuItem;
|
||||||
|
// Текстурная панель
|
||||||
|
private var propertyPanel:Panel;
|
||||||
|
//Менеджер библиотек
|
||||||
|
private var libraryManager:LibraryManager;
|
||||||
|
private var tanksXmlImporter:TanksXmlImporter;
|
||||||
|
private var binaryImporter:BinaryImporter;
|
||||||
|
|
||||||
|
|
||||||
|
private function onApplicationComplete():void {
|
||||||
|
|
||||||
|
// Создание превью
|
||||||
|
preview = new Preview();
|
||||||
|
previewPanel.addChild(preview);
|
||||||
|
previewPanel.addEventListener(Event.RESIZE, onPreviewPanelResize);
|
||||||
|
|
||||||
|
// Проплист
|
||||||
|
proplistPanel.addEventListener(PropListEvent.SELECT, onPropSelect);
|
||||||
|
proplistPanel.addEventListener(MouseEvent.CLICK, onPropListClick);
|
||||||
|
|
||||||
|
// Контейнер со сценами
|
||||||
|
// sceneContainer = new SceneContainer();
|
||||||
|
// workspace.addChild(sceneContainer);
|
||||||
|
mainScene = sceneContainer.mainScene;
|
||||||
|
propertyPanel = new Panel();
|
||||||
|
propertyPanel.percentWidth = 100;
|
||||||
|
mainScene.propertyPanel = propertyPanel;
|
||||||
|
cursorScene = sceneContainer.cursorScene;
|
||||||
|
|
||||||
|
|
||||||
|
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
|
||||||
|
|
||||||
|
libraryManager = new LibraryManager();
|
||||||
|
libraryManager.addEventListener(Event.CHANGE, onLibraryManagerChange);
|
||||||
|
comboBox.dataProvider = libraryManager.libraries;
|
||||||
|
|
||||||
|
initFileWork();
|
||||||
|
initMenuItem();
|
||||||
|
|
||||||
|
addEventListener(Event.CLOSING, onClosing);
|
||||||
|
|
||||||
|
initProgressBar();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onClosing(e:Event):void {
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onPreviewPanelResize(e:Event):void {
|
||||||
|
preview.onResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initProgressBar():void {
|
||||||
|
|
||||||
|
progressBar = new ProgressBar();
|
||||||
|
progressBar.labelPlacement = "left";
|
||||||
|
progressBar.indeterminate = true;
|
||||||
|
progressBar.direction = "right";
|
||||||
|
progressBar.width = 200;
|
||||||
|
progressBar.visible = false;
|
||||||
|
|
||||||
|
var stBar:StatusBar = statusBar as StatusBar;
|
||||||
|
stBar.addChild(libraryManager.libraryProgressBar);
|
||||||
|
libraryManager.libraryProgressBar.percentWidth = 100;
|
||||||
|
stBar.addChild(progressBar);
|
||||||
|
progressBar.percentWidth = 100;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initFileWork():void {
|
||||||
|
|
||||||
|
file.addEventListener(Event.SELECT, onOpenFileSelect);
|
||||||
|
fileForSave.addEventListener(Event.SELECT, onSaveFileSelect);
|
||||||
|
fileForSaveXML.addEventListener(Event.SELECT, onSaveFileSelect);
|
||||||
|
|
||||||
|
fileFilter = new FileFilter("Ballance level (*.lvl)", "*.lvl");
|
||||||
|
xmlFilter = new FileFilter("Tanks level(*.xml)", "*.xml");
|
||||||
|
|
||||||
|
fileStream = new FileStream();
|
||||||
|
tanksXmlImporter = new TanksXmlImporter(mainScene, libraryManager);
|
||||||
|
tanksXmlImporter.addEventListener(LevelLoaded.LEVEL_LOADED, endLoadLevel);
|
||||||
|
binaryImporter = new BinaryImporter(mainScene, libraryManager);
|
||||||
|
binaryImporter.addEventListener(LevelLoaded.LEVEL_LOADED, endLoadLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initMenuItem():void {
|
||||||
|
|
||||||
|
var option:NativeMenuItem = menu.nativeMenu.getItemAt(3);
|
||||||
|
var optionSubmenu:NativeMenu = option.submenu;
|
||||||
|
var viewMode:NativeMenuItem = optionSubmenu.getItemAt(1);
|
||||||
|
normalItem = viewMode.submenu.getItemAt(0);
|
||||||
|
normalItem.checked = true;
|
||||||
|
collisionItem = viewMode.submenu.getItemAt(1);
|
||||||
|
|
||||||
|
var multipleProp:NativeMenuItem = optionSubmenu.getItemAt(0);
|
||||||
|
lockItem = multipleProp.submenu.getItemAt(2);
|
||||||
|
lockGroupItem = multipleProp.submenu.getItemAt(1);
|
||||||
|
lockGroupItem.checked = true;
|
||||||
|
unlockItem = multipleProp.submenu.getItemAt(0);
|
||||||
|
|
||||||
|
snapItem = optionSubmenu.getItemAt(2);
|
||||||
|
snapItem.checked = true;
|
||||||
|
|
||||||
|
showGridItem = optionSubmenu.getItemAt(3);
|
||||||
|
showGridItem.checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработка выделения элемента в проплисте.
|
||||||
|
*/
|
||||||
|
private function onPropSelect(e:PropListEvent):void {
|
||||||
|
preview.showProp(e.selectedItem);
|
||||||
|
workspace.setFocus();
|
||||||
|
cursorScene.object = e.selectedItem.clone();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onPropListClick(e:MouseEvent):void {
|
||||||
|
workspace.setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function onLibraryManagerChange(e:Event):void {
|
||||||
|
|
||||||
|
cursorScene.clear();
|
||||||
|
var libraries:Array = libraryManager.libraries;
|
||||||
|
comboBox.selectedItem = libraries[libraries.length - 1];
|
||||||
|
onComboBoxChange(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создает новый уровень.
|
||||||
|
*/
|
||||||
|
private function newLevel():void {
|
||||||
|
mainScene.clear();
|
||||||
|
cursorScene.updateMaterial();
|
||||||
|
emptyPath = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function loadLevel():void {
|
||||||
|
progressBar.label = "Loading level...";
|
||||||
|
progressBar.x = width - progressBar.width;
|
||||||
|
progressBar.visible = true;
|
||||||
|
|
||||||
|
fileStream.open(file, FileMode.READ);
|
||||||
|
mainScene.clear();
|
||||||
|
if (xmlLevel) {
|
||||||
|
tanksXmlImporter.importFromFileStream(fileStream);
|
||||||
|
xmlLevel = false;
|
||||||
|
} else {
|
||||||
|
binaryImporter.importFromFileStream(fileStream);
|
||||||
|
// loadingLevel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var libname:String = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Загрузка уровня.
|
||||||
|
*/
|
||||||
|
private function loadingLevel():void {
|
||||||
|
try {
|
||||||
|
while (fileStream.bytesAvailable) {
|
||||||
|
// Проверка на то, что в предыдущей итерации не загружали библиотеку
|
||||||
|
if (libname == "") {
|
||||||
|
var lib:String = fileStream.readUTF();
|
||||||
|
// Составляем ключ: имя библиотеки + имя группы + имя меша
|
||||||
|
libname = lib + fileStream.readUTF() + fileStream.readUTF();
|
||||||
|
}
|
||||||
|
// Ищем проп по ключу
|
||||||
|
var prop:Prop = libraryManager.nameProp[libname];
|
||||||
|
if (prop) {
|
||||||
|
// Добавляем проп на сцену
|
||||||
|
prop = mainScene.addProp(prop, new Point3D(fileStream.readFloat(), fileStream.readFloat(), fileStream.readFloat()), fileStream.readFloat(), true, false);
|
||||||
|
var free:Boolean = fileStream.readBoolean();
|
||||||
|
if (!free) {
|
||||||
|
// Заполняем карту
|
||||||
|
mainScene.occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
var textureName:String = fileStream.readUTF();
|
||||||
|
// var isMirror:Boolean = fileStream.readBoolean();
|
||||||
|
var tile:Tile = prop as Tile;
|
||||||
|
if (tile) {
|
||||||
|
try {
|
||||||
|
if (textureName != "") {
|
||||||
|
tile.textureName = textureName;
|
||||||
|
}
|
||||||
|
} catch (err:Error) {
|
||||||
|
Alert.show("Tile " + tile.name + ": texture " + textureName + " is not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (isMirror) {
|
||||||
|
// tile.mirrorTexture();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
libname = "";
|
||||||
|
mainScene.calculate();
|
||||||
|
} else {
|
||||||
|
Alert.show("Library '"+ lib + "' is used by the level. Load?", "", Alert.YES|Alert.NO, this, libAlertListener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err:Error) {
|
||||||
|
Alert.show(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
endLoadLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конец загрузки уровня.
|
||||||
|
*/
|
||||||
|
private function endLoadLevel():void {
|
||||||
|
// trace("end");
|
||||||
|
fileStream.close();
|
||||||
|
mainScene.changed = false;
|
||||||
|
emptyPath = false;
|
||||||
|
fileForSave = file.clone();
|
||||||
|
fileForSave.addEventListener(Event.SELECT, onSaveFileSelect);
|
||||||
|
libname = "";
|
||||||
|
progressBar.visible = false;
|
||||||
|
cursorScene.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработка алерта загрузки библиотеки.
|
||||||
|
*/
|
||||||
|
private function libAlertListener(e:CloseEvent):void {
|
||||||
|
|
||||||
|
switch (e.detail) {
|
||||||
|
case Alert.YES:
|
||||||
|
libraryManager.loadLibrary(loadingLevel);
|
||||||
|
break;
|
||||||
|
case Alert.NO:
|
||||||
|
mainScene.clear();
|
||||||
|
endLoadLevel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сохраняет в файл.
|
||||||
|
*/
|
||||||
|
private function saveFile():void {
|
||||||
|
|
||||||
|
if (emptyPath) {
|
||||||
|
// Если не знаем куда сохранить...
|
||||||
|
saveFileAs();
|
||||||
|
} else {
|
||||||
|
onSaveFileSelect(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Сохранить как...
|
||||||
|
*/
|
||||||
|
private function saveFileAs():void {
|
||||||
|
if (xmlLevel) {
|
||||||
|
fileForSaveXML.browseForSave("Export to tank...");
|
||||||
|
} else {
|
||||||
|
fileForSave.browseForSave("Save as...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Открывает файл.
|
||||||
|
*/
|
||||||
|
private function openFile():void {
|
||||||
|
if (xmlLevel) {
|
||||||
|
file.browseForOpen("Открыть", [xmlFilter]);
|
||||||
|
} else {
|
||||||
|
file.browseForOpen("Открыть", [fileFilter]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработка выбора файла.
|
||||||
|
*/
|
||||||
|
private function onOpenFileSelect(e:Event):void {
|
||||||
|
|
||||||
|
func = loadLevel;
|
||||||
|
changesCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onSaveFileSelect(e:Event):void {
|
||||||
|
progressBar.label = "Level is saving...";
|
||||||
|
progressBar.x = width - progressBar.width;
|
||||||
|
progressBar.visible = true;
|
||||||
|
var fileStream:FileStream = new FileStream();
|
||||||
|
|
||||||
|
if (xmlLevel) {
|
||||||
|
fileStream.open(fileForSaveXML, FileMode.WRITE);
|
||||||
|
mainScene.export(MainScene.EXPORT_XML, fileStream);
|
||||||
|
xmlLevel = false;
|
||||||
|
} else {
|
||||||
|
fileStream.open(fileForSave, FileMode.WRITE);
|
||||||
|
mainScene.export(MainScene.EXPORT_BINARY, fileStream);
|
||||||
|
emptyPath = false;
|
||||||
|
mainScene.changed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStream.close();
|
||||||
|
executeFunction();
|
||||||
|
progressBar.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяет были ли изменения в текущем уровне.
|
||||||
|
*/
|
||||||
|
private function changesCheck():void {
|
||||||
|
|
||||||
|
if (mainScene.changed) {
|
||||||
|
Alert.show("Save changes?", "Save level", Alert.YES|Alert.NO|Alert.CANCEL, this, alertClick)
|
||||||
|
} else {
|
||||||
|
executeFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function alertClick(e:CloseEvent):void {
|
||||||
|
|
||||||
|
switch (e.detail) {
|
||||||
|
case Alert.YES:
|
||||||
|
saveFile();
|
||||||
|
break;
|
||||||
|
case Alert.NO:
|
||||||
|
executeFunction();
|
||||||
|
break;
|
||||||
|
case Alert.CANCEL:
|
||||||
|
func = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function executeFunction():void {
|
||||||
|
|
||||||
|
if (func != null) {
|
||||||
|
func();
|
||||||
|
func = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработка нажатия клавиш.
|
||||||
|
*/
|
||||||
|
private function onKeyDown(e:KeyboardEvent):void {
|
||||||
|
|
||||||
|
switch (e.keyCode) {
|
||||||
|
case KeyboardUtils.SPACE:
|
||||||
|
if (normalItem.checked) {
|
||||||
|
|
||||||
|
if (cursorScene.visible) {
|
||||||
|
var prop:Prop = cursorScene.object;
|
||||||
|
if (prop) {
|
||||||
|
sceneContainer.addProp(prop);
|
||||||
|
} else {
|
||||||
|
Alert.show("Select prop for pasting!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (propertyPanel.enabled && showPropertyButton.downMode && mainScene.isTexturePanel) {
|
||||||
|
mainScene.onTexturePanelSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace.setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function snapMode():void {
|
||||||
|
|
||||||
|
if (snapItem.checked) {
|
||||||
|
sceneContainer.snapMode = snapItem.checked = false;
|
||||||
|
} else {
|
||||||
|
sceneContainer.snapMode = snapItem.checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function showGrid():void {
|
||||||
|
|
||||||
|
if (showGridButton.downMode) {
|
||||||
|
mainScene.hideGrid();
|
||||||
|
showGridItem.checked = false;
|
||||||
|
showGridButton.toolTip = "show grid"
|
||||||
|
} else {
|
||||||
|
mainScene.showGrid();
|
||||||
|
showGridItem.checked = true;
|
||||||
|
showGridButton.toolTip = "hide grid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function showPropertyPanel():void {
|
||||||
|
|
||||||
|
if (showPropertyButton.downMode) {
|
||||||
|
vbox.addChild(propertyPanel);
|
||||||
|
} else {
|
||||||
|
vbox.removeChild(propertyPanel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function viewMode():void {
|
||||||
|
if (normalItem.checked) {
|
||||||
|
collisionItem.checked = true;
|
||||||
|
normalItem.checked = false;
|
||||||
|
mainScene.showCollisionBoxes();
|
||||||
|
} else {
|
||||||
|
collisionItem.checked = false;
|
||||||
|
normalItem.checked = true;
|
||||||
|
mainScene.hideCollisionBoxes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onComboBoxChange(e:Event):void {
|
||||||
|
|
||||||
|
proplistPanel.deleteAllProps();
|
||||||
|
if (comboBox.selectedLabel != "") {
|
||||||
|
var props:Array = libraryManager.getLibrary(comboBox.selectedLabel);
|
||||||
|
var len:int = props.length;
|
||||||
|
|
||||||
|
for (var i:int = 0; i < len; i++) {
|
||||||
|
var prop:Prop = props[i];
|
||||||
|
proplistPanel.addProp(prop.group, prop.name, prop.icon, prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onTypeSelectingChange(event:Event):void {
|
||||||
|
|
||||||
|
mainScene.allowSelectingTypes.clear();
|
||||||
|
switch (typeSelecting.selectedIndex) {
|
||||||
|
case 0:
|
||||||
|
mainScene.allowSelectingTypes.add("Tile");
|
||||||
|
mainScene.allowSelectingTypes.add("TileSprite3D");
|
||||||
|
mainScene.allowSelectingTypes.add("Spawn");
|
||||||
|
mainScene.allowSelectingTypes.add("Prop");
|
||||||
|
mainScene.allowSelectingTypes.add("Bonus");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
mainScene.allowSelectingTypes.add("Tile");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mainScene.allowSelectingTypes.add("TileSprite3D");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
mainScene.allowSelectingTypes.add("Spawn");
|
||||||
|
mainScene.allowSelectingTypes.add("Bonus");
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hideSelectedProps():void {
|
||||||
|
mainScene.hideSelectedProps();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function showAll():void {
|
||||||
|
mainScene.showAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
import mx.events.MenuEvent;
|
||||||
|
import mx.controls.Alert;
|
||||||
|
import mx.collections.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Выбор пункта меню.
|
||||||
|
*/
|
||||||
|
private function menuHandler(event:FlexNativeMenuEvent):void {
|
||||||
|
|
||||||
|
switch (event.label) {
|
||||||
|
case "New":
|
||||||
|
func = newLevel;
|
||||||
|
changesCheck();
|
||||||
|
break;
|
||||||
|
case "Open...":
|
||||||
|
openFile();
|
||||||
|
break;
|
||||||
|
case "Save as...":
|
||||||
|
saveFileAs();
|
||||||
|
break;
|
||||||
|
case "Save":
|
||||||
|
saveFile();
|
||||||
|
break;
|
||||||
|
case "Clear":
|
||||||
|
mainScene.clear();
|
||||||
|
mainScene.changed = true;
|
||||||
|
cursorScene.updateMaterial();
|
||||||
|
break;
|
||||||
|
case "Clear and load...":
|
||||||
|
libraryManager.clearAndLoadLibrary();
|
||||||
|
break;
|
||||||
|
case "Load...":
|
||||||
|
libraryManager.loadLibrary();
|
||||||
|
break;
|
||||||
|
case "Unlock":
|
||||||
|
if (!unlockItem.checked) {
|
||||||
|
unlockItem.checked = true;
|
||||||
|
lockGroupItem.checked = false;
|
||||||
|
lockItem.checked = false;
|
||||||
|
sceneContainer.multiplePropMode = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Lock group":
|
||||||
|
if (!lockGroupItem.checked) {
|
||||||
|
lockGroupItem.checked = true;
|
||||||
|
lockItem.checked = false;
|
||||||
|
unlockItem.checked = false;
|
||||||
|
sceneContainer.multiplePropMode = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Lock":
|
||||||
|
if (!lockItem.checked) {
|
||||||
|
lockItem.checked = true;
|
||||||
|
unlockItem.checked = false;
|
||||||
|
lockGroupItem.checked = false;
|
||||||
|
sceneContainer.multiplePropMode = 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Export to Tanks":
|
||||||
|
xmlLevel = true;
|
||||||
|
saveFileAs();
|
||||||
|
break;
|
||||||
|
case "Import from Tanks":
|
||||||
|
xmlLevel = true;
|
||||||
|
openFile();
|
||||||
|
break;
|
||||||
|
case "Normal":
|
||||||
|
case "Collision boxes":
|
||||||
|
viewButton.changeMode();
|
||||||
|
viewMode();
|
||||||
|
break;
|
||||||
|
case "Snap":
|
||||||
|
snapButton.changeMode();
|
||||||
|
snapMode();
|
||||||
|
break;
|
||||||
|
case "Undo":
|
||||||
|
sceneContainer.undo();
|
||||||
|
break;
|
||||||
|
case "Redo":
|
||||||
|
sceneContainer.redo();
|
||||||
|
break;
|
||||||
|
case "Show grid":
|
||||||
|
showGridButton.changeMode();
|
||||||
|
showGrid();
|
||||||
|
break;
|
||||||
|
case "Help":
|
||||||
|
|
||||||
|
Alert.show("Esc - deselect prop\n" +
|
||||||
|
"Delete, C - delete selected prop\n" +
|
||||||
|
"X, Z - rotate selected prop or cursor\n" +
|
||||||
|
"drag, arrows - moving selected prop or cursor\n" +
|
||||||
|
"Shift + drag - copy and moving selected prop\n" +
|
||||||
|
"V + drag, W, S - vertical moving\n" +
|
||||||
|
"Space - paste prop\n" +
|
||||||
|
"Middle - panning\n"+
|
||||||
|
"F - mirror texture\n"+
|
||||||
|
"Q - select conflict props\n"+
|
||||||
|
"Alt + middle - rotate camera", "Help");
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace.setFocus();
|
||||||
|
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</mx:Script>
|
||||||
|
<!--<mx:MenuBar labelField="@label" itemClick="menuHandler(event);"
|
||||||
|
dataProvider="{menuBarCollection}" width="100%" id="menuBar"/> -->
|
||||||
|
|
||||||
|
<mx:menu>
|
||||||
|
<mx:FlexNativeMenu dataProvider="{menuData}"
|
||||||
|
labelField="@label"
|
||||||
|
showRoot="false"
|
||||||
|
keyEquivalentField="@keyEquivalent"
|
||||||
|
itemClick="menuHandler(event);"
|
||||||
|
id="menu"/>
|
||||||
|
</mx:menu>
|
||||||
|
<mx:XML id="menuData">
|
||||||
|
<root>
|
||||||
|
<menuitem label="File">
|
||||||
|
<menuitem label="New" keyEquivalent="n" ctrlKey="true"/>
|
||||||
|
<menuitem label="Open..." keyEquivalent="o" ctrlKey="true"/>
|
||||||
|
<menuitem label="Save" keyEquivalent="s" ctrlKey="true"/>
|
||||||
|
<menuitem label="Save as..." keyEquivalent="s" ctrlKey="true" altKey="true"/>
|
||||||
|
<menuitem type="separator"/>
|
||||||
|
<menuitem label="Export to Tanks" keyEquivalent="t" ctrlKey="true"/>
|
||||||
|
<menuitem label="Import from Tanks" keyEquivalent="i" ctrlKey="true"/>
|
||||||
|
</menuitem>
|
||||||
|
<menuitem label="Edit">
|
||||||
|
<menuitem label="Undo" keyEquivalent="z" ctrlKey="true"/>
|
||||||
|
<menuitem label="Redo" keyEquivalent="y" ctrlKey="true"/>
|
||||||
|
<menuitem label="Clear" keyEquivalent="c" altKey="true" ctrlKey="true"/>
|
||||||
|
</menuitem>
|
||||||
|
<menuitem label="Library">
|
||||||
|
<menuitem label="Clear and load..." keyEquivalent="l" altKey="true" ctrlKey="true"/>
|
||||||
|
<menuitem label="Load..." keyEquivalent="l" ctrlKey="true"/>
|
||||||
|
</menuitem>
|
||||||
|
<menuitem label="Option" name="Option">
|
||||||
|
<menuitem label="Multiple prop">
|
||||||
|
<menuitem label="Unlock" />
|
||||||
|
<menuitem label="Lock group"/>
|
||||||
|
<menuitem label="Lock"/>
|
||||||
|
</menuitem>
|
||||||
|
<menuitem label="View mode">
|
||||||
|
<menuitem label="Normal"/>
|
||||||
|
<menuitem label="Collision boxes"/>
|
||||||
|
</menuitem>
|
||||||
|
<menuitem label="Snap"/>
|
||||||
|
<menuitem label="Show grid" type="check" toggled="true"/>
|
||||||
|
</menuitem>
|
||||||
|
<menuitem label="Help">
|
||||||
|
</menuitem>
|
||||||
|
</root>
|
||||||
|
</mx:XML>
|
||||||
|
|
||||||
|
<mx:ControlBar width="100%" height="26" paddingTop="0" paddingLeft="0" paddingRight="0" paddingBottom="0" >
|
||||||
|
<local:ModeButton width="24" height="24" toolTip="snap mode"
|
||||||
|
upModeIcon="@Embed('icons/editor_snap_icon_on.png')"
|
||||||
|
icon="@Embed('icons/editor_snap_icon_on.png')"
|
||||||
|
downModeIcon="@Embed('icons/editor_snap_icon.png')"
|
||||||
|
id="snapButton"
|
||||||
|
click="snapMode()"/>
|
||||||
|
<local:ModeButton width="24" height="24" toolTip="view mode"
|
||||||
|
upModeIcon="@Embed('icons/editor_boxes_icon_on.png')"
|
||||||
|
icon="@Embed('icons/editor_boxes_icon_on.png')"
|
||||||
|
downModeIcon="@Embed('icons/editor_boxes_icon.png')"
|
||||||
|
click="viewMode()"
|
||||||
|
id="viewButton"/>
|
||||||
|
<local:ModeButton width="24" height="24" toolTip="hide grid" id="showGridButton"
|
||||||
|
upModeIcon="@Embed('icons/editor_grid_icon_on.png')"
|
||||||
|
icon="@Embed('icons/editor_grid_icon_on.png')"
|
||||||
|
downModeIcon="@Embed('icons/editor_grid_icon.png')"
|
||||||
|
click="showGrid()"
|
||||||
|
/>
|
||||||
|
<local:ModeButton width="24" height="24" toolTip="show property panel"
|
||||||
|
upModeIcon="@Embed('icons/editor_textures_icon_on.png')"
|
||||||
|
icon="@Embed('icons/editor_textures_icon_on.png')"
|
||||||
|
downModeIcon="@Embed('icons/editor_textures_icon.png')"
|
||||||
|
id="showPropertyButton"
|
||||||
|
click="showPropertyPanel()"/>
|
||||||
|
<mx:Button width="24" height="24" toolTip="hide selected props"
|
||||||
|
icon="@Embed('icons/icon_hide_selected.png')"
|
||||||
|
id="hideSelectedButton"
|
||||||
|
click="hideSelectedProps()"/>
|
||||||
|
<mx:Button width="24" height="24" toolTip="show all"
|
||||||
|
icon="@Embed('icons/icon_show_all.png')"
|
||||||
|
id="showAllButton"
|
||||||
|
click="showAll()"/>
|
||||||
|
<mx:Label text="Selecting prop:" width="89"/>
|
||||||
|
<mx:ComboBox width="130" change="onTypeSelectingChange(event)" id="typeSelecting">
|
||||||
|
<mx:ArrayCollection>
|
||||||
|
<mx:String>All</mx:String>
|
||||||
|
<mx:String>Tile</mx:String>
|
||||||
|
<mx:String>Sprite</mx:String>
|
||||||
|
<mx:String>Bonus + Spawn</mx:String>
|
||||||
|
</mx:ArrayCollection>
|
||||||
|
</mx:ComboBox>
|
||||||
|
|
||||||
|
</mx:ControlBar>
|
||||||
|
|
||||||
|
<mx:HDividedBox width="100%" height="100%" y="26">
|
||||||
|
<mx:VBox width="100%" height="100%" id="vbox">
|
||||||
|
<mx:Panel title="Workspace" width="100%" height="100%" layout="absolute" id="workspace" horizontalScrollPolicy="off" >
|
||||||
|
<local1:SceneContainer width="100%" height="100%" id="sceneContainer"/>
|
||||||
|
</mx:Panel>
|
||||||
|
</mx:VBox>
|
||||||
|
|
||||||
|
<mx:VDividedBox width="300" height="100%" >
|
||||||
|
<mx:Panel title="Preview" width="100%" height="300" id="previewPanel">
|
||||||
|
</mx:Panel>
|
||||||
|
<mx:VBox width="100%" height="100%">
|
||||||
|
<mx:ComboBox width="100%" change="onComboBoxChange(event)" id="comboBox"/>
|
||||||
|
<local:PropListPanel id="proplistPanel"/>
|
||||||
|
</mx:VBox>
|
||||||
|
</mx:VDividedBox>
|
||||||
|
</mx:HDividedBox>
|
||||||
|
|
||||||
|
</mx:WindowedApplication>
|
||||||
45
src/ImageItemRenderer.as
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package {
|
||||||
|
import flash.display.Bitmap;
|
||||||
|
|
||||||
|
import mx.containers.VBox;
|
||||||
|
import mx.controls.Image;
|
||||||
|
import mx.controls.Label;
|
||||||
|
import mx.core.ScrollPolicy;
|
||||||
|
import mx.events.FlexEvent;
|
||||||
|
|
||||||
|
public class ImageItemRenderer extends VBox {
|
||||||
|
private var img:Image = new Image();
|
||||||
|
private var lbl:Label = new Label();
|
||||||
|
|
||||||
|
|
||||||
|
public function ImageItemRenderer() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.width=52;
|
||||||
|
this.height=82;
|
||||||
|
|
||||||
|
setStyle("horizontalAlign","center");
|
||||||
|
setStyle("verticalGap","0");
|
||||||
|
|
||||||
|
addChild(img);
|
||||||
|
addChild(lbl);
|
||||||
|
|
||||||
|
img.width = img.height = 50;
|
||||||
|
|
||||||
|
verticalScrollPolicy = ScrollPolicy.OFF;
|
||||||
|
horizontalScrollPolicy = ScrollPolicy.OFF;
|
||||||
|
|
||||||
|
updateDisplayList(52,82);
|
||||||
|
|
||||||
|
addEventListener(FlexEvent.DATA_CHANGE, dataChangeHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dataChangeHandler(event:FlexEvent):void {
|
||||||
|
|
||||||
|
img.source = data["image"];
|
||||||
|
lbl.text = data["label"];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/ModeButton.mxml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<mx:Button xmlns:mx="http://www.adobe.com/2006/mxml">
|
||||||
|
<mx:Script>
|
||||||
|
<![CDATA[
|
||||||
|
private var _downMode:Boolean = false;
|
||||||
|
public var downModeIcon:Class;
|
||||||
|
public var upModeIcon:Class;
|
||||||
|
|
||||||
|
public function get downMode():Boolean {
|
||||||
|
return _downMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected function clickHandler(event:MouseEvent):void {
|
||||||
|
changeMode();
|
||||||
|
super.clickHandler(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function changeMode():void {
|
||||||
|
_downMode = !_downMode;
|
||||||
|
if (_downMode) {
|
||||||
|
setStyle("icon", downModeIcon);
|
||||||
|
} else {
|
||||||
|
setStyle("icon", upModeIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</mx:Script>
|
||||||
|
</mx:Button>
|
||||||
97
src/PropListPanel.mxml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<mx:Panel title="Props" xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" focusEnabled="false" >
|
||||||
|
<mx:Script>
|
||||||
|
<![CDATA[
|
||||||
|
import mx.core.ScrollPolicy;
|
||||||
|
import gui.events.PropListEvent;
|
||||||
|
import gui.events.PropListEvent;
|
||||||
|
import mx.events.ListEvent;
|
||||||
|
import mx.core.BitmapAsset;
|
||||||
|
import mx.states.SetStyle;
|
||||||
|
import mx.controls.Label;
|
||||||
|
import mx.controls.Image;
|
||||||
|
import mx.collections.ArrayCollection;
|
||||||
|
import mx.controls.TileList;
|
||||||
|
import mx.controls.Button;
|
||||||
|
import mx.containers.Canvas;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private var _groupName:Array = new Array();
|
||||||
|
private var _group:Array = new Array();
|
||||||
|
private var dataProviders:Array = new Array();
|
||||||
|
public var selectedItem:* = null;
|
||||||
|
|
||||||
|
public function addProp(group:String, name:String, img:DisplayObject, object:*=null):void {
|
||||||
|
var groupIndex:int = _groupName.indexOf(group)
|
||||||
|
var panel:Canvas;
|
||||||
|
var list:TileList;
|
||||||
|
var dp:ArrayCollection;
|
||||||
|
|
||||||
|
if (groupIndex>=0){
|
||||||
|
list = _group[groupIndex] as TileList;
|
||||||
|
dp = dataProviders[groupIndex] as ArrayCollection;
|
||||||
|
} else {
|
||||||
|
panel = new Canvas()
|
||||||
|
panel.label = group;
|
||||||
|
panel.autoLayout=true;
|
||||||
|
panel.percentHeight = panel.percentWidth = 100;
|
||||||
|
panel.horizontalScrollPolicy = ScrollPolicy.OFF;
|
||||||
|
|
||||||
|
accordion.addChild(panel);
|
||||||
|
|
||||||
|
list = new TileList();
|
||||||
|
list.percentHeight = list.percentWidth = 100;
|
||||||
|
list.focusEnabled = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dp = new ArrayCollection();
|
||||||
|
dataProviders.push(dp);
|
||||||
|
|
||||||
|
list.dataProvider = dp;
|
||||||
|
|
||||||
|
list.itemRenderer = new ClassFactory(ImageItemRenderer);
|
||||||
|
list.addEventListener(ListEvent.ITEM_CLICK, changeSelected);
|
||||||
|
|
||||||
|
_group.push(list);
|
||||||
|
_groupName.push(group);
|
||||||
|
|
||||||
|
panel.addChild(list);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var picture:Sprite = new Sprite();
|
||||||
|
picture.addChild(img);
|
||||||
|
dp.addItem({label:name, image:picture, obj:object});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public function deleteAllProps():void {
|
||||||
|
var list:TileList;
|
||||||
|
for (var i:int = 0; i<_group.length; i++) {
|
||||||
|
list = _group[i] as TileList;
|
||||||
|
list.removeEventListener(ListEvent.ITEM_CLICK, changeSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (accordion.numChildren>0){
|
||||||
|
accordion.removeChildAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataProviders = new Array();
|
||||||
|
_group = new Array();
|
||||||
|
_groupName = new Array();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function changeSelected(e:ListEvent):void {
|
||||||
|
dispatchEvent(new PropListEvent(0, e.itemRenderer.data.obj));
|
||||||
|
selectedItem = e.itemRenderer.data.obj;
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</mx:Script>
|
||||||
|
<mx:Accordion width="100%" height="100%" id="accordion">
|
||||||
|
|
||||||
|
</mx:Accordion>
|
||||||
|
</mx:Panel>
|
||||||
|
|
||||||
87
src/TexturePanel.as
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package {
|
||||||
|
import flash.display.Bitmap;
|
||||||
|
import flash.display.Sprite;
|
||||||
|
|
||||||
|
import gui.events.PropListEvent;
|
||||||
|
|
||||||
|
import mx.collections.ArrayCollection;
|
||||||
|
import mx.containers.HBox;
|
||||||
|
import mx.containers.Panel;
|
||||||
|
import mx.controls.Image;
|
||||||
|
import mx.controls.TileList;
|
||||||
|
import mx.core.ClassFactory;
|
||||||
|
import mx.events.ListEvent;
|
||||||
|
|
||||||
|
public class TexturePanel extends Panel {
|
||||||
|
|
||||||
|
private var list:TileList = new TileList();
|
||||||
|
private var thumb:Image = new Image();
|
||||||
|
private static var dp:Array;
|
||||||
|
public var selectedItem:* = null;
|
||||||
|
public var empty:Boolean = true;
|
||||||
|
|
||||||
|
public function TexturePanel() {
|
||||||
|
var hbox:HBox = new HBox();
|
||||||
|
|
||||||
|
super();
|
||||||
|
dp = new Array();
|
||||||
|
this.title = "Textures";
|
||||||
|
// this.minimizable = this.maximizable = false;
|
||||||
|
// this.type = NativeWindowType.UTILITY;
|
||||||
|
// this.alwaysInFront = true;
|
||||||
|
|
||||||
|
addChild(hbox);
|
||||||
|
|
||||||
|
hbox.addChild(list);
|
||||||
|
hbox.addChild(thumb);
|
||||||
|
|
||||||
|
this.percentWidth = 100;
|
||||||
|
this.height = 140;
|
||||||
|
|
||||||
|
hbox.percentHeight = hbox.percentWidth = 100;
|
||||||
|
hbox.setStyle("verticalAlign","middle");
|
||||||
|
|
||||||
|
thumb.width = thumb.height = 100;
|
||||||
|
list.percentWidth = 100;
|
||||||
|
list.height = 80;
|
||||||
|
list.setStyle("verticalAlign","middle");
|
||||||
|
|
||||||
|
|
||||||
|
list.dataProvider = dp;
|
||||||
|
|
||||||
|
list.rowHeight=82;
|
||||||
|
list.columnWidth=52;
|
||||||
|
list.itemRenderer = new ClassFactory(ImageItemRenderer);
|
||||||
|
list.addEventListener(ListEvent.ITEM_CLICK, onSelect);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onSelect(e:ListEvent):void {
|
||||||
|
thumb.source = e.itemRenderer.data.pr;// new Bitmap(bmp);
|
||||||
|
selectedItem = e.itemRenderer.data.id;
|
||||||
|
dispatchEvent(new PropListEvent(0, e.itemRenderer.data.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addItem(id:Object, picture:Bitmap=null, label:String=''):void {
|
||||||
|
var img:Sprite = new Sprite();
|
||||||
|
var pr:Sprite = new Sprite();
|
||||||
|
img.addChild(picture);
|
||||||
|
pr.addChild(new Bitmap(picture.bitmapData));
|
||||||
|
var item:Object = {id:id, image:img, label:label, pr:pr};
|
||||||
|
// dp.addItem(item);
|
||||||
|
dp.push(item);
|
||||||
|
dp.sortOn("label");
|
||||||
|
empty = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteAllProps():void {
|
||||||
|
dp = new Array();
|
||||||
|
list.dataProvider = dp;
|
||||||
|
thumb.source=null;
|
||||||
|
empty = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
116
src/alternativa/editor/KeyMapper.as
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package alternativa.editor {
|
||||||
|
import __AS3__.vec.Vector;
|
||||||
|
|
||||||
|
import flash.events.IEventDispatcher;
|
||||||
|
import flash.events.KeyboardEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class KeyMapper {
|
||||||
|
|
||||||
|
private const MAX_KEYS:int = 31;
|
||||||
|
private var keys:int;
|
||||||
|
private var map:Vector.<int> = new Vector.<int>(MAX_KEYS);
|
||||||
|
private var _dispatcher:IEventDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function KeyMapper(dispatcher:IEventDispatcher = null) {
|
||||||
|
if (dispatcher != null) startListening(dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param keyNum
|
||||||
|
*/
|
||||||
|
private function checkKey(keyNum:int):void {
|
||||||
|
if (keyNum < 0 || keyNum > MAX_KEYS - 1) throw new ArgumentError("keyNum is out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param keyNum
|
||||||
|
* @param keyCode
|
||||||
|
*/
|
||||||
|
public function mapKey(keyNum:int, keyCode:int):void {
|
||||||
|
checkKey(keyNum);
|
||||||
|
map[keyNum] = keyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param keyNum
|
||||||
|
*/
|
||||||
|
public function unmapKey(keyNum:int):void {
|
||||||
|
checkKey(keyNum);
|
||||||
|
map[keyNum] = 0;
|
||||||
|
keys &= ~(1 << keyNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
public function checkEvent(e:KeyboardEvent):void {
|
||||||
|
var idx:int = map.indexOf(e.keyCode);
|
||||||
|
if (idx > -1) e.type == KeyboardEvent.KEY_DOWN ? keys |= (0x1 << idx): keys &= ~(0x1 << idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param keyNum
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function getKeyState(keyNum:int):int {
|
||||||
|
return (keys >> keyNum) & 0x1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param keyNum
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function keyPressed(keyNum:int):Boolean {
|
||||||
|
return getKeyState(keyNum) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param dispatcher
|
||||||
|
*/
|
||||||
|
public function startListening(dispatcher:IEventDispatcher):void {
|
||||||
|
if (_dispatcher == dispatcher) return;
|
||||||
|
if (_dispatcher != null) unregisterListeners();
|
||||||
|
_dispatcher = dispatcher;
|
||||||
|
if (_dispatcher != null) registerListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function stopListening():void {
|
||||||
|
if (_dispatcher != null) unregisterListeners();
|
||||||
|
_dispatcher = null;
|
||||||
|
keys = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function registerListeners():void {
|
||||||
|
_dispatcher.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||||
|
_dispatcher.addEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function unregisterListeners():void {
|
||||||
|
_dispatcher.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||||
|
_dispatcher.removeEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function onKey(e:KeyboardEvent):void {
|
||||||
|
checkEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
203
src/alternativa/editor/LibraryManager.as
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
package alternativa.editor {
|
||||||
|
import __AS3__.vec.Vector;
|
||||||
|
|
||||||
|
import alternativa.editor.prop.Bonus;
|
||||||
|
import alternativa.editor.prop.Flag;
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.prop.Spawn;
|
||||||
|
import alternativa.editor.prop.Tile;
|
||||||
|
import alternativa.editor.prop.TileSprite3D;
|
||||||
|
import alternativa.editor.propslib.PropData;
|
||||||
|
import alternativa.editor.propslib.PropGroup;
|
||||||
|
import alternativa.editor.propslib.PropMesh;
|
||||||
|
import alternativa.editor.propslib.PropObject;
|
||||||
|
import alternativa.editor.propslib.PropsLibrary;
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.core.Sprite3D;
|
||||||
|
import alternativa.types.Map;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
|
||||||
|
import flash.events.Event;
|
||||||
|
import flash.events.EventDispatcher;
|
||||||
|
import flash.filesystem.File;
|
||||||
|
|
||||||
|
import mx.controls.Alert;
|
||||||
|
import mx.controls.ProgressBar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author danilova
|
||||||
|
*/
|
||||||
|
public class LibraryManager extends EventDispatcher {
|
||||||
|
|
||||||
|
private static const GRP_SPAWN_POINTS:String = "Spawn Points";
|
||||||
|
private static const GRP_BONUS_REGIONS:String = "Bonus Regions";
|
||||||
|
private static const GRP_FLAGS:String = "Flags";
|
||||||
|
|
||||||
|
// Файл библиотеки
|
||||||
|
private var fileLoadLibrary:File = new File();
|
||||||
|
// Индикатор загрузки библиотеки с очисткой текущих
|
||||||
|
private var clearLibrary:Boolean = false;
|
||||||
|
//
|
||||||
|
public var libraryProgressBar:ProgressBar;
|
||||||
|
//
|
||||||
|
public var libraries:Array = [];
|
||||||
|
//
|
||||||
|
private var propsLibraries:Map = new Map();
|
||||||
|
//
|
||||||
|
private var nextFunction:Function;
|
||||||
|
// Имя пропа (библа + группа + имя меша) -> проп
|
||||||
|
public var nameProp:Map = new Map();
|
||||||
|
|
||||||
|
private var libraryCount:int;
|
||||||
|
private var index:int = 0;
|
||||||
|
|
||||||
|
public function LibraryManager() {
|
||||||
|
|
||||||
|
fileLoadLibrary.addEventListener(Event.SELECT, onSelect);
|
||||||
|
libraryProgressBar = new ProgressBar();
|
||||||
|
libraryProgressBar.labelPlacement = "left";
|
||||||
|
libraryProgressBar.indeterminate = true;
|
||||||
|
libraryProgressBar.label = "Loading library...";
|
||||||
|
libraryProgressBar.direction = "right";
|
||||||
|
libraryProgressBar.width = 200;
|
||||||
|
libraryProgressBar.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadLibrary(nextFunction:Function = null):void {
|
||||||
|
this.nextFunction = nextFunction;
|
||||||
|
fileLoadLibrary.browseForDirectory("Load library");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clearAndLoadLibrary():void {
|
||||||
|
clearLibrary = true;
|
||||||
|
loadLibrary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onSelect(e:Event):void {
|
||||||
|
|
||||||
|
libraryProgressBar.visible = true;
|
||||||
|
|
||||||
|
var propsLibrary:PropsLibrary;
|
||||||
|
var list:Array = fileLoadLibrary.getDirectoryListing();
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if ((list[0] as File).isDirectory) {
|
||||||
|
libraryCount = list.length;
|
||||||
|
for (var i:int = 0; i < libraryCount; i++) {
|
||||||
|
var file:File = list[i];
|
||||||
|
if (file.isDirectory) {
|
||||||
|
propsLibrary = new PropsLibrary();
|
||||||
|
propsLibrary.addEventListener(Event.COMPLETE, onLoadingComplete);
|
||||||
|
propsLibrary.load(file.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
libraryCount = 1;
|
||||||
|
propsLibrary = new PropsLibrary();
|
||||||
|
propsLibrary.addEventListener(Event.COMPLETE, onLoadingComplete);
|
||||||
|
propsLibrary.load(fileLoadLibrary.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearLibrary) {
|
||||||
|
clearLibrary = false;
|
||||||
|
// cursorScene.clear();
|
||||||
|
libraries.length = 0;
|
||||||
|
propsLibraries.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private function onLoadingComplete(e:Event):void {
|
||||||
|
try {
|
||||||
|
index++;
|
||||||
|
var propslibrary:PropsLibrary = e.target as PropsLibrary;
|
||||||
|
var library:String = propslibrary.name;
|
||||||
|
libraries.push(library);
|
||||||
|
var libraryProps:Array = [];
|
||||||
|
var groups:Vector.<PropGroup> = propslibrary.rootGroup.groups;
|
||||||
|
var len:int = groups.length;
|
||||||
|
for (var i:int = 0; i < len; i++) {
|
||||||
|
var group:PropGroup = groups[i];
|
||||||
|
// Получаем имя группы
|
||||||
|
var groupName:String = group.name;
|
||||||
|
var props:Vector.<PropData> = group.props;
|
||||||
|
var propsLen:int = props.length;
|
||||||
|
for (var j:int = 0; j < propsLen; j++) {
|
||||||
|
var propData:PropData = props[j];
|
||||||
|
var propObject:PropObject = propData.statelessData.object;
|
||||||
|
var name:String = propData.name;
|
||||||
|
if (propObject) {
|
||||||
|
var object:Object3D = propObject.object3d;
|
||||||
|
object.coords = new Point3D();
|
||||||
|
var prop:Prop;
|
||||||
|
if (object is Mesh) {
|
||||||
|
switch (groupName) {
|
||||||
|
case GRP_SPAWN_POINTS:
|
||||||
|
prop = new Spawn(object, library, groupName);
|
||||||
|
break;
|
||||||
|
case GRP_BONUS_REGIONS:
|
||||||
|
prop = new Bonus(object, library, groupName);
|
||||||
|
break;
|
||||||
|
case GRP_FLAGS:
|
||||||
|
prop = new Flag(object, library, groupName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var tile:Tile = new Tile(object, library, groupName);
|
||||||
|
prop = tile;
|
||||||
|
tile.bitmaps = (propObject as PropMesh).bitmaps;
|
||||||
|
// Установка текстуры по умолчанию
|
||||||
|
// TODO: Желательно, чтобы устанавливалась первая указанная в XML текстура
|
||||||
|
if (tile.bitmaps != null) {
|
||||||
|
for (var tName:String in tile.bitmaps) {
|
||||||
|
tile.textureName = tName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (object is Sprite3D) {
|
||||||
|
prop = new TileSprite3D(object as Sprite3D, library, groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
prop.name = name;
|
||||||
|
|
||||||
|
// Получаем иконку пропа
|
||||||
|
prop.icon = AlternativaEditor.preview.getPropIcon(prop);
|
||||||
|
libraryProps.push(prop);
|
||||||
|
nameProp.add(library + groupName + name, prop);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
propsLibraries.add(library, libraryProps);
|
||||||
|
libraryProgressBar.visible = false;
|
||||||
|
if (index == libraryCount) {
|
||||||
|
dispatchEvent(new Event(Event.CHANGE));
|
||||||
|
if (nextFunction != null) {
|
||||||
|
// Если загрузили библиотеку в процессе загрузки уровня, продолжаем загружать уровень
|
||||||
|
nextFunction();
|
||||||
|
nextFunction = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (err:Error) {
|
||||||
|
Alert.show(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLibrary(libraryName:String):Array {
|
||||||
|
return propsLibraries[libraryName];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
212
src/alternativa/editor/Preview.as
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
package alternativa.editor {
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.prop.TileSprite3D;
|
||||||
|
import alternativa.engine3d.core.Camera3D;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.core.Scene3D;
|
||||||
|
import alternativa.engine3d.core.Vertex;
|
||||||
|
import alternativa.engine3d.display.View;
|
||||||
|
import alternativa.types.Map;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.utils.MathUtils;
|
||||||
|
|
||||||
|
import flash.display.Bitmap;
|
||||||
|
import flash.display.BitmapData;
|
||||||
|
import flash.events.Event;
|
||||||
|
import flash.geom.Matrix;
|
||||||
|
import flash.geom.Point;
|
||||||
|
|
||||||
|
import mx.core.UIComponent;
|
||||||
|
|
||||||
|
|
||||||
|
public class Preview extends UIComponent {
|
||||||
|
|
||||||
|
private var sceneProp:Scene3D;
|
||||||
|
private var cameraProp:Camera3D;
|
||||||
|
private var cameraPropContainer:Object3D;
|
||||||
|
private var viewProp:View;
|
||||||
|
private var matrix:Matrix = new Matrix();
|
||||||
|
// Мап проп -> оптимальное расстояние от камеры до пропа
|
||||||
|
private var propDistance:Map = new Map();
|
||||||
|
private var halfFov:Number;
|
||||||
|
private const iconWidth:Number = 50;
|
||||||
|
private const sqrt2:Number = Math.sqrt(2);
|
||||||
|
private const sqrt3:Number = Math.sqrt(3);
|
||||||
|
|
||||||
|
public function Preview() {
|
||||||
|
super();
|
||||||
|
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onAddedToStage(e:Event):void {
|
||||||
|
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
|
||||||
|
initScene();
|
||||||
|
addEventListener(Event.ENTER_FRAME, onEnterFrame);
|
||||||
|
addEventListener(Event.RESIZE, onResize);
|
||||||
|
onResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация сцены предосмотра пропа.
|
||||||
|
*/
|
||||||
|
private function initScene(): void {
|
||||||
|
|
||||||
|
// Создание сцены
|
||||||
|
sceneProp = new Scene3D();
|
||||||
|
sceneProp.root = new Object3D();
|
||||||
|
|
||||||
|
// Добавление камеры и области вывода
|
||||||
|
cameraProp = new Camera3D();
|
||||||
|
cameraProp.rotationX = -MathUtils.DEG90 - MathUtils.DEG30;
|
||||||
|
cameraPropContainer = new Object3D();
|
||||||
|
cameraPropContainer.addChild(cameraProp);
|
||||||
|
cameraProp.coords = new Point3D(0, -100, 40);
|
||||||
|
sceneProp.root.addChild(cameraPropContainer);
|
||||||
|
|
||||||
|
viewProp = new View(cameraProp);
|
||||||
|
addChild(viewProp);
|
||||||
|
viewProp.graphics.beginFill(0xFFFFFF);
|
||||||
|
viewProp.graphics.drawRect(0, 0, 1, 1);
|
||||||
|
viewProp.graphics.endFill();
|
||||||
|
|
||||||
|
halfFov = cameraProp.fov/2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onEnterFrame(e:Event):void {
|
||||||
|
|
||||||
|
cameraPropContainer.rotationZ += MathUtils.DEG1;
|
||||||
|
sceneProp.calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Находит расстояние, на котором должна быть камера, чтобы проп было хорошо видно.
|
||||||
|
* @param prop проп
|
||||||
|
* @return расстояние
|
||||||
|
*/
|
||||||
|
private function analyzeProp(prop:Prop):Point {
|
||||||
|
|
||||||
|
var point:Point;
|
||||||
|
var maxSqrDistance:Number = 0;
|
||||||
|
var maxZ:Number = 0;
|
||||||
|
var h:Number;
|
||||||
|
|
||||||
|
var tileSprite3D:TileSprite3D = prop as TileSprite3D;
|
||||||
|
if (tileSprite3D) {
|
||||||
|
|
||||||
|
var bitmapData:BitmapData = prop.bitmapData;
|
||||||
|
maxSqrDistance = tileSprite3D.scale*1.5*Math.max(bitmapData.width, bitmapData.height);
|
||||||
|
maxZ = bitmapData.height;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var vertices:Array = prop.vertices.toArray(true);
|
||||||
|
var len:int = vertices.length;
|
||||||
|
var deltaZ:Number;
|
||||||
|
var deltaY:Number;
|
||||||
|
var deltaX:Number;
|
||||||
|
var sqrDistance:Number;
|
||||||
|
for (var i:int = 0; i < len; i++) {
|
||||||
|
var vertex1:Point3D = (vertices[i] as Vertex).coords;
|
||||||
|
deltaX = vertex1.x - prop.x;
|
||||||
|
deltaY = vertex1.y - prop.y;
|
||||||
|
deltaZ = vertex1.z - prop.z;
|
||||||
|
sqrDistance = deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ;
|
||||||
|
if (sqrDistance > maxSqrDistance) {
|
||||||
|
maxSqrDistance = sqrDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var j:int = i + 1; j < len; j++) {
|
||||||
|
var vertex2:Point3D = (vertices[j] as Vertex).coords;
|
||||||
|
deltaZ = vertex1.z - vertex2.z;
|
||||||
|
deltaZ = deltaZ < 0 ? -deltaZ : deltaZ;
|
||||||
|
|
||||||
|
if (deltaZ > maxZ) {
|
||||||
|
maxZ = deltaZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxSqrDistance = 2*Math.sqrt(maxSqrDistance);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
h = sqrt2*maxSqrDistance/(2*Math.tan(halfFov)) + maxSqrDistance/2;
|
||||||
|
point = new Point(h, maxZ/2);
|
||||||
|
|
||||||
|
propDistance.add(prop, point);
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получение иконки для пропа
|
||||||
|
* @param prop
|
||||||
|
* @return иконка
|
||||||
|
*/
|
||||||
|
public function getPropIcon(prop:Prop):Bitmap {
|
||||||
|
|
||||||
|
clearPropScene();
|
||||||
|
analyzeProp(prop);
|
||||||
|
setCameraCoords(prop);
|
||||||
|
sceneProp.root.addChild(prop);
|
||||||
|
sceneProp.calculate();
|
||||||
|
var bitmapData:BitmapData = new BitmapData(iconWidth, iconWidth, false, 0x0);
|
||||||
|
matrix.a = iconWidth/viewProp.width;
|
||||||
|
matrix.d = matrix.a;
|
||||||
|
bitmapData.draw(viewProp, matrix);
|
||||||
|
var result:Bitmap = new Bitmap(bitmapData);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Установка координат камеры.
|
||||||
|
* @param prop
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function setCameraCoords(prop:Object3D):void {
|
||||||
|
|
||||||
|
var yzDistance:Point = propDistance[prop];
|
||||||
|
if (yzDistance) {
|
||||||
|
cameraProp.y = -yzDistance.x;
|
||||||
|
cameraProp.z = yzDistance.y/2 + yzDistance.x/sqrt3;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очистка сцены предосмотра.
|
||||||
|
*/
|
||||||
|
private function clearPropScene():void {
|
||||||
|
|
||||||
|
for (var child:* in sceneProp.root.children) {
|
||||||
|
var prop:Prop = child as Prop;
|
||||||
|
if (prop) {
|
||||||
|
sceneProp.root.removeChild(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showProp(prop:Prop):void {
|
||||||
|
|
||||||
|
clearPropScene();
|
||||||
|
setCameraCoords(prop);
|
||||||
|
sceneProp.root.addChild(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Корректировка размеров и положения объектов при изменении окна плеера.
|
||||||
|
*/
|
||||||
|
public function onResize(e:Event = null):void {
|
||||||
|
|
||||||
|
viewProp.width = parent.width;
|
||||||
|
viewProp.height = parent.height;
|
||||||
|
sceneProp.calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
696
src/alternativa/editor/SceneContainer.as
Normal file
@@ -0,0 +1,696 @@
|
|||||||
|
package alternativa.editor {
|
||||||
|
import alternativa.editor.eventjournal.EventJournal;
|
||||||
|
import alternativa.editor.eventjournal.EventJournalItem;
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.scene.CursorScene;
|
||||||
|
import alternativa.editor.scene.EditorScene;
|
||||||
|
import alternativa.editor.scene.MainScene;
|
||||||
|
import alternativa.editor.scene.OccupyMap;
|
||||||
|
import alternativa.engine3d.events.MouseEvent3D;
|
||||||
|
import alternativa.types.Matrix3D;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.types.Set;
|
||||||
|
import alternativa.utils.KeyboardUtils;
|
||||||
|
|
||||||
|
import flash.display.Graphics;
|
||||||
|
import flash.display.Shape;
|
||||||
|
import flash.events.Event;
|
||||||
|
import flash.events.KeyboardEvent;
|
||||||
|
import flash.events.MouseEvent;
|
||||||
|
import flash.geom.Point;
|
||||||
|
import flash.ui.Keyboard;
|
||||||
|
import flash.utils.setTimeout;
|
||||||
|
|
||||||
|
import mx.controls.Alert;
|
||||||
|
import mx.core.UIComponent;
|
||||||
|
import mx.events.CloseEvent;
|
||||||
|
|
||||||
|
public class SceneContainer extends UIComponent {
|
||||||
|
// Сцена с курсором
|
||||||
|
public var cursorScene:CursorScene;
|
||||||
|
// Сцена уровня
|
||||||
|
public var mainScene:MainScene;
|
||||||
|
// Индикатор вертикального движения
|
||||||
|
private var verticalMoving:Boolean = false;
|
||||||
|
//
|
||||||
|
private var copy:Boolean = false;
|
||||||
|
//
|
||||||
|
private var mouseDown:Boolean;
|
||||||
|
// Журнал событий
|
||||||
|
private var eventJournal:EventJournal;
|
||||||
|
// Индикатор режима вставки пропов
|
||||||
|
public var multiplePropMode:int = 1;
|
||||||
|
//
|
||||||
|
private var cameraTransformation:Matrix3D;
|
||||||
|
//
|
||||||
|
private var _snapMode:Boolean = true;
|
||||||
|
|
||||||
|
private var cameraDistance:Number;
|
||||||
|
|
||||||
|
private var shape:Shape;
|
||||||
|
|
||||||
|
private var keyMapper:KeyMapper;
|
||||||
|
|
||||||
|
public function SceneContainer() {
|
||||||
|
super();
|
||||||
|
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
|
||||||
|
initKeyMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initKeyMapper():void {
|
||||||
|
keyMapper = new KeyMapper();
|
||||||
|
keyMapper.mapKey(0, KeyboardUtils.N);
|
||||||
|
keyMapper.mapKey(1, KeyboardUtils.M);
|
||||||
|
keyMapper.mapKey(2, Keyboard.NUMPAD_4);
|
||||||
|
keyMapper.mapKey(3, Keyboard.NUMPAD_6);
|
||||||
|
keyMapper.mapKey(4, Keyboard.NUMPAD_8);
|
||||||
|
keyMapper.mapKey(5, Keyboard.NUMPAD_2);
|
||||||
|
keyMapper.mapKey(6, Keyboard.NUMPAD_9);
|
||||||
|
keyMapper.mapKey(7, Keyboard.NUMPAD_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onAddedToStage(e:Event):void {
|
||||||
|
|
||||||
|
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
|
||||||
|
|
||||||
|
keyMapper.startListening(stage);
|
||||||
|
|
||||||
|
cursorScene = new CursorScene(stage);
|
||||||
|
mainScene = new MainScene();
|
||||||
|
cursorScene.occupyMap = mainScene.occupyMap;
|
||||||
|
|
||||||
|
addChild(mainScene.view);
|
||||||
|
addChild(cursorScene.view);
|
||||||
|
shape = new Shape();
|
||||||
|
addChild(shape);
|
||||||
|
|
||||||
|
initListeners();
|
||||||
|
eventJournal = new EventJournal();
|
||||||
|
|
||||||
|
var cameraCoords:Point3D = cursorScene.camera.coords;
|
||||||
|
cameraDistance = Math.sqrt(cameraCoords.x*cameraCoords.x + cameraCoords.y*cameraCoords.y + cameraCoords.z*cameraCoords.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Установка обработчиков.
|
||||||
|
*/
|
||||||
|
protected function initListeners():void {
|
||||||
|
|
||||||
|
addEventListener(Event.ENTER_FRAME, onEnterFrame);
|
||||||
|
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
|
||||||
|
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
|
||||||
|
parent.addEventListener(Event.RESIZE, onResize);
|
||||||
|
parent.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||||
|
parent.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||||
|
parent.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
|
||||||
|
parent.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
||||||
|
parent.addEventListener(MouseEvent.MIDDLE_MOUSE_DOWN, onMiddleMouseDown);
|
||||||
|
parent.addEventListener(MouseEvent.MIDDLE_MOUSE_UP, onMiddleMouseUp);
|
||||||
|
parent.addEventListener(MouseEvent.ROLL_OUT, onMouseOut);
|
||||||
|
onResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Точка клика
|
||||||
|
private var mouseDownPoint:Point = new Point();
|
||||||
|
// Координаты выделенного пропа на момент выделения
|
||||||
|
private var startSelCoords:Point3D;
|
||||||
|
|
||||||
|
private function onMouseDown(e:MouseEvent):void {
|
||||||
|
if (mainScene.propMouseDown) {
|
||||||
|
|
||||||
|
var selProp:Prop = mainScene.selectedProp;
|
||||||
|
if (selProp) {
|
||||||
|
// Запоминаем исходные координаты
|
||||||
|
startSelCoords = selProp.coords;
|
||||||
|
cursorScene.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseDown = true;
|
||||||
|
mouseDownPoint.x = mainScene.view.mouseX;
|
||||||
|
mouseDownPoint.y = mainScene.view.mouseY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private function onMouseUp(e:MouseEvent):void {
|
||||||
|
var selProp:Prop = mainScene.selectedProp;
|
||||||
|
var selectedProps:Set = mainScene.selectedProps;
|
||||||
|
|
||||||
|
if (mainScene.propMouseDown) {
|
||||||
|
if (selProp) {
|
||||||
|
var move:Boolean = false;
|
||||||
|
if (copy) {
|
||||||
|
// Заносим в журнал
|
||||||
|
eventJournal.addEvent(EventJournal.COPY, selectedProps.clone());
|
||||||
|
} else {
|
||||||
|
// Проверка на перемещение
|
||||||
|
if (!startSelCoords.equals(selProp.coords)) {
|
||||||
|
var delta:Point3D = selProp.coords;
|
||||||
|
delta.difference(delta, startSelCoords);
|
||||||
|
// Заносим в журнал
|
||||||
|
eventJournal.addEvent(EventJournal.MOVE, selectedProps.clone(), delta);
|
||||||
|
move = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_snapMode && (copy || move)) {
|
||||||
|
checkConflict();
|
||||||
|
}
|
||||||
|
|
||||||
|
copy = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
mainScene.propMouseDown = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Проверка на клик
|
||||||
|
var deltaX:Number = mouseDownPoint.x - mainScene.view.mouseX;
|
||||||
|
var deltaY:Number = mouseDownPoint.y - mainScene.view.mouseY;
|
||||||
|
deltaX = deltaX < 0 ? -deltaX : deltaX;
|
||||||
|
deltaY = deltaY < 0 ? -deltaY : deltaY;
|
||||||
|
if ((deltaX < 3) && (deltaY < 3)) {
|
||||||
|
// Перемещаем курсор туда, куда кликнули мышью
|
||||||
|
if (propDown) {
|
||||||
|
if (cursorScene.object) {
|
||||||
|
cursorScene.object.z = clickZ;
|
||||||
|
}
|
||||||
|
propDown = false;
|
||||||
|
}
|
||||||
|
cursorScene.moveCursorByMouse();
|
||||||
|
|
||||||
|
if (!cursorScene.visible) {
|
||||||
|
mainScene.deselectProps();
|
||||||
|
cursorScene.visible = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// выделяем/снимаем выделение с пропов под прямоугольником
|
||||||
|
if (e.shiftKey) {
|
||||||
|
for (var p:* in rectProps) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
if (e.altKey) {
|
||||||
|
if (selectedProps.has(prop)) {
|
||||||
|
mainScene.deselectProp(prop);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!selectedProps.has(prop)) {
|
||||||
|
mainScene.selectProp(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseDown = false;
|
||||||
|
shape.graphics.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function alertConflict(e:CloseEvent):void {
|
||||||
|
if (e.detail == Alert.NO) {
|
||||||
|
// Отменяем
|
||||||
|
mainScene.undo(eventJournal.undo(true));
|
||||||
|
}
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkConflict():void {
|
||||||
|
|
||||||
|
if (multiplePropMode != 0) {
|
||||||
|
// Ищем пересекающие пропы
|
||||||
|
var selectedProps:Set = mainScene.selectedProps;
|
||||||
|
var occupyMap:OccupyMap = mainScene.occupyMap;
|
||||||
|
for (var p:* in selectedProps) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
if ((multiplePropMode == 2 && occupyMap.isConflict(prop)) || (multiplePropMode == 1 && occupyMap.isConflictGroup(prop))) {
|
||||||
|
Alert.show("This location is occupied. Continue?", "", Alert.YES|Alert.NO, this, alertConflict, null, Alert.YES);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var prevMoveX:Number;
|
||||||
|
private var prevMoveY:Number;
|
||||||
|
private var rectProps:Set = new Set();
|
||||||
|
|
||||||
|
private function onMouseMove(e:MouseEvent):void {
|
||||||
|
var p:*;
|
||||||
|
var prop:Prop;
|
||||||
|
var selProp:Prop = mainScene.selectedProp;
|
||||||
|
var selectedProps:Set = mainScene.selectedProps;
|
||||||
|
|
||||||
|
if (mainScene.propMouseDown && selProp) {
|
||||||
|
// Проверка на необходимость копирования
|
||||||
|
if (e.shiftKey && !copy) {
|
||||||
|
// Проверка на перемещение
|
||||||
|
if (!startSelCoords.equals(selProp.coords)) {
|
||||||
|
var delta:Point3D = selProp.coords;
|
||||||
|
delta.difference(delta, startSelCoords);
|
||||||
|
// Заносим в журнал
|
||||||
|
eventJournal.addEvent(EventJournal.MOVE, selectedProps.clone(), delta);
|
||||||
|
}
|
||||||
|
// Копируем пропы
|
||||||
|
var copyProps:Set = new Set();
|
||||||
|
for (p in selectedProps) {
|
||||||
|
prop = p as Prop;
|
||||||
|
var copyProp:Prop = mainScene.addProp(prop, prop.coords, prop.rotationZ);
|
||||||
|
if (prop == selProp) {
|
||||||
|
selProp = copyProp;
|
||||||
|
}
|
||||||
|
copyProps.add(copyProp);
|
||||||
|
}
|
||||||
|
// Выделяем копии
|
||||||
|
mainScene.selectProps(copyProps);
|
||||||
|
mainScene.selectedProp = selProp;
|
||||||
|
// Запоминаем исходные координаты
|
||||||
|
startSelCoords = selProp.coords;
|
||||||
|
copy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Перемещаем проп
|
||||||
|
mainScene.moveSelectedProps(verticalMoving);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Проверка на паннинг
|
||||||
|
if (middleDown) {
|
||||||
|
var matrix:Matrix3D = cursorScene.camera.transformation;
|
||||||
|
var axisX:Point3D = new Point3D(matrix.a, matrix.e, matrix.i);
|
||||||
|
var axisY:Point3D = new Point3D(matrix.b, matrix.f, matrix.j);
|
||||||
|
axisX.multiply(10*(prevMoveX - e.stageX));
|
||||||
|
axisY.multiply(10*(prevMoveY - e.stageY));
|
||||||
|
|
||||||
|
var coords:Point3D = new Point3D(matrix.d, matrix.h, matrix.l);
|
||||||
|
coords.add(axisX);
|
||||||
|
coords.add(axisY);
|
||||||
|
cursorScene.cameraController.coords = cursorScene.container.globalToLocal(coords);
|
||||||
|
|
||||||
|
} else if (mouseDown) {
|
||||||
|
var dx:Number = mouseDownPoint.x - mainScene.view.mouseX;
|
||||||
|
dx = dx > 0 ? dx : -dx;
|
||||||
|
var dy:Number = mouseDownPoint.y - mainScene.view.mouseY;
|
||||||
|
dy = dy > 0 ? dy : -dy;
|
||||||
|
if (dx > 3 && dy > 3) {
|
||||||
|
// Отрисовка прямоугольника выделения
|
||||||
|
var point:Point = new Point(Math.min(mainScene.view.mouseX, mouseDownPoint.x), Math.min(mainScene.view.mouseY, mouseDownPoint.y));
|
||||||
|
var gfx:Graphics = shape.graphics;
|
||||||
|
gfx.clear();
|
||||||
|
gfx.lineStyle(0, 0x000000);
|
||||||
|
gfx.moveTo(point.x, point.y);
|
||||||
|
|
||||||
|
gfx.lineTo(point.x + dx, point.y);
|
||||||
|
gfx.lineTo(point.x + dx, point.y + dy);
|
||||||
|
gfx.lineTo(point.x, point.y + dy);
|
||||||
|
gfx.lineTo(point.x, point.y);
|
||||||
|
|
||||||
|
// Выделяем пропы, попавшие под прямоугольник
|
||||||
|
if (e.shiftKey) {
|
||||||
|
var prevRectProps:Set = rectProps.clone();
|
||||||
|
if (e.altKey) {
|
||||||
|
// Снимаем выделение
|
||||||
|
rectProps = mainScene.getPropsUnderRect(point, dx, dy, false);
|
||||||
|
for (p in prevRectProps) {
|
||||||
|
prop = p;
|
||||||
|
if (!rectProps.has(prop) && selectedProps.has(prop)) {
|
||||||
|
prop.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Выделяем
|
||||||
|
rectProps = mainScene.getPropsUnderRect(point, dx, dy, true);
|
||||||
|
for (p in prevRectProps) {
|
||||||
|
prop = p;
|
||||||
|
if (!rectProps.has(prop) && !selectedProps.has(prop)) {
|
||||||
|
prop.deselect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
mainScene.selectProps(mainScene.getPropsUnderRect(point, dx, dy, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorScene.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prevMoveX = e.stageX;
|
||||||
|
prevMoveY = e.stageY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private var cameraPoint:Point3D = new Point3D(0, 0, 1000);
|
||||||
|
/**
|
||||||
|
* Зум.
|
||||||
|
*/
|
||||||
|
private function onMouseWheel(e:MouseEvent):void {
|
||||||
|
zoom(e.delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param delta
|
||||||
|
*/
|
||||||
|
private function zoom(delta:int):void {
|
||||||
|
var point:Point3D = mainScene.selectedProp ? mainScene.selectedProp.coords : cursorScene.camera.localToGlobal(cameraPoint);
|
||||||
|
var coords:Point3D = cursorScene.container.localToGlobal(cursorScene.cameraController.coords);
|
||||||
|
var old:Point3D = coords.clone();
|
||||||
|
coords.x = (point.x + delta*coords.x)/(1 + delta);
|
||||||
|
coords.y = (point.y + delta*coords.y)/(1 + delta);
|
||||||
|
coords.z = (point.z + delta*coords.z)/(1 + delta);
|
||||||
|
cursorScene.cameraController.coords = cursorScene.container.globalToLocal(coords);
|
||||||
|
coords.difference(coords, old);
|
||||||
|
if (delta > 0) cameraDistance -= Math.sqrt(coords.x*coords.x + coords.y*coords.y + coords.z*coords.z);
|
||||||
|
else cameraDistance += Math.sqrt(coords.x*coords.x + coords.y*coords.y + coords.z*coords.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
private var outDown:Boolean = false;
|
||||||
|
private function onMouseOut(e:MouseEvent):void {
|
||||||
|
if (e.buttonDown) {
|
||||||
|
parent.addEventListener(MouseEvent.ROLL_OVER, onMouseOver);
|
||||||
|
cursorScene.containerController.setMouseLook(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onMouseOver(e:MouseEvent):void {
|
||||||
|
parent.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
|
||||||
|
if (!e.buttonDown) {
|
||||||
|
onMouseUp(e);
|
||||||
|
} else {
|
||||||
|
onMouseDown(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var middleDown:Boolean = false;
|
||||||
|
private function onMiddleMouseDown(e:MouseEvent):void {
|
||||||
|
var coords:Point3D;
|
||||||
|
|
||||||
|
if (e.altKey) {
|
||||||
|
var selProp:Prop = mainScene.selectedProp;
|
||||||
|
if (selProp) {
|
||||||
|
var centre:Point = mainScene.getCentrePropsGroup();
|
||||||
|
coords = new Point3D(centre.x, centre.y, selProp.z);
|
||||||
|
} else {
|
||||||
|
coords = cursorScene.camera.localToGlobal(new Point3D(0, 0, cameraDistance));
|
||||||
|
}
|
||||||
|
var offset:Point3D = cursorScene.containerController.coords.clone();
|
||||||
|
offset.subtract(coords);
|
||||||
|
var cameraCoords:Point3D = cursorScene.container.localToGlobal(cursorScene.cameraController.coords);
|
||||||
|
cameraCoords.add(offset);
|
||||||
|
cursorScene.cameraController.coords = cursorScene.container.globalToLocal(cameraCoords);
|
||||||
|
cursorScene.containerController.coords = coords;
|
||||||
|
cursorScene.containerController.setMouseLook(true);
|
||||||
|
} else {
|
||||||
|
middleDown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onMiddleMouseUp(e:MouseEvent):void {
|
||||||
|
middleDown = false;
|
||||||
|
cursorScene.containerController.setMouseLook(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var cameraOffset:Point3D = new Point3D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Покадровая обработка.
|
||||||
|
*/
|
||||||
|
private function onEnterFrame(e:Event):void {
|
||||||
|
|
||||||
|
cursorScene.containerController.yawLeft(keyMapper.keyPressed(0));
|
||||||
|
cursorScene.containerController.yawRight(keyMapper.keyPressed(1));
|
||||||
|
cursorScene.containerController.pitchDown(keyMapper.keyPressed(6));
|
||||||
|
cursorScene.containerController.pitchUp(keyMapper.keyPressed(7));
|
||||||
|
|
||||||
|
cursorScene.containerController.speed = 2000;
|
||||||
|
cursorScene.containerController.moveLeft(keyMapper.keyPressed(2));
|
||||||
|
cursorScene.containerController.moveRight(keyMapper.keyPressed(3));
|
||||||
|
cursorScene.containerController.moveForward(keyMapper.keyPressed(4));
|
||||||
|
cursorScene.containerController.moveBack(keyMapper.keyPressed(5));
|
||||||
|
|
||||||
|
cursorScene.cameraController.processInput();
|
||||||
|
cursorScene.containerController.processInput();
|
||||||
|
|
||||||
|
cursorScene.calculate();
|
||||||
|
|
||||||
|
cameraTransformation = cursorScene.camera.transformation;
|
||||||
|
cameraOffset.x = cameraTransformation.d;
|
||||||
|
cameraOffset.y = cameraTransformation.h;
|
||||||
|
cameraOffset.z = cameraTransformation.l;
|
||||||
|
// Рисуем оси
|
||||||
|
cursorScene.drawAxis(cameraTransformation);
|
||||||
|
var rotation:Point3D = cameraTransformation.getRotations();
|
||||||
|
// Синхронизируем камеру главной сцены
|
||||||
|
mainScene.synchronize(cameraOffset, rotation.x, rotation.y, rotation.z);
|
||||||
|
mainScene.calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Корректировка размеров и положения объектов при изменении окна плеера.
|
||||||
|
*/
|
||||||
|
private function onResize(e:Event = null):void {
|
||||||
|
|
||||||
|
cursorScene.viewResize(parent.width - 20, parent.height - 40);
|
||||||
|
mainScene.viewResize(parent.width - 20, parent.height - 40);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
|
||||||
|
// if (cursorScene == null) return;
|
||||||
|
//
|
||||||
|
// cursorScene.viewResize(unscaledWidth, unscaledHeight);
|
||||||
|
// mainScene.viewResize(unscaledWidth, unscaledHeight);
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработка нажатия клавиш.
|
||||||
|
*/
|
||||||
|
private function onKeyDown(event:KeyboardEvent):void {
|
||||||
|
|
||||||
|
var selProp:Prop;
|
||||||
|
var p:*;
|
||||||
|
var delta:Point3D;
|
||||||
|
var oldCoords:Point3D;
|
||||||
|
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case KeyboardUtils.UP:
|
||||||
|
case KeyboardUtils.DOWN:
|
||||||
|
case KeyboardUtils.LEFT:
|
||||||
|
case KeyboardUtils.RIGHT:
|
||||||
|
var sector:int = mainScene.getCameraSector();
|
||||||
|
if (cursorScene.visible) {
|
||||||
|
cursorScene.moveByArrows(event.keyCode, sector);
|
||||||
|
} else {
|
||||||
|
selProp = mainScene.selectedProp;
|
||||||
|
oldCoords = selProp.coords;
|
||||||
|
// Перемещаем
|
||||||
|
mainScene.moveByArrows(event.keyCode, sector);
|
||||||
|
// Вычисляем перемещение
|
||||||
|
delta = selProp.coords;
|
||||||
|
delta.difference(delta, oldCoords);
|
||||||
|
// Заносим в журнал
|
||||||
|
eventJournal.addEvent(EventJournal.MOVE, mainScene.selectedProps, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case KeyboardUtils.V:
|
||||||
|
verticalMoving = true;
|
||||||
|
break;
|
||||||
|
case KeyboardUtils.W:
|
||||||
|
if (cursorScene.visible) {
|
||||||
|
cursorScene.object.z += EditorScene.vBase;
|
||||||
|
cursorScene.updateMaterial();
|
||||||
|
} else {
|
||||||
|
selProp = mainScene.selectedProp;
|
||||||
|
if (selProp) {
|
||||||
|
oldCoords = selProp.coords;
|
||||||
|
mainScene.verticalMove(false);
|
||||||
|
delta = selProp.coords;
|
||||||
|
delta.difference(delta, oldCoords);
|
||||||
|
// Заносим в журнал
|
||||||
|
eventJournal.addEvent(EventJournal.MOVE, mainScene.selectedProps, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyboardUtils.S:
|
||||||
|
if (!event.ctrlKey) {
|
||||||
|
if (cursorScene.visible) {
|
||||||
|
cursorScene.object.z -= EditorScene.vBase;
|
||||||
|
cursorScene.updateMaterial();
|
||||||
|
} else {
|
||||||
|
selProp = mainScene.selectedProp;
|
||||||
|
if (selProp) {
|
||||||
|
oldCoords = selProp.coords;
|
||||||
|
mainScene.verticalMove(true);
|
||||||
|
delta = selProp.coords;
|
||||||
|
delta.difference(delta, oldCoords);
|
||||||
|
// Заносим в журнал
|
||||||
|
eventJournal.addEvent(EventJournal.MOVE, mainScene.selectedProps, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KeyboardUtils.DELETE:
|
||||||
|
case KeyboardUtils.C:
|
||||||
|
selProp = mainScene.selectedProp;
|
||||||
|
if (selProp) {
|
||||||
|
var cursor:Prop = cursorScene.object;
|
||||||
|
if (cursor) {
|
||||||
|
cursor.coords = selProp.coords;
|
||||||
|
if (snapMode) {
|
||||||
|
cursor.snapCoords();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventJournal.addEvent(EventJournal.DELETE, mainScene.deleteProps());
|
||||||
|
cursorScene.visible = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyboardUtils.Z:
|
||||||
|
if (cursorScene.visible) {
|
||||||
|
cursorScene.rotateProps(true);
|
||||||
|
cursorScene.updateMaterial();
|
||||||
|
} else {
|
||||||
|
eventJournal.addEvent(EventJournal.ROTATE, mainScene.selectedProps.clone(), false);
|
||||||
|
mainScene.rotateProps(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyboardUtils.X:
|
||||||
|
if (cursorScene.visible) {
|
||||||
|
cursorScene.rotateProps(false);
|
||||||
|
cursorScene.updateMaterial();
|
||||||
|
} else {
|
||||||
|
eventJournal.addEvent(EventJournal.ROTATE, mainScene.selectedProps.clone(), true);
|
||||||
|
mainScene.rotateProps(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyboardUtils.ESCAPE:
|
||||||
|
selProp = mainScene.selectedProp;
|
||||||
|
if (selProp) {
|
||||||
|
if (cursorScene.object) {
|
||||||
|
cursorScene.object.coords = selProp.coords;
|
||||||
|
cursorScene.object.snapCoords();
|
||||||
|
}
|
||||||
|
mainScene.deselectProps();
|
||||||
|
cursorScene.visible = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Keyboard.NUMPAD_ADD:
|
||||||
|
zoom(3);
|
||||||
|
break;
|
||||||
|
case Keyboard.NUMPAD_SUBTRACT:
|
||||||
|
zoom(-3);
|
||||||
|
break;
|
||||||
|
case Keyboard.F:
|
||||||
|
mainScene.mirrorTextures();
|
||||||
|
break;
|
||||||
|
case Keyboard.Q:
|
||||||
|
mainScene.selectConflictProps();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function onKeyUp(e:KeyboardEvent):void {
|
||||||
|
switch (e.keyCode) {
|
||||||
|
case KeyboardUtils.V:
|
||||||
|
verticalMoving = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очистка сцен.
|
||||||
|
*/
|
||||||
|
public function clear():void {
|
||||||
|
|
||||||
|
mainScene.clear();
|
||||||
|
cursorScene.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function set snapMode(value:Boolean):void {
|
||||||
|
_snapMode = value;
|
||||||
|
mainScene.snapMode = value;
|
||||||
|
cursorScene.snapMode = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function get snapMode():Boolean {
|
||||||
|
return _snapMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Добавление пропа.
|
||||||
|
* @param sourceProp исходный проп
|
||||||
|
*/
|
||||||
|
public function addProp(sourceProp:Prop):void {
|
||||||
|
|
||||||
|
var prop:Prop = mainScene.addProp(sourceProp, cursorScene.object.coords, cursorScene.object.rotationZ);
|
||||||
|
var props:Set = new Set();
|
||||||
|
props.add(prop);
|
||||||
|
eventJournal.addEvent(EventJournal.ADD, props);
|
||||||
|
setTimeout(cursorScene.updateMaterial, 200);
|
||||||
|
prop.addEventListener(MouseEvent3D.MOUSE_DOWN, onPropMouseDown);
|
||||||
|
|
||||||
|
// if (_snapMode && !cursorScene.freeState && ((multiplePropMode == 2) || (multiplePropMode == 1 && cursorScene.occupyMap.isConflictGroup(cursorScene.object)))) {
|
||||||
|
|
||||||
|
if (_snapMode && !cursorScene.freeState && ((multiplePropMode == 2 && cursorScene.occupyMap.isConflict(prop)) || (multiplePropMode == 1 && cursorScene.occupyMap.isConflictGroup(prop)))) {
|
||||||
|
Alert.show("This location is occupied. Continue?", "", Alert.YES|Alert.NO, this, alertConflict, null, Alert.YES);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private var clickZ:Number;
|
||||||
|
private var propDown:Boolean = false;
|
||||||
|
private function onPropMouseDown(e:MouseEvent3D):void {
|
||||||
|
clickZ = e.object.z;
|
||||||
|
propDown = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function undo():void {
|
||||||
|
var e:EventJournalItem = eventJournal.undo();
|
||||||
|
if (e) {
|
||||||
|
mainScene.undo(e);
|
||||||
|
if (cursorScene.visible) {
|
||||||
|
cursorScene.updateMaterial();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function redo():void {
|
||||||
|
var e:EventJournalItem = eventJournal.redo();
|
||||||
|
if (e) {
|
||||||
|
mainScene.redo(e);
|
||||||
|
if (cursorScene.visible) {
|
||||||
|
cursorScene.updateMaterial();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/alternativa/editor/eventjournal/EventJournal.as
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package alternativa.editor.eventjournal {
|
||||||
|
import alternativa.types.Set;
|
||||||
|
|
||||||
|
|
||||||
|
public class EventJournal {
|
||||||
|
|
||||||
|
public static const ADD:int = 0;
|
||||||
|
public static const DELETE:int = 1;
|
||||||
|
public static const ROTATE:int = 2;
|
||||||
|
public static const MOVE:int = 3;
|
||||||
|
public static const COPY:int = 4;
|
||||||
|
public static const CHANGE_TEXTURE:int = 5;
|
||||||
|
|
||||||
|
private var events:Array;
|
||||||
|
private var cancelEvents:Array;
|
||||||
|
|
||||||
|
public function EventJournal() {
|
||||||
|
events = [];
|
||||||
|
cancelEvents = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addEvent(operation:int, props:Set, oldState:* = null):void {
|
||||||
|
|
||||||
|
events.push(new EventJournalItem(operation, props, oldState));
|
||||||
|
cancelEvents.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function undo(deleteEvent:Boolean = false):EventJournalItem {
|
||||||
|
var len:int = events.length;
|
||||||
|
if (len > 0) {
|
||||||
|
var e:EventJournalItem = events[len - 1];
|
||||||
|
events.pop();
|
||||||
|
if (!deleteEvent) {
|
||||||
|
cancelEvents.push(e);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function redo():EventJournalItem {
|
||||||
|
var len:int = cancelEvents.length;
|
||||||
|
if (len > 0) {
|
||||||
|
var e:EventJournalItem = cancelEvents[len - 1];
|
||||||
|
cancelEvents.pop();
|
||||||
|
events.push(e);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
18
src/alternativa/editor/eventjournal/EventJournalItem.as
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package alternativa.editor.eventjournal {
|
||||||
|
import alternativa.types.Set;
|
||||||
|
|
||||||
|
|
||||||
|
public class EventJournalItem {
|
||||||
|
|
||||||
|
public var operation:int;
|
||||||
|
public var props:Set;
|
||||||
|
public var oldState:*;
|
||||||
|
|
||||||
|
public function EventJournalItem(operation:int, props:Set, oldState:*) {
|
||||||
|
|
||||||
|
this.operation = operation;
|
||||||
|
this.props = props;
|
||||||
|
this.oldState = oldState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/alternativa/editor/events/LevelLoaded.as
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package alternativa.editor.events {
|
||||||
|
import flash.events.Event;
|
||||||
|
|
||||||
|
public class LevelLoaded extends Event {
|
||||||
|
public static const LEVEL_LOADED:String = "level_loaded";
|
||||||
|
|
||||||
|
public function LevelLoaded(bubbles:Boolean=false, cancelable:Boolean=false) {
|
||||||
|
super(LEVEL_LOADED, bubbles, cancelable);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/alternativa/editor/export/BinaryExporter.as
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package alternativa.editor.export {
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.prop.Tile;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
|
||||||
|
import flash.filesystem.FileStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сохраняет уровень в бинарном формате.
|
||||||
|
*/
|
||||||
|
public class BinaryExporter extends FileExporter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function BinaryExporter(root:Object3D) {
|
||||||
|
super(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param stream
|
||||||
|
*/
|
||||||
|
override public function exportToFileStream(stream:FileStream):void {
|
||||||
|
for (var child:* in root.children) {
|
||||||
|
var prop:Prop = child as Prop;
|
||||||
|
if (prop) {
|
||||||
|
stream.writeUTF(prop.library);
|
||||||
|
stream.writeUTF(prop.group);
|
||||||
|
stream.writeUTF(prop.name);
|
||||||
|
stream.writeFloat(prop.x);
|
||||||
|
stream.writeFloat(prop.y);
|
||||||
|
stream.writeFloat(prop.z);
|
||||||
|
stream.writeFloat(prop.rotationZ);
|
||||||
|
stream.writeBoolean(prop.free);
|
||||||
|
var tile:Tile = prop as Tile;
|
||||||
|
if (tile) {
|
||||||
|
stream.writeUTF(tile.textureName);
|
||||||
|
// stream.writeBoolean(tile.isMirror);
|
||||||
|
} else {
|
||||||
|
stream.writeUTF("");
|
||||||
|
// stream.writeBoolean(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/alternativa/editor/export/CollisionBox.as
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package alternativa.editor.export {
|
||||||
|
|
||||||
|
import alternativa.engine3d.alternativa3d;
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.engine3d.core.Vertex;
|
||||||
|
import alternativa.types.Matrix3D;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
|
||||||
|
import flash.geom.Vector3D;
|
||||||
|
|
||||||
|
use namespace alternativa3d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Физический примитив, представляющий ориентированный бокс.
|
||||||
|
*/
|
||||||
|
public class CollisionBox extends CollisionPrimitive {
|
||||||
|
|
||||||
|
// Размеры бокса вдоль локальных осей (x-ширина, y-длина, z-высота)
|
||||||
|
public var size:Point3D = new Point3D();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mesh
|
||||||
|
*/
|
||||||
|
public function CollisionBox(mesh:Mesh = null) {
|
||||||
|
super(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mesh
|
||||||
|
*/
|
||||||
|
override public function parse(mesh:Mesh):void {
|
||||||
|
// Поиск максимальных положительных координат по каждой из осей
|
||||||
|
var minX:Number = Number.MAX_VALUE;
|
||||||
|
var maxX:Number = -Number.MAX_VALUE;
|
||||||
|
var minY:Number = Number.MAX_VALUE;
|
||||||
|
var maxY:Number = -Number.MAX_VALUE;
|
||||||
|
var minZ:Number = Number.MAX_VALUE;
|
||||||
|
var maxZ:Number = -Number.MAX_VALUE;
|
||||||
|
for each (var v:Vertex in mesh._vertices) {
|
||||||
|
var p:Point3D = v._coords;
|
||||||
|
if (p.x < minX) minX = p.x;
|
||||||
|
if (p.x > maxX) maxX = p.x;
|
||||||
|
|
||||||
|
if (p.y < minY) minY = p.y;
|
||||||
|
if (p.y > maxY) maxY = p.y;
|
||||||
|
|
||||||
|
if (p.z < minZ) minZ = p.z;
|
||||||
|
if (p.z > maxZ) maxZ = p.z;
|
||||||
|
}
|
||||||
|
size.x = maxX - minX;
|
||||||
|
size.y = maxY - minY;
|
||||||
|
size.z = maxZ - minZ;
|
||||||
|
|
||||||
|
var midPoint:Point3D = new Point3D(0.5*(maxX + minX), 0.5*(maxY + minY), 0.5*(maxZ + minZ));
|
||||||
|
|
||||||
|
transform.toIdentity();
|
||||||
|
transform.rotate(mesh._rotationX, mesh._rotationY, mesh._rotationZ);
|
||||||
|
transform.translate(mesh._coords.x, mesh._coords.y, mesh._coords.z);
|
||||||
|
midPoint.transform(transform);
|
||||||
|
|
||||||
|
transform.d = midPoint.x;
|
||||||
|
transform.h = midPoint.y;
|
||||||
|
transform.l = midPoint.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param parentTransform
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
override public function getXml(parentTransform:Matrix3D):XML {
|
||||||
|
var globalTransfrom:Matrix3D = transform.clone();
|
||||||
|
globalTransfrom.combine(parentTransform);
|
||||||
|
var angles:Point3D = globalTransfrom.getRotations();
|
||||||
|
var xml:XML =
|
||||||
|
<collision-box>
|
||||||
|
<size>
|
||||||
|
<x>{size.x}</x>
|
||||||
|
<y>{size.y}</y>
|
||||||
|
<z>{size.z}</z>
|
||||||
|
</size>
|
||||||
|
<position>
|
||||||
|
<x>{globalTransfrom.d}</x>
|
||||||
|
<y>{globalTransfrom.h}</y>
|
||||||
|
<z>{globalTransfrom.l}</z>
|
||||||
|
</position>
|
||||||
|
<rotation>
|
||||||
|
<x>{angles.x}</x>
|
||||||
|
<y>{angles.y}</y>
|
||||||
|
<z>{angles.z}</z>
|
||||||
|
</rotation>
|
||||||
|
</collision-box>;
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
102
src/alternativa/editor/export/CollisionPlane.as
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package alternativa.editor.export {
|
||||||
|
|
||||||
|
import __AS3__.vec.Vector;
|
||||||
|
|
||||||
|
import alternativa.engine3d.alternativa3d;
|
||||||
|
import alternativa.engine3d.core.Face;
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.types.Matrix3D;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
|
||||||
|
use namespace alternativa3d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Физический примитив, представляющий собой прямоугольник, лежащий в плоскости XY локальной системы координат.
|
||||||
|
* Нормаль прямоугольника направлена вдоль локальной оси Z.
|
||||||
|
*/
|
||||||
|
public class CollisionPlane extends CollisionPrimitive {
|
||||||
|
// Ширина, размер по оси X
|
||||||
|
public var width:Number = 0;
|
||||||
|
// Длина, размер по оси Y
|
||||||
|
public var length:Number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mesh
|
||||||
|
*/
|
||||||
|
public function CollisionPlane(mesh:Mesh = null) {
|
||||||
|
super(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конструирует примитив из полигонального объекта. Объект должен содержать в себе ориентированный прямоугольник, состоящий из двух треугольников.
|
||||||
|
* @param mesh
|
||||||
|
*/
|
||||||
|
override public function parse(mesh:Mesh):void {
|
||||||
|
var i:int;
|
||||||
|
// Подразумевается, что прямоугольник состоит из двух треугольников. Для определения параметров воспользуемся первым из них.
|
||||||
|
var face:Face = mesh._faces.peek() as Face;
|
||||||
|
// Найдём длины рёбер треугольника и индекс гипотенузы
|
||||||
|
var max:Number = -1;
|
||||||
|
var imax:int = 0;
|
||||||
|
var edges:Vector.<Point3D> = Vector.<Point3D>([new Point3D(), new Point3D(), new Point3D()]);
|
||||||
|
var lengths:Vector.<Number> = new Vector.<Number>(3);
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
var edge:Point3D = edges[i];
|
||||||
|
edge.difference(face.vertices[(i + 1)%3]._coords, face.vertices[i]._coords);
|
||||||
|
var len:Number = lengths[i] = edge.length;
|
||||||
|
if (len > max) {
|
||||||
|
max = len;
|
||||||
|
imax = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Выберем оси X и Y
|
||||||
|
var ix:int = (imax + 2)%3;
|
||||||
|
var iy:int = (imax + 1)%3;
|
||||||
|
var xAxis:Point3D = edges[ix];
|
||||||
|
var yAxis:Point3D = edges[iy];
|
||||||
|
yAxis.invert();
|
||||||
|
width = lengths[ix];
|
||||||
|
length = lengths[iy];
|
||||||
|
// Смещение локального начала координат
|
||||||
|
var trans:Point3D = face.vertices[(imax + 2)%3]._coords.clone();
|
||||||
|
trans.x += 0.5*(xAxis.x + yAxis.x);
|
||||||
|
trans.y += 0.5*(xAxis.y + yAxis.y);
|
||||||
|
trans.z += 0.5*(xAxis.z + yAxis.z);
|
||||||
|
// Оси локального базиса в родительской системе координат
|
||||||
|
xAxis.normalize();
|
||||||
|
yAxis.normalize();
|
||||||
|
var zAxis:Point3D = Point3D.cross(xAxis, yAxis);
|
||||||
|
// Матрица трансформации примитива в родительской системе координат
|
||||||
|
transform.setVectors(xAxis, yAxis, zAxis, trans);
|
||||||
|
transform.rotate(mesh._rotationX, mesh._rotationY, mesh._rotationZ);
|
||||||
|
transform.translate(mesh._coords.x, mesh._coords.y, mesh._coords.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param parentTransform
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
override public function getXml(parentTransform:Matrix3D):XML {
|
||||||
|
var globalTransfrom:Matrix3D = transform.clone();
|
||||||
|
globalTransfrom.combine(parentTransform);
|
||||||
|
var angles:Point3D = globalTransfrom.getRotations();
|
||||||
|
var xml:XML =
|
||||||
|
<collision-plane>
|
||||||
|
<width>{width}</width>
|
||||||
|
<length>{length}</length>
|
||||||
|
<position>
|
||||||
|
<x>{globalTransfrom.d}</x>
|
||||||
|
<y>{globalTransfrom.h}</y>
|
||||||
|
<z>{globalTransfrom.l}</z>
|
||||||
|
</position>
|
||||||
|
<rotation>
|
||||||
|
<x>{angles.x}</x>
|
||||||
|
<y>{angles.y}</y>
|
||||||
|
<z>{angles.z}</z>
|
||||||
|
</rotation>
|
||||||
|
</collision-plane>;
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/alternativa/editor/export/CollisionPrimitive.as
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package alternativa.editor.export {
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.types.Matrix3D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Базовый класс для примитивов физической геометрии.
|
||||||
|
*/
|
||||||
|
public class CollisionPrimitive {
|
||||||
|
// Трансформация примитива в системе координат пропа. Трансформация не должна содержать масштабирования.
|
||||||
|
public var transform:Matrix3D = new Matrix3D();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mesh
|
||||||
|
*/
|
||||||
|
public function CollisionPrimitive(mesh:Mesh = null) {
|
||||||
|
if (mesh != null) parse(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Строит примитив на основе полигонального объекта.
|
||||||
|
* @param mesh
|
||||||
|
*/
|
||||||
|
public function parse(mesh:Mesh):void {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Формирует представление примитива в формате XML с учётом трансформации родительского пропа.
|
||||||
|
*
|
||||||
|
* @param parentTransform трансформация родительского пропа
|
||||||
|
* @return представление примитива в виде XML
|
||||||
|
*/
|
||||||
|
public function getXml(parentTransform:Matrix3D):XML {
|
||||||
|
return new XML();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/alternativa/editor/export/CollisionPrimitivesCache.as
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package alternativa.editor.export {
|
||||||
|
import __AS3__.vec.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Кэш физических примитивов. Хранит наборы физических примтивов, индексированных значениями "имя библиотеки"-"имя группы"-"имя пропа".
|
||||||
|
*/
|
||||||
|
public class CollisionPrimitivesCache {
|
||||||
|
|
||||||
|
private var cache:Object = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function CollisionPrimitivesCache() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Добавляет список примитивов пропа в кэш.
|
||||||
|
*
|
||||||
|
* @param libName
|
||||||
|
* @param grpName
|
||||||
|
* @param propName
|
||||||
|
* @param prim
|
||||||
|
*/
|
||||||
|
public function addPrimitives(libName:String, grpName:String, propName:String, primitives:Vector.<CollisionPrimitive>):void {
|
||||||
|
var libCache:Object = cache[libName];
|
||||||
|
if (libCache == null) cache[libName] = libCache = {};
|
||||||
|
var grpCache:Object = libCache[grpName];
|
||||||
|
if (grpCache == null) libCache[grpName] = grpCache = {};
|
||||||
|
grpCache[propName] = primitives;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает кэшированный список примитив пропа или null в случае отсутствия списка.
|
||||||
|
*
|
||||||
|
* @param libName
|
||||||
|
* @param grpName
|
||||||
|
* @param propName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function getPrimitives(libName:String, grpName:String, propName:String):Vector.<CollisionPrimitive> {
|
||||||
|
var currCache:Object = cache[libName];
|
||||||
|
if (currCache == null) return null;
|
||||||
|
currCache = currCache[grpName];
|
||||||
|
return currCache != null ? currCache[propName] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очищает кэш.
|
||||||
|
*/
|
||||||
|
public function clear():void {
|
||||||
|
cache = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/alternativa/editor/export/FileExporter.as
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package alternativa.editor.export {
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
|
||||||
|
import flash.filesystem.FileStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Базовый класс для экспортёров данных.
|
||||||
|
*/
|
||||||
|
public class FileExporter {
|
||||||
|
|
||||||
|
// Корневой объект сцены, в котором находятся пропы экспортируемого уровня
|
||||||
|
public var root:Object3D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param root
|
||||||
|
*/
|
||||||
|
public function FileExporter(root:Object3D) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод сохраняет данные уровня в заданный файловый поток.
|
||||||
|
* @param stream
|
||||||
|
*/
|
||||||
|
public function exportToFileStream(stream:FileStream):void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
224
src/alternativa/editor/export/TanksXmlExporter.as
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
package alternativa.editor.export {
|
||||||
|
import __AS3__.vec.Vector;
|
||||||
|
|
||||||
|
import alternativa.editor.prop.Bonus;
|
||||||
|
import alternativa.editor.prop.Flag;
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.prop.Spawn;
|
||||||
|
import alternativa.editor.prop.Tile;
|
||||||
|
import alternativa.engine3d.alternativa3d;
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
|
||||||
|
import flash.filesystem.FileStream;
|
||||||
|
|
||||||
|
use namespace alternativa3d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Экспортёр, сохраняющий уровень в формате XML, который используется для представления танкового уровня на сервере.
|
||||||
|
*/
|
||||||
|
public class TanksXmlExporter extends FileExporter {
|
||||||
|
|
||||||
|
private var collPrimCache:CollisionPrimitivesCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function TanksXmlExporter(root:Object3D) {
|
||||||
|
super(root);
|
||||||
|
collPrimCache = new CollisionPrimitivesCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param tile
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private function getTileXml(tile:Tile):XML {
|
||||||
|
// trace("export ", tile.free);
|
||||||
|
var xml:XML =
|
||||||
|
<prop library-name={tile.library} group-name={tile.group} name={tile.name} mirror={tile.isMirror} free={tile.free}>
|
||||||
|
<rotation>
|
||||||
|
<z>{tile.rotationZ}</z>
|
||||||
|
</rotation>
|
||||||
|
<position>
|
||||||
|
<x>{tile.x}</x>
|
||||||
|
<y>{tile.y}</y>
|
||||||
|
<z>{tile.z}</z>
|
||||||
|
</position>
|
||||||
|
<texture-name>{tile.textureName}</texture-name>
|
||||||
|
</prop>;
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param stream
|
||||||
|
*/
|
||||||
|
override public function exportToFileStream(stream:FileStream):void {
|
||||||
|
var xml:XML =
|
||||||
|
<map version="1.0">
|
||||||
|
<static-geometry>
|
||||||
|
</static-geometry>
|
||||||
|
<collision-geometry>
|
||||||
|
</collision-geometry>
|
||||||
|
<spawn-points>
|
||||||
|
</spawn-points>
|
||||||
|
<bonus-regions>
|
||||||
|
</bonus-regions>
|
||||||
|
</map>;
|
||||||
|
|
||||||
|
var staticGeometry:XML = xml.child("static-geometry")[0];
|
||||||
|
var collisionGeometry:XML = xml.child("collision-geometry")[0];
|
||||||
|
var bonusRegions:XML = xml.child("bonus-regions")[0];
|
||||||
|
var spawnPoints:XML = xml.child("spawn-points")[0];
|
||||||
|
for (var child:* in root.children) {
|
||||||
|
var prop:Prop = child as Prop;
|
||||||
|
if (prop) {
|
||||||
|
switch (prop.type) {
|
||||||
|
case Prop.BONUS:
|
||||||
|
bonusRegions.appendChild(getBonusXml(prop as Bonus));
|
||||||
|
break;
|
||||||
|
case Prop.SPAWN:
|
||||||
|
spawnPoints.appendChild(getSpawnXml(prop as Spawn));
|
||||||
|
break;
|
||||||
|
case Prop.FLAG:
|
||||||
|
addCtfFlag(xml, prop as Flag);
|
||||||
|
break;
|
||||||
|
case Prop.TILE:
|
||||||
|
var tile:Tile = prop as Tile;
|
||||||
|
staticGeometry.appendChild(getTileXml(tile));
|
||||||
|
createTileCollisionXml(tile, collisionGeometry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeUTFBytes(xml.toXMLString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mapXml
|
||||||
|
*/
|
||||||
|
private function addCtfFlag(mapXml:XML, flag:Flag):void {
|
||||||
|
var flags:XMLList = mapXml.elements("ctf-flags");
|
||||||
|
var flagsElement:XML;
|
||||||
|
if (flags.length() == 0) {
|
||||||
|
flagsElement = <ctf-flags></ctf-flags>;
|
||||||
|
mapXml.appendChild(flagsElement);
|
||||||
|
} else {
|
||||||
|
flagsElement = flags[0];
|
||||||
|
}
|
||||||
|
var flagElement:XML;
|
||||||
|
switch (flag.name) {
|
||||||
|
case "red_flag":
|
||||||
|
flagElement =
|
||||||
|
<flag-red>
|
||||||
|
<x>{flag.x}</x>
|
||||||
|
<y>{flag.y}</y>
|
||||||
|
<z>{flag.z}</z>
|
||||||
|
</flag-red>;
|
||||||
|
break;
|
||||||
|
case "blue_flag":
|
||||||
|
flagElement =
|
||||||
|
<flag-blue>
|
||||||
|
<x>{flag.x}</x>
|
||||||
|
<y>{flag.y}</y>
|
||||||
|
<z>{flag.z}</z>
|
||||||
|
</flag-blue>;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
flagsElement.appendChild(flagElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param tile
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private function createTileCollisionXml(tile:Tile, parentElement:XML):void {
|
||||||
|
// Пробуем достать примитивы из кэша, если не удаётся, создаём набор для пропа и добавляем его в кэш
|
||||||
|
var primitives:Vector.<CollisionPrimitive> = collPrimCache.getPrimitives(tile.library, tile.group, tile.name);
|
||||||
|
if (primitives == null) {
|
||||||
|
primitives = createPropCollisionPrimitives(tile);
|
||||||
|
collPrimCache.addPrimitives(tile.library, tile.group, tile.name, primitives);
|
||||||
|
}
|
||||||
|
// Записываем XML представления примитивов
|
||||||
|
for each (var p:CollisionPrimitive in primitives) parentElement.appendChild(p.getXml(tile._transformation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создаёт набор физической геометрии для пропа.
|
||||||
|
* @param tile
|
||||||
|
*/
|
||||||
|
private function createPropCollisionPrimitives(tile:Tile):Vector.<CollisionPrimitive> {
|
||||||
|
var primitives:Vector.<CollisionPrimitive> = new Vector.<CollisionPrimitive>();
|
||||||
|
for (var key:* in tile.collisionGeometry) {
|
||||||
|
var mesh:Mesh = key;
|
||||||
|
var meshName:String = mesh.name.toLowerCase();
|
||||||
|
if (meshName.indexOf("plane") == 0) primitives.push(new CollisionPlane(mesh));
|
||||||
|
else if (meshName.indexOf("box") == 0) primitives.push(new CollisionBox(mesh));
|
||||||
|
}
|
||||||
|
return primitives;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param spawn
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private function getSpawnXml(spawn:Spawn):XML {
|
||||||
|
var xml:XML =
|
||||||
|
<spawn-point type={spawn.name} free={spawn.free}>
|
||||||
|
<position>
|
||||||
|
<x>{spawn.x}</x>
|
||||||
|
<y>{spawn.y}</y>
|
||||||
|
<z>{spawn.z}</z>
|
||||||
|
</position>
|
||||||
|
<rotation>
|
||||||
|
<z>{spawn.rotationZ}</z>
|
||||||
|
</rotation>
|
||||||
|
</spawn-point>
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param prop
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private function getBonusXml(prop:Bonus):XML {
|
||||||
|
|
||||||
|
var xml:XML =
|
||||||
|
<bonus-region name={prop.name} free={prop.free}>
|
||||||
|
<position>
|
||||||
|
<x>{prop.x}</x>
|
||||||
|
<y>{prop.y}</y>
|
||||||
|
<z>{prop.z}</z>
|
||||||
|
</position>
|
||||||
|
<rotation>
|
||||||
|
<z>{prop.rotationZ}</z>
|
||||||
|
</rotation>
|
||||||
|
<min>
|
||||||
|
<x>{prop.x + prop.distancesX.x}</x>
|
||||||
|
<y>{prop.y + prop.distancesY.x}</y>
|
||||||
|
<z>{prop.z + prop.distancesZ.x}</z>
|
||||||
|
</min>
|
||||||
|
<max>
|
||||||
|
<x>{prop.x + prop.distancesX.y}</x>
|
||||||
|
<y>{prop.y + prop.distancesY.y}</y>
|
||||||
|
<z>{prop.z + prop.distancesZ.y}</z>
|
||||||
|
</max>
|
||||||
|
</bonus-region>;
|
||||||
|
|
||||||
|
|
||||||
|
for (var type:* in prop.types) {
|
||||||
|
xml.appendChild(<bonus-type>{type}</bonus-type>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/alternativa/editor/importLevel/BinaryImporter.as
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package alternativa.editor.importLevel {
|
||||||
|
import alternativa.editor.LibraryManager;
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.prop.Tile;
|
||||||
|
import alternativa.editor.scene.MainScene;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
|
||||||
|
import flash.filesystem.FileStream;
|
||||||
|
|
||||||
|
import mx.controls.Alert;
|
||||||
|
|
||||||
|
public class BinaryImporter extends FileImporter {
|
||||||
|
|
||||||
|
public function BinaryImporter(scene:MainScene, libraryManager:LibraryManager) {
|
||||||
|
super(scene, libraryManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function importFromFileStream(stream:FileStream):void {
|
||||||
|
try {
|
||||||
|
while (stream.bytesAvailable) {
|
||||||
|
// Проверка на то, что в предыдущей итерации не загружали библиотеку
|
||||||
|
if (libname == "") {
|
||||||
|
var lib:String = stream.readUTF();
|
||||||
|
// Составляем ключ: имя библиотеки + имя группы + имя меша
|
||||||
|
libname = lib + stream.readUTF() + stream.readUTF();
|
||||||
|
}
|
||||||
|
// Ищем проп по ключу
|
||||||
|
var prop:Prop = libraryManager.nameProp[libname];
|
||||||
|
if (prop) {
|
||||||
|
// Добавляем проп на сцену
|
||||||
|
prop = scene.addProp(prop, new Point3D(stream.readFloat(), stream.readFloat(), stream.readFloat()), stream.readFloat(), true, false);
|
||||||
|
var free:Boolean = stream.readBoolean();
|
||||||
|
if (!free) {
|
||||||
|
// Заполняем карту
|
||||||
|
scene.occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
var textureName:String = stream.readUTF();
|
||||||
|
// var isMirror:Boolean = stream.readBoolean();
|
||||||
|
var tile:Tile = prop as Tile;
|
||||||
|
if (tile) {
|
||||||
|
try {
|
||||||
|
if (textureName != "") {
|
||||||
|
tile.textureName = textureName;
|
||||||
|
}
|
||||||
|
} catch (err:Error) {
|
||||||
|
Alert.show("Tile " + tile.name + ": texture " + textureName + " is not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (isMirror) {
|
||||||
|
// tile.mirrorTexture();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
libname = "";
|
||||||
|
scene.calculate();
|
||||||
|
} else {
|
||||||
|
Alert.show("Library '"+ lib + "' is used by the level. Load?", "", Alert.YES|Alert.NO, null, libAlertListener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err:Error) {
|
||||||
|
Alert.show(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
endLoadLevel();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/alternativa/editor/importLevel/FileImporter.as
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package alternativa.editor.importLevel {
|
||||||
|
import alternativa.editor.LibraryManager;
|
||||||
|
import alternativa.editor.events.LevelLoaded;
|
||||||
|
import alternativa.editor.scene.MainScene;
|
||||||
|
|
||||||
|
import flash.events.Event;
|
||||||
|
import flash.events.EventDispatcher;
|
||||||
|
import flash.filesystem.FileStream;
|
||||||
|
|
||||||
|
import mx.controls.Alert;
|
||||||
|
import mx.events.CloseEvent;
|
||||||
|
|
||||||
|
|
||||||
|
public class FileImporter extends EventDispatcher {
|
||||||
|
protected var scene:MainScene;
|
||||||
|
protected var libraryManager:LibraryManager;
|
||||||
|
protected var libname:String = "";
|
||||||
|
|
||||||
|
public function FileImporter(scene:MainScene, libraryManager:LibraryManager) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.libraryManager = libraryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importFromFileStream(stream:FileStream):void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработка алерта загрузки библиотеки.
|
||||||
|
*/
|
||||||
|
protected function libAlertListener(e:CloseEvent):void {
|
||||||
|
|
||||||
|
switch (e.detail) {
|
||||||
|
case Alert.YES:
|
||||||
|
// libraryManager.loadLibrary(loadingLevel);
|
||||||
|
break;
|
||||||
|
case Alert.NO:
|
||||||
|
scene.clear();
|
||||||
|
endLoadLevel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конец загрузки уровня.
|
||||||
|
*/
|
||||||
|
protected function endLoadLevel():void {
|
||||||
|
// fileStream.close();
|
||||||
|
scene.changed = false;
|
||||||
|
// emptyPath = false;
|
||||||
|
// fileForSave = file.clone();
|
||||||
|
// fileForSave.addEventListener(Event.SELECT, onSaveFileSelect);
|
||||||
|
libname = "";
|
||||||
|
// progressBar.visible = false;
|
||||||
|
// cursorScene.visible = true;
|
||||||
|
// dispatchEvent(new LevelLoaded());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
184
src/alternativa/editor/importLevel/TanksXmlImporter.as
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
package alternativa.editor.importLevel {
|
||||||
|
|
||||||
|
import alternativa.editor.LibraryManager;
|
||||||
|
import alternativa.editor.prop.Bonus;
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.prop.Tile;
|
||||||
|
import alternativa.editor.prop.TileSprite3D;
|
||||||
|
import alternativa.editor.scene.MainScene;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
|
||||||
|
import flash.filesystem.FileStream;
|
||||||
|
|
||||||
|
import mx.controls.Alert;
|
||||||
|
|
||||||
|
public class TanksXmlImporter extends FileImporter {
|
||||||
|
|
||||||
|
public function TanksXmlImporter(scene:MainScene, libraryManager:LibraryManager) {
|
||||||
|
|
||||||
|
super(scene, libraryManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function importFromFileStream(stream:FileStream):void {
|
||||||
|
|
||||||
|
var xml:XML = new XML(stream.readUTFBytes(stream.bytesAvailable));
|
||||||
|
stream.close();
|
||||||
|
try {
|
||||||
|
var staticGeometry:XML = xml.child("static-geometry")[0];
|
||||||
|
var bonusRegions:XML = xml.child("bonus-regions")[0];
|
||||||
|
var spawnPoints:XML = xml.child("spawn-points")[0];
|
||||||
|
|
||||||
|
if (staticGeometry) {
|
||||||
|
loadTiles(staticGeometry);
|
||||||
|
}
|
||||||
|
if (bonusRegions) {
|
||||||
|
loadBonuses(bonusRegions);
|
||||||
|
}
|
||||||
|
if (spawnPoints) {
|
||||||
|
loadSpawns(spawnPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
var flagList:XMLList = xml.child("ctf-flags");
|
||||||
|
if (flagList.length() > 0) loadFlags(flagList[0]);
|
||||||
|
|
||||||
|
} catch (err:Error) {
|
||||||
|
Alert.show(err.message);
|
||||||
|
}
|
||||||
|
endLoadLevel();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param flags
|
||||||
|
*/
|
||||||
|
private function loadFlags(flags:XML):void {
|
||||||
|
addFlag(flags.child("flag-red")[0], "red_flag");
|
||||||
|
addFlag(flags.child("flag-blue")[0], "blue_flag");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param flagXml
|
||||||
|
* @param flagPropName
|
||||||
|
*/
|
||||||
|
private function addFlag(flagXml:XML, flagPropName:String):void {
|
||||||
|
libname = "FunctionalFlags" + flagPropName;
|
||||||
|
var prop:Prop = libraryManager.nameProp[libname];
|
||||||
|
if (prop != null) {
|
||||||
|
// Добавляем проп на сцену
|
||||||
|
prop = scene.addProp(prop, new Point3D(Number(flagXml.x),Number(flagXml.y), Number(flagXml.z)), 0, true, false);
|
||||||
|
libname = "";
|
||||||
|
scene.calculate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param staticGeometry
|
||||||
|
*/
|
||||||
|
private function loadTiles(staticGeometry:XML):void {
|
||||||
|
var tiles:XMLList = staticGeometry.child("prop");
|
||||||
|
for (var i:int = 0; i < tiles.length(); i++) {
|
||||||
|
var propXML:XML = tiles[i];
|
||||||
|
libname = propXML.attribute("library-name").toString() + propXML.attribute("group-name").toString() + propXML.attribute("name").toString();
|
||||||
|
var prop:Prop = libraryManager.nameProp[libname];
|
||||||
|
if (prop) {
|
||||||
|
var position:XML = propXML.child("position")[0];
|
||||||
|
var rotation:XML = propXML.child("rotation")[0];
|
||||||
|
// Добавляем проп на сцену
|
||||||
|
prop = scene.addProp(prop, new Point3D(Number(position.child("x")[0]),Number(position.child("y")[0]), Number(position.child("z")[0])), Number(rotation.child("z")[0]), true, false);
|
||||||
|
var free:Boolean = propXML.attribute("free").toString() == "true";
|
||||||
|
// trace("import free", free);
|
||||||
|
if (!(free && prop is TileSprite3D)) {
|
||||||
|
// Заполняем карту
|
||||||
|
scene.occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureName:String = propXML.child("texture-name")[0];
|
||||||
|
// var isMirror:Boolean = fileStream.readBoolean();
|
||||||
|
var tile:Tile = prop as Tile;
|
||||||
|
if (tile) {
|
||||||
|
try {
|
||||||
|
if (textureName != "") {
|
||||||
|
tile.textureName = textureName;
|
||||||
|
}
|
||||||
|
} catch (err:Error) {
|
||||||
|
Alert.show("Tile " + tile.name + ": texture " + textureName + " is not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (isMirror) {
|
||||||
|
// tile.mirrorTexture();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
libname = "";
|
||||||
|
scene.calculate();
|
||||||
|
// } else {
|
||||||
|
// Alert.show("Library '"+ lib + "' is used by the level. Load?", "", Alert.YES|Alert.NO, this, libAlertListener);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadBonuses(bonusRegions:XML):void {
|
||||||
|
var bonuses:XMLList = bonusRegions.child("bonus-region");
|
||||||
|
for (var i:int = 0; i < bonuses.length(); i++) {
|
||||||
|
var bonusXML:XML = bonuses[i];
|
||||||
|
libname = "FunctionalBonus Regions" + bonusXML.attribute("name").toString();
|
||||||
|
var prop:Prop = libraryManager.nameProp[libname];
|
||||||
|
if (prop) {
|
||||||
|
var position:XML = bonusXML.child("position")[0];
|
||||||
|
var rotation:XML = bonusXML.child("rotation")[0];
|
||||||
|
// Добавляем проп на сцену
|
||||||
|
prop = scene.addProp(prop, new Point3D(Number(position.child("x")[0]),Number(position.child("y")[0]), Number(position.child("z")[0])), Number(rotation.child("z")[0]), true, false);
|
||||||
|
var free:Boolean = bonusXML.attribute("free");
|
||||||
|
if (!free) {
|
||||||
|
// Заполняем карту
|
||||||
|
scene.occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
var bonusType:XMLList = bonusXML.child("bonus-type");
|
||||||
|
|
||||||
|
(prop as Bonus).types.clear();
|
||||||
|
for (var j:int = 0; j < bonusType.length(); j++) {
|
||||||
|
// trace("j", bonusType[j].toString());
|
||||||
|
(prop as Bonus).types.add(bonusType[j].toString());
|
||||||
|
}
|
||||||
|
libname = "";
|
||||||
|
scene.calculate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadSpawns(spawnPoints:XML):void {
|
||||||
|
var spawns:XMLList = spawnPoints.child("spawn-point");
|
||||||
|
for (var i:int = 0; i < spawns.length(); i++) {
|
||||||
|
var spawnXML:XML = spawns[i];
|
||||||
|
libname = "FunctionalSpawn Points" + spawnXML.attribute("type").toString();
|
||||||
|
var prop:Prop = libraryManager.nameProp[libname];
|
||||||
|
if (prop) {
|
||||||
|
var position:XML = spawnXML.child("position")[0];
|
||||||
|
var rotation:XML = spawnXML.child("rotation")[0];
|
||||||
|
// Добавляем проп на сцену
|
||||||
|
prop = scene.addProp(prop, new Point3D(Number(position.child("x")[0]),Number(position.child("y")[0]), Number(position.child("z")[0])), Number(rotation.child("z")[0]), true, false);
|
||||||
|
var free:Boolean = spawnXML.attribute("free");;
|
||||||
|
if (!free) {
|
||||||
|
// Заполняем карту
|
||||||
|
scene.occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
libname = "";
|
||||||
|
scene.calculate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/alternativa/editor/prop/Bonus.as
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package alternativa.editor.prop {
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.materials.TextureMaterial;
|
||||||
|
import alternativa.types.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author danilova
|
||||||
|
*/
|
||||||
|
public class Bonus extends Prop {
|
||||||
|
|
||||||
|
public var types:Set;
|
||||||
|
public function Bonus(object:Object3D, library:String, group:String, needCalculate:Boolean=true) {
|
||||||
|
super(object, library, group, needCalculate);
|
||||||
|
types = new Set();
|
||||||
|
types.add("damageup");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override public function clone():Object3D {
|
||||||
|
|
||||||
|
var copyObject:Mesh = _object.clone() as Mesh;
|
||||||
|
copyObject.cloneMaterialToAllSurfaces(_material as TextureMaterial);
|
||||||
|
// Создаем проп
|
||||||
|
var copy:Bonus = new Bonus(copyObject, _library, _group, false);
|
||||||
|
// Копируем свойства
|
||||||
|
copy.distancesX = distancesX.clone();
|
||||||
|
copy.distancesY = distancesY.clone();
|
||||||
|
copy.distancesZ = distancesZ.clone();
|
||||||
|
copy._multi = _multi;
|
||||||
|
copy.name = name;
|
||||||
|
copy.types = types.clone();
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/alternativa/editor/prop/CustomFillMaterial.as
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package alternativa.editor.prop {
|
||||||
|
import alternativa.engine3d.alternativa3d;
|
||||||
|
import alternativa.engine3d.core.Camera3D;
|
||||||
|
import alternativa.engine3d.core.PolyPrimitive;
|
||||||
|
import alternativa.engine3d.display.Skin;
|
||||||
|
import alternativa.engine3d.materials.FillMaterial;
|
||||||
|
import alternativa.engine3d.materials.Material;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.utils.ColorUtils;
|
||||||
|
|
||||||
|
use namespace alternativa3d;
|
||||||
|
|
||||||
|
public class CustomFillMaterial extends FillMaterial {
|
||||||
|
|
||||||
|
private var center:Point3D = new Point3D();
|
||||||
|
private var lightPoint:Point3D = new Point3D();
|
||||||
|
private var normal:Point3D = new Point3D();
|
||||||
|
|
||||||
|
public function CustomFillMaterial(lightPoint:Point3D, color:uint, alpha:Number=1, blendMode:String="normal", wireThickness:Number=-1, wireColor:uint=0) {
|
||||||
|
super(color, alpha, blendMode, wireThickness, wireColor);
|
||||||
|
this.lightPoint.copy(lightPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
override alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void {
|
||||||
|
var poly:PolyPrimitive = skin.primitive;
|
||||||
|
center.reset();
|
||||||
|
for (var i:int = 0; i < poly.num; i++) {
|
||||||
|
center.add(poly.points[i]);
|
||||||
|
}
|
||||||
|
center.multiply(1/poly.num);
|
||||||
|
normal.difference(lightPoint, center);
|
||||||
|
normal.normalize();
|
||||||
|
var c:uint = _color;
|
||||||
|
var k:Number = 0.5*(1 + normal.dot(poly.face.globalNormal));
|
||||||
|
_color = ColorUtils.multiply(_color, k);
|
||||||
|
super.draw(camera, skin, length, points);
|
||||||
|
_color = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function clone():Material {
|
||||||
|
return new CustomFillMaterial(lightPoint, color, alpha, blendMode, wireThickness, wireColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/alternativa/editor/prop/Flag.as
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package alternativa.editor.prop {
|
||||||
|
import alternativa.editor.scene.EditorScene;
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.materials.TextureMaterial;
|
||||||
|
|
||||||
|
import flash.geom.Point;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Flag extends Prop {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param object
|
||||||
|
* @param library
|
||||||
|
* @param group
|
||||||
|
* @param needCalculate
|
||||||
|
*/
|
||||||
|
public function Flag(object:Object3D, library:String, group:String, needCalculate:Boolean=true) {
|
||||||
|
super(object, library, group, needCalculate);
|
||||||
|
type = Prop.FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
override public function calculate():void {
|
||||||
|
distancesX = new Point(-EditorScene.hBase, EditorScene.hBase);
|
||||||
|
distancesY = new Point(-EditorScene.hBase, EditorScene.hBase);
|
||||||
|
distancesZ = new Point(0, EditorScene.vBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
override public function clone():Object3D {
|
||||||
|
var copyObject:Mesh = _object.clone() as Mesh;
|
||||||
|
copyObject.cloneMaterialToAllSurfaces(_material as TextureMaterial);
|
||||||
|
// Создаем проп
|
||||||
|
var copy:Flag = new Flag(copyObject, _library, _group, false);
|
||||||
|
// Копируем свойства
|
||||||
|
copy.distancesX = distancesX.clone();
|
||||||
|
copy.distancesY = distancesY.clone();
|
||||||
|
copy.distancesZ = distancesZ.clone();
|
||||||
|
copy._multi = _multi;
|
||||||
|
copy.name = name;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
373
src/alternativa/editor/prop/Prop.as
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
package alternativa.editor.prop {
|
||||||
|
|
||||||
|
import alternativa.editor.scene.EditorScene;
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.events.MouseEvent3D;
|
||||||
|
import alternativa.engine3d.materials.Material;
|
||||||
|
import alternativa.engine3d.materials.SurfaceMaterial;
|
||||||
|
import alternativa.engine3d.materials.TextureMaterial;
|
||||||
|
import alternativa.types.Map;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.types.Texture;
|
||||||
|
import alternativa.utils.MathUtils;
|
||||||
|
|
||||||
|
import flash.display.Bitmap;
|
||||||
|
import flash.display.BitmapData;
|
||||||
|
import flash.display.BlendMode;
|
||||||
|
import flash.geom.Matrix;
|
||||||
|
import flash.geom.Point;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author danilova
|
||||||
|
*/
|
||||||
|
public class Prop extends Object3D {
|
||||||
|
|
||||||
|
public static const TILE:int = 1;
|
||||||
|
public static const SPAWN:int = 2;
|
||||||
|
public static const BONUS:int = 3;
|
||||||
|
public static const FLAG:int = 4;
|
||||||
|
|
||||||
|
public var type:int = BONUS;
|
||||||
|
|
||||||
|
// Объект на сцене
|
||||||
|
protected var _object:Object3D;
|
||||||
|
// Группа
|
||||||
|
protected var _group:String;
|
||||||
|
// Библиотека
|
||||||
|
protected var _library:String;
|
||||||
|
// Расстояния от центра до крайних вершин по оси X
|
||||||
|
public var distancesX:Point;
|
||||||
|
// Расстояния от центра до крайних вершин по оси Y
|
||||||
|
public var distancesY:Point;
|
||||||
|
// Расстояния от центра до крайних вершин по оси Z
|
||||||
|
public var distancesZ:Point;
|
||||||
|
// Индикатор многоячеечности
|
||||||
|
protected var _multi:Boolean = false;
|
||||||
|
// Индикатор занесенности на карту
|
||||||
|
public var free:Boolean = true;
|
||||||
|
// Исходный материал
|
||||||
|
protected var _material:Material;
|
||||||
|
// Битмапдата текстуры исходного материала
|
||||||
|
public var bitmapData:BitmapData;
|
||||||
|
// Битмапдата выделенного пропа
|
||||||
|
protected var _selectBitmapData:BitmapData;
|
||||||
|
//
|
||||||
|
public var icon:Bitmap;
|
||||||
|
//
|
||||||
|
protected var _selected:Boolean = false;
|
||||||
|
//
|
||||||
|
private var matrix:Matrix = new Matrix();
|
||||||
|
|
||||||
|
|
||||||
|
[Embed (source = "red_cursor.jpg")] private static var redClass:Class;
|
||||||
|
private static const redBmp:BitmapData = new redClass().bitmapData;
|
||||||
|
|
||||||
|
public function Prop(object:Object3D, library:String, group:String, needCalculate:Boolean=true) {
|
||||||
|
|
||||||
|
super(object.name);
|
||||||
|
addChild(object);
|
||||||
|
|
||||||
|
_object = object;
|
||||||
|
_object.addEventListener(MouseEvent3D.MOUSE_DOWN, onMouseDown);
|
||||||
|
_library = library;
|
||||||
|
_group = group;
|
||||||
|
|
||||||
|
initBitmapData();
|
||||||
|
|
||||||
|
if (needCalculate) {
|
||||||
|
calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function onMouseDown(e:MouseEvent3D):void {
|
||||||
|
e.object = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function initBitmapData():void {
|
||||||
|
_material = (_object as Mesh).surfaces.peek().material;
|
||||||
|
bitmapData = (_material as TextureMaterial).texture.bitmapData;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Расчет расстояний от центра по всем осям.
|
||||||
|
*/
|
||||||
|
public function calculate():void {
|
||||||
|
|
||||||
|
var vertices:Array = (_object as Mesh).vertices.toArray(true);
|
||||||
|
var maxZ:Number = 0;
|
||||||
|
var maxY:Number = 0;
|
||||||
|
var maxX:Number = 0;
|
||||||
|
|
||||||
|
var z1:Number = 0;
|
||||||
|
var z2:Number = 0;
|
||||||
|
var y1:Number = 0;
|
||||||
|
var y2:Number = 0;
|
||||||
|
var x1:Number = 0;
|
||||||
|
var x2:Number = 0;
|
||||||
|
var len:int = vertices.length;
|
||||||
|
for (var i:int = 0; i < len; i++) {
|
||||||
|
var vertex1:Point3D = vertices[i].coords;
|
||||||
|
if (scene) {
|
||||||
|
vertex1 = localToGlobal(vertex1);
|
||||||
|
}
|
||||||
|
for (var j:int = i+1; j < len; j++) {
|
||||||
|
var vertex2:Point3D = vertices[j].coords;
|
||||||
|
if (scene) {
|
||||||
|
vertex2 = localToGlobal(vertex2);
|
||||||
|
}
|
||||||
|
var dx:Number = (vertex1.x - vertex2.x);
|
||||||
|
var dy:Number = (vertex1.y - vertex2.y);
|
||||||
|
var dz:Number = (vertex1.z - vertex2.z);
|
||||||
|
var distanceX:Number = dx*dx;
|
||||||
|
var distanceY:Number = dy*dy;
|
||||||
|
var distanceZ:Number = dz*dz;
|
||||||
|
|
||||||
|
if (distanceX > maxX) {
|
||||||
|
maxX = distanceX;
|
||||||
|
x1 = vertex1.x;
|
||||||
|
x2 = vertex2.x;
|
||||||
|
}
|
||||||
|
if (distanceY > maxY) {
|
||||||
|
maxY = distanceY;
|
||||||
|
y1 = vertex1.y;
|
||||||
|
y2 = vertex2.y;
|
||||||
|
}
|
||||||
|
if (distanceZ > maxZ) {
|
||||||
|
maxZ = distanceZ;
|
||||||
|
z1 = vertex1.z;
|
||||||
|
z2 = vertex2.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
distancesX = calcDistance(x, x1, x2, EditorScene.hBase);
|
||||||
|
distancesY = calcDistance(y, y1, y2, EditorScene.hBase);
|
||||||
|
distancesZ = calcDistance(z, z1, z2, EditorScene.vBase);
|
||||||
|
|
||||||
|
if (Math.abs(int(x2) - int(x1))/EditorScene.hBase2 > 1 ||
|
||||||
|
Math.abs(int(y1) - int(y2))/EditorScene.hBase2 > 1) {
|
||||||
|
_multi = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Расчет расстояния от точки центра до 1й и 2й точек, приведённых к сетке.
|
||||||
|
* @param centre точка центра
|
||||||
|
* @param value1 1я точка
|
||||||
|
* @param value2 2я точка
|
||||||
|
* @param base шаг сетки
|
||||||
|
* return расстояние от центра до меньшей точки со знаком "-", расстояние от центра до большей точки
|
||||||
|
*/
|
||||||
|
private function calcDistance(centre:Number, value1:Number, value2:Number, base:Number):Point {
|
||||||
|
|
||||||
|
var distances:Point = new Point();
|
||||||
|
|
||||||
|
value2 = floorTo(value2, base);
|
||||||
|
value1 = floorTo(value1, base);
|
||||||
|
|
||||||
|
if (value2 == 0 && value1 == 0) {
|
||||||
|
distances.x = 0;
|
||||||
|
distances.y = base;
|
||||||
|
} else {
|
||||||
|
if (value2 > value1) {
|
||||||
|
if (value1 == 0) {
|
||||||
|
distances.x = 0;
|
||||||
|
distances.y = value2 - centre;
|
||||||
|
} else {
|
||||||
|
distances.x = value1 - centre;
|
||||||
|
distances.y = value2 - centre;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (value2 == 0) {
|
||||||
|
distances.x = 0;
|
||||||
|
distances.y = value1 - centre;
|
||||||
|
} else {
|
||||||
|
distances.x = value2 - centre;
|
||||||
|
distances.y = value1 - centre;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return distances;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Привязка к узлу сетки.
|
||||||
|
* @param value округляемое значение
|
||||||
|
* @param base шаг сетки
|
||||||
|
* @return округленное значение
|
||||||
|
*/
|
||||||
|
public static function floorTo(value:Number, base:Number):Number {
|
||||||
|
|
||||||
|
return Math.floor((value + base/2)/base)*base;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Привязка к центру ячейки.
|
||||||
|
* @param value округляемое значение
|
||||||
|
* @param base шаг сетки
|
||||||
|
* @return округленное значение
|
||||||
|
*/
|
||||||
|
public static function roundTo(value:Number, base:Number):Number {
|
||||||
|
|
||||||
|
return Math.round((value + base/2)/base)*base - base/2;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Выделить проп.
|
||||||
|
*/
|
||||||
|
public function select():void {
|
||||||
|
|
||||||
|
_selectBitmapData = bitmapData.clone();
|
||||||
|
matrix.a = bitmapData.width/redBmp.width;
|
||||||
|
matrix.d = matrix.a;
|
||||||
|
_selectBitmapData.draw(redBmp, matrix, null, BlendMode.MULTIPLY);
|
||||||
|
setMaterial(newSelectedMaterial);
|
||||||
|
_selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Снять выделение.
|
||||||
|
*/
|
||||||
|
public function deselect():void {
|
||||||
|
|
||||||
|
_selectBitmapData.dispose();
|
||||||
|
setMaterial(_material);
|
||||||
|
_selected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создает материал для выделения пропа.
|
||||||
|
* @return новый материал
|
||||||
|
*/
|
||||||
|
protected function get newSelectedMaterial():Material {
|
||||||
|
return new TextureMaterial(new Texture(_selectBitmapData));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Назначает пропу материал.
|
||||||
|
* @param material материал
|
||||||
|
*/
|
||||||
|
public function setMaterial(material:Material):void {
|
||||||
|
var sm:SurfaceMaterial = material as SurfaceMaterial;
|
||||||
|
(_object as Mesh).cloneMaterialToAllSurfaces(sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get multi():Boolean {
|
||||||
|
return _multi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get library():String {
|
||||||
|
return _library;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get selected():Boolean {
|
||||||
|
return _selected;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get group():String {
|
||||||
|
return _group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get vertices():Map {
|
||||||
|
return (_object as Mesh).vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get material():Material {
|
||||||
|
return _material;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поворот.
|
||||||
|
* @param plus флаг положительного поворота
|
||||||
|
*/
|
||||||
|
public function rotate(plus:Boolean):void {
|
||||||
|
|
||||||
|
var point:Point;
|
||||||
|
|
||||||
|
if (plus) {
|
||||||
|
point = new Point(distancesX.x, distancesX.y);
|
||||||
|
distancesX.x = distancesY.x;
|
||||||
|
distancesX.y = distancesY.y;
|
||||||
|
distancesY.x = -point.y;
|
||||||
|
distancesY.y = -point.x;
|
||||||
|
rotationZ -= MathUtils.DEG90;
|
||||||
|
} else {
|
||||||
|
point = new Point(distancesY.x, distancesY.y);
|
||||||
|
distancesY.x = distancesX.x;
|
||||||
|
distancesY.y = distancesX.y;
|
||||||
|
distancesX.x = -point.y;
|
||||||
|
distancesX.y = -point.x;
|
||||||
|
rotationZ += MathUtils.DEG90;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void {
|
||||||
|
|
||||||
|
_object.addEventListener(type, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function clone():Object3D {
|
||||||
|
var copyObject:Mesh = _object.clone() as Mesh;
|
||||||
|
copyObject.cloneMaterialToAllSurfaces(_material as TextureMaterial);
|
||||||
|
var prop:Prop = new Prop(copyObject, _library, _group, false);
|
||||||
|
prop.distancesX = distancesX.clone();
|
||||||
|
prop.distancesY = distancesY.clone();
|
||||||
|
prop.distancesZ = distancesZ.clone();
|
||||||
|
prop._multi = _multi;
|
||||||
|
prop.name = name;
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Выравнивание по сетке.
|
||||||
|
*/
|
||||||
|
public function snapCoords():void {
|
||||||
|
|
||||||
|
if (_multi) {
|
||||||
|
x = floorTo(x, EditorScene.hBase2);
|
||||||
|
y = floorTo(y, EditorScene.hBase2);
|
||||||
|
} else {
|
||||||
|
x = roundTo(x, EditorScene.hBase2);
|
||||||
|
y = roundTo(y, EditorScene.hBase2);
|
||||||
|
}
|
||||||
|
|
||||||
|
z = floorTo(z, EditorScene.vBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/alternativa/editor/prop/Spawn.as
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package alternativa.editor.prop {
|
||||||
|
import alternativa.editor.scene.EditorScene;
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.materials.TextureMaterial;
|
||||||
|
import alternativa.utils.MathUtils;
|
||||||
|
|
||||||
|
import flash.geom.Point;
|
||||||
|
|
||||||
|
public class Spawn extends Prop {
|
||||||
|
|
||||||
|
public function Spawn(object:Object3D, library:String, group:String, needCalculate:Boolean=true) {
|
||||||
|
super(object, library, group, needCalculate);
|
||||||
|
type = Prop.SPAWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function calculate():void {
|
||||||
|
|
||||||
|
distancesX = new Point(-EditorScene.hBase, EditorScene.hBase);
|
||||||
|
distancesY = new Point(-EditorScene.hBase, EditorScene.hBase);
|
||||||
|
distancesZ = new Point(0, EditorScene.vBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function rotate(plus:Boolean):void {
|
||||||
|
if (plus) {
|
||||||
|
rotationZ += MathUtils.DEG5;
|
||||||
|
} else {
|
||||||
|
rotationZ -= MathUtils.DEG5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function clone():Object3D {
|
||||||
|
|
||||||
|
var copyObject:Mesh = _object.clone() as Mesh;
|
||||||
|
copyObject.cloneMaterialToAllSurfaces(_material as TextureMaterial);
|
||||||
|
// Создаем проп
|
||||||
|
var copy:Spawn = new Spawn(copyObject, _library, _group, false);
|
||||||
|
// Копируем свойства
|
||||||
|
copy.distancesX = distancesX.clone();
|
||||||
|
copy.distancesY = distancesY.clone();
|
||||||
|
copy.distancesZ = distancesZ.clone();
|
||||||
|
copy._multi = _multi;
|
||||||
|
copy.name = name;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
142
src/alternativa/editor/prop/Tile.as
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package alternativa.editor.prop {
|
||||||
|
|
||||||
|
import alternativa.engine3d.alternativa3d;
|
||||||
|
import alternativa.engine3d.core.Mesh;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.materials.TextureMaterial;
|
||||||
|
import alternativa.types.Map;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.types.Set;
|
||||||
|
import alternativa.types.Texture;
|
||||||
|
|
||||||
|
import flash.display.BitmapData;
|
||||||
|
import flash.geom.Matrix;
|
||||||
|
|
||||||
|
use namespace alternativa3d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author danilova
|
||||||
|
*/
|
||||||
|
public class Tile extends Prop {
|
||||||
|
|
||||||
|
private var collisionMaterial:CustomFillMaterial;
|
||||||
|
|
||||||
|
private var _isMirror:Boolean = false;
|
||||||
|
|
||||||
|
private var collisionBoxes:Set;
|
||||||
|
//
|
||||||
|
public var bitmaps:Map;
|
||||||
|
//
|
||||||
|
protected var _textureName:String = "";
|
||||||
|
|
||||||
|
public function Tile(object:Object3D, library:String, group:String, needCalculate:Boolean = true) {
|
||||||
|
super(object, library, group, needCalculate);
|
||||||
|
type = Prop.TILE;
|
||||||
|
collisionBoxes = new Set();
|
||||||
|
// Collision boxes
|
||||||
|
for (var child:* in object.children) {
|
||||||
|
var box:Mesh = child as Mesh;
|
||||||
|
box.cloneMaterialToAllSurfaces(null);
|
||||||
|
if (box.name.substr(0, 3) != "occ") {
|
||||||
|
collisionBoxes.add(box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collisionMaterial = new CustomFillMaterial(new Point3D(-1e10, -0.7e10, 0.4e10), 0xFF7F7F);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Показать коллижн-боксы.
|
||||||
|
*/
|
||||||
|
public function showCollisionBoxes():void {
|
||||||
|
|
||||||
|
for (var child:* in collisionBoxes) {
|
||||||
|
var box:Mesh = child as Mesh;
|
||||||
|
box.cloneMaterialToAllSurfaces(collisionMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaterial(null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Скрыть коллижн-боксы.
|
||||||
|
*/
|
||||||
|
public function hideCollisionBoxes():void {
|
||||||
|
|
||||||
|
for (var child:* in collisionBoxes) {
|
||||||
|
var box:Mesh = child as Mesh;
|
||||||
|
box.cloneMaterialToAllSurfaces(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaterial(_material);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get collisionGeometry():Set {
|
||||||
|
return collisionBoxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get textureName():String {
|
||||||
|
return _textureName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set textureName(value:String):void {
|
||||||
|
_textureName = value;
|
||||||
|
|
||||||
|
bitmapData = _isMirror ? getMirrorBitmapData(bitmaps[value]) : bitmaps[value];
|
||||||
|
_material = new TextureMaterial(new Texture(bitmapData));
|
||||||
|
if (_selected) {
|
||||||
|
_selectBitmapData.dispose();
|
||||||
|
select();
|
||||||
|
} else {
|
||||||
|
setMaterial(_material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function getMirrorBitmapData(bmd:BitmapData):BitmapData {
|
||||||
|
|
||||||
|
var mirrorBmd:BitmapData = new BitmapData(bmd.width, bmd.height);
|
||||||
|
mirrorBmd.draw(bmd, new Matrix(-1, 0, 0, 1, bmd.width, 0 ));
|
||||||
|
return mirrorBmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mirrorTexture():void {
|
||||||
|
|
||||||
|
_isMirror = !_isMirror;
|
||||||
|
|
||||||
|
bitmapData = getMirrorBitmapData(bitmapData);
|
||||||
|
(_material as TextureMaterial).texture = new Texture(bitmapData);
|
||||||
|
if (selected) {
|
||||||
|
_selectBitmapData.dispose();
|
||||||
|
select();
|
||||||
|
} else {
|
||||||
|
setMaterial(_material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get isMirror():Boolean {
|
||||||
|
return _isMirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function clone():Object3D {
|
||||||
|
|
||||||
|
var copyObject:Mesh = _object.clone() as Mesh;
|
||||||
|
copyObject.cloneMaterialToAllSurfaces(_material as TextureMaterial);
|
||||||
|
// Создаем проп
|
||||||
|
var copy:Tile = new Tile(copyObject, _library, _group, false);
|
||||||
|
// Копируем свойства
|
||||||
|
copy.distancesX = distancesX.clone();
|
||||||
|
copy.distancesY = distancesY.clone();
|
||||||
|
copy.distancesZ = distancesZ.clone();
|
||||||
|
copy._multi = _multi;
|
||||||
|
copy.name = name;
|
||||||
|
copy.bitmaps = bitmaps;
|
||||||
|
copy._textureName = _textureName;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/alternativa/editor/prop/TileSprite3D.as
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package alternativa.editor.prop {
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.core.Sprite3D;
|
||||||
|
import alternativa.engine3d.core.Vertex;
|
||||||
|
import alternativa.engine3d.materials.Material;
|
||||||
|
import alternativa.engine3d.materials.SpriteTextureMaterial;
|
||||||
|
import alternativa.types.Map;
|
||||||
|
import alternativa.types.Texture;
|
||||||
|
|
||||||
|
import flash.geom.Point;
|
||||||
|
|
||||||
|
public class TileSprite3D extends Tile {
|
||||||
|
private var spriteTextureMaterial:SpriteTextureMaterial;
|
||||||
|
|
||||||
|
public function TileSprite3D(object:Sprite3D, library:String, group:String, needCalculate:Boolean=true) {
|
||||||
|
super(object, library, group, needCalculate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get scale():Number {
|
||||||
|
return (_object as Sprite3D).scaleX;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function calculate():void {
|
||||||
|
distancesX = new Point();
|
||||||
|
distancesY = new Point();
|
||||||
|
distancesZ = new Point();
|
||||||
|
_multi = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function setMaterial(material:Material):void {
|
||||||
|
var spriteMaterial:SpriteTextureMaterial = material as SpriteTextureMaterial;
|
||||||
|
if (spriteMaterial) {
|
||||||
|
spriteMaterial.originX = spriteTextureMaterial.originX;
|
||||||
|
spriteMaterial.originY = spriteTextureMaterial.originY;
|
||||||
|
}
|
||||||
|
(_object as Sprite3D).material = spriteMaterial;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected function initBitmapData():void {
|
||||||
|
_material = (_object as Sprite3D).material;
|
||||||
|
spriteTextureMaterial = _material as SpriteTextureMaterial;
|
||||||
|
bitmapData = spriteTextureMaterial.texture.bitmapData;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function get vertices():Map {
|
||||||
|
var vertex:Vertex = new Vertex(0, 0, 0);
|
||||||
|
var map:Map = new Map();
|
||||||
|
map.add("1", vertex);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected function get newSelectedMaterial():Material {
|
||||||
|
var material:SpriteTextureMaterial = new SpriteTextureMaterial(new Texture(_selectBitmapData));
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function clone():Object3D {
|
||||||
|
|
||||||
|
var copyObject:Sprite3D = _object.clone() as Sprite3D;
|
||||||
|
copyObject.material = _material.clone() as SpriteTextureMaterial;
|
||||||
|
// Создаем проп
|
||||||
|
var copy:TileSprite3D = new TileSprite3D(copyObject, _library, _group, false);
|
||||||
|
// Копируем свойства
|
||||||
|
copy.distancesX = distancesX.clone();
|
||||||
|
copy.distancesY = distancesY.clone();
|
||||||
|
copy.distancesZ = distancesZ.clone();
|
||||||
|
copy._multi = _multi;
|
||||||
|
copy.name = name;
|
||||||
|
copy.bitmaps = bitmaps;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
321
src/alternativa/editor/scene/CursorScene.as
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
package alternativa.editor.scene {
|
||||||
|
|
||||||
|
import alternativa.editor.prop.CustomFillMaterial;
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.prop.TileSprite3D;
|
||||||
|
import alternativa.engine3d.controllers.WalkController;
|
||||||
|
import alternativa.engine3d.core.Camera3D;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.display.View;
|
||||||
|
import alternativa.engine3d.materials.Material;
|
||||||
|
import alternativa.engine3d.materials.SpriteTextureMaterial;
|
||||||
|
import alternativa.types.Matrix3D;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.types.Set;
|
||||||
|
import alternativa.types.Texture;
|
||||||
|
import alternativa.utils.MathUtils;
|
||||||
|
|
||||||
|
import flash.display.BitmapData;
|
||||||
|
import flash.display.BlendMode;
|
||||||
|
import flash.display.DisplayObject;
|
||||||
|
import flash.display.Graphics;
|
||||||
|
import flash.display.Shape;
|
||||||
|
import flash.geom.Matrix;
|
||||||
|
import flash.geom.Point;
|
||||||
|
|
||||||
|
public class CursorScene extends EditorScene {
|
||||||
|
// Курсорный проп
|
||||||
|
protected var _object:Prop;
|
||||||
|
private var redMaterial:Material;
|
||||||
|
private var greenMaterial:Material;
|
||||||
|
private var material:Material;
|
||||||
|
// Индикатор свободного состояния ячейки, в которой находится курсор
|
||||||
|
private var _freeState:Boolean = true;
|
||||||
|
// Контроллер камеры
|
||||||
|
public var cameraController:WalkController;
|
||||||
|
// Контроллер для контейнера камеры
|
||||||
|
public var containerController:WalkController;
|
||||||
|
// Контейнер для камеры
|
||||||
|
public var container:Object3D;
|
||||||
|
// Индикатор вертикального перемещения
|
||||||
|
protected var verticalMoving:Boolean = false;
|
||||||
|
// stage
|
||||||
|
private var eventSourceObject:DisplayObject;
|
||||||
|
// Индикатор режима snap
|
||||||
|
protected var _snapMode:Boolean = true;
|
||||||
|
//
|
||||||
|
private var matrix:Matrix = new Matrix();
|
||||||
|
|
||||||
|
private var axisIndicatorOverlay:Shape;
|
||||||
|
private var axisIndicatorSize:Number = 30;
|
||||||
|
|
||||||
|
[Embed (source = "red_cursor.jpg")] private static var redClass:Class;
|
||||||
|
private static const redBmp:BitmapData = new redClass().bitmapData;
|
||||||
|
[Embed (source = "green_cursor.jpg")] private static var greenClass:Class;
|
||||||
|
private static const greenBmp:BitmapData = new greenClass().bitmapData;
|
||||||
|
|
||||||
|
public function CursorScene(eventSourceObject:DisplayObject) {
|
||||||
|
super();
|
||||||
|
this.eventSourceObject = eventSourceObject;
|
||||||
|
initControllers();
|
||||||
|
view.addChild(axisIndicatorOverlay = new Shape());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function initControllers():void {
|
||||||
|
// Подключение контроллера камеры
|
||||||
|
cameraController = new WalkController(eventSourceObject);
|
||||||
|
cameraController.object = camera;
|
||||||
|
cameraController.speedMultiplier = 4;
|
||||||
|
cameraController.speedThreshold = 1;
|
||||||
|
cameraController.mouseEnabled = false;
|
||||||
|
|
||||||
|
cameraController.coords = new Point3D(250, -7800, 4670);
|
||||||
|
|
||||||
|
// Контейнер
|
||||||
|
container = new Object3D();
|
||||||
|
root.addChild(container);
|
||||||
|
// Контроллер контейнера
|
||||||
|
containerController = new WalkController(eventSourceObject);
|
||||||
|
containerController.object = container;
|
||||||
|
containerController.mouseEnabled = false;
|
||||||
|
container.addChild(camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Установка курсора.
|
||||||
|
* @param value
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function set object(value:Prop):void {
|
||||||
|
var point3D:Point3D;
|
||||||
|
if (_object) {
|
||||||
|
point3D = _object.coords;
|
||||||
|
if (_visible) {
|
||||||
|
root.removeChild(_object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_object = value;
|
||||||
|
material = _object.material.clone();
|
||||||
|
material.alpha = 0.5;
|
||||||
|
|
||||||
|
if (point3D) {
|
||||||
|
_object.coords = point3D;
|
||||||
|
}
|
||||||
|
if (_visible) {
|
||||||
|
root.addChild(_object);
|
||||||
|
}
|
||||||
|
if (_snapMode) {
|
||||||
|
snapObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get object():Prop {
|
||||||
|
return _object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public function set snapMode(value:Boolean):void {
|
||||||
|
if (_snapMode != value && _object) {
|
||||||
|
|
||||||
|
_snapMode = value;
|
||||||
|
if (value) {
|
||||||
|
snapObject();
|
||||||
|
} else {
|
||||||
|
_object.setMaterial(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get snapMode():Boolean {
|
||||||
|
return _snapMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function snapObject():void {
|
||||||
|
createMaterials();
|
||||||
|
_object.snapCoords();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cоздание зеленого и красного материалов.
|
||||||
|
*/
|
||||||
|
private function createMaterials():void {
|
||||||
|
|
||||||
|
var redBmd:BitmapData = _object.bitmapData.clone();
|
||||||
|
var greenBmd:BitmapData = redBmd.clone();
|
||||||
|
matrix.a = redBmd.width/redBmp.width;
|
||||||
|
matrix.d = matrix.a;
|
||||||
|
redBmd.draw(redBmp, matrix, null, BlendMode.HARDLIGHT);
|
||||||
|
greenBmd.draw(greenBmp, matrix, null, BlendMode.HARDLIGHT);
|
||||||
|
|
||||||
|
if (_object is TileSprite3D) {
|
||||||
|
greenMaterial = new SpriteTextureMaterial(new Texture(greenBmd));
|
||||||
|
redMaterial = new SpriteTextureMaterial(new Texture(redBmd));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// greenMaterial = new TextureMaterial(new Texture(greenBmd));
|
||||||
|
// redMaterial = new TextureMaterial(new Texture(redBmd));
|
||||||
|
greenMaterial = new CustomFillMaterial(new Point3D(-1e10, -0.7e10, 0.4e10), 0x00FF00);
|
||||||
|
redMaterial = new CustomFillMaterial(new Point3D(-1e10, -0.7e10, 0.4e10), 0xFF0000);
|
||||||
|
}
|
||||||
|
|
||||||
|
redMaterial.alpha = greenMaterial.alpha = 0.8;
|
||||||
|
|
||||||
|
updateMaterial();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Перемещение курсора мышью.
|
||||||
|
*/
|
||||||
|
public function moveCursorByMouse():void {
|
||||||
|
if (_object) {
|
||||||
|
var point:Point3D = view.projectViewPointToPlane(new Point(view.mouseX, view.mouseY), znormal, _object.z);
|
||||||
|
_object.x = point.x;
|
||||||
|
_object.y = point.y;
|
||||||
|
if (_snapMode) {
|
||||||
|
_object.snapCoords();
|
||||||
|
}
|
||||||
|
updateMaterial();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get freeState():Boolean {
|
||||||
|
return _freeState;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация основной сцены.
|
||||||
|
*/
|
||||||
|
override protected function initScene():void {
|
||||||
|
root = new Object3D();
|
||||||
|
|
||||||
|
// Добавление камеры и области вывода
|
||||||
|
camera = new Camera3D();
|
||||||
|
camera.rotationX = -MathUtils.DEG90 - MathUtils.DEG30;
|
||||||
|
|
||||||
|
view = new View(camera);
|
||||||
|
view.interactive = false;
|
||||||
|
view.mouseEnabled = false;
|
||||||
|
view.mouseChildren = false;
|
||||||
|
|
||||||
|
view.graphics.beginFill(0xFFFFFF);
|
||||||
|
view.graphics.drawRect(0, 0, 1, 1);
|
||||||
|
view.graphics.endFill();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function updateMaterial():void {
|
||||||
|
if (_object && _snapMode) {
|
||||||
|
if (occupyMap.isConflict(_object)) {
|
||||||
|
_freeState = false;
|
||||||
|
_object.setMaterial(redMaterial);
|
||||||
|
} else {
|
||||||
|
_freeState = true;
|
||||||
|
_object.setMaterial(greenMaterial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clear():void {
|
||||||
|
if (_object) {
|
||||||
|
if (root.getChildByName(_object.name)) {
|
||||||
|
root.removeChild(_object);
|
||||||
|
}
|
||||||
|
_object = null;
|
||||||
|
_visible = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Рисование координатных осей.
|
||||||
|
* @param matrix
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function drawAxis(matrix:Matrix3D):void {
|
||||||
|
var gfx:Graphics = axisIndicatorOverlay.graphics;
|
||||||
|
var centreX:Number = axisIndicatorSize;
|
||||||
|
var centreY:Number = 0;
|
||||||
|
gfx.clear();
|
||||||
|
gfx.lineStyle(2, 0xFF0000);
|
||||||
|
gfx.moveTo(centreX, centreY);
|
||||||
|
gfx.lineTo(matrix.a*axisIndicatorSize + centreX, matrix.b*axisIndicatorSize + centreY);
|
||||||
|
gfx.lineStyle(2, 0x00FF00);
|
||||||
|
gfx.moveTo(centreX, centreY);
|
||||||
|
gfx.lineTo(matrix.e*axisIndicatorSize + centreX, matrix.f*axisIndicatorSize + centreY);
|
||||||
|
gfx.lineStyle(2, 0x0000FF);
|
||||||
|
gfx.moveTo(centreX, centreY);
|
||||||
|
gfx.lineTo(matrix.i*axisIndicatorSize + centreX, matrix.j*axisIndicatorSize + centreY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var _visible:Boolean = false;
|
||||||
|
|
||||||
|
public function set visible(value:Boolean):void {
|
||||||
|
|
||||||
|
if (value != _visible) {
|
||||||
|
_visible = value;
|
||||||
|
if (_object) {
|
||||||
|
if (_visible) {
|
||||||
|
root.addChild(_object);
|
||||||
|
updateMaterial();
|
||||||
|
} else {
|
||||||
|
root.removeChild(_object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get visible():Boolean {
|
||||||
|
return _visible;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function moveByArrows(keyCode:uint, sector:int):void {
|
||||||
|
move(_object, keyCode, sector);
|
||||||
|
updateMaterial();
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function viewResize(viewWidth:Number, viewHeight:Number):void {
|
||||||
|
super.viewResize(viewWidth, viewHeight);
|
||||||
|
axisIndicatorOverlay.y = view.height - axisIndicatorSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function rotateProps(plus:Boolean, props:Set = null):void {
|
||||||
|
|
||||||
|
props = new Set();
|
||||||
|
props.add(_object);
|
||||||
|
super.rotateProps(plus, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
248
src/alternativa/editor/scene/EditorScene.as
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
package alternativa.editor.scene {
|
||||||
|
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.engine3d.core.Camera3D;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.core.Scene3D;
|
||||||
|
import alternativa.engine3d.display.View;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.types.Set;
|
||||||
|
import alternativa.utils.KeyboardUtils;
|
||||||
|
import alternativa.utils.MathUtils;
|
||||||
|
|
||||||
|
import flash.geom.Point;
|
||||||
|
|
||||||
|
public class EditorScene extends Scene3D {
|
||||||
|
public var camera:Camera3D;
|
||||||
|
public var view:View;
|
||||||
|
// Карта занятых ячеек
|
||||||
|
public var occupyMap:OccupyMap;
|
||||||
|
|
||||||
|
public static const hBase:Number = 250;
|
||||||
|
public static const hBase2:Number = 2*hBase;
|
||||||
|
public static const vBase:Number = 300;
|
||||||
|
|
||||||
|
protected var znormal:Point3D = new Point3D(0, 0, 1);
|
||||||
|
protected var ynormal:Point3D = new Point3D(0, 1, 0);
|
||||||
|
protected var xnormal:Point3D = new Point3D(1, 0, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function EditorScene() {
|
||||||
|
super();
|
||||||
|
// Инициализация основной сцены
|
||||||
|
initScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Корректировка размеров view.
|
||||||
|
* @param viewWidth ширина
|
||||||
|
* @param viewHeight высота
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function viewResize(viewWidth:Number, viewHeight:Number):void {
|
||||||
|
|
||||||
|
view.width = viewWidth;
|
||||||
|
view.height = viewHeight;
|
||||||
|
calculate();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация основной сцены.
|
||||||
|
*/
|
||||||
|
protected function initScene():void {
|
||||||
|
root = new Object3D();
|
||||||
|
|
||||||
|
// Добавление камеры и области вывода
|
||||||
|
camera = new Camera3D();
|
||||||
|
camera.rotationX = -MathUtils.DEG90 - MathUtils.DEG30;
|
||||||
|
camera.coords = new Point3D(250, -7800, 4670);
|
||||||
|
root.addChild(camera);
|
||||||
|
|
||||||
|
view = new View(camera);
|
||||||
|
view.interactive = true;
|
||||||
|
view.buttonMode = true;
|
||||||
|
view.useHandCursor = false;
|
||||||
|
|
||||||
|
view.graphics.beginFill(0xFFFFFF);
|
||||||
|
view.graphics.drawRect(0, 0, 1, 1);
|
||||||
|
view.graphics.endFill();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Переопределяется наследниками.
|
||||||
|
* @param keyCode
|
||||||
|
* @param sector
|
||||||
|
*/
|
||||||
|
public function moveByArrows(keyCode:uint, sector:int):void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function move(prop:Prop, keyCode:uint, sector:int):void {
|
||||||
|
|
||||||
|
if (prop) {
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyboardUtils.UP:
|
||||||
|
|
||||||
|
switch (sector) {
|
||||||
|
case 1:
|
||||||
|
prop.x -= hBase2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
prop.y += hBase2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
prop.x += hBase2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
prop.y -= hBase2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyboardUtils.DOWN:
|
||||||
|
switch (sector) {
|
||||||
|
case 1:
|
||||||
|
prop.x += hBase2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
prop.y -= hBase2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
prop.x -= hBase2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
prop.y += hBase2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case KeyboardUtils.LEFT:
|
||||||
|
|
||||||
|
switch (sector) {
|
||||||
|
case 1:
|
||||||
|
prop.y -= hBase2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
prop.x -= hBase2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
prop.y += hBase2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
prop.x += hBase2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyboardUtils.RIGHT:
|
||||||
|
switch (sector) {
|
||||||
|
case 1:
|
||||||
|
prop.y += hBase2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
prop.x += hBase2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
prop.y -= hBase2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
prop.x -= hBase2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Вычисляет центр группы пропов.
|
||||||
|
* @param props
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function getCentrePropsGroup(props:Set = null):Point {
|
||||||
|
|
||||||
|
var minX:Number = Number.POSITIVE_INFINITY;
|
||||||
|
var maxX:Number = Number.NEGATIVE_INFINITY;
|
||||||
|
var minY:Number = Number.POSITIVE_INFINITY;
|
||||||
|
var maxY:Number = Number.NEGATIVE_INFINITY;
|
||||||
|
// Среднее арифметическое центров
|
||||||
|
var averageX:Number = 0;
|
||||||
|
var averageY:Number = 0;
|
||||||
|
|
||||||
|
for (var p:* in props) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
var left:Number = prop.distancesX.x + prop.x;
|
||||||
|
var right:Number = prop.distancesX.y + prop.x;
|
||||||
|
if (left < minX) {
|
||||||
|
minX = left;
|
||||||
|
}
|
||||||
|
if (right > maxX) {
|
||||||
|
maxX = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
left = prop.distancesY.x + prop.y;
|
||||||
|
right = prop.distancesY.y + prop.y;
|
||||||
|
if (left < minY) {
|
||||||
|
minY = left;
|
||||||
|
}
|
||||||
|
if (right > maxY) {
|
||||||
|
maxY = right;
|
||||||
|
}
|
||||||
|
averageX += prop.x;
|
||||||
|
averageY += prop.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем протяженность по x и по y на четность
|
||||||
|
var modX:Number = (maxX - minX)/EditorScene.hBase2 % 2;
|
||||||
|
if ( modX != ((maxY - minY)/EditorScene.hBase2 % 2)) {
|
||||||
|
|
||||||
|
if (modX != 0) {
|
||||||
|
// Если протяженность по x нечетная, прибавим ширину ячейки со стороны, ближайшей к центру
|
||||||
|
averageX = averageX/props.length;
|
||||||
|
if (Math.abs(averageX - maxX) < Math.abs(averageX - minX)) {
|
||||||
|
maxX += EditorScene.hBase2;
|
||||||
|
} else {
|
||||||
|
minX -= EditorScene.hBase2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
averageY = averageY/props.length;
|
||||||
|
if (Math.abs(averageY - maxY) < Math.abs(averageY - minY)) {
|
||||||
|
maxY += EditorScene.hBase2;
|
||||||
|
} else {
|
||||||
|
minY -= EditorScene.hBase2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Point((maxX + minX)/2, (maxY + minY)/2);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Поворот пропа.
|
||||||
|
* @param plus флаг положительного поворота
|
||||||
|
* @param prop
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function rotateProps(plus:Boolean, props:Set = null):void {
|
||||||
|
|
||||||
|
var centre:Point = getCentrePropsGroup(props);
|
||||||
|
for (var p:* in props) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
var x:Number = prop.x;
|
||||||
|
var y:Number = prop.y;
|
||||||
|
if (plus) {
|
||||||
|
prop.x = y + centre.x - centre.y;
|
||||||
|
prop.y = -x + centre.y + centre.x;
|
||||||
|
} else {
|
||||||
|
prop.x = -y + centre.x + centre.y;
|
||||||
|
prop.y = x + centre.y - centre.x;
|
||||||
|
}
|
||||||
|
prop.rotate(plus);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
219
src/alternativa/editor/scene/MainScene.as
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
package alternativa.editor.scene {
|
||||||
|
import alternativa.editor.eventjournal.EventJournal;
|
||||||
|
import alternativa.editor.eventjournal.EventJournalItem;
|
||||||
|
import alternativa.editor.export.BinaryExporter;
|
||||||
|
import alternativa.editor.export.FileExporter;
|
||||||
|
import alternativa.editor.export.TanksXmlExporter;
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.prop.Tile;
|
||||||
|
import alternativa.engine3d.core.Object3D;
|
||||||
|
import alternativa.engine3d.materials.WireMaterial;
|
||||||
|
import alternativa.engine3d.primitives.Plane;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.types.Set;
|
||||||
|
|
||||||
|
import flash.filesystem.FileStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Главная сцена.
|
||||||
|
* @author danilova
|
||||||
|
*/
|
||||||
|
public class MainScene extends PropsScene {
|
||||||
|
|
||||||
|
public static const EXPORT_BINARY:int = 1;
|
||||||
|
public static const EXPORT_XML:int = 2;
|
||||||
|
|
||||||
|
// Сетка
|
||||||
|
private var grid:Plane;
|
||||||
|
|
||||||
|
private var exporters:Object = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function MainScene() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// Сетка
|
||||||
|
var count:int = 15;
|
||||||
|
var width:Number = count*hBase2;
|
||||||
|
grid = new Plane(width, width, count, count);
|
||||||
|
grid.cloneMaterialToAllSurfaces(new WireMaterial());
|
||||||
|
root.addChild(grid);
|
||||||
|
grid.x = hBase;
|
||||||
|
grid.y = hBase;
|
||||||
|
grid.mouseEnabled = false;
|
||||||
|
|
||||||
|
exporters[EXPORT_BINARY] = new BinaryExporter(root);
|
||||||
|
exporters[EXPORT_XML] = new TanksXmlExporter(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
override public function set root(value:Object3D):void {
|
||||||
|
super.root = value;
|
||||||
|
for each (var exp:FileExporter in exporters) exp.root = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @param stream
|
||||||
|
*/
|
||||||
|
public function export(type:int, stream:FileStream):void {
|
||||||
|
(exporters[type] as FileExporter).exportToFileStream(stream);
|
||||||
|
_changed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Перемещение пропов.
|
||||||
|
* @param props перемещаемые пропы
|
||||||
|
* @param delta смещение
|
||||||
|
*/
|
||||||
|
public function moveProps(props:Set, delta:Point3D):void {
|
||||||
|
|
||||||
|
for (var p:* in props) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
occupyMap.free(prop);
|
||||||
|
prop.x -= delta.x;
|
||||||
|
prop.y -= delta.y;
|
||||||
|
prop.z -= delta.z;
|
||||||
|
if (snapMode) {
|
||||||
|
prop.snapCoords();
|
||||||
|
occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отмена действия.
|
||||||
|
* @param e отменяемое событие
|
||||||
|
*/
|
||||||
|
public function undo(e:EventJournalItem):void {
|
||||||
|
var props:Set = e.props;
|
||||||
|
var p:*;
|
||||||
|
|
||||||
|
switch (e.operation) {
|
||||||
|
case EventJournal.ADD:
|
||||||
|
deleteProps(props);
|
||||||
|
break;
|
||||||
|
case EventJournal.COPY:
|
||||||
|
deleteProps(props);
|
||||||
|
break;
|
||||||
|
case EventJournal.DELETE:
|
||||||
|
for (p in props) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
prop.deselect();
|
||||||
|
addProp(prop, prop.coords, prop.rotationZ, false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EventJournal.MOVE:
|
||||||
|
moveProps(props, e.oldState);
|
||||||
|
(e.oldState as Point3D).multiply(-1);
|
||||||
|
break;
|
||||||
|
case EventJournal.ROTATE:
|
||||||
|
rotateProps(e.oldState, props);
|
||||||
|
e.oldState = !e.oldState;
|
||||||
|
break;
|
||||||
|
case EventJournal.CHANGE_TEXTURE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возврат действия.
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
public function redo(e:EventJournalItem):void {
|
||||||
|
var props:Set = e.props;
|
||||||
|
var prop:Prop;
|
||||||
|
var p:*;
|
||||||
|
switch (e.operation) {
|
||||||
|
case EventJournal.ADD:
|
||||||
|
prop = props.peek();
|
||||||
|
addProp(prop, prop.coords, prop.rotationZ, false);
|
||||||
|
break;
|
||||||
|
case EventJournal.COPY:
|
||||||
|
for (p in props) {
|
||||||
|
prop = p;
|
||||||
|
addProp(prop, prop.coords, prop.rotationZ, false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EventJournal.DELETE:
|
||||||
|
deleteProps(props);
|
||||||
|
break;
|
||||||
|
case EventJournal.MOVE:
|
||||||
|
moveProps(props, e.oldState);
|
||||||
|
(e.oldState as Point3D).multiply(-1);
|
||||||
|
break;
|
||||||
|
case EventJournal.ROTATE:
|
||||||
|
rotateProps(e.oldState, props);
|
||||||
|
e.oldState = !e.oldState;
|
||||||
|
break;
|
||||||
|
case EventJournal.CHANGE_TEXTURE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Синхронизация с камерой главной сцены
|
||||||
|
* @param cameraCoords
|
||||||
|
* @param rotationX
|
||||||
|
* @param rotationY
|
||||||
|
* @param rotationZ
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function synchronize(cameraCoords:Point3D, rotationX:Number, rotationY:Number, rotationZ:Number):void {
|
||||||
|
|
||||||
|
camera.coords = cameraCoords;
|
||||||
|
camera.rotationX = rotationX;
|
||||||
|
camera.rotationY = rotationY;
|
||||||
|
camera.rotationZ = rotationZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function showCollisionBoxes():void {
|
||||||
|
|
||||||
|
for (var child:* in root.children) {
|
||||||
|
var tile:Tile = child as Tile;
|
||||||
|
if (tile) {
|
||||||
|
tile.showCollisionBoxes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function hideCollisionBoxes():void {
|
||||||
|
|
||||||
|
for (var child:* in root.children) {
|
||||||
|
var tile:Tile = child as Tile;
|
||||||
|
if (tile) {
|
||||||
|
tile.hideCollisionBoxes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function showGrid():void {
|
||||||
|
root.addChild(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function hideGrid():void {
|
||||||
|
root.removeChild(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
222
src/alternativa/editor/scene/OccupyMap.as
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
package alternativa.editor.scene {
|
||||||
|
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.types.Map;
|
||||||
|
import alternativa.types.Set;
|
||||||
|
|
||||||
|
public class OccupyMap {
|
||||||
|
private var map:Map;
|
||||||
|
|
||||||
|
public function OccupyMap() {
|
||||||
|
|
||||||
|
map = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function occupy(prop:Prop):void {
|
||||||
|
if (prop.free) {
|
||||||
|
var z1:Number = prop.distancesZ.x + prop.z;
|
||||||
|
var z2:Number = prop.distancesZ.y + prop.z;
|
||||||
|
|
||||||
|
var mapZ:Map = new Map();
|
||||||
|
for (var i:Number = z1; i < z2; i += EditorScene.vBase) {
|
||||||
|
mapZ.add(i, [prop]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var y1:Number = prop.distancesY.x + prop.y;
|
||||||
|
var y2:Number = prop.distancesY.y + prop.y;
|
||||||
|
|
||||||
|
var setY:Set = new Set();
|
||||||
|
for (i = y1; i < y2; i += EditorScene.hBase) {
|
||||||
|
setY.add(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var x1:Number = prop.distancesX.x + prop.x;
|
||||||
|
var x2:Number = prop.distancesX.y + prop.x;
|
||||||
|
|
||||||
|
for (var x:Number = x1; x < x2; x += EditorScene.hBase) {
|
||||||
|
for (var y:Number = y1; y < y2; y += EditorScene.hBase) {
|
||||||
|
for (var z:Number = z1; z < z2; z += EditorScene.vBase) {
|
||||||
|
addElement(x, y, z, prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
prop.free = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addElement(x:Number, y:Number, z:Number, prop:Prop):void {
|
||||||
|
|
||||||
|
var mapY:Map = map[x];
|
||||||
|
if (!mapY) {
|
||||||
|
mapY = new Map();
|
||||||
|
map[x] = mapY;
|
||||||
|
}
|
||||||
|
var mapZ:Map = mapY[y];
|
||||||
|
if (!mapZ) {
|
||||||
|
mapZ = new Map();
|
||||||
|
mapY[y] = mapZ;
|
||||||
|
}
|
||||||
|
var props:Array = mapZ[z];
|
||||||
|
if (!props) {
|
||||||
|
mapZ.add(z, [prop]);
|
||||||
|
} else {
|
||||||
|
props.push(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function free(prop:Prop):void {
|
||||||
|
if (!prop.free) {
|
||||||
|
var z1:Number = prop.distancesZ.x + prop.z;
|
||||||
|
var z2:Number = prop.distancesZ.y + prop.z;
|
||||||
|
|
||||||
|
var setZ:Set = new Set();
|
||||||
|
for (var i:Number = z1; i < z2; i += EditorScene.vBase) {
|
||||||
|
setZ.add(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var y1:Number = prop.distancesY.x + prop.y;
|
||||||
|
var y2:Number = prop.distancesY.y + prop.y;
|
||||||
|
|
||||||
|
var setY:Set = new Set();
|
||||||
|
for (i = y1; i < y2; i += EditorScene.hBase) {
|
||||||
|
setY.add(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var x1:Number = prop.distancesX.x + prop.x;
|
||||||
|
var x2:Number = prop.distancesX.y + prop.x;
|
||||||
|
|
||||||
|
for (i = x1; i < x2; i += EditorScene.hBase) {
|
||||||
|
var mapY:Map = map[i];
|
||||||
|
if (mapY) {
|
||||||
|
for (var cy:* in setY) {
|
||||||
|
var y:Number = cy;
|
||||||
|
var mapZ:Map = mapY[y];
|
||||||
|
if (mapZ) {
|
||||||
|
for (var cz:* in setZ) {
|
||||||
|
var z:Number = cz;
|
||||||
|
var arr:Array = mapZ[z];
|
||||||
|
if (arr) {
|
||||||
|
var index:int = arr.indexOf(prop);
|
||||||
|
if (index > -1) {
|
||||||
|
arr.splice(index, 1);
|
||||||
|
if (arr.length == 0) {
|
||||||
|
mapZ.remove(z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mapZ.length == 0) {
|
||||||
|
mapY.remove(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapY.length == 0) {
|
||||||
|
map.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prop.free = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isOccupy(x:Number, y:Number, z:Number):Array {
|
||||||
|
|
||||||
|
var mapY:Map = map[x];
|
||||||
|
if (mapY) {
|
||||||
|
var mapZ:Map = mapY[y];
|
||||||
|
if (mapZ) {
|
||||||
|
if (mapZ.hasKey(z)) {
|
||||||
|
return mapZ[z];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function clear():void {
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConflict(prop:Prop):Boolean {
|
||||||
|
var x1:Number = prop.distancesX.x + prop.x;
|
||||||
|
var x2:Number = prop.distancesX.y + prop.x;
|
||||||
|
for (var i:Number = x1; i < x2; i += EditorScene.hBase) {
|
||||||
|
var y1:Number = prop.distancesY.x + prop.y;
|
||||||
|
var y2:Number = prop.distancesY.y + prop.y;
|
||||||
|
for (var j:Number = y1; j < y2; j += EditorScene.hBase) {
|
||||||
|
var z1:Number = prop.distancesZ.x + prop.z;
|
||||||
|
var z2:Number = prop.distancesZ.y + prop.z;
|
||||||
|
for (var k:Number = z1; k < z2; k += EditorScene.vBase) {
|
||||||
|
var props:Array = isOccupy(i, j, k);
|
||||||
|
|
||||||
|
if (props && (props.indexOf(prop) == -1 || props.length > 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConflictGroup(prop:Prop):Boolean {
|
||||||
|
|
||||||
|
var x1:Number = prop.distancesX.x + prop.x;
|
||||||
|
var x2:Number = prop.distancesX.y + prop.x;
|
||||||
|
for (var i:Number = x1; i < x2; i += EditorScene.hBase) {
|
||||||
|
var y1:Number = prop.distancesY.x + prop.y;
|
||||||
|
var y2:Number = prop.distancesY.y + prop.y;
|
||||||
|
for (var j:Number = y1; j < y2; j += EditorScene.hBase) {
|
||||||
|
var z1:Number = prop.distancesZ.x + prop.z;
|
||||||
|
var z2:Number = prop.distancesZ.y + prop.z;
|
||||||
|
for (var k:Number = z1; k < z2; k += EditorScene.vBase) {
|
||||||
|
var props:Array = isOccupy(i, j, k);
|
||||||
|
if (props) {
|
||||||
|
var len:int = props.length;
|
||||||
|
for (var p:int = 0; p < len; p++) {
|
||||||
|
var conflictProp:Prop = props[p];
|
||||||
|
if (conflictProp != prop && conflictProp.group == prop.group) {
|
||||||
|
// trace('name', conflictProp.name, i, j, k);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConflictProps():Set {
|
||||||
|
|
||||||
|
var conflictProps:Set = new Set();
|
||||||
|
for (var x:* in map) {
|
||||||
|
var mapY:Map = map[x];
|
||||||
|
for (var y:* in mapY) {
|
||||||
|
var mapZ:Map = mapY[y];
|
||||||
|
for (var z:* in mapZ) {
|
||||||
|
var props:Array = mapZ[z];
|
||||||
|
if (props && props.length > 1) {
|
||||||
|
for (var i:int = 0; i < props.length; i++) {
|
||||||
|
conflictProps.add(props[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conflictProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
700
src/alternativa/editor/scene/PropsScene.as
Normal file
@@ -0,0 +1,700 @@
|
|||||||
|
package alternativa.editor.scene {
|
||||||
|
import alternativa.editor.prop.Bonus;
|
||||||
|
import alternativa.editor.prop.Prop;
|
||||||
|
import alternativa.editor.prop.Tile;
|
||||||
|
import alternativa.editor.prop.TileSprite3D;
|
||||||
|
import alternativa.engine3d.events.MouseEvent3D;
|
||||||
|
import alternativa.types.Map;
|
||||||
|
import alternativa.types.Point3D;
|
||||||
|
import alternativa.types.Set;
|
||||||
|
import alternativa.utils.MathUtils;
|
||||||
|
|
||||||
|
import flash.display.Bitmap;
|
||||||
|
import flash.display.BitmapData;
|
||||||
|
import flash.events.Event;
|
||||||
|
import flash.geom.Point;
|
||||||
|
import flash.utils.getQualifiedClassName;
|
||||||
|
|
||||||
|
import gui.events.PropListEvent;
|
||||||
|
|
||||||
|
import mx.containers.HBox;
|
||||||
|
import mx.containers.Panel;
|
||||||
|
import mx.controls.CheckBox;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Главная сцена.
|
||||||
|
* @author danilova
|
||||||
|
*/
|
||||||
|
public class PropsScene extends EditorScene {
|
||||||
|
|
||||||
|
public var selectedProp:Prop;
|
||||||
|
// Выделенные пропы
|
||||||
|
public var selectedProps:Set;
|
||||||
|
// Индикатор нажатия на проп
|
||||||
|
public var propMouseDown:Boolean = false;
|
||||||
|
// Индикатор изменений на сцене
|
||||||
|
protected var _changed:Boolean = false;
|
||||||
|
// Индикатор режима выравнивания по сетке
|
||||||
|
public var snapMode:Boolean = true;
|
||||||
|
|
||||||
|
private var _texturePanel:TexturePanel;
|
||||||
|
private var _propertyPanel:Panel;
|
||||||
|
private var bonusPanel:HBox;
|
||||||
|
private var checkTypeMap:Map;
|
||||||
|
|
||||||
|
private var currentBitmaps:Map;
|
||||||
|
|
||||||
|
protected var hideProps:Array = [];
|
||||||
|
|
||||||
|
public var allowSelectingTypes:Set = new Set();
|
||||||
|
|
||||||
|
public function PropsScene() {
|
||||||
|
super();
|
||||||
|
occupyMap = new OccupyMap();
|
||||||
|
selectedProps = new Set();
|
||||||
|
allowSelectingTypes.add("Tile");
|
||||||
|
allowSelectingTypes.add("TileSprite3D");
|
||||||
|
allowSelectingTypes.add("Spawn");
|
||||||
|
allowSelectingTypes.add("Prop");
|
||||||
|
allowSelectingTypes.add("Bonus");
|
||||||
|
allowSelectingTypes.add("Flag");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get isTexturePanel():Boolean {
|
||||||
|
return (_texturePanel.visible && _texturePanel.selectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public function set propertyPanel(value:Panel):void {
|
||||||
|
_propertyPanel = value;
|
||||||
|
createBonusTypePanel();
|
||||||
|
_texturePanel = new TexturePanel();
|
||||||
|
_texturePanel.addEventListener(PropListEvent.SELECT, onTexturePanelSelect);
|
||||||
|
_propertyPanel.addChild(_texturePanel);
|
||||||
|
_texturePanel.visible = false;
|
||||||
|
_propertyPanel.addChild(bonusPanel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function createBonusTypePanel():void {
|
||||||
|
bonusPanel = new HBox();
|
||||||
|
bonusPanel.percentWidth = 100;
|
||||||
|
bonusPanel.visible = false;
|
||||||
|
checkTypeMap = new Map();
|
||||||
|
|
||||||
|
var damage:CheckBox = new CheckBox();
|
||||||
|
damage.label = "damageup";
|
||||||
|
damage.addEventListener(Event.CHANGE, onBonusTypeChange);
|
||||||
|
bonusPanel.addChild(damage);
|
||||||
|
checkTypeMap.add(damage.label, damage);
|
||||||
|
|
||||||
|
var armor:CheckBox = new CheckBox();
|
||||||
|
armor.label = "armorup";
|
||||||
|
armor.addEventListener(Event.CHANGE, onBonusTypeChange);
|
||||||
|
bonusPanel.addChild(armor);
|
||||||
|
checkTypeMap.add(armor.label, armor);
|
||||||
|
|
||||||
|
var nitro:CheckBox = new CheckBox();
|
||||||
|
nitro.label = "nitro";
|
||||||
|
nitro.addEventListener(Event.CHANGE, onBonusTypeChange);
|
||||||
|
bonusPanel.addChild(nitro);
|
||||||
|
checkTypeMap.add(nitro.label, nitro);
|
||||||
|
|
||||||
|
var repkit:CheckBox = new CheckBox();
|
||||||
|
repkit.label = "repkit";
|
||||||
|
repkit.addEventListener(Event.CHANGE, onBonusTypeChange);
|
||||||
|
bonusPanel.addChild(repkit);
|
||||||
|
checkTypeMap.add(repkit.label, repkit);
|
||||||
|
|
||||||
|
var check:CheckBox = new CheckBox();
|
||||||
|
check.label = "medkit";
|
||||||
|
check.addEventListener(Event.CHANGE, onBonusTypeChange);
|
||||||
|
bonusPanel.addChild(check);
|
||||||
|
checkTypeMap.add(check.label, check);
|
||||||
|
|
||||||
|
check = new CheckBox();
|
||||||
|
check.label = "money";
|
||||||
|
check.addEventListener(Event.CHANGE, onBonusTypeChange);
|
||||||
|
bonusPanel.addChild(check);
|
||||||
|
checkTypeMap.add(check.label, check);
|
||||||
|
|
||||||
|
check = new CheckBox();
|
||||||
|
check.label = "crystal";
|
||||||
|
check.addEventListener(Event.CHANGE, onBonusTypeChange);
|
||||||
|
bonusPanel.addChild(check);
|
||||||
|
checkTypeMap.add(check.label, check);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get changed():Boolean {
|
||||||
|
return _changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set changed(value:Boolean):void {
|
||||||
|
_changed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getClassName(qualifiedClassName:String):String {
|
||||||
|
|
||||||
|
//alternativa.editor.prop::
|
||||||
|
return qualifiedClassName.substr(25);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isAllowClassName(prop:Prop):Boolean {
|
||||||
|
|
||||||
|
return allowSelectingTypes.has(getClassName(getQualifiedClassName(prop)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function getCameraSector():int {
|
||||||
|
|
||||||
|
var sector:Number = camera.rotationZ/MathUtils.DEG90 % 4;
|
||||||
|
if ((sector >= -0.5 && sector <= 0.5) || (sector <= -3.5)) {
|
||||||
|
return 4;
|
||||||
|
} else if ((sector >= 0.5 && sector <= 1.5)
|
||||||
|
|| (sector >= -3.5 && sector <= -2.5) ) {
|
||||||
|
return 1;
|
||||||
|
} else if ((sector >= 1.5 && sector <= 2.5) ||
|
||||||
|
(sector >= -2.5 && sector <= -1.5)) {
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Перемещение выделенных пропов.
|
||||||
|
* @param verticalMoving индикатор вертикального движения
|
||||||
|
*/
|
||||||
|
public function moveSelectedProps(verticalMoving:Boolean):void {
|
||||||
|
|
||||||
|
if (selectedProp) {
|
||||||
|
var viewPoint:Point = new Point(view.mouseX, view.mouseY);
|
||||||
|
var point:Point3D;
|
||||||
|
var p:*;
|
||||||
|
|
||||||
|
// Стираем с карты
|
||||||
|
for (p in selectedProps) {
|
||||||
|
occupyMap.free(p as Prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
var deltaX:Number = 0;
|
||||||
|
var deltaY:Number = 0;
|
||||||
|
var deltaZ:Number = 0;
|
||||||
|
|
||||||
|
if (verticalMoving) {
|
||||||
|
var sector:Number = getCameraSector();
|
||||||
|
|
||||||
|
if (sector == 2 || sector == 4) {
|
||||||
|
point = view.projectViewPointToPlane(viewPoint, ynormal, selectedProp.y);
|
||||||
|
deltaX = point.x - selectedProp.x;
|
||||||
|
selectedProp.x = point.x;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
point = view.projectViewPointToPlane(viewPoint, xnormal, selectedProp.x);
|
||||||
|
deltaY = point.y - selectedProp.y;
|
||||||
|
selectedProp.y = point.y;
|
||||||
|
|
||||||
|
}
|
||||||
|
deltaZ = point.z - selectedProp.z;
|
||||||
|
selectedProp.z = point.z;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
point = view.projectViewPointToPlane(viewPoint, znormal, selectedProp.z);
|
||||||
|
|
||||||
|
deltaX = point.x - selectedProp.x;
|
||||||
|
deltaY = point.y - selectedProp.y;
|
||||||
|
|
||||||
|
selectedProp.x = point.x;
|
||||||
|
selectedProp.y = point.y;
|
||||||
|
|
||||||
|
}
|
||||||
|
// Смещаем все выделенные пропы
|
||||||
|
for (p in selectedProps) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
if (prop != selectedProp) {
|
||||||
|
prop.x += deltaX;
|
||||||
|
prop.y += deltaY;
|
||||||
|
prop.z += deltaZ;
|
||||||
|
}
|
||||||
|
if (snapMode || (prop is Tile && !(prop is TileSprite3D))) {
|
||||||
|
prop.snapCoords();
|
||||||
|
occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Перемещение пропа стрелками.
|
||||||
|
* @param prop проп
|
||||||
|
* @param keyCode код стрелки
|
||||||
|
* @param sector сектор, на который смотрит камера
|
||||||
|
*/
|
||||||
|
override public function moveByArrows(keyCode:uint, sector:int):void {
|
||||||
|
|
||||||
|
for (var p:* in selectedProps) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
occupyMap.free(prop);
|
||||||
|
move(prop, keyCode, sector);
|
||||||
|
if (snapMode) {
|
||||||
|
prop.snapCoords();
|
||||||
|
occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Вертикальное перемещение.
|
||||||
|
* @param down
|
||||||
|
*/
|
||||||
|
public function verticalMove(down:Boolean):void {
|
||||||
|
var delta:Number = vBase;
|
||||||
|
if (down) {
|
||||||
|
delta = -delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var p:* in selectedProps) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
occupyMap.free(prop);
|
||||||
|
prop.z += delta;
|
||||||
|
if (snapMode) {
|
||||||
|
occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Клик на проп.
|
||||||
|
*/
|
||||||
|
public function onPropMouseDown(e:MouseEvent3D):void {
|
||||||
|
if (!e.ctrlKey) {
|
||||||
|
var downProp:Prop = e.object as Prop;
|
||||||
|
if (isAllowClassName(downProp)) {
|
||||||
|
var selected:Boolean = downProp.selected;
|
||||||
|
if (e.shiftKey) {
|
||||||
|
if (e.altKey) {
|
||||||
|
if (selected) {
|
||||||
|
deselectProp(downProp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!selected) {
|
||||||
|
selectProp(downProp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (!selected) {
|
||||||
|
deselectProps();
|
||||||
|
selectProp(downProp);
|
||||||
|
} else {
|
||||||
|
selectedProp = downProp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
propMouseDown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onPropMouseOut(e:MouseEvent3D):void {
|
||||||
|
|
||||||
|
view.useHandCursor = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function onPropMouseOver(e:MouseEvent3D):void {
|
||||||
|
|
||||||
|
view.useHandCursor = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отменяет выделение пропов.
|
||||||
|
*/
|
||||||
|
public function deselectProps():void {
|
||||||
|
for (var p:* in selectedProps) {
|
||||||
|
(p as Prop).deselect();
|
||||||
|
}
|
||||||
|
selectedProps.clear();
|
||||||
|
selectedProp = null;
|
||||||
|
|
||||||
|
bonusPanel.visible = false;
|
||||||
|
_texturePanel.visible = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deselectProp(prop:Prop):void {
|
||||||
|
|
||||||
|
prop.deselect();
|
||||||
|
selectedProps.remove(prop);
|
||||||
|
if (prop == selectedProp) {
|
||||||
|
selectedProp = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (_texturePanel.visible && !noConflictBitmaps()) {
|
||||||
|
// _texturePanel.visible = false;
|
||||||
|
// } else
|
||||||
|
bonusPanel.visible = oneBonusSelected();
|
||||||
|
_texturePanel.visible = !bonusPanel.visible && noConflictBitmaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Выделение пропа.
|
||||||
|
* @param prop
|
||||||
|
*/
|
||||||
|
public function selectProps(props:Set):void {
|
||||||
|
|
||||||
|
deselectProps();
|
||||||
|
|
||||||
|
for (var p:* in props) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
if (isAllowClassName(prop)) {
|
||||||
|
prop.select();
|
||||||
|
selectedProps.add(prop);
|
||||||
|
selectedProp = prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
showPropertyPanel();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Выделить конфликтующие пропы.
|
||||||
|
*/
|
||||||
|
public function selectConflictProps():void {
|
||||||
|
selectProps(occupyMap.getConflictProps());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectProp(prop:Prop):void {
|
||||||
|
if (isAllowClassName(prop)) {
|
||||||
|
prop.select();
|
||||||
|
selectedProps.add(prop);
|
||||||
|
selectedProp = prop;
|
||||||
|
showPropertyPanel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPropsUnderRect(point:Point, dx:Number, dy:Number, select:Boolean):Set {
|
||||||
|
var result:Set = new Set();
|
||||||
|
for (var child:* in root.children) {
|
||||||
|
var prop:Prop = child as Prop;
|
||||||
|
if (prop && isAllowClassName(prop)) {
|
||||||
|
var view_coords:Point3D = view.projectPoint(prop.coords);
|
||||||
|
if (view_coords.x >= point.x && view_coords.x <= point.x + dx
|
||||||
|
&& view_coords.y >= point.y && view_coords.y <= point.y + dy) {
|
||||||
|
if (select) {
|
||||||
|
if (!prop.selected) {
|
||||||
|
prop.select();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (prop.selected) {
|
||||||
|
prop.deselect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создание пропа.
|
||||||
|
* @param sourceProp прототип
|
||||||
|
*/
|
||||||
|
public function addProp(sourceProp:Prop, coords:Point3D, rotation:Number, copy:Boolean = true, addToMap:Boolean = true):Prop {
|
||||||
|
var prop:Prop;
|
||||||
|
if (copy) {
|
||||||
|
prop = sourceProp.clone() as Prop;
|
||||||
|
prop.rotationZ = rotation;
|
||||||
|
} else {
|
||||||
|
prop = sourceProp;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.addChild(prop);
|
||||||
|
|
||||||
|
if (rotation != 0 && copy) {
|
||||||
|
// Расчитывать надо после добавления на сцену
|
||||||
|
prop.calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Определяем координаты
|
||||||
|
prop.x = coords.x;
|
||||||
|
prop.y = coords.y;
|
||||||
|
prop.z = coords.z;
|
||||||
|
|
||||||
|
|
||||||
|
prop.addEventListener(MouseEvent3D.MOUSE_DOWN, onPropMouseDown);
|
||||||
|
prop.addEventListener(MouseEvent3D.MOUSE_OUT, onPropMouseOut);
|
||||||
|
prop.addEventListener(MouseEvent3D.MOUSE_OVER, onPropMouseOver);
|
||||||
|
_changed = true;
|
||||||
|
|
||||||
|
if (snapMode && addToMap) {
|
||||||
|
occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
return prop;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Удаление пропа.
|
||||||
|
* @param prop
|
||||||
|
* @return проп
|
||||||
|
*/
|
||||||
|
public function deleteProps(props:Set = null):Set {
|
||||||
|
|
||||||
|
if (!props) {
|
||||||
|
props = selectedProps;
|
||||||
|
selectedProp = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props) {
|
||||||
|
var result:Set = props.clone();
|
||||||
|
for (var p:* in props) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
root.removeChild(prop);
|
||||||
|
occupyMap.free(prop);
|
||||||
|
if (selectedProps.has(prop)) {
|
||||||
|
deselectProp(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _propertyPanel.enabled = false;
|
||||||
|
bonusPanel.visible = false;
|
||||||
|
_texturePanel.visible = false;
|
||||||
|
propMouseDown = false;
|
||||||
|
_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очистка сцены.
|
||||||
|
*/
|
||||||
|
public function clear():void {
|
||||||
|
|
||||||
|
for (var child:* in root.children) {
|
||||||
|
var prop:Prop = child as Prop;
|
||||||
|
if (prop) {
|
||||||
|
root.removeChild(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedProp = null;
|
||||||
|
selectedProps.clear();
|
||||||
|
occupyMap.clear();
|
||||||
|
view.interactive = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Смена текстуры.
|
||||||
|
*/
|
||||||
|
public function onTexturePanelSelect(e:PropListEvent = null):void {
|
||||||
|
|
||||||
|
for (var p:* in selectedProps) {
|
||||||
|
var tile:Tile = p;
|
||||||
|
if (tile && tile.bitmaps) {
|
||||||
|
tile.textureName = _texturePanel.selectedItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onBonusTypeChange(e:Event):void {
|
||||||
|
|
||||||
|
var check:CheckBox = e.target as CheckBox;
|
||||||
|
for (var p:* in selectedProps) {
|
||||||
|
var bonus:Bonus = p;
|
||||||
|
if (check.selected) {
|
||||||
|
bonus.types.add(check.label);
|
||||||
|
} else {
|
||||||
|
bonus.types.remove(check.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function noConflictBitmaps():Map {
|
||||||
|
|
||||||
|
var bitmaps:Map;
|
||||||
|
for (var p:* in selectedProps) {
|
||||||
|
var tile:Tile = p as Tile;
|
||||||
|
if (tile && tile.bitmaps) {
|
||||||
|
if (!bitmaps) {
|
||||||
|
bitmaps = tile.bitmaps;
|
||||||
|
} else {
|
||||||
|
if (bitmaps != tile.bitmaps) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function oneBonusSelected():Boolean {
|
||||||
|
|
||||||
|
if (selectedProps.length > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bonus:Bonus = selectedProps.peek() as Bonus;
|
||||||
|
if (!bonus) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
public function showPropertyPanel():void {
|
||||||
|
|
||||||
|
bonusPanel.visible = oneBonusSelected();
|
||||||
|
if (bonusPanel.visible) {
|
||||||
|
var types:Set = (selectedProps.peek() as Bonus).types;
|
||||||
|
|
||||||
|
for (var type:* in checkTypeMap) {
|
||||||
|
(checkTypeMap[type] as CheckBox).selected = types.has(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var bitmaps:Map = noConflictBitmaps();
|
||||||
|
if (bitmaps) {
|
||||||
|
_texturePanel.visible = true;
|
||||||
|
|
||||||
|
if (bitmaps != currentBitmaps) {
|
||||||
|
_texturePanel.deleteAllProps();
|
||||||
|
_texturePanel.selectedItem = null;
|
||||||
|
for (var key:* in bitmaps) {
|
||||||
|
var bitmapData:BitmapData = bitmaps[key];
|
||||||
|
var bitmap:Bitmap = new Bitmap(bitmapData);
|
||||||
|
_texturePanel.addItem(key, bitmap, key);
|
||||||
|
}
|
||||||
|
currentBitmaps = bitmaps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _texturePanel.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mirrorTextures():void {
|
||||||
|
for (var p:* in selectedProps) {
|
||||||
|
var tile:Tile = p as Tile;
|
||||||
|
if (tile != null) tile.mirrorTexture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function hideSelectedProps():void {
|
||||||
|
|
||||||
|
var props:Set = selectedProps.clone();
|
||||||
|
deselectProps();
|
||||||
|
for (var p:* in props) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
hideProps.push(prop);
|
||||||
|
if (!prop.free) {
|
||||||
|
occupyMap.free(prop);
|
||||||
|
prop.free = true;
|
||||||
|
}
|
||||||
|
root.removeChild(prop);
|
||||||
|
calculate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function showAll():void {
|
||||||
|
var len:int = hideProps.length;
|
||||||
|
for (var i:int = 0; i < len; i++) {
|
||||||
|
var prop:Prop = hideProps[i];
|
||||||
|
root.addChild(prop);
|
||||||
|
if (prop.free) {
|
||||||
|
occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
hideProps.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override public function getCentrePropsGroup(props:Set = null):Point {
|
||||||
|
if (!props) {
|
||||||
|
props = selectedProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getCentrePropsGroup(props);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function rotateProps(plus:Boolean, props:Set = null):void {
|
||||||
|
if (!props) {
|
||||||
|
props = selectedProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
var centre:Point = getCentrePropsGroup(props);
|
||||||
|
|
||||||
|
for (var p:* in props) {
|
||||||
|
var prop:Prop = p;
|
||||||
|
occupyMap.free(prop);
|
||||||
|
|
||||||
|
var x:Number = prop.x;
|
||||||
|
var y:Number = prop.y;
|
||||||
|
if (plus) {
|
||||||
|
prop.x = y + centre.x - centre.y;
|
||||||
|
prop.y = -x + centre.y + centre.x;
|
||||||
|
} else {
|
||||||
|
prop.x = -y + centre.x + centre.y;
|
||||||
|
prop.y = x + centre.y - centre.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
prop.rotate(plus);
|
||||||
|
if (snapMode) {
|
||||||
|
prop.snapCoords();
|
||||||
|
occupyMap.occupy(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/green_cursor.jpg
Normal file
|
After Width: | Height: | Size: 825 B |
36
src/gui/events/PropListEvent.as
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package gui.events {
|
||||||
|
|
||||||
|
import flash.events.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael
|
||||||
|
*/
|
||||||
|
public class PropListEvent extends Event {
|
||||||
|
public static const SELECT:String = "select";
|
||||||
|
private var _selectedIndex:int;
|
||||||
|
private var _selectedItem:*;
|
||||||
|
|
||||||
|
public function PropListEvent(index:int, item:*) {
|
||||||
|
super(PropListEvent.SELECT, false, false);
|
||||||
|
_selectedIndex = index;
|
||||||
|
_selectedItem = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get selectedIndex():int {
|
||||||
|
return _selectedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get selectedItem():* {
|
||||||
|
return _selectedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function toString():String {
|
||||||
|
return formatToString("ListEvents", "type", "bubbles", "cancelable", "selectedIndex", "selectedItem");
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function clone():Event {
|
||||||
|
return new PropListEvent(_selectedIndex, _selectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/icons/editor_boxes_icon.png
Normal file
|
After Width: | Height: | Size: 860 B |
BIN
src/icons/editor_boxes_icon_on.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/icons/editor_grid_icon.png
Normal file
|
After Width: | Height: | Size: 188 B |
BIN
src/icons/editor_grid_icon_on.png
Normal file
|
After Width: | Height: | Size: 656 B |
BIN
src/icons/editor_snap_icon.png
Normal file
|
After Width: | Height: | Size: 297 B |
BIN
src/icons/editor_snap_icon_on.png
Normal file
|
After Width: | Height: | Size: 718 B |
BIN
src/icons/editor_textures_icon.png
Normal file
|
After Width: | Height: | Size: 929 B |
BIN
src/icons/editor_textures_icon_on.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/icons/icon_hide_selected.png
Normal file
|
After Width: | Height: | Size: 499 B |
BIN
src/icons/icon_show_all.png
Normal file
|
After Width: | Height: | Size: 588 B |
BIN
src/red_cursor.jpg
Normal file
|
After Width: | Height: | Size: 825 B |