Add sources

This commit is contained in:
Pyogenics
2024-09-28 17:46:56 +01:00
parent 9fac521811
commit ea2aedf185
53 changed files with 5918 additions and 0 deletions

2
.gitignore vendored
View File

@@ -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

Binary file not shown.

BIN
libs/AlternativaTypes.swc Normal file

Binary file not shown.

BIN
libs/AlternativaUtils.swc Normal file

Binary file not shown.

BIN
libs/PropsLib.swc Normal file

Binary file not shown.

View 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
View 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
View 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
View 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
View 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
View 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;
}
}
}

View 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);
}
}
}

View 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];
}
}
}

View 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();
}
}
}

View 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();
}
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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();
}
}
}

View 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 = {};
}
}
}

View 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 {
}
}
}

View 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;
}
}
}

View 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();
}
}
}

View 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());
}
}
}

View 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();
}
}
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

View 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);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

BIN
src/icons/icon_show_all.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

BIN
src/red_cursor.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B