Add sources
2
.gitignore
vendored
@@ -16,3 +16,5 @@ bin-release/
|
||||
# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
|
||||
# should NOT be excluded as they contain compiler settings and other important
|
||||
# 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 |