mirror of
https://github.com/electronicarts/CnC_Generals_Zero_Hour.git
synced 2026-02-04 07:53:58 -05:00
Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.
This commit is contained in:
78
GeneralsMD/Code/Tools/WorldBuilder/src/AutoEdgeOutTool.cpp
Normal file
78
GeneralsMD/Code/Tools/WorldBuilder/src/AutoEdgeOutTool.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// AutoEdgeOutTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "AutoEdgeOutTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
//
|
||||
// AutoEdgeOutTool class.
|
||||
//
|
||||
/// Constructor
|
||||
AutoEdgeOutTool::AutoEdgeOutTool(void) :
|
||||
Tool(ID_AUTO_EDGE_OUT_TOOL, IDC_AUTO_EDGE_OUT)
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
AutoEdgeOutTool::~AutoEdgeOutTool(void)
|
||||
{
|
||||
}
|
||||
|
||||
/// Shows the brush options panel.
|
||||
void AutoEdgeOutTool::activate()
|
||||
{
|
||||
Tool::activate();
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_BLEND_MATERIAL);
|
||||
}
|
||||
|
||||
|
||||
/** Execute the tool on mouse down - Create a copy of the height map
|
||||
* to edit, blend the edges, and give the undoable command to the doc. */
|
||||
void AutoEdgeOutTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
|
||||
CPoint ndx;
|
||||
if (!pDoc->getCellIndexFromCoord(cpt, &ndx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
WorldHeightMapEdit *htMapEditCopy = pDoc->GetHeightMap()->duplicate();
|
||||
htMapEditCopy->autoBlendOut(ndx.x, ndx.y, BlendMaterial::getBlendTexClass());
|
||||
IRegion2D partialRange = {0,0,0,0};
|
||||
pDoc->updateHeightMap(htMapEditCopy, false, partialRange);
|
||||
WBDocUndoable *pUndo = new WBDocUndoable(pDoc, htMapEditCopy);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
REF_PTR_RELEASE(htMapEditCopy);
|
||||
}
|
||||
|
||||
127
GeneralsMD/Code/Tools/WorldBuilder/src/BaseBuildProps.cpp
Normal file
127
GeneralsMD/Code/Tools/WorldBuilder/src/BaseBuildProps.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// BaseBuildProps.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "BaseBuildProps.h"
|
||||
#include "EditParameter.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// BaseBuildProps dialog
|
||||
|
||||
|
||||
BaseBuildProps::BaseBuildProps(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(BaseBuildProps::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(BaseBuildProps)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void BaseBuildProps::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(BaseBuildProps)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
BOOL BaseBuildProps::OnInitDialog()
|
||||
{
|
||||
// add name
|
||||
CWnd* pName = GetDlgItem(IDC_MAPOBJECT_Name);
|
||||
if (pName) {
|
||||
pName->SetWindowText(m_name.str());
|
||||
}
|
||||
|
||||
// add script
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_MAPOBJECT_Script);
|
||||
EditParameter::loadScripts(pCombo, true);
|
||||
pCombo->AddString("<none>");
|
||||
if (m_script.isEmpty()) {
|
||||
pCombo->SelectString(-1, "<none>");
|
||||
} else {
|
||||
pCombo->SelectString(-1, m_script.str());
|
||||
}
|
||||
|
||||
// add health
|
||||
CWnd* pHealth = GetDlgItem(IDC_MAPOBJECT_StartingHealthEdit);
|
||||
static char buff[12];
|
||||
sprintf(buff, "%d", m_health);
|
||||
pHealth->SetWindowText(buff);
|
||||
|
||||
CButton* pItem;
|
||||
pItem = (CButton*) GetDlgItem(IDC_MAPOBJECT_Unsellable);
|
||||
if (pItem) {
|
||||
pItem->SetCheck(m_unsellable);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(BaseBuildProps, CDialog)
|
||||
//{{AFX_MSG_MAP(BaseBuildProps)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// BaseBuildProps message handlers
|
||||
void BaseBuildProps::setProps(AsciiString name, AsciiString script, Int health, Bool unsellable)
|
||||
{
|
||||
m_name = name;
|
||||
m_script = script;
|
||||
m_health = health;
|
||||
m_unsellable = unsellable;
|
||||
}
|
||||
|
||||
void BaseBuildProps::OnOK()
|
||||
{
|
||||
CComboBox *combo;
|
||||
CWnd* edit;
|
||||
CString cstr;
|
||||
static char buf[1024];
|
||||
|
||||
edit = (CComboBox*)GetDlgItem(IDC_MAPOBJECT_Name);
|
||||
edit->GetWindowText(buf, sizeof(buf)-2);
|
||||
m_name = AsciiString(buf);
|
||||
|
||||
combo = (CComboBox*)GetDlgItem(IDC_MAPOBJECT_Script);
|
||||
combo->GetWindowText(buf, sizeof(buf)-2);
|
||||
m_script = AsciiString(buf);
|
||||
|
||||
edit = GetDlgItem(IDC_MAPOBJECT_StartingHealthEdit);
|
||||
edit->GetWindowText(cstr);
|
||||
if (cstr.IsEmpty()) {
|
||||
m_health = 100;
|
||||
} else {
|
||||
m_health = atoi(cstr.GetBuffer(0));
|
||||
if (m_health < 0)
|
||||
m_health = 0;
|
||||
}
|
||||
|
||||
CButton *check;
|
||||
check = (CButton*) GetDlgItem(IDC_MAPOBJECT_Unsellable);
|
||||
m_unsellable = (check->GetCheck() != 0);
|
||||
|
||||
CDialog::OnOK();
|
||||
}
|
||||
91
GeneralsMD/Code/Tools/WorldBuilder/src/BlendEdgeTool.cpp
Normal file
91
GeneralsMD/Code/Tools/WorldBuilder/src/BlendEdgeTool.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// BlendEdgeTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "BlendEdgeTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
//
|
||||
// BlendEdgeTool class.
|
||||
//
|
||||
/// Constructor
|
||||
BlendEdgeTool::BlendEdgeTool(void) :
|
||||
Tool(ID_BLEND_EDGE_TOOL, IDC_BLEND_EDGE)
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
BlendEdgeTool::~BlendEdgeTool(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/** Execute the tool on mouse down - Place an object. */
|
||||
void BlendEdgeTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
|
||||
m_downPt = cpt;
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse up - Blend an edge. Left mouse blends the from
|
||||
texture to the to texture based on your drag. Right mouse blends the
|
||||
foreground texture in the texture panel on top of the destination tile for
|
||||
custom blends. */
|
||||
void BlendEdgeTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
CPoint from, to;
|
||||
|
||||
Coord3D cpt;
|
||||
// Don't constrain this tool.
|
||||
pView->viewToDocCoords(viewPt, &cpt, false);
|
||||
|
||||
if (!pDoc->getCellIndexFromCoord(m_downPt, &from) || !pDoc->getCellIndexFromCoord(cpt, &to)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.x == to.x && from.y == to.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
// WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
WorldHeightMapEdit *htMapEditCopy = pDoc->GetHeightMap()->duplicate();
|
||||
if (m == TRACK_L)
|
||||
htMapEditCopy->blendTile(to.x, to.y, from.x, from.y, -1, -1); // does all the work.
|
||||
else
|
||||
htMapEditCopy->blendTile(to.x, to.y, from.x, from.y, TerrainMaterial::getFgTexClass(), -1); // does all the work.
|
||||
IRegion2D partialRange = {to.x, to.y, to.x+1, to.y+1};
|
||||
pDoc->updateHeightMap(htMapEditCopy, true, partialRange); //update the render object with new tile and/or height data.
|
||||
WBDocUndoable *pUndo = new WBDocUndoable(pDoc, htMapEditCopy);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
|
||||
REF_PTR_RELEASE(htMapEditCopy);
|
||||
|
||||
}
|
||||
|
||||
287
GeneralsMD/Code/Tools/WorldBuilder/src/BlendMaterial.cpp
Normal file
287
GeneralsMD/Code/Tools/WorldBuilder/src/BlendMaterial.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// BlendMaterial.cpp : implementation file
|
||||
//
|
||||
|
||||
#define DEFINE_TERRAIN_TYPE_NAMES
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "BlendMaterial.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "TileTool.h"
|
||||
#include "Common/TerrainTypes.h"
|
||||
#include "W3DDevice/GameClient/TerrainTex.h"
|
||||
|
||||
BlendMaterial *BlendMaterial::m_staticThis = NULL;
|
||||
|
||||
static Int defaultMaterialIndex = -1;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// BlendMaterial dialog
|
||||
|
||||
Int BlendMaterial::m_currentBlendTexture(-1);
|
||||
|
||||
BlendMaterial::BlendMaterial(CWnd* pParent /*=NULL*/) :
|
||||
m_updating(false)
|
||||
{
|
||||
//{{AFX_DATA_INIT(BlendMaterial)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void BlendMaterial::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(BlendMaterial)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(BlendMaterial, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(BlendMaterial)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// BlendMaterial data access method.
|
||||
|
||||
/// Set foreground texture and invalidate swatches.
|
||||
void BlendMaterial::setBlendTexClass(Int texClass)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
m_staticThis->m_currentBlendTexture=texClass;
|
||||
m_staticThis->setTerrainTreeViewSelection(TVI_ROOT, texClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Set the selected texture in the tree view.
|
||||
Bool BlendMaterial::setTerrainTreeViewSelection(HTREEITEM parent, Int selection)
|
||||
{
|
||||
TVITEM item;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&item, 0, sizeof(item));
|
||||
HTREEITEM child = m_terrainTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM;
|
||||
item.hItem = child;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_terrainTreeView.GetItem(&item);
|
||||
if (item.lParam == selection) {
|
||||
m_terrainTreeView.SelectItem(child);
|
||||
return(true);
|
||||
}
|
||||
if (setTerrainTreeViewSelection(child, selection)) {
|
||||
return(true);
|
||||
}
|
||||
child = m_terrainTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// BlendMaterial message handlers
|
||||
|
||||
/// Setup the controls in the dialog.
|
||||
BOOL BlendMaterial::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
CWnd *pWnd = GetDlgItem(IDC_TERRAIN_TREEVIEW);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_terrainTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_TERRAIN_TREEVIEW);
|
||||
m_terrainTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
pWnd = GetDlgItem(IDC_TERRAIN_SWATCHES);
|
||||
pWnd->GetWindowRect(&rect);
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
//m_terrainSwatches.Create(NULL, "", WS_CHILD, rect, this, IDC_TERRAIN_SWATCHES);
|
||||
//m_terrainSwatches.ShowWindow(SW_SHOW);
|
||||
|
||||
m_staticThis = this;
|
||||
updateTextures();
|
||||
m_updating = false;
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
HTREEITEM BlendMaterial::findOrAdd(HTREEITEM parent, char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = m_terrainTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
m_terrainTreeView.GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = m_terrainTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_LAST;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = m_terrainTreeView.InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
/** Add the terrain path to the tree view. */
|
||||
void BlendMaterial::addTerrain(const char *pPath, Int terrainNdx, HTREEITEM parent)
|
||||
{
|
||||
AsciiString className;
|
||||
if (terrainNdx >= 0) {
|
||||
className = WorldHeightMapEdit::getTexClassName( terrainNdx );
|
||||
}
|
||||
TerrainType *terrain = TheTerrainTypes->findTerrain( className );
|
||||
Bool doAdd = FALSE;
|
||||
char buffer[_MAX_PATH];
|
||||
//
|
||||
// if we have a 'terrain' entry, it means that our terrain index was properly defined
|
||||
// in an INI file, otherwise it was from eval textures. We will sort all of
|
||||
// the eval texture entries in a tree leaf all their own while the others are
|
||||
// sorted according to a field specified in INI
|
||||
//
|
||||
if( terrain )
|
||||
{
|
||||
if (!terrain->isBlendEdge()) {
|
||||
return; // Only do blend edges to the blend material list.
|
||||
}
|
||||
|
||||
// set the name in the tree view to that of the entry
|
||||
strcpy( buffer, terrain->getName().str() );
|
||||
|
||||
doAdd = TRUE;
|
||||
} else if (terrainNdx==-1) {
|
||||
strcpy(buffer, pPath);
|
||||
doAdd = true;
|
||||
} else if (WorldHeightMapEdit::getTexClassIsBlendEdge(terrainNdx)) {
|
||||
parent = findOrAdd( parent, "**EVAL**" );
|
||||
strcpy(buffer, pPath);
|
||||
doAdd = true;
|
||||
} // end if
|
||||
|
||||
// Int tilesPerRow = TEXTURE_WIDTH/(2*TILE_PIXEL_EXTENT+TILE_OFFSET);
|
||||
// Int availableTiles = 4 * tilesPerRow * tilesPerRow;
|
||||
// Int percent = (WorldHeightMapEdit::getTexClassNumTiles(terrainNdx)*100 + availableTiles/2) / availableTiles;
|
||||
|
||||
char label[_MAX_PATH];
|
||||
sprintf(label, "%s", buffer);
|
||||
|
||||
|
||||
if( doAdd )
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_LAST;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = terrainNdx;
|
||||
ins.item.pszText = label;
|
||||
ins.item.cchTextMax = strlen(label)+2;
|
||||
m_terrainTreeView.InsertItem(&ins);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//* Create the tree view of textures from the textures in pMap. */
|
||||
void BlendMaterial::updateTextures(void)
|
||||
{
|
||||
m_updating = true;
|
||||
m_terrainTreeView.DeleteAllItems();
|
||||
Int i;
|
||||
CString label;
|
||||
label.LoadString(IDS_ALPHA_BLEND);
|
||||
addTerrain(label, -1, TVI_ROOT);
|
||||
for (i=WorldHeightMapEdit::getNumTexClasses()-1; i>=0; i--) {
|
||||
char path[_MAX_PATH];
|
||||
AsciiString uiName = WorldHeightMapEdit::getTexClassUiName(i);
|
||||
strncpy(path, uiName.str(), _MAX_PATH-2);
|
||||
addTerrain(path, i, TVI_ROOT);
|
||||
}
|
||||
m_updating = false;
|
||||
m_currentBlendTexture = defaultMaterialIndex;
|
||||
setTerrainTreeViewSelection(TVI_ROOT, m_currentBlendTexture);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BOOL BlendMaterial::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
if (pHdr->hdr.hwndFrom == m_terrainTreeView.m_hWnd) {
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
HTREEITEM hItem = m_terrainTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM;
|
||||
item.hItem = hItem;
|
||||
m_terrainTreeView.GetItem(&item);
|
||||
if (item.lParam >= -1) {
|
||||
Int texClass = item.lParam;
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (!pDoc) return 0;
|
||||
|
||||
WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
if (!pMap) return 0;
|
||||
if (m_updating) return 0;
|
||||
m_currentBlendTexture = texClass;
|
||||
} else if (!(item.state & TVIS_EXPANDEDONCE) ) {
|
||||
HTREEITEM child = m_terrainTreeView.GetChildItem(hItem);
|
||||
while (child != NULL) {
|
||||
hItem = child;
|
||||
child = m_terrainTreeView.GetChildItem(hItem);
|
||||
}
|
||||
if (hItem != m_terrainTreeView.GetSelectedItem()) {
|
||||
m_terrainTreeView.SelectItem(hItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
173
GeneralsMD/Code/Tools/WorldBuilder/src/BorderTool.cpp
Normal file
173
GeneralsMD/Code/Tools/WorldBuilder/src/BorderTool.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "Resource.h"
|
||||
|
||||
#include "BorderTool.h"
|
||||
#include "DrawObject.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WbView3d.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
|
||||
const long BOUNDARY_PICK_DISTANCE = 5.0f;
|
||||
|
||||
BorderTool::BorderTool() : Tool(ID_BORDERTOOL, IDC_POINTER),
|
||||
m_mouseDown(false),
|
||||
m_addingNewBorder(false),
|
||||
m_modifyBorderNdx(-1)
|
||||
|
||||
{ }
|
||||
|
||||
BorderTool::~BorderTool()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void BorderTool::setCursor(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void BorderTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_NO_OPTIONS);
|
||||
DrawObject::setDoBoundaryFeedback(TRUE);
|
||||
}
|
||||
|
||||
void BorderTool::deactivate()
|
||||
{
|
||||
WbView3d *p3View = CWorldBuilderDoc::GetActive3DView();
|
||||
DrawObject::setDoBoundaryFeedback(p3View->getShowMapBoundaryFeedback());
|
||||
}
|
||||
|
||||
void BorderTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_addingNewBorder) {
|
||||
Int count = pDoc->getNumBoundaries();
|
||||
ICoord2D current;
|
||||
pDoc->getBoundary(count - 1, ¤t);
|
||||
Coord3D new3DPoint;
|
||||
pView->viewToDocCoords(viewPt, &new3DPoint, false);
|
||||
|
||||
if (current.x < 0) {
|
||||
current.x = 0;
|
||||
}
|
||||
|
||||
if (current.y < 0) {
|
||||
current.y = 0;
|
||||
}
|
||||
|
||||
current.x = REAL_TO_INT((new3DPoint.x / MAP_XY_FACTOR) + 0.5f);
|
||||
current.y = REAL_TO_INT((new3DPoint.y / MAP_XY_FACTOR) + 0.5f);
|
||||
pDoc->changeBoundary(count - 1, ¤t);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_modifyBorderNdx >= 0) {
|
||||
ICoord2D currentBorder;
|
||||
pDoc->getBoundary(m_modifyBorderNdx, ¤tBorder);
|
||||
|
||||
Coord3D new3DPoint;
|
||||
pView->viewToDocCoords(viewPt, &new3DPoint, false);
|
||||
|
||||
switch (m_modificationType)
|
||||
{
|
||||
case MOD_TYPE_INVALID: m_modifyBorderNdx = -1; return;
|
||||
case MOD_TYPE_UP:
|
||||
currentBorder.y = REAL_TO_INT((new3DPoint.y / MAP_XY_FACTOR) + 0.5f);
|
||||
break;
|
||||
case MOD_TYPE_RIGHT:
|
||||
currentBorder.x = REAL_TO_INT((new3DPoint.x / MAP_XY_FACTOR) + 0.5f);
|
||||
break;
|
||||
case MOD_TYPE_FREE:
|
||||
currentBorder.x = REAL_TO_INT((new3DPoint.x / MAP_XY_FACTOR) + 0.5f);
|
||||
currentBorder.y = REAL_TO_INT((new3DPoint.y / MAP_XY_FACTOR) + 0.5f);
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentBorder.x < 0) {
|
||||
currentBorder.x = 0;
|
||||
}
|
||||
|
||||
if (currentBorder.y < 0) {
|
||||
currentBorder.y = 0;
|
||||
}
|
||||
|
||||
|
||||
pDoc->changeBoundary(m_modifyBorderNdx, ¤tBorder);
|
||||
}
|
||||
}
|
||||
|
||||
void BorderTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) {
|
||||
return;
|
||||
}
|
||||
|
||||
//static Coord3D zero = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
Coord3D groundPt;
|
||||
pView->viewToDocCoords(viewPt, &groundPt);
|
||||
if (groundPt.length() < BOUNDARY_PICK_DISTANCE) {
|
||||
m_addingNewBorder = true;
|
||||
|
||||
ICoord2D initialBoundary = { 1, 1 };
|
||||
pDoc->addBoundary(&initialBoundary);
|
||||
return;
|
||||
}
|
||||
|
||||
Int motion;
|
||||
pDoc->findBoundaryNear(&groundPt, BOUNDARY_PICK_DISTANCE, &m_modifyBorderNdx, &motion);
|
||||
|
||||
// if bottom left boundary grabbed
|
||||
if (motion == 0)
|
||||
{
|
||||
// modifying the bottom left is not allowed.
|
||||
m_modifyBorderNdx = -1;
|
||||
}
|
||||
// else if no boundary is near
|
||||
else if (motion == -1)
|
||||
{
|
||||
// add a boundary
|
||||
m_addingNewBorder = true;
|
||||
|
||||
ICoord2D initialBoundary = { 1, 1 };
|
||||
pDoc->addBoundary(&initialBoundary);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_modificationType = (ModificationType) motion;
|
||||
}
|
||||
}
|
||||
|
||||
void BorderTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_addingNewBorder) {
|
||||
m_addingNewBorder = false;
|
||||
// Do the undoable on the last border
|
||||
}
|
||||
}
|
||||
202
GeneralsMD/Code/Tools/WorldBuilder/src/BrushTool.cpp
Normal file
202
GeneralsMD/Code/Tools/WorldBuilder/src/BrushTool.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// BrushTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "BrushTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "BrushOptions.h"
|
||||
#include "DrawObject.h"
|
||||
//
|
||||
// BrushTool class.
|
||||
//
|
||||
|
||||
Int BrushTool::m_brushWidth;
|
||||
Int BrushTool::m_brushFeather;
|
||||
Bool BrushTool::m_brushSquare;
|
||||
Int BrushTool::m_brushHeight;
|
||||
|
||||
|
||||
|
||||
/// Constructor
|
||||
BrushTool::BrushTool(void) :
|
||||
Tool(ID_BRUSH_TOOL, IDC_BRUSH_CROSS)
|
||||
{
|
||||
m_htMapEditCopy = NULL;
|
||||
m_htMapFeatherCopy = NULL;
|
||||
|
||||
m_brushWidth = 0;
|
||||
m_brushFeather = 0;
|
||||
m_brushHeight = 0;
|
||||
m_brushSquare = false;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
BrushTool::~BrushTool(void)
|
||||
{
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
REF_PTR_RELEASE(m_htMapFeatherCopy);
|
||||
}
|
||||
|
||||
/// Set the brush height and notify the height options panel of the change.
|
||||
void BrushTool::setHeight(Int height)
|
||||
{
|
||||
if (m_brushHeight != height) {
|
||||
m_brushHeight = height;
|
||||
// notify height palette options panel
|
||||
BrushOptions::setHeight(height);
|
||||
}
|
||||
};
|
||||
|
||||
/// Set the brush width and notify the height options panel of the change.
|
||||
void BrushTool::setWidth(Int width)
|
||||
{
|
||||
if (m_brushWidth != width) {
|
||||
m_brushWidth = width;
|
||||
// notify brush palette options panel
|
||||
BrushOptions::setWidth(width);
|
||||
DrawObject::setBrushFeedbackParms(m_brushSquare, m_brushWidth, m_brushFeather);
|
||||
}
|
||||
};
|
||||
|
||||
/// Set the brush feather and notify the height options panel of the change.
|
||||
void BrushTool::setFeather(Int feather)
|
||||
{
|
||||
if (m_brushFeather != feather) {
|
||||
m_brushFeather = feather;
|
||||
// notify height palette options panel
|
||||
BrushOptions::setFeather(feather);
|
||||
DrawObject::setBrushFeedbackParms(m_brushSquare, m_brushWidth, m_brushFeather);
|
||||
}
|
||||
};
|
||||
|
||||
/// Shows the brush options panel.
|
||||
void BrushTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_BRUSH_OPTIONS);
|
||||
DrawObject::setDoBrushFeedback(true);
|
||||
DrawObject::setBrushFeedbackParms(m_brushSquare, m_brushWidth, m_brushFeather);
|
||||
}
|
||||
|
||||
/// Start tool.
|
||||
/** Setup the tool to start brushing - make a copy of the height map
|
||||
to edit, another copy because we need it :), and call mouseMovedDown. */
|
||||
void BrushTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
// WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
// just in case, release it.
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
m_htMapEditCopy = pDoc->GetHeightMap()->duplicate();
|
||||
m_prevXIndex = -1;
|
||||
m_prevYIndex = -1;
|
||||
REF_PTR_RELEASE(m_htMapFeatherCopy);
|
||||
m_htMapFeatherCopy = m_htMapEditCopy->duplicate();
|
||||
mouseMoved(m, viewPt, pView, pDoc);
|
||||
}
|
||||
|
||||
/// End tool.
|
||||
/** Finish the tool operation - create a command, pass it to the
|
||||
doc to execute, and cleanup ref'd objects. */
|
||||
void BrushTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
WBDocUndoable *pUndo = new WBDocUndoable(pDoc, m_htMapEditCopy);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
REF_PTR_RELEASE(m_htMapFeatherCopy);
|
||||
}
|
||||
|
||||
/// Execute the tool.
|
||||
/** Apply the height brush at the current point. */
|
||||
void BrushTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
DrawObject::setFeedbackPos(cpt);
|
||||
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
|
||||
int brushWidth = m_brushWidth;
|
||||
if (m_brushFeather>0) {
|
||||
brushWidth += 2*m_brushFeather;
|
||||
}
|
||||
brushWidth += 2;
|
||||
|
||||
CPoint ndx;
|
||||
getCenterIndex(&cpt, m_brushWidth, &ndx, pDoc);
|
||||
|
||||
if (m_prevXIndex == ndx.x && m_prevYIndex == ndx.y) return;
|
||||
|
||||
m_prevXIndex = ndx.x;
|
||||
m_prevYIndex = ndx.y;
|
||||
|
||||
int sub = brushWidth/2;
|
||||
int add = brushWidth-sub;
|
||||
|
||||
Int i, j;
|
||||
for (i=ndx.x-sub; i<ndx.x+add; i++) {
|
||||
if (i<0 || i>=m_htMapEditCopy->getXExtent()) {
|
||||
continue;
|
||||
}
|
||||
for (j=ndx.y-sub; j<ndx.y+add; j++) {
|
||||
if (j<0 || j>=m_htMapEditCopy->getYExtent()) {
|
||||
continue;
|
||||
}
|
||||
Real blendFactor;
|
||||
if (m_brushSquare) {
|
||||
blendFactor = calcSquareBlendFactor(ndx, i, j, m_brushWidth, m_brushFeather);
|
||||
} else {
|
||||
blendFactor = calcRoundBlendFactor(ndx, i, j, m_brushWidth, m_brushFeather);
|
||||
}
|
||||
Int curHeight = m_htMapFeatherCopy->getHeight(i,j);
|
||||
float fNewHeight = blendFactor*m_brushHeight+((1.0f-blendFactor)*curHeight) ;
|
||||
Int newHeight = floor(fNewHeight+0.5);
|
||||
if (m_brushHeight > curHeight) {
|
||||
if (m_htMapEditCopy->getHeight(i,j)>newHeight) {
|
||||
newHeight = m_htMapEditCopy->getHeight(i,j);
|
||||
}
|
||||
} else {
|
||||
if (m_htMapEditCopy->getHeight(i,j)<newHeight) {
|
||||
newHeight = m_htMapEditCopy->getHeight(i,j);
|
||||
}
|
||||
}
|
||||
m_htMapEditCopy->setHeight(i, j, newHeight);
|
||||
}
|
||||
}
|
||||
IRegion2D partialRange;
|
||||
partialRange.lo.x = ndx.x - brushWidth;
|
||||
partialRange.hi.x = ndx.x + brushWidth;
|
||||
partialRange.lo.y = ndx.y - brushWidth;
|
||||
partialRange.hi.y = ndx.y + brushWidth;
|
||||
pDoc->updateHeightMap(m_htMapEditCopy, true, partialRange);
|
||||
}
|
||||
799
GeneralsMD/Code/Tools/WorldBuilder/src/BuildList.cpp
Normal file
799
GeneralsMD/Code/Tools/WorldBuilder/src/BuildList.cpp
Normal file
@@ -0,0 +1,799 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// BuildList.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "BuildList.h"
|
||||
#include "BuildListTool.h"
|
||||
#include "BaseBuildProps.h"
|
||||
#include "CUndoable.h"
|
||||
#include "PointerTool.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "WbView3D.h"
|
||||
|
||||
BuildList *BuildList::m_staticThis = NULL;
|
||||
Bool BuildList::m_updating = false;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// BuildList dialog
|
||||
|
||||
|
||||
BuildList::BuildList(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
//{{AFX_DATA_INIT(BuildList)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
BuildList::~BuildList(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void BuildList::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(BuildList)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(BuildList, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(BuildList)
|
||||
ON_CBN_SELCHANGE(IDC_SIDES_COMBO, OnSelchangeSidesCombo)
|
||||
ON_BN_CLICKED(IDC_MOVE_UP, OnMoveUp)
|
||||
ON_BN_CLICKED(IDC_MOVE_DOWN, OnMoveDown)
|
||||
ON_BN_CLICKED(IDC_ADD_BUILDING, OnAddBuilding)
|
||||
ON_LBN_SELCHANGE(IDC_BUILD_LIST, OnSelchangeBuildList)
|
||||
ON_BN_CLICKED(IDC_ALREADY_BUILD, OnAlreadyBuild)
|
||||
ON_BN_CLICKED(IDC_DELETE_BUILDING, OnDeleteBuilding)
|
||||
ON_CBN_SELENDOK(IDC_REBUILDS, OnSelendokRebuilds)
|
||||
ON_CBN_EDITCHANGE(IDC_REBUILDS, OnEditchangeRebuilds)
|
||||
ON_LBN_DBLCLK(IDC_BUILD_LIST, OnDblclkBuildList)
|
||||
ON_EN_CHANGE(IDC_MAPOBJECT_ZOffset, OnChangeZOffset)
|
||||
ON_EN_CHANGE(IDC_MAPOBJECT_Angle, OnChangeAngle)
|
||||
ON_BN_CLICKED(IDC_EXPORT, OnExport)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// BuildList data access method.
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// BuildList message handlers
|
||||
|
||||
/// Setup the controls in the dialog.
|
||||
BOOL BuildList::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_heightSlider.SetupPopSliderButton(this, IDC_HEIGHT_POPUP, this);
|
||||
m_angleSlider.SetupPopSliderButton(this, IDC_ANGLE_POPUP, this);
|
||||
|
||||
m_updating = true;
|
||||
loadSides();
|
||||
m_curSide = 0;
|
||||
updateCurSide();
|
||||
OnSelchangeBuildList();
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/// Load the sides in the sides list.
|
||||
void BuildList::loadSides(void)
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_SIDES_COMBO);
|
||||
if (!pCombo) {
|
||||
DEBUG_LOG(("*** BuildList::loadSides Missing resource!!!\n"));
|
||||
return;
|
||||
}
|
||||
pCombo->ResetContent();
|
||||
Int i;
|
||||
for (i=0; i<TheSidesList->getNumSides(); i++) {
|
||||
Dict *d = TheSidesList->getSideInfo(i)->getDict();
|
||||
AsciiString name = d->getAsciiString(TheKey_playerName);
|
||||
UnicodeString uni = d->getUnicodeString(TheKey_playerDisplayName);
|
||||
AsciiString fmt;
|
||||
if (name.isEmpty())
|
||||
fmt = "(neutral player, cannot be edited)";
|
||||
else
|
||||
fmt.format("%s=\"%ls\"",name.str(),uni.str());
|
||||
pCombo->AddString(fmt.str());
|
||||
}
|
||||
updateCurSide();
|
||||
}
|
||||
|
||||
/// Updates the current side, loading it's build list.
|
||||
void BuildList::updateCurSide(void)
|
||||
{
|
||||
if (TheSidesList->getNumSides() < 1)
|
||||
return;
|
||||
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_SIDES_COMBO);
|
||||
if (!pCombo) {
|
||||
DEBUG_LOG(("*** BuildList::updateCurSide Missing resource!!!\n"));
|
||||
return;
|
||||
}
|
||||
if (m_curSide<0 || m_curSide >= TheSidesList->getNumSides()) {
|
||||
m_curSide = 0;
|
||||
}
|
||||
if (pCombo->GetCurSel() != m_curSide) {
|
||||
pCombo->SetCurSel(m_curSide);
|
||||
}
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(m_curSide);
|
||||
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
if (!pList) {
|
||||
DEBUG_LOG(("*** BuildList::updateCurSide Missing resource!!! IDC_BUILD_LIST\n"));
|
||||
return;
|
||||
}
|
||||
pList->ResetContent();
|
||||
|
||||
BuildListInfo *pBuild = pSide->getBuildList();
|
||||
while (pBuild) {
|
||||
const char *pName = pBuild->getTemplateName().str();
|
||||
pList->AddString(pName);
|
||||
pBuild = pBuild->getNext();
|
||||
}
|
||||
OnSelchangeBuildList();
|
||||
}
|
||||
|
||||
void BuildList::OnSelchangeSidesCombo()
|
||||
{
|
||||
if (TheSidesList->getNumSides() < 1)
|
||||
return;
|
||||
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_SIDES_COMBO);
|
||||
if (!pCombo) {
|
||||
DEBUG_LOG(("*** BuildList::OnSelchangeSidesCombo Missing resource!!!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pCombo->GetCurSel() != m_curSide) {
|
||||
m_curSide = pCombo->GetCurSel();
|
||||
updateCurSide();
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
if (pList) {
|
||||
pList->SetCurSel(0);
|
||||
}
|
||||
OnSelchangeBuildList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void BuildList::OnMoveUp()
|
||||
{
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
if (!pList) {
|
||||
DEBUG_LOG(("*** BuildList::updateCurSide Missing resource!!! IDC_BUILD_LIST\n"));
|
||||
return;
|
||||
}
|
||||
m_curBuildList = pList->GetCurSel();
|
||||
if (m_curBuildList < 1) return;
|
||||
SidesList sides;
|
||||
sides = *TheSidesList;
|
||||
|
||||
SidesInfo *pSide = sides.getSideInfo(m_curSide);
|
||||
Int count = m_curBuildList;
|
||||
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
while (count) {
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
if (pBuildInfo == NULL) return;
|
||||
}
|
||||
Int newSel = m_curBuildList-1;
|
||||
pSide->reorderInBuildList(pBuildInfo, newSel);
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
SidesListUndoable *pUndo = new SidesListUndoable(sides, pDoc);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
|
||||
updateCurSide();
|
||||
pList->SetCurSel(newSel);
|
||||
OnSelchangeBuildList();
|
||||
}
|
||||
|
||||
void BuildList::OnMoveDown()
|
||||
{
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
if (!pList) {
|
||||
DEBUG_LOG(("*** BuildList::updateCurSide Missing resource!!! IDC_BUILD_LIST\n"));
|
||||
return;
|
||||
}
|
||||
if (m_curBuildList < 0) return;
|
||||
SidesList sides;
|
||||
sides = *TheSidesList;
|
||||
|
||||
SidesInfo *pSide = sides.getSideInfo(m_curSide);
|
||||
m_curBuildList = pList->GetCurSel();
|
||||
Int count = m_curBuildList;
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
while (count) {
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
if (pBuildInfo == NULL) return;
|
||||
}
|
||||
if (pBuildInfo->getNext() == NULL) {
|
||||
// there isn't one to move down after.
|
||||
return;
|
||||
}
|
||||
Int newSel = m_curBuildList+1;
|
||||
|
||||
pSide->reorderInBuildList(pBuildInfo, newSel);
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
SidesListUndoable *pUndo = new SidesListUndoable(sides, pDoc);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
updateCurSide();
|
||||
pList->SetCurSel(newSel);
|
||||
OnSelchangeBuildList();
|
||||
}
|
||||
|
||||
void BuildList::OnAddBuilding()
|
||||
{
|
||||
BuildListTool::addBuilding();
|
||||
}
|
||||
|
||||
|
||||
void BuildList::addBuilding(Coord3D loc, Real angle, AsciiString name)
|
||||
{
|
||||
if (!m_staticThis) {
|
||||
return;
|
||||
}
|
||||
BuildListInfo *pBuildInfo = newInstance( BuildListInfo);
|
||||
pBuildInfo->setAngle(angle);
|
||||
pBuildInfo->setTemplateName(name);
|
||||
pBuildInfo->setLocation(loc);
|
||||
|
||||
SidesList sides;
|
||||
sides = *TheSidesList;
|
||||
|
||||
SidesInfo *pSide = sides.getSideInfo(m_staticThis->m_curSide);
|
||||
pSide->addToBuildList(pBuildInfo, 1000); // add at end.
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
SidesListUndoable *pUndo = new SidesListUndoable(sides, pDoc);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_staticThis->updateCurSide();
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
p3View->invalBuildListItemInView(pBuildInfo);
|
||||
};
|
||||
|
||||
|
||||
void BuildList::setSelectedBuildList(BuildListInfo *pInfo)
|
||||
{
|
||||
if (!m_staticThis) {
|
||||
return;
|
||||
}
|
||||
Int i;
|
||||
for (i=0; i<TheSidesList->getNumSides(); i++) {
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(i);
|
||||
Int listSel = 0;
|
||||
for (BuildListInfo *pBuild = pSide->getBuildList(); pBuild; pBuild = pBuild->getNext()) {
|
||||
if (pInfo == pBuild) {
|
||||
pBuild->setSelected(true);
|
||||
// CComboBox *pCombo = (CComboBox*)m_staticThis->GetDlgItem(IDC_SIDES_COMBO);
|
||||
if (m_staticThis->m_curSide != i) {
|
||||
m_staticThis->m_curSide = i;
|
||||
m_staticThis->updateCurSide();
|
||||
}
|
||||
CListBox *pList = (CListBox*)m_staticThis->GetDlgItem(IDC_BUILD_LIST);
|
||||
pList->SetCurSel(listSel);
|
||||
m_staticThis->OnSelchangeBuildList();
|
||||
} else {
|
||||
pBuild->setSelected(false);
|
||||
}
|
||||
listSel++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void BuildList::OnSelchangeBuildList()
|
||||
{
|
||||
if (TheSidesList->getNumSides() < 1)
|
||||
return;
|
||||
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
if (!pList) {
|
||||
DEBUG_LOG(("*** BuildList::updateCurSide Missing resource!!! IDC_BUILD_LIST\n"));
|
||||
return;
|
||||
}
|
||||
m_curBuildList = pList->GetCurSel();
|
||||
Int numBL = pList->GetCount();
|
||||
|
||||
SidesInfo *pSide = NULL;
|
||||
if (TheSidesList) {
|
||||
pSide = TheSidesList->getSideInfo(m_curSide);
|
||||
}
|
||||
Int count = m_curBuildList;
|
||||
BuildListInfo *pBuildInfo = NULL;
|
||||
if (pSide) {
|
||||
pBuildInfo = pSide->getBuildList();
|
||||
}
|
||||
if (count<0) pBuildInfo = NULL;
|
||||
while (count && pBuildInfo) {
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
}
|
||||
|
||||
Bool enableUp=false;
|
||||
Bool enableDown=false;
|
||||
Bool enableAttrs = true;
|
||||
|
||||
Int energyConsumption = 0;
|
||||
Int energyProduction = 0;
|
||||
Real energyUsed = 0.0f;
|
||||
const Dict *playerDict = pSide->getDict();
|
||||
AsciiString playerName = playerDict->getAsciiString(TheKey_playerName);
|
||||
|
||||
// add up energy consumed/produced by objects on the map
|
||||
const ThingTemplate *thingTemplate;
|
||||
for (MapObject *pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext())
|
||||
{
|
||||
// get thing template based from map object name
|
||||
thingTemplate = pMapObj->getThingTemplate();
|
||||
if (!thingTemplate)
|
||||
continue;
|
||||
Dict *d = pMapObj->getProperties();
|
||||
Bool exists;
|
||||
AsciiString objectTeamName = d->getAsciiString(TheKey_originalOwner, &exists);
|
||||
TeamsInfo *teamInfo = TheSidesList->findTeamInfo(objectTeamName);
|
||||
Dict *teamDict = (teamInfo)?teamInfo->getDict():NULL;
|
||||
AsciiString objectOwnerName = (teamDict)?teamDict->getAsciiString(TheKey_teamOwner):AsciiString::TheEmptyString;
|
||||
|
||||
Int energy = 0;
|
||||
if (objectOwnerName.compareNoCase(playerName) == 0)
|
||||
energy = thingTemplate->getEnergyProduction();
|
||||
|
||||
if (energy > 0)
|
||||
energyProduction += energy;
|
||||
else
|
||||
energyConsumption -= energy; // keep it positive
|
||||
}
|
||||
|
||||
// add up energy consumed/produced by objects in the build list
|
||||
{
|
||||
count = m_curBuildList;
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
if (count<0) pBuildInfo = NULL;
|
||||
while (count>=0 && pBuildInfo) {
|
||||
AsciiString tName = pBuildInfo->getTemplateName();
|
||||
const ThingTemplate *templ = TheThingFactory->findTemplate(tName);
|
||||
if (!templ)
|
||||
break;
|
||||
|
||||
Int energy = templ->getEnergyProduction();
|
||||
|
||||
if (energy > 0)
|
||||
energyProduction += energy;
|
||||
else
|
||||
energyConsumption -= energy; // keep it positive
|
||||
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
}
|
||||
}
|
||||
|
||||
if (energyProduction)
|
||||
{
|
||||
energyUsed = (Real)energyConsumption/(Real)energyProduction;
|
||||
energyUsed = min(1.0f, energyUsed);
|
||||
energyUsed = max(0.0f, energyUsed);
|
||||
}
|
||||
else if (energyConsumption)
|
||||
{
|
||||
energyUsed = 1.0f;
|
||||
}
|
||||
//DEBUG_LOG(("Energy: %d/%d - %g\n", energyConsumption, energyProduction, energyUsed));
|
||||
CProgressCtrl *progressWnd = (CProgressCtrl *)GetDlgItem(IDC_POWER);
|
||||
if (progressWnd)
|
||||
{
|
||||
progressWnd->EnableWindow(true);
|
||||
progressWnd->SetPos((Int)((1.0f-energyUsed)*100));
|
||||
}
|
||||
|
||||
if (pBuildInfo==NULL) {
|
||||
enableAttrs = false;
|
||||
}
|
||||
if (m_curBuildList > 0) {
|
||||
enableUp = true;
|
||||
}
|
||||
if (m_curBuildList >= 0 && m_curBuildList < numBL-1) {
|
||||
enableDown = true;
|
||||
}
|
||||
CWnd *pWnd = GetDlgItem(IDC_MOVE_UP);
|
||||
if (pWnd) pWnd->EnableWindow(enableUp);
|
||||
pWnd = GetDlgItem(IDC_MOVE_DOWN);
|
||||
if (pWnd) pWnd->EnableWindow(enableDown);
|
||||
pWnd = GetDlgItem(IDC_ALREADY_BUILD);
|
||||
if (pWnd) pWnd->EnableWindow(enableAttrs);
|
||||
pWnd = GetDlgItem(IDC_REBUILDS);
|
||||
if (pWnd) pWnd->EnableWindow(enableAttrs);
|
||||
pWnd = GetDlgItem(IDC_DELETE_BUILDING);
|
||||
if (pWnd) pWnd->EnableWindow(enableAttrs);
|
||||
PointerTool::clearSelection(); // unselect other stuff.
|
||||
if (pBuildInfo) {
|
||||
CWnd *edit;
|
||||
static char buff[12];
|
||||
pBuildInfo->setSelected(true);
|
||||
|
||||
m_angle = pBuildInfo->getAngle() * 180/PI;
|
||||
sprintf(buff, "%0.2f", m_angle);
|
||||
edit = GetDlgItem(IDC_MAPOBJECT_Angle);
|
||||
edit->SetWindowText(buff);
|
||||
|
||||
m_height = pBuildInfo->getLocation()->z;
|
||||
sprintf(buff, "%0.2f", m_height);
|
||||
edit = GetDlgItem(IDC_MAPOBJECT_ZOffset);
|
||||
edit->SetWindowText(buff);
|
||||
|
||||
CButton *pBtn = (CButton *)GetDlgItem(IDC_ALREADY_BUILD);
|
||||
if (pBtn) pBtn->SetCheck(pBuildInfo->isInitiallyBuilt()?1:0);
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(IDC_REBUILDS);
|
||||
if (pCombo==NULL) return;
|
||||
UnsignedInt nr = pBuildInfo->getNumRebuilds();
|
||||
if (nr == BuildListInfo::UNLIMITED_REBUILDS) {
|
||||
pCombo->SetCurSel(6);
|
||||
} else if (nr<6) {
|
||||
pCombo->SetCurSel(nr);
|
||||
} else {
|
||||
CString str;
|
||||
str.Format("%d", nr);
|
||||
pCombo->SetWindowText(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void BuildList::OnAlreadyBuild()
|
||||
{
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
if (!pList) {
|
||||
DEBUG_LOG(("*** BuildList::updateCurSide Missing resource!!! IDC_BUILD_LIST\n"));
|
||||
return;
|
||||
}
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(m_curSide);
|
||||
m_curBuildList = pList->GetCurSel();
|
||||
if (m_curBuildList < 0) return;
|
||||
Int count = m_curBuildList;
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
while (count) {
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
if (pBuildInfo == NULL) return;
|
||||
}
|
||||
CButton *pBtn = (CButton *)GetDlgItem(IDC_ALREADY_BUILD);
|
||||
if (pBtn) {
|
||||
pBuildInfo->setInitiallyBuilt(pBtn->GetCheck()==1);
|
||||
}
|
||||
}
|
||||
|
||||
void BuildList::OnDeleteBuilding()
|
||||
{
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
if (!pList) {
|
||||
DEBUG_LOG(("*** BuildList::updateCurSide Missing resource!!! IDC_BUILD_LIST\n"));
|
||||
return;
|
||||
}
|
||||
m_curBuildList = pList->GetCurSel();
|
||||
if (m_curBuildList < 0) return;
|
||||
SidesList sides;
|
||||
sides = *TheSidesList;
|
||||
|
||||
SidesInfo *pSide = sides.getSideInfo(m_curSide);
|
||||
Int count = m_curBuildList;
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
while (count) {
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
if (pBuildInfo == NULL) return;
|
||||
}
|
||||
pSide->removeFromBuildList(pBuildInfo);
|
||||
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
SidesListUndoable *pUndo = new SidesListUndoable(sides, pDoc);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
updateCurSide();
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
p3View->invalBuildListItemInView(pBuildInfo);
|
||||
pList->SetCurSel(-1);
|
||||
}
|
||||
|
||||
void BuildList::OnSelendokRebuilds()
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(IDC_REBUILDS);
|
||||
if (pCombo==NULL) return;
|
||||
Int sel = pCombo->GetCurSel();
|
||||
if (sel<0) return; // no selection.
|
||||
UnsignedInt nr;
|
||||
if (sel == 6) {
|
||||
nr = BuildListInfo::UNLIMITED_REBUILDS;
|
||||
} else if (sel<6) {
|
||||
nr = sel;
|
||||
}
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(m_curSide);
|
||||
if (m_curBuildList < 0) return;
|
||||
Int count = m_curBuildList;
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
while (count) {
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
if (pBuildInfo == NULL) return;
|
||||
}
|
||||
pBuildInfo->setNumRebuilds(nr);
|
||||
}
|
||||
|
||||
void BuildList::OnEditchangeRebuilds()
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(IDC_REBUILDS);
|
||||
if (pCombo==NULL) return;
|
||||
Int sel = pCombo->GetCurSel();
|
||||
if (sel>=0) return; // An entry is selected, and handled by OnSelendokRebuilds..
|
||||
char buffer[_MAX_PATH];
|
||||
if (pCombo) {
|
||||
pCombo->GetWindowText(buffer, sizeof(buffer));
|
||||
Int nr;
|
||||
if (1==sscanf(buffer, "%d", &nr)) {
|
||||
}
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(m_curSide);
|
||||
if (m_curBuildList < 0) return;
|
||||
Int count = m_curBuildList;
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
while (count) {
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
if (pBuildInfo == NULL) return;
|
||||
}
|
||||
pBuildInfo->setNumRebuilds(nr);
|
||||
}
|
||||
}
|
||||
|
||||
void BuildList::OnDblclkBuildList()
|
||||
{
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(m_curSide);
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
m_curBuildList = pList->GetCurSel();
|
||||
if (m_curBuildList < 0) return;
|
||||
Int count = m_curBuildList;
|
||||
BuildListInfo *pBI = pSide->getBuildList();
|
||||
while (count) {
|
||||
count--;
|
||||
pBI = pBI->getNext();
|
||||
if (pBI == NULL) return;
|
||||
}
|
||||
|
||||
BaseBuildProps dlg;
|
||||
dlg.setProps(pBI->getBuildingName(), pBI->getScript(), pBI->getHealth(), pBI->getUnsellable());
|
||||
if (dlg.DoModal() == IDOK) {
|
||||
pBI->setBuildingName(dlg.getName());
|
||||
pBI->setScript(dlg.getScript());
|
||||
pBI->setHealth(dlg.getHealth());
|
||||
pBI->setUnsellable(dlg.getUnsellable());
|
||||
}
|
||||
}
|
||||
|
||||
void BuildList::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_HEIGHT_POPUP:
|
||||
*pMin = -50;
|
||||
*pMax = 50;
|
||||
*pInitial = m_height;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_ANGLE_POPUP:
|
||||
*pMin = 0;
|
||||
*pMax = 360;
|
||||
*pInitial = m_angle;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void BuildList::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
// CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
CWnd* edit;
|
||||
static char buff[12];
|
||||
switch (sliderID) {
|
||||
case IDC_HEIGHT_POPUP:
|
||||
m_height = theVal;
|
||||
sprintf(buff, "%0.2f", m_height);
|
||||
edit = GetDlgItem(IDC_MAPOBJECT_ZOffset);
|
||||
edit->SetWindowText(buff);
|
||||
OnChangeZOffset();
|
||||
break;
|
||||
|
||||
case IDC_ANGLE_POPUP:
|
||||
m_angle = theVal;
|
||||
sprintf(buff, "%0.2f", m_angle);
|
||||
edit = GetDlgItem(IDC_MAPOBJECT_Angle);
|
||||
edit->SetWindowText(buff);
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void BuildList::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_HEIGHT_POPUP:
|
||||
case IDC_ANGLE_POPUP:
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
void BuildList::OnChangeZOffset()
|
||||
{
|
||||
Real value = 0.0f;
|
||||
CWnd* edit = GetDlgItem(IDC_MAPOBJECT_ZOffset);
|
||||
CString cstr;
|
||||
edit->GetWindowText(cstr);
|
||||
if (!cstr.IsEmpty()) {
|
||||
value = atof(cstr);
|
||||
}
|
||||
m_height = value;
|
||||
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(m_curSide);
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
m_curBuildList = pList->GetCurSel();
|
||||
if (m_curBuildList < 0) return;
|
||||
Int count = m_curBuildList;
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
while (count) {
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
if (pBuildInfo == NULL) return;
|
||||
}
|
||||
Coord3D loc = *pBuildInfo->getLocation();
|
||||
loc.z = m_height;
|
||||
pBuildInfo->setLocation(loc);
|
||||
WbView3d *p3View = CWorldBuilderDoc::GetActiveDoc()->GetActive3DView();
|
||||
p3View->invalBuildListItemInView(pBuildInfo);
|
||||
}
|
||||
|
||||
void BuildList::OnChangeAngle()
|
||||
{
|
||||
Real angle = 0.0f;
|
||||
CWnd* edit = GetDlgItem(IDC_MAPOBJECT_Angle);
|
||||
CString cstr;
|
||||
edit->GetWindowText(cstr);
|
||||
if (!cstr.IsEmpty()) {
|
||||
angle = atof(cstr);
|
||||
}
|
||||
m_angle = angle;
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(m_curSide);
|
||||
CListBox *pList = (CListBox*)GetDlgItem(IDC_BUILD_LIST);
|
||||
m_curBuildList = pList->GetCurSel();
|
||||
if (m_curBuildList < 0) return;
|
||||
Int count = m_curBuildList;
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
while (count) {
|
||||
count--;
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
if (pBuildInfo == NULL) return;
|
||||
}
|
||||
pBuildInfo->setAngle(m_angle * PI/180);
|
||||
WbView3d *p3View = CWorldBuilderDoc::GetActiveDoc()->GetActive3DView();
|
||||
p3View->invalBuildListItemInView(pBuildInfo);
|
||||
}
|
||||
|
||||
void BuildList::OnExport()
|
||||
{
|
||||
static FILE *theLogFile = NULL;
|
||||
Bool open = false;
|
||||
try {
|
||||
char dirbuf[ _MAX_PATH ];
|
||||
::GetModuleFileName( NULL, dirbuf, sizeof( dirbuf ) );
|
||||
char *pEnd = dirbuf + strlen( dirbuf );
|
||||
while( pEnd != dirbuf )
|
||||
{
|
||||
if( *pEnd == '\\' )
|
||||
{
|
||||
*(pEnd + 1) = 0;
|
||||
break;
|
||||
}
|
||||
pEnd--;
|
||||
}
|
||||
|
||||
char curbuf[ _MAX_PATH ];
|
||||
|
||||
strcpy(curbuf, dirbuf);
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(m_curSide);
|
||||
Dict *d = TheSidesList->getSideInfo(m_curSide)->getDict();
|
||||
AsciiString name = d->getAsciiString(TheKey_playerName);
|
||||
strcat(curbuf, name.str());
|
||||
strcat(curbuf, "_BuildList");
|
||||
strcat(curbuf, ".ini");
|
||||
|
||||
theLogFile = fopen(curbuf, "w");
|
||||
if (theLogFile == NULL)
|
||||
throw;
|
||||
|
||||
AsciiString tmplname = d->getAsciiString(TheKey_playerFaction);
|
||||
const PlayerTemplate* pt = ThePlayerTemplateStore->findPlayerTemplate(NAMEKEY(tmplname));
|
||||
DEBUG_ASSERTCRASH(pt != NULL, ("PlayerTemplate %s not found -- this is an obsolete map (please open and resave in WB)\n",tmplname.str()));
|
||||
|
||||
fprintf(theLogFile, ";Skirmish AI Build List\n");
|
||||
fprintf(theLogFile, "SkirmishBuildList %s\n", pt->getSide().str());
|
||||
|
||||
open = true;
|
||||
|
||||
BuildListInfo *pBuildInfo = pSide->getBuildList();
|
||||
while (pBuildInfo) {
|
||||
fprintf(theLogFile, " Structure %s\n", pBuildInfo->getTemplateName().str());
|
||||
if (!pBuildInfo->getBuildingName().isEmpty()) {
|
||||
fprintf(theLogFile, " Name = %s\n", pBuildInfo->getBuildingName().str());
|
||||
}
|
||||
fprintf(theLogFile, " Location = X:%.2f Y:%.2f\n", pBuildInfo->getLocation()->x, pBuildInfo->getLocation()->y);
|
||||
fprintf(theLogFile, " Rebuilds = %d\n", pBuildInfo->getNumRebuilds());
|
||||
fprintf(theLogFile, " Angle = %.2f\n", pBuildInfo->getAngle()*180/PI);
|
||||
fprintf(theLogFile, " InitiallyBuilt = %s\n", pBuildInfo->isInitiallyBuilt()?"Yes":"No");
|
||||
fprintf(theLogFile, " AutomaticallyBuild = %s\n", false?"Yes":"No");
|
||||
fprintf(theLogFile, " END ;Structure %s\n", pBuildInfo->getTemplateName().str());
|
||||
pBuildInfo = pBuildInfo->getNext();
|
||||
}
|
||||
fprintf(theLogFile, "END ;SkirmishBuildList %s\n", tmplname.str());
|
||||
fclose(theLogFile);
|
||||
open = false;
|
||||
} catch (...) {
|
||||
if (open) {
|
||||
fclose(theLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
280
GeneralsMD/Code/Tools/WorldBuilder/src/BuildListTool.cpp
Normal file
280
GeneralsMD/Code/Tools/WorldBuilder/src/BuildListTool.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// BuildListTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "BuildListTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "DrawObject.h"
|
||||
#include "MainFrm.h"
|
||||
#include "ObjectTool.h"
|
||||
#include "PointerTool.h"
|
||||
#include "PickUnitDialog.h"
|
||||
#include "WbView3D.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
//
|
||||
// BuildListTool class.
|
||||
//
|
||||
|
||||
Bool BuildListTool::m_isActive = false;
|
||||
PickUnitDialog* BuildListTool::m_static_pickBuildingDlg = NULL;
|
||||
|
||||
/// Constructor
|
||||
BuildListTool::BuildListTool(void) :
|
||||
Tool(ID_BUILD_LIST_TOOL, IDC_BUILD_LIST_TOOL),
|
||||
m_rotateCursor(NULL),
|
||||
m_pointerCursor(NULL),
|
||||
m_moveCursor(NULL),
|
||||
m_created(false)
|
||||
{
|
||||
m_curObject = false;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
BuildListTool::~BuildListTool(void)
|
||||
{
|
||||
}
|
||||
|
||||
void BuildListTool::createWindow(void)
|
||||
{
|
||||
CRect frameRect;
|
||||
frameRect.top = ::AfxGetApp()->GetProfileInt(BUILD_PICK_PANEL_SECTION, "Top", 0);
|
||||
frameRect.left =::AfxGetApp()->GetProfileInt(BUILD_PICK_PANEL_SECTION, "Left", 0);
|
||||
|
||||
m_pickBuildingDlg.SetAllowableType(ES_STRUCTURE);
|
||||
m_pickBuildingDlg.SetFactionOnly(true);
|
||||
m_pickBuildingDlg.Create(IDD_PICKUNIT, CMainFrame::GetMainFrame());
|
||||
m_pickBuildingDlg.SetupAsPanel();
|
||||
m_pickBuildingDlg.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_static_pickBuildingDlg = &m_pickBuildingDlg;
|
||||
m_created = true;
|
||||
}
|
||||
|
||||
Bool BuildListTool::isDoingAdd(void)
|
||||
{
|
||||
if (!m_created) {
|
||||
return false;
|
||||
}
|
||||
if (!m_pickBuildingDlg.IsWindowVisible()) {
|
||||
return false;
|
||||
}
|
||||
if (m_pickBuildingDlg.getPickedUnit() == AsciiString::TheEmptyString) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Shows the object options panel.
|
||||
void BuildListTool::addBuilding()
|
||||
{
|
||||
//PickUnitDialog dlg;
|
||||
//dlg.SetAllowableType(ES_STRUCTURE);
|
||||
//dlg.SetFactionOnly(true);
|
||||
//if (dlg.DoModal() == IDOK) {
|
||||
//}
|
||||
//CMainFrame::GetMainFrame()->showOptionsDialog(IDD_OBJECT_OPTIONS);
|
||||
m_static_pickBuildingDlg->ShowWindow(SW_SHOWNA);
|
||||
}
|
||||
|
||||
/// Shows the object options panel.
|
||||
void BuildListTool::activate()
|
||||
{
|
||||
Bool wasActive = m_isActive;
|
||||
m_isActive = true;
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_BUILD_LIST_PANEL);
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
BuildList::update();
|
||||
WbView3d *p3View = CWorldBuilderDoc::GetActive3DView();
|
||||
if (!wasActive)
|
||||
{
|
||||
p3View->resetRenderObjects();
|
||||
p3View->invalObjectInView(NULL);
|
||||
}
|
||||
if (!m_created) {
|
||||
createWindow();
|
||||
}
|
||||
m_pickBuildingDlg.ShowWindow(SW_SHOWNA);
|
||||
}
|
||||
|
||||
/// Clears the isActive flag.
|
||||
void BuildListTool::deactivate()
|
||||
{
|
||||
m_isActive = false;
|
||||
WbView3d *p3View = CWorldBuilderDoc::GetActive3DView();
|
||||
Coord3D loc;
|
||||
loc.x=loc.y=loc.z=0;
|
||||
p3View->setObjTracking(NULL, loc, 0, false); // Turn off object cursor tracking.
|
||||
p3View->resetRenderObjects();
|
||||
p3View->invalObjectInView(NULL);
|
||||
m_pickBuildingDlg.ShowWindow(SW_HIDE);
|
||||
}
|
||||
|
||||
/** Set the cursor. */
|
||||
void BuildListTool::setCursor(void)
|
||||
{
|
||||
if (isDoingAdd()) {
|
||||
Tool::setCursor(); // Default cursor is the adding cursor
|
||||
} else if (m_mouseUpMove) {
|
||||
if (m_moveCursor == NULL) {
|
||||
m_moveCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_BUILD_MOVE));
|
||||
}
|
||||
::SetCursor(m_moveCursor);
|
||||
} else if (m_mouseUpRotate) {
|
||||
if (m_rotateCursor == NULL) {
|
||||
m_rotateCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_BUILD_ROTATE));
|
||||
}
|
||||
::SetCursor(m_rotateCursor);
|
||||
} else {
|
||||
if (m_pointerCursor == NULL) {
|
||||
m_pointerCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_BUILD_POINTER));
|
||||
}
|
||||
::SetCursor(m_pointerCursor);
|
||||
}
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse down - Place an object. */
|
||||
void BuildListTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
m_moving = false;
|
||||
m_rotating = false;
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
m_downPt2d = viewPt;
|
||||
m_downPt3d = cpt;
|
||||
if (isDoingAdd()) return;
|
||||
BuildListInfo *pInfo = pView->pickedBuildObjectInView(viewPt);
|
||||
if (pInfo) {
|
||||
m_curObject = pInfo;
|
||||
Coord3D center = *pInfo->getLocation();
|
||||
center.x -= m_downPt3d.x;
|
||||
center.y -= m_downPt3d.y;
|
||||
center.z = 0;
|
||||
Real len = center.length();
|
||||
// Check and see if we are within 1 cell size of the center.
|
||||
if (pInfo->isSelected() && len>0.5f*MAP_XY_FACTOR && len < 1.5f*MAP_XY_FACTOR) {
|
||||
m_rotating = true;
|
||||
}
|
||||
m_moving = true;
|
||||
m_prevPt3d = m_downPt3d;
|
||||
pView->snapPoint(&m_prevPt3d);
|
||||
BuildList::setSelectedBuildList(pInfo);
|
||||
} else {
|
||||
PointerTool::clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void BuildListTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt, false);
|
||||
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
if (isDoingAdd()) {
|
||||
Coord3D loc = cpt;
|
||||
MapObject *pCur = ObjectOptions::getObjectNamed(m_pickBuildingDlg.getPickedUnit());
|
||||
loc.z = 0;
|
||||
if (pCur) {
|
||||
// Display the transparent version of this object.
|
||||
p3View->setObjTracking(pCur, loc, 0, true);
|
||||
} else {
|
||||
// Don't display anything.
|
||||
p3View->setObjTracking(NULL, loc, 0, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
p3View->setObjTracking(NULL, cpt, 0, false);
|
||||
|
||||
if (m == TRACK_NONE) {
|
||||
// See if the cursor is over an object.
|
||||
BuildListInfo *pInfo = pView->pickedBuildObjectInView(viewPt);
|
||||
m_mouseUpMove = false;
|
||||
m_mouseUpRotate = false;
|
||||
if (pInfo) {
|
||||
Coord3D center = *pInfo->getLocation();
|
||||
center.x -= cpt.x;
|
||||
center.y -= cpt.y;
|
||||
center.z = 0;
|
||||
Real len = center.length();
|
||||
// Check and see if we are within 1 cell size of the center.
|
||||
if (pInfo->isSelected() && len>0.5f*MAP_XY_FACTOR && len < 1.5f*MAP_XY_FACTOR) {
|
||||
m_mouseUpRotate = true;
|
||||
} else {
|
||||
m_mouseUpMove = true;
|
||||
}
|
||||
}
|
||||
return; // setCursor will use the value of m_mouseUpRotate. jba.
|
||||
}
|
||||
|
||||
if (m != TRACK_L) return;
|
||||
if (!m_moving || !m_curObject) return;
|
||||
|
||||
Coord3D loc = *m_curObject->getLocation();
|
||||
if (m_rotating) {
|
||||
Real angle = ObjectTool::calcAngle(m_downPt3d, cpt, pView);
|
||||
m_curObject->setAngle(angle);
|
||||
} else {
|
||||
pView->snapPoint(&cpt);
|
||||
Real xOffset = (cpt.x-m_prevPt3d.x);
|
||||
Real yOffset = (cpt.y-m_prevPt3d.y);
|
||||
loc.x += xOffset;
|
||||
loc.y += yOffset;
|
||||
m_curObject->setLocation(loc);
|
||||
}
|
||||
p3View->invalBuildListItemInView(m_curObject);
|
||||
m_prevPt3d = cpt;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Execute the tool on mouse up - Place an object. */
|
||||
void BuildListTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
if (!isDoingAdd()) {
|
||||
// Update the cursor.
|
||||
mouseMoved(TRACK_NONE, viewPt, pView, pDoc);
|
||||
setCursor();
|
||||
return;
|
||||
}
|
||||
// always check hysteresis in view coords.
|
||||
enum {HYSTERESIS = 3};
|
||||
Bool justAClick = (abs(viewPt.x - m_downPt2d.x)<HYSTERESIS || abs(viewPt.x - m_downPt2d.x)<HYSTERESIS);
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt, false); // Don't constrain.
|
||||
|
||||
Coord3D loc = m_downPt3d;
|
||||
pView->snapPoint(&loc);
|
||||
loc.z = ObjectOptions::getCurObjectHeight();
|
||||
Real angle = justAClick ? 0 : ObjectTool::calcAngle(loc, cpt, pView);
|
||||
if (!m_pickBuildingDlg.getPickedUnit().isEmpty()) {
|
||||
BuildList::addBuilding(loc, angle, m_pickBuildingDlg.getPickedUnit());
|
||||
}
|
||||
//CMainFrame::GetMainFrame()->showOptionsDialog(IDD_BUILD_LIST_PANEL);
|
||||
}
|
||||
|
||||
76
GeneralsMD/Code/Tools/WorldBuilder/src/CButtonShowColor.cpp
Normal file
76
GeneralsMD/Code/Tools/WorldBuilder/src/CButtonShowColor.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
#include "Gameclient/ParticleSys.h"
|
||||
#include "CButtonShowColor.h"
|
||||
|
||||
// CButtonShowColor ///////////////////////////////////////////////////////////////////////////////
|
||||
void CButtonShowColor::OnPaint()
|
||||
{
|
||||
try {
|
||||
CPaintDC paintDC(this);
|
||||
|
||||
CPen pen(PS_SOLID, 1, 0xFFFFFF00);
|
||||
CBrush brush(RGBtoBGR(m_color.getAsInt()));
|
||||
// Select my stuff
|
||||
CPen *oldPen = paintDC.SelectObject(&pen);
|
||||
|
||||
CRect rect;
|
||||
GetWindowRect(&rect);
|
||||
ScreenToClient(&rect);
|
||||
|
||||
paintDC.FillRect(&rect, &brush);
|
||||
paintDC.MoveTo(rect.left, rect.top);
|
||||
paintDC.LineTo(rect.right, rect.top);
|
||||
paintDC.LineTo(rect.right, rect.bottom);
|
||||
paintDC.LineTo(rect.left, rect.bottom);
|
||||
paintDC.LineTo(rect.left, rect.top);
|
||||
|
||||
// Restore the states.
|
||||
paintDC.SelectObject(oldPen);
|
||||
|
||||
} catch (...) {
|
||||
// Unlikely, but possible.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CButtonShowColor::~CButtonShowColor()
|
||||
{
|
||||
DestroyWindow();
|
||||
}
|
||||
|
||||
// Convert from 0x00RRGGBB to 0x00BBGGRR
|
||||
COLORREF CButtonShowColor::RGBtoBGR(Int color)
|
||||
{
|
||||
return ((color & 0x00FF0000) >> 16 |
|
||||
(color & 0x0000FF00) << 0 |
|
||||
(color & 0x000000FF) << 16);
|
||||
}
|
||||
|
||||
// Convert from 0x00BBGGRR to 0x00RRGGBB
|
||||
Int CButtonShowColor::BGRtoRGB(COLORREF color)
|
||||
{
|
||||
return RGBtoBGR(color);
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(CButtonShowColor, CButton)
|
||||
ON_WM_PAINT()
|
||||
END_MESSAGE_MAP()
|
||||
46
GeneralsMD/Code/Tools/WorldBuilder/src/CButtonShowColor.h
Normal file
46
GeneralsMD/Code/Tools/WorldBuilder/src/CButtonShowColor.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _H_CBUTTONSHOWCOLOR_
|
||||
#define _H_CBUTTONSHOWCOLOR_
|
||||
|
||||
class CButtonShowColor : public CButton
|
||||
{
|
||||
protected:
|
||||
RGBColor m_color;
|
||||
|
||||
public:
|
||||
const RGBColor& getColor(void) const { return m_color; }
|
||||
void setColor(Int color) { m_color.setFromInt(color); }
|
||||
void setColor(const RGBColor& color) { m_color = color; }
|
||||
~CButtonShowColor();
|
||||
|
||||
|
||||
static COLORREF RGBtoBGR(Int color);
|
||||
static Int BGRtoRGB(COLORREF color);
|
||||
|
||||
|
||||
protected:
|
||||
afx_msg void OnPaint();
|
||||
|
||||
DECLARE_MESSAGE_MAP();
|
||||
};
|
||||
|
||||
#endif /* _H_CBUTTONSHOWCOLOR_ */
|
||||
106
GeneralsMD/Code/Tools/WorldBuilder/src/CFixTeamOwnerDialog.cpp
Normal file
106
GeneralsMD/Code/Tools/WorldBuilder/src/CFixTeamOwnerDialog.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "Resource.h"
|
||||
#include "CFixTeamOwnerDialog.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "Common/WellknownKeys.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
|
||||
static const char* NEUTRAL_NAME_STR = "(neutral)";
|
||||
|
||||
CFixTeamOwnerDialog::CFixTeamOwnerDialog( TeamsInfo *ti, SidesList *sl, UINT nIDTemplate, CWnd* pParentWnd) : CDialog( nIDTemplate, pParentWnd )
|
||||
{
|
||||
m_ti = ti;
|
||||
m_sl = sl;
|
||||
m_pickedValidTeam = false;
|
||||
}
|
||||
|
||||
AsciiString CFixTeamOwnerDialog::getSelectedOwner()
|
||||
{
|
||||
return m_selectedOwner;
|
||||
}
|
||||
|
||||
BOOL CFixTeamOwnerDialog::OnInitDialog()
|
||||
{
|
||||
AsciiString teamName = "No Name";
|
||||
|
||||
Bool exists;
|
||||
AsciiString temp = m_ti->getDict()->getAsciiString(TheKey_teamName, &exists);
|
||||
if (exists) {
|
||||
teamName = temp;
|
||||
}
|
||||
|
||||
CString loadStr;
|
||||
loadStr.Format(IDS_REPLACEOWNER, teamName.str());
|
||||
CWnd *pWnd = GetDlgItem(IDC_REPLACETHISTEXT);
|
||||
pWnd->SetWindowText(loadStr);
|
||||
|
||||
// now load all of the things with the other things
|
||||
Int numSides = m_sl->getNumSides();
|
||||
CListBox *pList = (CListBox*) GetDlgItem(IDC_VALIDTEAMLIST);
|
||||
|
||||
for (Int i = 0; i < numSides; ++i) {
|
||||
SidesInfo *si = m_sl->getSideInfo(i);
|
||||
if (!si) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Bool displayExists;
|
||||
AsciiString displayName = si->getDict()->getAsciiString(TheKey_playerDisplayName, &displayExists);
|
||||
if (displayExists) {
|
||||
if (displayName.isEmpty()) {
|
||||
displayName = NEUTRAL_NAME_STR;
|
||||
}
|
||||
pList->InsertString(-1, displayName.str());
|
||||
} else {
|
||||
AsciiString internalName = si->getDict()->getAsciiString(TheKey_playerName, &displayExists);
|
||||
if (internalName.isEmpty()) {
|
||||
internalName = NEUTRAL_NAME_STR;
|
||||
}
|
||||
pList->InsertString(-1, internalName.str());
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CFixTeamOwnerDialog::OnOK()
|
||||
{
|
||||
CDialog::OnOK();
|
||||
|
||||
CListBox *pList = (CListBox*) GetDlgItem(IDC_VALIDTEAMLIST);
|
||||
int curSel = pList->GetCurSel();
|
||||
|
||||
if (curSel < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SidesInfo *si = m_sl->getSideInfo(curSel);
|
||||
if (!si) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_pickedValidTeam = true;
|
||||
Bool exists;
|
||||
m_selectedOwner = si->getDict()->getAsciiString(TheKey_playerName, &exists);
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(CFixTeamOwnerDialog, CDialog)
|
||||
END_MESSAGE_MAP()
|
||||
1446
GeneralsMD/Code/Tools/WorldBuilder/src/CUndoable.cpp
Normal file
1446
GeneralsMD/Code/Tools/WorldBuilder/src/CUndoable.cpp
Normal file
File diff suppressed because it is too large
Load Diff
328
GeneralsMD/Code/Tools/WorldBuilder/src/CameraOptions.cpp
Normal file
328
GeneralsMD/Code/Tools/WorldBuilder/src/CameraOptions.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// CameraOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "CameraOptions.h"
|
||||
#include "WbView3d.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "wbview3d.h"
|
||||
|
||||
|
||||
#include "WaypointOptions.h" //WST 10/7/2002
|
||||
#include "CUndoable.h" //WST 10/7/2002
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CameraOptions dialog
|
||||
|
||||
|
||||
CameraOptions::CameraOptions(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(CameraOptions::IDD, pParent)
|
||||
{
|
||||
m_updating = false;
|
||||
//{{AFX_DATA_INIT(CameraOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void CameraOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(CameraOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(CameraOptions, CDialog)
|
||||
//{{AFX_MSG_MAP(CameraOptions)
|
||||
ON_BN_CLICKED(IDC_CameraReset, OnCameraReset)
|
||||
ON_BN_CLICKED(IDC_DROP_WAYPOINT_BUTTON, OnDropWaypointButton)
|
||||
ON_BN_CLICKED(IDC_CENTER_ON_SELECTED, OnCenterOnSelectedButton)
|
||||
ON_WM_MOVE()
|
||||
ON_EN_CHANGE(IDC_PITCH_EDIT, OnChangePitchEdit)
|
||||
ON_WM_SHOWWINDOW()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CameraOptions message handlers
|
||||
|
||||
void CameraOptions::OnCameraReset()
|
||||
{
|
||||
WbView3d * p3View = CWorldBuilderDoc::GetActive3DView();
|
||||
if (p3View)
|
||||
{
|
||||
p3View->setDefaultCamera();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
//WST 10/7/2002 - Drop waypoint button for Adam Isgreen -----------
|
||||
void CameraOptions::OnDropWaypointButton()
|
||||
{
|
||||
//The following code is taken from waypointTool.cpp. On mouse down.
|
||||
WbView3d * p3View = CWorldBuilderDoc::GetActive3DView();
|
||||
Coord3D docPt;
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
|
||||
Vector3 camTarget = p3View->getCameraTarget();
|
||||
docPt.x = camTarget.X;
|
||||
docPt.y = camTarget.Y;
|
||||
docPt.z = MAGIC_GROUND_Z;
|
||||
|
||||
//
|
||||
// MBL CNC3 INCURSION 10.29.2002 - Fix compile error w/ 10-15-2002 Drop
|
||||
//
|
||||
// MapObject *pNew = new MapObject(docPt, AsciiString("*Waypoints/Waypoint"), 0, 0, NULL, NULL );
|
||||
MapObject *pNew = newInstance(MapObject)(docPt, AsciiString("*Waypoints/Waypoint"), 0, 0, NULL, NULL );
|
||||
|
||||
Int id = pDoc->getNextWaypointID();
|
||||
AsciiString name = WaypointOptions::GenerateUniqueName(id);
|
||||
pNew->setSelected(true);
|
||||
pNew->setIsWaypoint();
|
||||
pNew->setWaypointID(id);
|
||||
pNew->setWaypointName(name);
|
||||
pNew->getProperties()->setAsciiString(TheKey_originalOwner, AsciiString("team"));
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, pNew);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
pNew = NULL; // undoable owns it now.
|
||||
}
|
||||
|
||||
//WST 11/25/2002 - New Center Camera button for Designers -----------
|
||||
void CameraOptions::OnCenterOnSelectedButton()
|
||||
{
|
||||
// Center camera on the selected map object
|
||||
|
||||
int count = 0;
|
||||
const Coord3D *objectPosition;
|
||||
|
||||
MapObject *mapObject = MapObject::getFirstMapObject();
|
||||
while (mapObject) {
|
||||
if (mapObject->isSelected()) {
|
||||
objectPosition = mapObject->getLocation();
|
||||
count++;
|
||||
}
|
||||
mapObject = mapObject->getNext();
|
||||
}
|
||||
|
||||
if (count==1) {
|
||||
// Only center if there is only one object selected on map
|
||||
WbView3d * p3View = CWorldBuilderDoc::GetActive3DView();
|
||||
if (p3View) {
|
||||
p3View->setCenterInView(objectPosition->x/MAP_XY_FACTOR,objectPosition->y/MAP_XY_FACTOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CameraOptions::OnMove(int x, int y)
|
||||
{
|
||||
CDialog::OnMove(x, y);
|
||||
|
||||
if (this->IsWindowVisible() && !this->IsIconic()) {
|
||||
CRect frameRect;
|
||||
GetWindowRect(&frameRect);
|
||||
::AfxGetApp()->WriteProfileInt(CAMERA_OPTIONS_PANEL_SECTION, "Top", frameRect.top);
|
||||
::AfxGetApp()->WriteProfileInt(CAMERA_OPTIONS_PANEL_SECTION, "Left", frameRect.left);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CameraOptions::putInt(Int ctrlID, Int val)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit = GetDlgItem(ctrlID);
|
||||
if (pEdit) {
|
||||
str.Format("%d", val);
|
||||
pEdit->SetWindowText(str);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraOptions::putReal(Int ctrlID, Real val)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit = GetDlgItem(ctrlID);
|
||||
if (pEdit) {
|
||||
str.Format("%g", val);
|
||||
pEdit->SetWindowText(str);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraOptions::putAsciiString(Int ctrlID, AsciiString val)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit = GetDlgItem(ctrlID);
|
||||
if (pEdit) {
|
||||
str.Format("%s", val.str());
|
||||
pEdit->SetWindowText(str);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CameraOptions::getReal(Int ctrlID, Real *rVal)
|
||||
{
|
||||
CWnd *pEdit = GetDlgItem(ctrlID);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Real val;
|
||||
if (1==sscanf(buffer, "%f", &val)) {
|
||||
*rVal = val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CameraOptions::stuffValuesIntoFields( void )
|
||||
{
|
||||
WbView3d * p3View = CWorldBuilderDoc::GetActive3DView();
|
||||
if (p3View)
|
||||
{
|
||||
m_updating = true;
|
||||
putReal(IDC_PITCH_EDIT, p3View->getCameraPitch());
|
||||
m_updating = false;
|
||||
|
||||
Real height = p3View->getHeightAboveGround();
|
||||
//WST 10/11/2002 This is inaccurate Real zoom = height/TheGlobalData->m_maxCameraHeight;
|
||||
Real zoom = p3View->getCurrentZoom(); //WST 10.11.2002
|
||||
|
||||
putReal(IDC_ZOOMTEXT, zoom);
|
||||
putReal(IDC_HEIGHTTEXT, height);
|
||||
|
||||
Vector3 source = p3View->getCameraSource();
|
||||
Vector3 target = p3View->getCameraTarget();
|
||||
// Real angle = p3View->getCameraAngle();
|
||||
|
||||
AsciiString s;
|
||||
s.format("(%g, %g)", target.X, target.Y);
|
||||
putAsciiString(IDC_POSTEXT, s);
|
||||
s.format("(%g, %g)", target.X - 1.0f*(source.X-target.X), target.Y - 1.0f*(source.Y-target.Y));
|
||||
putAsciiString(IDC_TARGETTEXT, s);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraOptions::update( void )
|
||||
{
|
||||
stuffValuesIntoFields();
|
||||
}
|
||||
|
||||
void CameraOptions::applyCameraPitch( Real pitch )
|
||||
{
|
||||
WbView3d * p3View = CWorldBuilderDoc::GetActive3DView();
|
||||
if (p3View)
|
||||
{
|
||||
p3View->setCameraPitch(pitch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CameraOptions::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_PITCH_POPUP:
|
||||
*pMin = 0;
|
||||
*pMax = 100;
|
||||
*pInitial = 100;
|
||||
*pLineSize = 1;
|
||||
{
|
||||
WbView3d * p3View = CWorldBuilderDoc::GetActive3DView();
|
||||
if (p3View)
|
||||
{
|
||||
*pInitial = (Int)(100.0f * p3View->getCameraPitch());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void CameraOptions::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_PITCH_POPUP:
|
||||
putReal(IDC_PITCH_EDIT, ((Real)theVal)*0.01f);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void CameraOptions::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_PITCH_POPUP:
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOL CameraOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_pitchPopup.SetupPopSliderButton(this, IDC_PITCH_POPUP, this);
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void CameraOptions::OnChangePitchEdit()
|
||||
{
|
||||
if (m_updating)
|
||||
return;
|
||||
|
||||
Real pitch;
|
||||
if (getReal(IDC_PITCH_EDIT, &pitch))
|
||||
{
|
||||
m_updating = true;
|
||||
applyCameraPitch(pitch);
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CameraOptions::OnShowWindow(BOOL bShow, UINT nStatus)
|
||||
{
|
||||
CDialog::OnShowWindow(bShow, nStatus);
|
||||
|
||||
update();
|
||||
}
|
||||
83
GeneralsMD/Code/Tools/WorldBuilder/src/CellWidth.cpp
Normal file
83
GeneralsMD/Code/Tools/WorldBuilder/src/CellWidth.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// CellWidth.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "CellWidth.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CellWidth dialog
|
||||
|
||||
/// Constructor and set initial cell width.
|
||||
CellWidth::CellWidth(int cellWidth, CWnd* pParent /*=NULL*/)
|
||||
: CDialog(CellWidth::IDD, pParent),
|
||||
mCellWidth(cellWidth)
|
||||
{
|
||||
//{{AFX_DATA_INIT(CellWidth)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void CellWidth::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(CellWidth)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CellWidth message handlers
|
||||
|
||||
/// Get the cell width value from the ui on ok.
|
||||
void CellWidth::OnOK()
|
||||
{
|
||||
CWnd *combo = GetDlgItem(IDC_CELL_WIDTH);
|
||||
CString val;
|
||||
if (combo) {
|
||||
combo->GetWindowText(val);
|
||||
mCellWidth = atoi(val);
|
||||
}
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
|
||||
/// Set the initial value of cell width into the combobox.
|
||||
BOOL CellWidth::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CWnd *combo = GetDlgItem(IDC_CELL_WIDTH);
|
||||
CString val;
|
||||
val.Format("%d", mCellWidth);
|
||||
if (combo) combo->SetWindowText(val);
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(CellWidth, CDialog)
|
||||
//{{AFX_MSG_MAP(CellWidth)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
160
GeneralsMD/Code/Tools/WorldBuilder/src/ContourOptions.cpp
Normal file
160
GeneralsMD/Code/Tools/WorldBuilder/src/ContourOptions.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ContourOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "ContourOptions.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
|
||||
Int ContourOptions::m_contourStep = 5;
|
||||
Int ContourOptions::m_contourOffset = 0;
|
||||
Int ContourOptions::m_contourWidth = 1;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// ContourOptions dialog trivial construstor - Create does the real work.
|
||||
|
||||
|
||||
ContourOptions::ContourOptions(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(ContourOptions::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(ContourOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
/// Windows default stuff.
|
||||
void ContourOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(ContourOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ContourOptions message handlers
|
||||
|
||||
/// Dialog UI initialization.
|
||||
/** Creates the slider controls, and sets the initial values for
|
||||
width and feather in the ui controls. */
|
||||
BOOL ContourOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_SLIDER1);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
pWnd->DestroyWindow();
|
||||
ScreenToClient(&rect);
|
||||
|
||||
m_contourStepSlider.Create(TBS_HORZ|TBS_BOTTOM|TBS_AUTOTICKS, rect, this, IDC_SLIDER1);
|
||||
m_contourStepSlider.SetRange(MIN_CONTOUR_STEP,MAX_CONTOUR_STEP);
|
||||
m_contourStepSlider.SetTicFreq(1);
|
||||
m_contourStepSlider.SetPos(MIN_CONTOUR_STEP + MAX_CONTOUR_STEP-m_contourStep);
|
||||
m_contourStepSlider.ShowWindow(SW_SHOW);
|
||||
|
||||
pWnd = GetDlgItem(IDC_SLIDER2);
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
pWnd->DestroyWindow();
|
||||
ScreenToClient(&rect);
|
||||
|
||||
m_contourOffsetSlider.Create(TBS_HORZ|TBS_BOTTOM|TBS_AUTOTICKS, rect, this, IDC_SLIDER2);
|
||||
m_contourOffsetSlider.SetRange(MIN_CONTOUR_OFFSET,MAX_CONTOUR_OFFSET);
|
||||
m_contourOffsetSlider.SetTicFreq(1);
|
||||
m_contourOffsetSlider.SetPos(m_contourOffset);
|
||||
m_contourOffsetSlider.ShowWindow(SW_SHOW);
|
||||
|
||||
pWnd = GetDlgItem(IDC_SLIDER3);
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
pWnd->DestroyWindow();
|
||||
ScreenToClient(&rect);
|
||||
|
||||
m_contourWidthSlider.Create(TBS_HORZ|TBS_BOTTOM|TBS_AUTOTICKS, rect, this, IDC_SLIDER3);
|
||||
m_contourWidthSlider.SetRange(MIN_WIDTH,MAX_WIDTH);
|
||||
m_contourWidthSlider.SetTicFreq(1);
|
||||
m_contourWidthSlider.SetPos(m_contourWidth);
|
||||
m_contourWidthSlider.ShowWindow(SW_SHOW);
|
||||
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_SHOW_CONTOURS);
|
||||
CWorldBuilderView *pView = CWorldBuilderDoc::GetActive2DView();
|
||||
if (pButton && pView) pButton->SetCheck(pView->getShowContours()?1:0);
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/// Handles slider ui messages.
|
||||
/** Gets the info, determines if it is the feather or width slider,
|
||||
gets the new value, and updates the correspondig edit control
|
||||
and the brush tool. */
|
||||
void ContourOptions::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
||||
{
|
||||
CString str;
|
||||
m_updating = true;
|
||||
Bool step = (pScrollBar && pScrollBar->m_hWnd == m_contourStepSlider.m_hWnd);
|
||||
Bool offset = (pScrollBar && pScrollBar->m_hWnd == m_contourOffsetSlider.m_hWnd);
|
||||
if (step) {
|
||||
if (nSBCode != TB_THUMBTRACK) {
|
||||
nPos = m_contourStepSlider.GetPos();
|
||||
}
|
||||
m_contourStep = MIN_CONTOUR_STEP + MAX_CONTOUR_STEP - nPos;
|
||||
} else if (offset) { // width
|
||||
if (nSBCode != TB_THUMBTRACK) {
|
||||
nPos = m_contourOffsetSlider.GetPos();
|
||||
}
|
||||
m_contourOffset = nPos;
|
||||
} else { // width
|
||||
if (nSBCode != TB_THUMBTRACK) {
|
||||
nPos = m_contourWidthSlider.GetPos();
|
||||
}
|
||||
m_contourWidth = nPos;
|
||||
}
|
||||
if (nSBCode!=TB_THUMBTRACK) {
|
||||
CWorldBuilderView *pView = CWorldBuilderDoc::GetActive2DView();
|
||||
if (pView)
|
||||
pView->setCellSize(pView->getCellSize());
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ContourOptions, CDialog)
|
||||
//{{AFX_MSG_MAP(ContourOptions)
|
||||
ON_WM_HSCROLL()
|
||||
ON_BN_CLICKED(IDC_SHOW_CONTOURS, OnShowContours)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
void ContourOptions::OnShowContours()
|
||||
{
|
||||
CWorldBuilderView *pView = CWorldBuilderDoc::GetActive2DView();
|
||||
if (pView) {
|
||||
Bool showContours = !pView->getShowContours();
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_SHOW_CONTOURS);
|
||||
if (pButton) pButton->SetCheck(showContours?1:0);
|
||||
pView->setShowContours(showContours);
|
||||
}
|
||||
}
|
||||
2518
GeneralsMD/Code/Tools/WorldBuilder/src/DrawObject.cpp
Normal file
2518
GeneralsMD/Code/Tools/WorldBuilder/src/DrawObject.cpp
Normal file
File diff suppressed because it is too large
Load Diff
460
GeneralsMD/Code/Tools/WorldBuilder/src/EditAction.cpp
Normal file
460
GeneralsMD/Code/Tools/WorldBuilder/src/EditAction.cpp
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// EditAction.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "EditAction.h"
|
||||
#include "EditParameter.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma message("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditAction dialog
|
||||
|
||||
|
||||
EditAction::EditAction(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(EditAction::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(EditAction)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void EditAction::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(EditAction)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(EditAction, CDialog)
|
||||
//{{AFX_MSG_MAP(EditAction)
|
||||
ON_WM_TIMER()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
static HTREEITEM findOrAdd(CTreeCtrl *tree, HTREEITEM parent, const char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = tree->GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
tree->GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = tree->GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = (char*)pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = tree->InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditAction message handlers
|
||||
|
||||
|
||||
BOOL EditAction::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
|
||||
// CDC *pDc =GetDC();
|
||||
CRect rect;
|
||||
|
||||
CTreeCtrl *pTree = (CTreeCtrl *)GetDlgItem(IDC_ACTION_TREE);
|
||||
pTree->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
m_actionTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP|WS_TABSTOP, rect, this, IDC_ACTION_TREE);
|
||||
m_actionTreeView.ShowWindow(SW_SHOW);
|
||||
pTree->DestroyWindow();
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_RICH_EDIT_HERE);
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_myEditCtrl.Create(WS_CHILD | WS_TABSTOP | ES_MULTILINE, rect, this, IDC_RICH_EDIT_HERE+1);
|
||||
m_myEditCtrl.ShowWindow(SW_SHOW);
|
||||
m_myEditCtrl.SetEventMask(m_myEditCtrl.GetEventMask() | ENM_LINK | ENM_SELCHANGE | ENM_KEYEVENTS);
|
||||
|
||||
Int i;
|
||||
HTREEITEM selItem = NULL;
|
||||
for (i=0; i<ScriptAction::NUM_ITEMS; i++) {
|
||||
const ActionTemplate *pTemplate = TheScriptEngine->getActionTemplate(i);
|
||||
char prefix[_MAX_PATH];
|
||||
const char *name = pTemplate->getName().str();
|
||||
|
||||
Int count = 0;
|
||||
HTREEITEM parent = TVI_ROOT;
|
||||
do {
|
||||
count = 0;
|
||||
const char *nameStart = name;
|
||||
while (*name && *name != '/') {
|
||||
count++;
|
||||
name++;
|
||||
}
|
||||
if (*name=='/') {
|
||||
count++;
|
||||
name++;
|
||||
} else {
|
||||
name = nameStart;
|
||||
count = 0;
|
||||
}
|
||||
if (count>0) {
|
||||
strncpy(prefix, nameStart, count);
|
||||
prefix[count-1] = 0;
|
||||
parent = findOrAdd(&m_actionTreeView, parent, prefix);
|
||||
}
|
||||
} while (count>0);
|
||||
|
||||
TVINSERTSTRUCT ins;
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = i;
|
||||
ins.item.pszText = (char*)name;
|
||||
ins.item.cchTextMax = 0;
|
||||
HTREEITEM item = m_actionTreeView.InsertItem(&ins);
|
||||
if (i == m_action->getActionType()) {
|
||||
selItem = item;
|
||||
}
|
||||
|
||||
name = pTemplate->getName2().str();
|
||||
count = 0;
|
||||
if (pTemplate->getName2().isEmpty()) continue;
|
||||
parent = TVI_ROOT;
|
||||
do {
|
||||
count = 0;
|
||||
const char *nameStart = name;
|
||||
while (*name && *name != '/') {
|
||||
count++;
|
||||
name++;
|
||||
}
|
||||
if (*name=='/') {
|
||||
count++;
|
||||
name++;
|
||||
} else {
|
||||
name = nameStart;
|
||||
count = 0;
|
||||
}
|
||||
if (count>0) {
|
||||
strncpy(prefix, nameStart, count);
|
||||
prefix[count-1] = 0;
|
||||
parent = findOrAdd(&m_actionTreeView, parent, prefix);
|
||||
}
|
||||
} while (count>0);
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = i;
|
||||
ins.item.pszText = (char*)name;
|
||||
ins.item.cchTextMax = 0;
|
||||
m_actionTreeView.InsertItem(&ins);
|
||||
}
|
||||
m_actionTreeView.Select(selItem, TVGN_FIRSTVISIBLE);
|
||||
m_actionTreeView.SelectItem(selItem);
|
||||
m_action->setWarnings(false);
|
||||
m_myEditCtrl.SetWindowText(m_action->getUiText().str());
|
||||
m_myEditCtrl.SetSel(-1, -1);
|
||||
formatScriptActionText(0);
|
||||
m_actionTreeView.SetFocus();
|
||||
|
||||
return FALSE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
|
||||
void EditAction::formatScriptActionText(Int parameterNdx) {
|
||||
CHARFORMAT2 cf;
|
||||
m_updating = true;
|
||||
long startSel, endSel;
|
||||
m_myEditCtrl.GetSel(startSel, endSel);
|
||||
memset(&cf, 0, sizeof(cf));
|
||||
cf.cbSize = sizeof(cf);
|
||||
cf.dwMask = CFM_FACE | CFM_SIZE |CFM_CHARSET | CFM_BOLD | CFM_LINK;
|
||||
cf.bCharSet = DEFAULT_CHARSET;
|
||||
cf.yHeight = 14;
|
||||
cf.bPitchAndFamily = FF_DONTCARE;
|
||||
strcpy(cf.szFaceName, "MS Sans Serif");
|
||||
cf.dwEffects = 0;
|
||||
m_myEditCtrl.SetDefaultCharFormat(cf);
|
||||
|
||||
m_myEditCtrl.SetSel(0, 1000);
|
||||
m_myEditCtrl.SetSelectionCharFormat(cf);
|
||||
//m_myEditCtrl.SetReadOnly();
|
||||
// Set up the links.
|
||||
cf.dwMask = CFE_UNDERLINE | CFM_LINK | CFM_COLOR;
|
||||
|
||||
cf.dwEffects = CFE_LINK | CFE_UNDERLINE;
|
||||
cf.crTextColor = RGB(0,0,255);
|
||||
m_curEditParameter = parameterNdx;
|
||||
|
||||
AsciiString strings[MAX_PARMS];
|
||||
Int curChar = 0;
|
||||
Int numChars = 0;
|
||||
Int numStrings = m_action->getUiStrings(strings);
|
||||
AsciiString warningText;
|
||||
AsciiString informationText;
|
||||
Int i;
|
||||
for (i=0; i<MAX_PARMS; i++) {
|
||||
if (i<numStrings) {
|
||||
curChar += strings[i].getLength();
|
||||
}
|
||||
if (i<m_action->getNumParameters()) {
|
||||
warningText.concat(EditParameter::getWarningText(m_action->getParameter(i), false));
|
||||
informationText.concat(EditParameter::getInfoText(m_action->getParameter(i)));
|
||||
numChars = m_action->getParameter(i)->getUiText().getLength();
|
||||
if (curChar==0) {
|
||||
curChar++;
|
||||
numChars--;
|
||||
}
|
||||
m_myEditCtrl.SetSel(curChar, curChar+numChars);
|
||||
if (i==parameterNdx) {
|
||||
startSel = curChar;
|
||||
endSel = curChar+numChars;
|
||||
cf.crTextColor = RGB(0,0,0); //black
|
||||
} else {
|
||||
cf.crTextColor = RGB(0,0,255); //blue
|
||||
}
|
||||
m_myEditCtrl.SetSelectionCharFormat(cf);
|
||||
curChar += numChars;
|
||||
}
|
||||
}
|
||||
|
||||
CString cstr;
|
||||
if (warningText.isEmpty()) {
|
||||
if (informationText.isEmpty()) {
|
||||
if (cstr.LoadString(IDS_SCRIPT_NOWARNINGS)) {
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->SetWindowText(cstr);
|
||||
}
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->EnableWindow(false);
|
||||
GetDlgItem(IDC_WARNINGS)->SetWindowText("");
|
||||
} else {
|
||||
if (cstr.LoadString(IDS_SCRIPT_INFORMATION)) {
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->SetWindowText(cstr);
|
||||
}
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->EnableWindow(true);
|
||||
GetDlgItem(IDC_WARNINGS)->SetWindowText(informationText.str());
|
||||
}
|
||||
} else {
|
||||
if (cstr.LoadString(IDS_SCRIPT_WARNINGS)) {
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->SetWindowText(cstr);
|
||||
}
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->EnableWindow(true);
|
||||
GetDlgItem(IDC_WARNINGS)->SetWindowText(warningText.str());
|
||||
}
|
||||
|
||||
m_modifiedTextColor = false;
|
||||
m_myEditCtrl.SetSel(startSel, endSel);
|
||||
m_curLinkChrg.cpMax = endSel;
|
||||
m_curLinkChrg.cpMin = startSel;
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL EditAction::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
|
||||
// Handle events from the tree control.
|
||||
if (pHdr->hdr.idFrom == IDC_ACTION_TREE) {
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
char buffer[_MAX_PATH];
|
||||
HTREEITEM hItem = m_actionTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_actionTreeView.GetItem(&item);
|
||||
if (item.lParam >= 0) {
|
||||
enum ScriptAction::ScriptActionType actionType = (enum ScriptAction::ScriptActionType)item.lParam;
|
||||
if (m_action->getActionType() != actionType) {
|
||||
m_action->setActionType( actionType );
|
||||
m_myEditCtrl.SetWindowText(m_action->getUiText().str());
|
||||
formatScriptActionText(0);
|
||||
}
|
||||
}
|
||||
} else if (pHdr->hdr.code == TVN_KEYDOWN) {
|
||||
NMTVKEYDOWN *pKey = (NMTVKEYDOWN*)lParam;
|
||||
Int key = pKey->wVKey;
|
||||
if (key==VK_SHIFT || key==VK_SPACE) {
|
||||
HTREEITEM hItem = m_actionTreeView.GetSelectedItem();
|
||||
if (!m_actionTreeView.ItemHasChildren(hItem)) {
|
||||
hItem = m_actionTreeView.GetParentItem(hItem);
|
||||
}
|
||||
m_actionTreeView.Expand(hItem, TVE_TOGGLE);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Handle events from the rich edit control containg the action pieces.
|
||||
if (LOWORD(wParam) == IDC_RICH_EDIT_HERE+1) {
|
||||
NMHDR *pHdr = (NMHDR *)lParam;
|
||||
if (pHdr->hwndFrom == m_myEditCtrl.m_hWnd) {
|
||||
if (pHdr->code == EN_LINK) {
|
||||
ENLINK *pLink = (ENLINK *)pHdr;
|
||||
CHARRANGE chrg = pLink->chrg;
|
||||
// Determine which parameter.
|
||||
Int numChars = 0;
|
||||
Int curChar = 0;
|
||||
AsciiString strings[MAX_PARMS];
|
||||
Int numStrings = m_action->getUiStrings(strings);
|
||||
Int i;
|
||||
Bool match = false;
|
||||
for (i=0; i<MAX_PARMS; i++) {
|
||||
if (i<numStrings) {
|
||||
curChar += strings[i].getLength();
|
||||
}
|
||||
if (i<m_action->getNumParameters()) {
|
||||
numChars = m_action->getParameter(i)->getUiText().getLength();
|
||||
match = (curChar+numChars/2 > chrg.cpMin && curChar+numChars/2 < chrg.cpMax);
|
||||
if (match) {
|
||||
m_curEditParameter = i;
|
||||
break;
|
||||
}
|
||||
curChar += numChars;
|
||||
}
|
||||
}
|
||||
if (pLink->msg == WM_LBUTTONDOWN)
|
||||
{
|
||||
// Determine which parameter.
|
||||
if (match)
|
||||
{
|
||||
if( m_action->getParameter( m_curEditParameter )->getParameterType() == Parameter::COMMANDBUTTON_ABILITY )
|
||||
{
|
||||
EditParameter::edit(m_action->getParameter(m_curEditParameter), 0, m_action->getParameter(0)->getString() );
|
||||
}
|
||||
else
|
||||
{
|
||||
EditParameter::edit(m_action->getParameter(m_curEditParameter), 0 );
|
||||
}
|
||||
m_myEditCtrl.SetWindowText(m_action->getUiText().str());
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CHARRANGE curChrg;
|
||||
m_myEditCtrl.GetSel(curChrg);
|
||||
if (curChrg.cpMin == chrg.cpMin && curChrg.cpMax == chrg.cpMax) {
|
||||
return true;
|
||||
}
|
||||
if (m_modifiedTextColor) {
|
||||
formatScriptActionText(m_curEditParameter);
|
||||
}
|
||||
m_curLinkChrg = chrg;
|
||||
m_myEditCtrl.SetSel(chrg.cpMin, chrg.cpMax);
|
||||
m_myEditCtrl.SetFocus();
|
||||
CHARFORMAT cf;
|
||||
memset(&cf, 0, sizeof(cf));
|
||||
cf.cbSize = sizeof(cf);
|
||||
cf.dwMask = CFM_COLOR;
|
||||
cf.crTextColor = RGB(0,0,0);
|
||||
m_myEditCtrl.SetSelectionCharFormat(cf);
|
||||
m_modifiedTextColor = true;
|
||||
return true;
|
||||
} else if (pHdr->code == EN_SETFOCUS) {
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
} else if (pHdr->code == EN_SELCHANGE) {
|
||||
if (m_updating) {
|
||||
return true;
|
||||
}
|
||||
CHARRANGE curChrg;
|
||||
m_myEditCtrl.GetSel(curChrg);
|
||||
if (curChrg.cpMin == m_curLinkChrg.cpMin && curChrg.cpMax == m_curLinkChrg.cpMax) {
|
||||
return true;
|
||||
}
|
||||
m_myEditCtrl.SetSel(m_curLinkChrg.cpMin, m_curLinkChrg.cpMax);
|
||||
if (m_modifiedTextColor) {
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
}
|
||||
} else if (pHdr->code == EN_MSGFILTER) {
|
||||
MSGFILTER *pFilter = (MSGFILTER *)pHdr;
|
||||
if (pFilter->msg == WM_CHAR) {
|
||||
Int keyPressed = pFilter->wParam;
|
||||
if (keyPressed==VK_RETURN) {
|
||||
return 1;
|
||||
}
|
||||
if (keyPressed==VK_RETURN || keyPressed == VK_TAB) {
|
||||
m_curEditParameter++;
|
||||
if (m_curEditParameter >= m_action->getNumParameters()) {
|
||||
m_curEditParameter = 0;
|
||||
return 1;
|
||||
}
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
EditParameter::edit(m_action->getParameter(m_curEditParameter), keyPressed);
|
||||
m_curEditParameter++;
|
||||
if (m_curEditParameter >= m_action->getNumParameters()) {
|
||||
m_curEditParameter = 0;
|
||||
}
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
/** Not actually a timer - just used to send a delayed message to self because rich
|
||||
edit control is stupid. jba. */
|
||||
void EditAction::OnTimer(UINT nIDEvent)
|
||||
{
|
||||
m_myEditCtrl.SetWindowText(m_action->getUiText().str());
|
||||
formatScriptActionText(m_curEditParameter);
|
||||
}
|
||||
482
GeneralsMD/Code/Tools/WorldBuilder/src/EditCondition.cpp
Normal file
482
GeneralsMD/Code/Tools/WorldBuilder/src/EditCondition.cpp
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// EditCondition.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "EditCondition.h"
|
||||
#include "EditParameter.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma message("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
LRESULT CMyTreeCtrl::WindowProc( UINT message, WPARAM wParam, LPARAM lParam )
|
||||
{
|
||||
if (message==WM_KEYDOWN) {
|
||||
Int nVirtKey = (int) wParam; // virtual-key code
|
||||
if (nVirtKey == ' ') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return CTreeCtrl::WindowProc(message, wParam, lParam);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditCondition dialog
|
||||
|
||||
|
||||
EditCondition::EditCondition(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(EditCondition::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(EditCondition)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void EditCondition::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(EditCondition)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(EditCondition, CDialog)
|
||||
//{{AFX_MSG_MAP(EditCondition)
|
||||
ON_CBN_SELCHANGE(IDC_CONDITION_TYPE, OnSelchangeConditionType)
|
||||
ON_WM_TIMER()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
static HTREEITEM findOrAdd(CTreeCtrl *tree, HTREEITEM parent, const char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = tree->GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
tree->GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = tree->GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = (char*)pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = tree->InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditCondition message handlers
|
||||
|
||||
|
||||
BOOL EditCondition::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
|
||||
// CDC *pDc =GetDC();
|
||||
CRect rect;
|
||||
|
||||
CTreeCtrl *pTree = (CTreeCtrl *)GetDlgItem(IDC_CONDITION_TREE);
|
||||
pTree->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
m_conditionTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP|WS_TABSTOP, rect, this, IDC_CONDITION_TREE);
|
||||
m_conditionTreeView.ShowWindow(SW_SHOW);
|
||||
pTree->DestroyWindow();
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_RICH_EDIT_HERE);
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_myEditCtrl.Create(WS_CHILD | WS_TABSTOP | ES_MULTILINE, rect, this, IDC_RICH_EDIT_HERE+1);
|
||||
m_myEditCtrl.ShowWindow(SW_SHOW);
|
||||
m_myEditCtrl.SetEventMask(m_myEditCtrl.GetEventMask() | ENM_LINK | ENM_SELCHANGE | ENM_KEYEVENTS);
|
||||
|
||||
Int i;
|
||||
HTREEITEM selItem = NULL;
|
||||
for (i=0; i<Condition::NUM_ITEMS; i++) {
|
||||
const ConditionTemplate *pTemplate = TheScriptEngine->getConditionTemplate(i);
|
||||
char prefix[_MAX_PATH];
|
||||
const char *name = pTemplate->getName().str();
|
||||
|
||||
Int count = 0;
|
||||
HTREEITEM parent = TVI_ROOT;
|
||||
do {
|
||||
count = 0;
|
||||
const char *nameStart = name;
|
||||
while (*name && *name != '/') {
|
||||
count++;
|
||||
name++;
|
||||
}
|
||||
if (*name=='/') {
|
||||
count++;
|
||||
name++;
|
||||
} else {
|
||||
name = nameStart;
|
||||
count = 0;
|
||||
}
|
||||
if (count>0) {
|
||||
strncpy(prefix, nameStart, count);
|
||||
prefix[count-1] = 0;
|
||||
parent = findOrAdd(&m_conditionTreeView, parent, prefix);
|
||||
}
|
||||
} while (count>0);
|
||||
|
||||
TVINSERTSTRUCT ins;
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = i;
|
||||
ins.item.pszText = (char*)name;
|
||||
ins.item.cchTextMax = 0;
|
||||
HTREEITEM item = m_conditionTreeView.InsertItem(&ins);
|
||||
if (i == m_condition->getConditionType()) {
|
||||
selItem = item;
|
||||
}
|
||||
|
||||
name = pTemplate->getName2().str();
|
||||
count = 0;
|
||||
if (pTemplate->getName2().isEmpty()) continue;
|
||||
parent = TVI_ROOT;
|
||||
do {
|
||||
count = 0;
|
||||
const char *nameStart = name;
|
||||
while (*name && *name != '/') {
|
||||
count++;
|
||||
name++;
|
||||
}
|
||||
if (*name=='/') {
|
||||
count++;
|
||||
name++;
|
||||
} else {
|
||||
name = nameStart;
|
||||
count = 0;
|
||||
}
|
||||
if (count>0) {
|
||||
strncpy(prefix, nameStart, count);
|
||||
prefix[count-1] = 0;
|
||||
parent = findOrAdd(&m_conditionTreeView, parent, prefix);
|
||||
}
|
||||
} while (count>0);
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = i;
|
||||
ins.item.pszText = (char*)name;
|
||||
ins.item.cchTextMax = 0;
|
||||
m_conditionTreeView.InsertItem(&ins);
|
||||
}
|
||||
m_conditionTreeView.Select(selItem, TVGN_FIRSTVISIBLE);
|
||||
m_conditionTreeView.SelectItem(selItem);
|
||||
m_condition->setWarnings(false);
|
||||
m_myEditCtrl.SetWindowText(m_condition->getUiText().str());
|
||||
m_myEditCtrl.SetSel(-1, -1);
|
||||
formatConditionText(0);
|
||||
m_conditionTreeView.SetFocus();
|
||||
|
||||
return FALSE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
|
||||
void EditCondition::formatConditionText(Int parameterNdx) {
|
||||
CHARFORMAT2 cf;
|
||||
m_updating = true;
|
||||
long startSel, endSel;
|
||||
m_myEditCtrl.GetSel(startSel, endSel);
|
||||
memset(&cf, 0, sizeof(cf));
|
||||
cf.cbSize = sizeof(cf);
|
||||
cf.dwMask = CFM_FACE | CFM_SIZE |CFM_CHARSET | CFM_BOLD | CFM_LINK;
|
||||
cf.bCharSet = DEFAULT_CHARSET;
|
||||
cf.yHeight = 14;
|
||||
cf.bPitchAndFamily = FF_DONTCARE;
|
||||
strcpy(cf.szFaceName, "MS Sans Serif");
|
||||
cf.dwEffects = 0;
|
||||
m_myEditCtrl.SetDefaultCharFormat(cf);
|
||||
|
||||
m_myEditCtrl.SetSel(0, 1000);
|
||||
m_myEditCtrl.SetSelectionCharFormat(cf);
|
||||
//m_myEditCtrl.SetReadOnly();
|
||||
// Set up the links.
|
||||
cf.dwMask = CFE_UNDERLINE | CFM_LINK | CFM_COLOR;
|
||||
|
||||
cf.dwEffects = CFE_LINK | CFE_UNDERLINE;
|
||||
cf.crTextColor = RGB(0,0,255);
|
||||
m_curEditParameter = parameterNdx;
|
||||
|
||||
AsciiString strings[MAX_PARMS];
|
||||
Int curChar = 0;
|
||||
Int numChars = 0;
|
||||
Int numStrings = m_condition->getUiStrings(strings);
|
||||
AsciiString warningText;
|
||||
AsciiString informationText;
|
||||
Int i;
|
||||
for (i=0; i<MAX_PARMS; i++) {
|
||||
if (i<numStrings) {
|
||||
curChar += strings[i].getLength();
|
||||
}
|
||||
if (i<m_condition->getNumParameters()) {
|
||||
warningText.concat(EditParameter::getWarningText(m_condition->getParameter(i), false));
|
||||
informationText.concat(EditParameter::getInfoText(m_condition->getParameter(i)));
|
||||
numChars = m_condition->getParameter(i)->getUiText().getLength();
|
||||
if (curChar==0) {
|
||||
curChar++;
|
||||
numChars--;
|
||||
}
|
||||
m_myEditCtrl.SetSel(curChar, curChar+numChars);
|
||||
if (i==parameterNdx) {
|
||||
startSel = curChar;
|
||||
endSel = curChar+numChars;
|
||||
cf.crTextColor = RGB(0,0,0); //black
|
||||
} else {
|
||||
cf.crTextColor = RGB(0,0,255); //blue
|
||||
}
|
||||
m_myEditCtrl.SetSelectionCharFormat(cf);
|
||||
curChar += numChars;
|
||||
}
|
||||
}
|
||||
|
||||
CString cstr;
|
||||
if (warningText.isEmpty()) {
|
||||
if (informationText.isEmpty()) {
|
||||
if (cstr.LoadString(IDS_SCRIPT_NOWARNINGS)) {
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->SetWindowText(cstr);
|
||||
}
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->EnableWindow(false);
|
||||
GetDlgItem(IDC_WARNINGS)->SetWindowText("");
|
||||
} else {
|
||||
if (cstr.LoadString(IDS_SCRIPT_INFORMATION)) {
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->SetWindowText(cstr);
|
||||
}
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->EnableWindow(true);
|
||||
GetDlgItem(IDC_WARNINGS)->SetWindowText(informationText.str());
|
||||
}
|
||||
} else {
|
||||
if (cstr.LoadString(IDS_SCRIPT_WARNINGS)) {
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->SetWindowText(cstr);
|
||||
}
|
||||
GetDlgItem(IDC_WARNINGS_CAPTION)->EnableWindow(true);
|
||||
GetDlgItem(IDC_WARNINGS)->SetWindowText(warningText.str());
|
||||
}
|
||||
|
||||
m_modifiedTextColor = false;
|
||||
m_myEditCtrl.SetSel(startSel, endSel);
|
||||
m_curLinkChrg.cpMax = endSel;
|
||||
m_curLinkChrg.cpMin = startSel;
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL EditCondition::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
|
||||
// Handle events from the tree control.
|
||||
if (pHdr->hdr.idFrom == IDC_CONDITION_TREE) {
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
char buffer[_MAX_PATH];
|
||||
HTREEITEM hItem = m_conditionTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_conditionTreeView.GetItem(&item);
|
||||
if (item.lParam >= 0) {
|
||||
enum Condition::ConditionType conditionType = (enum Condition::ConditionType)item.lParam;
|
||||
if (m_condition->getConditionType() != conditionType) {
|
||||
m_condition->setConditionType( conditionType );
|
||||
m_myEditCtrl.SetWindowText(m_condition->getUiText().str());
|
||||
formatConditionText(0);
|
||||
}
|
||||
}
|
||||
} else if (pHdr->hdr.code == TVN_KEYDOWN) {
|
||||
NMTVKEYDOWN *pKey = (NMTVKEYDOWN*)lParam;
|
||||
Int key = pKey->wVKey;
|
||||
if (key==VK_SHIFT || key==VK_SPACE) {
|
||||
HTREEITEM hItem = m_conditionTreeView.GetSelectedItem();
|
||||
if (!m_conditionTreeView.ItemHasChildren(hItem)) {
|
||||
hItem = m_conditionTreeView.GetParentItem(hItem);
|
||||
}
|
||||
m_conditionTreeView.Expand(hItem, TVE_TOGGLE);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Handle events from the rich edit control containg the condition pieces.
|
||||
if (LOWORD(wParam) == IDC_RICH_EDIT_HERE+1) {
|
||||
NMHDR *pHdr = (NMHDR *)lParam;
|
||||
if (pHdr->hwndFrom == m_myEditCtrl.m_hWnd) {
|
||||
if (pHdr->code == EN_LINK) {
|
||||
ENLINK *pLink = (ENLINK *)pHdr;
|
||||
CHARRANGE chrg = pLink->chrg;
|
||||
// Determine which parameter.
|
||||
Int numChars = 0;
|
||||
Int curChar = 0;
|
||||
AsciiString strings[MAX_PARMS];
|
||||
Int numStrings = m_condition->getUiStrings(strings);
|
||||
Int i;
|
||||
Bool match = false;
|
||||
for (i=0; i<MAX_PARMS; i++) {
|
||||
if (i<numStrings) {
|
||||
curChar += strings[i].getLength();
|
||||
}
|
||||
if (i<m_condition->getNumParameters()) {
|
||||
numChars = m_condition->getParameter(i)->getUiText().getLength();
|
||||
match = (curChar+numChars/2 > chrg.cpMin && curChar+numChars/2 < chrg.cpMax);
|
||||
if (match) {
|
||||
m_curEditParameter = i;
|
||||
break;
|
||||
}
|
||||
curChar += numChars;
|
||||
}
|
||||
}
|
||||
if (pLink->msg == WM_LBUTTONDOWN) {
|
||||
// Determine which parameter.
|
||||
if (match) {
|
||||
EditParameter::edit(m_condition->getParameter(m_curEditParameter), 0);
|
||||
m_myEditCtrl.SetWindowText(m_condition->getUiText().str());
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CHARRANGE curChrg;
|
||||
m_myEditCtrl.GetSel(curChrg);
|
||||
if (curChrg.cpMin == chrg.cpMin && curChrg.cpMax == chrg.cpMax) {
|
||||
return true;
|
||||
}
|
||||
if (m_modifiedTextColor) {
|
||||
formatConditionText(m_curEditParameter);
|
||||
}
|
||||
m_curLinkChrg = chrg;
|
||||
m_myEditCtrl.SetSel(chrg.cpMin, chrg.cpMax);
|
||||
m_myEditCtrl.SetFocus();
|
||||
CHARFORMAT cf;
|
||||
memset(&cf, 0, sizeof(cf));
|
||||
cf.cbSize = sizeof(cf);
|
||||
cf.dwMask = CFM_COLOR;
|
||||
cf.crTextColor = RGB(0,0,0);
|
||||
m_myEditCtrl.SetSelectionCharFormat(cf);
|
||||
m_modifiedTextColor = true;
|
||||
return true;
|
||||
} else if (pHdr->code == EN_SETFOCUS) {
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
} else if (pHdr->code == EN_SELCHANGE) {
|
||||
if (m_updating) {
|
||||
return true;
|
||||
}
|
||||
CHARRANGE curChrg;
|
||||
m_myEditCtrl.GetSel(curChrg);
|
||||
if (curChrg.cpMin == m_curLinkChrg.cpMin && curChrg.cpMax == m_curLinkChrg.cpMax) {
|
||||
return true;
|
||||
}
|
||||
m_myEditCtrl.SetSel(m_curLinkChrg.cpMin, m_curLinkChrg.cpMax);
|
||||
if (m_modifiedTextColor) {
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
}
|
||||
} else if (pHdr->code == EN_MSGFILTER) {
|
||||
MSGFILTER *pFilter = (MSGFILTER *)pHdr;
|
||||
if (pFilter->msg == WM_CHAR) {
|
||||
Int keyPressed = pFilter->wParam;
|
||||
if (keyPressed==VK_RETURN) {
|
||||
return 1;
|
||||
}
|
||||
if (keyPressed==VK_RETURN || keyPressed == VK_TAB) {
|
||||
m_curEditParameter++;
|
||||
if (m_curEditParameter >= m_condition->getNumParameters()) {
|
||||
m_curEditParameter = 0;
|
||||
return 1;
|
||||
}
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
EditParameter::edit(m_condition->getParameter(m_curEditParameter), keyPressed);
|
||||
m_curEditParameter++;
|
||||
if (m_curEditParameter >= m_condition->getNumParameters()) {
|
||||
m_curEditParameter = 0;
|
||||
}
|
||||
this->PostMessage(WM_TIMER, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
void EditCondition::OnSelchangeConditionType()
|
||||
{
|
||||
CComboBox *pCmbo = (CComboBox *)GetDlgItem(IDC_CONDITION_TYPE);
|
||||
Int index = 0;
|
||||
CString str;
|
||||
pCmbo->GetWindowText(str);
|
||||
Int i;
|
||||
for (i=0; i<ScriptAction::NUM_ITEMS; i++) {
|
||||
const ConditionTemplate *pTemplate = TheScriptEngine->getConditionTemplate(i);
|
||||
if (str == pTemplate->getName().str()) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_condition->setConditionType((enum Condition::ConditionType)index);
|
||||
m_myEditCtrl.SetWindowText(m_condition->getUiText().str());
|
||||
formatConditionText(m_curEditParameter);
|
||||
}
|
||||
|
||||
/** Not actually a timer - just used to send a delayed message to self because rich
|
||||
edit control is stupid. jba. */
|
||||
void EditCondition::OnTimer(UINT nIDEvent)
|
||||
{
|
||||
m_myEditCtrl.SetWindowText(m_condition->getUiText().str());
|
||||
formatConditionText(m_curEditParameter);
|
||||
}
|
||||
120
GeneralsMD/Code/Tools/WorldBuilder/src/EditCoordParameter.cpp
Normal file
120
GeneralsMD/Code/Tools/WorldBuilder/src/EditCoordParameter.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// EditCoordParameter.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "EditCoordParameter.h"
|
||||
#include "GameLogic/Scripts.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "GameLogic/PolygonTrigger.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditCoordParameter dialog
|
||||
|
||||
|
||||
EditCoordParameter::EditCoordParameter(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(EditCoordParameter::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(EditCoordParameter)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void EditCoordParameter::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(EditCoordParameter)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(EditCoordParameter, CDialog)
|
||||
//{{AFX_MSG_MAP(EditCoordParameter)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditCoordParameter message handlers
|
||||
|
||||
BOOL EditCoordParameter::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CEdit *pEditX = (CEdit *)GetDlgItem(IDC_EDIT_X);
|
||||
CEdit *pEditY = (CEdit *)GetDlgItem(IDC_EDIT_Y);
|
||||
CEdit *pEditZ = (CEdit *)GetDlgItem(IDC_EDIT_Z);
|
||||
|
||||
m_parameter->getCoord3D(&m_coord);
|
||||
CString string;
|
||||
string.Format("%.2f", m_coord.x);
|
||||
pEditX->SetWindowText(string);
|
||||
string.Format("%.2f", m_coord.y);
|
||||
pEditY->SetWindowText(string);
|
||||
string.Format("%.2f", m_coord.z);
|
||||
pEditZ->SetWindowText(string);
|
||||
|
||||
return FALSE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void EditCoordParameter::OnOK()
|
||||
{
|
||||
CEdit *pEditX = (CEdit *)GetDlgItem(IDC_EDIT_X);
|
||||
CEdit *pEditY = (CEdit *)GetDlgItem(IDC_EDIT_Y);
|
||||
CEdit *pEditZ = (CEdit *)GetDlgItem(IDC_EDIT_Z);
|
||||
CString txt;
|
||||
|
||||
pEditX->GetWindowText(txt);
|
||||
Real theReal;
|
||||
if (1==sscanf(txt, "%f", &theReal)) {
|
||||
m_coord.x = theReal;
|
||||
} else {
|
||||
pEditX->SetFocus();
|
||||
::MessageBeep(MB_ICONEXCLAMATION);
|
||||
return;
|
||||
}
|
||||
pEditY->GetWindowText(txt);
|
||||
if (1==sscanf(txt, "%f", &theReal)) {
|
||||
m_coord.y = theReal;
|
||||
} else {
|
||||
pEditX->SetFocus();
|
||||
::MessageBeep(MB_ICONEXCLAMATION);
|
||||
return;
|
||||
}
|
||||
pEditZ->GetWindowText(txt);
|
||||
if (1==sscanf(txt, "%f", &theReal)) {
|
||||
m_coord.z = theReal;
|
||||
} else {
|
||||
pEditX->SetFocus();
|
||||
::MessageBeep(MB_ICONEXCLAMATION);
|
||||
return;
|
||||
}
|
||||
m_parameter->friend_setCoord3D(&m_coord);
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
void EditCoordParameter::OnCancel()
|
||||
{
|
||||
|
||||
CDialog::OnCancel();
|
||||
}
|
||||
84
GeneralsMD/Code/Tools/WorldBuilder/src/EditGroup.cpp
Normal file
84
GeneralsMD/Code/Tools/WorldBuilder/src/EditGroup.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// EditGroup.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "EditGroup.h"
|
||||
#include "GameLogic/Scripts.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditGroup dialog
|
||||
|
||||
|
||||
EditGroup::EditGroup(ScriptGroup *pGroup, CWnd* pParent /*=NULL*/)
|
||||
: CDialog(EditGroup::IDD, pParent),
|
||||
m_scriptGroup(pGroup)
|
||||
{
|
||||
//{{AFX_DATA_INIT(EditGroup)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void EditGroup::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(EditGroup)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(EditGroup, CDialog)
|
||||
//{{AFX_MSG_MAP(EditGroup)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditGroup message handlers
|
||||
|
||||
void EditGroup::OnOK()
|
||||
{
|
||||
CString name;
|
||||
GetDlgItem(IDC_GROUP_NAME)->GetWindowText(name);
|
||||
m_scriptGroup->setName(AsciiString(name));
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_GROUP_ACTIVE);
|
||||
m_scriptGroup->setActive(pButton->GetCheck()==1);
|
||||
pButton = (CButton*)GetDlgItem(IDC_GROUP_SUBROUTINE);
|
||||
m_scriptGroup->setSubroutine(pButton->GetCheck()==1);
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
BOOL EditGroup::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_GROUP_ACTIVE);
|
||||
pButton->SetCheck(m_scriptGroup->isActive()?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_GROUP_SUBROUTINE);
|
||||
pButton->SetCheck(m_scriptGroup->isSubroutine()?1:0);
|
||||
|
||||
CEdit *pEdit = (CEdit*)GetDlgItem(IDC_GROUP_NAME);
|
||||
pEdit->SetWindowText(m_scriptGroup->getName().str());
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
287
GeneralsMD/Code/Tools/WorldBuilder/src/EditObjectParameter.cpp
Normal file
287
GeneralsMD/Code/Tools/WorldBuilder/src/EditObjectParameter.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// EditObjectParameter.cpp : implementation file
|
||||
//
|
||||
|
||||
#define DEFINE_EDITOR_SORTING_NAMES
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "EditObjectParameter.h"
|
||||
#include "EditParameter.h"
|
||||
|
||||
|
||||
#include "GameLogic/Scripts.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "GameLogic/PolygonTrigger.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingSort.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditObjectParameter dialog
|
||||
|
||||
|
||||
EditObjectParameter::EditObjectParameter(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(EditObjectParameter::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(EditObjectParameter)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void EditObjectParameter::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(EditObjectParameter)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(EditObjectParameter, CDialog)
|
||||
//{{AFX_MSG_MAP(EditObjectParameter)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EditObjectParameter message handlers
|
||||
|
||||
BOOL EditObjectParameter::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_OBJECT_TREEVIEW);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_objectTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_TERRAIN_TREEVIEW);
|
||||
m_objectTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
// add entries from the thing factory as the available objects to use
|
||||
const ThingTemplate *tTemplate;
|
||||
for( tTemplate = TheThingFactory->firstTemplate();
|
||||
tTemplate;
|
||||
tTemplate = tTemplate->friend_getNextTemplate() )
|
||||
{
|
||||
addObject(tTemplate);
|
||||
|
||||
} // end for tTemplate
|
||||
|
||||
|
||||
addObjectLists();
|
||||
|
||||
return FALSE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Add the object hierarchy paths to the tree view. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void EditObjectParameter::addObject( const ThingTemplate *thingTemplate )
|
||||
{
|
||||
char buffer[ _MAX_PATH ];
|
||||
HTREEITEM parent = TVI_ROOT;
|
||||
const char *leafName;
|
||||
//
|
||||
// if we have an thing template in mapObject, we've read it from the new INI database,
|
||||
// we will sort those items into the tree based on properties of the template that
|
||||
// make it easier for us to browse when building levels
|
||||
//
|
||||
// Feel free to reorganize how this tree is constructed from the template
|
||||
// data at will, whatever makes it easier for design
|
||||
//
|
||||
if( thingTemplate )
|
||||
{
|
||||
|
||||
// first check for test sorted objects
|
||||
if( thingTemplate->getEditorSorting() == ES_TEST )
|
||||
parent = findOrAdd( parent, "TEST" );
|
||||
|
||||
// first sort by Side, either create or find the tree item with matching side name
|
||||
AsciiString side = thingTemplate->getDefaultOwningSide();
|
||||
DEBUG_ASSERTCRASH(!side.isEmpty(), ("NULL default side in template\n") );
|
||||
strcpy( buffer, side.str() );
|
||||
parent = findOrAdd( parent, buffer );
|
||||
|
||||
// next tier uses the editor sorting that design can specify in the INI
|
||||
for( EditorSortingType i = ES_FIRST;
|
||||
i < ES_NUM_SORTING_TYPES;
|
||||
i = (EditorSortingType)(i + 1) )
|
||||
{
|
||||
|
||||
if( thingTemplate->getEditorSorting() == i )
|
||||
{
|
||||
|
||||
parent = findOrAdd( parent, EditorSortingNames[ i ] );
|
||||
break; // exit for
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
if( i == ES_NUM_SORTING_TYPES )
|
||||
parent = findOrAdd( parent, "UNSORTED" );
|
||||
|
||||
// the leaf name is the name of the template
|
||||
leafName = thingTemplate->getName().str();
|
||||
|
||||
} // end if
|
||||
|
||||
// add to the tree view
|
||||
if( leafName )
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = 0;
|
||||
ins.item.pszText = (char*)leafName;
|
||||
ins.item.cchTextMax = strlen(leafName)+2;
|
||||
m_objectTreeView.InsertItem(&ins);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
void EditObjectParameter::addObjectLists( )
|
||||
{
|
||||
HTREEITEM parent = TVI_ROOT;
|
||||
const char *leafName;
|
||||
|
||||
parent = findOrAdd(parent, "Object Lists");
|
||||
|
||||
std::vector<AsciiString> strings;
|
||||
EditParameter::loadObjectTypeList(NULL, &strings);
|
||||
|
||||
Int numItems = strings.size();
|
||||
|
||||
for (Int i = 0; i < numItems; ++i) {
|
||||
// add to the tree view
|
||||
|
||||
leafName = strings[i].str();
|
||||
|
||||
if( leafName )
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = 0;
|
||||
ins.item.pszText = (char*)leafName;
|
||||
ins.item.cchTextMax = strlen(leafName)+2;
|
||||
m_objectTreeView.InsertItem(&ins);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
HTREEITEM EditObjectParameter::findOrAdd(HTREEITEM parent, const char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = m_objectTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = m_objectTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = (char*)pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = m_objectTreeView.InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
BOOL EditObjectParameter::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
|
||||
// Handle events from the tree control.
|
||||
if (pHdr->hdr.idFrom == IDC_TERRAIN_TREEVIEW) {
|
||||
if (pHdr->hdr.code == TVN_KEYDOWN) {
|
||||
NMTVKEYDOWN *pKey = (NMTVKEYDOWN*)lParam;
|
||||
Int key = pKey->wVKey;
|
||||
if (key==VK_SHIFT || key==VK_SPACE) {
|
||||
HTREEITEM hItem = m_objectTreeView.GetSelectedItem();
|
||||
if (!m_objectTreeView.ItemHasChildren(hItem)) {
|
||||
hItem = m_objectTreeView.GetParentItem(hItem);
|
||||
}
|
||||
m_objectTreeView.Expand(hItem, TVE_TOGGLE);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
void EditObjectParameter::OnOK()
|
||||
{
|
||||
char buffer[_MAX_PATH];
|
||||
HTREEITEM hItem = m_objectTreeView.GetSelectedItem();
|
||||
if (!hItem) {
|
||||
::MessageBeep(MB_ICONEXCLAMATION);
|
||||
return;
|
||||
}
|
||||
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
AsciiString objName = buffer;
|
||||
// We used to try to find the TT here, but now we don't because we
|
||||
// use object lists as well.
|
||||
m_parameter->friend_setString(objName);
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
void EditObjectParameter::OnCancel()
|
||||
{
|
||||
|
||||
CDialog::OnCancel();
|
||||
}
|
||||
2393
GeneralsMD/Code/Tools/WorldBuilder/src/EditParameter.cpp
Normal file
2393
GeneralsMD/Code/Tools/WorldBuilder/src/EditParameter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
87
GeneralsMD/Code/Tools/WorldBuilder/src/EulaDialog.cpp
Normal file
87
GeneralsMD/Code/Tools/WorldBuilder/src/EulaDialog.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// EulaDialog.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "euladialog.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define new DEBUG_NEW
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EulaDialog dialog
|
||||
|
||||
|
||||
EulaDialog::EulaDialog(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(EulaDialog::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(EulaDialog)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void EulaDialog::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(EulaDialog)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(EulaDialog, CDialog)
|
||||
//{{AFX_MSG_MAP(EulaDialog)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EulaDialog message handlers
|
||||
|
||||
BOOL EulaDialog::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CString theText;
|
||||
theText.LoadString( IDS_EULA_AGREEMENT1 );
|
||||
|
||||
CString concatText;
|
||||
concatText.LoadString( IDS_EULA_AGREEMENT2 );
|
||||
theText += concatText;
|
||||
concatText.LoadString( IDS_EULA_AGREEMENT3 );
|
||||
theText += concatText;
|
||||
concatText.LoadString( IDS_EULA_AGREEMENT4 );
|
||||
theText += concatText;
|
||||
concatText.LoadString( IDS_EULA_AGREEMENT5 );
|
||||
theText += concatText;
|
||||
|
||||
CWnd *theEditDialog = GetDlgItem( IDC_EDIT1 );
|
||||
if( theEditDialog )
|
||||
{
|
||||
theEditDialog->SetWindowText( theText );
|
||||
}
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ExportScriptsOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "ExportScriptsOptions.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ExportScriptsOptions dialog
|
||||
Bool ExportScriptsOptions::m_units = true;
|
||||
Bool ExportScriptsOptions::m_waypoints = true;
|
||||
Bool ExportScriptsOptions::m_triggers = true;
|
||||
Bool ExportScriptsOptions::m_allScripts = false;
|
||||
Bool ExportScriptsOptions::m_sides = true;
|
||||
|
||||
ExportScriptsOptions::ExportScriptsOptions(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(ExportScriptsOptions::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(ExportScriptsOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void ExportScriptsOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(ExportScriptsOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ExportScriptsOptions, CDialog)
|
||||
//{{AFX_MSG_MAP(ExportScriptsOptions)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ExportScriptsOptions message handlers
|
||||
|
||||
void ExportScriptsOptions::OnOK()
|
||||
{
|
||||
// TODO: Add extra validation here
|
||||
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_WAYPOINTS);
|
||||
m_waypoints = pButton->GetCheck()==1;
|
||||
pButton = (CButton*)GetDlgItem(IDC_UNITS);
|
||||
m_units = pButton->GetCheck()==1;
|
||||
pButton = (CButton*)GetDlgItem(IDC_TRIGGERS);
|
||||
m_triggers = pButton->GetCheck()==1;
|
||||
pButton = (CButton*)GetDlgItem(IDC_ALL_SCRIPTS);
|
||||
m_allScripts = pButton->GetCheck()==1;
|
||||
pButton = (CButton*)GetDlgItem(IDC_SIDES);
|
||||
m_sides = pButton->GetCheck()==1;
|
||||
|
||||
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
BOOL ExportScriptsOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_WAYPOINTS);
|
||||
pButton->SetCheck(m_waypoints?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_UNITS);
|
||||
pButton->SetCheck(m_units?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_TRIGGERS);
|
||||
pButton->SetCheck(m_triggers?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_ALL_SCRIPTS);
|
||||
pButton->SetCheck(m_allScripts?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_SELECTED_SCRIPTS);
|
||||
pButton->SetCheck(m_allScripts?0:1);
|
||||
pButton = (CButton*)GetDlgItem(IDC_SIDES);
|
||||
pButton->SetCheck(m_sides?1:0);
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
74
GeneralsMD/Code/Tools/WorldBuilder/src/EyedropperTool.cpp
Normal file
74
GeneralsMD/Code/Tools/WorldBuilder/src/EyedropperTool.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// EyedropperTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "EyedropperTool.h"
|
||||
#include "TerrainMaterial.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "MainFrm.h"
|
||||
#include "DrawObject.h"
|
||||
|
||||
//
|
||||
// EyedropperTool class.
|
||||
//
|
||||
/// Constructor
|
||||
EyedropperTool::EyedropperTool(void) :
|
||||
Tool(ID_EYEDROPPER_TOOL, IDC_EYEDROPPER)
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
EyedropperTool::~EyedropperTool(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Shows the terrain materials options panel.
|
||||
void EyedropperTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_TERRAIN_MATERIAL);
|
||||
TerrainMaterial::setToolOptions(true);
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
}
|
||||
|
||||
/// Perform the tool behavior on mouse down.
|
||||
/** Finds the current texture class, and tells the terrain material panel to use it as fg. */
|
||||
void EyedropperTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
|
||||
CPoint ndx;
|
||||
if (!pDoc->getCellIndexFromCoord(cpt, &ndx))
|
||||
return;
|
||||
|
||||
WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
Int texClass = pMap->getTextureClass(ndx.x, ndx.y, true);
|
||||
TerrainMaterial::setFgTexClass(texClass);
|
||||
}
|
||||
|
||||
236
GeneralsMD/Code/Tools/WorldBuilder/src/FeatherOptions.cpp
Normal file
236
GeneralsMD/Code/Tools/WorldBuilder/src/FeatherOptions.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// brushoptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "FeatherOptions.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "FeatherTool.h"
|
||||
|
||||
FeatherOptions *FeatherOptions::m_staticThis = NULL;
|
||||
Int FeatherOptions::m_currentFeather = 0;
|
||||
Int FeatherOptions::m_currentRate = 3;
|
||||
Int FeatherOptions::m_currentRadius = 1;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// FeatherOptions dialog trivial construstor - Create does the real work.
|
||||
|
||||
|
||||
FeatherOptions::FeatherOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
//{{AFX_DATA_INIT(FeatherOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
/// Windows default stuff.
|
||||
void FeatherOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(FeatherOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
/// Sets the feather value in the dialog.
|
||||
/** Update the value in the edit control and the slider. */
|
||||
void FeatherOptions::setFeather(Int feather)
|
||||
{
|
||||
CString buf;
|
||||
buf.Format("%d", feather);
|
||||
m_currentFeather = feather;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Sets the rate value in the dialog.
|
||||
/** Update the value in the edit control and the slider. */
|
||||
void FeatherOptions::setRate(Int rate)
|
||||
{
|
||||
CString buf;
|
||||
buf.Format("%d", rate);
|
||||
m_currentRate = rate;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_RATE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Sets the radius value in the dialog.
|
||||
/** Update the value in the edit control and the slider. */
|
||||
void FeatherOptions::setRadius(Int radius)
|
||||
{
|
||||
CString buf;
|
||||
buf.Format("%d", radius);
|
||||
m_currentRadius = radius;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_RADIUS_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// FeatherOptions message handlers
|
||||
|
||||
/// Dialog UI initialization.
|
||||
/** Creates the slider controls, and sets the initial values for
|
||||
width and feather in the ui controls. */
|
||||
BOOL FeatherOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
m_featherPopup.SetupPopSliderButton(this, IDC_SIZE_POPUP, this);
|
||||
m_radiusPopup.SetupPopSliderButton(this, IDC_RADIUS_POPUP, this);
|
||||
m_ratePopup.SetupPopSliderButton(this, IDC_RATE_POPUP, this);
|
||||
|
||||
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
setFeather(m_currentFeather);
|
||||
setRate(m_currentRate);
|
||||
setRadius(m_currentRadius);
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void FeatherOptions::OnChangeSizeEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int width;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &width)) {
|
||||
m_currentFeather = width;
|
||||
FeatherTool::setFeather(m_currentFeather);
|
||||
sprintf(buffer, "%.1f FEET.", m_currentFeather*MAP_XY_FACTOR);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_WIDTH_LABEL);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FeatherOptions::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_SIZE_POPUP:
|
||||
*pMin = MIN_FEATHER_SIZE;
|
||||
*pMax = MAX_FEATHER_SIZE;
|
||||
*pInitial = m_currentFeather;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_RADIUS_POPUP:
|
||||
*pMin = MIN_RADIUS;
|
||||
*pMax = MAX_RADIUS;
|
||||
*pInitial = m_currentRadius;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_RATE_POPUP:
|
||||
*pMin = MIN_RATE;
|
||||
*pMax = MAX_RATE;
|
||||
*pInitial = m_currentRate;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void FeatherOptions::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit;
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_SIZE_POPUP:
|
||||
m_currentFeather = theVal;
|
||||
str.Format("%d",m_currentFeather);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
FeatherTool::setFeather(m_currentFeather);
|
||||
break;
|
||||
|
||||
case IDC_RADIUS_POPUP:
|
||||
m_currentRadius = theVal;
|
||||
str.Format("%d",m_currentRadius);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_RADIUS_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
FeatherTool::setRadius(m_currentRadius);
|
||||
break;
|
||||
|
||||
case IDC_RATE_POPUP:
|
||||
m_currentRate = theVal;
|
||||
str.Format("%d",m_currentRate);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_RATE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
FeatherTool::setRate(m_currentRate);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void FeatherOptions::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_SIZE_POPUP:
|
||||
case IDC_RADIUS_POPUP:
|
||||
case IDC_RATE_POPUP:
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(FeatherOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(FeatherOptions)
|
||||
ON_EN_CHANGE(IDC_SIZE_EDIT, OnChangeSizeEdit)
|
||||
ON_WM_HSCROLL()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
247
GeneralsMD/Code/Tools/WorldBuilder/src/FeatherTool.cpp
Normal file
247
GeneralsMD/Code/Tools/WorldBuilder/src/FeatherTool.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FeatherTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "FeatherTool.h"
|
||||
#include "FeatherOptions.h"
|
||||
#include "CUndoable.h"
|
||||
#include "DrawObject.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
//
|
||||
// FeatherTool class.
|
||||
Int FeatherTool::m_feather = 0;
|
||||
Int FeatherTool::m_rate = 0;
|
||||
Int FeatherTool::m_radius = 0;
|
||||
|
||||
//
|
||||
/// Constructor
|
||||
FeatherTool::FeatherTool(void) :
|
||||
Tool(ID_FEATHERTOOL, IDC_BRUSH_CROSS)
|
||||
{
|
||||
m_htMapEditCopy = NULL;
|
||||
m_htMapFeatherCopy = NULL;
|
||||
m_htMapRateCopy = NULL;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
FeatherTool::~FeatherTool(void)
|
||||
{
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
REF_PTR_RELEASE(m_htMapFeatherCopy);
|
||||
REF_PTR_RELEASE(m_htMapRateCopy);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Shows the brush options panel.
|
||||
void FeatherTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_FEATHER_OPTIONS);
|
||||
DrawObject::setDoBrushFeedback(true);
|
||||
DrawObject::setBrushFeedbackParms(false, m_feather, 0);
|
||||
}
|
||||
|
||||
/// Set the brush feather and notify the height options panel of the change.
|
||||
void FeatherTool::setFeather(Int feather)
|
||||
{
|
||||
if (m_feather != feather) {
|
||||
m_feather = feather;
|
||||
// notify feather palette options panel
|
||||
FeatherOptions::setFeather(m_feather);
|
||||
DrawObject::setBrushFeedbackParms(false, m_feather, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/// Set the brush feather and notify the height options panel of the change.
|
||||
void FeatherTool::setRate(Int rate)
|
||||
{
|
||||
if (m_rate != rate) {
|
||||
m_rate = rate;
|
||||
// notify feather palette options panel
|
||||
FeatherOptions::setRate(rate);
|
||||
}
|
||||
};
|
||||
|
||||
/// Set the brush feather and notify the height options panel of the change.
|
||||
void FeatherTool::setRadius(Int radius)
|
||||
{
|
||||
if (m_radius != radius) {
|
||||
m_radius = radius;
|
||||
// notify feather palette options panel
|
||||
FeatherOptions::setRadius(radius);
|
||||
}
|
||||
};
|
||||
|
||||
/// Start tool.
|
||||
/** Setup the tool to start brushing - make a copy of the height map
|
||||
to edit, another copy because we need it :), and call mouseMovedDown. */
|
||||
void FeatherTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
// WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
// just in case, release it.
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
m_htMapEditCopy = pDoc->GetHeightMap()->duplicate();
|
||||
m_htMapFeatherCopy = pDoc->GetHeightMap()->duplicate();
|
||||
m_htMapRateCopy = pDoc->GetHeightMap()->duplicate();
|
||||
Int size = m_htMapRateCopy->getXExtent() * m_htMapRateCopy->getYExtent();
|
||||
UnsignedByte *pData = m_htMapRateCopy->getDataPtr();
|
||||
Int i;
|
||||
for (i=0; i<size; i++) {
|
||||
*pData++ = 0;
|
||||
}
|
||||
m_prevXIndex = -1;
|
||||
m_prevYIndex = -1;
|
||||
mouseMoved(m, viewPt, pView, pDoc);
|
||||
}
|
||||
|
||||
/// End tool.
|
||||
/** Finish the tool operation - create a command, pass it to the
|
||||
doc to execute, and cleanup ref'd objects. */
|
||||
void FeatherTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
WBDocUndoable *pUndo = new WBDocUndoable(pDoc, m_htMapEditCopy);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
REF_PTR_RELEASE(m_htMapFeatherCopy);
|
||||
REF_PTR_RELEASE(m_htMapRateCopy);
|
||||
}
|
||||
|
||||
/// Execute the tool.
|
||||
/** Smooth the height map at the current point. */
|
||||
void FeatherTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
DrawObject::setFeedbackPos(cpt);
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
int brushWidth = m_feather;
|
||||
|
||||
CPoint ndx;
|
||||
getCenterIndex(&cpt, m_feather, &ndx, pDoc);
|
||||
|
||||
//if (m_prevXIndex == ndx.x && m_prevYIndex == ndx.y) return;
|
||||
|
||||
m_prevXIndex = ndx.x;
|
||||
m_prevYIndex = ndx.y;
|
||||
|
||||
int sub = brushWidth/2;
|
||||
int add = brushWidth-sub;
|
||||
|
||||
// round brush
|
||||
Int i, j;
|
||||
Bool redoRate = false;
|
||||
|
||||
for (i= ndx.x-sub; i< ndx.x+add; i++) {
|
||||
if (i<0 || i>=m_htMapEditCopy->getXExtent()) {
|
||||
continue;
|
||||
}
|
||||
for (j=ndx.y-sub; j<ndx.y+add; j++) {
|
||||
if (j<0 || j>=m_htMapEditCopy->getYExtent()) {
|
||||
continue;
|
||||
}
|
||||
Real blendFactor;
|
||||
blendFactor = calcRoundBlendFactor(ndx, i, j, m_feather, 0);
|
||||
// m_htMapEditCopy is the output.
|
||||
// m_htMapFeatherCopy is the original input.
|
||||
// m_htMapRateCopy is how much we use of the feathered data.
|
||||
if (blendFactor > 0.0f) {
|
||||
Int rate = m_htMapRateCopy->getHeight(i, j);
|
||||
rate += blendFactor * m_rate*5;
|
||||
if (rate>255) {
|
||||
rate = 255;
|
||||
redoRate = true;
|
||||
}
|
||||
m_htMapRateCopy->setHeight(i,j,rate);
|
||||
Int total=0;
|
||||
Real numSamples=0;
|
||||
Int ii, jj;
|
||||
Int radius = m_radius;
|
||||
if (radius<1) radius=1;
|
||||
if (radius>FeatherOptions::MAX_RADIUS) radius = FeatherOptions::MAX_RADIUS;
|
||||
for (ii = i-radius; ii < i+radius+1; ii++) {
|
||||
for (jj = j-radius; jj<j+radius+1; jj++) {
|
||||
Real factor;
|
||||
if (i==ii && j==jj) {
|
||||
factor = 1.0f;
|
||||
} else {
|
||||
Real dist = sqrt((ii-i)*(ii-i)+(jj-j)*(jj-j));
|
||||
if (dist<1.0) dist = 1.0;
|
||||
if (dist>radius) {
|
||||
factor = 0;
|
||||
} else {
|
||||
factor = 1.0f - (dist-1)/radius;
|
||||
}
|
||||
}
|
||||
int iNdx = ii;
|
||||
if (iNdx<0) iNdx = 1;
|
||||
if (iNdx >=m_htMapEditCopy->getXExtent()) {
|
||||
iNdx = m_htMapEditCopy->getXExtent()-1;
|
||||
}
|
||||
int jNdx = jj;
|
||||
if (jNdx<0) jNdx = 1;
|
||||
if (jNdx >=m_htMapEditCopy->getYExtent()) {
|
||||
jNdx = m_htMapEditCopy->getYExtent()-1;
|
||||
}
|
||||
total += m_htMapFeatherCopy->getHeight(iNdx, jNdx);
|
||||
numSamples+=1;
|
||||
}
|
||||
}
|
||||
total = floor((total/numSamples));
|
||||
UnsignedByte origHeight = m_htMapFeatherCopy->getHeight(i, j);
|
||||
float rateF = rate/255.0;
|
||||
total = floor(origHeight*(1.0f-rateF) + total*rateF + 0.5f);
|
||||
m_htMapEditCopy->setHeight(i, j, total);
|
||||
pDoc->invalCell(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IRegion2D partialRange;
|
||||
partialRange.lo.x = ndx.x - brushWidth;
|
||||
partialRange.hi.x = ndx.x + brushWidth;
|
||||
partialRange.lo.y = ndx.y - brushWidth;
|
||||
partialRange.hi.y = ndx.y + brushWidth;
|
||||
pDoc->updateHeightMap(m_htMapEditCopy, true, partialRange);
|
||||
if (redoRate) {
|
||||
Int size = m_htMapRateCopy->getXExtent() * m_htMapRateCopy->getYExtent();
|
||||
UnsignedByte *pData = m_htMapRateCopy->getDataPtr();
|
||||
UnsignedByte *pFeather = m_htMapFeatherCopy->getDataPtr();
|
||||
UnsignedByte *pEdit = m_htMapEditCopy->getDataPtr();
|
||||
Int i;
|
||||
for (i=0; i<size; i++) {
|
||||
*pData++ = 0;
|
||||
*pFeather = *pEdit;
|
||||
pFeather++; pEdit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
413
GeneralsMD/Code/Tools/WorldBuilder/src/FenceOptions.cpp
Normal file
413
GeneralsMD/Code/Tools/WorldBuilder/src/FenceOptions.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FenceOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
/** The fence options is essentially a front for the object options panel.
|
||||
The fence options panel has a subset of the objects available, and when one is
|
||||
selected, makes the current object in the object options panel match this object.
|
||||
Then the new object is created by the object options panel, so team parenting and
|
||||
so forth is all handled in the object options panel. jba. */
|
||||
|
||||
#define DEFINE_EDITOR_SORTING_NAMES
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "FenceOptions.h"
|
||||
#include "ObjectOptions.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "CUndoable.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingSort.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "GameClient/Color.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
FenceOptions *FenceOptions::m_staticThis = NULL;
|
||||
Bool FenceOptions::m_updating = false;
|
||||
Int FenceOptions::m_currentObjectIndex=-1;
|
||||
Real FenceOptions::m_fenceSpacing=1;
|
||||
Real FenceOptions::m_fenceOffset=0;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// FenceOptions dialog
|
||||
|
||||
|
||||
FenceOptions::FenceOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
m_objectsList = NULL;
|
||||
m_customSpacing = false;
|
||||
//{{AFX_DATA_INIT(FenceOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
FenceOptions::~FenceOptions(void)
|
||||
{
|
||||
if (m_objectsList) {
|
||||
m_objectsList->deleteInstance();
|
||||
}
|
||||
m_objectsList = NULL;
|
||||
}
|
||||
|
||||
|
||||
void FenceOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(FenceOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(FenceOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(FenceOptions)
|
||||
ON_EN_CHANGE(IDC_FENCE_SPACING_EDIT, OnChangeFenceSpacingEdit)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// FenceOptions data access method.
|
||||
|
||||
/*static*/ void FenceOptions::update()
|
||||
{
|
||||
if (m_staticThis) {
|
||||
m_staticThis->updateObjectOptions();
|
||||
}
|
||||
}
|
||||
|
||||
void FenceOptions::updateObjectOptions()
|
||||
{
|
||||
if (m_currentObjectIndex >= 0) {
|
||||
MapObject *pObj = m_objectsList;
|
||||
int count = 0;
|
||||
while (pObj) {
|
||||
if (count == m_currentObjectIndex) {
|
||||
ObjectOptions::selectObject(pObj);
|
||||
const ThingTemplate *t = pObj->getThingTemplate();
|
||||
if (t && !m_customSpacing) {
|
||||
if ( !m_customSpacing) {
|
||||
m_fenceSpacing = t->getFenceWidth();
|
||||
}
|
||||
m_fenceOffset = t->getFenceXOffset();
|
||||
}
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
pObj = pObj->getNext();
|
||||
}
|
||||
}
|
||||
CWnd *pWnd = GetDlgItem(IDC_FENCE_SPACING_EDIT);
|
||||
if (pWnd) {
|
||||
CString s;
|
||||
s.Format("%f",m_fenceSpacing);
|
||||
pWnd->SetWindowText(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// FenceOptions message handlers
|
||||
|
||||
/// Setup the controls in the dialog.
|
||||
BOOL FenceOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
|
||||
// CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
|
||||
// add entries from the thing factory as the available objects to use
|
||||
const ThingTemplate *tTemplate;
|
||||
for( tTemplate = TheThingFactory->firstTemplate();
|
||||
tTemplate;
|
||||
tTemplate = tTemplate->friend_getNextTemplate() )
|
||||
{
|
||||
Coord3D loc = { 0, 0, 0 };
|
||||
MapObject *pMap;
|
||||
|
||||
// Only add fence type objects.
|
||||
if (tTemplate->getFenceWidth() == 0) continue;
|
||||
|
||||
// create new map object
|
||||
pMap = newInstance( MapObject)( loc, tTemplate->getName(), 0.0f, 0, NULL, tTemplate );
|
||||
pMap->setNextMap( m_objectsList );
|
||||
m_objectsList = pMap;
|
||||
|
||||
// get display color for the editor
|
||||
Color cc = tTemplate->getDisplayColor();
|
||||
pMap->setColor(cc);
|
||||
|
||||
} // end for tTemplate
|
||||
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_OBJECT_HEIGHT_EDIT);
|
||||
if (pWnd) {
|
||||
CString s;
|
||||
s.Format("%d",MAGIC_GROUND_Z);
|
||||
pWnd->SetWindowText(s);
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_TERRAIN_TREEVIEW);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_objectTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_TERRAIN_TREEVIEW);
|
||||
m_objectTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
MapObject *pMap = m_objectsList;
|
||||
Int index = 0;
|
||||
while (pMap) {
|
||||
|
||||
addObject( pMap, pMap->getName().str(), "", index, TVI_ROOT);
|
||||
index++;
|
||||
pMap = pMap->getNext();
|
||||
}
|
||||
|
||||
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
HTREEITEM FenceOptions::findOrAdd(HTREEITEM parent, const char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = m_objectTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = m_objectTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = (char*)pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = m_objectTreeView.InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Add the object hierarchy paths to the tree view. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FenceOptions::addObject( MapObject *mapObject, const char *pPath, const char *name,
|
||||
Int terrainNdx, HTREEITEM parent )
|
||||
{
|
||||
char buffer[ _MAX_PATH ];
|
||||
const char *leafName = NULL;
|
||||
|
||||
// sanity
|
||||
if( mapObject == NULL )
|
||||
return;
|
||||
|
||||
//
|
||||
// if we have an thing template in mapObject, we've read it from the new INI database,
|
||||
// we will sort those items into the tree based on properties of the template that
|
||||
// make it easier for us to browse when building levels
|
||||
//
|
||||
// Feel free to reorganize how this tree is constructed from the template
|
||||
// data at will, whatever makes it easier for design
|
||||
//
|
||||
const ThingTemplate *thingTemplate = mapObject->getThingTemplate();
|
||||
if( thingTemplate )
|
||||
{
|
||||
|
||||
// first check for test sorted objects
|
||||
if( thingTemplate->getEditorSorting() == ES_TEST )
|
||||
parent = findOrAdd( parent, "TEST" );
|
||||
|
||||
// first sort by side, either create or find the tree item with matching side name
|
||||
AsciiString side = thingTemplate->getDefaultOwningSide();
|
||||
DEBUG_ASSERTCRASH( !side.isEmpty(), ("NULL default side in template\n") );
|
||||
strcpy( buffer, side.str() );
|
||||
parent = findOrAdd( parent, buffer );
|
||||
|
||||
// next tier uses the editor sorting that design can specify in the INI
|
||||
for( EditorSortingType i = ES_FIRST;
|
||||
i < ES_NUM_SORTING_TYPES;
|
||||
i = (EditorSortingType)(i + 1) )
|
||||
{
|
||||
|
||||
if( thingTemplate->getEditorSorting() == i )
|
||||
{
|
||||
|
||||
parent = findOrAdd( parent, EditorSortingNames[ i ] );
|
||||
break; // exit for
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
if( i == ES_NUM_SORTING_TYPES )
|
||||
parent = findOrAdd( parent, "UNSORTED" );
|
||||
|
||||
// the leaf name is the name of the template
|
||||
leafName = thingTemplate->getName().str();
|
||||
|
||||
} // end if
|
||||
|
||||
// add to the tree view
|
||||
if( leafName )
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = terrainNdx;
|
||||
ins.item.pszText = (char*)leafName;
|
||||
ins.item.cchTextMax = strlen(leafName)+2;
|
||||
m_objectTreeView.InsertItem(&ins);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Bool FenceOptions::hasSelectedObject(void)
|
||||
{
|
||||
// If we have no selected object, return false.
|
||||
if (m_currentObjectIndex==-1) return false;
|
||||
// If the objects panel has no selected object, return false.
|
||||
if (ObjectOptions::getCurGdfName() == AsciiString::TheEmptyString) return false;
|
||||
// If our selection is valid, return true.
|
||||
if (m_staticThis && m_currentObjectIndex >= 0) {
|
||||
MapObject *pObj = m_staticThis->m_objectsList;
|
||||
int count = 0;
|
||||
while (pObj) {
|
||||
if (count == m_currentObjectIndex) {
|
||||
return(true);
|
||||
}
|
||||
count++;
|
||||
pObj = pObj->getNext();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Set the selected object in the tree view.
|
||||
Bool FenceOptions::setObjectTreeViewSelection(HTREEITEM parent, Int selection)
|
||||
{
|
||||
TVITEM item;
|
||||
char buffer[NAME_MAX_LEN];
|
||||
::memset(&item, 0, sizeof(item));
|
||||
HTREEITEM child = m_objectTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT;
|
||||
item.hItem = child;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
if (item.lParam == selection) {
|
||||
m_objectTreeView.SelectItem(child);
|
||||
return(true);
|
||||
}
|
||||
if (setObjectTreeViewSelection(child, selection))
|
||||
{
|
||||
updateObjectOptions();
|
||||
return(true);
|
||||
}
|
||||
child = m_objectTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
BOOL FenceOptions::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
if (pHdr->hdr.hwndFrom == m_objectTreeView.m_hWnd) {
|
||||
|
||||
if (pHdr->hdr.code == TVN_ITEMEXPANDED) {
|
||||
if (pHdr->action == TVE_COLLAPSE) {
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_STATE;
|
||||
item.hItem = pHdr->itemOld.hItem;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
item.state &= (~TVIS_EXPANDEDONCE);
|
||||
item.mask = TVIF_STATE;
|
||||
m_objectTreeView.SetItem(&item);
|
||||
}
|
||||
}
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
char buffer[NAME_MAX_LEN];
|
||||
HTREEITEM hItem = m_objectTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
if (item.lParam >= 0) {
|
||||
m_currentObjectIndex = item.lParam;
|
||||
m_customSpacing = false;
|
||||
} else if (m_objectTreeView.ItemHasChildren(item.hItem)) {
|
||||
m_currentObjectIndex = -1;
|
||||
}
|
||||
updateObjectOptions();
|
||||
}
|
||||
}
|
||||
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void FenceOptions::OnChangeFenceSpacingEdit()
|
||||
{
|
||||
CWnd *pWnd = m_staticThis->GetDlgItem(IDC_FENCE_SPACING_EDIT);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
m_fenceSpacing = atof(val);
|
||||
m_customSpacing = true;
|
||||
}
|
||||
}
|
||||
218
GeneralsMD/Code/Tools/WorldBuilder/src/FenceTool.cpp
Normal file
218
GeneralsMD/Code/Tools/WorldBuilder/src/FenceTool.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FenceTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "FenceTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "DrawObject.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WbView3d.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
//
|
||||
// FenceTool class.
|
||||
//
|
||||
enum {MAX_OBJECTS = 200};
|
||||
/// Constructor
|
||||
FenceTool::FenceTool(void) :
|
||||
Tool(ID_FENCE_TOOL, IDC_FENCE),
|
||||
m_mapObjectList(NULL),
|
||||
m_objectCount(1)
|
||||
{
|
||||
m_curObjectWidth = 27.35f;
|
||||
m_curObjectOffset = 0;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
FenceTool::~FenceTool(void)
|
||||
{
|
||||
if (m_mapObjectList) {
|
||||
m_mapObjectList->deleteInstance();
|
||||
}
|
||||
m_mapObjectList = NULL;
|
||||
}
|
||||
|
||||
void FenceTool::updateMapObjectList(Coord3D downPt, Coord3D curPt, WbView* pView, CWorldBuilderDoc *pDoc, Bool checkPlayers)
|
||||
{
|
||||
Bool shiftKey = (0x8000 & ::GetAsyncKeyState(VK_SHIFT))!=0;
|
||||
Real angle = ObjectTool::calcAngle(downPt, curPt, pView);
|
||||
Coord3D delta;
|
||||
Coord3D normalDelta;
|
||||
delta.x = curPt.x-downPt.x;
|
||||
delta.y = curPt.y-downPt.y;
|
||||
delta.z = 0;
|
||||
Real length = delta.length();
|
||||
Int newCount = floor(length/m_curObjectWidth);
|
||||
if (newCount<1) newCount = 1;
|
||||
if (newCount > MAX_OBJECTS) newCount = MAX_OBJECTS;
|
||||
if (shiftKey) {
|
||||
// stretch the fence spacing. Keep the same count.
|
||||
delta.x /= (m_objectCount*m_curObjectWidth);
|
||||
delta.y /= (m_objectCount*m_curObjectWidth);
|
||||
} else {
|
||||
// Use exactly the spacing.
|
||||
m_objectCount = newCount;
|
||||
delta.normalize();
|
||||
}
|
||||
normalDelta = delta;
|
||||
if (angle==0) {
|
||||
normalDelta.x = 1;
|
||||
normalDelta.y = 0;
|
||||
}
|
||||
normalDelta.normalize();
|
||||
|
||||
Int i;
|
||||
if (m_mapObjectList == NULL) return;
|
||||
MapObject *pCurObj = m_mapObjectList;
|
||||
for (i=1; i<m_objectCount; i++) {
|
||||
if (pCurObj->getNext() == NULL) {
|
||||
pCurObj->setNextMap(ObjectOptions::duplicateCurMapObjectForPlace(&downPt, angle, checkPlayers));
|
||||
}
|
||||
pCurObj=pCurObj->getNext();
|
||||
if (pCurObj == NULL) return;
|
||||
}
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
MapObject *pXtraObjects = pCurObj->getNext();
|
||||
pCurObj->setNextMap(NULL);
|
||||
if (pXtraObjects) {
|
||||
p3View->removeFenceListObjects(pXtraObjects);
|
||||
pXtraObjects->deleteInstance();
|
||||
pXtraObjects = NULL;
|
||||
}
|
||||
|
||||
pCurObj = m_mapObjectList;
|
||||
for (i=0; i<m_objectCount; i++) {
|
||||
Real factor = m_curObjectWidth*(i);
|
||||
Coord3D curLoc = downPt;
|
||||
curLoc.x += factor*delta.x;
|
||||
curLoc.y += factor*delta.y;
|
||||
curLoc.x += m_curObjectOffset*normalDelta.x;
|
||||
curLoc.y += m_curObjectOffset*normalDelta.y;
|
||||
curLoc.z = MAGIC_GROUND_Z;
|
||||
|
||||
pCurObj->setAngle(angle);
|
||||
pCurObj->setLocation(&curLoc);
|
||||
pCurObj=pCurObj->getNext();
|
||||
}
|
||||
|
||||
p3View->updateFenceListObjects(m_mapObjectList);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Turn off object tracking.
|
||||
void FenceTool::deactivate()
|
||||
{
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc==NULL) return;
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
p3View->setObjTracking(NULL, m_downPt3d, 0, false);
|
||||
}
|
||||
/// Shows the object options panel
|
||||
void FenceTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_FENCE_OPTIONS);
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc==NULL) return;
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
p3View->setObjTracking(NULL, m_downPt3d, 0, false);
|
||||
FenceOptions::update();
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse down - Place an object. */
|
||||
void FenceTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
pView->snapPoint(&cpt);
|
||||
m_downPt2d = viewPt;
|
||||
m_downPt3d = cpt;
|
||||
if (m_mapObjectList) {
|
||||
m_mapObjectList->deleteInstance();
|
||||
m_mapObjectList = NULL;
|
||||
}
|
||||
if (FenceOptions::hasSelectedObject()) {
|
||||
FenceOptions::update();
|
||||
m_curObjectWidth = FenceOptions::getFenceSpacing();
|
||||
m_curObjectOffset = FenceOptions::getFenceOffset();
|
||||
m_mapObjectList = ObjectOptions::duplicateCurMapObjectForPlace(&m_downPt3d, 0, false);
|
||||
m_objectCount = 1;
|
||||
mouseMoved(m, viewPt, pView, pDoc);
|
||||
}
|
||||
}
|
||||
|
||||
/** Tracking - show the object. */
|
||||
void FenceTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
// Bool justAClick = true;
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt); // Do constrain.
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
Coord3D loc = cpt;
|
||||
pView->snapPoint(&loc);
|
||||
Real angle = 0 ;
|
||||
if (m == TRACK_L) { // Mouse is down, so fence.
|
||||
p3View->setObjTracking(NULL, loc, angle, false);
|
||||
updateMapObjectList(m_downPt3d,loc, pView, pDoc, false);
|
||||
return;
|
||||
}
|
||||
MapObject *pCur = ObjectOptions::getObjectNamed(AsciiString(ObjectOptions::getCurObjectName()));
|
||||
p3View->setObjTracking(NULL, m_downPt3d, 0, false);
|
||||
loc.z = ObjectOptions::getCurObjectHeight();
|
||||
if (pCur && FenceOptions::hasSelectedObject()) {
|
||||
// Display the transparent version of this object.
|
||||
m_curObjectOffset = FenceOptions::getFenceOffset();
|
||||
loc.x += m_curObjectOffset;
|
||||
p3View->setObjTracking(pCur, loc, angle, true);
|
||||
} else {
|
||||
// Don't display anything.
|
||||
p3View->setObjTracking(NULL, loc, angle, false);
|
||||
}
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse up - Place an object. */
|
||||
void FenceTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt); // Do constrain.
|
||||
Coord3D loc = cpt;
|
||||
pView->snapPoint(&loc);
|
||||
updateMapObjectList(m_downPt3d, loc, pView, pDoc, true);
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
if (m_mapObjectList) {
|
||||
p3View->removeFenceListObjects(m_mapObjectList);
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, m_mapObjectList);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_mapObjectList = NULL; // undoable owns it now.
|
||||
}
|
||||
}
|
||||
|
||||
119
GeneralsMD/Code/Tools/WorldBuilder/src/FloodFillTool.cpp
Normal file
119
GeneralsMD/Code/Tools/WorldBuilder/src/FloodFillTool.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FloodFillTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "FloodFillTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "DrawObject.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "TerrainMaterial.h"
|
||||
//
|
||||
// FloodFillTool class.
|
||||
//
|
||||
|
||||
Bool FloodFillTool::m_adjustCliffTextures = false;
|
||||
|
||||
/// Constructor
|
||||
FloodFillTool::FloodFillTool(void) :
|
||||
Tool(ID_TILE_FLOOD_FILL, IDC_FLOOD_FILL),
|
||||
m_cliffCursor(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
FloodFillTool::~FloodFillTool(void)
|
||||
{
|
||||
if (m_cliffCursor) {
|
||||
::DestroyCursor(m_cliffCursor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Shows the terrain materials options panel.
|
||||
void FloodFillTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_TERRAIN_MATERIAL);
|
||||
TerrainMaterial::setToolOptions(true);
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
m_adjustCliffTextures = false;
|
||||
}
|
||||
|
||||
/** Set the cursor. */
|
||||
void FloodFillTool::setCursor(void)
|
||||
{
|
||||
if (m_adjustCliffTextures) {
|
||||
if (m_cliffCursor == NULL) {
|
||||
m_cliffCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_CLIFF));
|
||||
}
|
||||
::SetCursor(m_cliffCursor);
|
||||
} else {
|
||||
Tool::setCursor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Left click code. Sets m_textureClassToDraw and calls eitherMouseDown()
|
||||
/// Perform the tool behavior on mouse down.
|
||||
/** Creates a copy of the height map, flood fills it at pt with m_textureClassToDraw which
|
||||
has been set by the calling routine. Then builds
|
||||
the command, and passes it to the doc. */
|
||||
void FloodFillTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
|
||||
CPoint ndx;
|
||||
if (!pDoc->getCellIndexFromCoord(cpt, &ndx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m == TRACK_L)
|
||||
m_textureClassToDraw = TerrainMaterial::getFgTexClass();
|
||||
else
|
||||
m_textureClassToDraw = TerrainMaterial::getBgTexClass();
|
||||
|
||||
// WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
WorldHeightMapEdit *htMapEditCopy = pDoc->GetHeightMap()->duplicate();
|
||||
Bool didIt = false;
|
||||
Bool shiftKey = (0x8000 & ::GetAsyncKeyState(VK_SHIFT))!=0;
|
||||
if (m_adjustCliffTextures) {
|
||||
didIt = htMapEditCopy->doCliffAdjustment(ndx.x, ndx.y);
|
||||
} else {
|
||||
didIt = htMapEditCopy->floodFill(ndx.x, ndx.y, m_textureClassToDraw, shiftKey);
|
||||
}
|
||||
if (didIt) {
|
||||
htMapEditCopy->optimizeTiles(); // force to optimize tileset
|
||||
IRegion2D partialRange = {0,0,0,0};
|
||||
pDoc->updateHeightMap(htMapEditCopy, false, partialRange);
|
||||
WBDocUndoable *pUndo = new WBDocUndoable(pDoc, htMapEditCopy);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
}
|
||||
REF_PTR_RELEASE(htMapEditCopy);
|
||||
}
|
||||
|
||||
967
GeneralsMD/Code/Tools/WorldBuilder/src/GlobalLightOptions.cpp
Normal file
967
GeneralsMD/Code/Tools/WorldBuilder/src/GlobalLightOptions.cpp
Normal file
@@ -0,0 +1,967 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// GlobalLightOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "GlobalLightOptions.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "common/GlobalData.h"
|
||||
#include "WbView3D.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// GlobalLightOptions dialog trivial construstor - Create does the real work.
|
||||
|
||||
|
||||
GlobalLightOptions::GlobalLightOptions(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(GlobalLightOptions::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(GlobalLightOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
/// Windows default stuff.
|
||||
void GlobalLightOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(GlobalLightOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
static void calcNewLight(Int lr, Int fb, Vector3 *newLight)
|
||||
{
|
||||
newLight->Set(0,0,-1);
|
||||
Real yAngle = PI*(lr-90)/180;
|
||||
Real xAngle = PI*(fb-90)/180;
|
||||
Real zAngle = xAngle * WWMath::Sin(yAngle);
|
||||
xAngle *= WWMath::Cos(yAngle);
|
||||
newLight->Rotate_Y(yAngle);
|
||||
newLight->Rotate_X(xAngle);
|
||||
newLight->Rotate_Z(zAngle);
|
||||
}
|
||||
|
||||
void GlobalLightOptions::updateEditFields(void)
|
||||
{
|
||||
m_updating = true;
|
||||
CString str;
|
||||
CWnd *pEdit;
|
||||
|
||||
str.Format("%d",m_angleAzimuth[K_SUN]);
|
||||
pEdit = GetDlgItem(IDC_FB_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
str.Format("%d",m_angleElevation[K_SUN]);
|
||||
pEdit = GetDlgItem(IDC_LR_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
|
||||
str.Format("%d",m_angleAzimuth[K_ACCENT1]);
|
||||
pEdit = GetDlgItem(IDC_FB_EDIT1);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
str.Format("%d",m_angleElevation[K_ACCENT1]);
|
||||
pEdit = GetDlgItem(IDC_LR_EDIT1);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
|
||||
str.Format("%d",m_angleAzimuth[K_ACCENT2]);
|
||||
pEdit = GetDlgItem(IDC_FB_EDIT2);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
str.Format("%d",m_angleElevation[K_ACCENT2]);
|
||||
pEdit = GetDlgItem(IDC_LR_EDIT2);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
void GlobalLightOptions::showLightFeedback(Int lightIndex)
|
||||
{
|
||||
Vector3 light(0,0,0);
|
||||
light.X = sin(PI/2.0f+m_angleElevation[lightIndex]/180.0f*PI)*cos(m_angleAzimuth[lightIndex]/180.0f*PI);// -WWMath::Sin(PI*(m_angleLR[lightIndex]-90)/180);
|
||||
light.Y = sin(PI/2.0f+m_angleElevation[lightIndex]/180.0f*PI)*sin(m_angleAzimuth[lightIndex]/180.0f*PI);//-WWMath::Sin(PI*(m_angleFB[lightIndex]-90)/180);
|
||||
light.Z = cos (PI/2.0f+m_angleElevation[lightIndex]/180.0f*PI);
|
||||
|
||||
WbView3d * pView = CWorldBuilderDoc::GetActive3DView();
|
||||
if (pView) {
|
||||
Coord3D lightRay;
|
||||
lightRay.x=light.X;lightRay.y=light.Y;lightRay.z=light.Z;
|
||||
pView->doLightFeedback(true,lightRay,lightIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalLightOptions::applyAngle(Int lightIndex)
|
||||
{
|
||||
Vector3 light(0,0,0);
|
||||
light.X = sin(PI/2.0f+m_angleElevation[lightIndex]/180.0f*PI)*cos(m_angleAzimuth[lightIndex]/180.0f*PI);// -WWMath::Sin(PI*(m_angleLR[lightIndex]-90)/180);
|
||||
light.Y = sin(PI/2.0f+m_angleElevation[lightIndex]/180.0f*PI)*sin(m_angleAzimuth[lightIndex]/180.0f*PI);//-WWMath::Sin(PI*(m_angleFB[lightIndex]-90)/180);
|
||||
light.Z = cos (PI/2.0f+m_angleElevation[lightIndex]/180.0f*PI);
|
||||
|
||||
CString str;
|
||||
str.Format("XYZ: %.2f, %.2f, %.2f", light.X, light.Y, light.Z);
|
||||
CWnd *pWnd = this->GetDlgItem(IDC_XYZ_STATIC);
|
||||
if (pWnd) {
|
||||
pWnd->SetWindowText(str);
|
||||
}
|
||||
GlobalData::TerrainLighting tl;
|
||||
if (m_lighting == K_OBJECTS || m_lighting == K_BOTH) {
|
||||
tl = TheGlobalData->m_terrainObjectsLighting[TheGlobalData->m_timeOfDay][lightIndex];
|
||||
} else {
|
||||
tl = TheGlobalData->m_terrainLighting[TheGlobalData->m_timeOfDay][lightIndex];
|
||||
}
|
||||
tl.lightPos.x = light.X;
|
||||
tl.lightPos.y = light.Y;
|
||||
tl.lightPos.z = light.Z;
|
||||
|
||||
WbView3d * pView = CWorldBuilderDoc::GetActive3DView();
|
||||
if (pView) {
|
||||
pView->setLighting(&tl, m_lighting, lightIndex);
|
||||
Coord3D lightRay;
|
||||
lightRay.x=light.X;lightRay.y=light.Y;lightRay.z=light.Z;
|
||||
pView->doLightFeedback(true,lightRay,lightIndex);
|
||||
}
|
||||
}
|
||||
|
||||
static void SpitLights()
|
||||
{
|
||||
#ifdef DEBUG_LOGGING
|
||||
CString lightstrings[100];
|
||||
DEBUG_LOG(("GlobalLighting\n\n"));
|
||||
Int redA, greenA, blueA;
|
||||
Int redD, greenD, blueD;
|
||||
Real x, y, z;
|
||||
CString times[4];
|
||||
CString lights[3];
|
||||
times[0] = "Morning";
|
||||
times[1] = "Afternoon";
|
||||
times[2] = "Evening";
|
||||
times[3] = "Night";
|
||||
|
||||
lights[0] = "";
|
||||
lights[1] = "2";
|
||||
lights[2] = "3";
|
||||
|
||||
for (Int time=0; time<4; time++) {
|
||||
for (Int light=0; light<3; light++) {
|
||||
redA = TheGlobalData->m_terrainLighting[time+TIME_OF_DAY_FIRST][light].ambient.red*255;
|
||||
greenA = TheGlobalData->m_terrainLighting[time+TIME_OF_DAY_FIRST][light].ambient.green*255;
|
||||
blueA = TheGlobalData->m_terrainLighting[time+TIME_OF_DAY_FIRST][light].ambient.blue*255;
|
||||
|
||||
redD = TheGlobalData->m_terrainLighting[time+TIME_OF_DAY_FIRST][light].diffuse.red*255;
|
||||
greenD = TheGlobalData->m_terrainLighting[time+TIME_OF_DAY_FIRST][light].diffuse.green*255;
|
||||
blueD = TheGlobalData->m_terrainLighting[time+TIME_OF_DAY_FIRST][light].diffuse.blue*255;
|
||||
|
||||
x = TheGlobalData->m_terrainLighting[time+TIME_OF_DAY_FIRST][light].lightPos.x;
|
||||
y = TheGlobalData->m_terrainLighting[time+TIME_OF_DAY_FIRST][light].lightPos.y;
|
||||
z = TheGlobalData->m_terrainLighting[time+TIME_OF_DAY_FIRST][light].lightPos.z;
|
||||
|
||||
DEBUG_LOG(("TerrainLighting%sAmbient%s = R:%d G:%d B:%d\n", times[time], lights[light], redA, greenA, blueA));
|
||||
DEBUG_LOG(("TerrainLighting%sDiffuse%s = R:%d G:%d B:%d\n", times[time], lights[light], redD, greenD, blueD));
|
||||
DEBUG_LOG(("TerrainLighting%sLightPos%s = X:%0.2f Y:%0.2f Z:%0.2f\n", times[time], lights[light], x, y, z));
|
||||
|
||||
redA = TheGlobalData->m_terrainObjectsLighting[time+TIME_OF_DAY_FIRST][light].ambient.red*255;
|
||||
greenA = TheGlobalData->m_terrainObjectsLighting[time+TIME_OF_DAY_FIRST][light].ambient.green*255;
|
||||
blueA = TheGlobalData->m_terrainObjectsLighting[time+TIME_OF_DAY_FIRST][light].ambient.blue*255;
|
||||
|
||||
redD = TheGlobalData->m_terrainObjectsLighting[time+TIME_OF_DAY_FIRST][light].diffuse.red*255;
|
||||
greenD = TheGlobalData->m_terrainObjectsLighting[time+TIME_OF_DAY_FIRST][light].diffuse.green*255;
|
||||
blueD = TheGlobalData->m_terrainObjectsLighting[time+TIME_OF_DAY_FIRST][light].diffuse.blue*255;
|
||||
|
||||
x = TheGlobalData->m_terrainObjectsLighting[time+TIME_OF_DAY_FIRST][light].lightPos.x;
|
||||
y = TheGlobalData->m_terrainObjectsLighting[time+TIME_OF_DAY_FIRST][light].lightPos.y;
|
||||
z = TheGlobalData->m_terrainObjectsLighting[time+TIME_OF_DAY_FIRST][light].lightPos.z;
|
||||
|
||||
DEBUG_LOG(("TerrainObjectsLighting%sAmbient%s = R:%d G:%d B:%d\n", times[time], lights[light], redA, greenA, blueA));
|
||||
DEBUG_LOG(("TerrainObjectsLighting%sDiffuse%s = R:%d G:%d B:%d\n", times[time], lights[light], redD, greenD, blueD));
|
||||
DEBUG_LOG(("TerrainObjectsLighting%sLightPos%s = X:%0.2f Y:%0.2f Z:%0.2f\n", times[time], lights[light], x, y, z));
|
||||
|
||||
DEBUG_LOG(("\n"));
|
||||
}
|
||||
DEBUG_LOG(("\n"));
|
||||
}
|
||||
|
||||
DEBUG_LOG(("GlobalLighting Code\n\n"));
|
||||
for (time=0; time<4; time++) {
|
||||
for (Int light=0; light<3; light++) {
|
||||
Int theTime = time+TIME_OF_DAY_FIRST;
|
||||
GlobalData::TerrainLighting tl = TheGlobalData->m_terrainLighting[theTime][light];
|
||||
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainLighting[%d][%d].ambient.red = %0.4ff;\n", theTime, light, tl.ambient.red));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainLighting[%d][%d].ambient.green = %0.4ff;\n", theTime, light, tl.ambient.green));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainLighting[%d][%d].ambient.blue = %0.4ff;\n", theTime, light, tl.ambient.blue));
|
||||
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainLighting[%d][%d].diffuse.red = %0.4ff;\n", theTime, light, tl.diffuse.red));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainLighting[%d][%d].diffuse.green = %0.4ff;\n", theTime, light, tl.diffuse.green));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainLighting[%d][%d].diffuse.blue = %0.4ff;\n", theTime, light, tl.diffuse.blue));
|
||||
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainLighting[%d][%d].lightPos.x = %0.4ff;\n", theTime, light, tl.lightPos.x));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainLighting[%d][%d].lightPos.y = %0.4ff;\n", theTime, light, tl.lightPos.y));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainLighting[%d][%d].lightPos.z = %0.4ff;\n", theTime, light, tl.lightPos.z));
|
||||
|
||||
tl = TheGlobalData->m_terrainObjectsLighting[theTime][light];
|
||||
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainObjectsLighting[%d][%d].ambient.red = %0.4ff;\n", theTime, light, tl.ambient.red));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainObjectsLighting[%d][%d].ambient.green = %0.4ff;\n", theTime, light, tl.ambient.green));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainObjectsLighting[%d][%d].ambient.blue = %0.4ff;\n", theTime, light, tl.ambient.blue));
|
||||
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainObjectsLighting[%d][%d].diffuse.red = %0.4ff;\n", theTime, light, tl.diffuse.red));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainObjectsLighting[%d][%d].diffuse.green = %0.4ff;\n", theTime, light, tl.diffuse.green));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainObjectsLighting[%d][%d].diffuse.blue = %0.4ff;\n", theTime, light, tl.diffuse.blue));
|
||||
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainObjectsLighting[%d][%d].lightPos.x = %0.4ff;\n", theTime, light, tl.lightPos.x));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainObjectsLighting[%d][%d].lightPos.y = %0.4ff;\n", theTime, light, tl.lightPos.y));
|
||||
DEBUG_LOG(("TheGlobalData->m_terrainObjectsLighting[%d][%d].lightPos.z = %0.4ff;\n", theTime, light, tl.lightPos.z));
|
||||
|
||||
DEBUG_LOG(("\n"));
|
||||
}
|
||||
DEBUG_LOG(("\n"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void GlobalLightOptions::OnResetLights()
|
||||
{
|
||||
TheWritableGlobalData->m_terrainLighting[1][0].ambient.red = 0.50f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][0].ambient.green = 0.39f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][0].ambient.blue = 0.30f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][0].diffuse.red = 0.90f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][0].diffuse.green = 0.71f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][0].diffuse.blue = 0.60f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][0].lightPos.x = -0.96f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][0].lightPos.y = 0.05f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][0].lightPos.z = -0.29f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][0].ambient.red = 0.50f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][0].ambient.green = 0.40f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][0].ambient.blue = 0.30f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][0].diffuse.red = 0.90f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][0].diffuse.green = 0.70f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][0].diffuse.blue = 0.60f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][0].lightPos.x = -0.96f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][0].lightPos.y = 0.05f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][0].lightPos.z = -0.29f;
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[1][1].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][1].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][1].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][1].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][1].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][1].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][1].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][1].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][1].lightPos.z = -1.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][1].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][1].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][1].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][1].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][1].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][1].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][1].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][1].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][1].lightPos.z = -1.00f;
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[1][2].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][2].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][2].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][2].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][2].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][2].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][2].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][2].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[1][2].lightPos.z = -1.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][2].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][2].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][2].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][2].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][2].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][2].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][2].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][2].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[1][2].lightPos.z = -1.00f;
|
||||
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[2][0].ambient.red = 0.2196f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][0].ambient.green = 0.2039f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][0].ambient.blue = 0.1725f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][0].diffuse.red = 1.0000f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][0].diffuse.green = 1.0000f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][0].diffuse.blue = 1.0000f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][0].lightPos.x = -0.8100f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][0].lightPos.y = 0.3800f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][0].lightPos.z = -0.4500f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][0].ambient.red = 0.2196f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][0].ambient.green = 0.2039f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][0].ambient.blue = 0.1725f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][0].diffuse.red = 1.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][0].diffuse.green = 1.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][0].diffuse.blue = 1.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][0].lightPos.x = -0.8100f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][0].lightPos.y = 0.3800f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][0].lightPos.z = -0.4500f;
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[2][1].ambient.red = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][1].ambient.green = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][1].ambient.blue = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][1].diffuse.red = 0.2353f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][1].diffuse.green = 0.2353f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][1].diffuse.blue = 0.4706f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][1].lightPos.x = 0.7900f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][1].lightPos.y = 0.6200f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][1].lightPos.z = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][1].ambient.red = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][1].ambient.green = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][1].ambient.blue = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][1].diffuse.red = 0.2353f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][1].diffuse.green = 0.2353f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][1].diffuse.blue = 0.3137f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][1].lightPos.x = 0.7900f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][1].lightPos.y = 0.6200f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][1].lightPos.z = 0.0000f;
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[2][2].ambient.red = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][2].ambient.green = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][2].ambient.blue = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][2].diffuse.red = 0.1176f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][2].diffuse.green = 0.1176f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][2].diffuse.blue = 0.0784f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][2].lightPos.x = 0.8100f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][2].lightPos.y = -0.4800f;
|
||||
TheWritableGlobalData->m_terrainLighting[2][2].lightPos.z = -0.3400f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][2].ambient.red = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][2].ambient.green = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][2].ambient.blue = 0.0000f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][2].diffuse.red = 0.1176f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][2].diffuse.green = 0.1176f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][2].diffuse.blue = 0.0784f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][2].lightPos.x = 0.8100f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][2].lightPos.y = -0.4800f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[2][2].lightPos.z = -0.3400f;
|
||||
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[3][0].ambient.red = 0.25f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][0].ambient.green = 0.23f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][0].ambient.blue = 0.20f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][0].diffuse.red = 0.60f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][0].diffuse.green = 0.50f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][0].diffuse.blue = 0.40f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][0].lightPos.x = -1.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][0].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][0].lightPos.z = -0.20f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][0].ambient.red = 0.25f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][0].ambient.green = 0.23f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][0].ambient.blue = 0.20f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][0].diffuse.red = 0.60f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][0].diffuse.green = 0.50f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][0].diffuse.blue = 0.40f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][0].lightPos.x = -1.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][0].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][0].lightPos.z = -0.20f;
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[3][1].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][1].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][1].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][1].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][1].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][1].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][1].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][1].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][1].lightPos.z = -1.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][1].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][1].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][1].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][1].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][1].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][1].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][1].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][1].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][1].lightPos.z = -1.00f;
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[3][2].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][2].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][2].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][2].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][2].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][2].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][2].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][2].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[3][2].lightPos.z = -1.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][2].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][2].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][2].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][2].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][2].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][2].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][2].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][2].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[3][2].lightPos.z = -1.00f;
|
||||
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[4][0].ambient.red = 0.10f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][0].ambient.green = 0.10f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][0].ambient.blue = 0.15f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][0].diffuse.red = 0.20f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][0].diffuse.green = 0.20f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][0].diffuse.blue = 0.30f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][0].lightPos.x = -1.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][0].lightPos.y = 1.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][0].lightPos.z = -2.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][0].ambient.red = 0.10f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][0].ambient.green = 0.10f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][0].ambient.blue = 0.15f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][0].diffuse.red = 0.20f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][0].diffuse.green = 0.20f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][0].diffuse.blue = 0.30f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][0].lightPos.x = -1.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][0].lightPos.y = 1.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][0].lightPos.z = -2.00f;
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[4][1].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][1].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][1].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][1].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][1].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][1].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][1].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][1].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][1].lightPos.z = -1.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][1].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][1].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][1].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][1].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][1].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][1].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][1].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][1].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][1].lightPos.z = -1.00f;
|
||||
|
||||
TheWritableGlobalData->m_terrainLighting[4][2].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][2].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][2].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][2].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][2].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][2].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][2].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][2].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainLighting[4][2].lightPos.z = -1.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][2].ambient.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][2].ambient.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][2].ambient.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][2].diffuse.red = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][2].diffuse.green = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][2].diffuse.blue = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][2].lightPos.x = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][2].lightPos.y = 0.00f;
|
||||
TheWritableGlobalData->m_terrainObjectsLighting[4][2].lightPos.z = -1.00f;
|
||||
|
||||
WbView3d * pView = CWorldBuilderDoc::GetActive3DView();
|
||||
if (pView) {
|
||||
pView->setLighting(&TheGlobalData->m_terrainLighting[TheGlobalData->m_timeOfDay][K_SUN], K_TERRAIN, K_SUN);
|
||||
pView->setLighting(&TheGlobalData->m_terrainLighting[TheGlobalData->m_timeOfDay][K_ACCENT1], K_TERRAIN, K_ACCENT1);
|
||||
pView->setLighting(&TheGlobalData->m_terrainLighting[TheGlobalData->m_timeOfDay][K_ACCENT2], K_TERRAIN, K_ACCENT2);
|
||||
|
||||
pView->setLighting(&TheGlobalData->m_terrainObjectsLighting[TheGlobalData->m_timeOfDay][K_SUN], K_OBJECTS, K_SUN);
|
||||
pView->setLighting(&TheGlobalData->m_terrainObjectsLighting[TheGlobalData->m_timeOfDay][K_ACCENT1], K_OBJECTS, K_ACCENT1);
|
||||
pView->setLighting(&TheGlobalData->m_terrainObjectsLighting[TheGlobalData->m_timeOfDay][K_ACCENT2], K_OBJECTS, K_ACCENT2);
|
||||
}
|
||||
stuffValuesIntoFields(K_SUN);
|
||||
stuffValuesIntoFields(K_ACCENT1);
|
||||
stuffValuesIntoFields(K_ACCENT2);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// GlobalLightOptions message handlers
|
||||
|
||||
/// Dialog UI initialization.
|
||||
/** Creates the slider controls, and sets the initial values for
|
||||
width and feather in the ui controls. */
|
||||
BOOL GlobalLightOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
kUIRedIDs[0] = IDC_RD_EDIT;
|
||||
kUIRedIDs[1] = IDC_RD_EDIT1;
|
||||
kUIRedIDs[2] = IDC_RD_EDIT2;
|
||||
|
||||
kUIGreenIDs[0] = IDC_GD_EDIT;
|
||||
kUIGreenIDs[1] = IDC_GD_EDIT1;
|
||||
kUIGreenIDs[2] = IDC_GD_EDIT2;
|
||||
|
||||
kUIBlueIDs[0] = IDC_BD_EDIT;
|
||||
kUIBlueIDs[1] = IDC_BD_EDIT1;
|
||||
kUIBlueIDs[2] = IDC_BD_EDIT2;
|
||||
|
||||
m_frontBackPopup.SetupPopSliderButton(this, IDC_FB_POPUP, this);
|
||||
m_leftRightPopup.SetupPopSliderButton(this, IDC_LR_POPUP, this);
|
||||
|
||||
m_frontBackPopupAccent1.SetupPopSliderButton(this, IDC_FB_POPUP1, this);
|
||||
m_leftRightPopupAccent1.SetupPopSliderButton(this, IDC_LR_POPUP1, this);
|
||||
|
||||
m_frontBackPopupAccent2.SetupPopSliderButton(this, IDC_FB_POPUP2, this);
|
||||
m_leftRightPopupAccent2.SetupPopSliderButton(this, IDC_LR_POPUP2, this);
|
||||
|
||||
CButton *pButton = (CButton *)GetDlgItem(IDC_RADIO_EVERYTHING);
|
||||
pButton->SetCheck(1);
|
||||
m_lighting = K_BOTH;
|
||||
|
||||
stuffValuesIntoFields(K_SUN);
|
||||
stuffValuesIntoFields(K_ACCENT1);
|
||||
stuffValuesIntoFields(K_ACCENT2);
|
||||
|
||||
CRect rect;
|
||||
CWnd *item = GetDlgItem(IDC_PSEd_Color1);
|
||||
if (item) {
|
||||
item->GetWindowRect(&rect);
|
||||
ScreenToClient(&rect);
|
||||
DWORD style = item->GetStyle();
|
||||
m_colorButton.Create("", style, rect, this, IDC_PSEd_Color1);
|
||||
item->DestroyWindow();
|
||||
}
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Displays the current values in the fields. */
|
||||
void GlobalLightOptions::stuffValuesIntoFields(Int lightIndex)
|
||||
{
|
||||
const GlobalData::TerrainLighting *tl;
|
||||
if (m_lighting == K_OBJECTS || m_lighting == K_BOTH) {
|
||||
tl = &TheGlobalData->m_terrainObjectsLighting[TheGlobalData->m_timeOfDay][lightIndex];
|
||||
} else {
|
||||
tl = &TheGlobalData->m_terrainLighting[TheGlobalData->m_timeOfDay][lightIndex];
|
||||
}
|
||||
Real azimuth = 90;
|
||||
Real elevation = 90;
|
||||
|
||||
Vector3 light(tl->lightPos.x, tl->lightPos.y, tl->lightPos.z);
|
||||
light.Normalize();
|
||||
|
||||
|
||||
Real angleAzimuth = atan2(light.Y,light.X);//WWMath::Asin(light.Y);
|
||||
azimuth = angleAzimuth*180.0f/PI;//90-(angleFB/PI)*180;
|
||||
if (azimuth < 0) {
|
||||
azimuth += 360;
|
||||
}
|
||||
Real angleElevation = acos(light.Z);//WWMath::Asin(light.X);
|
||||
elevation = (angleElevation-PI/2.0f)*180.0f/PI;//90-(angleLR/PI)*180;
|
||||
|
||||
m_angleElevation[lightIndex] = elevation;
|
||||
m_angleAzimuth[lightIndex] = azimuth;
|
||||
|
||||
updateEditFields();
|
||||
|
||||
m_updating = true;
|
||||
CString str;
|
||||
str.Format("XYZ: %.2f, %.2f, %.2f", light.X, light.Y, light.Z);
|
||||
CWnd *pWnd = this->GetDlgItem(IDC_XYZ_STATIC);
|
||||
if (pWnd && lightIndex==0) {
|
||||
pWnd->SetWindowText(str);
|
||||
}
|
||||
|
||||
switch (lightIndex)
|
||||
{
|
||||
case K_SUN:
|
||||
default:
|
||||
PutInt(IDC_RA_EDIT, PercentToComponent(tl->ambient.red));
|
||||
PutInt(IDC_GA_EDIT, PercentToComponent(tl->ambient.green));
|
||||
PutInt(IDC_BA_EDIT, PercentToComponent(tl->ambient.blue));
|
||||
|
||||
PutInt(IDC_RD_EDIT, PercentToComponent(tl->diffuse.red));
|
||||
PutInt(IDC_GD_EDIT, PercentToComponent(tl->diffuse.green));
|
||||
PutInt(IDC_BD_EDIT, PercentToComponent(tl->diffuse.blue));
|
||||
m_colorButton.setColor(tl->ambient);
|
||||
break;
|
||||
|
||||
case K_ACCENT1:
|
||||
PutInt(IDC_RD_EDIT1, PercentToComponent(tl->diffuse.red));
|
||||
PutInt(IDC_GD_EDIT1, PercentToComponent(tl->diffuse.green));
|
||||
PutInt(IDC_BD_EDIT1, PercentToComponent(tl->diffuse.blue));
|
||||
break;
|
||||
|
||||
case K_ACCENT2:
|
||||
PutInt(IDC_RD_EDIT2, PercentToComponent(tl->diffuse.red));
|
||||
PutInt(IDC_GD_EDIT2, PercentToComponent(tl->diffuse.green));
|
||||
PutInt(IDC_BD_EDIT2, PercentToComponent(tl->diffuse.blue));
|
||||
break;
|
||||
}
|
||||
|
||||
m_updating = false;
|
||||
pWnd = GetDlgItem(IDC_TIME_OF_DAY_CAPTION);
|
||||
if (pWnd) {
|
||||
switch (TheGlobalData->m_timeOfDay) {
|
||||
default:
|
||||
case TIME_OF_DAY_MORNING: pWnd->SetWindowText("Time of day: Morning."); break;
|
||||
case TIME_OF_DAY_AFTERNOON: pWnd->SetWindowText("Time of day: Afternoon."); break;
|
||||
case TIME_OF_DAY_EVENING: pWnd->SetWindowText("Time of day: Evening."); break;
|
||||
case TIME_OF_DAY_NIGHT: pWnd->SetWindowText("Time of day: Night."); break;
|
||||
}
|
||||
}
|
||||
showLightFeedback(lightIndex);
|
||||
}
|
||||
|
||||
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
|
||||
BOOL GlobalLightOptions::GetInt(Int ctrlID, Int *rVal)
|
||||
{
|
||||
CWnd *pEdit = GetDlgItem(ctrlID);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int val;
|
||||
if (1==sscanf(buffer, "%d", &val)) {
|
||||
*rVal = val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GlobalLightOptions::PutInt(Int ctrlID, Int val)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit = GetDlgItem(ctrlID);
|
||||
if (pEdit) {
|
||||
str.Format("%d", val);
|
||||
pEdit->SetWindowText(str);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalLightOptions::OnChangeFrontBackEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
GetInt(IDC_FB_EDIT, &m_angleAzimuth[K_SUN]);
|
||||
GetInt(IDC_FB_EDIT1, &m_angleAzimuth[K_ACCENT1]);
|
||||
GetInt(IDC_FB_EDIT2, &m_angleAzimuth[K_ACCENT2]);
|
||||
m_updating = true;
|
||||
applyAngle(K_SUN);
|
||||
applyAngle(K_ACCENT1);
|
||||
applyAngle(K_ACCENT2);
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the angles. */
|
||||
void GlobalLightOptions::OnChangeLeftRightEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
GetInt(IDC_LR_EDIT, &m_angleElevation[K_SUN]);
|
||||
GetInt(IDC_LR_EDIT1, &m_angleElevation[K_ACCENT1]);
|
||||
GetInt(IDC_LR_EDIT2, &m_angleElevation[K_ACCENT2]);
|
||||
m_updating = true;
|
||||
applyAngle(K_SUN);
|
||||
applyAngle(K_ACCENT1);
|
||||
applyAngle(K_ACCENT2);
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void GlobalLightOptions::applyColor(Int lightIndex)
|
||||
{
|
||||
Int clr;
|
||||
GlobalData::TerrainLighting tl;
|
||||
if (m_lighting == K_OBJECTS || m_lighting == K_BOTH) {
|
||||
tl = TheGlobalData->m_terrainObjectsLighting[TheGlobalData->m_timeOfDay][lightIndex];
|
||||
} else {
|
||||
tl = TheGlobalData->m_terrainLighting[TheGlobalData->m_timeOfDay][lightIndex];
|
||||
}
|
||||
if (lightIndex == K_SUN) {
|
||||
tl.ambient.red = TheGlobalData->m_terrainAmbient[K_SUN].red;
|
||||
tl.ambient.green = TheGlobalData->m_terrainAmbient[K_SUN].green;
|
||||
tl.ambient.blue = TheGlobalData->m_terrainAmbient[K_SUN].blue;
|
||||
|
||||
if (GetInt(IDC_RA_EDIT, &clr))
|
||||
tl.ambient.red = ComponentToPercent(clr);
|
||||
if (GetInt(IDC_GA_EDIT, &clr))
|
||||
tl.ambient.green = ComponentToPercent(clr);
|
||||
if (GetInt(IDC_BA_EDIT, &clr))
|
||||
tl.ambient.blue = ComponentToPercent(clr);
|
||||
} else {
|
||||
tl.ambient.red = 0;
|
||||
tl.ambient.green = 0;
|
||||
tl.ambient.blue = 0;
|
||||
}
|
||||
|
||||
tl.diffuse.red = TheGlobalData->m_terrainDiffuse[lightIndex].red;
|
||||
tl.diffuse.green = TheGlobalData->m_terrainDiffuse[lightIndex].green;
|
||||
tl.diffuse.blue = TheGlobalData->m_terrainDiffuse[lightIndex].blue;
|
||||
|
||||
if (GetInt(kUIRedIDs[lightIndex], &clr))
|
||||
tl.diffuse.red = ComponentToPercent(clr);
|
||||
if (GetInt(kUIGreenIDs[lightIndex], &clr))
|
||||
tl.diffuse.green = ComponentToPercent(clr);
|
||||
if (GetInt(kUIBlueIDs[lightIndex], &clr))
|
||||
tl.diffuse.blue = ComponentToPercent(clr);
|
||||
WbView3d * pView = CWorldBuilderDoc::GetActive3DView();
|
||||
if (pView) {
|
||||
pView->setLighting(&tl, m_lighting, lightIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalLightOptions::OnChangeColorEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
applyColor(K_SUN);
|
||||
applyColor(K_ACCENT1);
|
||||
applyColor(K_ACCENT2);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void GlobalLightOptions::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_FB_POPUP:
|
||||
*pMin = 0;
|
||||
*pMax = 359;
|
||||
*pInitial = m_angleAzimuth[K_SUN];
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
case IDC_FB_POPUP1:
|
||||
*pMin = 0;
|
||||
*pMax = 359;
|
||||
*pInitial = m_angleAzimuth[K_ACCENT1];
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
case IDC_FB_POPUP2:
|
||||
*pMin = 0;
|
||||
*pMax = 359;
|
||||
*pInitial = m_angleAzimuth[K_ACCENT2];
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_LR_POPUP:
|
||||
*pMin = 0;
|
||||
*pMax = 90;
|
||||
*pInitial = m_angleElevation[K_SUN];
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
case IDC_LR_POPUP1:
|
||||
*pMin = 0;
|
||||
*pMax = 90;
|
||||
*pInitial = m_angleElevation[K_ACCENT1];
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
case IDC_LR_POPUP2:
|
||||
*pMin = 0;
|
||||
*pMax = 90;
|
||||
*pInitial = m_angleElevation[K_ACCENT2];
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void GlobalLightOptions::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_FB_POPUP:
|
||||
m_angleAzimuth[K_SUN] = theVal;
|
||||
PutInt(IDC_FB_EDIT, m_angleAzimuth[K_SUN]);
|
||||
applyAngle(0);
|
||||
break;
|
||||
|
||||
case IDC_FB_POPUP1:
|
||||
m_angleAzimuth[K_ACCENT1] = theVal;
|
||||
PutInt(IDC_FB_EDIT1, m_angleAzimuth[K_ACCENT1]);
|
||||
applyAngle(1);
|
||||
break;
|
||||
|
||||
case IDC_FB_POPUP2:
|
||||
m_angleAzimuth[K_ACCENT2] = theVal;
|
||||
PutInt(IDC_FB_EDIT2, m_angleAzimuth[K_ACCENT2]);
|
||||
applyAngle(2);
|
||||
break;
|
||||
|
||||
case IDC_LR_POPUP:
|
||||
m_angleElevation[K_SUN] = theVal;
|
||||
PutInt(IDC_LR_EDIT, m_angleElevation[K_SUN]);
|
||||
applyAngle(0);
|
||||
break;
|
||||
|
||||
case IDC_LR_POPUP1:
|
||||
m_angleElevation[K_ACCENT1] = theVal;
|
||||
PutInt(IDC_LR_EDIT1, m_angleElevation[K_ACCENT1]);
|
||||
applyAngle(1);
|
||||
break;
|
||||
|
||||
case IDC_LR_POPUP2:
|
||||
m_angleElevation[K_ACCENT2] = theVal;
|
||||
PutInt(IDC_LR_EDIT2, m_angleElevation[K_ACCENT2]);
|
||||
applyAngle(2);
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void GlobalLightOptions::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_FB_POPUP:
|
||||
break;
|
||||
case IDC_LR_POPUP:
|
||||
break;
|
||||
case IDC_FB_POPUP1:
|
||||
break;
|
||||
case IDC_LR_POPUP1:
|
||||
break;
|
||||
case IDC_FB_POPUP2:
|
||||
break;
|
||||
case IDC_LR_POPUP2:
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(GlobalLightOptions, CDialog)
|
||||
//{{AFX_MSG_MAP(GlobalLightOptions)
|
||||
ON_WM_MOVE()
|
||||
ON_WM_SHOWWINDOW()
|
||||
ON_WM_CLOSE()
|
||||
ON_EN_CHANGE(IDC_RA_EDIT, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_BA_EDIT, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_GA_EDIT, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_FB_EDIT, OnChangeFrontBackEdit)
|
||||
ON_EN_CHANGE(IDC_FB_EDIT1, OnChangeFrontBackEdit)
|
||||
ON_EN_CHANGE(IDC_FB_EDIT2, OnChangeFrontBackEdit)
|
||||
ON_EN_CHANGE(IDC_LR_EDIT, OnChangeLeftRightEdit)
|
||||
ON_EN_CHANGE(IDC_LR_EDIT1, OnChangeLeftRightEdit)
|
||||
ON_EN_CHANGE(IDC_LR_EDIT2, OnChangeLeftRightEdit)
|
||||
ON_EN_CHANGE(IDC_RD_EDIT, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_GD_EDIT, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_BD_EDIT, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_RD_EDIT1, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_GD_EDIT1, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_BD_EDIT1, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_RD_EDIT2, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_GD_EDIT2, OnChangeColorEdit)
|
||||
ON_EN_CHANGE(IDC_BD_EDIT2, OnChangeColorEdit)
|
||||
ON_BN_CLICKED(IDC_RADIO_EVERYTHING, OnRadioEverything)
|
||||
ON_BN_CLICKED(IDC_RADIO_OBJECTS, OnRadioObjects)
|
||||
ON_BN_CLICKED(IDC_RADIO_TERRAIN, OnRadioTerrain)
|
||||
ON_BN_CLICKED(IDC_PSEd_Color1, OnColorPress)
|
||||
ON_BN_CLICKED(IDC_GlobalLightingReset, OnResetLights)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
|
||||
void GlobalLightOptions::OnRadioEverything()
|
||||
{
|
||||
m_lighting = K_BOTH;
|
||||
stuffValuesIntoFields(K_SUN);
|
||||
stuffValuesIntoFields(K_ACCENT1);
|
||||
stuffValuesIntoFields(K_ACCENT2);
|
||||
}
|
||||
|
||||
void GlobalLightOptions::OnRadioObjects()
|
||||
{
|
||||
m_lighting = K_OBJECTS;
|
||||
stuffValuesIntoFields(K_SUN);
|
||||
stuffValuesIntoFields(K_ACCENT1);
|
||||
stuffValuesIntoFields(K_ACCENT2);
|
||||
}
|
||||
|
||||
void GlobalLightOptions::OnRadioTerrain()
|
||||
{
|
||||
m_lighting = K_TERRAIN;
|
||||
stuffValuesIntoFields(K_SUN);
|
||||
stuffValuesIntoFields(K_ACCENT1);
|
||||
stuffValuesIntoFields(K_ACCENT2);
|
||||
}
|
||||
|
||||
void GlobalLightOptions::OnColorPress()
|
||||
{
|
||||
CColorDialog dlg;
|
||||
if (dlg.DoModal() == IDOK) {
|
||||
m_colorButton.setColor(CButtonShowColor::BGRtoRGB(dlg.GetColor()));
|
||||
RGBColor color = m_colorButton.getColor();
|
||||
PutInt(IDC_RA_EDIT, PercentToComponent(color.red));
|
||||
PutInt(IDC_GA_EDIT, PercentToComponent(color.green));
|
||||
PutInt(IDC_BA_EDIT, PercentToComponent(color.blue));
|
||||
applyColor(K_SUN);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalLightOptions::OnClose()
|
||||
{
|
||||
ShowWindow(SW_HIDE);
|
||||
|
||||
WbView3d * pView = CWorldBuilderDoc::GetActive3DView();
|
||||
if (pView) {
|
||||
Coord3D lightRay;
|
||||
lightRay.x=0.0f;lightRay.y=0.0f;lightRay.z=-1.0f; //default light above terrain.
|
||||
pView->doLightFeedback(false,lightRay,0); //turn off the light direction indicator
|
||||
}
|
||||
};
|
||||
|
||||
void GlobalLightOptions::OnShowWindow(BOOL bShow, UINT nStatus)
|
||||
{
|
||||
CDialog::OnShowWindow(bShow, nStatus);
|
||||
|
||||
stuffValuesIntoFields(K_SUN);
|
||||
stuffValuesIntoFields(K_ACCENT1);
|
||||
stuffValuesIntoFields(K_ACCENT2);
|
||||
if (!bShow) {
|
||||
WbView3d * pView = CWorldBuilderDoc::GetActive3DView();
|
||||
if (pView) {
|
||||
Coord3D lightRay;
|
||||
lightRay.x=0.0f;lightRay.y=0.0f;lightRay.z=-1.0f; //default light above terrain.
|
||||
pView->doLightFeedback(false,lightRay,0); //turn off the light direction indicator
|
||||
pView->doLightFeedback(false,lightRay,1); //turn off the light direction indicator
|
||||
pView->doLightFeedback(false,lightRay,2); //turn off the light direction indicator
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
SpitLights();
|
||||
#endif
|
||||
};
|
||||
|
||||
void GlobalLightOptions::OnMove(int x, int y)
|
||||
{
|
||||
CDialog::OnMove(x, y);
|
||||
|
||||
if (this->IsWindowVisible() && !this->IsIconic()) {
|
||||
CRect frameRect;
|
||||
GetWindowRect(&frameRect);
|
||||
::AfxGetApp()->WriteProfileInt(GLOBALLIGHT_OPTIONS_PANEL_SECTION, "Top", frameRect.top);
|
||||
::AfxGetApp()->WriteProfileInt(GLOBALLIGHT_OPTIONS_PANEL_SECTION, "Left", frameRect.left);
|
||||
}
|
||||
|
||||
}
|
||||
492
GeneralsMD/Code/Tools/WorldBuilder/src/GroveOptions.cpp
Normal file
492
GeneralsMD/Code/Tools/WorldBuilder/src/GroveOptions.cpp
Normal file
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "Common/STLTypedefs.h"
|
||||
#include "GroveOptions.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingSort.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
|
||||
#define ARBITRARY_BUFF_SIZE 128
|
||||
|
||||
/*extern*/ GroveOptions *TheGroveOptions = NULL;
|
||||
|
||||
void GroveOptions::makeMain(void)
|
||||
{
|
||||
TheGroveOptions = this;
|
||||
}
|
||||
|
||||
GroveOptions::GroveOptions(CWnd* pParent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int GroveOptions::getNumTrees(void)
|
||||
{
|
||||
CWnd* pWnd = GetDlgItem(IDC_Grove_NumberTrees);
|
||||
if (!pWnd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char buff[ARBITRARY_BUFF_SIZE];
|
||||
pWnd->GetWindowText(buff, ARBITRARY_BUFF_SIZE - 1);
|
||||
return atoi(buff);
|
||||
}
|
||||
|
||||
|
||||
int GroveOptions::getNumType(int type)
|
||||
{
|
||||
static char buff[ARBITRARY_BUFF_SIZE];
|
||||
|
||||
if (type < 1 || type > 5) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
CWnd *pWnd;
|
||||
CComboBox* pBox;
|
||||
if (type == 1) {
|
||||
pWnd = GetDlgItem(IDC_Grove_Per1);
|
||||
pBox = (CComboBox*) GetDlgItem(IDC_Grove_Type1);
|
||||
} else if (type == 2) {
|
||||
pWnd = GetDlgItem(IDC_Grove_Per2);
|
||||
pBox = (CComboBox*) GetDlgItem(IDC_Grove_Type2);
|
||||
} else if (type == 3) {
|
||||
pWnd = GetDlgItem(IDC_Grove_Per3);
|
||||
pBox = (CComboBox*) GetDlgItem(IDC_Grove_Type3);
|
||||
} else if (type == 4) {
|
||||
pWnd = GetDlgItem(IDC_Grove_Per4);
|
||||
pBox = (CComboBox*) GetDlgItem(IDC_Grove_Type4);
|
||||
} else if (type == 5) {
|
||||
pWnd = GetDlgItem(IDC_Grove_Per5);
|
||||
pBox = (CComboBox*) GetDlgItem(IDC_Grove_Type5);
|
||||
}
|
||||
|
||||
if (pWnd && pBox) {
|
||||
if (pBox->GetCurSel() > 0) {
|
||||
pWnd->GetWindowText(buff, ARBITRARY_BUFF_SIZE - 1);
|
||||
return atoi(buff);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
AsciiString GroveOptions::getTypeName(int type)
|
||||
{
|
||||
if (type < 1 || type > 5) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CComboBox *pComboBox;
|
||||
if (type == 1) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type1);
|
||||
} else if (type == 2) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type2);
|
||||
} else if (type == 3) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type3);
|
||||
} else if (type == 4) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type4);
|
||||
} else if (type == 5) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type5);
|
||||
}
|
||||
|
||||
int curSel = pComboBox->GetCurSel();
|
||||
if (curSel < 0 || curSel > mVecDisplayNames.size()) {
|
||||
return "";
|
||||
}
|
||||
CString cstr;
|
||||
|
||||
pComboBox->GetLBText(curSel, cstr);
|
||||
|
||||
return cstr.GetBuffer(0);
|
||||
}
|
||||
|
||||
int GroveOptions::getTotalTreePerc(void)
|
||||
{
|
||||
static char buff[ARBITRARY_BUFF_SIZE];
|
||||
|
||||
CWnd* pWnd = GetDlgItem(IDC_Grove_PerTotal);
|
||||
if (!pWnd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pWnd) {
|
||||
pWnd->GetWindowText(buff, ARBITRARY_BUFF_SIZE - 1);
|
||||
return atoi(buff);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Bool GroveOptions::getCanPlaceInWater(void)
|
||||
{
|
||||
CButton* pButt;
|
||||
|
||||
pButt = (CButton*) GetDlgItem(IDC_Grove_AllowWaterPlacement);
|
||||
if (pButt) {
|
||||
return (pButt->GetState() != 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Bool GroveOptions::getCanPlaceOnCliffs(void)
|
||||
{
|
||||
CButton* pButt;
|
||||
|
||||
pButt = (CButton*) GetDlgItem(IDC_Grove_AllowCliffPlacement);
|
||||
if (pButt) {
|
||||
return (pButt->GetState() != 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL GroveOptions::OnInitDialog()
|
||||
{
|
||||
_buildTreeList();
|
||||
_setTreesToLists();
|
||||
_setDefaultRatios();
|
||||
_updateTreeWeights();
|
||||
_setDefaultNumTrees();
|
||||
_setDefaultPlacementAllowed();
|
||||
return true;
|
||||
}
|
||||
|
||||
GroveOptions::~GroveOptions()
|
||||
{
|
||||
TheGroveOptions = NULL;
|
||||
}
|
||||
|
||||
|
||||
void GroveOptions::_setTreesToLists(void)
|
||||
{
|
||||
CString str;
|
||||
for (VecPairNameDisplayNameIt it = mVecDisplayNames.begin(); it != mVecDisplayNames.end(); it++) {
|
||||
// TODO: If/when Models get Display strings, we need to replace the
|
||||
// current (str = ...) line with the commented one JKMCD
|
||||
str = it->first.str();
|
||||
//str = GetDisplayNameFromPair(it).str();
|
||||
|
||||
CComboBox* pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type1);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
}
|
||||
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type2);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
}
|
||||
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type3);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
}
|
||||
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type4);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
}
|
||||
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type5);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
}
|
||||
}
|
||||
|
||||
int selValue;
|
||||
str = "";
|
||||
CComboBox* pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type1);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
selValue = AfxGetApp()->GetProfileInt("GroveOptions", "TreeType1", 0);
|
||||
pComboBox->SetCurSel(selValue % mVecDisplayNames.size());
|
||||
}
|
||||
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type2);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
selValue = AfxGetApp()->GetProfileInt("GroveOptions", "TreeType2", 0);
|
||||
pComboBox->SetCurSel(selValue % mVecDisplayNames.size());
|
||||
}
|
||||
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type3);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
selValue = AfxGetApp()->GetProfileInt("GroveOptions", "TreeType3", 0);
|
||||
pComboBox->SetCurSel(selValue % mVecDisplayNames.size());
|
||||
}
|
||||
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type4);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
selValue = AfxGetApp()->GetProfileInt("GroveOptions", "TreeType4", 0);
|
||||
pComboBox->SetCurSel(selValue % mVecDisplayNames.size());
|
||||
}
|
||||
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type5);
|
||||
if (pComboBox) {
|
||||
pComboBox->AddString(str);
|
||||
selValue = AfxGetApp()->GetProfileInt("GroveOptions", "TreeType5", 0);
|
||||
pComboBox->SetCurSel(selValue % mVecDisplayNames.size());
|
||||
}
|
||||
}
|
||||
|
||||
void GroveOptions::_buildTreeList(void)
|
||||
{
|
||||
const ThingTemplate* pTemplate;
|
||||
for (pTemplate = TheThingFactory->firstTemplate(); pTemplate; pTemplate = pTemplate->friend_getNextTemplate()) {
|
||||
if (pTemplate->getEditorSorting() == ES_SHRUBBERY) {
|
||||
PairNameDisplayName currentName;
|
||||
currentName.first = pTemplate->getName();
|
||||
currentName.second = pTemplate->getDisplayName();
|
||||
mVecDisplayNames.push_back(currentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroveOptions::_setDefaultRatios(void)
|
||||
{
|
||||
static char buff[ARBITRARY_BUFF_SIZE];
|
||||
int defaultRatio;
|
||||
|
||||
CWnd* pWnd = GetDlgItem(IDC_Grove_Per1);
|
||||
if (pWnd) {
|
||||
defaultRatio = AfxGetApp()->GetProfileInt("GroveOptions", "DefaultRatio1", 0);
|
||||
sprintf(buff, "%d", defaultRatio);
|
||||
pWnd->SetWindowText(buff);
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_Grove_Per2);
|
||||
if (pWnd) {
|
||||
defaultRatio = AfxGetApp()->GetProfileInt("GroveOptions", "DefaultRatio2", 0);
|
||||
sprintf(buff, "%d", defaultRatio);
|
||||
pWnd->SetWindowText(buff);
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_Grove_Per3);
|
||||
if (pWnd) {
|
||||
defaultRatio = AfxGetApp()->GetProfileInt("GroveOptions", "DefaultRatio3", 0);
|
||||
sprintf(buff, "%d", defaultRatio);
|
||||
pWnd->SetWindowText(buff);
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_Grove_Per4);
|
||||
if (pWnd) {
|
||||
defaultRatio = AfxGetApp()->GetProfileInt("GroveOptions", "DefaultRatio4", 0);
|
||||
sprintf(buff, "%d", defaultRatio);
|
||||
pWnd->SetWindowText(buff);
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_Grove_Per5);
|
||||
if (pWnd) {
|
||||
defaultRatio = AfxGetApp()->GetProfileInt("GroveOptions", "DefaultRatio5", 0);
|
||||
sprintf(buff, "%d", defaultRatio);
|
||||
pWnd->SetWindowText(buff);
|
||||
}
|
||||
}
|
||||
|
||||
void GroveOptions::_setDefaultNumTrees(void)
|
||||
{
|
||||
CWnd* pWnd = GetDlgItem(IDC_Grove_NumberTrees);
|
||||
if (!pWnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
int defaultNumTrees = AfxGetApp()->GetProfileInt("GroveOptions", "NumberofTrees", 10);
|
||||
static char buff[ARBITRARY_BUFF_SIZE];
|
||||
sprintf(buff, "%d", defaultNumTrees);
|
||||
|
||||
pWnd->SetWindowText(buff);
|
||||
}
|
||||
|
||||
void GroveOptions::_setDefaultPlacementAllowed(void)
|
||||
{
|
||||
CButton* pButt;
|
||||
int state;
|
||||
|
||||
pButt = (CButton*) GetDlgItem(IDC_Grove_AllowCliffPlacement);
|
||||
if (pButt) {
|
||||
state = AfxGetApp()->GetProfileInt("GroveOptions", "AllowCliffPlace", 1);
|
||||
pButt->SetCheck(state);
|
||||
}
|
||||
|
||||
pButt = (CButton*) GetDlgItem(IDC_Grove_AllowWaterPlacement);
|
||||
if (pButt) {
|
||||
state = AfxGetApp()->GetProfileInt("GroveOptions", "AllowWaterPlace", 1);
|
||||
pButt->SetCheck(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GroveOptions::_updateTreeWeights(void)
|
||||
{
|
||||
static char buff[ARBITRARY_BUFF_SIZE];
|
||||
int val = 0;
|
||||
int ratio;
|
||||
CWnd* pWnd = GetDlgItem(IDC_Grove_Per1);
|
||||
if (pWnd) {
|
||||
pWnd->GetWindowText(buff, ARBITRARY_BUFF_SIZE - 1);
|
||||
ratio = atoi(buff);
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "DefaultRatio1", ratio);
|
||||
val += ratio;
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_Grove_Per2);
|
||||
if (pWnd) {
|
||||
pWnd->GetWindowText(buff, ARBITRARY_BUFF_SIZE - 1);
|
||||
ratio = atoi(buff);
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "DefaultRatio2", ratio);
|
||||
val += ratio;
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_Grove_Per3);
|
||||
if (pWnd) {
|
||||
pWnd->GetWindowText(buff, ARBITRARY_BUFF_SIZE - 1);
|
||||
ratio = atoi(buff);
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "DefaultRatio3", ratio);
|
||||
val += ratio;
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_Grove_Per4);
|
||||
if (pWnd) {
|
||||
pWnd->GetWindowText(buff, ARBITRARY_BUFF_SIZE - 1);
|
||||
ratio = atoi(buff);
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "DefaultRatio4", ratio);
|
||||
val += ratio;
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_Grove_Per5);
|
||||
if (pWnd) {
|
||||
pWnd->GetWindowText(buff, ARBITRARY_BUFF_SIZE - 1);
|
||||
ratio = atoi(buff);
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "DefaultRatio5", ratio);
|
||||
val += ratio;
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_Grove_PerTotal);
|
||||
if (pWnd) {
|
||||
sprintf(buff, "%d", val);
|
||||
pWnd->SetWindowText(buff);
|
||||
}
|
||||
}
|
||||
|
||||
void GroveOptions::_updateTreeCount(void)
|
||||
{
|
||||
static char buff[ARBITRARY_BUFF_SIZE];
|
||||
CWnd* pWnd = GetDlgItem(IDC_Grove_NumberTrees);
|
||||
if (pWnd) {
|
||||
pWnd->GetWindowText(buff, ARBITRARY_BUFF_SIZE - 1);
|
||||
int val = atoi(buff);
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "NumberofTrees", val);
|
||||
}
|
||||
}
|
||||
|
||||
void GroveOptions::_updateGroveMakeup(void)
|
||||
{
|
||||
for (int type = 1; type <= 5; ++type) {
|
||||
CComboBox *pComboBox;
|
||||
if (type == 1) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type1);
|
||||
} else if (type == 2) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type2);
|
||||
} else if (type == 3) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type3);
|
||||
} else if (type == 4) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type4);
|
||||
} else if (type == 5) {
|
||||
pComboBox = (CComboBox*) GetDlgItem(IDC_Grove_Type5);
|
||||
}
|
||||
|
||||
if (!pComboBox) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int curSel = pComboBox->GetCurSel();
|
||||
if (type == 1) {
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "TreeType1", curSel);
|
||||
} else if (type == 2) {
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "TreeType2", curSel);
|
||||
} else if (type == 3) {
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "TreeType3", curSel);
|
||||
} else if (type == 4) {
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "TreeType4", curSel);
|
||||
} else if (type == 5) {
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "TreeType5", curSel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroveOptions::_updatePlacementAllowed(void)
|
||||
{
|
||||
// huh huh huh-huh
|
||||
CButton* pButt;
|
||||
|
||||
pButt = (CButton*) GetDlgItem(IDC_Grove_AllowCliffPlacement);
|
||||
if (pButt) {
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "AllowCliffPlace", pButt->GetCheck());
|
||||
}
|
||||
|
||||
pButt = (CButton*) GetDlgItem(IDC_Grove_AllowWaterPlacement);
|
||||
if (pButt) {
|
||||
AfxGetApp()->WriteProfileInt("GroveOptions", "AllowWaterPlace", pButt->GetCheck());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void GroveOptions::OnOK()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GroveOptions::OnClose()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
UnicodeString GetDisplayNameFromPair(const PairNameDisplayName* pNamePair)
|
||||
{
|
||||
if (!pNamePair) {
|
||||
return UnicodeString::TheEmptyString;
|
||||
}
|
||||
|
||||
if (!pNamePair->second.isEmpty()) {
|
||||
return pNamePair->second;
|
||||
}
|
||||
|
||||
// The unicode portion of the pair was empty. We need to use the Ascii version instead.
|
||||
UnicodeString retStr;
|
||||
retStr.translate(pNamePair->first);
|
||||
return retStr;
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(GroveOptions, CDialog)
|
||||
ON_EN_KILLFOCUS(IDC_Grove_Per1, _updateTreeWeights)
|
||||
ON_EN_KILLFOCUS(IDC_Grove_Per2, _updateTreeWeights)
|
||||
ON_EN_KILLFOCUS(IDC_Grove_Per3, _updateTreeWeights)
|
||||
ON_EN_KILLFOCUS(IDC_Grove_Per4, _updateTreeWeights)
|
||||
ON_EN_KILLFOCUS(IDC_Grove_Per5, _updateTreeWeights)
|
||||
ON_EN_KILLFOCUS(IDC_Grove_NumberTrees, _updateTreeCount)
|
||||
ON_CBN_SELENDOK(IDC_Grove_Type1, _updateGroveMakeup)
|
||||
ON_CBN_SELENDOK(IDC_Grove_Type2, _updateGroveMakeup)
|
||||
ON_CBN_SELENDOK(IDC_Grove_Type3, _updateGroveMakeup)
|
||||
ON_CBN_SELENDOK(IDC_Grove_Type4, _updateGroveMakeup)
|
||||
ON_CBN_SELENDOK(IDC_Grove_Type5, _updateGroveMakeup)
|
||||
ON_BN_CLICKED(IDC_Grove_AllowCliffPlacement, _updatePlacementAllowed)
|
||||
ON_BN_CLICKED(IDC_Grove_AllowWaterPlacement, _updatePlacementAllowed)
|
||||
END_MESSAGE_MAP()
|
||||
434
GeneralsMD/Code/Tools/WorldBuilder/src/GroveTool.cpp
Normal file
434
GeneralsMD/Code/Tools/WorldBuilder/src/GroveTool.cpp
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// GroveTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "GroveTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "common/GlobalData.h"
|
||||
#include "GameLogic/LogicRandomValue.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
#include "Common/Debug.h"
|
||||
#include "DrawObject.h"
|
||||
#include "GroveOptions.h"
|
||||
#include "GameLogic/PolygonTrigger.h"
|
||||
#include "MapObjectProps.h"
|
||||
|
||||
#define DRAG_THRESHOLD 5
|
||||
#define MAX_TREE_RISE_OVER_RUN (1.5f)
|
||||
|
||||
// surface normal Z must be greater than this to plant a tree
|
||||
static Real flatTolerance = 0.8f;
|
||||
|
||||
static Bool _positionIsTooCliffyForTrees(Coord3D pos);
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** See if a location is underwater, and what the water height is. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool localIsUnderwater( Real x, Real y)
|
||||
{
|
||||
ICoord3D iLoc;
|
||||
iLoc.x = (floor(x+0.5f));
|
||||
iLoc.y = (floor(y+0.5f));
|
||||
iLoc.z = 0;
|
||||
// Look for water areas.
|
||||
for (PolygonTrigger *pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
|
||||
if (!pTrig->isWaterArea()) {
|
||||
continue;
|
||||
}
|
||||
// See if point is in a water area
|
||||
if (pTrig->pointInTrigger(iLoc)) {
|
||||
Real wZ = pTrig->getPoint(0)->z;
|
||||
// See if the ground height is less than the water level.
|
||||
Real curHeight = TheTerrainRenderObject->getHeightMapHeight(x, y, NULL);
|
||||
return (curHeight<wZ);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// plant a tree
|
||||
void GroveTool::plantTree( Coord3D *pos )
|
||||
{
|
||||
AsciiString treeName;
|
||||
int totalValue = TheGroveOptions->getTotalTreePerc();
|
||||
int randVal = GameLogicRandomValue(0, totalValue - 1);
|
||||
|
||||
|
||||
int runningCum = 0;
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
runningCum += TheGroveOptions->getNumType(i);
|
||||
if (randVal < runningCum) {
|
||||
if (TheGroveOptions->getTypeName(i).isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
treeName = TheGroveOptions->getTypeName(i).str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
addObj(pos, treeName);
|
||||
}
|
||||
|
||||
void GroveTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_GROVE_OPTIONS);
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
}
|
||||
|
||||
|
||||
// plant a shrub
|
||||
void GroveTool::plantShrub( Coord3D *pos )
|
||||
{
|
||||
// TODO: Determine when we can tell something is a shurubbery, and plant it here - jkmcd
|
||||
// addObj(pos, AsciiString("Shrub"));
|
||||
}
|
||||
|
||||
void GroveTool::_plantGroveInBox(CPoint tl, CPoint br, WbView* pView)
|
||||
{
|
||||
|
||||
int numTotalTrees = TheGroveOptions->getNumTrees();
|
||||
|
||||
Coord3D tl3d, tr3d, bl3d;
|
||||
pView->viewToDocCoords(tl, &tl3d);
|
||||
pView->viewToDocCoords(CPoint(tl.x, br.y), &bl3d);
|
||||
pView->viewToDocCoords(CPoint(br.x, tl.y), &tr3d);
|
||||
|
||||
tl3d.z = 0;
|
||||
|
||||
bl3d.x -= tl3d.x;
|
||||
bl3d.y -= tl3d.y;
|
||||
bl3d.z = 0;
|
||||
|
||||
tr3d.x -= tl3d.x;
|
||||
tr3d.y -= tl3d.y;
|
||||
tr3d.z = 0;
|
||||
|
||||
for (int i = 0; i < numTotalTrees; ++i) {
|
||||
Real trModifier = GameLogicRandomValueReal(0.0f, 1.0f);
|
||||
Real blModifier = GameLogicRandomValueReal(0.0f, 1.0f);
|
||||
|
||||
Vector3 tlVec(tl3d.x, tl3d.y, tl3d.z);
|
||||
Vector3 trVec(tr3d.x * trModifier, tr3d.y * trModifier, tr3d.z);
|
||||
Vector3 blVec(bl3d.x * blModifier, bl3d.y * blModifier, bl3d.z);
|
||||
|
||||
tlVec = tlVec + trVec;
|
||||
tlVec = tlVec + blVec;
|
||||
|
||||
Coord3D position;
|
||||
|
||||
position.x = tlVec.X;
|
||||
position.y = tlVec.Y;
|
||||
position.z = 0;
|
||||
|
||||
// (maybe) don't put trees on steep slopes
|
||||
// (maybe) tree must not be in the water
|
||||
if (!TheGroveOptions->getCanPlaceInWater() && localIsUnderwater(position.x, position.y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TheGroveOptions->getCanPlaceOnCliffs() && _positionIsTooCliffyForTrees(position)) {
|
||||
continue;
|
||||
}
|
||||
// We've satisfied our conditions. Plant the little bugger
|
||||
plantTree(&position);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plant a grove of trees, recursively. Given a "seed location", create child
|
||||
* trees nearby, and recursively call the grove function on those children.
|
||||
*/
|
||||
void GroveTool::plantGrove( Coord3D pos, Coord3D prevDir, Real baseHeight, Int level, CPoint bounds )
|
||||
{
|
||||
Coord3D childPos, normal, dir;
|
||||
Real childChance = 1.0f;
|
||||
Real heightTolerance = 1.5f*MAP_XY_FACTOR; // was 4
|
||||
Real angle, spread;
|
||||
Int numChildren = 2, retry, maxTries = 5;
|
||||
|
||||
if (level == 1)
|
||||
{
|
||||
// plant a shrub around the outskirts of the grove
|
||||
plantShrub( &pos );
|
||||
}
|
||||
else
|
||||
{
|
||||
// plant a tree here
|
||||
plantTree( &pos );
|
||||
}
|
||||
|
||||
// if reached base level, stop
|
||||
if (level == 0)
|
||||
return;
|
||||
|
||||
|
||||
// spawn child trees
|
||||
for( Int i=0; i<numChildren; i++ )
|
||||
{
|
||||
if (GameLogicRandomValueReal( 0.0f, 1.0f ) < childChance)
|
||||
{
|
||||
for( retry=0; retry<maxTries; retry++ )
|
||||
{
|
||||
angle = GameLogicRandomValueReal( 0.0f, 2.0f * PI );
|
||||
spread = GameLogicRandomValueReal( 3.0f*MAP_XY_FACTOR, 6.0f*MAP_XY_FACTOR );
|
||||
childPos.x = pos.x + spread * (Real)cos( angle );
|
||||
childPos.y = pos.y + spread * (Real)sin( angle );
|
||||
childPos.z = 0;
|
||||
if (TheTerrainRenderObject) {
|
||||
TheTerrainRenderObject->getHeightMapHeight( childPos.x, childPos.y, &normal );
|
||||
}
|
||||
|
||||
dir.x = childPos.x - pos.x;
|
||||
dir.y = childPos.y - pos.y;
|
||||
|
||||
// don't select a spot too much towards where we came from
|
||||
// (note that if prevDir the zero vector, it passes this test (for the 1st tree))
|
||||
// don't put trees on steep slopes
|
||||
// dont' plant if height changed too much
|
||||
// tree must be on map
|
||||
// tree must not be in the water
|
||||
if (dir.x * prevDir.x + dir.y * prevDir.y >= 0.0f &&
|
||||
normal.z > flatTolerance &&
|
||||
fabs(childPos.z - baseHeight) < heightTolerance &&
|
||||
childPos.x > 0 && childPos.y > 0 &&
|
||||
childPos.x < bounds.x && childPos.y < bounds.y &&
|
||||
(!localIsUnderwater(childPos.x, childPos.y)))
|
||||
break;
|
||||
}
|
||||
|
||||
if (retry < maxTries)
|
||||
plantGrove( childPos, dir, baseHeight, level-1, bounds );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// GroveTool class.
|
||||
//
|
||||
/// Constructor
|
||||
GroveTool::GroveTool(void) :
|
||||
Tool(ID_GROVE_TOOL, IDC_GROVE)
|
||||
{
|
||||
m_headMapObj = NULL;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
GroveTool::~GroveTool(void)
|
||||
{
|
||||
if (m_headMapObj) {
|
||||
m_headMapObj->deleteInstance();
|
||||
}
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse down - Place an object. */
|
||||
void GroveTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
m_downPt = viewPt;
|
||||
m_dragging = false;
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse up - Place an object. */
|
||||
void GroveTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
if (pView && m_dragging) {
|
||||
CRect box;
|
||||
box.left = viewPt.x;
|
||||
box.top = viewPt.y;
|
||||
box.right = m_downPt.x;
|
||||
box.bottom = m_downPt.y;
|
||||
box.NormalizeRect();
|
||||
pView->doRectFeedback(false, box);
|
||||
pView->Invalidate();
|
||||
|
||||
_plantGroveInBox(m_downPt, viewPt, pView);
|
||||
if (m_headMapObj != NULL) {
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, m_headMapObj);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_headMapObj = NULL; // undoable owns it now.
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Coord3D loc;
|
||||
|
||||
pView->viewToDocCoords(m_downPt, &loc);
|
||||
|
||||
WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
CPoint bounds;
|
||||
bounds.x = pMap->getXExtent()*MAP_XY_FACTOR;
|
||||
bounds.y = pMap->getYExtent()*MAP_XY_FACTOR;
|
||||
// Real angle = 0;
|
||||
Int depth = 3;
|
||||
Coord3D zeroDir;
|
||||
|
||||
zeroDir.x = 0.0f;
|
||||
zeroDir.y = 0.0f;
|
||||
zeroDir.z = 0.0f;
|
||||
loc.z = TheTerrainRenderObject ? TheTerrainRenderObject->getHeightMapHeight( loc.x, loc.y, NULL ) : 0;
|
||||
|
||||
// grow tree grove out from here
|
||||
plantGrove( loc, zeroDir, loc.z, depth, bounds );
|
||||
if (m_headMapObj != NULL) {
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, m_headMapObj);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_headMapObj = NULL; // undoable owns it now.
|
||||
}
|
||||
}
|
||||
|
||||
void GroveTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (abs(viewPt.x - m_downPt.x) > DRAG_THRESHOLD || abs(viewPt.y - m_downPt.y) > DRAG_THRESHOLD) {
|
||||
m_dragging = true;
|
||||
}
|
||||
|
||||
if (pView && m_dragging) {
|
||||
CRect box;
|
||||
box.left = viewPt.x;
|
||||
box.bottom = viewPt.y;
|
||||
box.top = m_downPt.y;
|
||||
box.right = m_downPt.x;
|
||||
box.NormalizeRect();
|
||||
pView->doRectFeedback(true, box);
|
||||
pView->Invalidate();
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void GroveTool::addObj(Coord3D *pos, AsciiString name)
|
||||
{
|
||||
MapObject *pCur = ObjectOptions::getObjectNamed(name);
|
||||
DEBUG_ASSERTCRASH(pCur!=NULL, ("oops"));
|
||||
if (!pCur) return;
|
||||
Coord3D theLoc = *pos;
|
||||
theLoc.z = 0;
|
||||
Real angle = GameLogicRandomValueReal( 0.0f, 2.0f * PI );
|
||||
MapObject *pNew = newInstance( MapObject)(theLoc, pCur->getName(), angle, 0, NULL, pCur->getThingTemplate() );
|
||||
pNew->getProperties()->setAsciiString(TheKey_originalOwner, NEUTRAL_TEAM_INTERNAL_STR);
|
||||
pNew->setNextMap(m_headMapObj);
|
||||
m_headMapObj = pNew;
|
||||
}
|
||||
|
||||
|
||||
#define SQRT_2 (1.41421356f)
|
||||
static Bool _positionIsTooCliffyForTrees(Coord3D pos)
|
||||
{
|
||||
Coord3D otherPos;
|
||||
|
||||
otherPos = pos;
|
||||
otherPos.x += MAP_XY_FACTOR;
|
||||
otherPos.z = TheTerrainRenderObject->getHeightMapHeight(otherPos.x, otherPos.y, NULL);
|
||||
|
||||
if (((pos.z / otherPos.z / 1) > MAX_TREE_RISE_OVER_RUN) ||
|
||||
((otherPos.z / pos.z / 1) > MAX_TREE_RISE_OVER_RUN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
otherPos = pos;
|
||||
otherPos.y += MAP_XY_FACTOR;
|
||||
otherPos.z = TheTerrainRenderObject->getHeightMapHeight(otherPos.x, otherPos.y, NULL);
|
||||
|
||||
if (((pos.z / otherPos.z / 1) > MAX_TREE_RISE_OVER_RUN) ||
|
||||
((otherPos.z / pos.z / 1) > MAX_TREE_RISE_OVER_RUN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
otherPos = pos;
|
||||
otherPos.y -= MAP_XY_FACTOR;
|
||||
otherPos.z = TheTerrainRenderObject->getHeightMapHeight(otherPos.x, otherPos.y, NULL);
|
||||
|
||||
if (((pos.z / otherPos.z / 1) > MAX_TREE_RISE_OVER_RUN) ||
|
||||
((otherPos.z / pos.z / 1) > MAX_TREE_RISE_OVER_RUN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
otherPos = pos;
|
||||
otherPos.x -= MAP_XY_FACTOR;
|
||||
otherPos.z = TheTerrainRenderObject->getHeightMapHeight(otherPos.x, otherPos.y, NULL);
|
||||
|
||||
if (((pos.z / otherPos.z / 1) > MAX_TREE_RISE_OVER_RUN) ||
|
||||
((otherPos.z / pos.z / 1) > MAX_TREE_RISE_OVER_RUN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
otherPos = pos;
|
||||
otherPos.x += MAP_XY_FACTOR;
|
||||
otherPos.y += MAP_XY_FACTOR;
|
||||
otherPos.z = TheTerrainRenderObject->getHeightMapHeight(otherPos.x, otherPos.y, NULL);
|
||||
|
||||
if (((pos.z / otherPos.z / SQRT_2) > MAX_TREE_RISE_OVER_RUN) ||
|
||||
((otherPos.z / pos.z / SQRT_2) > MAX_TREE_RISE_OVER_RUN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
otherPos = pos;
|
||||
otherPos.x += MAP_XY_FACTOR;
|
||||
otherPos.y -= MAP_XY_FACTOR;
|
||||
otherPos.z = TheTerrainRenderObject->getHeightMapHeight(otherPos.x, otherPos.y, NULL);
|
||||
|
||||
if (((pos.z / otherPos.z / SQRT_2) > MAX_TREE_RISE_OVER_RUN) ||
|
||||
((otherPos.z / pos.z / SQRT_2) > MAX_TREE_RISE_OVER_RUN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
otherPos = pos;
|
||||
otherPos.x -= MAP_XY_FACTOR;
|
||||
otherPos.y -= MAP_XY_FACTOR;
|
||||
otherPos.z = TheTerrainRenderObject->getHeightMapHeight(otherPos.x, otherPos.y, NULL);
|
||||
|
||||
if (((pos.z / otherPos.z / SQRT_2) > MAX_TREE_RISE_OVER_RUN) ||
|
||||
((otherPos.z / pos.z / SQRT_2) > MAX_TREE_RISE_OVER_RUN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
otherPos = pos;
|
||||
otherPos.x -= MAP_XY_FACTOR;
|
||||
otherPos.y += MAP_XY_FACTOR;
|
||||
otherPos.z = TheTerrainRenderObject->getHeightMapHeight(otherPos.x, otherPos.y, NULL);
|
||||
|
||||
if (((pos.z / otherPos.z / SQRT_2) > MAX_TREE_RISE_OVER_RUN) ||
|
||||
((otherPos.z / pos.z / SQRT_2) > MAX_TREE_RISE_OVER_RUN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#undef SQRT_2
|
||||
142
GeneralsMD/Code/Tools/WorldBuilder/src/HandScrollTool.cpp
Normal file
142
GeneralsMD/Code/Tools/WorldBuilder/src/HandScrollTool.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// HandScrollTool.cpp
|
||||
// Scrolling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "HandScrollTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilder.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
//
|
||||
// HandScrollTool class.
|
||||
//
|
||||
|
||||
inline Int IABS(Int x) { if (x>=0) return x; return -x;};
|
||||
static const Int MAX_SCROLL = 1000;
|
||||
|
||||
/// Constructor
|
||||
HandScrollTool::HandScrollTool(void) :
|
||||
Tool(ID_HAND_SCROLL_TOOL, IDC_HAND_SCROLL)
|
||||
{
|
||||
}
|
||||
|
||||
/// Shows the terrain materials options panel.
|
||||
void HandScrollTool::activate()
|
||||
{
|
||||
return; // Hand scroll tool intentionally doesn't change tool panel.
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
HandScrollTool::~HandScrollTool(void)
|
||||
{
|
||||
}
|
||||
|
||||
void HandScrollTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L && m != TRACK_R && m != TRACK_M) return;
|
||||
|
||||
// pView->viewToDocCoords(viewPt, &m_prevPt);
|
||||
m_prevPt2d = viewPt;
|
||||
m_downPt2d = viewPt;
|
||||
m_scrolling = false;
|
||||
// Save the start point.
|
||||
m_mouseDownTime = ::GetTickCount();
|
||||
}
|
||||
|
||||
/// Left button move code.
|
||||
void HandScrollTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m == TRACK_NONE)
|
||||
return;
|
||||
|
||||
if (m == TRACK_M) {
|
||||
|
||||
// camera rotation
|
||||
const Real factor = 0.01f;
|
||||
Real rot = factor * (viewPt.x - m_prevPt2d.x);
|
||||
/*
|
||||
if (pView->isDoingPitch())
|
||||
pView->pitchCamera(rot);
|
||||
else
|
||||
*/
|
||||
pView->rotateCamera(rot);
|
||||
m_prevPt2d = viewPt;
|
||||
|
||||
} else if (m == TRACK_L || m == TRACK_R) {
|
||||
if (!m_scrolling) {
|
||||
// see if we moved enough to start scrolling.
|
||||
if (abs(viewPt.x - m_downPt2d.x) > HYSTERESIS) m_scrolling = true;
|
||||
if (abs(viewPt.y - m_downPt2d.y) > HYSTERESIS) m_scrolling = true;
|
||||
}
|
||||
if (!m_scrolling) {
|
||||
return;
|
||||
}
|
||||
// Scroll dynamically.
|
||||
Coord3D prev, cur;
|
||||
if (pView->viewToDocCoords(m_prevPt2d, &prev, false) &&
|
||||
pView->viewToDocCoords(viewPt, &cur, false))
|
||||
{
|
||||
|
||||
Real dx = cur.x - prev.x;
|
||||
Real dy = cur.y - prev.y;
|
||||
if (IABS(dx)>MAX_SCROLL) {
|
||||
dx = 0;
|
||||
}
|
||||
if (IABS(dy)>MAX_SCROLL) {
|
||||
dy = 0;
|
||||
}
|
||||
dx /= MAP_XY_FACTOR;
|
||||
dy /= MAP_XY_FACTOR;
|
||||
pView->scrollInView(dx, dy, false);
|
||||
|
||||
m_prevPt2d = viewPt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandScrollTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m == TRACK_M) {
|
||||
// if haven't moved, reset view
|
||||
Bool moved = false;
|
||||
if (abs(viewPt.x - m_downPt2d.x) > HYSTERESIS) moved = true;
|
||||
if (abs(viewPt.y - m_downPt2d.y) > HYSTERESIS) moved = true;
|
||||
|
||||
if (!moved && GetTickCount() - m_mouseDownTime < ::GetDoubleClickTime())
|
||||
{
|
||||
pView->setDefaultCamera();
|
||||
}
|
||||
} else if (m == TRACK_L || m == TRACK_R) {
|
||||
if (m_scrolling) {
|
||||
// Tell the view we are done scrolling.
|
||||
pView->scrollInView(0,0,true);
|
||||
} else if (m==TRACK_R) {
|
||||
// Clicked right. Deselect & go to pointer.
|
||||
WbApp()->selectPointerTool();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
111
GeneralsMD/Code/Tools/WorldBuilder/src/ImpassableOptions.cpp
Normal file
111
GeneralsMD/Code/Tools/WorldBuilder/src/ImpassableOptions.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ImpassableOptions.cpp
|
||||
// Author: John McDonald, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "ImpassableOptions.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
#include "WbView3d.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ImpassableOptions::ImpassableOptions(CWnd* pParent, Real defaultSlope) :
|
||||
CDialog(ImpassableOptions::IDD, pParent),
|
||||
m_slopeToShow(defaultSlope),
|
||||
m_defaultSlopeToShow(defaultSlope)
|
||||
{
|
||||
// nada to do
|
||||
m_showImpassableAreas = TheTerrainRenderObject->getShowImpassableAreas();
|
||||
TheTerrainRenderObject->setShowImpassableAreas(TRUE);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ImpassableOptions::~ImpassableOptions()
|
||||
{
|
||||
TheTerrainRenderObject->setShowImpassableAreas(m_showImpassableAreas);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool ImpassableOptions::ValidateSlope()
|
||||
{
|
||||
if (m_slopeToShow < 0.0f) {
|
||||
m_slopeToShow = 0.0f;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (m_slopeToShow >= 90.0f) {
|
||||
m_slopeToShow = 89.9f;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
BOOL ImpassableOptions::OnInitDialog()
|
||||
{
|
||||
CWnd* pWnd = GetDlgItem(IDC_ANGLE);
|
||||
if (!pWnd) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
AsciiString astr;
|
||||
astr.format("%.2f", m_slopeToShow);
|
||||
pWnd->SetWindowText(astr.str());
|
||||
|
||||
return CDialog::OnInitDialog();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ImpassableOptions::OnAngleChange()
|
||||
{
|
||||
CWnd* pWnd = GetDlgItem(IDC_ANGLE);
|
||||
if (!pWnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
CString cstr;
|
||||
pWnd->GetWindowText(cstr);
|
||||
char* buff = cstr.GetBuffer(0);
|
||||
|
||||
m_slopeToShow = (Real)atof(buff);
|
||||
|
||||
if (!ValidateSlope()) {
|
||||
AsciiString astr;
|
||||
astr.format("%.2f", m_slopeToShow);
|
||||
pWnd->SetWindowText(astr.str());
|
||||
}
|
||||
TheTerrainRenderObject->setViewImpassableAreaSlope(m_slopeToShow);
|
||||
}
|
||||
|
||||
void ImpassableOptions::OnPreview()
|
||||
{
|
||||
// update it.
|
||||
IRegion2D range = {0,0,0,0};
|
||||
WbView3d *pView = CWorldBuilderDoc::GetActive3DView();
|
||||
pView->updateHeightMapInView(TheTerrainRenderObject->getMap(), false, range);
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(ImpassableOptions, CDialog)
|
||||
ON_EN_UPDATE(IDC_ANGLE, OnAngleChange)
|
||||
ON_BN_CLICKED(IDC_PREVIEW, OnPreview)
|
||||
END_MESSAGE_MAP()
|
||||
1346
GeneralsMD/Code/Tools/WorldBuilder/src/LayersList.cpp
Normal file
1346
GeneralsMD/Code/Tools/WorldBuilder/src/LayersList.cpp
Normal file
File diff suppressed because it is too large
Load Diff
214
GeneralsMD/Code/Tools/WorldBuilder/src/LightOptions.cpp
Normal file
214
GeneralsMD/Code/Tools/WorldBuilder/src/LightOptions.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// LightOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "LightOptions.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WbView3D.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
|
||||
LightOptions *LightOptions::m_staticThis = NULL;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// LightOptions dialog trivial construstor - Create does the real work.
|
||||
|
||||
|
||||
LightOptions::LightOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
//{{AFX_DATA_INIT(LightOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
/// Windows default stuff.
|
||||
void LightOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(LightOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
MapObject *LightOptions::getSingleSelectedLight(void)
|
||||
{
|
||||
MapObject *pMapObj;
|
||||
MapObject *theMapObj = NULL;
|
||||
// Bool found = false;
|
||||
Int selCount=0;
|
||||
for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) {
|
||||
if (pMapObj->isSelected()) {
|
||||
if (pMapObj->isLight()) {
|
||||
theMapObj = pMapObj;
|
||||
}
|
||||
selCount++;
|
||||
}
|
||||
}
|
||||
if (selCount==1 && theMapObj) {
|
||||
return theMapObj;
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
||||
void LightOptions::updateTheUI(void)
|
||||
{
|
||||
MapObject *theMapObj = getSingleSelectedLight();
|
||||
if (!theMapObj) return;
|
||||
Dict *props = theMapObj->getProperties();
|
||||
|
||||
CString str;
|
||||
|
||||
Real lightHeightAboveTerrain, lightInnerRadius, lightOuterRadius;
|
||||
RGBColor lightAmbientColor, lightDiffuseColor;
|
||||
|
||||
lightHeightAboveTerrain = props->getReal(TheKey_lightHeightAboveTerrain);
|
||||
lightInnerRadius = props->getReal(TheKey_lightInnerRadius);
|
||||
lightOuterRadius = props->getReal(TheKey_lightOuterRadius);
|
||||
lightAmbientColor.setFromInt(props->getInt(TheKey_lightAmbientColor));
|
||||
lightDiffuseColor.setFromInt(props->getInt(TheKey_lightDiffuseColor));
|
||||
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_RA_EDIT);
|
||||
if (pEdit) {
|
||||
str.Format("%.2f", lightAmbientColor.red);
|
||||
pEdit->SetWindowText(str);
|
||||
}
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_GA_EDIT);
|
||||
if (pEdit) {
|
||||
str.Format("%.2f", lightAmbientColor.green);
|
||||
pEdit->SetWindowText(str);
|
||||
}
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_BA_EDIT);
|
||||
if (pEdit) {
|
||||
str.Format("%.2f", lightAmbientColor.blue);
|
||||
pEdit->SetWindowText(str);
|
||||
}
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
if (pEdit) {
|
||||
str.Format("%.2f", lightHeightAboveTerrain);
|
||||
pEdit->SetWindowText(str);
|
||||
}
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_RADIUS_EDIT);
|
||||
if (pEdit) {
|
||||
str.Format("%.2f", lightOuterRadius);
|
||||
pEdit->SetWindowText(str);
|
||||
}
|
||||
}
|
||||
|
||||
void LightOptions::update(void)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
m_staticThis->updateTheUI();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// LightOptions message handlers
|
||||
|
||||
/// Dialog UI initialization.
|
||||
/** Creates the slider controls, and sets the initial values for
|
||||
width and feather in the ui controls. */
|
||||
BOOL LightOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(LightOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(LightOptions)
|
||||
ON_EN_CHANGE(IDC_RA_EDIT, OnChangeLightEdit)
|
||||
ON_EN_CHANGE(IDC_GA_EDIT, OnChangeLightEdit)
|
||||
ON_EN_CHANGE(IDC_BA_EDIT, OnChangeLightEdit)
|
||||
ON_EN_CHANGE(IDC_HEIGHT_EDIT, OnChangeLightEdit)
|
||||
ON_EN_CHANGE(IDC_RADIUS_EDIT, OnChangeLightEdit)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
void LightOptions::OnChangeLightEdit()
|
||||
{
|
||||
MapObject *theMapObj = getSingleSelectedLight();
|
||||
if (!theMapObj) return;
|
||||
if (!theMapObj->isLight()) return;
|
||||
|
||||
Real lightHeightAboveTerrain, lightInnerRadius, lightOuterRadius;
|
||||
RGBColor lightAmbientColor, lightDiffuseColor;
|
||||
|
||||
Dict *props = theMapObj->getProperties();
|
||||
lightHeightAboveTerrain = props->getReal(TheKey_lightHeightAboveTerrain);
|
||||
lightInnerRadius = props->getReal(TheKey_lightInnerRadius);
|
||||
lightOuterRadius = props->getReal(TheKey_lightOuterRadius);
|
||||
lightAmbientColor.setFromInt(props->getInt(TheKey_lightAmbientColor));
|
||||
lightDiffuseColor.setFromInt(props->getInt(TheKey_lightDiffuseColor));
|
||||
|
||||
Real clr;
|
||||
char buffer[_MAX_PATH];
|
||||
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_RA_EDIT);
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
if (1==sscanf(buffer, "%f", &clr)) lightAmbientColor.red = clr;
|
||||
}
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_GA_EDIT);
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
if (1==sscanf(buffer, "%f", &clr)) lightAmbientColor.green = clr;
|
||||
}
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_BA_EDIT);
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
if (1==sscanf(buffer, "%f", &clr)) lightAmbientColor.blue = clr;
|
||||
}
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
Real r;
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
if (1==sscanf(buffer, "%f", &r)) lightHeightAboveTerrain = r;
|
||||
}
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_RADIUS_EDIT);
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
if (1==sscanf(buffer, "%f", &r)) lightOuterRadius = r;
|
||||
}
|
||||
lightDiffuseColor.red = 0;
|
||||
lightDiffuseColor.green = 0;
|
||||
lightDiffuseColor.blue = 0;
|
||||
lightInnerRadius = lightOuterRadius/2;
|
||||
|
||||
props->setReal(TheKey_lightHeightAboveTerrain, lightHeightAboveTerrain);
|
||||
props->setReal(TheKey_lightInnerRadius, lightInnerRadius);
|
||||
props->setReal(TheKey_lightOuterRadius, lightOuterRadius);
|
||||
props->setInt(TheKey_lightAmbientColor, lightAmbientColor.getAsInt());
|
||||
props->setInt(TheKey_lightDiffuseColor, lightDiffuseColor.getAsInt());
|
||||
|
||||
WbView3d * pView = CWorldBuilderDoc::GetActive3DView();
|
||||
if (pView) {
|
||||
pView->invalObjectInView(theMapObj);
|
||||
}
|
||||
}
|
||||
534
GeneralsMD/Code/Tools/WorldBuilder/src/MainFrm.cpp
Normal file
534
GeneralsMD/Code/Tools/WorldBuilder/src/MainFrm.cpp
Normal file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// MainFrm.cpp : implementation of the CMainFrame class
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "MainFrm.h"
|
||||
|
||||
#include "common/GlobalData.h"
|
||||
|
||||
#include "DrawObject.h"
|
||||
#include "LayersList.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WbView3d.h"
|
||||
#include "WorldBuilder.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
|
||||
#include "ScriptDialog.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMainFrame
|
||||
|
||||
IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)
|
||||
|
||||
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
|
||||
//{{AFX_MSG_MAP(CMainFrame)
|
||||
ON_WM_CREATE()
|
||||
ON_WM_MOVE()
|
||||
ON_COMMAND(ID_VIEW_BRUSHFEEDBACK, OnViewBrushfeedback)
|
||||
ON_UPDATE_COMMAND_UI(ID_VIEW_BRUSHFEEDBACK, OnUpdateViewBrushfeedback)
|
||||
ON_WM_DESTROY()
|
||||
ON_WM_TIMER()
|
||||
ON_WM_CANCELMODE()
|
||||
ON_COMMAND(ID_EDIT_CAMERAOPTIONS, OnEditCameraoptions)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
static UINT indicators[] =
|
||||
{
|
||||
ID_SEPARATOR, // status line indicator
|
||||
ID_INDICATOR_CAPS,
|
||||
ID_INDICATOR_NUM,
|
||||
ID_INDICATOR_SCRL,
|
||||
};
|
||||
|
||||
CMainFrame *CMainFrame::TheMainFrame = NULL;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMainFrame construction/destruction
|
||||
|
||||
CMainFrame::CMainFrame()
|
||||
{
|
||||
TheMainFrame = this;
|
||||
m_curOptions = NULL;
|
||||
m_hAutoSaveTimer = NULL;
|
||||
m_autoSaving = false;
|
||||
m_layersList = NULL;
|
||||
m_scriptDialog = NULL;
|
||||
}
|
||||
|
||||
CMainFrame::~CMainFrame()
|
||||
{
|
||||
if (m_layersList) {
|
||||
delete m_layersList;
|
||||
}
|
||||
|
||||
if (m_scriptDialog) {
|
||||
delete m_scriptDialog;
|
||||
m_scriptDialog = NULL;
|
||||
}
|
||||
|
||||
SaveBarState("MainFrame");
|
||||
TheMainFrame = NULL;
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "AutoSave", m_autoSave);
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "AutoSaveIntervalSeconds", m_autoSaveInterval);
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
||||
{
|
||||
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
|
||||
return -1;
|
||||
adjustWindowSize();
|
||||
CRect frameRect;
|
||||
GetWindowRect(&frameRect);
|
||||
|
||||
CWnd *pDesk = GetDesktopWindow();
|
||||
CRect top;
|
||||
pDesk->GetWindowRect(&top);
|
||||
top.left += 10;
|
||||
top.top += 10;
|
||||
top.top = ::AfxGetApp()->GetProfileInt(MAIN_FRAME_SECTION, "Top", top.top);
|
||||
top.left =::AfxGetApp()->GetProfileInt(MAIN_FRAME_SECTION, "Left", top.left);
|
||||
SetWindowPos(NULL, top.left, top.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
GetWindowRect(&frameRect);
|
||||
EnableDocking(CBRS_ALIGN_TOP);
|
||||
|
||||
#if 0 // For a floating toolbar.
|
||||
#define WRAP(btn) m_floatingToolBar.SetButtonStyle( btn, m_floatingToolBar.GetButtonStyle( btn )|TBBS_WRAPPED)
|
||||
if (!m_floatingToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_LEFT
|
||||
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_FIXED ) ||
|
||||
!m_floatingToolBar.LoadToolBar(IDR_TOOLBAR2))
|
||||
WRAP(1);
|
||||
WRAP(4);
|
||||
WRAP(6);
|
||||
WRAP(9);
|
||||
WRAP(11);
|
||||
WRAP(14);
|
||||
WRAP(16);
|
||||
#undef WRAP
|
||||
CPoint pos(frameRect.left,frameRect.top+60);
|
||||
this->FloatControlBar(&m_floatingToolBar, pos, CBRS_ALIGN_LEFT);
|
||||
m_floatingToolBar.EnableDocking(CBRS_ALIGN_TOP);
|
||||
#endif
|
||||
|
||||
if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT)))
|
||||
{
|
||||
DEBUG_CRASH(("Failed to create status bar\n"));
|
||||
}
|
||||
|
||||
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
|
||||
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_FIXED ) ||
|
||||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
|
||||
{
|
||||
TRACE0("Failed to create toolbar\n");
|
||||
return -1; // fail to create
|
||||
}
|
||||
m_wndToolBar.EnableDocking(CBRS_ALIGN_TOP);
|
||||
|
||||
frameRect.left = frameRect.right;
|
||||
frameRect.top = ::AfxGetApp()->GetProfileInt(OPTIONS_PANEL_SECTION, "Top", frameRect.top);
|
||||
frameRect.left =::AfxGetApp()->GetProfileInt(OPTIONS_PANEL_SECTION, "Left", frameRect.left);
|
||||
|
||||
|
||||
|
||||
m_brushOptions.Create(IDD_BRUSH_OPTIONS, this);
|
||||
m_brushOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_brushOptions.GetWindowRect(&frameRect);
|
||||
m_optionsPanelWidth = frameRect.Width();
|
||||
m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_featherOptions.Create(IDD_FEATHER_OPTIONS, this);
|
||||
m_featherOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_featherOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
|
||||
m_noOptions.Create(IDD_NO_OPTIONS, this);
|
||||
m_noOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_noOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_terrainMaterial.Create(IDD_TERRAIN_MATERIAL, this);
|
||||
m_terrainMaterial.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_terrainMaterial.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_blendMaterial.Create(IDD_BLEND_MATERIAL, this);
|
||||
m_blendMaterial.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_blendMaterial.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_moundOptions.Create(IDD_MOUND_OPTIONS, this);
|
||||
m_moundOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_moundOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_rulerOptions.Create(IDD_RULER_OPTIONS, this);
|
||||
m_rulerOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_rulerOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_objectOptions.Create(IDD_OBJECT_OPTIONS, this);
|
||||
m_objectOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_objectOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_fenceOptions.Create(IDD_FENCE_OPTIONS, this);
|
||||
m_fenceOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_fenceOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_mapObjectProps.Create(IDD_MAPOBJECT_PROPS, this);
|
||||
m_mapObjectProps.makeMain();
|
||||
m_mapObjectProps.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_mapObjectProps.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_roadOptions.Create(IDD_ROAD_OPTIONS, this);
|
||||
m_roadOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_roadOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_waypointOptions.Create(IDD_WAYPOINT_OPTIONS, this);
|
||||
m_waypointOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_waypointOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_waterOptions.Create(IDD_WATER_OPTIONS, this);
|
||||
m_waterOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_waterOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_lightOptions.Create(IDD_LIGHT_OPTIONS, this);
|
||||
m_lightOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_lightOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_meshMoldOptions.Create(IDD_MESHMOLD_OPTIONS, this);
|
||||
m_meshMoldOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_meshMoldOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_buildListOptions.Create(IDD_BUILD_LIST_PANEL, this);
|
||||
m_buildListOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_buildListOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_groveOptions.Create(IDD_GROVE_OPTIONS, this);
|
||||
m_groveOptions.makeMain();
|
||||
m_groveOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_groveOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_rampOptions.Create(IDD_RAMP_OPTIONS, this);
|
||||
m_rampOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_rampOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
m_scorchOptions.Create(IDD_SCORCH_OPTIONS, this);
|
||||
m_scorchOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_scorchOptions.GetWindowRect(&frameRect);
|
||||
if (m_optionsPanelWidth < frameRect.Width()) m_optionsPanelWidth = frameRect.Width();
|
||||
if (m_optionsPanelHeight < frameRect.Height()) m_optionsPanelHeight = frameRect.Height();
|
||||
|
||||
frameRect.top = ::AfxGetApp()->GetProfileInt(GLOBALLIGHT_OPTIONS_PANEL_SECTION, "Top", frameRect.top);
|
||||
frameRect.left =::AfxGetApp()->GetProfileInt(GLOBALLIGHT_OPTIONS_PANEL_SECTION, "Left", frameRect.left);
|
||||
|
||||
m_globalLightOptions.Create(IDD_GLOBAL_LIGHT_OPTIONS, this);
|
||||
m_globalLightOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_globalLightOptions.GetWindowRect(&frameRect);
|
||||
m_globalLightOptionsWidth = frameRect.Width();
|
||||
m_globalLightOptionsHeight = frameRect.Height();
|
||||
|
||||
frameRect.top = ::AfxGetApp()->GetProfileInt(CAMERA_OPTIONS_PANEL_SECTION, "Top", frameRect.top);
|
||||
frameRect.left =::AfxGetApp()->GetProfileInt(CAMERA_OPTIONS_PANEL_SECTION, "Left", frameRect.left);
|
||||
|
||||
m_cameraOptions.Create(IDD_CAMERA_OPTIONS, this);
|
||||
m_cameraOptions.SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_cameraOptions.GetWindowRect(&frameRect);
|
||||
|
||||
// now, setup the Layers Panel
|
||||
m_layersList = new LayersList(LayersList::IDD, this);
|
||||
m_layersList->Create(LayersList::IDD, this);
|
||||
m_layersList->ShowWindow(::AfxGetApp()->GetProfileInt(MAIN_FRAME_SECTION, "ShowLayersList", 0) ? SW_SHOW : SW_HIDE);
|
||||
|
||||
CRect optionsRect;
|
||||
m_globalLightOptions.GetWindowRect(&optionsRect);
|
||||
m_layersList->SetWindowPos(NULL, optionsRect.left, optionsRect.bottom + 100, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
|
||||
|
||||
Int sbf = ::AfxGetApp()->GetProfileInt(MAIN_FRAME_SECTION, "ShowBrushFeedback", 1);
|
||||
if (sbf != 0) {
|
||||
DrawObject::enableFeedback();
|
||||
} else {
|
||||
DrawObject::disableFeedback();
|
||||
}
|
||||
|
||||
Int autoSave = ::AfxGetApp()->GetProfileInt(MAIN_FRAME_SECTION, "AutoSave", 1);
|
||||
m_autoSave = autoSave != 0;
|
||||
autoSave = ::AfxGetApp()->GetProfileInt(MAIN_FRAME_SECTION, "AutoSaveIntervalSeconds", 120);
|
||||
m_autoSaveInterval = autoSave;
|
||||
m_hAutoSaveTimer = this->SetTimer(1, m_autoSaveInterval*1000, NULL);
|
||||
|
||||
#if USE_STREAMING_AUDIO
|
||||
StartMusic();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CMainFrame::adjustWindowSize(void)
|
||||
{
|
||||
HWND hDesk = ::GetDesktopWindow();
|
||||
CRect top;
|
||||
::GetWindowRect(hDesk, &top);
|
||||
top.right -= 2*::GetSystemMetrics(SM_CYCAPTION);
|
||||
top.bottom -= 3*::GetSystemMetrics(SM_CYCAPTION);
|
||||
|
||||
CRect client, window;
|
||||
Int borderX = ::GetSystemMetrics(SM_CXEDGE);
|
||||
// Int borderY = ::GetSystemMetrics(SM_CYEDGE);
|
||||
Int viewWidth = ::AfxGetApp()->GetProfileInt(MAIN_FRAME_SECTION, "Width", THREE_D_VIEW_WIDTH);
|
||||
Int viewHeight = ::AfxGetApp()->GetProfileInt(MAIN_FRAME_SECTION, "Height", THREE_D_VIEW_HEIGHT);
|
||||
WbView3d * pView = CWorldBuilderDoc::GetActive3DView();
|
||||
if (pView) {
|
||||
pView->GetClientRect(&client);
|
||||
} else {
|
||||
GetClientRect(&client);
|
||||
client.right -= 2*borderX;
|
||||
}
|
||||
int widthDelta = client.Width() - (viewWidth);
|
||||
int heightDelta = client.Height() - (viewHeight);
|
||||
this->GetWindowRect(window);
|
||||
Int newWidth = window.Width()-widthDelta;
|
||||
Int newHeight = window.Height()-heightDelta;
|
||||
this->SetWindowPos(NULL, 0,
|
||||
0, newWidth, newHeight,
|
||||
SWP_NOMOVE|SWP_NOZORDER); // MainFrm.cpp sets the top and left.
|
||||
if (pView) {
|
||||
pView->reset3dEngineDisplaySize(viewWidth, viewHeight);
|
||||
}
|
||||
m_3dViewWidth = viewWidth;
|
||||
}
|
||||
|
||||
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
|
||||
{
|
||||
if( !CFrameWnd::PreCreateWindow(cs) )
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CMainFrame::ResetWindowPositions(void)
|
||||
{
|
||||
if (m_curOptions == NULL) {
|
||||
m_curOptions = &m_brushOptions;
|
||||
}
|
||||
SetWindowPos(NULL, 20, 20, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
|
||||
ShowWindow(SW_SHOW);
|
||||
m_curOptions->SetWindowPos(NULL, 40, 40, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
|
||||
m_curOptions->ShowWindow(SW_SHOW);
|
||||
CView *pView = CWorldBuilderDoc::GetActive2DView();
|
||||
if (pView) {
|
||||
CWnd *pParent = pView->GetParentFrame();
|
||||
if (pParent) {
|
||||
pParent->SetWindowPos(NULL, 60, 60, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
|
||||
}
|
||||
}
|
||||
CPoint pos(20,200);
|
||||
|
||||
this->FloatControlBar(&m_floatingToolBar, pos, CBRS_ALIGN_LEFT);
|
||||
m_floatingToolBar.SetWindowPos(NULL, pos.x, pos.y, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
|
||||
m_floatingToolBar.ShowWindow(SW_SHOW);
|
||||
}
|
||||
|
||||
void CMainFrame::showOptionsDialog(Int dialogID)
|
||||
{
|
||||
CWnd *newOptions = NULL;
|
||||
switch(dialogID) {
|
||||
case IDD_BRUSH_OPTIONS : newOptions = &m_brushOptions; break;
|
||||
case IDD_TERRAIN_MATERIAL: newOptions = &m_terrainMaterial; break;
|
||||
case IDD_BLEND_MATERIAL: newOptions = &m_blendMaterial; break;
|
||||
case IDD_OBJECT_OPTIONS: newOptions = &m_objectOptions; break;
|
||||
case IDD_FENCE_OPTIONS: newOptions = &m_fenceOptions; break;
|
||||
case IDD_MAPOBJECT_PROPS: newOptions = &m_mapObjectProps; break;
|
||||
case IDD_ROAD_OPTIONS:newOptions = &m_roadOptions; break;
|
||||
case IDD_MOUND_OPTIONS:newOptions = &m_moundOptions; break;
|
||||
case IDD_RULER_OPTIONS:newOptions = &m_rulerOptions; break;
|
||||
case IDD_FEATHER_OPTIONS:newOptions = &m_featherOptions; break;
|
||||
case IDD_MESHMOLD_OPTIONS:newOptions = &m_meshMoldOptions; break;
|
||||
case IDD_WAYPOINT_OPTIONS:newOptions = &m_waypointOptions; break;
|
||||
case IDD_WATER_OPTIONS:newOptions = &m_waterOptions; break;
|
||||
case IDD_LIGHT_OPTIONS:newOptions = &m_lightOptions; break;
|
||||
case IDD_BUILD_LIST_PANEL:newOptions = &m_buildListOptions; break;
|
||||
case IDD_GROVE_OPTIONS:newOptions = &m_groveOptions; break;
|
||||
case IDD_RAMP_OPTIONS:newOptions = &m_rampOptions; break;
|
||||
case IDD_SCORCH_OPTIONS:newOptions = &m_scorchOptions; break;
|
||||
case IDD_NO_OPTIONS:newOptions = &m_noOptions; break;
|
||||
default : break;
|
||||
}
|
||||
CRect frameRect;
|
||||
if (newOptions && newOptions != m_curOptions) {
|
||||
newOptions->GetWindowRect(&frameRect);
|
||||
if (m_curOptions) {
|
||||
m_curOptions->GetWindowRect(&frameRect);
|
||||
}
|
||||
newOptions->SetWindowPos(m_curOptions, frameRect.left, frameRect.top,
|
||||
m_optionsPanelWidth, m_optionsPanelHeight,
|
||||
SWP_NOZORDER | SWP_NOACTIVATE );
|
||||
::AfxGetApp()->WriteProfileInt(OPTIONS_PANEL_SECTION, "Top", frameRect.top);
|
||||
::AfxGetApp()->WriteProfileInt(OPTIONS_PANEL_SECTION, "Left", frameRect.left);
|
||||
newOptions->ShowWindow(SW_SHOWNA);
|
||||
if (m_curOptions) {
|
||||
m_curOptions->ShowWindow(SW_HIDE);
|
||||
}
|
||||
m_curOptions = newOptions;
|
||||
}
|
||||
}
|
||||
|
||||
void CMainFrame::OnEditGloballightoptions()
|
||||
{
|
||||
m_globalLightOptions.ShowWindow(SW_SHOWNA);
|
||||
}
|
||||
|
||||
void CMainFrame::onEditScripts()
|
||||
{
|
||||
if (m_scriptDialog) {
|
||||
// Delete the old one since it is no longer valid.
|
||||
delete m_scriptDialog;
|
||||
}
|
||||
|
||||
CRect frameRect;
|
||||
GetWindowRect(&frameRect);
|
||||
|
||||
// Setup the Script Dialog.
|
||||
// This needs to be recreated each time so that it will have the current data.
|
||||
m_scriptDialog = new ScriptDialog(this);
|
||||
m_scriptDialog->Create(IDD_ScriptDialog, this);
|
||||
m_scriptDialog->SetWindowPos(NULL, frameRect.left, frameRect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
|
||||
m_scriptDialog->GetWindowRect(&frameRect);
|
||||
m_scriptDialog->ShowWindow(SW_SHOWNA);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMainFrame diagnostics
|
||||
|
||||
#ifdef _DEBUG
|
||||
void CMainFrame::AssertValid() const
|
||||
{
|
||||
CFrameWnd::AssertValid();
|
||||
}
|
||||
|
||||
void CMainFrame::Dump(CDumpContext& dc) const
|
||||
{
|
||||
CFrameWnd::Dump(dc);
|
||||
}
|
||||
|
||||
#endif //_DEBUG
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMainFrame message handlers
|
||||
|
||||
|
||||
#if DEAD
|
||||
void CMainFrame::OnEditContouroptions()
|
||||
{
|
||||
ContourOptions contourOptsDialog(this);
|
||||
contourOptsDialog.DoModal();
|
||||
}
|
||||
#endif
|
||||
|
||||
void CMainFrame::OnMove(int x, int y)
|
||||
{
|
||||
CFrameWnd::OnMove(x, y);
|
||||
if (this->IsWindowVisible() && !this->IsIconic()) {
|
||||
CRect frameRect;
|
||||
GetWindowRect(&frameRect);
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Top", frameRect.top);
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Left", frameRect.left);
|
||||
}
|
||||
}
|
||||
|
||||
void CMainFrame::OnViewBrushfeedback()
|
||||
{
|
||||
if (DrawObject::isFeedbackEnabled()) {
|
||||
DrawObject::disableFeedback();
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "ShowBrushFeedback", 0);
|
||||
} else {
|
||||
DrawObject::enableFeedback();
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "ShowBrushFeedback", 1);
|
||||
}
|
||||
}
|
||||
|
||||
void CMainFrame::OnUpdateViewBrushfeedback(CCmdUI* pCmdUI)
|
||||
{
|
||||
pCmdUI->SetCheck(DrawObject::isFeedbackEnabled()?1:0);
|
||||
}
|
||||
|
||||
void CMainFrame::OnDestroy()
|
||||
{
|
||||
if (m_hAutoSaveTimer) {
|
||||
KillTimer(m_hAutoSaveTimer);
|
||||
}
|
||||
m_hAutoSaveTimer = NULL;
|
||||
CFrameWnd::OnDestroy();
|
||||
}
|
||||
|
||||
void CMainFrame::OnTimer(UINT nIDEvent)
|
||||
{
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc && pDoc->needAutoSave()) {
|
||||
m_autoSaving = true;
|
||||
HCURSOR old = SetCursor(::LoadCursor(0, IDC_WAIT));
|
||||
SetMessageText("Auto Saving map...");
|
||||
pDoc->autoSave();
|
||||
if (old) SetCursor(old);
|
||||
SetMessageText("Auto Save Complete.");
|
||||
m_autoSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CMainFrame::OnEditCameraoptions()
|
||||
{
|
||||
m_cameraOptions.ShowWindow(SW_SHOWNA);
|
||||
}
|
||||
|
||||
void CMainFrame::handleCameraChange(void)
|
||||
{
|
||||
m_cameraOptions.update();
|
||||
}
|
||||
|
||||
431
GeneralsMD/Code/Tools/WorldBuilder/src/MapPreview.cpp
Normal file
431
GeneralsMD/Code/Tools/WorldBuilder/src/MapPreview.cpp
Normal file
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FILE: MapPreview.cpp /////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Electronic Arts Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2002 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// created: Oct 2002
|
||||
//
|
||||
// Filename: MapPreview.cpp
|
||||
//
|
||||
// author: Chris Huybregts
|
||||
//
|
||||
// purpose: Contains the code used to generate and save the map preview to
|
||||
// the map file. (Original code was taken from the Radar code in
|
||||
// game engine).
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "MapPreview.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
#include "Common/MapReaderWriterInfo.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "WWLib/Targa.h"
|
||||
#include "common/DataChunk.h"
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEFINES ////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool localIsUnderwater( Real x, Real y);
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
MapPreview::MapPreview(void )
|
||||
{
|
||||
memset(m_pixelBuffer, 0xffffffff, sizeof(m_pixelBuffer));
|
||||
}
|
||||
|
||||
void MapPreview::save( CString mapName )
|
||||
{
|
||||
CString newStr = mapName;
|
||||
newStr.Replace(".map", ".tga");
|
||||
buildMapPreviewTexture( newStr );
|
||||
|
||||
/*
|
||||
chunkWriter.openDataChunk("MapPreview", K_MAPPREVIEW_VERSION_1);
|
||||
chunkWriter.writeInt(MAP_PREVIEW_WIDTH);
|
||||
chunkWriter.writeInt(MAP_PREVIEW_HEIGHT);
|
||||
DEBUG_LOG(("BeginMapPreviewInfo\n"));
|
||||
for(Int i = 0; i < MAP_PREVIEW_HEIGHT; ++i)
|
||||
{
|
||||
for(Int j = 0; j < MAP_PREVIEW_WIDTH; ++j)
|
||||
{
|
||||
chunkWriter.writeInt(m_pixelBuffer[i][j]);
|
||||
DEBUG_LOG(("x:%d, y:%d, %X\n", j, i, m_pixelBuffer[i][j]));
|
||||
}
|
||||
}
|
||||
chunkWriter.closeDataChunk();
|
||||
DEBUG_LOG(("EndMapPreviewInfo\n"));
|
||||
*/
|
||||
}
|
||||
|
||||
void MapPreview::interpolateColorForHeight( RGBColor *color,
|
||||
Real height,
|
||||
Real hiZ,
|
||||
Real midZ,
|
||||
Real loZ )
|
||||
{
|
||||
const Real howBright = 0.30f; // bigger is brighter (0.0 to 1.0)
|
||||
const Real howDark = 0.60f; // bigger is darker (0.0 to 1.0)
|
||||
|
||||
// sanity on map height (flat maps bomb)
|
||||
if (hiZ == midZ)
|
||||
hiZ = midZ+0.1f;
|
||||
if (midZ == loZ)
|
||||
loZ = midZ-0.1f;
|
||||
if (hiZ == loZ)
|
||||
hiZ = loZ+0.2f;
|
||||
|
||||
// Real heightPercent = height / (hiZ - loZ);
|
||||
Real t;
|
||||
RGBColor colorTarget;
|
||||
|
||||
// if "over" the middle height, interpolate lighter
|
||||
if( height >= midZ )
|
||||
{
|
||||
|
||||
// how far are we from the middleZ towards the hi Z
|
||||
t = (height - midZ) / (hiZ - midZ);
|
||||
|
||||
// compute what our "lightest" color possible we want to use is
|
||||
colorTarget.red = color->red + (1.0f - color->red) * howBright;
|
||||
colorTarget.green = color->green + (1.0f - color->green) * howBright;
|
||||
colorTarget.blue = color->blue + (1.0f - color->blue) * howBright;
|
||||
|
||||
} // end if
|
||||
else // interpolate darker
|
||||
{
|
||||
|
||||
// how far are we from the middleZ towards the low Z
|
||||
t = (midZ - height) / (midZ - loZ);
|
||||
|
||||
// compute what the "darkest" color possible we want to use is
|
||||
colorTarget.red = color->red + (0.0f - color->red) * howDark;
|
||||
colorTarget.green = color->green + (0.0f - color->green) * howDark;
|
||||
colorTarget.blue = color->blue + (0.0f - color->blue) * howDark;
|
||||
|
||||
} // end else
|
||||
|
||||
// interpolate toward the target color
|
||||
color->red = color->red + (colorTarget.red - color->red) * t;
|
||||
color->green = color->green + (colorTarget.green - color->green) * t;
|
||||
color->blue = color->blue + (colorTarget.blue - color->blue) * t;
|
||||
|
||||
// keep the color real
|
||||
if( color->red < 0.0f )
|
||||
color->red = 0.0f;
|
||||
if( color->red > 1.0f )
|
||||
color->red = 1.0f;
|
||||
if( color->green < 0.0f )
|
||||
color->green = 0.0f;
|
||||
if( color->green > 1.0f )
|
||||
color->green = 1.0f;
|
||||
if( color->blue < 0.0f )
|
||||
color->blue = 0.0f;
|
||||
if( color->blue > 1.0f )
|
||||
color->blue = 1.0f;
|
||||
|
||||
} // end interpolateColorForHeight
|
||||
|
||||
Bool MapPreview::mapPreviewToWorld(const ICoord2D *radar, Coord3D *world)
|
||||
{
|
||||
Int x, y;
|
||||
|
||||
// sanity
|
||||
if( radar == NULL || world == NULL )
|
||||
return FALSE;
|
||||
|
||||
// get the coords
|
||||
x = radar->x;
|
||||
y = radar->y;
|
||||
|
||||
// more sanity
|
||||
if( x < 0 )
|
||||
x = 0;
|
||||
if( x >= MAP_PREVIEW_WIDTH )
|
||||
x = MAP_PREVIEW_WIDTH - 1;
|
||||
if( y < 0 )
|
||||
y = 0;
|
||||
if( y >= MAP_PREVIEW_HEIGHT )
|
||||
y = MAP_PREVIEW_HEIGHT - 1;
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
Real xSample, ySample;
|
||||
|
||||
xSample = INT_TO_REAL(pMap->getXExtent() - (2 * pMap->getBorderSize())) / MAP_PREVIEW_WIDTH;
|
||||
ySample = INT_TO_REAL(pMap->getYExtent() - (2 * pMap->getBorderSize())) / MAP_PREVIEW_HEIGHT;
|
||||
// translate to world
|
||||
world->x = x * xSample + pMap->getBorderSize();
|
||||
world->y = y * ySample + pMap->getBorderSize();
|
||||
|
||||
// find the terrain height here
|
||||
// world->z = pMap->gethigh
|
||||
// TheTerrainLogic->getGroundHeight( world->x, world->y );
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void MapPreview::buildMapPreviewTexture( CString tgaName )
|
||||
{
|
||||
// SurfaceClass *surface;
|
||||
RGBColor waterColor;
|
||||
|
||||
// we will want to reconstruct our new view box now
|
||||
//m_reconstructViewBox = TRUE;
|
||||
|
||||
// setup our water color
|
||||
waterColor.red = 0.55f;
|
||||
waterColor.green = 0.55f;
|
||||
waterColor.blue = 1.0f;
|
||||
|
||||
// fill the terrain texture with a representation of the terrain
|
||||
// Real mapDepth = m_mapExtent.depth();
|
||||
|
||||
// build the terrain
|
||||
RGBColor sampleColor;
|
||||
RGBColor color;
|
||||
Int i, j, samples;
|
||||
Int x, y, z;
|
||||
ICoord2D radarPoint, boundary;
|
||||
Coord3D worldPoint;
|
||||
Real getTerrainAverageZ = 0;
|
||||
Real maxHeight = 0;
|
||||
Real minHeight = 100.0f;
|
||||
Real tempHeight = 0;
|
||||
Int terrainCount = 0;
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
pMap->getBoundary(0, &boundary );
|
||||
for(i = 0; i < MAP_PREVIEW_HEIGHT; ++i)
|
||||
{
|
||||
for(j = 0; j < MAP_PREVIEW_WIDTH; ++j)
|
||||
{
|
||||
radarPoint.x = j;
|
||||
radarPoint.y = i;
|
||||
mapPreviewToWorld( &radarPoint, &worldPoint );
|
||||
tempHeight = pMap->getHeight(worldPoint.x, worldPoint.y);
|
||||
getTerrainAverageZ += tempHeight;
|
||||
if(tempHeight > maxHeight)
|
||||
maxHeight = tempHeight;
|
||||
if(tempHeight < minHeight)
|
||||
minHeight = tempHeight;
|
||||
++terrainCount;
|
||||
}
|
||||
}
|
||||
getTerrainAverageZ = getTerrainAverageZ /terrainCount;
|
||||
|
||||
|
||||
|
||||
// Color paddingColor = GameMakeColor( 100, 100, 100, 255 );
|
||||
for( y = 0; y < MAP_PREVIEW_HEIGHT; y++ )
|
||||
{
|
||||
|
||||
for( x = 0; x < MAP_PREVIEW_WIDTH; x++ )
|
||||
{
|
||||
|
||||
// what point are we inspecting
|
||||
radarPoint.x = x;
|
||||
radarPoint.y = y;
|
||||
mapPreviewToWorld( &radarPoint, &worldPoint );
|
||||
|
||||
|
||||
// get height of the terrain at this sample point
|
||||
z = pMap->getHeight(worldPoint.x, worldPoint.y);
|
||||
|
||||
// create a color based on the Z height of the map
|
||||
//Real waterZ;
|
||||
|
||||
if( localIsUnderwater( MAP_XY_FACTOR *(worldPoint.x - pMap->getBorderSize()), MAP_XY_FACTOR * ( worldPoint.y - pMap->getBorderSize())) )
|
||||
{
|
||||
const Int waterSamplesAway = 1; // how many "tiles" from the center tile we will sample away
|
||||
// to average a color for the tile color
|
||||
|
||||
sampleColor.red = sampleColor.green = sampleColor.blue = 0.0f;
|
||||
samples = 0;
|
||||
|
||||
for( j = y - waterSamplesAway; j <= y + waterSamplesAway; j++ )
|
||||
{
|
||||
|
||||
if( j >= 0 && j < MAP_PREVIEW_HEIGHT )
|
||||
{
|
||||
|
||||
for( i = x - waterSamplesAway; i <= x + waterSamplesAway; i++ )
|
||||
{
|
||||
|
||||
if( i >= 0 && i < MAP_PREVIEW_WIDTH )
|
||||
{
|
||||
|
||||
// the the world point we are concerned with
|
||||
radarPoint.x = i;
|
||||
radarPoint.y = j;
|
||||
mapPreviewToWorld( &radarPoint, &worldPoint );
|
||||
|
||||
// get Z at this sample height
|
||||
Real underwaterZ = pMap->getHeight(worldPoint.x, worldPoint.y);
|
||||
|
||||
// get color for this Z and add to our sample color
|
||||
if( localIsUnderwater( MAP_XY_FACTOR *(worldPoint.x - pMap->getBorderSize()), MAP_XY_FACTOR * ( worldPoint.y - pMap->getBorderSize())))
|
||||
{
|
||||
|
||||
// this is our "color" for water
|
||||
color = waterColor;
|
||||
|
||||
// interpolate the water color for height in the water table
|
||||
interpolateColorForHeight( &color, underwaterZ, pMap->getMaxHeightValue(),getTerrainAverageZ, pMap->getMinHeightValue() );
|
||||
|
||||
// add color to our samples
|
||||
sampleColor.red += color.red;
|
||||
sampleColor.green += color.green;
|
||||
sampleColor.blue += color.blue;
|
||||
samples++;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for j
|
||||
|
||||
// prevent divide by zeros
|
||||
if( samples == 0 )
|
||||
samples = 1;
|
||||
|
||||
// set the color to an average of the colors read
|
||||
color.red = sampleColor.red / (Real)samples;
|
||||
color.green = sampleColor.green / (Real)samples;
|
||||
color.blue = sampleColor.blue / (Real)samples;
|
||||
|
||||
} // end if
|
||||
else // regular terrain ...
|
||||
{
|
||||
const Int samplesAway = 1; // how many "tiles" from the center tile we will sample away
|
||||
// to average a color for the tile color
|
||||
|
||||
sampleColor.red = sampleColor.green = sampleColor.blue = 0.0f;
|
||||
samples = 0;
|
||||
|
||||
for( j = y - samplesAway; j <= y + samplesAway; j++ )
|
||||
{
|
||||
|
||||
if( j >= 0 && j < MAP_PREVIEW_HEIGHT )
|
||||
{
|
||||
|
||||
for( i = x - samplesAway; i <= x + samplesAway; i++ )
|
||||
{
|
||||
|
||||
if( i >= 0 && i < MAP_PREVIEW_WIDTH )
|
||||
{
|
||||
|
||||
// the the world point we are concerned with
|
||||
radarPoint.x = i;
|
||||
radarPoint.y = j;
|
||||
// radarPoint.x = x;
|
||||
// radarPoint.y = y;
|
||||
mapPreviewToWorld( &radarPoint, &worldPoint );
|
||||
|
||||
// get the color at this point
|
||||
pMap->getTerrainColorAt( MAP_XY_FACTOR *(worldPoint.x - pMap->getBorderSize()) ,MAP_XY_FACTOR * ( worldPoint.y - pMap->getBorderSize()), &color );
|
||||
|
||||
// interpolate the color for height
|
||||
interpolateColorForHeight( &color, z,
|
||||
maxHeight, getTerrainAverageZ, minHeight);
|
||||
//pMap->getMaxHeightValue(),getTerrainAverageZ, pMap->getMinHeightValue() );
|
||||
|
||||
// add color to our sample
|
||||
sampleColor.red += color.red;
|
||||
sampleColor.green += color.green;
|
||||
sampleColor.blue += color.blue;
|
||||
samples++;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for j
|
||||
|
||||
// prevent divide by zeros
|
||||
if( samples == 0 )
|
||||
samples = 1;
|
||||
|
||||
// set the color to an average of the colors read
|
||||
color.red = sampleColor.red / (Real)samples;
|
||||
color.green = sampleColor.green / (Real)samples;
|
||||
color.blue = sampleColor.blue / (Real)samples;
|
||||
|
||||
} // end else
|
||||
|
||||
//
|
||||
// draw the pixel for the terrain at this point, note that because of the orientation
|
||||
// of our world we draw it with positive y in the "up" direction
|
||||
//
|
||||
// FYI: I tried making this faster by pulling out all the code inside DrawPixel
|
||||
// and locking only once ... but it made absolutely *no* performance difference,
|
||||
// the sampling and interpolation algorithm for generating pretty looking terrain
|
||||
// and water for the radar is just, well, expensive.
|
||||
//
|
||||
m_pixelBuffer[y][x]= 255 | (REAL_TO_INT(color.red *255) << 8) | (REAL_TO_INT(color.green *255) << 16) | REAL_TO_INT(color.blue * 255)<< 24;
|
||||
|
||||
} // end for x
|
||||
|
||||
} // end for y
|
||||
{
|
||||
Targa tga;
|
||||
tga.Header.Width = MAP_PREVIEW_WIDTH;
|
||||
tga.Header.Height = MAP_PREVIEW_HEIGHT;
|
||||
tga.Header.PixelDepth = 32;
|
||||
tga.Header.ImageType = TGA_TRUECOLOR;
|
||||
tga.SetImage((char *)m_pixelBuffer);
|
||||
tga.Save(tgaName,TGAF_IMAGE, FALSE);
|
||||
}
|
||||
|
||||
} // end buildTerrainTexture
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
166
GeneralsMD/Code/Tools/WorldBuilder/src/MapSettings.cpp
Normal file
166
GeneralsMD/Code/Tools/WorldBuilder/src/MapSettings.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// MapSettings.cpp : implementation file
|
||||
//
|
||||
|
||||
#define DEFINE_TIME_OF_DAY_NAMES
|
||||
#define DEFINE_WEATHER_NAMES
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "MapSettings.h"
|
||||
#include "Common\GlobalData.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Compression.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// MapSettings dialog
|
||||
|
||||
|
||||
MapSettings::MapSettings(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(MapSettings::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(MapSettings)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void MapSettings::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(MapSettings)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(MapSettings, CDialog)
|
||||
//{{AFX_MSG_MAP(MapSettings)
|
||||
ON_CBN_SELENDOK(IDC_MAP_TIMEOFDAY, OnChangeMapTimeofday)
|
||||
ON_CBN_SELENDOK(IDC_MAP_WEATHER, OnChangeMapWeather)
|
||||
ON_EN_CHANGE(IDC_MAP_TITLE, OnChangeMapTitle)
|
||||
ON_CBN_SELENDOK(IDC_MAP_COMPRESSION, OnChangeMapCompression)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// MapSettings message handlers
|
||||
|
||||
void MapSettings::OnChangeMapTimeofday()
|
||||
{
|
||||
}
|
||||
|
||||
void MapSettings::OnChangeMapWeather()
|
||||
{
|
||||
}
|
||||
|
||||
BOOL MapSettings::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
Int i;
|
||||
|
||||
CComboBox *timeofday = (CComboBox*)GetDlgItem(IDC_MAP_TIMEOFDAY);
|
||||
timeofday->ResetContent();
|
||||
for (i = TIME_OF_DAY_FIRST; i < TIME_OF_DAY_COUNT; i++)
|
||||
{
|
||||
timeofday->AddString(TimeOfDayNames[i]);
|
||||
}
|
||||
timeofday->SetCurSel(TheGlobalData->m_timeOfDay-TIME_OF_DAY_FIRST);
|
||||
|
||||
|
||||
CComboBox *weather = (CComboBox*)GetDlgItem(IDC_MAP_WEATHER);
|
||||
weather->ResetContent();
|
||||
for (i = 0; i < WEATHER_COUNT; i++)
|
||||
{
|
||||
weather->AddString(WeatherNames[i]);
|
||||
}
|
||||
weather->SetCurSel(TheGlobalData->m_weather);
|
||||
|
||||
Dict *worldDict = MapObject::getWorldDict();
|
||||
Bool exists = false;
|
||||
AsciiString mapName = worldDict->getAsciiString(TheKey_mapName, &exists);
|
||||
CEdit *mapTitle = (CEdit *)GetDlgItem(IDC_MAP_TITLE);
|
||||
if (exists)
|
||||
{
|
||||
mapTitle->SetWindowText(mapName.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
mapTitle->SetWindowText("");
|
||||
}
|
||||
|
||||
CComboBox *compressionComboBox = (CComboBox*)GetDlgItem(IDC_MAP_COMPRESSION);
|
||||
compressionComboBox->ResetContent();
|
||||
for (i = COMPRESSION_MIN; i <= COMPRESSION_MAX; i++)
|
||||
{
|
||||
compressionComboBox->AddString(CompressionManager::getCompressionNameByType((CompressionType)i));
|
||||
}
|
||||
exists = FALSE;
|
||||
Int index = worldDict->getInt(TheKey_compression, &exists);
|
||||
if (!exists)
|
||||
index = CompressionManager::getPreferredCompression();
|
||||
compressionComboBox->SetCurSel(index);
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void MapSettings::OnOK()
|
||||
{
|
||||
CComboBox *timeofday = (CComboBox*)GetDlgItem(IDC_MAP_TIMEOFDAY);
|
||||
CComboBox *weather = (CComboBox*)GetDlgItem(IDC_MAP_WEATHER);
|
||||
TimeOfDay tod = (TimeOfDay) (timeofday->GetCurSel()+TIME_OF_DAY_FIRST);
|
||||
Weather theWeather = (Weather) weather->GetCurSel();
|
||||
|
||||
TheWritableGlobalData->setTimeOfDay(tod);
|
||||
TheWritableGlobalData->m_weather = theWeather;
|
||||
|
||||
CEdit *mapTitle = (CEdit *)GetDlgItem(IDC_MAP_TITLE);
|
||||
char munkee[256];
|
||||
AsciiString mapName;
|
||||
mapTitle->GetWindowText(munkee, 256);
|
||||
mapName = munkee;
|
||||
Dict *worldDict = MapObject::getWorldDict();
|
||||
worldDict->setAsciiString(TheKey_mapName, mapName);
|
||||
|
||||
CComboBox *compressionComboBox = (CComboBox*)GetDlgItem(IDC_MAP_COMPRESSION);
|
||||
CompressionType compType = (CompressionType) (compressionComboBox->GetCurSel()+COMPRESSION_MIN);
|
||||
worldDict->setInt(TheKey_compression, compType);
|
||||
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
void MapSettings::OnChangeMapTitle()
|
||||
{
|
||||
// TODO: If this is a RICHEDIT control, the control will not
|
||||
// send this notification unless you override the CDialog::OnInitDialog()
|
||||
// function and call CRichEditCtrl().SetEventMask()
|
||||
// with the ENM_CHANGE flag ORed into the mask.
|
||||
|
||||
// TODO: Add your control notification handler code here
|
||||
|
||||
}
|
||||
|
||||
void MapSettings::OnChangeMapCompression()
|
||||
{
|
||||
// TODO: Add your control notification handler code here
|
||||
|
||||
}
|
||||
399
GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldOptions.cpp
Normal file
399
GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldOptions.cpp
Normal file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// MeshMoldOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "worldbuilderDoc.h"
|
||||
#include "MeshMoldOptions.h"
|
||||
#include "Common/FileSystem.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// MeshMoldOptions dialog
|
||||
|
||||
Real MeshMoldOptions::m_currentHeight=0;
|
||||
Real MeshMoldOptions::m_currentScale=1.0f;
|
||||
Int MeshMoldOptions::m_currentAngle=0;
|
||||
MeshMoldOptions * MeshMoldOptions::m_staticThis=NULL;
|
||||
Bool MeshMoldOptions::m_doingPreview=false;
|
||||
Bool MeshMoldOptions::m_raiseOnly=false;
|
||||
Bool MeshMoldOptions::m_lowerOnly=false;
|
||||
|
||||
MeshMoldOptions::MeshMoldOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
//{{AFX_DATA_INIT(MeshMoldOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void MeshMoldOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
COptionsPanel::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(MeshMoldOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
/// Dialog UI initialization.
|
||||
/** Creates the slider controls, and sets the initial values for
|
||||
width and feather in the ui controls. */
|
||||
BOOL MeshMoldOptions::OnInitDialog()
|
||||
{
|
||||
COptionsPanel::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
m_anglePopup.SetupPopSliderButton(this, IDC_ANGLE_POPUP, this);
|
||||
m_scalePopup.SetupPopSliderButton(this, IDC_SCALE_POPUP, this);
|
||||
m_HeightPopup.SetupPopSliderButton(this, IDC_HEIGHT_POPUP, this);
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_MESHMOLD_TREEVIEW);
|
||||
CRect rect(0,0,100,100);
|
||||
if (pWnd) pWnd->GetWindowRect(&rect);
|
||||
|
||||
CButton *pRaise = (CButton *)GetDlgItem(IDC_RAISE_LOWER);
|
||||
if (pRaise) {
|
||||
pRaise->SetCheck(1);
|
||||
}
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_moldTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_TERRAIN_TREEVIEW);
|
||||
m_moldTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
{
|
||||
char dirBuf[_MAX_PATH];
|
||||
char findBuf[_MAX_PATH];
|
||||
char fileBuf[_MAX_PATH];
|
||||
Int i;
|
||||
|
||||
strcpy(dirBuf, ".\\data\\Editor\\Molds");
|
||||
int len = strlen(dirBuf);
|
||||
|
||||
if (len > 0 && dirBuf[len - 1] != '\\') {
|
||||
dirBuf[len++] = '\\';
|
||||
dirBuf[len] = 0;
|
||||
}
|
||||
strcpy(findBuf, dirBuf);
|
||||
strcat(findBuf, "*.w3d");
|
||||
|
||||
FilenameList filenameList;
|
||||
TheFileSystem->getFileListInDirectory(AsciiString(dirBuf), AsciiString("*.w3d"), filenameList, FALSE);
|
||||
|
||||
if (filenameList.size() > 0) {
|
||||
HTREEITEM child = NULL;
|
||||
FilenameList::iterator it = filenameList.begin();
|
||||
do {
|
||||
AsciiString filename = *it;
|
||||
|
||||
len = filename.getLength();
|
||||
if (len<5) continue;
|
||||
strcpy(fileBuf, filename.str());
|
||||
for (i=strlen(fileBuf)-1; i>0; i--) {
|
||||
if (fileBuf[i] == '.') {
|
||||
// strip off .w3d file extension.
|
||||
fileBuf[i] = 0;
|
||||
}
|
||||
}
|
||||
char *nameStart = fileBuf;
|
||||
for (i=0; i<strlen(fileBuf)-1; i++) {
|
||||
if (fileBuf[i] == '\\') {
|
||||
nameStart = fileBuf+i+1;
|
||||
}
|
||||
}
|
||||
|
||||
TVINSERTSTRUCT ins;
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = TVI_ROOT;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = nameStart;
|
||||
ins.item.cchTextMax = strlen(nameStart);
|
||||
child = m_moldTreeView.InsertItem(&ins);
|
||||
|
||||
++it;
|
||||
} while (it != filenameList.end());
|
||||
if (child) m_moldTreeView.SelectItem(child);
|
||||
}
|
||||
}
|
||||
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
setAngle(m_currentAngle);
|
||||
setScale(m_currentScale);
|
||||
setHeight(m_currentHeight);
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void MeshMoldOptions::setHeight(Real height)
|
||||
{
|
||||
char buffer[50];
|
||||
sprintf(buffer, "%.2f", height);
|
||||
m_currentHeight = height;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
}
|
||||
|
||||
void MeshMoldOptions::setScale(Real scale)
|
||||
{
|
||||
char buffer[50];
|
||||
sprintf(buffer, "%d", (int)floor(scale*100));
|
||||
m_currentScale = scale;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SCALE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
}
|
||||
|
||||
void MeshMoldOptions::setAngle(Int angle)
|
||||
{
|
||||
char buffer[50];
|
||||
sprintf(buffer, "%d", angle);
|
||||
m_currentAngle = angle;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_ANGLE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MeshMoldOptions::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_ANGLE_POPUP:
|
||||
*pMin = MIN_ANGLE;
|
||||
*pMax = MAX_ANGLE;
|
||||
*pInitial = m_currentAngle;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_HEIGHT_POPUP:
|
||||
*pMin = MIN_HEIGHT;
|
||||
*pMax = MAX_HEIGHT;
|
||||
*pInitial = floor(m_currentHeight/MAP_HEIGHT_SCALE+0.5f);
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_SCALE_POPUP:
|
||||
*pMin = MIN_SCALE;
|
||||
*pMax = MAX_SCALE;
|
||||
*pInitial = floor(m_currentScale*100 + 0.5f);
|
||||
if (*pInitial > *pMax/2) *pMax = *pInitial*2;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Missing ID."));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void MeshMoldOptions::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit;
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_ANGLE_POPUP:
|
||||
m_currentAngle = theVal;
|
||||
str.Format("%d",m_currentAngle);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_ANGLE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
break;
|
||||
|
||||
case IDC_HEIGHT_POPUP:
|
||||
m_currentHeight = theVal*MAP_HEIGHT_SCALE;
|
||||
str.Format("%.2f",m_currentHeight);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
break;
|
||||
|
||||
case IDC_SCALE_POPUP:
|
||||
m_currentScale = theVal/100.0f; //scale is in %
|
||||
str.Format("%d",theVal);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_SCALE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_CRASH(("Missing ID."));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void MeshMoldOptions::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_ANGLE_POPUP:
|
||||
break;
|
||||
case IDC_HEIGHT_POPUP:
|
||||
break;
|
||||
case IDC_SCALE_POPUP:
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_CRASH(("Missing ID."));
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(MeshMoldOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(MeshMoldOptions)
|
||||
ON_BN_CLICKED(IDC_PREVIEW, OnPreview)
|
||||
ON_BN_CLICKED(IDC_APPLY_MESH, OnApplyMesh)
|
||||
ON_EN_CHANGE(IDC_SCALE_EDIT, OnChangeScaleEdit)
|
||||
ON_EN_CHANGE(IDC_HEIGHT_EDIT, OnChangeHeightEdit)
|
||||
ON_EN_CHANGE(IDC_ANGLE_EDIT, OnChangeAngleEdit)
|
||||
ON_BN_CLICKED(IDC_RAISE, OnRaise)
|
||||
ON_BN_CLICKED(IDC_RAISE_LOWER, OnRaiseLower)
|
||||
ON_BN_CLICKED(IDC_LOWER, OnLower)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// MeshMoldOptions message handlers
|
||||
|
||||
void MeshMoldOptions::OnPreview()
|
||||
{
|
||||
CButton *pButton = (CButton*)this->GetDlgItem(IDC_PREVIEW);
|
||||
m_doingPreview = (pButton->GetCheck() == 1);
|
||||
MeshMoldTool::updateMeshLocation(true);
|
||||
}
|
||||
|
||||
void MeshMoldOptions::OnApplyMesh()
|
||||
{
|
||||
MeshMoldTool::apply(CWorldBuilderDoc::GetActiveDoc());
|
||||
}
|
||||
|
||||
void MeshMoldOptions::OnChangeScaleEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SCALE_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int scale;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &scale)) {
|
||||
m_currentScale = scale/100.0f;
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MeshMoldOptions::OnChangeHeightEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Real height;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%f", &height)) {
|
||||
m_currentHeight = height;
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MeshMoldOptions::OnChangeAngleEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_ANGLE_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int angle;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &angle)) {
|
||||
m_currentAngle = angle;
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOL MeshMoldOptions::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
if (pHdr->hdr.hwndFrom == m_moldTreeView.m_hWnd) {
|
||||
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
char buffer[_MAX_PATH];
|
||||
HTREEITEM hItem = m_moldTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_moldTreeView.GetItem(&item);
|
||||
m_meshModelName = AsciiString(buffer);
|
||||
if (m_doingPreview) {
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return COptionsPanel::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
|
||||
void MeshMoldOptions::OnRaise()
|
||||
{
|
||||
m_raiseOnly = true;
|
||||
m_lowerOnly = false;
|
||||
if (m_doingPreview) {
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshMoldOptions::OnRaiseLower()
|
||||
{
|
||||
m_raiseOnly = false;
|
||||
m_lowerOnly = false;
|
||||
if (m_doingPreview) {
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshMoldOptions::OnLower()
|
||||
{
|
||||
m_raiseOnly = false;
|
||||
m_lowerOnly = true;
|
||||
if (m_doingPreview) {
|
||||
MeshMoldTool::updateMeshLocation(false);
|
||||
}
|
||||
}
|
||||
275
GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldTool.cpp
Normal file
275
GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldTool.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// MeshMoldTool.cpp
|
||||
// Terrain shaping tool for worldbuilder.
|
||||
// Author: John Ahlquist, Oct 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "MeshMoldTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "WorldBuilder.h"
|
||||
#include "DrawObject.h"
|
||||
#include "WbView3d.h"
|
||||
#include "WW3D2/Mesh.h"
|
||||
#include "WW3D2/MeshMdl.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
//
|
||||
// MeshMoldTool class.
|
||||
//
|
||||
|
||||
Bool MeshMoldTool::m_tracking = false;
|
||||
Coord3D MeshMoldTool::m_toolPos;
|
||||
WorldHeightMapEdit *MeshMoldTool::m_htMapEditCopy = NULL;
|
||||
|
||||
/// Constructor
|
||||
MeshMoldTool::MeshMoldTool(void) :
|
||||
Tool(ID_MOLD_TOOL, IDC_MOLD_POINTER)
|
||||
{
|
||||
m_offsettingZ = false;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
MeshMoldTool::~MeshMoldTool(void)
|
||||
{
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
}
|
||||
|
||||
|
||||
/// Shows the mesh mold options panel.
|
||||
void MeshMoldTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_MESHMOLD_OPTIONS);
|
||||
m_prevXIndex = -1;
|
||||
m_prevYIndex = -1;
|
||||
if (m_tracking) {
|
||||
DrawObject::setDoMeshFeedback(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows the mesh mold options panel.
|
||||
void MeshMoldTool::deactivate()
|
||||
{
|
||||
m_tracking = false;
|
||||
DrawObject::setDoMeshFeedback(false);
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
if (MeshMoldOptions::isDoingPreview()) {
|
||||
// Update the height map in case we were doing previews.
|
||||
IRegion2D partialRange = {0,0,0,0};
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc) {
|
||||
pDoc->updateHeightMap(m_htMapEditCopy, false, partialRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Start tool.
|
||||
/** Setup the tool to start mesh molding - make a copy of the height map
|
||||
to edit, another copy because we need it :), and call mouseMovedDown. */
|
||||
void MeshMoldTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
if (!m_tracking) {
|
||||
m_tracking = true;
|
||||
m_toolPos = cpt;
|
||||
Real height = TheTerrainRenderObject->getHeightMapHeight(cpt.x, cpt.y, NULL);
|
||||
MeshMoldOptions::setHeight(height);
|
||||
}
|
||||
DrawObject::setDoMeshFeedback(true);
|
||||
m_prevDocPt = cpt;
|
||||
Bool shiftKey = (0x8000 & ::GetAsyncKeyState(VK_SHIFT))!=0;
|
||||
if (shiftKey) {
|
||||
m_offsettingZ = true;
|
||||
m_prevViewPt = viewPt;
|
||||
} else {
|
||||
m_offsettingZ = false;
|
||||
}
|
||||
|
||||
mouseMoved(m, viewPt, pView, pDoc);
|
||||
}
|
||||
|
||||
/// End tool.
|
||||
/** Finish the tool operation - create a command, pass it to the
|
||||
doc to execute, and cleanup ref'd objects. */
|
||||
void MeshMoldTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
m_offsettingZ = false;
|
||||
}
|
||||
|
||||
/// Track the mouse.
|
||||
void MeshMoldTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) {
|
||||
return;
|
||||
}
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
if (m_offsettingZ) {
|
||||
Real deltaZ = (m_prevViewPt.y - viewPt.y)*MAP_HEIGHT_SCALE*0.5f;
|
||||
MeshMoldOptions::setHeight(MeshMoldOptions::getHeight()+deltaZ);
|
||||
} else {
|
||||
m_toolPos.x += cpt.x-m_prevDocPt.x;
|
||||
m_toolPos.y += cpt.y-m_prevDocPt.y;
|
||||
}
|
||||
updateMeshLocation(false);
|
||||
m_prevViewPt = viewPt;
|
||||
m_prevDocPt = cpt;
|
||||
}
|
||||
|
||||
|
||||
/// Update the mesh location onscreen.
|
||||
void MeshMoldTool::updateMeshLocation(Bool changePreview)
|
||||
{
|
||||
if (m_tracking) {
|
||||
m_toolPos.z = MeshMoldOptions::getHeight();
|
||||
DrawObject::setFeedbackPos(m_toolPos);
|
||||
if (MeshMoldOptions::isDoingPreview() || changePreview) {
|
||||
// doing preview
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (MeshMoldOptions::isDoingPreview()) {
|
||||
applyMesh(pDoc);
|
||||
} else if (m_htMapEditCopy) {
|
||||
// Unapply the mesh :)
|
||||
Int i, j;
|
||||
WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
for (j=0; j<m_htMapEditCopy->getYExtent(); j++) {
|
||||
for (i=0; i<m_htMapEditCopy->getXExtent(); i++) {
|
||||
m_htMapEditCopy->setHeight(i, j, pMap->getHeight(i, j));
|
||||
}
|
||||
}
|
||||
IRegion2D partialRange = {0,0,0,0};
|
||||
pDoc->updateHeightMap(m_htMapEditCopy, false, partialRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply the tool.
|
||||
/** Apply the height mesh mold at the current point. */
|
||||
void MeshMoldTool::apply(CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
applyMesh(pDoc);
|
||||
WBDocUndoable *pUndo = new WBDocUndoable(pDoc, m_htMapEditCopy);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
|
||||
}
|
||||
|
||||
/// Apply the tool.
|
||||
/** Apply the height mesh mold at the current point. */
|
||||
void MeshMoldTool::applyMesh(CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
|
||||
HCURSOR old = SetCursor(::LoadCursor(0, IDC_WAIT));
|
||||
if (m_htMapEditCopy == NULL) {
|
||||
m_htMapEditCopy = pDoc->GetHeightMap()->duplicate();
|
||||
} else {
|
||||
// Restore original heights to edit copy.
|
||||
Int i, j;
|
||||
for (j=0; j<m_htMapEditCopy->getYExtent(); j++) {
|
||||
for (i=0; i<m_htMapEditCopy->getXExtent(); i++) {
|
||||
m_htMapEditCopy->setHeight(i, j, pMap->getHeight(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
Int border = m_htMapEditCopy->getBorderSize();
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
if (p3View) {
|
||||
DrawObject *pDraw = p3View->getDrawObject();
|
||||
if (pDraw) {
|
||||
MeshClass *pMesh = pDraw->peekMesh();
|
||||
if (pMesh) {
|
||||
SphereClass bounds;
|
||||
pDraw->getMeshBounds(&bounds);
|
||||
bounds.Center *= MeshMoldOptions::getScale();
|
||||
bounds.Radius *= MeshMoldOptions::getScale();
|
||||
Int minX = ceil((m_toolPos.x+bounds.Center.X-bounds.Radius)/MAP_XY_FACTOR);
|
||||
Int minY = ceil((m_toolPos.y+bounds.Center.Y-bounds.Radius)/MAP_XY_FACTOR);
|
||||
Int maxX = floor((m_toolPos.x+bounds.Center.X+bounds.Radius)/MAP_XY_FACTOR);
|
||||
Int maxY = floor((m_toolPos.y+bounds.Center.Y+bounds.Radius)/MAP_XY_FACTOR);
|
||||
maxX++; maxY++;
|
||||
if (minX<0) minX = 0;
|
||||
if (minY<0) minY = 0;
|
||||
if (maxX > m_htMapEditCopy->getXExtent()) {
|
||||
maxX = m_htMapEditCopy->getXExtent();
|
||||
}
|
||||
if (maxY > m_htMapEditCopy->getYExtent()) {
|
||||
maxY = m_htMapEditCopy->getYExtent();
|
||||
}
|
||||
Int i, j;
|
||||
for (j=minY; j<maxY; j++) {
|
||||
for (i=minX; i<maxX; i++) {
|
||||
Real X, Y;
|
||||
X = i*MAP_XY_FACTOR;
|
||||
X -= m_toolPos.x;
|
||||
Y = j*MAP_XY_FACTOR;
|
||||
Y -= m_toolPos.y;
|
||||
X /= MeshMoldOptions::getScale();
|
||||
Y /= MeshMoldOptions::getScale();
|
||||
Vector3 vLoc(X, Y, 10000);
|
||||
vLoc.Rotate_Z(-MeshMoldOptions::getAngle()*PI/180.0f);
|
||||
|
||||
LineSegClass ray(vLoc, Vector3(vLoc.X, vLoc.Y, -10000));
|
||||
CastResultStruct castResult;
|
||||
castResult.ComputeContactPoint = true;
|
||||
RayCollisionTestClass rayCollide(ray, &castResult) ;
|
||||
rayCollide.CollisionType = 0xFFFFFFFF;
|
||||
if (pMesh->Cast_Ray(rayCollide)) {
|
||||
// get the point of intersection according to W3D
|
||||
Vector3 intersection = castResult.ContactPoint;
|
||||
intersection.Z *= MeshMoldOptions::getScale();
|
||||
Int newHeight = floor( ((intersection.Z+MeshMoldOptions::getHeight())/MAP_HEIGHT_SCALE)+0.5);
|
||||
// check boundary values
|
||||
if (newHeight < m_htMapEditCopy->getMinHeightValue()) newHeight = m_htMapEditCopy->getMinHeightValue();
|
||||
if (newHeight > m_htMapEditCopy->getMaxHeightValue()) newHeight = m_htMapEditCopy->getMaxHeightValue();
|
||||
Bool apply = true;
|
||||
if (MeshMoldOptions::isRaisingOnly()) {
|
||||
apply = newHeight > m_htMapEditCopy->getHeight(i+border, j+border);
|
||||
} else if (MeshMoldOptions::isLoweringOnly()) {
|
||||
apply = newHeight < m_htMapEditCopy->getHeight(i+border, j+border);
|
||||
}
|
||||
if (apply) {
|
||||
m_htMapEditCopy->setHeight(i+border, j+border, newHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
IRegion2D partialRange;
|
||||
// Offset by 2, because the normals change out two past the actual height change.
|
||||
partialRange.lo.x = minX-2+border;
|
||||
partialRange.hi.x = maxX+2+border;
|
||||
partialRange.lo.y = minY-2+border;
|
||||
partialRange.hi.y = maxY+2+border;
|
||||
pDoc->updateHeightMap(m_htMapEditCopy, true, partialRange);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (old) SetCursor(old);
|
||||
}
|
||||
282
GeneralsMD/Code/Tools/WorldBuilder/src/MoundOptions.cpp
Normal file
282
GeneralsMD/Code/Tools/WorldBuilder/src/MoundOptions.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// MoundOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "MoundOptions.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "MoundTool.h"
|
||||
|
||||
MoundOptions *MoundOptions::m_staticThis = NULL;
|
||||
Int MoundOptions::m_currentWidth = 0;
|
||||
Int MoundOptions::m_currentHeight = 0;
|
||||
Int MoundOptions::m_currentFeather = 0;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// MoundOptions dialog trivial construstor - Create does the real work.
|
||||
|
||||
|
||||
MoundOptions::MoundOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
//{{AFX_DATA_INIT(MoundOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
/// Windows default stuff.
|
||||
void MoundOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(MoundOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
/// Sets the feather value in the dialog.
|
||||
/** Update the value in the edit control and the slider. */
|
||||
void MoundOptions::setFeather(Int feather)
|
||||
{
|
||||
CString buf;
|
||||
buf.Format("%d", feather);
|
||||
m_currentFeather = feather;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_FEATHER_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the brush width value in the dialog.
|
||||
/** Update the value in the edit control and the slider. */
|
||||
void MoundOptions::setWidth(Int width)
|
||||
{
|
||||
CString buf;
|
||||
buf.Format("%d", width);
|
||||
m_currentWidth = width;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void MoundOptions::setHeight(Int height)
|
||||
{
|
||||
char buffer[50];
|
||||
sprintf(buffer, "%d", height);
|
||||
m_currentHeight = height;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// MoundOptions message handlers
|
||||
|
||||
/// Dialog UI initialization.
|
||||
/** Creates the slider controls, and sets the initial values for
|
||||
width and feather in the ui controls. */
|
||||
BOOL MoundOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
m_brushWidthPopup.SetupPopSliderButton(this, IDC_SIZE_POPUP, this);
|
||||
m_brushFeatherPopup.SetupPopSliderButton(this, IDC_FEATHER_POPUP, this);
|
||||
m_brushHeightPopup.SetupPopSliderButton(this, IDC_HEIGHT_POPUP, this);
|
||||
|
||||
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
setFeather(m_currentFeather);
|
||||
setWidth(m_currentWidth);
|
||||
setHeight(m_currentHeight);
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/// Handles feather edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void MoundOptions::OnChangeFeatherEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_FEATHER_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int feather;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &feather)) {
|
||||
m_currentFeather = feather;
|
||||
MoundTool::setFeather(m_currentFeather);
|
||||
sprintf(buffer, "%.1f FEET.", m_currentFeather*MAP_XY_FACTOR);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_FEATHER_LABEL);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void MoundOptions::OnChangeSizeEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int width;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &width)) {
|
||||
m_currentWidth = width;
|
||||
MoundTool::setWidth(m_currentWidth);
|
||||
sprintf(buffer, "%.1f FEET.", m_currentWidth*MAP_XY_FACTOR);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_WIDTH_LABEL);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void MoundOptions::OnChangeHeightEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int height;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &height)) {
|
||||
m_currentHeight = height;
|
||||
MoundTool::setMoundHeight(m_currentHeight);
|
||||
sprintf(buffer, "%.1f FEET.", m_currentHeight*MAP_HEIGHT_SCALE);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_LABEL);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MoundOptions::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_SIZE_POPUP:
|
||||
*pMin = MIN_BRUSH_SIZE;
|
||||
*pMax = MAX_BRUSH_SIZE;
|
||||
*pInitial = m_currentWidth;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_HEIGHT_POPUP:
|
||||
*pMin = MIN_MOUND_HEIGHT;
|
||||
*pMax = MAX_MOUND_HEIGHT;
|
||||
*pInitial = m_currentHeight;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_FEATHER_POPUP:
|
||||
*pMin = MIN_FEATHER;
|
||||
*pMax = MAX_FEATHER;
|
||||
*pInitial = m_currentFeather;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void MoundOptions::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit;
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_SIZE_POPUP:
|
||||
m_currentWidth = theVal;
|
||||
str.Format("%d",m_currentWidth);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
MoundTool::setWidth(m_currentWidth);
|
||||
break;
|
||||
|
||||
case IDC_HEIGHT_POPUP:
|
||||
m_currentHeight = theVal;
|
||||
str.Format("%d",m_currentHeight);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
MoundTool::setMoundHeight(m_currentHeight);
|
||||
break;
|
||||
|
||||
case IDC_FEATHER_POPUP:
|
||||
m_currentFeather = theVal;
|
||||
str.Format("%d",m_currentFeather);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_FEATHER_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
MoundTool::setFeather(m_currentFeather);
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void MoundOptions::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_SIZE_POPUP:
|
||||
break;
|
||||
case IDC_HEIGHT_POPUP:
|
||||
break;
|
||||
case IDC_FEATHER_POPUP:
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(MoundOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(MoundOptions)
|
||||
ON_WM_HSCROLL()
|
||||
ON_EN_CHANGE(IDC_FEATHER_EDIT, OnChangeFeatherEdit)
|
||||
ON_EN_CHANGE(IDC_SIZE_EDIT, OnChangeSizeEdit)
|
||||
ON_EN_CHANGE(IDC_HEIGHT_EDIT, OnChangeHeightEdit)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
247
GeneralsMD/Code/Tools/WorldBuilder/src/MoundTool.cpp
Normal file
247
GeneralsMD/Code/Tools/WorldBuilder/src/MoundTool.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// MoundTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "MoundTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "DrawObject.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "MoundOptions.h"
|
||||
//
|
||||
// MoundTool class.
|
||||
//
|
||||
|
||||
Int MoundTool::m_moundHeight=0;
|
||||
Int MoundTool::m_brushWidth;
|
||||
Int MoundTool::m_brushFeather;
|
||||
|
||||
|
||||
|
||||
/// Constructor
|
||||
MoundTool::MoundTool(void) :
|
||||
Tool(ID_BRUSH_ADD_TOOL, IDC_BRUSH_CROSS)
|
||||
{
|
||||
m_htMapEditCopy = NULL;
|
||||
m_htMapSaveCopy = NULL;
|
||||
m_raising = true;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
MoundTool::~MoundTool(void)
|
||||
{
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
REF_PTR_RELEASE(m_htMapSaveCopy);
|
||||
}
|
||||
|
||||
|
||||
void MoundTool::setMoundHeight(Int height)
|
||||
{
|
||||
if (m_moundHeight != height) {
|
||||
m_moundHeight = height;
|
||||
// notify mound options panel
|
||||
MoundOptions::setHeight(height);
|
||||
DrawObject::setBrushFeedbackParms(false, m_brushWidth, m_brushFeather);
|
||||
}
|
||||
};
|
||||
/// Set the brush width and notify the height options panel of the change.
|
||||
void MoundTool::setWidth(Int width)
|
||||
{
|
||||
if (m_brushWidth != width) {
|
||||
m_brushWidth = width;
|
||||
// notify brush palette options panel
|
||||
MoundOptions::setWidth(width);
|
||||
DrawObject::setBrushFeedbackParms(false, m_brushWidth, m_brushFeather);
|
||||
}
|
||||
};
|
||||
|
||||
/// Set the brush feather and notify the height options panel of the change.
|
||||
void MoundTool::setFeather(Int feather)
|
||||
{
|
||||
if (m_brushFeather != feather) {
|
||||
m_brushFeather = feather;
|
||||
// notify height palette options panel
|
||||
MoundOptions::setFeather(feather);
|
||||
DrawObject::setBrushFeedbackParms(false, m_brushWidth, m_brushFeather);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Shows the brush options panel.
|
||||
void MoundTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_MOUND_OPTIONS);
|
||||
DrawObject::setDoBrushFeedback(true);
|
||||
DrawObject::setBrushFeedbackParms(false, m_brushWidth, m_brushFeather);
|
||||
}
|
||||
|
||||
void MoundTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
// WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
// just in case, release it.
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
m_htMapEditCopy = pDoc->GetHeightMap()->duplicate();
|
||||
m_prevXIndex = -1;
|
||||
m_prevYIndex = -1;
|
||||
REF_PTR_RELEASE(m_htMapSaveCopy);
|
||||
m_htMapSaveCopy = m_htMapEditCopy->duplicate();
|
||||
m_lastMoveTime = ::GetTickCount();
|
||||
m_lastMoveTime -= MIN_DELAY_TIME + 1; // Make the tool fire the first time.
|
||||
mouseMoved(m, viewPt, pView, pDoc);
|
||||
}
|
||||
|
||||
void MoundTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
WBDocUndoable *pUndo = new WBDocUndoable(pDoc, m_htMapEditCopy);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
REF_PTR_RELEASE(m_htMapSaveCopy);
|
||||
}
|
||||
|
||||
void MoundTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
DrawObject::setFeedbackPos(cpt);
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
Int curTime = ::GetTickCount();
|
||||
Int deltaTime = curTime - m_lastMoveTime;
|
||||
if (deltaTime < MIN_DELAY_TIME) return;
|
||||
m_lastMoveTime = curTime;
|
||||
|
||||
|
||||
int brushWidth = m_brushWidth;
|
||||
int setFeather = m_brushFeather;
|
||||
if (setFeather>0) {
|
||||
brushWidth += 2*setFeather;
|
||||
brushWidth += 2; // for round brush.
|
||||
}
|
||||
|
||||
CPoint ndx;
|
||||
getCenterIndex(&cpt, m_brushWidth, &ndx, pDoc);
|
||||
|
||||
// do nothing if in the same cell....
|
||||
// if (m_prevXIndex == ndx.x && m_prevYIndex == ndx.y) return;
|
||||
|
||||
m_prevXIndex = ndx.x;
|
||||
m_prevYIndex = ndx.y;
|
||||
|
||||
int sub = brushWidth/2;
|
||||
int add = brushWidth-sub;
|
||||
|
||||
Int htDelta = m_moundHeight;
|
||||
if (!m_raising) htDelta = -m_moundHeight;
|
||||
// round brush
|
||||
Int i, j;
|
||||
for (i=ndx.x-sub; i<ndx.x+add; i++) {
|
||||
if (i<0 || i>=m_htMapEditCopy->getXExtent()) {
|
||||
continue;
|
||||
}
|
||||
for (j=ndx.y-sub; j<ndx.y+add; j++) {
|
||||
if (j<0 || j>=m_htMapEditCopy->getYExtent()) {
|
||||
continue;
|
||||
}
|
||||
#if 1
|
||||
// New floating point based blending calculation. jba.
|
||||
Real blendFactor;
|
||||
blendFactor = calcRoundBlendFactor(ndx, i, j, m_brushWidth, m_brushFeather);
|
||||
Int curHeight = m_htMapEditCopy->getHeight(i,j);
|
||||
float fNewHeight = (blendFactor*(htDelta+curHeight))+((1.0f-blendFactor)*curHeight);
|
||||
Int newHeight = floor(fNewHeight+0.5f);
|
||||
|
||||
// check boundary values
|
||||
if (newHeight < m_htMapSaveCopy->getMinHeightValue()) newHeight = m_htMapSaveCopy->getMinHeightValue();
|
||||
if (newHeight > m_htMapSaveCopy->getMaxHeightValue()) newHeight = m_htMapSaveCopy->getMaxHeightValue();
|
||||
|
||||
m_htMapEditCopy->setHeight(i, j, newHeight);
|
||||
pDoc->invalCell(i, j);
|
||||
#else
|
||||
// Previous integer calculated mounding and blending.
|
||||
// If not re-enabled by Dec 2001, delete as obsolete.
|
||||
Int xd = abs( (2*(i-(ndx.x-sub)))+1 - brushWidth);
|
||||
Int yd = abs( (2*(j-(ndx.y-sub)))+1 - brushWidth);
|
||||
|
||||
float delta = (float)sqrt(xd*xd+yd*yd);
|
||||
//Int curHeight = m_htMapSaveCopy->getHeight(i,j);
|
||||
Int curHeight = m_htMapEditCopy->getHeight(i,j);
|
||||
Int newHeight = curHeight + htDelta;
|
||||
|
||||
// check boundary values
|
||||
if (newHeight < m_htMapSaveCopy->getMinHeightValue()) newHeight = m_htMapSaveCopy->getMinHeightValue();
|
||||
if (newHeight > m_htMapSaveCopy->getMaxHeightValue()) newHeight = m_htMapSaveCopy->getMaxHeightValue();
|
||||
|
||||
if (delta<m_brushWidth) {
|
||||
m_htMapEditCopy->setHeight(i, j, newHeight);
|
||||
pDoc->invalCell(i, j);
|
||||
} else if (delta<brushWidth+0.7 && setFeather) {
|
||||
Int factor = setFeather+1;
|
||||
factor *= 2;
|
||||
float feather = delta-m_brushWidth;
|
||||
float fNewHeight = ((factor-feather)*(htDelta+curHeight)+(feather*curHeight) )/factor;
|
||||
newHeight = (Int)(fNewHeight);
|
||||
if (newHeight < m_htMapSaveCopy->getMinHeightValue()) newHeight = m_htMapSaveCopy->getMinHeightValue();
|
||||
if (newHeight > m_htMapSaveCopy->getMaxHeightValue()) newHeight = m_htMapSaveCopy->getMaxHeightValue();
|
||||
if (newHeight > m_htMapEditCopy->getHeight(i,j)) {
|
||||
if (htDelta<0) {
|
||||
newHeight = m_htMapEditCopy->getHeight(i,j);
|
||||
}
|
||||
} else {
|
||||
if (htDelta>0) {
|
||||
newHeight = m_htMapEditCopy->getHeight(i,j);
|
||||
}
|
||||
}
|
||||
|
||||
m_htMapEditCopy->setHeight(i, j, newHeight);
|
||||
pDoc->invalCell(i, j);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
IRegion2D partialRange;
|
||||
partialRange.lo.x = ndx.x - brushWidth;
|
||||
partialRange.hi.x = ndx.x + brushWidth;
|
||||
partialRange.lo.y = ndx.y - brushWidth;
|
||||
partialRange.hi.y = ndx.y + brushWidth;
|
||||
pDoc->updateHeightMap(m_htMapEditCopy, true, partialRange);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** DigTool
|
||||
***************************************************************************/
|
||||
/// Constructor
|
||||
DigTool::DigTool(void)
|
||||
{
|
||||
m_toolID = ID_BRUSH_SUBTRACT_TOOL;
|
||||
m_raising = false; // digging.
|
||||
}
|
||||
|
||||
105
GeneralsMD/Code/Tools/WorldBuilder/src/MyToolbar.cpp
Normal file
105
GeneralsMD/Code/Tools/WorldBuilder/src/MyToolbar.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// MyToolBar.cpp
|
||||
// Class to handle cell size slider.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
#include "Lib\BaseType.h"
|
||||
#include "MyToolBar.h"
|
||||
#include "resource.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
|
||||
BEGIN_MESSAGE_MAP(CellSizeToolBar, CDialogBar)
|
||||
//{{AFX_MSG_MAP(CellSizeToolBar)
|
||||
ON_WM_VSCROLL()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
#define MAX_POS 7
|
||||
#define MIN_POS 1
|
||||
|
||||
CellSizeToolBar *CellSizeToolBar::m_staticThis = NULL;
|
||||
|
||||
void CellSizeToolBar::CellSizeChanged(Int cellSize)
|
||||
{
|
||||
Int newSize = 1;
|
||||
Int i;
|
||||
for (i=MIN_POS; i<MAX_POS; i++) {
|
||||
newSize *= 2;
|
||||
if (newSize >= cellSize) break;
|
||||
}
|
||||
i = MAX_POS - i + MIN_POS; // Invert the range
|
||||
if (m_staticThis != NULL) {
|
||||
m_staticThis->m_cellSlider.SetPos(i);
|
||||
}
|
||||
}
|
||||
|
||||
CellSizeToolBar::~CellSizeToolBar(void)
|
||||
{
|
||||
m_staticThis = NULL;
|
||||
}
|
||||
|
||||
void CellSizeToolBar::SetupSlider(void)
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(ID_SLIDER);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
pWnd->DestroyWindow();
|
||||
ScreenToClient(&rect);
|
||||
|
||||
m_cellSlider.Create(TBS_VERT|TBS_AUTOTICKS|TBS_RIGHT, rect, this, ID_SLIDER);
|
||||
m_cellSlider.SetRange(MIN_POS,MAX_POS);
|
||||
m_cellSlider.SetPos(3);
|
||||
m_cellSlider.ShowWindow(SW_SHOW);
|
||||
m_staticThis = this;
|
||||
}
|
||||
|
||||
void CellSizeToolBar::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
||||
{
|
||||
if (nSBCode != TB_THUMBTRACK) {
|
||||
nPos = m_cellSlider.GetPos();
|
||||
}
|
||||
UnsignedInt i;
|
||||
// invert
|
||||
nPos = MAX_POS - nPos + MIN_POS;
|
||||
int newSize = 1;
|
||||
for (i=1; i<nPos; i++) {
|
||||
newSize *= 2;
|
||||
}
|
||||
if (newSize>64) return;
|
||||
|
||||
CWorldBuilderView* pView = CWorldBuilderDoc::GetActive2DView();
|
||||
if (pView == NULL || newSize == pView->getCellSize()) return;
|
||||
pView->setCellSize(newSize);
|
||||
}
|
||||
|
||||
LRESULT CellSizeToolBar::WindowProc( UINT message, WPARAM wParam, LPARAM lParam )
|
||||
{
|
||||
if (message == WM_VSCROLL) {
|
||||
int nScrollCode = (short)LOWORD(wParam);
|
||||
int nPos = (short)HIWORD(wParam);
|
||||
OnVScroll(nScrollCode, nPos, NULL);
|
||||
return(0);
|
||||
}
|
||||
return(CDialogBar::WindowProc(message, wParam, lParam));
|
||||
}
|
||||
227
GeneralsMD/Code/Tools/WorldBuilder/src/NewHeightMap.cpp
Normal file
227
GeneralsMD/Code/Tools/WorldBuilder/src/NewHeightMap.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// NewHeightMap.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "NewHeightMap.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CNewHeightMap dialog
|
||||
|
||||
|
||||
CNewHeightMap::CNewHeightMap(TNewHeightInfo *hiP, const char *label, CWnd* pParent /*=NULL*/)
|
||||
: CDialog(CNewHeightMap::IDD, pParent)
|
||||
{
|
||||
mHeightInfo = *hiP;
|
||||
m_label = label;
|
||||
//{{AFX_DATA_INIT(CNewHeightMap)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void CNewHeightMap::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(CNewHeightMap)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(CNewHeightMap, CDialog)
|
||||
//{{AFX_MSG_MAP(CNewHeightMap)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CNewHeightMap message handlers
|
||||
|
||||
BOOL CNewHeightMap::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CWnd *edit = GetDlgItem(IDC_INITIAL_HEIGHT);
|
||||
CString val;
|
||||
val.Format("%d", mHeightInfo.initialHeight);
|
||||
if (edit) edit->SetWindowText(val);
|
||||
|
||||
edit = GetDlgItem(IDC_X_SIZE);
|
||||
val.Format("%d", mHeightInfo.xExtent);
|
||||
if (edit) edit->SetWindowText(val);
|
||||
|
||||
edit = GetDlgItem(IDC_BORDER_SIZE);
|
||||
val.Format("%d", mHeightInfo.borderWidth);
|
||||
if (edit) edit->SetWindowText(val);
|
||||
|
||||
edit = GetDlgItem(IDC_Y_SIZE);
|
||||
val.Format("%d", mHeightInfo.yExtent);
|
||||
if (edit) edit->SetWindowText(val);
|
||||
if (m_label) SetWindowText(m_label);
|
||||
|
||||
|
||||
CButton *pButton;
|
||||
if (mHeightInfo.forResize) {
|
||||
pButton = (CButton*)GetDlgItem(IDC_CENTER);
|
||||
pButton->SetCheck(1);
|
||||
mHeightInfo.anchorBottom = false;
|
||||
mHeightInfo.anchorTop = false;
|
||||
mHeightInfo.anchorLeft = false;
|
||||
mHeightInfo.anchorRight = false;
|
||||
doAnchorButton(IDC_CENTER);
|
||||
} else {
|
||||
pButton = (CButton*)GetDlgItem(IDC_CENTER);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
pButton = (CButton*)GetDlgItem(IDC_TOP_LEFT);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
pButton = (CButton*)GetDlgItem(IDC_TOP);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
pButton = (CButton*)GetDlgItem(IDC_TOP_RIGHT);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
pButton = (CButton*)GetDlgItem(IDC_CENTER_LEFT);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
pButton = (CButton*)GetDlgItem(IDC_CENTER_RIGHT);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
pButton = (CButton*)GetDlgItem(IDC_BOTTOM);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
pButton = (CButton*)GetDlgItem(IDC_BOTTOM_LEFT);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
pButton = (CButton*)GetDlgItem(IDC_BOTTOM_RIGHT);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
pButton = (CButton*)GetDlgItem(IDC_ANCHOR_LABEL);
|
||||
pButton->ShowWindow(SW_HIDE);
|
||||
}
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/** Handles the button press for this button. */
|
||||
Bool CNewHeightMap::doAnchorButton(Int buttonID)
|
||||
{
|
||||
TNewHeightInfo heightInfo = mHeightInfo;
|
||||
Bool processed = true;
|
||||
heightInfo.anchorBottom = false;
|
||||
heightInfo.anchorTop = false;
|
||||
heightInfo.anchorLeft = false;
|
||||
heightInfo.anchorRight = false;
|
||||
switch(buttonID) {
|
||||
case IDC_CENTER:
|
||||
break;
|
||||
case IDC_TOP_LEFT:
|
||||
heightInfo.anchorTop = true;
|
||||
heightInfo.anchorLeft = true;
|
||||
break;
|
||||
case IDC_TOP:
|
||||
heightInfo.anchorTop = true;
|
||||
break;
|
||||
case IDC_TOP_RIGHT:
|
||||
heightInfo.anchorTop = true;
|
||||
heightInfo.anchorRight = true;
|
||||
break;
|
||||
case IDC_CENTER_LEFT:
|
||||
heightInfo.anchorLeft = true;
|
||||
break;
|
||||
case IDC_CENTER_RIGHT:
|
||||
heightInfo.anchorRight = true;
|
||||
break;
|
||||
case IDC_BOTTOM:
|
||||
heightInfo.anchorBottom = true;
|
||||
break;
|
||||
case IDC_BOTTOM_LEFT:
|
||||
heightInfo.anchorBottom = true;
|
||||
heightInfo.anchorLeft = true;
|
||||
break;
|
||||
case IDC_BOTTOM_RIGHT:
|
||||
heightInfo.anchorBottom = true;
|
||||
heightInfo.anchorRight = true;
|
||||
break;
|
||||
default:
|
||||
processed = false;
|
||||
break;
|
||||
}
|
||||
if (!processed) return(false);
|
||||
mHeightInfo = heightInfo;
|
||||
CButton *pButton;
|
||||
pButton = (CButton*)GetDlgItem(IDC_CENTER);
|
||||
pButton->SetCheck(buttonID==IDC_CENTER?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_TOP_LEFT);
|
||||
pButton->SetCheck(buttonID==IDC_TOP_LEFT?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_TOP);
|
||||
pButton->SetCheck(buttonID==IDC_TOP?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_TOP_RIGHT);
|
||||
pButton->SetCheck(buttonID==IDC_TOP_RIGHT?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_CENTER_LEFT);
|
||||
pButton->SetCheck(buttonID==IDC_CENTER_LEFT?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_CENTER_RIGHT);
|
||||
pButton->SetCheck(buttonID==IDC_CENTER_RIGHT?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_BOTTOM);
|
||||
pButton->SetCheck(buttonID==IDC_BOTTOM?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_BOTTOM_LEFT);
|
||||
pButton->SetCheck(buttonID==IDC_BOTTOM_LEFT?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_BOTTOM_RIGHT);
|
||||
pButton->SetCheck(buttonID==IDC_BOTTOM_RIGHT?1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_ANCHOR_LABEL);
|
||||
pButton->SetCheck(buttonID==IDC_ANCHOR_LABEL?1:0);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CNewHeightMap::OnOK()
|
||||
{
|
||||
CWnd *edit = GetDlgItem(IDC_INITIAL_HEIGHT);
|
||||
CString val;
|
||||
if (edit) {
|
||||
edit->GetWindowText(val);
|
||||
mHeightInfo.initialHeight = atoi(val);
|
||||
}
|
||||
edit = GetDlgItem(IDC_X_SIZE);
|
||||
if (edit) {
|
||||
edit->GetWindowText(val);
|
||||
mHeightInfo.xExtent = atoi(val);
|
||||
}
|
||||
edit = GetDlgItem(IDC_Y_SIZE);
|
||||
if (edit) {
|
||||
edit->GetWindowText(val);
|
||||
mHeightInfo.yExtent = atoi(val);
|
||||
}
|
||||
edit = GetDlgItem(IDC_BORDER_SIZE);
|
||||
if (edit) {
|
||||
edit->GetWindowText(val);
|
||||
mHeightInfo.borderWidth = atoi(val);
|
||||
}
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
|
||||
BOOL CNewHeightMap::OnCommand(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
Int cmd = HIWORD(wParam);
|
||||
if (cmd == BN_CLICKED) {
|
||||
Int idButton = (int) LOWORD(wParam); // identifier of button
|
||||
if (doAnchorButton(idButton)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return CDialog::OnCommand(wParam, lParam);
|
||||
}
|
||||
847
GeneralsMD/Code/Tools/WorldBuilder/src/ObjectOptions.cpp
Normal file
847
GeneralsMD/Code/Tools/WorldBuilder/src/ObjectOptions.cpp
Normal file
@@ -0,0 +1,847 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ObjectOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#define DEFINE_EDITOR_SORTING_NAMES
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "ObjectOptions.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "AddPlayerDialog.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "CUndoable.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingSort.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/FileSystem.h" // for LOAD_TEST_ASSETS
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "GameClient/Color.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
ObjectOptions *ObjectOptions::m_staticThis = NULL;
|
||||
Bool ObjectOptions::m_updating = false;
|
||||
char ObjectOptions::m_currentObjectName[NAME_MAX_LEN];
|
||||
Int ObjectOptions::m_currentObjectIndex=-1;
|
||||
AsciiString ObjectOptions::m_curOwnerName;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ObjectOptions dialog
|
||||
|
||||
|
||||
ObjectOptions::ObjectOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
m_objectsList = NULL;
|
||||
strcpy(m_currentObjectName, "No Selection");
|
||||
m_curOwnerName.clear();
|
||||
//{{AFX_DATA_INIT(ObjectOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
ObjectOptions::~ObjectOptions(void)
|
||||
{
|
||||
if (m_objectsList) {
|
||||
m_objectsList->deleteInstance();
|
||||
}
|
||||
m_objectsList = NULL;
|
||||
}
|
||||
|
||||
|
||||
void ObjectOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(ObjectOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ObjectOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(ObjectOptions)
|
||||
ON_CBN_EDITCHANGE(IDC_OWNINGTEAM, OnEditchangeOwningteam)
|
||||
ON_CBN_CLOSEUP(IDC_OWNINGTEAM, OnCloseupOwningteam)
|
||||
ON_CBN_SELCHANGE(IDC_OWNINGTEAM, OnSelchangeOwningteam)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
static Int findSideListEntryWithPlayerOfSide(AsciiString side)
|
||||
{
|
||||
for (int i = 0; i < TheSidesList->getNumSides(); i++)
|
||||
{
|
||||
AsciiString ptname = TheSidesList->getSideInfo(i)->getDict()->getAsciiString(TheKey_playerFaction);
|
||||
const PlayerTemplate* pt = ThePlayerTemplateStore->findPlayerTemplate(NAMEKEY(ptname));
|
||||
if (pt && pt->getSide() == side)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG_CRASH(("no SideList entry found for %s!\n",side.str()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ObjectOptions data access method.
|
||||
|
||||
/*static*/ void ObjectOptions::update()
|
||||
{
|
||||
if (m_staticThis)
|
||||
m_staticThis->updateLabel();
|
||||
}
|
||||
|
||||
|
||||
void ObjectOptions::updateLabel()
|
||||
{
|
||||
CWnd *pLabel = GetDlgItem(IDC_OBJECT_NAME);
|
||||
if (pLabel) {
|
||||
pLabel->SetWindowText(m_currentObjectName);
|
||||
}
|
||||
|
||||
CComboBox *list = (CComboBox*)GetDlgItem(IDC_OWNINGTEAM);
|
||||
list->ResetContent();
|
||||
|
||||
Bool found = false;
|
||||
AsciiString defTeamName;
|
||||
MapObject *pCur = getCurMapObject();
|
||||
if (!pCur) {
|
||||
// No valid selection. Just return.
|
||||
return;
|
||||
}
|
||||
if (pCur)
|
||||
{
|
||||
const ThingTemplate* tt = pCur->getThingTemplate();
|
||||
if (tt)
|
||||
{
|
||||
AsciiString defPlayerSide = tt->getDefaultOwningSide();
|
||||
Int i = findSideListEntryWithPlayerOfSide(defPlayerSide);
|
||||
if (i >= 0)
|
||||
{
|
||||
defTeamName.set("team");
|
||||
defTeamName.concat(TheSidesList->getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// put it on neutral
|
||||
defTeamName.set("team");
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
// flag the preview for redraw also
|
||||
if (pCur && pCur->getThingTemplate())
|
||||
{
|
||||
m_objectPreview.SetThingTemplate(pCur->getThingTemplate());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_objectPreview.SetThingTemplate(NULL);
|
||||
}
|
||||
m_objectPreview.Invalidate();
|
||||
|
||||
Int neutral = -1;
|
||||
Int sel = -1;
|
||||
for (int i = 0; i < TheSidesList->getNumTeams(); i++)
|
||||
{
|
||||
Dict *d = TheSidesList->getTeamInfo(i)->getDict();
|
||||
AsciiString name = d->getAsciiString(TheKey_teamName);
|
||||
DEBUG_ASSERTCRASH(!name.isEmpty(),("bad"));
|
||||
|
||||
if (name == defTeamName)
|
||||
sel = i;
|
||||
|
||||
if (name == "team")
|
||||
{
|
||||
name = "(neutral)"; // aka, neutral
|
||||
neutral = i;
|
||||
}
|
||||
list->AddString(name.str());
|
||||
}
|
||||
DEBUG_ASSERTCRASH(TheSidesList->getNumTeams() == 0 || neutral != -1, ("must have a neutral"));
|
||||
if (sel == -1)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(defTeamName.isEmpty(), ("owning team not found, using neutral"));
|
||||
sel = neutral;
|
||||
}
|
||||
if (sel == -1)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(!pCur || TheSidesList->getNumTeams()==0,("hmm, should not happen"));
|
||||
m_curOwnerName.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_ASSERTCRASH(pCur,("hmm, should not happen"));
|
||||
m_curOwnerName = TheSidesList->getTeamInfo(sel)->getDict()->getAsciiString(TheKey_teamName);
|
||||
}
|
||||
list->SetCurSel(sel);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
static const PlayerTemplate* findFirstPlayerTemplateOnSide(AsciiString side)
|
||||
{
|
||||
if (side.isEmpty())
|
||||
return NULL; // neutral, this is ok
|
||||
|
||||
for (int i = 0; i < ThePlayerTemplateStore->getPlayerTemplateCount(); i++)
|
||||
{
|
||||
const PlayerTemplate* pt = ThePlayerTemplateStore->getNthPlayerTemplate(i);
|
||||
if (pt && pt->getSide() == side)
|
||||
{
|
||||
return pt;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_CRASH(("no player found for %s!\n",side.str()));
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ObjectOptions message handlers
|
||||
|
||||
/// Setup the controls in the dialog.
|
||||
BOOL ObjectOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
|
||||
// CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
|
||||
|
||||
// add entries from the thing factory as the available objects to use
|
||||
const ThingTemplate *tTemplate;
|
||||
for( tTemplate = TheThingFactory->firstTemplate();
|
||||
tTemplate;
|
||||
tTemplate = tTemplate->friend_getNextTemplate() )
|
||||
{
|
||||
Coord3D loc = { 0, 0, 0 };
|
||||
MapObject *pMap;
|
||||
|
||||
// create new map object
|
||||
pMap = newInstance( MapObject)( loc, tTemplate->getName(), 0.0f, 0, NULL, tTemplate );
|
||||
pMap->setNextMap( m_objectsList );
|
||||
m_objectsList = pMap;
|
||||
|
||||
// get display color for the editor
|
||||
Color cc = tTemplate->getDisplayColor();
|
||||
pMap->setColor(cc);
|
||||
|
||||
} // end for tTemplate
|
||||
|
||||
#if 0 // Lights are not working right now.
|
||||
{
|
||||
Coord3D pt = {0,0,0};
|
||||
char base[1024] = "*Lights/Light";
|
||||
MapObject *pMap = newInstance(MapObject)(pt, AsciiString(base), 0.0f, 0, NULL, NULL );
|
||||
pMap->setIsLight();
|
||||
|
||||
Dict *props = pMap->getProperties();
|
||||
RGBColor tmp;
|
||||
tmp.red = tmp.green = tmp.blue = 1.0f;
|
||||
Int tmpi = tmp.getAsInt();
|
||||
props->setReal(TheKey_lightHeightAboveTerrain, 0.0f);
|
||||
props->setReal(TheKey_lightInnerRadius, 15.0f);
|
||||
props->setReal(TheKey_lightOuterRadius, 25.0f);
|
||||
props->setInt(TheKey_lightAmbientColor, tmpi);
|
||||
props->setInt(TheKey_lightDiffuseColor, tmpi);
|
||||
|
||||
pMap->setNextMap(m_objectsList);
|
||||
m_objectsList = pMap;
|
||||
}
|
||||
#endif
|
||||
#ifdef LOAD_TEST_ASSETS
|
||||
{
|
||||
char dirBuf[_MAX_PATH];
|
||||
char findBuf[_MAX_PATH];
|
||||
char fileBuf[_MAX_PATH];
|
||||
Int i;
|
||||
|
||||
strcpy(dirBuf, TEST_W3D_DIR_PATH);
|
||||
int len = strlen(dirBuf);
|
||||
|
||||
if (len > 0 && dirBuf[len - 1] != '\\' && dirBuf[len-1] != '/') {
|
||||
dirBuf[len++] = '\\';
|
||||
dirBuf[len] = 0;
|
||||
}
|
||||
strcpy(findBuf, dirBuf);
|
||||
strcat(findBuf, "*.*");
|
||||
|
||||
FilenameList filenameList;
|
||||
TheFileSystem->getFileListInDirectory(AsciiString(dirBuf), AsciiString("*.w3d"), filenameList, FALSE);
|
||||
|
||||
if (filenameList.size() > 0) {
|
||||
FilenameList::iterator it = filenameList.begin();
|
||||
do {
|
||||
AsciiString filename = *it;
|
||||
len = filename.getLength();
|
||||
if (len<5) continue;
|
||||
// only do .w3d files
|
||||
|
||||
// strip out the path, and just keep the filename itself.
|
||||
AsciiString token;
|
||||
while (filename.getLength() > 0) {
|
||||
filename.nextToken(&token, "\\/");
|
||||
}
|
||||
|
||||
strcpy(fileBuf, TEST_STRING);
|
||||
strcat(fileBuf, "/");
|
||||
strcat(fileBuf, token.str());
|
||||
for (i=strlen(fileBuf)-1; i>0; i--) {
|
||||
if (fileBuf[i] == '.') {
|
||||
// strip off .w3d file extension.
|
||||
fileBuf[i] = 0;
|
||||
break; // we stripped it already, we don't need to go any further.
|
||||
}
|
||||
}
|
||||
Coord3D pt = {0,0,0};
|
||||
MapObject *pMap = newInstance(MapObject)(pt, AsciiString(fileBuf), 0.0f, 0, NULL, NULL );
|
||||
pMap->setNextMap(m_objectsList);
|
||||
m_objectsList = pMap;
|
||||
|
||||
++it;
|
||||
} while (it != filenameList.end());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_OBJECT_HEIGHT_EDIT);
|
||||
if (pWnd) {
|
||||
CString s;
|
||||
s.Format("%d",MAGIC_GROUND_Z);
|
||||
pWnd->SetWindowText(s);
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_TERRAIN_TREEVIEW);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_objectTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_TERRAIN_TREEVIEW);
|
||||
m_objectTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
pWnd = GetDlgItem(IDC_TERRAIN_SWATCHES);
|
||||
pWnd->GetWindowRect(&rect);
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_objectPreview.Create(NULL, "", WS_CHILD, rect, this, IDC_TERRAIN_SWATCHES);
|
||||
m_objectPreview.ShowWindow(SW_SHOW);
|
||||
|
||||
MapObject *pMap = m_objectsList;
|
||||
Int index = 0;
|
||||
while (pMap) {
|
||||
addObject( pMap, pMap->getName().str(), index, TVI_ROOT);
|
||||
index++;
|
||||
pMap = pMap->getNext();
|
||||
}
|
||||
|
||||
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
|
||||
updateLabel();
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
HTREEITEM ObjectOptions::findOrAdd(HTREEITEM parent, const char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = m_objectTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = m_objectTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = (char*)pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = m_objectTreeView.InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
HTREEITEM ObjectOptions::findOrDont(const char *pLabel)
|
||||
{
|
||||
return _FindOrDont(pLabel, m_objectTreeView.GetRootItem());
|
||||
}
|
||||
|
||||
HTREEITEM ObjectOptions::_FindOrDont(const char* pLabel, HTREEITEM startPoint)
|
||||
{
|
||||
std::list<HTREEITEM> itemsToEx;
|
||||
itemsToEx.push_back(startPoint);
|
||||
|
||||
while (itemsToEx.front()) {
|
||||
char buffer[_MAX_PATH];
|
||||
HTREEITEM hItem = itemsToEx.front();
|
||||
itemsToEx.pop_front();
|
||||
|
||||
if (!m_objectTreeView.ItemHasChildren(hItem)) {
|
||||
TVITEM item;
|
||||
memset(&item, 0, sizeof(item));
|
||||
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
|
||||
char* strToTest = strrchr(pLabel, '/');
|
||||
// if (strstr((strToTest ? strToTest : pLabel), buffer))
|
||||
if (strcmp((strToTest ? strToTest : pLabel), buffer) == 0)
|
||||
{
|
||||
return hItem;
|
||||
}
|
||||
} else {
|
||||
// add the first child, the others will be caught by the adding of the siblings
|
||||
itemsToEx.push_back(m_objectTreeView.GetChildItem(hItem));
|
||||
} // Always add the first sibling, if it exists
|
||||
|
||||
if (m_objectTreeView.GetNextSiblingItem(hItem)) {
|
||||
itemsToEx.push_back(m_objectTreeView.GetNextSiblingItem(hItem));
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Add the object hierarchy paths to the tree view. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ObjectOptions::addObject( MapObject *mapObject, const char *pPath,
|
||||
Int terrainNdx, HTREEITEM parent )
|
||||
{
|
||||
char buffer[ _MAX_PATH ];
|
||||
const char *leafName = NULL;
|
||||
|
||||
// sanity
|
||||
if( mapObject == NULL )
|
||||
return;
|
||||
|
||||
//
|
||||
// if we have an thing template in mapObject, we've read it from the new INI database,
|
||||
// we will sort those items into the tree based on properties of the template that
|
||||
// make it easier for us to browse when building levels
|
||||
//
|
||||
// Feel free to reorganize how this tree is constructed from the template
|
||||
// data at will, whatever makes it easier for design
|
||||
//
|
||||
const ThingTemplate *thingTemplate = mapObject->getThingTemplate();
|
||||
if( thingTemplate )
|
||||
{
|
||||
|
||||
// first check for test sorted objects
|
||||
if( thingTemplate->getEditorSorting() == ES_TEST )
|
||||
parent = findOrAdd( parent, "TEST" );
|
||||
|
||||
// first sort by side, either create or find the tree item with matching side name
|
||||
AsciiString side = thingTemplate->getDefaultOwningSide();
|
||||
DEBUG_ASSERTCRASH( !side.isEmpty(), ("NULL default side in template\n") );
|
||||
strcpy( buffer, side.str() );
|
||||
parent = findOrAdd( parent, buffer );
|
||||
|
||||
// next tier uses the editor sorting that design can specify in the INI
|
||||
for( EditorSortingType i = ES_FIRST;
|
||||
i < ES_NUM_SORTING_TYPES;
|
||||
i = (EditorSortingType)(i + 1) )
|
||||
{
|
||||
|
||||
if( thingTemplate->getEditorSorting() == i )
|
||||
{
|
||||
|
||||
parent = findOrAdd( parent, EditorSortingNames[ i ] );
|
||||
break; // exit for
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
if( i == ES_NUM_SORTING_TYPES )
|
||||
parent = findOrAdd( parent, "UNSORTED" );
|
||||
|
||||
// the leaf name is the name of the template
|
||||
leafName = thingTemplate->getName().str();
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
// all these old entries we will put in a tree for legacy GDF items
|
||||
parent = findOrAdd( parent, "**TEST MODELS" );
|
||||
|
||||
Int i=0;
|
||||
leafName = pPath;
|
||||
while (pPath[i] && i<sizeof(buffer)) {
|
||||
if (pPath[i] == 0) {
|
||||
return;
|
||||
}
|
||||
if (pPath[i] == '/') {
|
||||
pPath+= i+1;
|
||||
i = 0;
|
||||
}
|
||||
buffer[i] = pPath[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
if( i > 0 )
|
||||
{
|
||||
buffer[ i ] = 0;
|
||||
leafName = buffer;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end else if
|
||||
|
||||
// add to the tree view
|
||||
if( leafName )
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = terrainNdx;
|
||||
ins.item.pszText = (char*)leafName;
|
||||
ins.item.cchTextMax = strlen(leafName)+2;
|
||||
m_objectTreeView.InsertItem(&ins);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Set the selected object in the tree view.
|
||||
Bool ObjectOptions::setObjectTreeViewSelection(HTREEITEM parent, Int selection)
|
||||
{
|
||||
TVITEM item;
|
||||
char buffer[NAME_MAX_LEN];
|
||||
::memset(&item, 0, sizeof(item));
|
||||
HTREEITEM child = m_objectTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT;
|
||||
item.hItem = child;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
if (item.lParam == selection) {
|
||||
m_objectTreeView.SelectItem(child);
|
||||
return(true);
|
||||
}
|
||||
if (setObjectTreeViewSelection(child, selection))
|
||||
{
|
||||
strcpy(m_currentObjectName, buffer);
|
||||
updateLabel();
|
||||
return(true);
|
||||
}
|
||||
child = m_objectTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
BOOL ObjectOptions::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
if (pHdr->hdr.hwndFrom == m_objectTreeView.m_hWnd) {
|
||||
|
||||
if (pHdr->hdr.code == TVN_ITEMEXPANDED) {
|
||||
if (pHdr->action == TVE_COLLAPSE) {
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_STATE;
|
||||
item.hItem = pHdr->itemOld.hItem;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
item.state &= (~TVIS_EXPANDEDONCE);
|
||||
item.mask = TVIF_STATE;
|
||||
m_objectTreeView.SetItem(&item);
|
||||
}
|
||||
}
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
char buffer[NAME_MAX_LEN];
|
||||
HTREEITEM hItem = m_objectTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
if (item.lParam >= 0) {
|
||||
m_currentObjectIndex = item.lParam;
|
||||
strcpy(m_currentObjectName, buffer);
|
||||
} else if (m_objectTreeView.ItemHasChildren(item.hItem)) {
|
||||
strcpy(m_currentObjectName, "No Selection");
|
||||
m_currentObjectIndex = -1;
|
||||
}
|
||||
updateLabel();
|
||||
}
|
||||
}
|
||||
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
|
||||
MapObject *ObjectOptions::getCurMapObject(void)
|
||||
{
|
||||
if (m_staticThis && m_currentObjectIndex >= 0) {
|
||||
MapObject *pObj = m_staticThis->m_objectsList;
|
||||
int count = 0;
|
||||
while (pObj) {
|
||||
if (count == m_currentObjectIndex) {
|
||||
return(pObj);
|
||||
}
|
||||
count++;
|
||||
pObj = pObj->getNext();
|
||||
}
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
AsciiString ObjectOptions::getCurGdfName(void)
|
||||
{
|
||||
MapObject *pCur = getCurMapObject();
|
||||
if (pCur) {
|
||||
return pCur->getName();
|
||||
}
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
MapObject *ObjectOptions::duplicateCurMapObjectForPlace(const Coord3D* loc, Real angle, Bool checkPlayers)
|
||||
{
|
||||
MapObject *pCur = getCurMapObject();
|
||||
if (pCur)
|
||||
{
|
||||
|
||||
Bool found = false;
|
||||
const ThingTemplate* tt = pCur->getThingTemplate();
|
||||
if (checkPlayers)
|
||||
{
|
||||
AsciiString defaultTeam("team");
|
||||
|
||||
AsciiString objectTeamName = m_curOwnerName;
|
||||
if (objectTeamName != defaultTeam) {
|
||||
TeamsInfo *teamInfo = TheSidesList->findTeamInfo(objectTeamName);
|
||||
if (teamInfo) {
|
||||
AsciiString teamOwner = teamInfo->getDict()->getAsciiString(TheKey_teamOwner);
|
||||
SidesInfo* pSide = TheSidesList->findSideInfo(teamOwner);
|
||||
if (pSide) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found && tt)
|
||||
{
|
||||
AsciiString defPlayerSide = tt->getDefaultOwningSide();
|
||||
Int si = findSideListEntryWithPlayerOfSide(defPlayerSide);
|
||||
|
||||
found = (si >= 0);
|
||||
if (!found)
|
||||
{
|
||||
AddPlayerDialog addPlyr(pCur->getThingTemplate()->getDefaultOwningSide());
|
||||
if (addPlyr.DoModal() == IDOK)
|
||||
{
|
||||
for (int i = 0; i < TheSidesList->getNumSides(); i++)
|
||||
{
|
||||
AsciiString playerTmplName = TheSidesList->getSideInfo(i)->getDict()->getAsciiString(TheKey_playerFaction);
|
||||
if (playerTmplName == addPlyr.getAddedSide())
|
||||
{
|
||||
m_curOwnerName.set("team");
|
||||
m_curOwnerName.concat(TheSidesList->getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!found) {
|
||||
// neutral
|
||||
m_curOwnerName.set("team");
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
found = true;
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
MapObject *pNew = newInstance(MapObject)( *loc, pCur->getName(), angle,
|
||||
pCur->getFlags(), pCur->getProperties(),
|
||||
pCur->getThingTemplate() );
|
||||
pNew->getProperties()->setAsciiString(TheKey_originalOwner, m_curOwnerName);
|
||||
pNew->setColor(pCur->getColor());
|
||||
return pNew;
|
||||
}
|
||||
}
|
||||
AfxMessageBox("Unable to add object.");
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
Real ObjectOptions::getCurObjectHeight(void)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
CWnd *pWnd = m_staticThis->GetDlgItem(IDC_OBJECT_HEIGHT_EDIT);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
Real height = atoi(val);
|
||||
if (height>0) {
|
||||
height *= MAP_HEIGHT_SCALE;
|
||||
}
|
||||
return(height);
|
||||
}
|
||||
}
|
||||
return(MAGIC_GROUND_Z);
|
||||
}
|
||||
|
||||
MapObject *ObjectOptions::getObjectNamed(AsciiString name)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
MapObject *pObj = m_staticThis->m_objectsList;
|
||||
// int count = 0;
|
||||
while (pObj) {
|
||||
if (name == pObj->getName()) {
|
||||
return(pObj);
|
||||
}
|
||||
const char *curName = pObj->getName().str();
|
||||
const char *leaf = curName;
|
||||
while (*curName) {
|
||||
if (*curName == '/') {
|
||||
leaf = curName+1;
|
||||
}
|
||||
curName++;
|
||||
}
|
||||
if (0==strcmp(leaf, name.str())) {
|
||||
return(pObj);
|
||||
}
|
||||
pObj = pObj->getNext();
|
||||
}
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
Int ObjectOptions::getObjectNamedIndex(const AsciiString& name)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
MapObject *pObj = m_staticThis->m_objectsList;
|
||||
int count = 0;
|
||||
while (pObj) {
|
||||
if (name == pObj->getName()) {
|
||||
return(count);
|
||||
}
|
||||
const char *curName = pObj->getName().str();
|
||||
const char *leaf = curName;
|
||||
while (*curName) {
|
||||
if (*curName == '/') {
|
||||
leaf = curName+1;
|
||||
}
|
||||
curName++;
|
||||
}
|
||||
if (0==strcmp(leaf, name.str())) {
|
||||
return(count);
|
||||
}
|
||||
pObj = pObj->getNext();
|
||||
}
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
||||
void ObjectOptions::OnEditchangeOwningteam()
|
||||
{
|
||||
CComboBox *list = (CComboBox*)GetDlgItem(IDC_OWNINGTEAM);
|
||||
Int sel = list->GetCurSel();
|
||||
|
||||
m_curOwnerName.clear();
|
||||
if (sel >= 0)
|
||||
{
|
||||
// note, get playername from the dicts, NOT from the popup.
|
||||
Dict *d = TheSidesList->getTeamInfo(sel)->getDict();
|
||||
m_curOwnerName = d->getAsciiString(TheKey_teamName);
|
||||
}
|
||||
// no, do NOT call this; it'll reset back to default
|
||||
//updateLabel();
|
||||
}
|
||||
|
||||
void ObjectOptions::selectObject(const MapObject* pObj)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
|
||||
char buffer[NAME_MAX_LEN];
|
||||
HTREEITEM objToSel = m_staticThis->findOrDont(pObj->getName().str());
|
||||
if (objToSel == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
|
||||
item.hItem = objToSel;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_staticThis->m_objectTreeView.GetItem(&item);
|
||||
|
||||
if (m_staticThis->m_objectTreeView.SelectItem(objToSel)) {
|
||||
m_staticThis->m_currentObjectIndex = item.lParam;
|
||||
strcpy(m_staticThis->m_currentObjectName, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectOptions::OnCloseupOwningteam()
|
||||
{
|
||||
OnEditchangeOwningteam();
|
||||
}
|
||||
|
||||
void ObjectOptions::OnSelchangeOwningteam()
|
||||
{
|
||||
OnEditchangeOwningteam();
|
||||
}
|
||||
|
||||
320
GeneralsMD/Code/Tools/WorldBuilder/src/ObjectPreview.cpp
Normal file
320
GeneralsMD/Code/Tools/WorldBuilder/src/ObjectPreview.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ObjectPreview.cpp : implementation file
|
||||
//
|
||||
|
||||
#include <rinfo.h>
|
||||
#include <camera.h>
|
||||
#include <light.h>
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "Lib\BaseType.h"
|
||||
|
||||
#include "ObjectPreview.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "ObjectOptions.h"
|
||||
#include "AddPlayerDialog.h"
|
||||
#include "CUndoable.h"
|
||||
#include "WbView3d.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingSort.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "GameClient/Color.h"
|
||||
|
||||
#include "W3DDevice/GameClient/W3DAssetManager.h"
|
||||
#include "WW3D2/DX8Wrapper.h"
|
||||
#include "WWLib/targa.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ObjectPreview
|
||||
|
||||
ObjectPreview::ObjectPreview()
|
||||
{
|
||||
m_tTempl = NULL;
|
||||
}
|
||||
|
||||
ObjectPreview::~ObjectPreview()
|
||||
{
|
||||
}
|
||||
|
||||
void ObjectPreview::SetThingTemplate(const ThingTemplate *tTempl)
|
||||
{
|
||||
m_tTempl = tTempl;
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ObjectPreview, CWnd)
|
||||
//{{AFX_MSG_MAP(ObjectPreview)
|
||||
ON_WM_PAINT()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
#define SWATCH_OFFSET 20
|
||||
|
||||
#define PREVIEW_WIDTH 128
|
||||
#define PREVIEW_HEIGHT 128
|
||||
|
||||
static UnsignedByte * saveSurface(IDirect3DSurface8 *surface)
|
||||
{
|
||||
D3DSURFACE_DESC desc;
|
||||
IDirect3DSurface8 *tempSurface;
|
||||
|
||||
surface->GetDesc(&desc);
|
||||
|
||||
LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
|
||||
|
||||
HRESULT hr=m_pDev->CreateImageSurface( desc.Width,desc.Height,desc.Format, &tempSurface);
|
||||
|
||||
hr=m_pDev->CopyRects(surface,NULL,0,tempSurface,NULL);
|
||||
|
||||
D3DLOCKED_RECT lrect;
|
||||
|
||||
DX8_ErrorCode(tempSurface->LockRect(&lrect,NULL,D3DLOCK_READONLY));
|
||||
|
||||
unsigned int x,y,index,index2,width,height;
|
||||
|
||||
width=desc.Width;
|
||||
height=desc.Height;
|
||||
|
||||
#ifdef CAPTURE_TO_TARGA
|
||||
char image[3*PREVIEW_WIDTH*PREVIEW_HEIGHT];
|
||||
//bytes are mixed in targa files, not rgb order.
|
||||
for (y=0; y<height; y++)
|
||||
{
|
||||
for (x=0; x<width; x++)
|
||||
{
|
||||
// index for image
|
||||
index=3*(x+y*width);
|
||||
// index for fb
|
||||
index2=y*lrect.Pitch+4*x;
|
||||
|
||||
image[index]=*((char *) lrect.pBits + index2+2);
|
||||
image[index+1]=*((char *) lrect.pBits + index2+1);
|
||||
image[index+2]=*((char *) lrect.pBits + index2+0);
|
||||
}
|
||||
}
|
||||
|
||||
Targa targ;
|
||||
memset(&targ.Header,0,sizeof(targ.Header));
|
||||
targ.Header.Width=width;
|
||||
targ.Header.Height=height;
|
||||
targ.Header.PixelDepth=24;
|
||||
targ.Header.ImageType=TGA_TRUECOLOR;
|
||||
targ.SetImage(image);
|
||||
targ.YFlip();
|
||||
|
||||
targ.Save("ObjectPreview.tga",TGAF_IMAGE,false);
|
||||
|
||||
return NULL;
|
||||
|
||||
#else
|
||||
|
||||
static UnsignedByte bgraImage[3*PREVIEW_WIDTH*PREVIEW_HEIGHT];
|
||||
//bmp is same byte order
|
||||
for (y=0; y<height; y++)
|
||||
{
|
||||
for (x=0; x<width; x++)
|
||||
{
|
||||
// index for image
|
||||
index=3*(x+y*width);
|
||||
// index for fb
|
||||
index2=y*lrect.Pitch+4*x;
|
||||
|
||||
bgraImage[index]=*((UnsignedByte *) lrect.pBits + index2+0);
|
||||
bgraImage[index+1]=*((UnsignedByte *) lrect.pBits + index2+1);
|
||||
bgraImage[index+2]=*((UnsignedByte *) lrect.pBits + index2+2);
|
||||
//bgraImage[index+3]=0;
|
||||
}
|
||||
}
|
||||
|
||||
//Flip the image
|
||||
UnsignedByte *ptr,*ptr1;
|
||||
UnsignedByte v,v1;
|
||||
|
||||
for (y = 0; y < (height >> 1); y++)
|
||||
{
|
||||
/* Compute address of lines to exchange. */
|
||||
ptr = (bgraImage + ((width * y) * 3));
|
||||
ptr1 = (bgraImage + ((width * (height - 1)) * 3));
|
||||
ptr1 -= ((width * y) * 3);
|
||||
|
||||
/* Exchange all the pixels on this scan line. */
|
||||
for (x = 0; x < (width * 3); x++)
|
||||
{
|
||||
v = *ptr;
|
||||
v1 = *ptr1;
|
||||
*ptr = v1;
|
||||
*ptr1 = v;
|
||||
ptr++;
|
||||
ptr1++;
|
||||
}
|
||||
}
|
||||
|
||||
tempSurface->Release();
|
||||
|
||||
return bgraImage;
|
||||
#endif
|
||||
}
|
||||
|
||||
// return an array of BGRA pixels
|
||||
static UnsignedByte * generatePreview( const ThingTemplate *tt )
|
||||
{
|
||||
// find the default model to preview
|
||||
RenderObjClass *model = NULL;
|
||||
Real scale = 1.0f;
|
||||
AsciiString modelName = "No Model Name";
|
||||
if (tt)
|
||||
{
|
||||
ModelConditionFlags state;
|
||||
state.clear();
|
||||
WbView3d *p3View = CWorldBuilderDoc::GetActiveDoc()->GetActive3DView();
|
||||
modelName = p3View->getBestModelName(tt, state);
|
||||
scale = tt->getAssetScale();
|
||||
}
|
||||
// set render object, or create if we need to
|
||||
if( modelName.isEmpty() == FALSE &&
|
||||
strncmp( modelName.str(), "No ", 3 ) )
|
||||
{
|
||||
WW3DAssetManager *pMgr = W3DAssetManager::Get_Instance();
|
||||
model = pMgr->Create_Render_Obj(modelName.str());
|
||||
if (model)
|
||||
{
|
||||
const AABoxClass bbox = model->Get_Bounding_Box();
|
||||
// Real height = bbox.Extent.Z;
|
||||
const SphereClass sphere = model->Get_Bounding_Sphere();
|
||||
Real dist = sphere.Radius*0.5;
|
||||
model->Set_Position(Vector3(-sphere.Center.X, -sphere.Center.Y, -sphere.Center.Z));
|
||||
|
||||
// Create reflection texture
|
||||
TextureClass *objectTexture = DX8Wrapper::Create_Render_Target (PREVIEW_WIDTH, PREVIEW_HEIGHT);
|
||||
if (!objectTexture)
|
||||
{
|
||||
model->Release_Ref();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Set the render target
|
||||
DX8Wrapper::Set_Render_Target_With_Z(objectTexture);
|
||||
|
||||
// create the camera
|
||||
Bool orthoCamera = false;
|
||||
CameraClass *camera = NEW_REF( CameraClass, () );
|
||||
Matrix3D camTran;
|
||||
camTran.Look_At(Vector3(dist*2,dist*2,dist),Vector3(0.0f, 0.0f, 0.0f),0);
|
||||
camera->Set_Transform( camTran);
|
||||
|
||||
Vector2 minVec = Vector2( -1, -1 );
|
||||
Vector2 maxVec = Vector2( +1, +1 );
|
||||
camera->Set_View_Plane( minVec, maxVec );
|
||||
camera->Set_Clip_Planes( 0.995f, 600.0f );
|
||||
if (orthoCamera)
|
||||
camera->Set_Projection_Type( CameraClass::ORTHO );
|
||||
|
||||
// Clear the backbuffer
|
||||
WW3D::Begin_Render(true,true,Vector3(0.5f,0.5f,0.5f));
|
||||
//WW3D::Begin_Render(true,true,Vector3(1.0f,1.0f,1.0f));
|
||||
|
||||
RenderInfoClass rinfo(*camera);
|
||||
LightEnvironmentClass lightEnv;
|
||||
rinfo.light_environment = &lightEnv;
|
||||
lightEnv.Reset(Vector3(0.0f, 0.0f, 0.0f), Vector3(1.0f, 1.0f, 1.0f));
|
||||
|
||||
WW3D::Render(*model, rinfo);
|
||||
|
||||
WW3D::End_Render(false);
|
||||
|
||||
// Change the rendertarget back to the main backbuffer
|
||||
DX8Wrapper::Set_Render_Target((IDirect3DSurface8 *)NULL);
|
||||
|
||||
SurfaceClass *surface = objectTexture->Get_Surface_Level();
|
||||
UnsignedByte *data = saveSurface(surface->Peek_D3D_Surface());
|
||||
|
||||
REF_PTR_RELEASE(surface);
|
||||
|
||||
REF_PTR_RELEASE(objectTexture);
|
||||
REF_PTR_RELEASE(camera);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ObjectPreview message handlers
|
||||
|
||||
void ObjectPreview::OnPaint()
|
||||
{
|
||||
CPaintDC dc(this); // device context for painting
|
||||
|
||||
CRect clientRect;
|
||||
GetClientRect(&clientRect);
|
||||
|
||||
CRect previewRect;
|
||||
previewRect = clientRect;
|
||||
|
||||
CBrush brush;
|
||||
brush.CreateSolidBrush(RGB(0,0,0));
|
||||
|
||||
UnsignedByte *pData;
|
||||
pData = generatePreview(m_tTempl);
|
||||
if (pData) {
|
||||
DrawMyTexture(&dc, previewRect.top, previewRect.left, previewRect.Width(), previewRect.Height(), pData);
|
||||
} else {
|
||||
dc.FillSolidRect(&previewRect, RGB(128,128,128));
|
||||
}
|
||||
dc.FrameRect(&previewRect, &brush);
|
||||
}
|
||||
|
||||
void ObjectPreview::DrawMyTexture(CDC *pDc, int top, int left, Int width, Int height, UnsignedByte *rgbData)
|
||||
{
|
||||
// Just blast about some dib bits.
|
||||
|
||||
LPBITMAPINFO pBI;
|
||||
// long bytes = sizeof(BITMAPINFO);
|
||||
pBI = new BITMAPINFO;
|
||||
pBI->bmiHeader.biSize = sizeof(pBI->bmiHeader);
|
||||
pBI->bmiHeader.biWidth = PREVIEW_WIDTH;
|
||||
pBI->bmiHeader.biHeight = PREVIEW_HEIGHT; /* match display top left == 0,0 */
|
||||
pBI->bmiHeader.biPlanes = 1;
|
||||
pBI->bmiHeader.biBitCount = 24;
|
||||
pBI->bmiHeader.biCompression = BI_RGB;
|
||||
pBI->bmiHeader.biSizeImage = (PREVIEW_WIDTH*PREVIEW_HEIGHT)*(pBI->bmiHeader.biBitCount/8);
|
||||
pBI->bmiHeader.biXPelsPerMeter = 1000;
|
||||
pBI->bmiHeader.biYPelsPerMeter = 1000;
|
||||
pBI->bmiHeader.biClrUsed = 0;
|
||||
pBI->bmiHeader.biClrImportant = 0;
|
||||
|
||||
//::Sleep(10);
|
||||
//int val=::StretchDIBits(pDc->m_hDC, left, top, width, height, 0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT, rgbData, pBI,
|
||||
// DIB_RGB_COLORS, SRCCOPY);
|
||||
/*int val=*/::StretchDIBits(pDc->m_hDC, left, top, width, height, PREVIEW_WIDTH/4, PREVIEW_HEIGHT/4, PREVIEW_WIDTH/2, PREVIEW_HEIGHT/2, rgbData, pBI,
|
||||
DIB_RGB_COLORS, SRCCOPY);
|
||||
delete(pBI);
|
||||
}
|
||||
|
||||
|
||||
170
GeneralsMD/Code/Tools/WorldBuilder/src/ObjectTool.cpp
Normal file
170
GeneralsMD/Code/Tools/WorldBuilder/src/ObjectTool.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ObjectTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "ObjectTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "DrawObject.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WbView3d.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
//
|
||||
// ObjectTool class.
|
||||
//
|
||||
enum {HYSTERESIS = 3};
|
||||
/// Constructor
|
||||
ObjectTool::ObjectTool(void) :
|
||||
Tool(ID_PLACE_OBJECT_TOOL, IDC_PLACE_OBJECT)
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
ObjectTool::~ObjectTool(void)
|
||||
{
|
||||
}
|
||||
|
||||
Real ObjectTool::calcAngle(Coord3D downPt, Coord3D curPt, WbView* pView)
|
||||
{
|
||||
enum {HYSTERESIS = 3};
|
||||
double dx = curPt.x - downPt.x;
|
||||
double dy = curPt.y - downPt.y;
|
||||
double dist = sqrt(dx*dx+dy*dy);
|
||||
double angle = 0;
|
||||
if (dist < 0.1) // check for div-by-zero.
|
||||
{
|
||||
angle = 0;
|
||||
}
|
||||
else if (abs(dx) > abs(dy))
|
||||
{
|
||||
angle = acos( (double)dx / dist);
|
||||
if (dy<0) angle = -angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = asin( ((double)dy) / dist);
|
||||
if (dx<0) angle = PI-angle;
|
||||
}
|
||||
if (angle > PI) angle -= 2*PI;
|
||||
#ifdef _DEBUG
|
||||
CString buf;
|
||||
buf.Format("Angle %f rad, %d degrees\n", angle, (int)(angle*180/PI));
|
||||
::OutputDebugString(buf);
|
||||
#endif
|
||||
return((Real)angle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Turn off object tracking.
|
||||
void ObjectTool::deactivate()
|
||||
{
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc==NULL) return;
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
p3View->setObjTracking(NULL, m_downPt3d, 0, false);
|
||||
}
|
||||
/// Shows the object options panel
|
||||
void ObjectTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_OBJECT_OPTIONS);
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc==NULL) return;
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
p3View->setObjTracking(NULL, m_downPt3d, 0, false);
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse down - Place an object. */
|
||||
void ObjectTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
m_downPt2d = viewPt;
|
||||
m_downPt3d = cpt;
|
||||
}
|
||||
|
||||
/** Tracking - show the object. */
|
||||
void ObjectTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Bool justAClick = true;
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt, false); // Don't constrain.
|
||||
Coord3D loc = cpt;
|
||||
pView->snapPoint(&loc);
|
||||
if (m == TRACK_L) { // Mouse is down, so preview the angle if > hysteresis.
|
||||
// always check hysteresis in view coords.
|
||||
justAClick = (abs(viewPt.x - m_downPt2d.x)<HYSTERESIS || abs(viewPt.x - m_downPt2d.x)<HYSTERESIS);
|
||||
loc = m_downPt3d;
|
||||
}
|
||||
MapObject *pCur = ObjectOptions::getObjectNamed(AsciiString(ObjectOptions::getCurObjectName()));
|
||||
Real angle = justAClick ? 0 : calcAngle(loc, cpt, pView);
|
||||
if (justAClick && pCur && pCur->getThingTemplate()) {
|
||||
angle = pCur->getThingTemplate()->getPlacementViewAngle();
|
||||
}
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
p3View->setObjTracking(NULL, m_downPt3d, 0, false);
|
||||
loc.z = ObjectOptions::getCurObjectHeight();
|
||||
if (pCur) {
|
||||
// Display the transparent version of this object.
|
||||
p3View->setObjTracking(pCur, loc, angle, true);
|
||||
} else {
|
||||
// Don't display anything.
|
||||
p3View->setObjTracking(NULL, loc, angle, false);
|
||||
}
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse up - Place an object. */
|
||||
void ObjectTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
// always check hysteresis in view coords.
|
||||
Bool justAClick = (abs(viewPt.x - m_downPt2d.x)<HYSTERESIS || abs(viewPt.x - m_downPt2d.x)<HYSTERESIS);
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt, false); // Don't constrain.
|
||||
|
||||
Coord3D loc = m_downPt3d;
|
||||
pView->snapPoint(&loc);
|
||||
loc.z = ObjectOptions::getCurObjectHeight();
|
||||
Real angle = justAClick ? 0 : calcAngle(loc, cpt, pView);
|
||||
MapObject *pNew = ObjectOptions::duplicateCurMapObjectForPlace(&loc, angle, true);
|
||||
if (pNew) {
|
||||
if (justAClick && pNew->getThingTemplate()) {
|
||||
angle = pNew->getThingTemplate()->getPlacementViewAngle();
|
||||
pNew->setAngle(angle);
|
||||
}
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, pNew);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
pNew = NULL; // undoable owns it now.
|
||||
}
|
||||
}
|
||||
|
||||
209
GeneralsMD/Code/Tools/WorldBuilder/src/OpenMap.cpp
Normal file
209
GeneralsMD/Code/Tools/WorldBuilder/src/OpenMap.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// OpenMap.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "OpenMap.h"
|
||||
#include "Common/GlobalData.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// OpenMap dialog
|
||||
|
||||
|
||||
OpenMap::OpenMap(TOpenMapInfo *pInfo, CWnd* pParent /*=NULL*/)
|
||||
: CDialog(OpenMap::IDD, pParent),
|
||||
m_pInfo(pInfo)
|
||||
{
|
||||
m_pInfo->browse = false;
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
m_usingSystemDir = ::AfxGetApp()->GetProfileInt(MAP_OPENSAVE_PANEL_SECTION, "UseSystemDir", TRUE);
|
||||
#else
|
||||
m_usingSystemDir = FALSE;
|
||||
#endif
|
||||
|
||||
//{{AFX_DATA_INIT(OpenMap)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void OpenMap::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(OpenMap)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(OpenMap, CDialog)
|
||||
//{{AFX_MSG_MAP(OpenMap)
|
||||
ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
|
||||
ON_BN_CLICKED(IDC_SYSTEMMAPS, OnSystemMaps)
|
||||
ON_BN_CLICKED(IDC_USERMAPS, OnUserMaps)
|
||||
ON_LBN_DBLCLK(IDC_OPEN_LIST, OnDblclkOpenList)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// OpenMap message handlers
|
||||
|
||||
void OpenMap::OnSystemMaps()
|
||||
{
|
||||
populateMapListbox( TRUE );
|
||||
}
|
||||
|
||||
void OpenMap::OnUserMaps()
|
||||
{
|
||||
populateMapListbox( FALSE );
|
||||
}
|
||||
|
||||
void OpenMap::OnBrowse()
|
||||
{
|
||||
m_pInfo->browse = true;
|
||||
OnOK();
|
||||
}
|
||||
|
||||
void OpenMap::OnOK()
|
||||
{
|
||||
CListBox *pList = (CListBox *)this->GetDlgItem(IDC_OPEN_LIST);
|
||||
if (pList == NULL) {
|
||||
OnCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
Int sel = pList->GetCurSel();
|
||||
if (sel == LB_ERR) {
|
||||
m_pInfo->browse = true;
|
||||
} else {
|
||||
CString newName;
|
||||
pList->GetText(sel, newName );
|
||||
if (m_usingSystemDir)
|
||||
m_pInfo->filename = ".\\Maps\\" + newName + "\\" + newName + ".map";
|
||||
else
|
||||
{
|
||||
m_pInfo->filename = TheGlobalData->getPath_UserData().str();
|
||||
m_pInfo->filename = m_pInfo->filename + "Maps\\" + newName + "\\" + newName + ".map";
|
||||
}
|
||||
}
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
void OpenMap::populateMapListbox( Bool systemMaps )
|
||||
{
|
||||
m_usingSystemDir = systemMaps;
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
::AfxGetApp()->WriteProfileInt(MAP_OPENSAVE_PANEL_SECTION, "UseSystemDir", m_usingSystemDir);
|
||||
#endif
|
||||
|
||||
HANDLE hFindFile = 0;
|
||||
WIN32_FIND_DATA findData;
|
||||
char dirBuf[_MAX_PATH];
|
||||
char findBuf[_MAX_PATH];
|
||||
char fileBuf[_MAX_PATH];
|
||||
|
||||
if (systemMaps)
|
||||
strcpy(dirBuf, "Maps\\");
|
||||
else
|
||||
{
|
||||
strcpy(dirBuf, TheGlobalData->getPath_UserData().str());
|
||||
strcat(dirBuf, "Maps\\");
|
||||
}
|
||||
|
||||
int len = strlen(dirBuf);
|
||||
|
||||
if (len > 0 && dirBuf[len - 1] != '\\') {
|
||||
dirBuf[len++] = '\\';
|
||||
dirBuf[len] = 0;
|
||||
}
|
||||
CListBox *pList = (CListBox *)this->GetDlgItem(IDC_OPEN_LIST);
|
||||
if (pList == NULL) return;
|
||||
pList->ResetContent();
|
||||
strcpy(findBuf, dirBuf);
|
||||
strcat(findBuf, "*.*");
|
||||
|
||||
Bool found = false;
|
||||
|
||||
hFindFile = FindFirstFile(findBuf, &findData);
|
||||
if (hFindFile != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0)
|
||||
continue;
|
||||
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
strcpy(fileBuf, dirBuf);
|
||||
strcat(fileBuf, findData.cFileName);
|
||||
strcat(fileBuf, "\\");
|
||||
strcat(fileBuf, findData.cFileName);
|
||||
strcat(fileBuf, ".map");
|
||||
try {
|
||||
CFileStatus status;
|
||||
if (CFile::GetStatus(fileBuf, status)) {
|
||||
if (!(status.m_attribute & CFile::directory)) {
|
||||
pList->AddString(findData.cFileName);
|
||||
found = true;
|
||||
};
|
||||
}
|
||||
} catch(...) {}
|
||||
|
||||
} while (FindNextFile(hFindFile, &findData));
|
||||
|
||||
if (hFindFile) FindClose(hFindFile);
|
||||
}
|
||||
if (found) {
|
||||
pList->SetCurSel(0);
|
||||
} else {
|
||||
CWnd *pOk = GetDlgItem(IDOK);
|
||||
if (pOk) pOk->EnableWindow(false);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL OpenMap::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CButton *pSystemMaps = (CButton *)this->GetDlgItem(IDC_SYSTEMMAPS);
|
||||
if (pSystemMaps != NULL)
|
||||
pSystemMaps->SetCheck( m_usingSystemDir );
|
||||
|
||||
CButton *pUserMaps = (CButton *)this->GetDlgItem(IDC_USERMAPS);
|
||||
if (pUserMaps != NULL)
|
||||
pUserMaps->SetCheck( !m_usingSystemDir );
|
||||
|
||||
#if !defined(_DEBUG) && !defined(_INTERNAL)
|
||||
if (pSystemMaps)
|
||||
pSystemMaps->ShowWindow( FALSE );
|
||||
if (pUserMaps)
|
||||
pUserMaps->ShowWindow( FALSE );
|
||||
#endif
|
||||
|
||||
populateMapListbox( m_usingSystemDir );
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void OpenMap::OnDblclkOpenList()
|
||||
{
|
||||
OnOK();
|
||||
}
|
||||
110
GeneralsMD/Code/Tools/WorldBuilder/src/OptionsPanel.cpp
Normal file
110
GeneralsMD/Code/Tools/WorldBuilder/src/OptionsPanel.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// OptionsPanel.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "worldbuilderdoc.h"
|
||||
#include "OptionsPanel.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// COptionsPanel dialog
|
||||
|
||||
|
||||
COptionsPanel::COptionsPanel(Int dlgid /*=0*/, CWnd* pParent /*=NULL*/)
|
||||
: CDialog(dlgid ? dlgid : COptionsPanel::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(COptionsPanel)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void COptionsPanel::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(COptionsPanel)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(COptionsPanel, CDialog)
|
||||
//{{AFX_MSG_MAP(COptionsPanel)
|
||||
ON_WM_MOVE()
|
||||
ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
|
||||
ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
|
||||
ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
|
||||
ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// COptionsPanel message handlers
|
||||
|
||||
void COptionsPanel::OnMove(int x, int y)
|
||||
{
|
||||
CDialog::OnMove(x, y);
|
||||
|
||||
if (this->IsWindowVisible() && !this->IsIconic()) {
|
||||
CRect frameRect;
|
||||
GetWindowRect(&frameRect);
|
||||
::AfxGetApp()->WriteProfileInt(OPTIONS_PANEL_SECTION, "Top", frameRect.top);
|
||||
::AfxGetApp()->WriteProfileInt(OPTIONS_PANEL_SECTION, "Left", frameRect.left);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void COptionsPanel::OnEditRedo()
|
||||
{
|
||||
// Redirect undo/redo to the doc so they get executed.
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc) {
|
||||
pDoc->OnEditRedo();
|
||||
}
|
||||
}
|
||||
|
||||
void COptionsPanel::OnUpdateEditRedo(CCmdUI* pCmdUI)
|
||||
{
|
||||
// Redirect undo/redo to the doc so they get executed.
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc) {
|
||||
pDoc->OnUpdateEditRedo(pCmdUI);
|
||||
}
|
||||
}
|
||||
|
||||
void COptionsPanel::OnEditUndo()
|
||||
{
|
||||
// Redirect undo/redo to the doc so they get executed.
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc) {
|
||||
pDoc->OnEditUndo();
|
||||
}
|
||||
}
|
||||
|
||||
void COptionsPanel::OnUpdateEditUndo(CCmdUI* pCmdUI)
|
||||
{
|
||||
// Redirect undo/redo to the doc so they get executed.
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc) {
|
||||
pDoc->OnUpdateEditUndo(pCmdUI);
|
||||
}
|
||||
}
|
||||
407
GeneralsMD/Code/Tools/WorldBuilder/src/PickUnitDialog.cpp
Normal file
407
GeneralsMD/Code/Tools/WorldBuilder/src/PickUnitDialog.cpp
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// PickUnitDialog.cpp : implementation file
|
||||
//
|
||||
|
||||
#define DEFINE_EDITOR_SORTING_NAMES
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "PickUnitDialog.h"
|
||||
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingSort.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PickUnitDialog dialog
|
||||
|
||||
|
||||
ReplaceUnitDialog::ReplaceUnitDialog(CWnd* pParent /*=NULL*/)
|
||||
: PickUnitDialog(IDD, pParent)
|
||||
{
|
||||
m_objectsList = NULL;
|
||||
m_currentObjectIndex = -1;
|
||||
m_currentObjectName[0] = 0;
|
||||
for (int i = ES_FIRST; i<ES_NUM_SORTING_TYPES; i++) {
|
||||
m_allowable[i] = false;
|
||||
}
|
||||
//{{AFX_DATA_INIT(PickUnitDialog)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
BOOL ReplaceUnitDialog::OnInitDialog()
|
||||
{
|
||||
PickUnitDialog::OnInitDialog();
|
||||
CWnd *pWnd = GetDlgItem(IDC_MISSINGLABEL);
|
||||
if (pWnd)
|
||||
pWnd->SetWindowText(m_missingName.str());
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(ReplaceUnitDialog, CDialog)
|
||||
//{{AFX_MSG_MAP(ReplaceUnitDialog)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
PickUnitDialog::PickUnitDialog(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(IDD, pParent)
|
||||
{
|
||||
m_objectsList = NULL;
|
||||
m_currentObjectIndex = -1;
|
||||
m_currentObjectName[0] = 0;
|
||||
for (int i = ES_FIRST; i<ES_NUM_SORTING_TYPES; i++) {
|
||||
m_allowable[i] = false;
|
||||
}
|
||||
//{{AFX_DATA_INIT(PickUnitDialog)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
PickUnitDialog::PickUnitDialog(UINT id, CWnd* pParent /*=NULL*/)
|
||||
: CDialog(id, pParent)
|
||||
{
|
||||
m_objectsList = NULL;
|
||||
m_currentObjectIndex = -1;
|
||||
m_currentObjectName[0] = 0;
|
||||
for (int i = ES_FIRST; i<ES_NUM_SORTING_TYPES; i++) {
|
||||
m_allowable[i] = false;
|
||||
}
|
||||
//{{AFX_DATA_INIT(PickUnitDialog)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
PickUnitDialog::~PickUnitDialog()
|
||||
{
|
||||
if (m_objectsList) {
|
||||
m_objectsList->deleteInstance();
|
||||
}
|
||||
m_objectsList = NULL;
|
||||
}
|
||||
|
||||
void PickUnitDialog::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(PickUnitDialog)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(PickUnitDialog, CDialog)
|
||||
//{{AFX_MSG_MAP(PickUnitDialog)
|
||||
ON_WM_MOVE()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
void PickUnitDialog::OnMove(int x, int y)
|
||||
{
|
||||
CDialog::OnMove(x, y);
|
||||
|
||||
if (this->IsWindowVisible() && !this->IsIconic()) {
|
||||
CRect frameRect;
|
||||
GetWindowRect(&frameRect);
|
||||
::AfxGetApp()->WriteProfileInt(BUILD_PICK_PANEL_SECTION, "Top", frameRect.top);
|
||||
::AfxGetApp()->WriteProfileInt(BUILD_PICK_PANEL_SECTION, "Left", frameRect.left);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Bool PickUnitDialog::IsAllowableType(EditorSortingType sort, Bool isBuildable)
|
||||
{
|
||||
if (m_factionOnly && !isBuildable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return (m_allowable[sort]);
|
||||
}
|
||||
|
||||
void PickUnitDialog::SetAllowableType(EditorSortingType sort)
|
||||
{
|
||||
m_allowable[sort] = true;
|
||||
}
|
||||
|
||||
void PickUnitDialog::SetupAsPanel(void)
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDCANCEL);
|
||||
if (pWnd) {
|
||||
pWnd->ShowWindow(SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PickUnitDialog message handlers
|
||||
|
||||
BOOL PickUnitDialog::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
// CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
|
||||
// add entries from the thing factory as the available objects to use
|
||||
const ThingTemplate *tTemplate;
|
||||
for( tTemplate = TheThingFactory->firstTemplate();
|
||||
tTemplate;
|
||||
tTemplate = tTemplate->friend_getNextTemplate() )
|
||||
{
|
||||
Coord3D loc = { 0, 0, 0 };
|
||||
MapObject *pMap;
|
||||
|
||||
EditorSortingType sort = tTemplate->getEditorSorting();
|
||||
if (!IsAllowableType(sort, tTemplate->isBuildableItem())) continue;
|
||||
|
||||
// create new map object
|
||||
pMap = newInstance(MapObject)( loc, tTemplate->getName(), 0.0f, 0, NULL, tTemplate );
|
||||
pMap->setNextMap( m_objectsList );
|
||||
m_objectsList = pMap;
|
||||
} // end for tTemplate
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_OBJECT_HEIGHT_EDIT);
|
||||
if (pWnd) {
|
||||
CString s;
|
||||
s.Format("%d",MAGIC_GROUND_Z);
|
||||
pWnd->SetWindowText(s);
|
||||
}
|
||||
|
||||
pWnd = GetDlgItem(IDC_OBJECT_TREEVIEW);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_objectTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_TERRAIN_TREEVIEW);
|
||||
m_objectTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
MapObject *pMap = m_objectsList;
|
||||
Int index = 0;
|
||||
while (pMap) {
|
||||
addObject( pMap, pMap->getName().str(), index, TVI_ROOT);
|
||||
index++;
|
||||
pMap = pMap->getNext();
|
||||
}
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
HTREEITEM PickUnitDialog::findOrAdd(HTREEITEM parent, const char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = m_objectTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = m_objectTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = (char*)pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = m_objectTreeView.InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
BOOL PickUnitDialog::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
if (pHdr->hdr.hwndFrom == m_objectTreeView.m_hWnd) {
|
||||
|
||||
if (pHdr->hdr.code == TVN_ITEMEXPANDED) {
|
||||
if (pHdr->action == TVE_COLLAPSE) {
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_STATE;
|
||||
item.hItem = pHdr->itemOld.hItem;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
item.state &= (~TVIS_EXPANDEDONCE);
|
||||
item.mask = TVIF_STATE;
|
||||
m_objectTreeView.SetItem(&item);
|
||||
}
|
||||
}
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
char buffer[NAME_MAX_LEN];
|
||||
HTREEITEM hItem = m_objectTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_objectTreeView.GetItem(&item);
|
||||
if (item.lParam >= 0) {
|
||||
m_currentObjectIndex = item.lParam;
|
||||
strcpy(m_currentObjectName, buffer);
|
||||
} else if (m_objectTreeView.ItemHasChildren(item.hItem)) {
|
||||
strcpy(m_currentObjectName, "");
|
||||
m_currentObjectIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Add the object hierarchy paths to the tree view. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void PickUnitDialog::addObject( MapObject *mapObject, const char *pPath, Int index, HTREEITEM parent )
|
||||
{
|
||||
char buffer[ _MAX_PATH ];
|
||||
const char *leafName = NULL;
|
||||
|
||||
// sanity
|
||||
if( mapObject == NULL )
|
||||
return;
|
||||
|
||||
//
|
||||
// if we have an thing template in mapObject, we've read it from the new INI database,
|
||||
// we will sort those items into the tree based on properties of the template that
|
||||
// make it easier for us to browse when building levels
|
||||
//
|
||||
// Feel free to reorganize how this tree is constructed from the template
|
||||
// data at will, whatever makes it easier for design
|
||||
//
|
||||
const ThingTemplate *thingTemplate = mapObject->getThingTemplate();
|
||||
if( thingTemplate )
|
||||
{
|
||||
|
||||
// first check for test sorted objects
|
||||
if( thingTemplate->getEditorSorting() == ES_TEST )
|
||||
parent = findOrAdd( parent, "TEST" );
|
||||
|
||||
// first sort by side, either create or find the tree item with matching side name
|
||||
AsciiString side = thingTemplate->getDefaultOwningSide();
|
||||
DEBUG_ASSERTCRASH( !side.isEmpty(), ("NULL default side in template\n") );
|
||||
strcpy( buffer, side.str() );
|
||||
parent = findOrAdd( parent, buffer );
|
||||
|
||||
// next tier uses the editor sorting that design can specify in the INI
|
||||
for( EditorSortingType i = ES_FIRST;
|
||||
i < ES_NUM_SORTING_TYPES;
|
||||
i = (EditorSortingType)(i + 1) )
|
||||
{
|
||||
|
||||
if( thingTemplate->getEditorSorting() == i )
|
||||
{
|
||||
|
||||
parent = findOrAdd( parent, EditorSortingNames[ i ] );
|
||||
break; // exit for
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
if( i == ES_NUM_SORTING_TYPES )
|
||||
parent = findOrAdd( parent, "UNSORTED" );
|
||||
|
||||
// the leaf name is the name of the template
|
||||
leafName = thingTemplate->getName().str();
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
// all these old entries we will put in a tree for legacy GDF items
|
||||
parent = findOrAdd( parent, "**TEST MODELS" );
|
||||
|
||||
Int i=0;
|
||||
leafName = pPath;
|
||||
while (pPath[i] && i<sizeof(buffer)) {
|
||||
if (pPath[i] == 0) {
|
||||
return;
|
||||
}
|
||||
if (pPath[i] == '/') {
|
||||
pPath+= i+1;
|
||||
i = 0;
|
||||
}
|
||||
buffer[i] = pPath[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
if( i > 0 )
|
||||
{
|
||||
buffer[ i ] = 0;
|
||||
leafName = buffer;
|
||||
|
||||
} // end if
|
||||
|
||||
} // end else if
|
||||
|
||||
// add to the tree view
|
||||
if( leafName )
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = index;
|
||||
ins.item.pszText = (char*)leafName;
|
||||
ins.item.cchTextMax = strlen(leafName)+2;
|
||||
m_objectTreeView.InsertItem(&ins);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AsciiString PickUnitDialog::getPickedUnit(void)
|
||||
{
|
||||
if (m_currentObjectIndex >= 0) {
|
||||
AsciiString retval(m_currentObjectName);
|
||||
return retval;
|
||||
}
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
const ThingTemplate* PickUnitDialog::getPickedThing(void)
|
||||
{
|
||||
if (m_currentObjectIndex >= 0) {
|
||||
const ThingTemplate *tTemplate;
|
||||
for( tTemplate = TheThingFactory->firstTemplate();
|
||||
tTemplate;
|
||||
tTemplate = tTemplate->friend_getNextTemplate() )
|
||||
{
|
||||
if (m_currentObjectName == tTemplate->getName())
|
||||
return tTemplate;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
550
GeneralsMD/Code/Tools/WorldBuilder/src/PointerTool.cpp
Normal file
550
GeneralsMD/Code/Tools/WorldBuilder/src/PointerTool.cpp
Normal file
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// PointerTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "PointerTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "Common/ThingSort.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "GameLogic/PolygonTrigger.h"
|
||||
#include "WBView3D.h"
|
||||
#include "ObjectTool.h"
|
||||
|
||||
//
|
||||
// Static helper functions
|
||||
// This function spiders out and un/picks all Waypoints that have some form of indirect contact with this point
|
||||
// Has a recursive helper function as well.
|
||||
//
|
||||
static void helper_pickAllWaypointsInPath( Int sourceID, CWorldBuilderDoc *pDoc, const Int numWaypointLinks, std::vector<Int>& alreadyTouched );
|
||||
|
||||
static void pickAllWaypointsInPath( Int sourceID, Bool select )
|
||||
{
|
||||
std::vector<Int> alreadyTouched;
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
|
||||
helper_pickAllWaypointsInPath(sourceID, pDoc, pDoc->getNumWaypointLinks(), alreadyTouched);
|
||||
|
||||
// already touched should now be filled with waypointIDs that want to be un/selected
|
||||
MapObject *pMapObj = MapObject::getFirstMapObject();
|
||||
while (pMapObj) {
|
||||
if (pMapObj->isWaypoint()) {
|
||||
if (std::find(alreadyTouched.begin(), alreadyTouched.end(), pMapObj->getWaypointID()) != alreadyTouched.end()) {
|
||||
pMapObj->setSelected(select);
|
||||
}
|
||||
}
|
||||
pMapObj = pMapObj->getNext();
|
||||
}
|
||||
}
|
||||
|
||||
static void helper_pickAllWaypointsInPath( Int sourceID, CWorldBuilderDoc *pDoc, const Int numWaypointLinks, std::vector<Int>& alreadyTouched )
|
||||
{
|
||||
if (std::find(alreadyTouched.begin(), alreadyTouched.end(), sourceID) != alreadyTouched.end() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
alreadyTouched.push_back(sourceID);
|
||||
for (int i = 0; i < numWaypointLinks; ++i) {
|
||||
Int way1, way2;
|
||||
pDoc->getWaypointLink(i, &way1, &way2);
|
||||
if (way1 == sourceID) {
|
||||
helper_pickAllWaypointsInPath(way2, pDoc, numWaypointLinks, alreadyTouched);
|
||||
}
|
||||
|
||||
if (way2 == sourceID) {
|
||||
helper_pickAllWaypointsInPath(way1, pDoc, numWaypointLinks, alreadyTouched);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// PointerTool class.
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
PointerTool::PointerTool(void) :
|
||||
m_modifyUndoable(NULL),
|
||||
m_curObject(NULL),
|
||||
m_rotateCursor(NULL),
|
||||
m_moveCursor(NULL)
|
||||
{
|
||||
m_toolID = ID_POINTER_TOOL;
|
||||
m_cursorID = IDC_POINTER;
|
||||
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
PointerTool::~PointerTool(void)
|
||||
{
|
||||
REF_PTR_RELEASE(m_modifyUndoable); // belongs to pDoc now.
|
||||
if (m_rotateCursor) {
|
||||
::DestroyCursor(m_rotateCursor);
|
||||
}
|
||||
if (m_moveCursor) {
|
||||
::DestroyCursor(m_moveCursor);
|
||||
}
|
||||
}
|
||||
|
||||
/// See if a single obj is selected that has properties.
|
||||
void PointerTool::checkForPropertiesPanel(void)
|
||||
{
|
||||
MapObject *theMapObj = WaypointOptions::getSingleSelectedWaypoint();
|
||||
PolygonTrigger *theTrigger = WaypointOptions::getSingleSelectedPolygon();
|
||||
MapObject *theLightObj = LightOptions::getSingleSelectedLight();
|
||||
MapObject *theObj = MapObjectProps::getSingleSelectedMapObject();
|
||||
if (theMapObj) {
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_WAYPOINT_OPTIONS);
|
||||
WaypointOptions::update();
|
||||
} else if (theTrigger) {
|
||||
if (theTrigger->isWaterArea()) {
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_WATER_OPTIONS);
|
||||
WaterOptions::update();
|
||||
} else {
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_WAYPOINT_OPTIONS);
|
||||
WaypointOptions::update();
|
||||
}
|
||||
} else if (theLightObj) {
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_LIGHT_OPTIONS);
|
||||
LightOptions::update();
|
||||
} else if (RoadOptions::selectionIsRoadsOnly()) {
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_ROAD_OPTIONS);
|
||||
RoadOptions::updateSelection();
|
||||
} else {
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_MAPOBJECT_PROPS);
|
||||
MapObjectProps::update();
|
||||
if (theObj) {
|
||||
ObjectOptions::selectObject(theObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the selection..
|
||||
void PointerTool::clearSelection(void) ///< Clears the selected objects selected flags.
|
||||
{
|
||||
// Clear selection.
|
||||
MapObject *pObj = MapObject::getFirstMapObject();
|
||||
while (pObj) {
|
||||
if (pObj->isSelected()) {
|
||||
pObj->setSelected(false);
|
||||
}
|
||||
pObj = pObj->getNext();
|
||||
}
|
||||
// Clear selected build list items.
|
||||
Int i;
|
||||
for (i=0; i<TheSidesList->getNumSides(); i++) {
|
||||
SidesInfo *pSide = TheSidesList->getSideInfo(i);
|
||||
for (BuildListInfo *pBuild = pSide->getBuildList(); pBuild; pBuild = pBuild->getNext()) {
|
||||
if (pBuild->isSelected()) {
|
||||
pBuild->setSelected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_poly_curSelectedPolygon = NULL;
|
||||
}
|
||||
|
||||
/// Activate.
|
||||
void PointerTool::activate()
|
||||
{
|
||||
Tool::activate();
|
||||
m_mouseUpRotate = false;
|
||||
m_mouseUpMove = false;
|
||||
checkForPropertiesPanel();
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc==NULL) return;
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
p3View->setObjTracking(NULL, m_downPt3d, 0, false);
|
||||
}
|
||||
|
||||
/// deactivate.
|
||||
void PointerTool::deactivate()
|
||||
{
|
||||
m_curObject = NULL;
|
||||
PolygonTool::deactivate();
|
||||
}
|
||||
|
||||
/** Set the cursor. */
|
||||
void PointerTool::setCursor(void)
|
||||
{
|
||||
if (m_mouseUpRotate) {
|
||||
if (m_rotateCursor == NULL) {
|
||||
m_rotateCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_ROTATE));
|
||||
}
|
||||
::SetCursor(m_rotateCursor);
|
||||
} else if (m_mouseUpMove) {
|
||||
if (m_moveCursor == NULL) {
|
||||
m_moveCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_MOVE_POINTER));
|
||||
}
|
||||
::SetCursor(m_moveCursor);
|
||||
} else {
|
||||
Tool::setCursor();
|
||||
}
|
||||
}
|
||||
|
||||
Bool PointerTool::allowPick(MapObject* pMapObj, WbView* pView)
|
||||
{
|
||||
EditorSortingType sort = ES_NONE;
|
||||
if (!pMapObj) {
|
||||
return false;
|
||||
}
|
||||
const ThingTemplate *tt = pMapObj->getThingTemplate();
|
||||
if (tt && tt->getEditorSorting() == ES_AUDIO) {
|
||||
if (pView->GetPickConstraint() == ES_NONE || pView->GetPickConstraint() == ES_AUDIO) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((tt && !pView->getShowModels()) || (pMapObj->getFlags() & FLAG_DONT_RENDER)) {
|
||||
return false;
|
||||
}
|
||||
if (pView->GetPickConstraint() != ES_NONE) {
|
||||
if (tt) {
|
||||
if (!pView->getShowModels()) {
|
||||
return false;
|
||||
}
|
||||
sort = tt->getEditorSorting();
|
||||
} else {
|
||||
if (pMapObj->isWaypoint()) {
|
||||
sort = ES_WAYPOINT;
|
||||
}
|
||||
if (pMapObj->getFlag(FLAG_ROAD_FLAGS)) {
|
||||
sort = ES_ROAD;
|
||||
}
|
||||
}
|
||||
if (sort != ES_NONE && sort != pView->GetPickConstraint()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse down - Pick an object. */
|
||||
void PointerTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
Coord3D loc;
|
||||
|
||||
m_downPt2d = viewPt;
|
||||
m_downPt3d = cpt;
|
||||
pView->snapPoint(&m_downPt3d);
|
||||
m_moving = false;
|
||||
m_rotating = false;
|
||||
m_dragSelect = false;
|
||||
Bool shiftKey = (0x8000 & ::GetAsyncKeyState(VK_SHIFT))!=0;
|
||||
Bool ctrlKey = (0x8000 & ::GetAsyncKeyState(VK_CONTROL))!=0;
|
||||
|
||||
m_doPolyTool = false;
|
||||
if (pView->GetPickConstraint() == ES_NONE || pView->GetPickConstraint() == ES_WAYPOINT) {
|
||||
// If polygon triggers are visible, see if we clicked on one.
|
||||
if (pView->isPolygonTriggerVisible()) {
|
||||
m_poly_unsnappedMouseDownPt = cpt;
|
||||
poly_pickOnMouseDown(viewPt, pView);
|
||||
if (m_poly_curSelectedPolygon) {
|
||||
// picked on one.
|
||||
if (!poly_snapToPoly(&cpt)) {
|
||||
pView->snapPoint(&cpt);
|
||||
}
|
||||
m_poly_mouseDownPt = cpt;
|
||||
m_poly_justPicked = true; // Makes poly tool move instead of inserting.
|
||||
m_doPolyTool = true;
|
||||
PolygonTool::startMouseDown(m, viewPt, pView, pDoc);
|
||||
return;
|
||||
}
|
||||
m_poly_curSelectedPolygon = NULL;
|
||||
m_poly_dragPointNdx = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
m_curObject = NULL;
|
||||
MapObject *pObj = MapObject::getFirstMapObject();
|
||||
MapObject *p3DObj = pView->picked3dObjectInView(viewPt);
|
||||
MapObject *pClosestPicked = NULL;
|
||||
if (allowPick(p3DObj, pView)) {
|
||||
pClosestPicked = p3DObj;
|
||||
}
|
||||
Real pickDistSqr = 10000*MAP_XY_FACTOR;
|
||||
pickDistSqr *= pickDistSqr;
|
||||
|
||||
// Find the closest pick.
|
||||
for (pObj = MapObject::getFirstMapObject(); pObj; pObj = pObj->getNext()) {
|
||||
if (!allowPick(pObj, pView)) {
|
||||
continue;
|
||||
}
|
||||
Bool picked = (pView->picked(pObj, cpt) != PICK_NONE);
|
||||
if (picked) {
|
||||
loc = *pObj->getLocation();
|
||||
Real dx = m_downPt3d.x-loc.x;
|
||||
Real dy = m_downPt3d.y-loc.y;
|
||||
Real distSqr = dx*dx+dy*dy;
|
||||
if (distSqr < pickDistSqr) {
|
||||
pClosestPicked = pObj;
|
||||
pickDistSqr = distSqr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bool anySelected = (pClosestPicked!=NULL);
|
||||
if (shiftKey) {
|
||||
if (pClosestPicked && pClosestPicked->isSelected()) {
|
||||
pClosestPicked->setSelected(false);
|
||||
if (ctrlKey && pClosestPicked->isWaypoint()) {
|
||||
pickAllWaypointsInPath(pClosestPicked->getWaypointID(), false);
|
||||
}
|
||||
} else if (pClosestPicked) {
|
||||
pClosestPicked->setSelected(true);
|
||||
if (ctrlKey && pClosestPicked->isWaypoint()) {
|
||||
pickAllWaypointsInPath(pClosestPicked->getWaypointID(), true);
|
||||
}
|
||||
}
|
||||
} else if (pClosestPicked && pClosestPicked->isSelected()) {
|
||||
// We picked a selected object
|
||||
m_curObject = pClosestPicked;
|
||||
} else {
|
||||
clearSelection();
|
||||
if (pClosestPicked) {
|
||||
pClosestPicked->setSelected(true);
|
||||
if (ctrlKey && pClosestPicked->isWaypoint()) {
|
||||
pickAllWaypointsInPath(pClosestPicked->getWaypointID(), true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Grab both ends of a road.
|
||||
if (pView->GetPickConstraint() == ES_NONE || pView->GetPickConstraint() == ES_ROAD) {
|
||||
if (!shiftKey && pClosestPicked && (pClosestPicked->getFlags()&FLAG_ROAD_FLAGS) ) {
|
||||
for (pObj = MapObject::getFirstMapObject(); pObj; pObj = pObj->getNext()) {
|
||||
if (pObj->getFlags()&FLAG_ROAD_FLAGS) {
|
||||
loc = *pObj->getLocation();
|
||||
Real dx = pClosestPicked->getLocation()->x - loc.x;
|
||||
Real dy = pClosestPicked->getLocation()->y - loc.y;
|
||||
Real dist = sqrt(dx*dx+dy*dy);
|
||||
if (dist < MAP_XY_FACTOR/100) {
|
||||
pObj->setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anySelected) {
|
||||
if (m_curObject) {
|
||||
// See if we are picking on the arrow.
|
||||
if (pView->picked(m_curObject, cpt) == PICK_ARROW) {
|
||||
m_rotating = true;
|
||||
}
|
||||
} else {
|
||||
pObj = MapObject::getFirstMapObject();
|
||||
while (pObj) {
|
||||
if (pObj->isSelected()) {
|
||||
m_curObject = pObj;
|
||||
break;
|
||||
}
|
||||
pObj = pObj->getNext();
|
||||
}
|
||||
}
|
||||
if (m_curObject) {
|
||||
// adjust the starting point so if we are snapping, the object snaps as well.
|
||||
loc = *m_curObject->getLocation();
|
||||
Coord3D snapLoc = loc;
|
||||
pView->snapPoint(&snapLoc);
|
||||
m_downPt3d.x += (loc.x-snapLoc.x);
|
||||
m_downPt3d.y += (loc.y-snapLoc.y);
|
||||
}
|
||||
} else {
|
||||
m_dragSelect = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Left button move code.
|
||||
void PointerTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt, false);
|
||||
if (m == TRACK_NONE) {
|
||||
// See if the cursor is over an object.
|
||||
MapObject *pObj = MapObject::getFirstMapObject();
|
||||
m_mouseUpRotate = false;
|
||||
m_mouseUpMove = false;
|
||||
while (pObj) {
|
||||
if (allowPick(pObj, pView)) {
|
||||
TPickedStatus stat = pView->picked(pObj, cpt);
|
||||
if (stat==PICK_ARROW) {
|
||||
m_mouseUpRotate = true;
|
||||
break;
|
||||
}
|
||||
if (stat==PICK_CENTER) {
|
||||
m_mouseUpMove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pObj = pObj->getNext();
|
||||
}
|
||||
if (!m_mouseUpRotate) {
|
||||
pObj = pView->picked3dObjectInView(viewPt);
|
||||
if (allowPick(pObj, pView)) {
|
||||
m_mouseUpMove = true;
|
||||
}
|
||||
}
|
||||
if (pView->isPolygonTriggerVisible() && pickPolygon(cpt, viewPt, pView)) {
|
||||
if (pView->GetPickConstraint() == ES_NONE || pView->GetPickConstraint() == ES_WAYPOINT) {
|
||||
m_mouseUpMove = true;
|
||||
m_mouseUpRotate = false;
|
||||
}
|
||||
}
|
||||
return; // setCursor will use the value of m_mouseUpRotate. jba.
|
||||
}
|
||||
|
||||
if (m != TRACK_L) return;
|
||||
if (m_doPolyTool) {
|
||||
PolygonTool::mouseMoved(m, viewPt, pView, pDoc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_dragSelect) {
|
||||
CRect box;
|
||||
box.left = viewPt.x;
|
||||
box.bottom = viewPt.y;
|
||||
box.top = m_downPt2d.y;
|
||||
box.right = m_downPt2d.x;
|
||||
box.NormalizeRect();
|
||||
pView->doRectFeedback(true, box);
|
||||
pView->Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_curObject == NULL) {
|
||||
return;
|
||||
}
|
||||
pView->viewToDocCoords(viewPt, &cpt, !m_rotating);
|
||||
if (!m_moving) {
|
||||
// always use view coords (not doc coords) for hysteresis
|
||||
Int dx = viewPt.x-m_downPt2d.x;
|
||||
Int dy = viewPt.y-m_downPt2d.y;
|
||||
if (abs(dx)>HYSTERESIS || abs(dy)>HYSTERESIS) {
|
||||
m_moving = true;
|
||||
m_modifyUndoable = new ModifyObjectUndoable(pDoc);
|
||||
}
|
||||
}
|
||||
if (!m_moving || !m_modifyUndoable) return;
|
||||
|
||||
MapObject *curMapObj = MapObject::getFirstMapObject();
|
||||
while (curMapObj) {
|
||||
if (curMapObj->isSelected()) {
|
||||
//pDoc->invalObject(curMapObj); // invaling in all views can be too slow.
|
||||
pView->invalObjectInView(curMapObj);
|
||||
}
|
||||
curMapObj = curMapObj->getNext();
|
||||
}
|
||||
|
||||
if (m_rotating) {
|
||||
Coord3D center = *m_curObject->getLocation();
|
||||
m_modifyUndoable->RotateTo(ObjectTool::calcAngle(center, cpt, pView));
|
||||
} else {
|
||||
pView->snapPoint(&cpt);
|
||||
Real xOffset = (cpt.x-m_downPt3d.x);
|
||||
Real yOffset = (cpt.y-m_downPt3d.y);
|
||||
m_modifyUndoable->SetOffset(xOffset, yOffset);
|
||||
}
|
||||
|
||||
curMapObj = MapObject::getFirstMapObject();
|
||||
while (curMapObj) {
|
||||
if (curMapObj->isSelected()) {
|
||||
//pDoc->invalObject(curMapObj); // invaling in all views can be too slow.
|
||||
pView->invalObjectInView(curMapObj);
|
||||
}
|
||||
curMapObj = curMapObj->getNext();
|
||||
}
|
||||
|
||||
pDoc->updateAllViews();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Execute the tool on mouse up - if modifying, do the modify,
|
||||
else update the selection. */
|
||||
void PointerTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
if (m_doPolyTool) {
|
||||
m_doPolyTool = false;
|
||||
PolygonTool::mouseUp(m, viewPt, pView, pDoc);
|
||||
checkForPropertiesPanel();
|
||||
return;
|
||||
}
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
|
||||
if (m_curObject && m_moving) {
|
||||
pDoc->AddAndDoUndoable(m_modifyUndoable);
|
||||
REF_PTR_RELEASE(m_modifyUndoable); // belongs to pDoc now.
|
||||
} else if (m_dragSelect) {
|
||||
CRect box;
|
||||
box.left = viewPt.x;
|
||||
box.top = viewPt.y;
|
||||
box.right = m_downPt2d.x;
|
||||
box.bottom = m_downPt2d.y;
|
||||
box.NormalizeRect();
|
||||
pView->doRectFeedback(false, box);
|
||||
pView->Invalidate();
|
||||
|
||||
MapObject *pObj;
|
||||
for (pObj = MapObject::getFirstMapObject(); pObj; pObj = pObj->getNext()) {
|
||||
// Don't pick on invisible waypoints
|
||||
if (pObj->isWaypoint() && !pView->isWaypointVisible()) {
|
||||
continue;
|
||||
}
|
||||
if (!allowPick(pObj, pView)) {
|
||||
continue;
|
||||
}
|
||||
Bool picked;
|
||||
Coord3D loc = *pObj->getLocation();
|
||||
CPoint viewPt;
|
||||
if (pView->docToViewCoords(loc, &viewPt)){
|
||||
picked = (viewPt.x>=box.left && viewPt.x<=box.right && viewPt.y>=box.top && viewPt.y<=box.bottom) ;
|
||||
if (picked) {
|
||||
if ((0x8000 && ::GetAsyncKeyState(VK_SHIFT))) {
|
||||
pObj->setSelected(!pObj->isSelected());
|
||||
} else {
|
||||
pObj->setSelected(true);
|
||||
}
|
||||
pDoc->invalObject(pObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
checkForPropertiesPanel();
|
||||
}
|
||||
|
||||
469
GeneralsMD/Code/Tools/WorldBuilder/src/PolygonTool.cpp
Normal file
469
GeneralsMD/Code/Tools/WorldBuilder/src/PolygonTool.cpp
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// PolygonTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "PolygonTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "PointerTool.h"
|
||||
#include "TerrainMaterial.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "MainFrm.h"
|
||||
#include "DrawObject.h"
|
||||
#include "GameLogic/PolygonTrigger.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
|
||||
//
|
||||
// PolygonTool class.
|
||||
//
|
||||
|
||||
Bool PolygonTool::m_poly_isActive = false;
|
||||
Bool PolygonTool::m_poly_isAdding = false;
|
||||
PolygonTrigger *PolygonTool::m_poly_curSelectedPolygon = NULL;
|
||||
Int PolygonTool::m_poly_dragPointNdx = -1;
|
||||
|
||||
enum {SNAP_DISTANCE = 5};
|
||||
|
||||
/// Constructor
|
||||
PolygonTool::PolygonTool(void) :
|
||||
Tool(ID_POLYGON_TOOL, IDC_POLYGON),
|
||||
m_poly_plusCursor(NULL),
|
||||
m_poly_moveCursor(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Destructor
|
||||
PolygonTool::~PolygonTool(void)
|
||||
{
|
||||
if (m_poly_plusCursor) {
|
||||
::DestroyCursor(m_poly_plusCursor);
|
||||
}
|
||||
if (m_poly_moveCursor) {
|
||||
::DestroyCursor(m_poly_moveCursor);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears it's is active flag.
|
||||
void PolygonTool::deactivate()
|
||||
{
|
||||
m_poly_dragPointNdx = -1;
|
||||
if (m_poly_curSelectedPolygon) {
|
||||
Int numPoints = m_poly_curSelectedPolygon->getNumPoints();
|
||||
if (numPoints < 3) {
|
||||
deleteSelectedPolygon();
|
||||
}
|
||||
}
|
||||
m_poly_isActive = false;
|
||||
m_poly_curSelectedPolygon = NULL;
|
||||
}
|
||||
|
||||
/// Shows the terrain materials options panel.
|
||||
void PolygonTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_WAYPOINT_OPTIONS);
|
||||
WaypointOptions::update();
|
||||
if (!m_poly_curSelectedPolygon) {
|
||||
PointerTool::clearSelection();
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
m_poly_isActive = true;
|
||||
m_poly_isAdding = false;
|
||||
m_poly_mouseUpMove = false;
|
||||
m_poly_mouseUpPlus = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Pick a polygon.
|
||||
Bool PolygonTool::poly_pickPoly(PolygonTrigger *pTrig, Coord3D loc, Int tolerance) {
|
||||
ICoord3D iDocPt;
|
||||
iDocPt.x = floor(loc.x+0.5f);
|
||||
iDocPt.y = floor(loc.y+0.5f);
|
||||
iDocPt.z = floor(loc.z+0.5f);
|
||||
Int i, j;
|
||||
ICoord3D ptArray[9];
|
||||
for (i=0; i<3; i++) {
|
||||
for (j=0; j<3; j++) {
|
||||
ptArray[i+3*j].x = iDocPt.x + (1-i)*tolerance;
|
||||
ptArray[i+3*j].y = iDocPt.y + (1-j)*tolerance;
|
||||
ptArray[i+3*j].z = iDocPt.z;
|
||||
}
|
||||
}
|
||||
int count = 0;
|
||||
for (i=0; i<9; i++) {
|
||||
if (pTrig->pointInTrigger(ptArray[i])) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count>0 && count<8) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Pick a polygon.
|
||||
PolygonTrigger *PolygonTool::pickPolygon(Coord3D loc, CPoint viewPt, WbView* pView) {
|
||||
for (PolygonTrigger *pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
|
||||
// For water, only do water
|
||||
if (WaterTool::isActive() && !pTrig->isWaterArea()) {
|
||||
continue;
|
||||
}
|
||||
if (poly_pickPoly(pTrig, loc, SNAP_DISTANCE/2)) {
|
||||
return pTrig;
|
||||
}
|
||||
}
|
||||
for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
|
||||
if (WaterTool::isActive() && !pTrig->isWaterArea()) {
|
||||
continue;
|
||||
}
|
||||
Coord3D docPt = loc;
|
||||
Int pt = poly_pickPoint(pTrig, viewPt, pView);
|
||||
if (pt>=0) {
|
||||
return pTrig;
|
||||
}
|
||||
if (poly_pickPoly(pTrig, docPt, 3*SNAP_DISTANCE/2)) {
|
||||
return pTrig;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Snap to other polys.
|
||||
Bool PolygonTool::poly_snapToPoly(Coord3D *pLoc) {
|
||||
for (PolygonTrigger *pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
|
||||
Int i;
|
||||
// Dont' snap to self.
|
||||
if (pTrig == m_poly_curSelectedPolygon) continue;
|
||||
for (i=0; i<pTrig->getNumPoints(); i++) {
|
||||
ICoord3D iLoc = *pTrig->getPoint(i);
|
||||
Coord3D cpt = *pLoc;
|
||||
cpt.x -= iLoc.x;
|
||||
cpt.y -= iLoc.y;
|
||||
cpt.z = 0;
|
||||
if (cpt.length() < 1.0f*SNAP_DISTANCE) {
|
||||
pLoc->x = iLoc.x;
|
||||
pLoc->y = iLoc.y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pick a point.
|
||||
Int PolygonTool::poly_pickPoint(PolygonTrigger *pTrig, CPoint viewPt, WbView* pView) {
|
||||
if (pTrig==NULL) return -1;
|
||||
Int i;
|
||||
const Int PICK_PIXELS = pView->getPickPixels();
|
||||
for (i=0; i<pTrig->getNumPoints(); i++) {
|
||||
ICoord3D iLoc = *pTrig->getPoint(i);
|
||||
Coord3D cpt;
|
||||
cpt.x = iLoc.x;
|
||||
cpt.y = iLoc.y;
|
||||
cpt.z = iLoc.z;
|
||||
cpt.z = 0;
|
||||
CPoint screenLoc;
|
||||
pView->docToViewCoords(cpt, &screenLoc);
|
||||
Int dx = screenLoc.x-viewPt.x;
|
||||
Int dy = screenLoc.y - viewPt.y;
|
||||
if (dy<0) dy = -dy;
|
||||
if (dx<0) dx = -dx;
|
||||
if (dx<=PICK_PIXELS && dy<= PICK_PIXELS) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the point to insert closest to loc.
|
||||
Int PolygonTool::poly_getInsertIndex(PolygonTrigger *pTrig, Coord3D loc) {
|
||||
if (pTrig==NULL) return 0;
|
||||
static Int tolerance = SNAP_DISTANCE;
|
||||
|
||||
Int i;
|
||||
for (i=0; i<pTrig->getNumPoints(); i++) {
|
||||
ICoord3D pt1 = *pTrig->getPoint(i);
|
||||
ICoord3D pt2;
|
||||
Int theNdx = i+1;
|
||||
if (i==pTrig->getNumPoints()-1) {
|
||||
pt2 = *pTrig->getPoint(0);
|
||||
theNdx = 0;
|
||||
} else {
|
||||
pt2 = *pTrig->getPoint(i+1);
|
||||
}
|
||||
Int minX = loc.x-tolerance;
|
||||
if (pt1.x<minX && pt2.x<minX) continue;
|
||||
Int maxX = loc.x+tolerance;
|
||||
if (pt1.x>maxX && pt2.x>maxX) continue;
|
||||
Int minY = loc.y-tolerance;
|
||||
if (pt1.y<minY && pt2.y<minY) continue;
|
||||
Int maxY = loc.y+tolerance;
|
||||
if (pt1.y>maxY && pt2.y>maxY) continue;
|
||||
Int dy = pt2.y-pt1.y;
|
||||
Int dx = pt2.x-pt1.x;
|
||||
if (abs(dy)>abs(dx)) {
|
||||
Real intersectionX = pt1.x + (dx * (loc.y-pt1.y)) / ((Real)dy);
|
||||
if (intersectionX >= minX && intersectionX <= maxX) {
|
||||
return theNdx;
|
||||
}
|
||||
} else {
|
||||
Real intersectionY = pt1.y + (dy * (loc.x-pt1.x)) / ((Real)dx);
|
||||
if (intersectionY >= minY && intersectionY <= maxY) {
|
||||
return theNdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Do the pick on poly triggers. */
|
||||
void PolygonTool::poly_pickOnMouseDown(CPoint viewPt, WbView* pView)
|
||||
{
|
||||
m_poly_dragPointNdx = -1;
|
||||
if (m_poly_curSelectedPolygon) {
|
||||
Bool found = false;
|
||||
for (PolygonTrigger *pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
|
||||
if (m_poly_curSelectedPolygon == pTrig) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
m_poly_curSelectedPolygon = NULL; // Undo probably made it go away.
|
||||
}
|
||||
}
|
||||
m_poly_justPicked = false; // Always false here. Pointer tool sets to true.
|
||||
if (m_poly_curSelectedPolygon == NULL || !m_poly_isAdding) {
|
||||
PolygonTrigger *pSel;
|
||||
Coord3D docPt = m_poly_unsnappedMouseDownPt;
|
||||
if (m_poly_curSelectedPolygon && poly_pickPoly(m_poly_curSelectedPolygon, docPt, SNAP_DISTANCE)) {
|
||||
pSel = m_poly_curSelectedPolygon;
|
||||
} else {
|
||||
pSel = pickPolygon(docPt, viewPt, pView);
|
||||
}
|
||||
m_poly_curSelectedPolygon = pSel;
|
||||
m_poly_isAdding = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Perform the tool behavior on mouse down.
|
||||
void PolygonTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
m_poly_unsnappedMouseDownPt = docPt;
|
||||
poly_pickOnMouseDown(viewPt, pView);
|
||||
if (!poly_snapToPoly(&docPt)) {
|
||||
pView->snapPoint(&docPt);
|
||||
}
|
||||
m_poly_mouseDownPt = docPt;
|
||||
startMouseDown(m, viewPt, pView, pDoc);
|
||||
}
|
||||
|
||||
/// Perform the tool behavior on mouse down.
|
||||
void PolygonTool::startMouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
// Clear any selected map objects.
|
||||
if (!m_poly_curSelectedPolygon) PointerTool::clearSelection();
|
||||
|
||||
ICoord3D iDocPt;
|
||||
iDocPt.x = floor(m_poly_mouseDownPt.x+0.5f);
|
||||
iDocPt.y = floor(m_poly_mouseDownPt.y+0.5f);
|
||||
iDocPt.z = floor(m_poly_mouseDownPt.z+0.5f);
|
||||
if (m_poly_curSelectedPolygon && m_poly_curSelectedPolygon->isWaterArea()) {
|
||||
iDocPt.z = m_poly_curSelectedPolygon->getPoint(0)->z;
|
||||
}
|
||||
m_poly_dragPointNdx = -1;
|
||||
m_poly_moveUndoable = NULL;
|
||||
if (m_poly_curSelectedPolygon==NULL) {
|
||||
// adding a new polygon.
|
||||
m_poly_curSelectedPolygon = newInstance(PolygonTrigger)(32);
|
||||
AsciiString name;
|
||||
name.format("Area %d", m_poly_curSelectedPolygon->getID());
|
||||
m_poly_curSelectedPolygon->setTriggerName(name);
|
||||
m_poly_curSelectedPolygon->addPoint(iDocPt);
|
||||
m_poly_dragPointNdx = 0;
|
||||
AddPolygonUndoable *pUndo = new AddPolygonUndoable(m_poly_curSelectedPolygon);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_poly_isAdding = true;
|
||||
} else {
|
||||
m_poly_dragPointNdx = poly_pickPoint(m_poly_curSelectedPolygon, viewPt, pView);
|
||||
if (m_poly_isAdding) {
|
||||
if (m_poly_dragPointNdx == 0) {
|
||||
// closing the poly.
|
||||
m_poly_isAdding = false;
|
||||
} else {
|
||||
m_poly_dragPointNdx = -1;
|
||||
}
|
||||
}
|
||||
if (m_poly_isAdding && m_poly_dragPointNdx == -1) {
|
||||
// Continuing to add points.
|
||||
m_poly_dragPointNdx = m_poly_curSelectedPolygon->getNumPoints();
|
||||
AddPolygonPointUndoable *pUndo = new AddPolygonPointUndoable(m_poly_curSelectedPolygon, iDocPt);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
} else if (m_poly_dragPointNdx != -1) {
|
||||
// Moving a point on the polygon.
|
||||
ModifyPolygonPointUndoable *pUndo = new ModifyPolygonPointUndoable(m_poly_curSelectedPolygon, m_poly_dragPointNdx);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
} else if (m_poly_justPicked) {
|
||||
// Move the entire polygon.
|
||||
m_poly_moveUndoable = new MovePolygonUndoable(m_poly_curSelectedPolygon);
|
||||
pDoc->AddAndDoUndoable(m_poly_moveUndoable);
|
||||
} else {
|
||||
// Insert a point.
|
||||
Coord3D docPt = m_poly_unsnappedMouseDownPt;
|
||||
m_poly_dragPointNdx = poly_getInsertIndex(m_poly_curSelectedPolygon, docPt);
|
||||
InsertPolygonPointUndoable *pUndo = new InsertPolygonPointUndoable(m_poly_curSelectedPolygon, iDocPt, m_poly_dragPointNdx);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
}
|
||||
}
|
||||
WaypointOptions::update();
|
||||
}
|
||||
|
||||
/// Delete the selected polygon or point.
|
||||
Bool PolygonTool::deleteSelectedPolygon(void)
|
||||
{
|
||||
if (m_poly_curSelectedPolygon) {
|
||||
Bool found = false;
|
||||
for (PolygonTrigger *pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
|
||||
if (m_poly_curSelectedPolygon == pTrig) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
m_poly_curSelectedPolygon = NULL; // Undo probably made it go away.
|
||||
}
|
||||
}
|
||||
if (m_poly_curSelectedPolygon == NULL) {
|
||||
return false;
|
||||
}
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (!pDoc) return false;
|
||||
if (m_poly_dragPointNdx != -1) {
|
||||
// Moving a point on the polygon.
|
||||
DeletePolygonPointUndoable *pUndo = new DeletePolygonPointUndoable(m_poly_curSelectedPolygon, m_poly_dragPointNdx);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_poly_dragPointNdx = -1;
|
||||
} else {
|
||||
// delete the polygon.
|
||||
DeletePolygonUndoable *pUndo = new DeletePolygonUndoable(m_poly_curSelectedPolygon);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_poly_curSelectedPolygon = NULL;
|
||||
m_poly_dragPointNdx = -1;
|
||||
}
|
||||
WaypointOptions::update();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Set the cursor. */
|
||||
void PolygonTool::setCursor(void)
|
||||
{
|
||||
if (m_poly_mouseUpPlus || (m_poly_isAdding && m_poly_curSelectedPolygon)) {
|
||||
if (m_poly_plusCursor == NULL) {
|
||||
m_poly_plusCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_POLYGON_PLUS));
|
||||
}
|
||||
::SetCursor(m_poly_plusCursor);
|
||||
} else if (m_poly_mouseUpMove) {
|
||||
if (m_poly_moveCursor == NULL) {
|
||||
m_poly_moveCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_POLYGON_MOVE));
|
||||
}
|
||||
::SetCursor(m_poly_moveCursor);
|
||||
} else {
|
||||
Tool::setCursor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Left button move code.
|
||||
void PolygonTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
if (m == TRACK_NONE) {
|
||||
PolygonTrigger *pCur = m_poly_curSelectedPolygon;
|
||||
Int curPt = m_poly_dragPointNdx;
|
||||
m_poly_unsnappedMouseDownPt = docPt;
|
||||
poly_pickOnMouseDown(viewPt, pView);
|
||||
m_poly_mouseUpPlus = false;
|
||||
m_poly_mouseUpMove = false;
|
||||
if (m_poly_curSelectedPolygon) {
|
||||
Int pt = poly_pickPoint(m_poly_curSelectedPolygon, viewPt, pView);
|
||||
if (pt >= 0) {
|
||||
m_poly_mouseUpMove = true;
|
||||
} else {
|
||||
m_poly_mouseUpPlus = true;
|
||||
}
|
||||
}
|
||||
m_poly_curSelectedPolygon = pCur;
|
||||
m_poly_dragPointNdx = curPt;
|
||||
return; // setCursor will use the value of m_mouseUpRotate. jba.
|
||||
}
|
||||
|
||||
if (m != TRACK_L) return;
|
||||
if (!poly_snapToPoly(&docPt)) {
|
||||
pView->snapPoint(&docPt);
|
||||
}
|
||||
if (m_poly_moveUndoable) {
|
||||
ICoord3D iDocPt;
|
||||
iDocPt.x = floor(docPt.x+0.5f-m_poly_mouseDownPt.x);
|
||||
iDocPt.y = floor(docPt.y+0.5f-m_poly_mouseDownPt.y);
|
||||
iDocPt.z = 0;
|
||||
m_poly_moveUndoable->SetOffset(iDocPt);
|
||||
pView->Invalidate();
|
||||
return;
|
||||
}
|
||||
if (m_poly_dragPointNdx >= 0 && m_poly_curSelectedPolygon) {
|
||||
ICoord3D iDocPt;
|
||||
iDocPt.x = floor(docPt.x+0.5f);
|
||||
iDocPt.y = floor(docPt.y+0.5f);
|
||||
iDocPt.z = floor(docPt.z+0.5f);
|
||||
if (m_poly_curSelectedPolygon->isWaterArea()) {
|
||||
iDocPt.z = m_poly_curSelectedPolygon->getPoint(m_poly_dragPointNdx)->z;
|
||||
}
|
||||
m_poly_curSelectedPolygon->setPoint(iDocPt, m_poly_dragPointNdx);
|
||||
pView->Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/** Mouse up - not much. */
|
||||
void PolygonTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
REF_PTR_RELEASE(m_poly_moveUndoable); // belongs to pDoc now.
|
||||
}
|
||||
|
||||
87
GeneralsMD/Code/Tools/WorldBuilder/src/RampOptions.cpp
Normal file
87
GeneralsMD/Code/Tools/WorldBuilder/src/RampOptions.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FILE: RampOptions.cpp
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* EA Pacific */
|
||||
/* Confidential Information */
|
||||
/* Copyright (C) 2001 - All Rights Reserved */
|
||||
/* DO NOT DISTRIBUTE */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Project: RTS3 */
|
||||
/* File name: RampOptions.cpp */
|
||||
/* Created: John K. McDonald, Jr., 4/23/2002 */
|
||||
/* Desc: // Ramp options. Contains the Apply button */
|
||||
/* Revision History: */
|
||||
/* 4/23/2002 : Initial creation */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "RampOptions.h"
|
||||
|
||||
RampOptions::RampOptions(CWnd* pParent) : COptionsPanel(RampOptions::IDD, pParent)
|
||||
{
|
||||
if (TheRampOptions) {
|
||||
// oh shit.
|
||||
return;
|
||||
}
|
||||
|
||||
TheRampOptions = this;
|
||||
m_rampWidth = 20;
|
||||
m_shouldApplyTheRamp = false;
|
||||
}
|
||||
|
||||
RampOptions::~RampOptions()
|
||||
{
|
||||
TheRampOptions = NULL;
|
||||
}
|
||||
|
||||
Bool RampOptions::shouldApplyTheRamp()
|
||||
{
|
||||
if (m_shouldApplyTheRamp) {
|
||||
m_shouldApplyTheRamp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RampOptions::OnApply()
|
||||
{
|
||||
// Set m_shouldApplyTheRamp true. The call to shouldApplyRamp will set it false
|
||||
m_shouldApplyTheRamp = true;
|
||||
}
|
||||
|
||||
void RampOptions::OnWidthChange()
|
||||
{
|
||||
CString str;
|
||||
CWnd* pWnd = GetDlgItem(IDC_RO_WIDTH);
|
||||
if (!pWnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
pWnd->GetWindowText(str);
|
||||
m_rampWidth = atof(str.GetBuffer(0));
|
||||
}
|
||||
|
||||
extern RampOptions* TheRampOptions = NULL;
|
||||
|
||||
BEGIN_MESSAGE_MAP(RampOptions, COptionsPanel)
|
||||
ON_BN_CLICKED(IDC_RO_APPLY, OnApply)
|
||||
ON_EN_CHANGE(IDC_RO_WIDTH, OnWidthChange)
|
||||
END_MESSAGE_MAP()
|
||||
185
GeneralsMD/Code/Tools/WorldBuilder/src/RampTool.cpp
Normal file
185
GeneralsMD/Code/Tools/WorldBuilder/src/RampTool.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FILE: RampTool.cpp
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* EA Pacific */
|
||||
/* Confidential Information */
|
||||
/* Copyright (C) 2001 - All Rights Reserved */
|
||||
/* DO NOT DISTRIBUTE */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Project: RTS3 */
|
||||
/* File name: RampTool.cpp */
|
||||
/* Created: John K. McDonald, Jr., 4/19/2002 */
|
||||
/* Desc: // Ramp tool implementation */
|
||||
/* Revision History: */
|
||||
/* 4/19/2002 : Initial creation */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#include "StdAfx.h"
|
||||
#include "RampTool.h"
|
||||
|
||||
#include "CUndoable.h"
|
||||
#include "MainFrm.h"
|
||||
#include "DrawObject.h"
|
||||
#include "RampOptions.h"
|
||||
#include "Resource.h"
|
||||
#include "WbView.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilder.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
|
||||
#include "GameClient/Line2D.h"
|
||||
|
||||
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
|
||||
RampTool::RampTool() : Tool(ID_RAMPTOOL, IDC_RAMP )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RampTool::activate()
|
||||
{
|
||||
Tool::activate();
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_RAMP_OPTIONS);
|
||||
|
||||
mIsMouseDown = false;
|
||||
}
|
||||
|
||||
void RampTool::deactivate()
|
||||
{
|
||||
DrawObject::setDoRampFeedback(false);
|
||||
mIsMouseDown = false;
|
||||
}
|
||||
|
||||
Bool RampTool::followsTerrain(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void RampTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (!((m == TRACK_L) && mIsMouseDown)) {
|
||||
if (TheRampOptions->shouldApplyTheRamp()) {
|
||||
// Call me now for your free ramp application!
|
||||
applyRamp(pDoc);
|
||||
return;
|
||||
}
|
||||
} else if (m == TRACK_L) {
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
docPt.z = TheTerrainRenderObject->getHeightMapHeight(docPt.x, docPt.y, NULL);
|
||||
mEndPoint = docPt;
|
||||
}
|
||||
|
||||
drawFeedback(&mEndPoint);
|
||||
}
|
||||
|
||||
void RampTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) {
|
||||
return;
|
||||
}
|
||||
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
mStartPoint = docPt;
|
||||
mStartPoint.z = TheTerrainRenderObject->getHeightMapHeight(mStartPoint.x, mStartPoint.y, NULL);
|
||||
|
||||
mIsMouseDown = true;
|
||||
}
|
||||
|
||||
void RampTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
|
||||
if (!((m == TRACK_L) && mIsMouseDown)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
mEndPoint = docPt;
|
||||
mEndPoint.z = TheTerrainRenderObject->getHeightMapHeight(mEndPoint.x, mEndPoint.y, NULL);
|
||||
|
||||
mIsMouseDown = false;
|
||||
}
|
||||
|
||||
void RampTool::drawFeedback(Coord3D* endPoint)
|
||||
{
|
||||
DrawObject::setDoRampFeedback(true);
|
||||
DrawObject::setRampFeedbackParms(&mStartPoint, endPoint, TheRampOptions->getRampWidth());
|
||||
}
|
||||
|
||||
void RampTool::applyRamp(CWorldBuilderDoc* pDoc)
|
||||
{
|
||||
Coord3D bl, tl, br, tr;
|
||||
VecHeightMapIndexes indices;
|
||||
|
||||
WorldHeightMapEdit *worldHeightDup = pDoc->GetHeightMap()->duplicate();
|
||||
|
||||
Real width = TheRampOptions->getRampWidth();
|
||||
|
||||
BuildRectFromSegmentAndWidth(&mStartPoint, &mEndPoint, width,
|
||||
&bl, &tl, &br, &tr);
|
||||
|
||||
if (bl == br || bl == tl) {
|
||||
return;
|
||||
}
|
||||
|
||||
getAllIndexesIn(&bl, &br, &tl, &tr, 0, pDoc, &indices);
|
||||
int indiceCount = indices.size();
|
||||
if (indiceCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
This part is pretty straightforward. Determine the U value for the shortest segment from the
|
||||
index's actual location to the segments from mStartPoint to mEndPoint, and then apply a
|
||||
linear gradient factor according to the height from the beginning to the end of mStartPoint
|
||||
and mEndPoint.
|
||||
*/
|
||||
|
||||
for (int i = 0; i < indiceCount; ++i) {
|
||||
Coord3D pt;
|
||||
pDoc->getCoordFromCellIndex(indices[i], &pt);
|
||||
|
||||
Real uVal;
|
||||
Coord2D start = { mStartPoint.x, mStartPoint.y };
|
||||
Coord2D end = { mEndPoint.x, mEndPoint.y };
|
||||
Coord2D pt2D = { pt.x, pt.y };
|
||||
|
||||
ShortestDistancePointToSegment2D(&start, &end, &pt2D, NULL, NULL, &uVal);
|
||||
Real height = mStartPoint.z + uVal * (mEndPoint.z - mStartPoint.z);
|
||||
|
||||
worldHeightDup->setHeight(indices[i].x, indices[i].y, (UnsignedByte) (height / MAP_HEIGHT_SCALE));
|
||||
}
|
||||
|
||||
IRegion2D partialRange = {0,0,0,0};
|
||||
pDoc->updateHeightMap(worldHeightDup, false, partialRange);
|
||||
|
||||
WBDocUndoable *pUndo = new WBDocUndoable(pDoc, worldHeightDup);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
REF_PTR_RELEASE(worldHeightDup);
|
||||
|
||||
|
||||
// Once we've applied the ramp, its no longer a mutable thing, so blow away the feedback
|
||||
DrawObject::setDoRampFeedback(false);
|
||||
}
|
||||
|
||||
|
||||
616
GeneralsMD/Code/Tools/WorldBuilder/src/RoadOptions.cpp
Normal file
616
GeneralsMD/Code/Tools/WorldBuilder/src/RoadOptions.cpp
Normal file
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// RoadOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "RoadOptions.h"
|
||||
#include "CUndoable.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "GameClient/TerrainRoads.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
RoadOptions *RoadOptions::m_staticThis = NULL;
|
||||
Bool RoadOptions::m_updating = false;
|
||||
AsciiString RoadOptions::m_currentRoadName;
|
||||
Int RoadOptions::m_currentRoadIndex=0;
|
||||
Int RoadOptions::m_numberOfRoads=0;
|
||||
Int RoadOptions::m_numberOfBridges=0;
|
||||
|
||||
Bool RoadOptions::m_angleCorners = false; ///<angled or curved.
|
||||
Bool RoadOptions::m_tightCurve = false; ///< Broad curve false, tight curve true.
|
||||
Bool RoadOptions::m_doJoin = false; ///< Is a join to different road type.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// RoadOptions dialog
|
||||
|
||||
|
||||
RoadOptions::RoadOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
m_currentRoadName = "Road";
|
||||
//{{AFX_DATA_INIT(RoadOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
RoadOptions::~RoadOptions(void)
|
||||
{
|
||||
m_currentRoadName.clear();
|
||||
}
|
||||
|
||||
|
||||
void RoadOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(RoadOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(RoadOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(RoadOptions)
|
||||
ON_BN_CLICKED(IDC_TIGHT_CURVE, OnTightCurve)
|
||||
ON_BN_CLICKED(IDC_ANGLED, OnAngled)
|
||||
ON_BN_CLICKED(IDC_BROAD_CURVE, OnBroadCurve)
|
||||
ON_BN_CLICKED(IDC_JOIN, OnJoin)
|
||||
ON_BN_CLICKED(IDC_APPLY_ROAD, OnApplyRoad)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// RoadOptions data access method.
|
||||
|
||||
|
||||
void RoadOptions::updateLabel(void)
|
||||
{
|
||||
const char *tName = getCurRoadName().str();
|
||||
|
||||
CWnd *pLabel = GetDlgItem(IDC_ROAD_NAME);
|
||||
if (pLabel) {
|
||||
pLabel->SetWindowText(tName);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if only one or more roads is selected. */
|
||||
Bool RoadOptions::selectionIsRoadsOnly(void)
|
||||
{
|
||||
// MapObject *theMapObj = NULL;
|
||||
Bool foundRoad = false;
|
||||
Bool foundAnythingElse = false;
|
||||
MapObject *pMapObj;
|
||||
for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) {
|
||||
if (pMapObj->isSelected()) {
|
||||
if (pMapObj->getFlag(FLAG_ROAD_FLAGS)) {
|
||||
foundRoad = true;
|
||||
} else {
|
||||
foundAnythingElse = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (foundRoad && (!foundAnythingElse));
|
||||
}
|
||||
|
||||
/** Returns true if only one or more roads is selected. */
|
||||
void RoadOptions::updateSelection(void)
|
||||
{
|
||||
// MapObject *theMapObj = NULL;
|
||||
Int angled = 0;
|
||||
Int tight = 0;
|
||||
Int broad = 0;
|
||||
Int join = 0;
|
||||
AsciiString roadName;
|
||||
Bool multipleNames = false;
|
||||
|
||||
if (!m_staticThis) return;
|
||||
MapObject *pMapObj;
|
||||
|
||||
for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) {
|
||||
if (pMapObj->isSelected() && pMapObj->getFlag(FLAG_ROAD_FLAGS)) {
|
||||
if (roadName.isEmpty()) {
|
||||
roadName = pMapObj->getName();
|
||||
} else {
|
||||
if (!(roadName==pMapObj->getName())) {
|
||||
multipleNames = true;
|
||||
}
|
||||
}
|
||||
if (pMapObj->getFlag(FLAG_ROAD_CORNER_ANGLED)) {
|
||||
angled = 1;
|
||||
} else if (pMapObj->getFlag(FLAG_ROAD_CORNER_TIGHT)) {
|
||||
tight = 1;
|
||||
} else {
|
||||
broad = 1;
|
||||
}
|
||||
if (pMapObj->getFlag(FLAG_ROAD_JOIN)) {
|
||||
join = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!roadName.isEmpty() && !multipleNames) {
|
||||
CWnd *pLabel = m_staticThis->GetDlgItem(IDC_ROAD_NAME);
|
||||
if (pLabel) {
|
||||
pLabel->SetWindowText(roadName.str());
|
||||
m_staticThis->findAndSelect(TVI_ROOT, roadName);
|
||||
}
|
||||
} else {
|
||||
m_staticThis->setRoadTreeViewSelection(TVI_ROOT, m_currentRoadIndex);
|
||||
}
|
||||
if (angled+broad+tight==0) {
|
||||
// nothing selected
|
||||
if (m_angleCorners) {
|
||||
angled = 1;
|
||||
} else if (m_tightCurve) {
|
||||
tight = 1;
|
||||
} else {
|
||||
broad = 1;
|
||||
}
|
||||
if (m_doJoin) {
|
||||
join = 1;
|
||||
}
|
||||
} else if (angled+broad+tight==1) {
|
||||
// One type selected
|
||||
} else {
|
||||
angled = tight = broad = join = 0;
|
||||
}
|
||||
if (m_staticThis) {
|
||||
CButton *pButton = (CButton *)m_staticThis->GetDlgItem(IDC_TIGHT_CURVE);
|
||||
pButton->SetCheck(tight);
|
||||
pButton = (CButton *)m_staticThis->GetDlgItem(IDC_BROAD_CURVE);
|
||||
pButton->SetCheck(broad);
|
||||
pButton = (CButton *)m_staticThis->GetDlgItem(IDC_ANGLED);
|
||||
pButton->SetCheck(angled);
|
||||
pButton = (CButton *)m_staticThis->GetDlgItem(IDC_JOIN);
|
||||
pButton->SetCheck(join);
|
||||
}
|
||||
}
|
||||
|
||||
/** Applies road corner flags and road type to selection. */
|
||||
void RoadOptions::applyToSelection(void)
|
||||
{
|
||||
Int flagMask = FLAG_ROAD_CORNER_ANGLED | FLAG_ROAD_CORNER_TIGHT;
|
||||
Int flagVal = 0;
|
||||
if (m_angleCorners) {
|
||||
flagVal = FLAG_ROAD_CORNER_ANGLED;
|
||||
} else if (m_tightCurve) {
|
||||
flagVal = FLAG_ROAD_CORNER_TIGHT;
|
||||
}
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
ModifyFlagsUndoable *pUndo = new ModifyFlagsUndoable(pDoc, flagMask, flagVal);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// RoadOptions message handlers
|
||||
|
||||
/// Setup the controls in the dialog.
|
||||
BOOL RoadOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_ROAD_TREEVIEW);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_roadTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_ROAD_TREEVIEW);
|
||||
m_roadTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
Int index = 0;
|
||||
m_numberOfRoads = 0;
|
||||
|
||||
// load roads from INI
|
||||
TerrainRoadType *road;
|
||||
for( road = TheTerrainRoads->firstRoad(); road; road = TheTerrainRoads->nextRoad( road ) )
|
||||
{
|
||||
|
||||
addRoad( (char *)road->getName().str(), index, TVI_ROOT );
|
||||
index++;
|
||||
m_numberOfRoads++;
|
||||
|
||||
} // end for raod
|
||||
|
||||
// load roads from test assets
|
||||
#ifdef LOAD_TEST_ASSETS
|
||||
{
|
||||
char dirBuf[_MAX_PATH];
|
||||
char findBuf[_MAX_PATH];
|
||||
char fileBuf[_MAX_PATH];
|
||||
|
||||
strcpy(dirBuf, ROAD_DIRECTORY);
|
||||
int len = strlen(dirBuf);
|
||||
|
||||
strcpy(findBuf, dirBuf);
|
||||
|
||||
FilenameList filenameList;
|
||||
TheFileSystem->getFileListInDirectory(AsciiString(findBuf), AsciiString("*.tga"), filenameList, FALSE);
|
||||
|
||||
if (filenameList.size() > 0) {
|
||||
FilenameList::iterator it = filenameList.begin();
|
||||
do {
|
||||
AsciiString filename = *it;
|
||||
|
||||
if ((filename.compare(".") == 0) || (filename.compare("..") == 0)) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
len = filename.getLength();
|
||||
if (len<5) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
// only do .tga files
|
||||
if (!(filename.endsWith(".tga"))) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
strcpy(fileBuf, TEST_STRING);
|
||||
strcat(fileBuf, "\\");
|
||||
strcat(fileBuf, filename.str());
|
||||
addRoad(fileBuf, index, TVI_ROOT);
|
||||
index++;
|
||||
m_numberOfRoads++;
|
||||
++it;
|
||||
} while (it != filenameList.end());
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_numberOfBridges = 0;
|
||||
// add bridge defs from INI
|
||||
TerrainRoadType *bridge;
|
||||
for( bridge = TheTerrainRoads->firstBridge();
|
||||
bridge;
|
||||
bridge = TheTerrainRoads->nextBridge( bridge ) )
|
||||
{
|
||||
|
||||
addRoad( (char *)bridge->getName().str(), index, TVI_ROOT );
|
||||
index++;
|
||||
m_numberOfBridges++;
|
||||
|
||||
} // end for bridge
|
||||
|
||||
m_currentRoadIndex = 1;
|
||||
setRoadTreeViewSelection(TVI_ROOT, m_currentRoadIndex);
|
||||
updateLabel();
|
||||
m_staticThis = this;
|
||||
updateSelection();
|
||||
m_updating = false;
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
HTREEITEM RoadOptions::findOrAdd(HTREEITEM parent, char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = m_roadTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
m_roadTreeView.GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = m_roadTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_LAST;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = m_roadTreeView.InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
/** Locate and select child item in tree item parent with name pLabel. If not
|
||||
found, false. If found, return true. */
|
||||
Bool RoadOptions::findAndSelect(HTREEITEM parent, AsciiString label)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = m_roadTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
m_roadTreeView.GetItem(&ins.item);
|
||||
if (label.compare(buffer) == 0) {
|
||||
m_roadTreeView.SelectItem(child);
|
||||
return(true);
|
||||
}
|
||||
if (findAndSelect(child, label)) {
|
||||
return true;
|
||||
}
|
||||
child = m_roadTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Add the road hierarchy paths to the tree view. */
|
||||
void RoadOptions::addRoad(char *pPath, Int terrainNdx, HTREEITEM parent)
|
||||
{
|
||||
TerrainRoadType *road;
|
||||
char buffer[_MAX_PATH];
|
||||
Bool doAdd = FALSE;
|
||||
|
||||
// try to find the road in our INI definition
|
||||
road = TheTerrainRoads->findRoadOrBridge( AsciiString( pPath ) );
|
||||
if( road )
|
||||
{
|
||||
|
||||
// roads go in a road tree, bridges in their own tree
|
||||
if( road->isBridge() == TRUE )
|
||||
parent = findOrAdd( parent, "Bridges" );
|
||||
else
|
||||
parent = findOrAdd( parent, "Roads" );
|
||||
|
||||
// set the name to place as the name of the road entry in INI
|
||||
strcpy( buffer, road->getName().str() );
|
||||
|
||||
// do the add
|
||||
doAdd = TRUE;
|
||||
|
||||
} // end if
|
||||
|
||||
#ifdef LOAD_TEST_ASSETS
|
||||
if (!doAdd && !strncmp(TEST_STRING, pPath, strlen(TEST_STRING))) {
|
||||
parent = findOrAdd(parent, TEST_STRING);
|
||||
strcpy(buffer, pPath + strlen(TEST_STRING) + 1);
|
||||
doAdd = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( doAdd )
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_LAST;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = terrainNdx;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = strlen(buffer)+2;
|
||||
m_roadTreeView.InsertItem(&ins);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Set the selected road in the tree view.
|
||||
Bool RoadOptions::setRoadTreeViewSelection(HTREEITEM parent, Int selection)
|
||||
{
|
||||
TVITEM item;
|
||||
char buffer[NAME_MAX_LEN];
|
||||
::memset(&item, 0, sizeof(item));
|
||||
HTREEITEM child = m_roadTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT;
|
||||
item.hItem = child;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_roadTreeView.GetItem(&item);
|
||||
if (item.lParam == selection) {
|
||||
m_roadTreeView.SelectItem(child);
|
||||
m_currentRoadName = buffer;
|
||||
return(true);
|
||||
}
|
||||
if (setRoadTreeViewSelection(child, selection)) {
|
||||
return(true);
|
||||
}
|
||||
child = m_roadTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
void RoadOptions::SelectConnected(void)
|
||||
{
|
||||
std::list<MapObject*> roadSegs;
|
||||
std::list<MapObject*> connectedSegs;
|
||||
for (MapObject* pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext())
|
||||
{
|
||||
if (pMapObj->getFlag(FLAG_ROAD_POINT1))
|
||||
{
|
||||
if (pMapObj->isSelected() || pMapObj->getNext() && pMapObj->getNext()->isSelected())
|
||||
{
|
||||
connectedSegs.push_back(pMapObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
roadSegs.push_back(pMapObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
Bool changed = true;
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
for (std::list<MapObject*>::iterator it = roadSegs.begin(); it != roadSegs.end(); ++it)
|
||||
{
|
||||
MapObject* o = *it;
|
||||
const Coord3D *oLoc = o->getLocation();
|
||||
const Coord3D *onLoc = o->getNext()->getLocation();
|
||||
for (std::list<MapObject*>::iterator connected = connectedSegs.begin(); connected != connectedSegs.end(); ++connected)
|
||||
{
|
||||
MapObject* p = *connected;
|
||||
const Coord3D *pLoc = p->getLocation();
|
||||
const Coord3D *pnLoc = p->getNext()->getLocation();
|
||||
|
||||
Real dx1 = oLoc->x - pLoc->x;
|
||||
Real dy1 = oLoc->y - pLoc->y;
|
||||
dx1 = abs(dx1);
|
||||
dy1 = abs(dy1);
|
||||
Real qd1 = max(dx1, dy1);
|
||||
//Real dist1 = sqrt(dx1*dx1+dy1*dy1);
|
||||
|
||||
Real dx2 = oLoc->x - pnLoc->x;
|
||||
Real dy2 = oLoc->y - pnLoc->y;
|
||||
dx2 = abs(dx2);
|
||||
dy2 = abs(dy2);
|
||||
Real qd2 = max(dx2, dy2);
|
||||
//Real dist2 = sqrt(dx2*dx2+dy2*dy2);
|
||||
|
||||
Real dx3 = onLoc->x - pLoc->x;
|
||||
Real dy3 = onLoc->y - pLoc->y;
|
||||
dx3 = abs(dx3);
|
||||
dy3 = abs(dy3);
|
||||
Real qd3 = max(dx3, dy3);
|
||||
//Real dist3 = sqrt(dx3*dx3+dy3*dy3);
|
||||
|
||||
Real dx4 = onLoc->x - pnLoc->x;
|
||||
Real dy4 = onLoc->y - pnLoc->y;
|
||||
dx4 = abs(dx4);
|
||||
dy4 = abs(dy4);
|
||||
Real qd4 = max(dx4, dy4);
|
||||
//Real dist4 = sqrt(dx4*dx4+dy4*dy4);
|
||||
|
||||
if (qd1 < MAP_XY_FACTOR/100 || qd2 < MAP_XY_FACTOR/100 || qd3 < MAP_XY_FACTOR/100 || qd4 < MAP_XY_FACTOR/100) {
|
||||
connectedSegs.push_back(o);
|
||||
roadSegs.erase(it);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::list<MapObject*>::iterator connected = connectedSegs.begin(); connected != connectedSegs.end(); ++connected)
|
||||
{
|
||||
MapObject* p = *connected;
|
||||
if (p) {
|
||||
p->setSelected(true);
|
||||
p->getNext()->setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RoadOptions::ChangeRoadType(AsciiString newRoad)
|
||||
{
|
||||
SelectConnected();
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
ModifyObjectUndoable *pUndo = new ModifyObjectUndoable(pDoc);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
pUndo->SetName(newRoad);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
}
|
||||
|
||||
BOOL RoadOptions::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
if (pHdr->hdr.hwndFrom == m_roadTreeView.m_hWnd) {
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
char buffer[NAME_MAX_LEN];
|
||||
HTREEITEM hItem = m_roadTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_roadTreeView.GetItem(&item);
|
||||
if (item.lParam >= 0) {
|
||||
m_currentRoadIndex = item.lParam;
|
||||
m_currentRoadName = buffer;
|
||||
updateLabel();
|
||||
} else if (!(item.state & TVIS_EXPANDEDONCE) ) {
|
||||
HTREEITEM child = m_roadTreeView.GetChildItem(hItem);
|
||||
while (child != NULL) {
|
||||
hItem = child;
|
||||
child = m_roadTreeView.GetChildItem(hItem);
|
||||
}
|
||||
if (hItem != m_roadTreeView.GetSelectedItem()) {
|
||||
m_roadTreeView.SelectItem(hItem);
|
||||
updateSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
|
||||
void RoadOptions::OnTightCurve()
|
||||
{
|
||||
m_angleCorners = false;
|
||||
m_tightCurve = true;
|
||||
applyToSelection();
|
||||
}
|
||||
|
||||
void RoadOptions::OnAngled()
|
||||
{
|
||||
m_angleCorners = true;
|
||||
m_tightCurve = false;
|
||||
applyToSelection();
|
||||
}
|
||||
|
||||
void RoadOptions::OnBroadCurve()
|
||||
{
|
||||
m_angleCorners = false;
|
||||
m_tightCurve = false;
|
||||
applyToSelection();
|
||||
}
|
||||
|
||||
void RoadOptions::OnJoin()
|
||||
{
|
||||
CButton *pButton = (CButton *)m_staticThis->GetDlgItem(IDC_JOIN);
|
||||
m_doJoin = pButton->GetCheck()==1;
|
||||
Int flagMask = FLAG_ROAD_JOIN;
|
||||
Int flagVal = 0;
|
||||
if (m_doJoin) {
|
||||
flagVal = FLAG_ROAD_JOIN;
|
||||
}
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
ModifyFlagsUndoable *pUndo = new ModifyFlagsUndoable(pDoc, flagMask, flagVal);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
}
|
||||
|
||||
void RoadOptions::OnApplyRoad()
|
||||
{
|
||||
if (m_currentRoadName != AsciiString::TheEmptyString)
|
||||
{
|
||||
ChangeRoadType(m_currentRoadName);
|
||||
}
|
||||
}
|
||||
341
GeneralsMD/Code/Tools/WorldBuilder/src/RoadTool.cpp
Normal file
341
GeneralsMD/Code/Tools/WorldBuilder/src/RoadTool.cpp
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// RoadTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "RoadTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "DrawObject.h"
|
||||
#include "PointerTool.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "GameClient/Line2D.h"
|
||||
//
|
||||
// RoadTool class.
|
||||
//
|
||||
/// Constructor
|
||||
RoadTool::RoadTool(void) :
|
||||
Tool(ID_ROAD_TOOL, IDC_ROAD)
|
||||
{
|
||||
m_mapObj = NULL;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
RoadTool::~RoadTool(void)
|
||||
{
|
||||
m_mapObj = NULL;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
MapObject* RoadTool::findSegment(const Coord3D *pLoc, Coord3D *outLoc)
|
||||
{
|
||||
for (MapObject* pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext())
|
||||
{
|
||||
if (pMapObj->getFlag(FLAG_ROAD_POINT1))
|
||||
{
|
||||
MapObject* pMapObj2 = pMapObj->getNext();
|
||||
if (pMapObj2==NULL)
|
||||
break;
|
||||
if (!pMapObj2->getFlag(FLAG_ROAD_POINT2))
|
||||
continue;
|
||||
Coord2D start, end, loc, snapLoc;
|
||||
start.x = pMapObj->getLocation()->x;
|
||||
start.y = pMapObj->getLocation()->y;
|
||||
end.x = pMapObj2->getLocation()->x;
|
||||
end.y = pMapObj2->getLocation()->y;
|
||||
loc.x = pLoc->x;
|
||||
loc.y = pLoc->y;
|
||||
Real dist;
|
||||
Real u;
|
||||
|
||||
ShortestDistancePointToSegment2D(&start, &end, &loc, NULL, &snapLoc, &u);
|
||||
if (u < 0 || u > 1) {
|
||||
continue;
|
||||
}
|
||||
Coord2D segment;
|
||||
segment.x = loc.x - snapLoc.x;
|
||||
segment.y = loc.y - snapLoc.y;
|
||||
dist = segment.length();
|
||||
|
||||
if (dist < ROAD_SNAP_DISTANCE*MAP_XY_FACTOR)
|
||||
{
|
||||
outLoc->x = snapLoc.x;
|
||||
outLoc->y = snapLoc.y;
|
||||
outLoc->z = MAGIC_GROUND_Z;
|
||||
return(pMapObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// RoadTool::snap
|
||||
//=============================================================================
|
||||
/** Snaps pLoc to another road endpoint if close enough. */
|
||||
//=============================================================================
|
||||
Bool RoadTool::snap(Coord3D *pLoc, Bool skipFirst)
|
||||
{
|
||||
MapObject *pMapObj;
|
||||
MapObject *pMapObj2;
|
||||
Real snapDist = ROAD_SNAP_DISTANCE*MAP_XY_FACTOR;
|
||||
Coord3D newLoc = *pLoc;
|
||||
Bool snapped = false;
|
||||
|
||||
for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) {
|
||||
if (skipFirst) {
|
||||
skipFirst = false;
|
||||
continue;
|
||||
}
|
||||
if (pMapObj->getFlag(FLAG_ROAD_POINT1)) {
|
||||
pMapObj2 = pMapObj->getNext();
|
||||
if (pMapObj2==NULL) break;
|
||||
if (!pMapObj2->getFlag(FLAG_ROAD_POINT2)) continue;
|
||||
Vector2 dist;
|
||||
if (!pMapObj->isSelected()) {
|
||||
dist.Set(pMapObj->getLocation()->x - pLoc->x, pMapObj->getLocation()->y - pLoc->y);
|
||||
if (dist.Length() < snapDist) {
|
||||
newLoc = *pMapObj->getLocation();
|
||||
snapDist = dist.Length();
|
||||
snapped = true;
|
||||
}
|
||||
}
|
||||
if (!pMapObj2->isSelected()) {
|
||||
dist.Set(pMapObj2->getLocation()->x - pLoc->x, pMapObj2->getLocation()->y - pLoc->y);
|
||||
if (dist.Length() < snapDist) {
|
||||
newLoc = *pMapObj2->getLocation();
|
||||
snapDist = dist.Length();
|
||||
snapped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
newLoc.z = MAGIC_GROUND_Z; // roads always snap to terrain.
|
||||
if (snapped) {
|
||||
*pLoc = newLoc;
|
||||
}
|
||||
return snapped;
|
||||
}
|
||||
|
||||
|
||||
/// Shows the road options panel.
|
||||
void RoadTool::activate()
|
||||
{
|
||||
PointerTool::clearSelection();
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_ROAD_OPTIONS);
|
||||
RoadOptions::updateSelection();
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse up - Place a road segment. */
|
||||
void RoadTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
|
||||
Coord3D loc1, loc2, loc3;
|
||||
loc1 = cpt;
|
||||
if (!pDoc->getCellPositionFromCoord(cpt, &loc1))
|
||||
return;
|
||||
|
||||
Bool isBridge = RoadOptions::isBridge();
|
||||
|
||||
if (isBridge) {
|
||||
Bool isLandmark = false;
|
||||
const ThingTemplate *tt = TheThingFactory->findTemplate(RoadOptions::getCurRoadName());
|
||||
if (tt) {
|
||||
isLandmark = tt->isBridge();
|
||||
}
|
||||
if (isLandmark) {
|
||||
MapObject *pNew1 = newInstance(MapObject)(loc1, RoadOptions::getCurRoadName(), 0.0f, 0, NULL, tt );
|
||||
pNew1->getProperties()->setAsciiString(TheKey_originalOwner, NEUTRAL_TEAM_INTERNAL_STR);
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, pNew1);
|
||||
pNew1->setSelected(true);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_mapObj = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Bool snapped = false;
|
||||
Bool divideSegment = false;
|
||||
MapObject* pickedSegment = NULL;
|
||||
if (!isBridge) {
|
||||
snapped = snap(&loc1, false);
|
||||
if (!snapped) {
|
||||
pickedSegment = findSegment(&loc1, &loc2);
|
||||
if (pickedSegment) {
|
||||
snapped = true;
|
||||
divideSegment = true;
|
||||
loc1 = *pickedSegment->getLocation();
|
||||
loc3 = *pickedSegment->getNext()->getLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!snapped) {
|
||||
pView->snapPoint(&loc1);
|
||||
}
|
||||
|
||||
if (!divideSegment) {
|
||||
loc2 = loc1;
|
||||
}
|
||||
|
||||
loc1.z = MAGIC_GROUND_Z; // roads stick to terrain anyway.
|
||||
loc2.z = MAGIC_GROUND_Z;
|
||||
loc3.z = MAGIC_GROUND_Z;
|
||||
AsciiString roadName;
|
||||
if (divideSegment) {
|
||||
roadName = pickedSegment->getName();
|
||||
} else {
|
||||
roadName = RoadOptions::getCurRoadName();
|
||||
}
|
||||
|
||||
MapObject *pNew1 = newInstance(MapObject)(loc1, roadName, 0.0f, 0, NULL, NULL );
|
||||
MapObject *pNew2 = newInstance(MapObject)(loc2, roadName, 0.0f, 0, NULL, NULL );
|
||||
MapObject *pNew3 = NULL;
|
||||
MapObject *pNew4 = NULL;
|
||||
if (divideSegment) {
|
||||
pNew3 = newInstance(MapObject)(loc2, roadName, 0.0f, 0, NULL, NULL );
|
||||
pNew4 = newInstance(MapObject)(loc3, roadName, 0.0f, 0, NULL, NULL );
|
||||
}
|
||||
|
||||
pNew1->setColor(RGB(255,255,0)); // make road endpoints yellow.
|
||||
pNew2->setColor(RGB(255,255,0)); // make road endpoints yellow.
|
||||
if (divideSegment) {
|
||||
pNew3->setColor(RGB(255,255,0)); // make road endpoints yellow.
|
||||
pNew4->setColor(RGB(255,255,0)); // make road endpoints yellow.
|
||||
}
|
||||
|
||||
if (RoadOptions::isBridge()) {
|
||||
pNew1->setFlag(FLAG_BRIDGE_POINT1);
|
||||
pNew2->setFlag(FLAG_BRIDGE_POINT2);
|
||||
} else {
|
||||
pNew1->setFlag(FLAG_ROAD_POINT1);
|
||||
pNew2->setFlag(FLAG_ROAD_POINT2);
|
||||
if (divideSegment) {
|
||||
pNew3->setFlag(FLAG_ROAD_POINT1);
|
||||
pNew4->setFlag(FLAG_ROAD_POINT2);
|
||||
}
|
||||
}
|
||||
|
||||
if (divideSegment) {
|
||||
pNew1->setFlag(pickedSegment->getFlags());
|
||||
pNew4->setFlag(pickedSegment->getNext()->getFlags());
|
||||
} else {
|
||||
if (RoadOptions::isAngled()) {
|
||||
pNew1->setFlag(FLAG_ROAD_CORNER_ANGLED);
|
||||
pNew2->setFlag(FLAG_ROAD_CORNER_ANGLED);
|
||||
} else if (RoadOptions::isTightCurve()) {
|
||||
pNew1->setFlag(FLAG_ROAD_CORNER_TIGHT);
|
||||
pNew2->setFlag(FLAG_ROAD_CORNER_TIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// Roads belong to the neutral player. :)
|
||||
pNew1->getProperties()->setAsciiString(TheKey_originalOwner, NEUTRAL_TEAM_INTERNAL_STR);
|
||||
pNew2->getProperties()->setAsciiString(TheKey_originalOwner, NEUTRAL_TEAM_INTERNAL_STR);
|
||||
pNew1->setNextMap(pNew2);
|
||||
m_mapObj = pNew2;
|
||||
|
||||
if (divideSegment) {
|
||||
// Roads belong to the neutral player. :)
|
||||
pNew3->getProperties()->setAsciiString(TheKey_originalOwner, NEUTRAL_TEAM_INTERNAL_STR);
|
||||
pNew4->getProperties()->setAsciiString(TheKey_originalOwner, NEUTRAL_TEAM_INTERNAL_STR);
|
||||
pNew3->setNextMap(pNew4);
|
||||
}
|
||||
|
||||
if (divideSegment) {
|
||||
PointerTool::clearSelection();
|
||||
pickedSegment->setSelected(true);
|
||||
DeleteObjectUndoable *pDeleteUndo = new DeleteObjectUndoable(pDoc);
|
||||
pDoc->AddAndDoUndoable(pDeleteUndo);
|
||||
REF_PTR_RELEASE(pDeleteUndo); // belongs to pDoc now.
|
||||
}
|
||||
// Clear the selection.
|
||||
PointerTool::clearSelection();
|
||||
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, pNew1);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_mapObj->setSelected(true);
|
||||
|
||||
if (divideSegment) {
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, pNew3);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_mapObj = NULL;
|
||||
PointerTool::clearSelection();
|
||||
pNew2->setSelected(true);
|
||||
pNew3->setSelected(true);
|
||||
}
|
||||
|
||||
}
|
||||
/** Move the end of the road segment. */
|
||||
void RoadTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
Coord3D loc1 ;
|
||||
if (m_mapObj == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
|
||||
loc1.x=loc1.y=loc1.z=0;
|
||||
if (!pDoc->getCellPositionFromCoord(cpt, &loc1))
|
||||
return;
|
||||
|
||||
Bool isBridge = RoadOptions::isBridge();
|
||||
loc1.z = MAGIC_GROUND_Z;
|
||||
Bool snapped = false;
|
||||
if (!isBridge) {
|
||||
snapped = snap(&loc1, false);
|
||||
}
|
||||
if (!snapped) {
|
||||
pView->snapPoint(&loc1);
|
||||
}
|
||||
pDoc->invalObject(m_mapObj);
|
||||
m_mapObj->setLocation(&loc1);
|
||||
pDoc->invalObject(m_mapObj);
|
||||
}
|
||||
|
||||
void RoadTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
m_mapObj = NULL;
|
||||
}
|
||||
|
||||
132
GeneralsMD/Code/Tools/WorldBuilder/src/RulerOptions.cpp
Normal file
132
GeneralsMD/Code/Tools/WorldBuilder/src/RulerOptions.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Mike Lytle
|
||||
// 01/07/03
|
||||
// RulerOptions.cpp
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "RulerOptions.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "RulerTool.h"
|
||||
|
||||
RulerOptions* RulerOptions::m_staticThis = NULL;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
RulerOptions::RulerOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
//{{AFX_DATA_INIT(RulerOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
/// Windows default stuff.
|
||||
void RulerOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(RulerOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
/** Update the value in the edit control. */
|
||||
void RulerOptions::setWidth(Real width)
|
||||
{
|
||||
CString buf;
|
||||
// Multiply by 2 because we are changing from radius to diameter.
|
||||
buf.Format("%f", width * 2.0f);
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_RULER_WIDTH);
|
||||
if (pEdit && pEdit->IsWindowEnabled()) {
|
||||
pEdit->SetWindowText(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Dialog UI initialization.
|
||||
BOOL RulerOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
if (RulerTool::getType() != RULER_CIRCLE) {
|
||||
CWnd *pEdit = GetDlgItem(IDC_RULER_WIDTH);
|
||||
if (pEdit) {
|
||||
// Disable the edit box since the ruler isn't a circle.
|
||||
pEdit->EnableWindow(false);
|
||||
}
|
||||
} else {
|
||||
// Only the circle rulers use the edit box to change the size.
|
||||
setWidth(RulerTool::getLength());
|
||||
}
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void RulerOptions::OnChangeWidthEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = GetDlgItem(IDC_RULER_WIDTH);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
float width;
|
||||
m_updating = true;
|
||||
// Pull out the length from the text.
|
||||
if (1 == sscanf(buffer, "%f", &width)) {
|
||||
// Change from diameter to radius.
|
||||
RulerTool::setLength(width / 2.0f);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RulerOptions::OnChangeCheckRuler()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pCheck = GetDlgItem(IDC_CHECK_RULER);
|
||||
if (pCheck) {
|
||||
if (RulerTool::switchType()) {
|
||||
CWnd *pEdit = GetDlgItem(IDC_RULER_WIDTH);
|
||||
if (pEdit) {
|
||||
// The edit box is disabled when the ruler is a line.
|
||||
pEdit->EnableWindow(!pEdit->IsWindowEnabled());
|
||||
// Make sure that the length is updated.
|
||||
if (pEdit->IsWindowEnabled()) {
|
||||
setWidth(RulerTool::getLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(RulerOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(RulerOptions)
|
||||
ON_EN_CHANGE(IDC_RULER_WIDTH, OnChangeWidthEdit)
|
||||
ON_BN_CLICKED(IDC_CHECK_RULER, OnChangeCheckRuler)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
186
GeneralsMD/Code/Tools/WorldBuilder/src/RulerTool.cpp
Normal file
186
GeneralsMD/Code/Tools/WorldBuilder/src/RulerTool.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// RulerTool.cpp
|
||||
// Author: Mike Lytle, January 2003
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "RulerTool.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "WBView3D.h"
|
||||
#include "ObjectTool.h"
|
||||
|
||||
|
||||
// Saved off so that static functions can access its members.
|
||||
RulerTool* RulerTool::m_staticThis = NULL;
|
||||
|
||||
/// Constructor
|
||||
RulerTool::RulerTool(void) :
|
||||
Tool(ID_RULER_TOOL, IDC_POINTER)
|
||||
{
|
||||
m_downPt3d.set(0.0f, 0.0f, 0.0f);
|
||||
m_rulerType = RULER_LINE;
|
||||
m_View = NULL;
|
||||
m_staticThis = this;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
RulerTool::~RulerTool(void)
|
||||
{
|
||||
}
|
||||
|
||||
// Activate.
|
||||
void RulerTool::activate()
|
||||
{
|
||||
Tool::activate();
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_RULER_OPTIONS);
|
||||
if (m_View != NULL) {
|
||||
// Is it dangerous to assume that the pointer is still good?
|
||||
m_View->doRulerFeedback(m_rulerType);
|
||||
}
|
||||
}
|
||||
|
||||
// Deactivate.
|
||||
void RulerTool::deactivate()
|
||||
{
|
||||
Tool::deactivate();
|
||||
|
||||
if (m_View != NULL) {
|
||||
m_View->doRulerFeedback(RULER_NONE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Set the cursor. */
|
||||
void RulerTool::setCursor(void)
|
||||
{
|
||||
Tool::setCursor();
|
||||
}
|
||||
|
||||
|
||||
/** Execute the tool on mouse down */
|
||||
void RulerTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
if (m_View == NULL) {
|
||||
// Save so that when we are done the view can stop drawing the rulers.
|
||||
m_View = pView;
|
||||
}
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
|
||||
m_downPt3d = cpt;
|
||||
pView->snapPoint(&m_downPt3d);
|
||||
}
|
||||
|
||||
/// Left button move code.
|
||||
void RulerTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
if (m_View == NULL) {
|
||||
// Save so that when we are done the view can stop drawing the rulers.
|
||||
m_View = pView;
|
||||
}
|
||||
|
||||
Coord3D cpt;
|
||||
pView->viewToDocCoords(viewPt, &cpt, false);
|
||||
|
||||
if (m_rulerType == RULER_CIRCLE) {
|
||||
Coord3D pt;
|
||||
pt.x = cpt.x + m_savedLength;
|
||||
pt.y = cpt.y + m_savedLength;
|
||||
pt.z = cpt.z;
|
||||
RulerOptions::setWidth(m_savedLength);
|
||||
pView->doRulerFeedback(RULER_CIRCLE);
|
||||
pView->rulerFeedbackInfo(cpt, pt, m_savedLength);
|
||||
pView->Invalidate();
|
||||
} else { //m_rulerType == RULER_LINE
|
||||
Coord3D diff;
|
||||
diff.set(&cpt);
|
||||
diff.sub(&m_downPt3d);
|
||||
m_savedLength = diff.length();
|
||||
RulerOptions::setWidth(m_savedLength);
|
||||
pView->doRulerFeedback(RULER_LINE);
|
||||
pView->rulerFeedbackInfo(cpt, m_downPt3d, m_savedLength);
|
||||
pView->Invalidate();
|
||||
}
|
||||
|
||||
pDoc->updateAllViews();
|
||||
}
|
||||
|
||||
void RulerTool::setLength(Real length)
|
||||
{
|
||||
if (!m_staticThis || (m_staticThis->m_rulerType != RULER_CIRCLE)) {
|
||||
// This should only be called when the ruler type is a circle.
|
||||
// Line rulers are always extended by the mouse.
|
||||
return;
|
||||
}
|
||||
|
||||
m_staticThis->m_savedLength = length;
|
||||
if (m_staticThis->m_View) {
|
||||
m_staticThis->m_View->rulerFeedbackInfo(m_staticThis->m_downPt3d, m_staticThis->m_downPt3d, length);
|
||||
m_staticThis->m_View->Invalidate();
|
||||
}
|
||||
|
||||
CString str;
|
||||
str.Format("Diameter (in feet): %f", length * 2.0f);
|
||||
CMainFrame::GetMainFrame()->SetMessageText(str);
|
||||
}
|
||||
|
||||
Bool RulerTool::switchType()
|
||||
{
|
||||
if (!m_staticThis) {
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
if (m_staticThis->m_rulerType == RULER_LINE) {
|
||||
m_staticThis->m_rulerType = RULER_CIRCLE;
|
||||
} else {
|
||||
m_staticThis->m_rulerType = RULER_LINE;
|
||||
}
|
||||
if (m_staticThis->m_View != NULL) {
|
||||
m_staticThis->m_View->doRulerFeedback(m_staticThis->m_rulerType);
|
||||
}
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
int RulerTool::getType()
|
||||
{
|
||||
if (!m_staticThis) {
|
||||
return (RULER_NONE);
|
||||
}
|
||||
|
||||
return (m_staticThis->m_rulerType);
|
||||
}
|
||||
|
||||
Real RulerTool::getLength(void)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
return m_staticThis->m_savedLength;
|
||||
}
|
||||
|
||||
return (0.0f);
|
||||
}
|
||||
239
GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp
Normal file
239
GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// SaveMap.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "SaveMap.h"
|
||||
#include "Common/GlobalData.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// SaveMap dialog
|
||||
|
||||
|
||||
SaveMap::SaveMap(TSaveMapInfo *pInfo, CWnd* pParent /*=NULL*/)
|
||||
: CDialog(SaveMap::IDD, pParent),
|
||||
m_pInfo(pInfo)
|
||||
{
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
m_pInfo->usingSystemDir = m_usingSystemDir = ::AfxGetApp()->GetProfileInt(MAP_OPENSAVE_PANEL_SECTION, "UseSystemDir", TRUE);
|
||||
#else
|
||||
m_pInfo->usingSystemDir = m_usingSystemDir = FALSE;
|
||||
#endif
|
||||
|
||||
//{{AFX_DATA_INIT(SaveMap)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void SaveMap::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(SaveMap)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(SaveMap, CDialog)
|
||||
//{{AFX_MSG_MAP(SaveMap)
|
||||
ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
|
||||
ON_BN_CLICKED(IDC_SYSTEMMAPS, OnSystemMaps)
|
||||
ON_BN_CLICKED(IDC_USERMAPS, OnUserMaps)
|
||||
ON_LBN_SELCHANGE(IDC_SAVE_LIST, OnSelchangeSaveList)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// SaveMap message handlers
|
||||
|
||||
void SaveMap::OnSystemMaps()
|
||||
{
|
||||
populateMapListbox( TRUE );
|
||||
}
|
||||
|
||||
void SaveMap::OnUserMaps()
|
||||
{
|
||||
populateMapListbox( FALSE );
|
||||
}
|
||||
|
||||
void SaveMap::OnOK()
|
||||
{
|
||||
CWnd *pEdit = GetDlgItem(IDC_SAVE_MAP_EDIT);
|
||||
if (pEdit == NULL) {
|
||||
DEBUG_CRASH(("Bad resources."));
|
||||
OnCancel();
|
||||
return;
|
||||
}
|
||||
pEdit->GetWindowText(m_pInfo->filename);
|
||||
m_pInfo->browse = false;
|
||||
// Construct file name of .\Maps\mapname\mapname.map
|
||||
CString testName;
|
||||
if (m_usingSystemDir)
|
||||
testName = ".\\Maps\\";
|
||||
else
|
||||
{
|
||||
testName = TheGlobalData->getPath_UserData().str();
|
||||
testName = testName + "Maps\\";
|
||||
}
|
||||
testName += m_pInfo->filename + "\\" + m_pInfo->filename + ".map" ;
|
||||
CFileStatus status;
|
||||
if (CFile::GetStatus(testName, status)) {
|
||||
CString warn;
|
||||
warn.Format(IDS_REPLACEFILE, LPCTSTR(testName));
|
||||
Int ret = ::AfxMessageBox(warn, MB_YESNO);
|
||||
if (ret == IDNO) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
void SaveMap::OnCancel()
|
||||
{
|
||||
CDialog::OnCancel();
|
||||
}
|
||||
|
||||
void SaveMap::OnBrowse()
|
||||
{
|
||||
m_pInfo->browse = true;
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
void SaveMap::populateMapListbox( Bool systemMaps )
|
||||
{
|
||||
m_pInfo->usingSystemDir = m_usingSystemDir = systemMaps;
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
::AfxGetApp()->WriteProfileInt(MAP_OPENSAVE_PANEL_SECTION, "UseSystemDir", m_usingSystemDir);
|
||||
#endif
|
||||
|
||||
HANDLE hFindFile = 0;
|
||||
WIN32_FIND_DATA findData;
|
||||
char dirBuf[_MAX_PATH];
|
||||
char findBuf[_MAX_PATH];
|
||||
char fileBuf[_MAX_PATH];
|
||||
|
||||
if (systemMaps)
|
||||
strcpy(dirBuf, ".\\Maps\\");
|
||||
else
|
||||
sprintf(dirBuf, "%sMaps\\", TheGlobalData->getPath_UserData().str());
|
||||
int len = strlen(dirBuf);
|
||||
|
||||
if (len > 0 && dirBuf[len - 1] != '\\') {
|
||||
dirBuf[len++] = '\\';
|
||||
dirBuf[len] = 0;
|
||||
}
|
||||
CListBox *pList = (CListBox *)this->GetDlgItem(IDC_SAVE_LIST);
|
||||
if (pList == NULL) return;
|
||||
pList->ResetContent();
|
||||
strcpy(findBuf, dirBuf);
|
||||
strcat(findBuf, "*.*");
|
||||
|
||||
hFindFile = FindFirstFile(findBuf, &findData);
|
||||
if (hFindFile != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0)
|
||||
continue;
|
||||
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
continue;
|
||||
}
|
||||
strcpy(fileBuf, dirBuf);
|
||||
strcat(fileBuf, findData.cFileName);
|
||||
strcat(fileBuf, "\\");
|
||||
strcat(fileBuf, findData.cFileName);
|
||||
strcat(fileBuf, ".map");
|
||||
try {
|
||||
CFileStatus status;
|
||||
if (CFile::GetStatus(fileBuf, status)) {
|
||||
if (!(status.m_attribute & CFile::directory)) {
|
||||
pList->AddString(findData.cFileName);
|
||||
};
|
||||
}
|
||||
} catch(...) {}
|
||||
|
||||
} while (FindNextFile(hFindFile, &findData));
|
||||
|
||||
if (hFindFile) FindClose(hFindFile);
|
||||
}
|
||||
CEdit *pEdit = (CEdit*)GetDlgItem(IDC_SAVE_MAP_EDIT);
|
||||
if (pEdit != NULL) {
|
||||
strcpy(fileBuf, m_pInfo->filename);
|
||||
Int len = strlen(fileBuf);
|
||||
if (len>4 && stricmp(".map", fileBuf+(len-4)) == 0) {
|
||||
// strip of the .map
|
||||
fileBuf[len-4] = 0;
|
||||
}
|
||||
while (len>0) {
|
||||
if (fileBuf[len] == '\\') {
|
||||
len++;
|
||||
break;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
pEdit->SetWindowText(&fileBuf[len]);
|
||||
pEdit->SetSel(0, 1000, true);
|
||||
pEdit->SetFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void SaveMap::OnSelchangeSaveList()
|
||||
{
|
||||
CListBox *pList = (CListBox *)this->GetDlgItem(IDC_SAVE_LIST);
|
||||
if (pList == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Int sel = pList->GetCurSel();
|
||||
CString filename;
|
||||
if (sel != LB_ERR) {
|
||||
pList->GetText(sel, filename);
|
||||
}
|
||||
CWnd *pEdit = GetDlgItem(IDC_SAVE_MAP_EDIT);
|
||||
if (pEdit == NULL) {
|
||||
return;
|
||||
}
|
||||
pEdit->SetWindowText(filename);
|
||||
}
|
||||
|
||||
BOOL SaveMap::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CButton *pSystemMaps = (CButton *)this->GetDlgItem(IDC_SYSTEMMAPS);
|
||||
if (pSystemMaps != NULL)
|
||||
pSystemMaps->SetCheck( m_usingSystemDir );
|
||||
|
||||
CButton *pUserMaps = (CButton *)this->GetDlgItem(IDC_USERMAPS);
|
||||
if (pUserMaps != NULL)
|
||||
pUserMaps->SetCheck( !m_usingSystemDir );
|
||||
|
||||
#if !defined(_DEBUG) && !defined(_INTERNAL)
|
||||
if (pSystemMaps)
|
||||
pSystemMaps->ShowWindow( FALSE );
|
||||
if (pUserMaps)
|
||||
pUserMaps->ShowWindow( FALSE );
|
||||
#endif
|
||||
|
||||
populateMapListbox( m_usingSystemDir );
|
||||
|
||||
return FALSE; // return TRUE unless you set the focus to a control
|
||||
|
||||
}
|
||||
259
GeneralsMD/Code/Tools/WorldBuilder/src/ScorchOptions.cpp
Normal file
259
GeneralsMD/Code/Tools/WorldBuilder/src/ScorchOptions.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ScorchOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "ScorchOptions.h"
|
||||
#include "CUndoable.h"
|
||||
#include "WbView3d.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
|
||||
#define DEFAULT_SCORCHMARK_RADIUS 20
|
||||
|
||||
Scorches ScorchOptions::m_scorchtype = SCORCH_1;
|
||||
Real ScorchOptions::m_scorchsize = DEFAULT_SCORCHMARK_RADIUS;
|
||||
ScorchOptions *ScorchOptions::m_staticThis = NULL;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScorchOptions dialog
|
||||
|
||||
|
||||
ScorchOptions::ScorchOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
//{{AFX_DATA_INIT(ScorchOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void ScorchOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(ScorchOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ScorchOptions, CDialog)
|
||||
//{{AFX_MSG_MAP(ScorchOptions)
|
||||
ON_CBN_SELENDOK(IDC_SCORCHTYPE, OnChangeScorchtype)
|
||||
ON_EN_CHANGE(IDC_SIZE_EDIT, OnChangeSizeEdit)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
MapObject *ScorchOptions::getSingleSelectedScorch(void)
|
||||
{
|
||||
MapObject *theMapObj = NULL;
|
||||
// Bool found = false;
|
||||
Int selCount=0;
|
||||
MapObject *pMapObj;
|
||||
for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) {
|
||||
if (pMapObj->isSelected()) {
|
||||
if (pMapObj->isScorch()) {
|
||||
theMapObj = pMapObj;
|
||||
}
|
||||
selCount++;
|
||||
}
|
||||
}
|
||||
if (selCount==1 && theMapObj) {
|
||||
return theMapObj;
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void ScorchOptions::updateTheUI(void)
|
||||
{
|
||||
m_updating = true;
|
||||
MapObject *theMapObj = getSingleSelectedScorch();
|
||||
CString str;
|
||||
CWnd *pEdit;
|
||||
if (theMapObj) {
|
||||
m_scorchtype = (Scorches) theMapObj->getProperties()->getInt(TheKey_scorchType);
|
||||
m_scorchsize = theMapObj->getProperties()->getReal(TheKey_objectRadius);
|
||||
}
|
||||
CComboBox *scorch = (CComboBox*)GetDlgItem(IDC_SCORCHTYPE);
|
||||
|
||||
scorch->SetCurSel((int)m_scorchtype);
|
||||
str.Format("%f",m_scorchsize);
|
||||
pEdit = GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit)
|
||||
pEdit->SetWindowText(str);
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
void ScorchOptions::update(void)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
m_staticThis->updateTheUI();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScorchOptions message handlers
|
||||
|
||||
BOOL ScorchOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
m_staticThis = this;
|
||||
|
||||
|
||||
m_radiusPopup.SetupPopSliderButton(this, IDC_SIZE_POPUP, this);
|
||||
|
||||
CString str;
|
||||
CComboBox *scorch = (CComboBox*)GetDlgItem(IDC_SCORCHTYPE);
|
||||
/* Use the values in the .rc file. jba.
|
||||
scorch->ResetContent();
|
||||
for (Int i = 0; i < SCORCH_COUNT; i++)
|
||||
{
|
||||
str.Format("Scorch %d", i);
|
||||
scorch->InsertString(-1, str);
|
||||
}
|
||||
*/
|
||||
scorch->SetCurSel(0);
|
||||
|
||||
update();
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void ScorchOptions::OnChangeScorchtype()
|
||||
{
|
||||
if (m_updating)
|
||||
return;
|
||||
CComboBox *scorch = (CComboBox*)GetDlgItem(IDC_SCORCHTYPE);
|
||||
int curSel = scorch->GetCurSel();
|
||||
m_scorchtype = (Scorches) curSel;
|
||||
changeScorch();
|
||||
}
|
||||
|
||||
void ScorchOptions::OnChangeSizeEdit()
|
||||
{
|
||||
if (m_updating)
|
||||
return;
|
||||
CWnd* edit = GetDlgItem(IDC_SIZE_EDIT);
|
||||
CString cstr;
|
||||
edit->GetWindowText(cstr);
|
||||
if (!cstr.IsEmpty()) {
|
||||
m_scorchsize = atof(cstr.GetBuffer(0));
|
||||
}
|
||||
changeSize();
|
||||
}
|
||||
|
||||
|
||||
void ScorchOptions::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_SIZE_POPUP:
|
||||
*pMin = 0;
|
||||
*pMax = 256;
|
||||
*pInitial = m_scorchsize;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void ScorchOptions::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit;
|
||||
m_updating = true;
|
||||
switch (sliderID) {
|
||||
case IDC_SIZE_POPUP:
|
||||
m_scorchsize = theVal;
|
||||
str.Format("%f",m_scorchsize);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
void ScorchOptions::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_SIZE_POPUP:
|
||||
changeSize();
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
void ScorchOptions::changeScorch(void)
|
||||
{
|
||||
getAllSelectedDicts();
|
||||
|
||||
Dict newDict;
|
||||
newDict.setInt(TheKey_scorchType, (Int)m_scorchtype);
|
||||
DictItemUndoable *pUndo = new DictItemUndoable(m_allSelectedDicts.begin(), newDict, newDict.getNthKey(0), m_allSelectedDicts.size());
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
|
||||
WbView3d *pView = CWorldBuilderDoc::GetActive3DView();
|
||||
pView->Invalidate();
|
||||
}
|
||||
|
||||
void ScorchOptions::changeSize(void)
|
||||
{
|
||||
getAllSelectedDicts();
|
||||
|
||||
Dict newDict;
|
||||
newDict.setReal(TheKey_objectRadius, m_scorchsize);
|
||||
DictItemUndoable *pUndo = new DictItemUndoable(m_allSelectedDicts.begin(), newDict, newDict.getNthKey(0), m_allSelectedDicts.size());
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
|
||||
WbView3d *pView = CWorldBuilderDoc::GetActive3DView();
|
||||
pView->Invalidate();
|
||||
}
|
||||
|
||||
void ScorchOptions::getAllSelectedDicts(void)
|
||||
{
|
||||
m_allSelectedDicts.clear();
|
||||
|
||||
for (MapObject *pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) {
|
||||
if (!pMapObj->isSelected() || !pMapObj->isScorch()) {
|
||||
continue;
|
||||
}
|
||||
m_allSelectedDicts.push_back(pMapObj->getProperties());
|
||||
}
|
||||
}
|
||||
149
GeneralsMD/Code/Tools/WorldBuilder/src/ScorchTool.cpp
Normal file
149
GeneralsMD/Code/Tools/WorldBuilder/src/ScorchTool.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ScorchTool.cpp
|
||||
// Author: Dennis Griffin, April 2002
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "ScorchTool.h"
|
||||
#include "PointerTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "ScorchOptions.h"
|
||||
#include "MainFrm.h"
|
||||
#include "DrawObject.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
|
||||
//
|
||||
// ScorchTool class.
|
||||
//
|
||||
|
||||
/// Constructor
|
||||
ScorchTool::ScorchTool(void) :
|
||||
Tool(ID_SCORCH_TOOL, IDC_SCORCH)
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
ScorchTool::~ScorchTool(void)
|
||||
{
|
||||
}
|
||||
|
||||
/// Clears it's is active flag.
|
||||
void ScorchTool::deactivate()
|
||||
{
|
||||
}
|
||||
|
||||
/// Shows the terrain materials options panel.
|
||||
void ScorchTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_SCORCH_OPTIONS);
|
||||
ScorchOptions::update();
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
}
|
||||
|
||||
// Pick a scorchmark.
|
||||
MapObject *ScorchTool::pickScorch(Coord3D loc){
|
||||
// Tight check first.
|
||||
MapObject *pObj;
|
||||
for (pObj = MapObject::getFirstMapObject(); pObj; pObj = pObj->getNext()) {
|
||||
if (!pObj->isScorch()) {
|
||||
continue;
|
||||
}
|
||||
Coord3D cloc = *pObj->getLocation();
|
||||
// Check and see if we are within 1/2 cell size of the center.
|
||||
Coord3D cpt = loc;
|
||||
cpt.x -= cloc.x;
|
||||
cpt.y -= cloc.y;
|
||||
cpt.z = 0;
|
||||
if (cpt.length() < 0.5f*MAP_XY_FACTOR) {
|
||||
return pObj;
|
||||
}
|
||||
}
|
||||
// Loose check
|
||||
for (pObj = MapObject::getFirstMapObject(); pObj; pObj = pObj->getNext()) {
|
||||
if (!pObj->isScorch()) {
|
||||
continue;
|
||||
}
|
||||
Coord3D cloc = *pObj->getLocation();
|
||||
// Check and see if we are within 1 & 1/2 cell size of the center.
|
||||
Coord3D cpt = loc;
|
||||
cpt.x -= cloc.x;
|
||||
cpt.y -= cloc.y;
|
||||
cpt.z = 0;
|
||||
if (cpt.length() < 1.5f*MAP_XY_FACTOR) {
|
||||
return pObj;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Perform the tool behavior on mouse down.
|
||||
void ScorchTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
MapObject *pObj = pickScorch(docPt);
|
||||
if (pObj) {
|
||||
pObj->setSelected(true);
|
||||
docPt = *pObj->getLocation();
|
||||
ScorchOptions::update();
|
||||
} else {
|
||||
pView->snapPoint(&docPt);
|
||||
MapObject *pNew = newInstance(MapObject)(docPt, AsciiString("Scorch"), 0, 0, NULL, NULL );
|
||||
pNew->getProperties()->setAsciiString(TheKey_originalOwner, NEUTRAL_TEAM_INTERNAL_STR);
|
||||
pNew->setSelected(true);
|
||||
pNew->setIsScorch();
|
||||
pNew->getProperties()->setReal(TheKey_objectRadius, ScorchOptions::getScorchSize());
|
||||
pNew->getProperties()->setInt(TheKey_scorchType, ScorchOptions::getScorchType());
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, pNew);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
pNew = NULL; // undoable owns it now.
|
||||
ScorchOptions::update();
|
||||
}
|
||||
m_mouseDownPt = docPt;
|
||||
}
|
||||
|
||||
/// Left button move code.
|
||||
void ScorchTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
MapObject *pObj = pickScorch(docPt);
|
||||
if (pObj) {
|
||||
docPt = *pObj->getLocation();
|
||||
} else {
|
||||
pView->snapPoint(&docPt);
|
||||
}
|
||||
pView->Invalidate();
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse up - Place an object. */
|
||||
void ScorchTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
275
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptActionsFalse.cpp
Normal file
275
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptActionsFalse.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ScriptActionsFalse.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "ScriptActionsFalse.h"
|
||||
#include "GameLogic/Scripts.h"
|
||||
#include "EditAction.h"
|
||||
#include "ScriptDialog.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptActionsFalse property page
|
||||
|
||||
IMPLEMENT_DYNCREATE(ScriptActionsFalse, CPropertyPage)
|
||||
|
||||
ScriptActionsFalse::ScriptActionsFalse() : CPropertyPage(ScriptActionsFalse::IDD),
|
||||
m_falseAction(NULL),
|
||||
m_index(0)
|
||||
{
|
||||
//{{AFX_DATA_INIT(ScriptActionsFalse)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
ScriptActionsFalse::~ScriptActionsFalse()
|
||||
{
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CPropertyPage::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(ScriptActionsFalse)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ScriptActionsFalse, CPropertyPage)
|
||||
//{{AFX_MSG_MAP(ScriptActionsFalse)
|
||||
ON_BN_CLICKED(IDC_EDIT, OnEditAction)
|
||||
ON_LBN_SELCHANGE(IDC_ACTION_LIST, OnSelchangeActionList)
|
||||
ON_LBN_DBLCLK(IDC_ACTION_LIST, OnDblclkActionList)
|
||||
ON_BN_CLICKED(IDC_NEW, OnNew)
|
||||
ON_BN_CLICKED(IDC_DELETE, OnDelete)
|
||||
ON_BN_CLICKED(IDC_COPY, OnCopy)
|
||||
ON_BN_CLICKED(IDC_MOVE_DOWN, OnMoveDown)
|
||||
ON_BN_CLICKED(IDC_MOVE_UP, OnMoveUp)
|
||||
ON_EN_CHANGE(IDC_EDIT_COMMENT, OnChangeEditComment)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptActionsFalse message handlers
|
||||
|
||||
BOOL ScriptActionsFalse::OnInitDialog()
|
||||
{
|
||||
CPropertyPage::OnInitDialog();
|
||||
CWnd *pWnd = GetDlgItem(IDC_EDIT_COMMENT);
|
||||
pWnd->SetWindowText(m_script->getActionComment().str());
|
||||
loadList();
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::loadList(void)
|
||||
{
|
||||
m_falseAction = NULL;
|
||||
ScriptDialog::updateScriptWarning(m_script);
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_ACTION_LIST);
|
||||
Int count = 0;
|
||||
if (pList) {
|
||||
pList->ResetContent();
|
||||
ScriptAction *pAction = m_script->getFalseAction();
|
||||
while (pAction) {
|
||||
AsciiString astr = pAction->getUiText();
|
||||
if (astr.isEmpty()) {
|
||||
astr = "Invalid Action";
|
||||
}
|
||||
|
||||
pList->AddString(astr.str());
|
||||
pAction = pAction->getNext();
|
||||
count++;
|
||||
}
|
||||
if (count>0 && count<=m_index) {
|
||||
m_index = count-1;
|
||||
}
|
||||
pList->SetCurSel(m_index);
|
||||
OnSelchangeActionList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScriptActionsFalse::OnEditAction()
|
||||
{
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_ACTION_LIST);
|
||||
if (m_falseAction == NULL) {
|
||||
return;
|
||||
}
|
||||
EditAction cDlg;
|
||||
cDlg.setAction(m_falseAction);
|
||||
cDlg.DoModal();
|
||||
ScriptDialog::updateScriptWarning(m_script);
|
||||
pList->DeleteString(m_index);
|
||||
pList->InsertString(m_index, m_falseAction->getUiText().str());
|
||||
pList->SetCurSel(m_index);
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::enableUI()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_EDIT);
|
||||
pWnd->EnableWindow(m_falseAction!=NULL);
|
||||
|
||||
pWnd = GetDlgItem(IDC_COPY);
|
||||
pWnd->EnableWindow(m_falseAction!=NULL);
|
||||
|
||||
pWnd = GetDlgItem(IDC_DELETE);
|
||||
pWnd->EnableWindow(m_falseAction!=NULL);
|
||||
|
||||
pWnd = GetDlgItem(IDC_MOVE_DOWN);
|
||||
pWnd->EnableWindow(m_falseAction && m_falseAction->getNext());
|
||||
|
||||
pWnd = GetDlgItem(IDC_MOVE_UP);
|
||||
pWnd->EnableWindow(m_falseAction && m_index>0);
|
||||
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::OnSelchangeActionList()
|
||||
{
|
||||
m_falseAction = NULL;
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_ACTION_LIST);
|
||||
if (pList) {
|
||||
Int count = pList->GetCurSel();
|
||||
m_index = count;
|
||||
if (count<0) {
|
||||
enableUI();
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
m_falseAction = m_script->getFalseAction();
|
||||
while (m_falseAction) {
|
||||
count--;
|
||||
if (count==0) {
|
||||
enableUI(); // Enable buttons based on selection.
|
||||
return;
|
||||
}
|
||||
m_falseAction = m_falseAction->getNext();
|
||||
}
|
||||
}
|
||||
enableUI(); // Enable buttons based on selection.
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::OnDblclkActionList()
|
||||
{
|
||||
OnEditAction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ScriptActionsFalse::OnNew()
|
||||
{
|
||||
ScriptAction *pAct = newInstance( ScriptAction)(ScriptAction::DEBUG_MESSAGE_BOX);
|
||||
EditAction aDlg;
|
||||
aDlg.setAction(pAct);
|
||||
if (IDOK==aDlg.DoModal()) {
|
||||
if (m_falseAction) {
|
||||
pAct->setNextAction(m_falseAction->getNext());
|
||||
m_falseAction->setNextAction(pAct);
|
||||
} else {
|
||||
pAct->setNextAction(m_script->getFalseAction());
|
||||
m_script->setFalseAction(pAct);
|
||||
}
|
||||
m_index++;
|
||||
loadList();
|
||||
} else {
|
||||
pAct->deleteInstance();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::OnDelete()
|
||||
{
|
||||
if (m_falseAction) {
|
||||
m_script->deleteFalseAction(m_falseAction);
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::OnCopy()
|
||||
{
|
||||
if (m_falseAction) {
|
||||
ScriptAction *pCopy = m_falseAction->duplicate();
|
||||
pCopy->setNextAction(m_falseAction->getNext());
|
||||
m_falseAction->setNextAction(pCopy);
|
||||
m_index++;
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
Bool ScriptActionsFalse::doMoveDown()
|
||||
{
|
||||
if (m_falseAction && m_falseAction->getNext()) {
|
||||
ScriptAction *pNext = m_falseAction->getNext();
|
||||
ScriptAction *pCur = m_script->getFalseAction();
|
||||
ScriptAction *pPrev = NULL;
|
||||
while (pCur != m_falseAction) {
|
||||
pPrev = pCur;
|
||||
pCur = pCur->getNext();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(pCur, ("Didn't find action in list."));
|
||||
if (!pCur) return false;
|
||||
if (pPrev) {
|
||||
pPrev->setNextAction(pNext);
|
||||
pCur->setNextAction(pNext->getNext());
|
||||
pNext->setNextAction(pCur);
|
||||
} else {
|
||||
DEBUG_ASSERTCRASH(m_falseAction == m_script->getFalseAction(), ("Logic error."));
|
||||
pCur->setNextAction(pNext->getNext());
|
||||
pNext->setNextAction(pCur);
|
||||
m_script->setFalseAction(pNext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::OnMoveDown()
|
||||
{
|
||||
if (doMoveDown()) {
|
||||
m_index++;
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::OnMoveUp()
|
||||
{
|
||||
if (m_falseAction && m_index>0) {
|
||||
// ScriptAction *pNext = m_falseAction;
|
||||
ScriptAction *pPrev = m_script->getFalseAction();
|
||||
while (pPrev->getNext() != m_falseAction) {
|
||||
pPrev = pPrev->getNext();
|
||||
}
|
||||
if (pPrev) {
|
||||
m_falseAction = pPrev;
|
||||
m_index--;
|
||||
if (doMoveDown()) {
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptActionsFalse::OnChangeEditComment()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_EDIT_COMMENT);
|
||||
CString comment;
|
||||
pWnd->GetWindowText(comment);
|
||||
m_script->setActionComment(AsciiString((LPCTSTR)comment));
|
||||
}
|
||||
275
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptActionsTrue.cpp
Normal file
275
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptActionsTrue.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ScriptActionsTrue.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "ScriptActionsTrue.h"
|
||||
#include "GameLogic/Scripts.h"
|
||||
#include "EditAction.h"
|
||||
#include "ScriptDialog.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptActionsTrue property page
|
||||
|
||||
IMPLEMENT_DYNCREATE(ScriptActionsTrue, CPropertyPage)
|
||||
|
||||
ScriptActionsTrue::ScriptActionsTrue() : CPropertyPage(ScriptActionsTrue::IDD),
|
||||
m_action(NULL),
|
||||
m_index(0)
|
||||
{
|
||||
//{{AFX_DATA_INIT(ScriptActionsTrue)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
ScriptActionsTrue::~ScriptActionsTrue()
|
||||
{
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CPropertyPage::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(ScriptActionsTrue)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ScriptActionsTrue, CPropertyPage)
|
||||
//{{AFX_MSG_MAP(ScriptActionsTrue)
|
||||
ON_BN_CLICKED(IDC_EDIT, OnEditAction)
|
||||
ON_LBN_SELCHANGE(IDC_ACTION_LIST, OnSelchangeActionList)
|
||||
ON_LBN_DBLCLK(IDC_ACTION_LIST, OnDblclkActionList)
|
||||
ON_BN_CLICKED(IDC_NEW, OnNew)
|
||||
ON_BN_CLICKED(IDC_DELETE, OnDelete)
|
||||
ON_BN_CLICKED(IDC_COPY, OnCopy)
|
||||
ON_BN_CLICKED(IDC_MOVE_DOWN, OnMoveDown)
|
||||
ON_BN_CLICKED(IDC_MOVE_UP, OnMoveUp)
|
||||
ON_EN_CHANGE(IDC_EDIT_COMMENT, OnChangeEditComment)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptActionsTrue message handlers
|
||||
|
||||
BOOL ScriptActionsTrue::OnInitDialog()
|
||||
{
|
||||
CPropertyPage::OnInitDialog();
|
||||
CWnd *pWnd = GetDlgItem(IDC_EDIT_COMMENT);
|
||||
pWnd->SetWindowText(m_script->getActionComment().str());
|
||||
loadList();
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::loadList(void)
|
||||
{
|
||||
m_action = NULL;
|
||||
ScriptDialog::updateScriptWarning(m_script);
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_ACTION_LIST);
|
||||
Int count = 0;
|
||||
if (pList) {
|
||||
pList->ResetContent();
|
||||
ScriptAction *pAction = m_script->getAction();
|
||||
while (pAction) {
|
||||
AsciiString astr = pAction->getUiText();
|
||||
if (astr.isEmpty()) {
|
||||
astr = "Invalid Action";
|
||||
}
|
||||
|
||||
pList->AddString(astr.str());
|
||||
pAction = pAction->getNext();
|
||||
count++;
|
||||
}
|
||||
if (count>0 && count<=m_index) {
|
||||
m_index = count-1;
|
||||
}
|
||||
pList->SetCurSel(m_index);
|
||||
OnSelchangeActionList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScriptActionsTrue::OnEditAction()
|
||||
{
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_ACTION_LIST);
|
||||
if (m_action == NULL) {
|
||||
return;
|
||||
}
|
||||
EditAction cDlg;
|
||||
cDlg.setAction(m_action);
|
||||
cDlg.DoModal();
|
||||
ScriptDialog::updateScriptWarning(m_script);
|
||||
pList->DeleteString(m_index);
|
||||
pList->InsertString(m_index, m_action->getUiText().str());
|
||||
pList->SetCurSel(m_index);
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::enableUI()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_EDIT);
|
||||
pWnd->EnableWindow(m_action!=NULL);
|
||||
|
||||
pWnd = GetDlgItem(IDC_COPY);
|
||||
pWnd->EnableWindow(m_action!=NULL);
|
||||
|
||||
pWnd = GetDlgItem(IDC_DELETE);
|
||||
pWnd->EnableWindow(m_action!=NULL);
|
||||
|
||||
pWnd = GetDlgItem(IDC_MOVE_DOWN);
|
||||
pWnd->EnableWindow(m_action && m_action->getNext());
|
||||
|
||||
pWnd = GetDlgItem(IDC_MOVE_UP);
|
||||
pWnd->EnableWindow(m_action && m_index>0);
|
||||
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::OnSelchangeActionList()
|
||||
{
|
||||
m_action = NULL;
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_ACTION_LIST);
|
||||
if (pList) {
|
||||
Int count = pList->GetCurSel();
|
||||
m_index = count;
|
||||
if (count<0) {
|
||||
enableUI();
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
m_action = m_script->getAction();
|
||||
while (m_action) {
|
||||
count--;
|
||||
if (count==0) {
|
||||
enableUI(); // Enable buttons based on selection.
|
||||
return;
|
||||
}
|
||||
m_action = m_action->getNext();
|
||||
}
|
||||
}
|
||||
enableUI(); // Enable buttons based on selection.
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::OnDblclkActionList()
|
||||
{
|
||||
OnEditAction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ScriptActionsTrue::OnNew()
|
||||
{
|
||||
ScriptAction *pAct = newInstance( ScriptAction)(ScriptAction::DEBUG_MESSAGE_BOX);
|
||||
EditAction aDlg;
|
||||
aDlg.setAction(pAct);
|
||||
if (IDOK==aDlg.DoModal()) {
|
||||
if (m_action) {
|
||||
pAct->setNextAction(m_action->getNext());
|
||||
m_action->setNextAction(pAct);
|
||||
} else {
|
||||
pAct->setNextAction(m_script->getAction());
|
||||
m_script->setAction(pAct);
|
||||
}
|
||||
m_index++;
|
||||
loadList();
|
||||
} else {
|
||||
pAct->deleteInstance();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::OnDelete()
|
||||
{
|
||||
if (m_action) {
|
||||
m_script->deleteAction(m_action);
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::OnCopy()
|
||||
{
|
||||
if (m_action) {
|
||||
ScriptAction *pCopy = m_action->duplicate();
|
||||
pCopy->setNextAction(m_action->getNext());
|
||||
m_action->setNextAction(pCopy);
|
||||
m_index++;
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
Bool ScriptActionsTrue::doMoveDown()
|
||||
{
|
||||
if (m_action && m_action->getNext()) {
|
||||
ScriptAction *pNext = m_action->getNext();
|
||||
ScriptAction *pCur = m_script->getAction();
|
||||
ScriptAction *pPrev = NULL;
|
||||
while (pCur != m_action) {
|
||||
pPrev = pCur;
|
||||
pCur = pCur->getNext();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(pCur, ("Didn't find action in list."));
|
||||
if (!pCur) return false;
|
||||
if (pPrev) {
|
||||
pPrev->setNextAction(pNext);
|
||||
pCur->setNextAction(pNext->getNext());
|
||||
pNext->setNextAction(pCur);
|
||||
} else {
|
||||
DEBUG_ASSERTCRASH(m_action == m_script->getAction(), ("Logic error."));
|
||||
pCur->setNextAction(pNext->getNext());
|
||||
pNext->setNextAction(pCur);
|
||||
m_script->setAction(pNext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::OnMoveDown()
|
||||
{
|
||||
if (doMoveDown()) {
|
||||
m_index++;
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::OnMoveUp()
|
||||
{
|
||||
if (m_action && m_index>0) {
|
||||
// ScriptAction *pNext = m_action;
|
||||
ScriptAction *pPrev = m_script->getAction();
|
||||
while (pPrev->getNext() != m_action) {
|
||||
pPrev = pPrev->getNext();
|
||||
}
|
||||
if (pPrev) {
|
||||
m_action = pPrev;
|
||||
m_index--;
|
||||
if (doMoveDown()) {
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptActionsTrue::OnChangeEditComment()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_EDIT_COMMENT);
|
||||
CString comment;
|
||||
pWnd->GetWindowText(comment);
|
||||
m_script->setActionComment(AsciiString((LPCTSTR)comment));
|
||||
}
|
||||
440
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptConditions.cpp
Normal file
440
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptConditions.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ScriptConditions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "ScriptConditions.h"
|
||||
#include "GameLogic/Scripts.h"
|
||||
#include "EditCondition.h"
|
||||
#include "ScriptDialog.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptConditionsDlg property page
|
||||
|
||||
IMPLEMENT_DYNCREATE(ScriptConditionsDlg, CPropertyPage)
|
||||
|
||||
ScriptConditionsDlg::ScriptConditionsDlg() : CPropertyPage(ScriptConditionsDlg::IDD),
|
||||
m_condition(NULL),
|
||||
m_orCondition(NULL),
|
||||
m_index(0)
|
||||
{
|
||||
//{{AFX_DATA_INIT(ScriptConditionsDlg)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
ScriptConditionsDlg::~ScriptConditionsDlg()
|
||||
{
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CPropertyPage::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(ScriptConditionsDlg)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ScriptConditionsDlg, CPropertyPage)
|
||||
//{{AFX_MSG_MAP(ScriptConditionsDlg)
|
||||
ON_BN_CLICKED(IDC_EDIT_CONDITION, OnEditCondition)
|
||||
ON_LBN_SELCHANGE(IDC_CONDITION_LIST, OnSelchangeConditionList)
|
||||
ON_LBN_DBLCLK(IDC_CONDITION_LIST, OnDblclkConditionList)
|
||||
ON_BN_CLICKED(IDC_OR, OnOr)
|
||||
ON_BN_CLICKED(IDC_NEW, OnNew)
|
||||
ON_BN_CLICKED(IDC_DELETE, OnDelete)
|
||||
ON_BN_CLICKED(IDC_COPY, OnCopy)
|
||||
ON_BN_CLICKED(IDC_MOVE_DOWN, OnMoveDown)
|
||||
ON_BN_CLICKED(IDC_MOVE_UP, OnMoveUp)
|
||||
ON_EN_CHANGE(IDC_EDIT_COMMENT, OnChangeEditComment)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptConditionsDlg message handlers
|
||||
|
||||
BOOL ScriptConditionsDlg::OnInitDialog()
|
||||
{
|
||||
CPropertyPage::OnInitDialog();
|
||||
loadList();
|
||||
CWnd *pWnd = GetDlgItem(IDC_EDIT_COMMENT);
|
||||
pWnd->SetWindowText(m_script->getConditionComment().str());
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_CONDITION_LIST);
|
||||
pList->SetHorizontalExtent(10000); // band-aid fix until more precise length can be determined
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::loadList(void)
|
||||
{
|
||||
Int count = 0;
|
||||
ScriptDialog::updateScriptWarning(m_script);
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_CONDITION_LIST);
|
||||
if (pList) {
|
||||
pList->ResetContent();
|
||||
OrCondition *pOr = m_script->getOrCondition();
|
||||
while (pOr) {
|
||||
if (count==0) {
|
||||
pList->AddString("*** IF ***");
|
||||
} else {
|
||||
pList->AddString("*** OR ***");
|
||||
}
|
||||
Condition *pCond = pOr->getFirstAndCondition();
|
||||
Bool first = true;
|
||||
while (pCond) {
|
||||
AsciiString label;
|
||||
if (first) label = " ";
|
||||
else label = " *AND* ";
|
||||
first = false;
|
||||
label.concat(pCond->getUiText());
|
||||
pList->AddString(label.str());
|
||||
pCond = pCond->getNext();
|
||||
}
|
||||
count++;
|
||||
pOr = pOr->getNextOrCondition();
|
||||
}
|
||||
if (count>0) pList->SetCurSel(1);
|
||||
OnSelchangeConditionList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScriptConditionsDlg::OnEditCondition()
|
||||
{
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_CONDITION_LIST);
|
||||
if (m_condition == NULL) {
|
||||
return;
|
||||
}
|
||||
EditCondition cDlg;
|
||||
cDlg.setCondition(m_condition);
|
||||
cDlg.DoModal();
|
||||
ScriptDialog::updateScriptWarning(m_script);
|
||||
pList->DeleteString(m_index);
|
||||
AsciiString label;
|
||||
Bool first;
|
||||
if (m_orCondition && m_orCondition->getFirstAndCondition() == m_condition) {
|
||||
first = true;
|
||||
}
|
||||
if (first) label = " ";
|
||||
else label = " AND ";
|
||||
label.concat(m_condition->getUiText());
|
||||
pList->InsertString(m_index, label.str());
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::enableUI()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_EDIT_CONDITION);
|
||||
pWnd->EnableWindow(m_condition!=NULL);
|
||||
|
||||
pWnd = GetDlgItem(IDC_COPY);
|
||||
pWnd->EnableWindow(m_condition!=NULL);
|
||||
|
||||
pWnd = GetDlgItem(IDC_DELETE);
|
||||
pWnd->EnableWindow(m_condition || m_orCondition);
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::setSel(OrCondition *pOr, Condition *pCond)
|
||||
{
|
||||
m_orCondition = NULL;
|
||||
m_condition = NULL;
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_CONDITION_LIST);
|
||||
if (pList) {
|
||||
pList->SetCurSel(-1);
|
||||
Int count = 0;
|
||||
m_orCondition = m_script->getOrCondition();
|
||||
while (m_orCondition) {
|
||||
if (m_orCondition==pOr && pCond==NULL) {
|
||||
pList->SetCurSel(count);
|
||||
enableUI();
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
m_condition = m_orCondition->getFirstAndCondition();
|
||||
while (m_condition) {
|
||||
if (m_condition == pCond) {
|
||||
pList->SetCurSel(count);
|
||||
enableUI();
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
m_condition = m_condition->getNext();
|
||||
}
|
||||
m_orCondition = m_orCondition->getNextOrCondition();
|
||||
}
|
||||
}
|
||||
enableUI();
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::OnSelchangeConditionList()
|
||||
{
|
||||
m_orCondition = NULL;
|
||||
m_condition = NULL;
|
||||
CListBox *pList = (CListBox *)GetDlgItem(IDC_CONDITION_LIST);
|
||||
if (pList) {
|
||||
Int count = pList->GetCurSel();
|
||||
m_index = count;
|
||||
if (count<0) return;
|
||||
count+=1;
|
||||
m_orCondition = m_script->getOrCondition();
|
||||
while (m_orCondition) {
|
||||
count--;
|
||||
if (count==0) {
|
||||
enableUI(); // Enable buttons based on selection.
|
||||
return;
|
||||
}
|
||||
m_condition = m_orCondition->getFirstAndCondition();
|
||||
while (m_condition) {
|
||||
count--;
|
||||
if (count==0) {
|
||||
enableUI(); // Enable buttons based on selection.
|
||||
return;
|
||||
}
|
||||
m_condition = m_condition->getNext();
|
||||
}
|
||||
m_orCondition = m_orCondition->getNextOrCondition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::OnDblclkConditionList()
|
||||
{
|
||||
OnEditCondition();
|
||||
}
|
||||
|
||||
|
||||
void ScriptConditionsDlg::OnOr()
|
||||
{
|
||||
OrCondition *pOr = newInstance( OrCondition);
|
||||
if (m_orCondition) {
|
||||
pOr->setNextOrCondition(m_orCondition->getNextOrCondition());
|
||||
m_orCondition->setNextOrCondition(pOr);
|
||||
} else {
|
||||
pOr->setNextOrCondition(m_script->getOrCondition());
|
||||
m_script->setOrCondition(pOr);
|
||||
}
|
||||
loadList();
|
||||
setSel(pOr, NULL);
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::OnNew()
|
||||
{
|
||||
Condition *pCond = newInstance( Condition)(Condition::CONDITION_TRUE);
|
||||
EditCondition cDlg;
|
||||
cDlg.setCondition(pCond);
|
||||
if (IDOK==cDlg.DoModal()) {
|
||||
OrCondition *pSavOr = m_orCondition;
|
||||
if (m_condition) {
|
||||
pCond->setNextCondition(m_condition->getNext());
|
||||
m_condition->setNextCondition(pCond);
|
||||
} else {
|
||||
if (m_orCondition == NULL) {
|
||||
OrCondition *pOr = newInstance( OrCondition);
|
||||
pOr->setNextOrCondition(m_script->getOrCondition());
|
||||
m_script->setOrCondition(pOr);
|
||||
m_orCondition = pOr;
|
||||
}
|
||||
pCond->setNextCondition(m_orCondition->getFirstAndCondition());
|
||||
m_orCondition->setFirstAndCondition(pCond);
|
||||
}
|
||||
loadList();
|
||||
setSel(pSavOr, pCond);
|
||||
} else {
|
||||
pCond->deleteInstance();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::OnDelete()
|
||||
{
|
||||
if (m_condition && m_orCondition) {
|
||||
m_orCondition->deleteCondition(m_condition);
|
||||
loadList();
|
||||
} else if (m_orCondition) {
|
||||
m_script->deleteOrCondition(m_orCondition);
|
||||
loadList();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::OnCopy()
|
||||
{
|
||||
if (m_condition) {
|
||||
OrCondition *pSavOr = m_orCondition;
|
||||
Condition *pCopy = m_condition->duplicate();
|
||||
pCopy->setNextCondition(m_condition->getNext());
|
||||
m_condition->setNextCondition(pCopy);
|
||||
loadList();
|
||||
setSel(pSavOr, pCopy);
|
||||
}
|
||||
}
|
||||
|
||||
Int ScriptConditionsDlg::doMoveDown( OrCondition **outWhichNow )
|
||||
{
|
||||
(*outWhichNow) = m_orCondition;
|
||||
if (m_condition && m_orCondition) {
|
||||
Condition *pNext = m_condition->getNext();
|
||||
if (pNext==NULL) {
|
||||
OrCondition *pNOr = m_orCondition->getNextOrCondition();
|
||||
if (!pNOr) {
|
||||
pNOr = newInstance( OrCondition);
|
||||
m_orCondition->setNextOrCondition(pNOr);
|
||||
}
|
||||
Condition *newNext = pNOr->getFirstAndCondition();
|
||||
pNOr->setFirstAndCondition(m_condition);
|
||||
m_orCondition->removeCondition(m_condition);
|
||||
m_condition->setNextCondition(newNext);
|
||||
|
||||
*outWhichNow = pNOr;
|
||||
return 2; // we moved 2 indices, not just one.
|
||||
}
|
||||
|
||||
Condition *pCur = m_orCondition->getFirstAndCondition();
|
||||
Condition *pPrev = NULL;
|
||||
while (pCur != m_condition) {
|
||||
pPrev = pCur;
|
||||
pCur = pCur->getNext();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(pCur, ("Didn't find condition in list."));
|
||||
if (!pCur) return 0;
|
||||
if (pPrev) {
|
||||
pPrev->setNextCondition(pNext);
|
||||
pCur->setNextCondition(pNext->getNext());
|
||||
pNext->setNextCondition(pCur);
|
||||
} else {
|
||||
DEBUG_ASSERTCRASH(m_condition == m_orCondition->getFirstAndCondition(), ("Logic error."));
|
||||
pCur->setNextCondition(pNext->getNext());
|
||||
pNext->setNextCondition(pCur);
|
||||
m_orCondition->setFirstAndCondition(pNext);
|
||||
}
|
||||
return 1;
|
||||
} else if (m_orCondition) {
|
||||
OrCondition *pNext = m_orCondition->getNextOrCondition();
|
||||
if (pNext==NULL) return 0;
|
||||
OrCondition *pCur = m_script->getOrCondition();
|
||||
OrCondition *pPrev = NULL;
|
||||
while (pCur != m_orCondition) {
|
||||
pPrev = pCur;
|
||||
pCur = pCur->getNextOrCondition();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(pCur, ("Didn't find Or in list."));
|
||||
if (!pCur) return 0;
|
||||
if (pPrev) {
|
||||
pPrev->setNextOrCondition(pNext);
|
||||
pCur->setNextOrCondition(pNext->getNextOrCondition());
|
||||
pNext->setNextOrCondition(pCur);
|
||||
} else {
|
||||
DEBUG_ASSERTCRASH(m_orCondition == m_script->getOrCondition(), ("Logic error."));
|
||||
pCur->setNextOrCondition(pNext->getNextOrCondition());
|
||||
pNext->setNextOrCondition(pCur);
|
||||
m_script->setOrCondition(pNext);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int ScriptConditionsDlg::doMoveUp( OrCondition **outWhichNow )
|
||||
{
|
||||
(*outWhichNow) = m_orCondition;
|
||||
if (m_condition && m_orCondition) {
|
||||
(*outWhichNow) = m_orCondition;
|
||||
Condition *pPrev = m_orCondition->findPreviousCondition(m_condition);
|
||||
if (pPrev == NULL) {
|
||||
OrCondition *pNOr = m_script->findPreviousOrCondition(m_orCondition);
|
||||
if (!pNOr) {
|
||||
pNOr = newInstance( OrCondition);
|
||||
pNOr->setNextOrCondition(m_orCondition);
|
||||
m_script->setOrCondition(pNOr);
|
||||
}
|
||||
Condition *previous = pNOr->findPreviousCondition(NULL);
|
||||
if (previous) {
|
||||
m_orCondition->removeCondition(m_condition);
|
||||
previous->setNextCondition(m_condition);
|
||||
} else {
|
||||
m_orCondition->removeCondition(m_condition);
|
||||
pNOr->setFirstAndCondition(m_condition);
|
||||
}
|
||||
|
||||
(*outWhichNow) = pNOr;
|
||||
return 2; // we moved 2 indices, not just one.
|
||||
}
|
||||
|
||||
pPrev->setNextCondition(m_condition->getNext());
|
||||
m_condition->setNextCondition(pPrev);
|
||||
|
||||
Condition *pPrevPrev = m_orCondition->findPreviousCondition(pPrev);
|
||||
if (pPrevPrev) {
|
||||
pPrevPrev->setNextCondition(m_condition);
|
||||
} else {
|
||||
m_orCondition->setFirstAndCondition(m_condition);
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else if (m_orCondition) {
|
||||
OrCondition *pOrPrev = m_script->findPreviousOrCondition(m_orCondition);
|
||||
if (!pOrPrev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pOrPrev->setNextOrCondition(m_orCondition->getNextOrCondition());
|
||||
m_orCondition->setNextOrCondition(pOrPrev);
|
||||
|
||||
OrCondition *pOrPrevPrev = m_script->findPreviousOrCondition(pOrPrev);
|
||||
if (pOrPrevPrev) {
|
||||
pOrPrevPrev->setNextOrCondition(m_orCondition);
|
||||
} else {
|
||||
m_script->setOrCondition(m_orCondition);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::OnMoveDown()
|
||||
{
|
||||
Condition *pSav = m_condition;
|
||||
OrCondition *pSavOr;
|
||||
if (doMoveDown(&pSavOr) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadList();
|
||||
setSel(pSavOr, pSav);
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::OnMoveUp()
|
||||
{
|
||||
Condition *pSav = m_condition;
|
||||
OrCondition *pSavOr;
|
||||
if (doMoveUp(&pSavOr) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadList();
|
||||
setSel(pSavOr, pSav);
|
||||
}
|
||||
|
||||
void ScriptConditionsDlg::OnChangeEditComment()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_EDIT_COMMENT);
|
||||
CString comment;
|
||||
pWnd->GetWindowText(comment);
|
||||
m_script->setConditionComment(AsciiString((LPCTSTR)comment));
|
||||
}
|
||||
2166
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
Normal file
2166
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
Normal file
File diff suppressed because it is too large
Load Diff
219
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptProperties.cpp
Normal file
219
GeneralsMD/Code/Tools/WorldBuilder/src/ScriptProperties.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ScriptProperties.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "ScriptProperties.h"
|
||||
#include "GameLogic/Scripts.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma message("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptProperties property page
|
||||
|
||||
IMPLEMENT_DYNCREATE(ScriptProperties, CPropertyPage)
|
||||
|
||||
ScriptProperties::ScriptProperties() : m_updating(false), m_script(NULL),
|
||||
CPropertyPage(ScriptProperties::IDD)
|
||||
{
|
||||
//{{AFX_DATA_INIT(ScriptProperties)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
ScriptProperties::~ScriptProperties()
|
||||
{
|
||||
}
|
||||
|
||||
void ScriptProperties::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CPropertyPage::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(ScriptProperties)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ScriptProperties, CPropertyPage)
|
||||
//{{AFX_MSG_MAP(ScriptProperties)
|
||||
ON_EN_CHANGE(IDC_SCRIPT_COMMENT, OnChangeScriptComment)
|
||||
ON_EN_CHANGE(IDC_SCRIPT_NAME, OnChangeScriptName)
|
||||
ON_BN_CLICKED(IDC_SCRIPT_ACTIVE, OnScriptActive)
|
||||
ON_BN_CLICKED(IDC_ONE_SHOT, OnOneShot)
|
||||
ON_BN_CLICKED(IDC_EASY, OnEasy)
|
||||
ON_BN_CLICKED(IDC_HARD, OnHard)
|
||||
ON_BN_CLICKED(IDC_NORMAL, OnNormal)
|
||||
ON_BN_CLICKED(IDC_SCRIPT_SUBROUTINE, OnScriptSubroutine)
|
||||
ON_BN_CLICKED(IDC_EVERY_FRAME, OnEveryFrame)
|
||||
ON_BN_CLICKED(IDC_EVERY_SECOND, OnEverySecond)
|
||||
ON_EN_CHANGE(IDC_SECONDS_EDIT, OnChangeSecondsEdit)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ScriptProperties message handlers
|
||||
|
||||
BOOL ScriptProperties::OnSetActive()
|
||||
{
|
||||
if ( CPropertyPage::OnSetActive()) {
|
||||
CWnd *pWnd = GetDlgItem(IDC_SCRIPT_COMMENT);
|
||||
pWnd->SetWindowText(m_script->getComment().str());
|
||||
|
||||
pWnd = GetDlgItem(IDC_SCRIPT_NAME);
|
||||
pWnd->SetWindowText(m_script->getName().str());
|
||||
enableControls();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptProperties::enableControls()
|
||||
{
|
||||
Bool isSubroutine = m_script->isSubroutine();
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_SCRIPT_SUBROUTINE);
|
||||
pButton->SetCheck(isSubroutine ? 1:0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_SCRIPT_ACTIVE);
|
||||
pButton->SetCheck(m_script->isActive() ? 1:0);
|
||||
//pButton->EnableWindow(!isSubroutine);
|
||||
pButton = (CButton*)GetDlgItem(IDC_ONE_SHOT);
|
||||
pButton->SetCheck(m_script->isOneShot() ? 1:0);
|
||||
//pButton->EnableWindow(!isSubroutine);
|
||||
pButton = (CButton*)GetDlgItem(IDC_EASY);
|
||||
pButton->SetCheck(m_script->isEasy() ? 1:0);
|
||||
//pButton->EnableWindow(!isSubroutine);
|
||||
pButton = (CButton*)GetDlgItem(IDC_NORMAL);
|
||||
pButton->SetCheck(m_script->isNormal() ? 1:0);
|
||||
//pButton->EnableWindow(!isSubroutine);
|
||||
pButton = (CButton*)GetDlgItem(IDC_HARD);
|
||||
pButton->SetCheck(m_script->isHard() ? 1:0);
|
||||
//pButton->EnableWindow(!isSubroutine);
|
||||
|
||||
m_updating = true;
|
||||
Int delay = m_script->getDelayEvalSeconds();
|
||||
pButton = (CButton*)GetDlgItem(IDC_EVERY_SECOND);
|
||||
pButton->SetCheck(delay>0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_EVERY_FRAME);
|
||||
pButton->SetCheck(delay==0);
|
||||
CString text="";
|
||||
if (delay>0) text.Format("%d", delay);
|
||||
GetDlgItem(IDC_SECONDS_EDIT)->SetWindowText(text);
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
void ScriptProperties::OnChangeScriptComment()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_SCRIPT_COMMENT);
|
||||
CString comment;
|
||||
pWnd->GetWindowText(comment);
|
||||
m_script->setComment(AsciiString((LPCTSTR)comment));
|
||||
}
|
||||
|
||||
void ScriptProperties::OnChangeScriptName()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_SCRIPT_NAME);
|
||||
CString name;
|
||||
pWnd->GetWindowText(name);
|
||||
m_script->setName(AsciiString((LPCTSTR)name));
|
||||
GetParent()->SetWindowText(name);
|
||||
}
|
||||
|
||||
void ScriptProperties::OnScriptActive()
|
||||
{
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_SCRIPT_ACTIVE);
|
||||
m_script->setActive(pButton->GetCheck()==1);
|
||||
}
|
||||
|
||||
void ScriptProperties::OnOneShot()
|
||||
{
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_ONE_SHOT);
|
||||
m_script->setOneShot(pButton->GetCheck()==1);
|
||||
}
|
||||
|
||||
void ScriptProperties::OnEasy()
|
||||
{
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_EASY);
|
||||
m_script->setEasy(pButton->GetCheck()==1);
|
||||
}
|
||||
|
||||
void ScriptProperties::OnHard()
|
||||
{
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_HARD);
|
||||
m_script->setHard(pButton->GetCheck()==1);
|
||||
}
|
||||
|
||||
void ScriptProperties::OnNormal()
|
||||
{
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_NORMAL);
|
||||
m_script->setNormal(pButton->GetCheck()==1);
|
||||
}
|
||||
|
||||
void ScriptProperties::OnScriptSubroutine()
|
||||
{
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_SCRIPT_SUBROUTINE);
|
||||
Bool isSubroutine = pButton->GetCheck()==1;
|
||||
m_script->setSubroutine(isSubroutine);
|
||||
|
||||
enableControls();
|
||||
|
||||
}
|
||||
|
||||
void ScriptProperties::OnEveryFrame()
|
||||
{
|
||||
m_updating = true;
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_EVERY_SECOND);
|
||||
pButton->SetCheck(0);
|
||||
pButton = (CButton*)GetDlgItem(IDC_EVERY_FRAME);
|
||||
pButton->SetCheck(1);
|
||||
GetDlgItem(IDC_SECONDS_EDIT)->SetWindowText("");
|
||||
m_script->setDelayEvalSeconds(0);
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
void ScriptProperties::OnEverySecond()
|
||||
{
|
||||
m_updating = true;
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_EVERY_SECOND);
|
||||
pButton->SetCheck(1);
|
||||
pButton = (CButton*)GetDlgItem(IDC_EVERY_FRAME);
|
||||
pButton->SetCheck(0);
|
||||
GetDlgItem(IDC_SECONDS_EDIT)->SetWindowText("1");
|
||||
m_script->setDelayEvalSeconds(1);
|
||||
m_updating = false;
|
||||
}
|
||||
|
||||
void ScriptProperties::OnChangeSecondsEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_EVERY_SECOND);
|
||||
pButton->SetCheck(1);
|
||||
pButton = (CButton*)GetDlgItem(IDC_EVERY_FRAME);
|
||||
pButton->SetCheck(0);
|
||||
CWnd *pEdit = GetDlgItem(IDC_SECONDS_EDIT);
|
||||
CString text;
|
||||
pEdit->GetWindowText(text);
|
||||
Int theInt;
|
||||
if (1==sscanf(text, "%d", &theInt)) {
|
||||
m_script->setDelayEvalSeconds(theInt);
|
||||
}
|
||||
}
|
||||
152
GeneralsMD/Code/Tools/WorldBuilder/src/SelectMacrotexture.cpp
Normal file
152
GeneralsMD/Code/Tools/WorldBuilder/src/SelectMacrotexture.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// SelectMacrotexture.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "SelectMacrotexture.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "common/GlobalData.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// SelectMacrotexture dialog
|
||||
|
||||
|
||||
SelectMacrotexture::SelectMacrotexture(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(SelectMacrotexture::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(SelectMacrotexture)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void SelectMacrotexture::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(SelectMacrotexture)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(SelectMacrotexture, CDialog)
|
||||
//{{AFX_MSG_MAP(SelectMacrotexture)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
#define DEFAULT "***Default"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// SelectMacrotexture message handlers
|
||||
|
||||
BOOL SelectMacrotexture::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_TEXTURE_TREEVIEW);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_textureTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_TERRAIN_TREEVIEW);
|
||||
m_textureTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
{
|
||||
char dirBuf[_MAX_PATH];
|
||||
char findBuf[_MAX_PATH];
|
||||
char fileBuf[_MAX_PATH];
|
||||
|
||||
strcpy(dirBuf, "..\\TestArt");
|
||||
int len = strlen(dirBuf);
|
||||
|
||||
if (len > 0 && dirBuf[len - 1] != '\\') {
|
||||
dirBuf[len++] = '\\';
|
||||
dirBuf[len] = 0;
|
||||
}
|
||||
strcpy(findBuf, dirBuf);
|
||||
|
||||
FilenameList filenameList;
|
||||
TheFileSystem->getFileListInDirectory(AsciiString(findBuf), AsciiString("*.tga"), filenameList, FALSE);
|
||||
|
||||
if (filenameList.size() > 0) {
|
||||
TVINSERTSTRUCT ins;
|
||||
HTREEITEM child = NULL;
|
||||
FilenameList::iterator it = filenameList.begin();
|
||||
do {
|
||||
AsciiString filename = *it;
|
||||
len = filename.getLength();
|
||||
if (len<5) continue;
|
||||
strcpy(fileBuf, filename.str());
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = TVI_ROOT;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = fileBuf;
|
||||
ins.item.cchTextMax = strlen(fileBuf);
|
||||
child = m_textureTreeView.InsertItem(&ins);
|
||||
++it;
|
||||
} while (it != filenameList.end());
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = TVI_ROOT;
|
||||
ins.hInsertAfter = TVI_SORT;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = DEFAULT;
|
||||
ins.item.cchTextMax = strlen(DEFAULT);
|
||||
child = m_textureTreeView.InsertItem(&ins);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
BOOL SelectMacrotexture::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
if (pHdr->hdr.hwndFrom == m_textureTreeView.m_hWnd) {
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
char buffer[_MAX_PATH];
|
||||
HTREEITEM hItem = m_textureTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_STATE;
|
||||
item.hItem = hItem;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_textureTreeView.GetItem(&item);
|
||||
if (0==strcmp(buffer, DEFAULT)) {
|
||||
TheTerrainRenderObject->updateMacroTexture(AsciiString(""));
|
||||
} else {
|
||||
TheTerrainRenderObject->updateMacroTexture(AsciiString(buffer));
|
||||
}
|
||||
TheWritableGlobalData->m_useLightMap = true;
|
||||
}
|
||||
}
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
193
GeneralsMD/Code/Tools/WorldBuilder/src/ShadowOptions.cpp
Normal file
193
GeneralsMD/Code/Tools/WorldBuilder/src/ShadowOptions.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ShadowOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "rendobj.h"
|
||||
#include "common/GlobalData.h"
|
||||
#include "ShadowOptions.h"
|
||||
#include "W3DDevice/GameClient/W3DShadow.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ShadowOptions dialog
|
||||
|
||||
|
||||
ShadowOptions::ShadowOptions(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(ShadowOptions::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(ShadowOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void ShadowOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(ShadowOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(ShadowOptions, CDialog)
|
||||
//{{AFX_MSG_MAP(ShadowOptions)
|
||||
ON_EN_CHANGE(IDC_ALPHA_EDIT, OnChangeAlphaEdit)
|
||||
ON_EN_CHANGE(IDC_BA_EDIT, OnChangeBaEdit)
|
||||
ON_EN_CHANGE(IDC_GA_EDIT, OnChangeGaEdit)
|
||||
ON_EN_CHANGE(IDC_RA_EDIT, OnChangeRaEdit)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// ShadowOptions message handlers
|
||||
|
||||
void ShadowOptions::setShadowColor(void)
|
||||
{
|
||||
Int r, g, b, shift;
|
||||
|
||||
shift = (1.0-m_intensity)*255;
|
||||
if (shift>255) shift = 255;
|
||||
if (shift<0) shift = 0;
|
||||
|
||||
r = m_intensity*m_red*255 + shift;
|
||||
if (r>255) r = 255;
|
||||
if (r<0) r = 0;
|
||||
|
||||
g = m_intensity*m_green*255 + shift;
|
||||
if (g>255) g = 255;
|
||||
if (g<0) g = 0;
|
||||
|
||||
b = m_intensity*m_blue*255 + shift;
|
||||
if (b>255) b = 255;
|
||||
if (b<0) b = 0;
|
||||
|
||||
UnsignedInt clr = (255<<24) + (r<<16) + (g<<8) + b;
|
||||
DEBUG_LOG(("Setting shadows to %x\n", clr));
|
||||
TheW3DShadowManager->setShadowColor(clr);
|
||||
}
|
||||
|
||||
void ShadowOptions::OnChangeAlphaEdit()
|
||||
{
|
||||
CWnd *pEdit = GetDlgItem(IDC_ALPHA_EDIT);
|
||||
Real clr;
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
if (1==sscanf(buffer, "%f", &clr)) {
|
||||
m_intensity = clr;
|
||||
setShadowColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowOptions::OnChangeBaEdit()
|
||||
{
|
||||
CWnd *pEdit = GetDlgItem(IDC_BA_EDIT);
|
||||
Real clr;
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
if (1==sscanf(buffer, "%f", &clr)) {
|
||||
m_blue = clr;
|
||||
setShadowColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowOptions::OnChangeGaEdit()
|
||||
{
|
||||
CWnd *pEdit = GetDlgItem(IDC_GA_EDIT);
|
||||
Real clr;
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
if (1==sscanf(buffer, "%f", &clr)) {
|
||||
m_green = clr;
|
||||
setShadowColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowOptions::OnChangeRaEdit()
|
||||
{
|
||||
CWnd *pEdit = GetDlgItem(IDC_RA_EDIT);
|
||||
Real clr;
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
if (1==sscanf(buffer, "%f", &clr)) {
|
||||
m_red = clr;
|
||||
setShadowColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL ShadowOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
UnsignedInt clr = TheW3DShadowManager->getShadowColor();
|
||||
m_red = ((clr>>16)&0x00FF)/255.0f;
|
||||
m_green = ((clr>>8)&0x00FF)/255.0f;
|
||||
m_blue = ((clr)&0x00FF)/255.0f;
|
||||
|
||||
m_intensity = m_red;
|
||||
if (m_green<m_red) m_intensity = m_green;
|
||||
if (m_blue < m_intensity) m_intensity = m_blue;
|
||||
m_intensity = 1.0f - m_intensity;
|
||||
if (m_intensity < (1/256.0f)) {
|
||||
m_intensity = 0;
|
||||
m_red = m_green = m_blue = 0;
|
||||
} else {
|
||||
m_red -= (1.0-m_intensity);
|
||||
m_red /= m_intensity;
|
||||
m_green -= (1.0-m_intensity);
|
||||
m_green /= m_intensity;
|
||||
m_blue -= (1.0-m_intensity);
|
||||
m_blue /= m_intensity;
|
||||
}
|
||||
|
||||
CWnd *pEdit = GetDlgItem(IDC_RA_EDIT);
|
||||
CString text;
|
||||
if (pEdit) {
|
||||
text.Format("%.2f", m_red);
|
||||
pEdit->SetWindowText(text);
|
||||
}
|
||||
pEdit = GetDlgItem(IDC_BA_EDIT);
|
||||
if (pEdit) {
|
||||
text.Format("%.2f", m_blue);
|
||||
pEdit->SetWindowText(text);
|
||||
}
|
||||
pEdit = GetDlgItem(IDC_GA_EDIT);
|
||||
if (pEdit) {
|
||||
text.Format("%.2f", m_green);
|
||||
pEdit->SetWindowText(text);
|
||||
}
|
||||
pEdit = GetDlgItem(IDC_ALPHA_EDIT);
|
||||
if (pEdit) {
|
||||
text.Format("%.2f", m_intensity);
|
||||
pEdit->SetWindowText(text);
|
||||
}
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
94
GeneralsMD/Code/Tools/WorldBuilder/src/SplashScreen.cpp
Normal file
94
GeneralsMD/Code/Tools/WorldBuilder/src/SplashScreen.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "SplashScreen.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SplashScreen::SplashScreen()
|
||||
{
|
||||
m_rect.left = 0;
|
||||
m_rect.right = 0;
|
||||
m_rect.top = 0;
|
||||
m_rect.bottom = 0;
|
||||
|
||||
m_loadString = "Cock & Beer";
|
||||
|
||||
|
||||
LOGFONT lf;
|
||||
lf.lfHeight = 12;
|
||||
lf.lfWidth = 0;
|
||||
lf.lfEscapement = 0;
|
||||
lf.lfOrientation = 0;
|
||||
lf.lfWeight = FW_NORMAL;
|
||||
lf.lfItalic = FALSE;
|
||||
lf.lfUnderline = FALSE;
|
||||
lf.lfStrikeOut = FALSE;
|
||||
lf.lfCharSet = ANSI_CHARSET;
|
||||
lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
||||
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
lf.lfQuality = DEFAULT_QUALITY;
|
||||
lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
|
||||
strcpy(lf.lfFaceName, "Arial");
|
||||
|
||||
m_font.CreateFontIndirect(&lf);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SplashScreen::setTextOutputLocation(const CRect& rect)
|
||||
{
|
||||
m_rect = rect;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SplashScreen::outputText(UINT nIDString)
|
||||
{
|
||||
CString str;
|
||||
if (!str.LoadString(nIDString)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_loadString = str;
|
||||
|
||||
RedrawWindow(&m_rect, NULL);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SplashScreen::OnPaint()
|
||||
{
|
||||
// we're extending the default behavior
|
||||
CDialog::OnPaint();
|
||||
|
||||
|
||||
CDC *dc = GetDC();
|
||||
|
||||
// Save off the old font
|
||||
CFont *oldFont = dc->SelectObject(&m_font);
|
||||
COLORREF oldRef = dc->SetTextColor(0x00000000);
|
||||
|
||||
// dc->DrawText(m_loadString, m_rect, DT_VCENTER | DT_LEFT);
|
||||
|
||||
// restore the old font
|
||||
dc->SelectObject(oldFont);
|
||||
dc->SetTextColor(oldRef);
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(SplashScreen, CDialog)
|
||||
ON_WM_PAINT()
|
||||
END_MESSAGE_MAP()
|
||||
26
GeneralsMD/Code/Tools/WorldBuilder/src/StdAfx.cpp
Normal file
26
GeneralsMD/Code/Tools/WorldBuilder/src/StdAfx.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// WorldBuilder.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
|
||||
|
||||
269
GeneralsMD/Code/Tools/WorldBuilder/src/TeamBehavior.cpp
Normal file
269
GeneralsMD/Code/Tools/WorldBuilder/src/TeamBehavior.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TeamBehavior.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "TeamBehavior.h"
|
||||
#include "EditParameter.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "GameLogic/AI.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TeamBehavior dialog
|
||||
|
||||
TeamBehavior::TeamBehavior()
|
||||
: CPropertyPage(TeamBehavior::IDD)
|
||||
{
|
||||
//{{AFX_DATA_INIT(TeamBehavior)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void TeamBehavior::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CPropertyPage::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(TeamBehavior)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(TeamBehavior, CPropertyPage)
|
||||
//{{AFX_MSG_MAP(TeamBehavior)
|
||||
ON_CBN_SELCHANGE(IDC_ON_CREATE_SCRIPT, OnSelchangeOnCreateScript)
|
||||
ON_BN_CLICKED(IDC_TRANSPORTS_RETURN, OnTransportsReturn)
|
||||
ON_BN_CLICKED(IDC_AVOID_THREATS, OnAvoidThreats)
|
||||
ON_CBN_SELCHANGE(IDC_ON_ENEMY_SIGHTED, OnSelchangeOnEnemySighted)
|
||||
ON_CBN_SELCHANGE(IDC_ON_DESTROYED, OnSelchangeOnDestroyed)
|
||||
ON_CBN_SELCHANGE(IDC_ON_UNIT_DESTROYED_SCRIPT, OnSelchangeOnUnitDestroyed)
|
||||
ON_BN_CLICKED(IDC_PERIMETER_DEFENSE, OnPerimeterDefense)
|
||||
ON_BN_CLICKED(IDC_BASE_DEFENSE, OnBaseDefense)
|
||||
ON_EN_CHANGE(IDC_PERCENT_DESTROYED, OnChangePercentDestroyed)
|
||||
ON_CBN_SELCHANGE(IDC_ENEMY_INTERACTIONS, OnSelchangeEnemyInteractions)
|
||||
ON_CBN_SELCHANGE(IDC_ON_ALL_CLEAR, OnSelchangeOnAllClear)
|
||||
ON_CBN_SELCHANGE(IDC_ON_IDLE_SCRIPT, OnSelchangeOnIdleScript)
|
||||
ON_BN_CLICKED(IDC_ATTACK_COMMON_TARGET, OnAttackCommonTarget)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TeamBehavior message handlers
|
||||
|
||||
void TeamBehavior::updateScript(NameKeyType keyScript, int idcScript)
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(idcScript);
|
||||
CString txt;
|
||||
Int curSel = pCombo->GetCurSel();
|
||||
pCombo->GetLBText(curSel, txt);
|
||||
AsciiString comboText = AsciiString(txt);
|
||||
if (comboText == NONE_STRING) {
|
||||
comboText.clear();
|
||||
}
|
||||
m_teamDict->setAsciiString(keyScript, comboText);
|
||||
}
|
||||
|
||||
void TeamBehavior::setupScript(NameKeyType keyScript, int idcScript)
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(idcScript);
|
||||
// Load the subroutine scripts into the combo box.
|
||||
EditParameter::loadScripts(pCombo, true);
|
||||
Int stringNdx = pCombo->AddString(NONE_STRING);
|
||||
pCombo->SetFocus();
|
||||
Bool exists;
|
||||
AsciiString script = m_teamDict->getAsciiString(keyScript, &exists);
|
||||
if (exists && !script.isEmpty()) {
|
||||
Int ndx = pCombo->FindStringExact(-1, script.str());
|
||||
if (ndx != CB_ERR) {
|
||||
stringNdx = ndx;
|
||||
} else {
|
||||
AsciiString badName = "*";
|
||||
badName.concat(script);
|
||||
stringNdx = pCombo->AddString(badName.str());
|
||||
}
|
||||
}
|
||||
pCombo->SetCurSel(stringNdx);
|
||||
}
|
||||
|
||||
|
||||
BOOL TeamBehavior::OnInitDialog()
|
||||
{
|
||||
CPropertyPage::OnInitDialog();
|
||||
|
||||
setupScript(TheKey_teamOnCreateScript, IDC_ON_CREATE_SCRIPT);
|
||||
setupScript(TheKey_teamOnIdleScript, IDC_ON_IDLE_SCRIPT);
|
||||
setupScript(TheKey_teamEnemySightedScript, IDC_ON_ENEMY_SIGHTED);
|
||||
setupScript(TheKey_teamOnDestroyedScript, IDC_ON_DESTROYED);
|
||||
setupScript(TheKey_teamAllClearScript, IDC_ON_ALL_CLEAR);
|
||||
setupScript(TheKey_teamOnUnitDestroyedScript, IDC_ON_UNIT_DESTROYED_SCRIPT);
|
||||
|
||||
Bool exists;
|
||||
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_TRANSPORTS_RETURN);
|
||||
Bool transportsReturn = m_teamDict->getBool(TheKey_teamTransportsReturn, &exists);
|
||||
pCheck->SetCheck(transportsReturn?1:0);
|
||||
|
||||
pCheck = (CButton *) GetDlgItem(IDC_BASE_DEFENSE);
|
||||
if( pCheck )
|
||||
{
|
||||
Bool baseDef = m_teamDict->getBool(TheKey_teamIsBaseDefense, &exists);
|
||||
pCheck->SetCheck(baseDef?1:0);
|
||||
}
|
||||
|
||||
pCheck = (CButton *) GetDlgItem(IDC_PERIMETER_DEFENSE);
|
||||
if( pCheck )
|
||||
{
|
||||
Bool perimeter = m_teamDict->getBool(TheKey_teamIsPerimeterDefense, &exists);
|
||||
pCheck->SetCheck(perimeter?1:0);
|
||||
}
|
||||
|
||||
pCheck = (CButton *) GetDlgItem(IDC_AVOID_THREATS);
|
||||
Bool avoid = m_teamDict->getBool(TheKey_teamAvoidThreats, &exists);
|
||||
pCheck->SetCheck(avoid?1:0);
|
||||
|
||||
pCheck = (CButton *) GetDlgItem(IDC_ATTACK_COMMON_TARGET);
|
||||
Bool attack = m_teamDict->getBool(TheKey_teamAttackCommonTarget, &exists);
|
||||
pCheck->SetCheck(attack?1:0);
|
||||
|
||||
AsciiString description;
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_PERCENT_DESTROYED);
|
||||
Real threshold = m_teamDict->getReal(TheKey_teamDestroyedThreshold, &exists);
|
||||
if (!exists) threshold = 0.5f;
|
||||
Int percent = floor((threshold*100)+0.5);
|
||||
description.format("%d", percent);
|
||||
pWnd->SetWindowText(description.str());
|
||||
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_ENEMY_INTERACTIONS);
|
||||
pCombo->SetCurSel(m_teamDict->getInt(TheKey_teamAggressiveness, &exists) - AI_SLEEP);
|
||||
|
||||
|
||||
return FALSE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void TeamBehavior::OnTransportsReturn()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_TRANSPORTS_RETURN);
|
||||
Bool checked = pCheck->GetCheck()==1;
|
||||
m_teamDict->setBool(TheKey_teamTransportsReturn, checked);
|
||||
}
|
||||
|
||||
void TeamBehavior::OnAvoidThreats()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_AVOID_THREATS);
|
||||
Bool checked = pCheck->GetCheck()==1;
|
||||
m_teamDict->setBool(TheKey_teamAvoidThreats, checked);
|
||||
}
|
||||
|
||||
void TeamBehavior::OnSelchangeOnEnemySighted()
|
||||
{
|
||||
updateScript(TheKey_teamEnemySightedScript, IDC_ON_ENEMY_SIGHTED);
|
||||
}
|
||||
|
||||
void TeamBehavior::OnSelchangeOnDestroyed()
|
||||
{
|
||||
updateScript(TheKey_teamOnDestroyedScript, IDC_ON_DESTROYED);
|
||||
}
|
||||
|
||||
void TeamBehavior::OnSelchangeOnUnitDestroyed()
|
||||
{
|
||||
updateScript(TheKey_teamOnUnitDestroyedScript, IDC_ON_UNIT_DESTROYED_SCRIPT);
|
||||
}
|
||||
|
||||
void TeamBehavior::OnSelchangeOnCreateScript()
|
||||
{
|
||||
updateScript(TheKey_teamOnCreateScript, IDC_ON_CREATE_SCRIPT);
|
||||
}
|
||||
|
||||
void TeamBehavior::OnPerimeterDefense()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_PERIMETER_DEFENSE);
|
||||
if( pCheck )
|
||||
{
|
||||
Bool checked = pCheck->GetCheck()==1;
|
||||
m_teamDict->setBool(TheKey_teamIsPerimeterDefense, checked);
|
||||
if (checked) { // Can't be both base & perimeter defense.
|
||||
pCheck = (CButton *) GetDlgItem(IDC_BASE_DEFENSE);
|
||||
if( pCheck )
|
||||
{
|
||||
pCheck->SetCheck(0);
|
||||
m_teamDict->setBool(TheKey_teamIsBaseDefense, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamBehavior::OnBaseDefense()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_BASE_DEFENSE);
|
||||
if( pCheck )
|
||||
{
|
||||
Bool checked = pCheck->GetCheck()==1;
|
||||
m_teamDict->setBool(TheKey_teamIsBaseDefense, checked);
|
||||
if (checked) { // Can't be both base & perimeter defense.
|
||||
pCheck = (CButton *) GetDlgItem(IDC_PERIMETER_DEFENSE);
|
||||
if( pCheck )
|
||||
{
|
||||
pCheck->SetCheck(0);
|
||||
m_teamDict->setBool(TheKey_teamIsPerimeterDefense, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamBehavior::OnChangePercentDestroyed()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_PERCENT_DESTROYED);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
Int percent = atoi(val);
|
||||
Real value = percent/100.0f;
|
||||
m_teamDict->setReal(TheKey_teamDestroyedThreshold, value);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamBehavior::OnSelchangeEnemyInteractions()
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_ENEMY_INTERACTIONS);
|
||||
Int mode = pCombo->GetCurSel();
|
||||
if (mode >= 0) {
|
||||
m_teamDict->setInt(TheKey_teamAggressiveness, mode + AI_SLEEP);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamBehavior::OnSelchangeOnAllClear()
|
||||
{
|
||||
updateScript(TheKey_teamAllClearScript, IDC_ON_ALL_CLEAR);
|
||||
}
|
||||
|
||||
void TeamBehavior::OnSelchangeOnIdleScript()
|
||||
{
|
||||
updateScript(TheKey_teamOnIdleScript, IDC_ON_IDLE_SCRIPT);
|
||||
}
|
||||
|
||||
void TeamBehavior::OnAttackCommonTarget()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_ATTACK_COMMON_TARGET);
|
||||
Bool checked = pCheck->GetCheck()==1;
|
||||
m_teamDict->setBool(TheKey_teamAttackCommonTarget, checked);
|
||||
}
|
||||
241
GeneralsMD/Code/Tools/WorldBuilder/src/TeamGeneric.cpp
Normal file
241
GeneralsMD/Code/Tools/WorldBuilder/src/TeamGeneric.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "Resource.h"
|
||||
|
||||
#include "TeamGeneric.h"
|
||||
#include "EditParameter.h"
|
||||
#include "WorldBuilder.h"
|
||||
|
||||
#include "Common/AsciiString.h"
|
||||
#include "Common/Dict.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
|
||||
static const UINT s_allControls[][2] =
|
||||
{
|
||||
{ IDC_SCRIPT_PREFIX1, IDC_TeamGeneric_Script1, },
|
||||
{ IDC_SCRIPT_PREFIX2, IDC_TeamGeneric_Script2, },
|
||||
{ IDC_SCRIPT_PREFIX3, IDC_TeamGeneric_Script3, },
|
||||
{ IDC_SCRIPT_PREFIX4, IDC_TeamGeneric_Script4, },
|
||||
{ IDC_SCRIPT_PREFIX5, IDC_TeamGeneric_Script5, },
|
||||
{ IDC_SCRIPT_PREFIX6, IDC_TeamGeneric_Script6, },
|
||||
{ IDC_SCRIPT_PREFIX7, IDC_TeamGeneric_Script7, },
|
||||
{ IDC_SCRIPT_PREFIX8, IDC_TeamGeneric_Script8, },
|
||||
{ IDC_SCRIPT_PREFIX9, IDC_TeamGeneric_Script9, },
|
||||
{ IDC_SCRIPT_PREFIX10, IDC_TeamGeneric_Script10, },
|
||||
{ IDC_SCRIPT_PREFIX11, IDC_TeamGeneric_Script11, },
|
||||
{ IDC_SCRIPT_PREFIX12, IDC_TeamGeneric_Script12, },
|
||||
{ IDC_SCRIPT_PREFIX13, IDC_TeamGeneric_Script13, },
|
||||
{ IDC_SCRIPT_PREFIX14, IDC_TeamGeneric_Script14, },
|
||||
{ IDC_SCRIPT_PREFIX15, IDC_TeamGeneric_Script15, },
|
||||
{ IDC_SCRIPT_PREFIX16, IDC_TeamGeneric_Script16, },
|
||||
{ 0,0, },
|
||||
};
|
||||
|
||||
TeamGeneric::TeamGeneric() : CPropertyPage(TeamGeneric::IDD)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BOOL TeamGeneric::OnInitDialog()
|
||||
{
|
||||
CPropertyPage::OnInitDialog();
|
||||
// Fill all the combo boxes with the scripts we have.
|
||||
_fillComboBoxesWithScripts();
|
||||
|
||||
// Set up the dialog as appropriate.
|
||||
_dictToScripts();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void TeamGeneric::_fillComboBoxesWithScripts()
|
||||
{
|
||||
int i = 0;
|
||||
while (s_allControls[i][1]) {
|
||||
CComboBox *pCombo = (CComboBox*) GetDlgItem(s_allControls[i][1]);
|
||||
if (!pCombo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load all the scripts, then add the NONE string.
|
||||
EditParameter::loadScripts(pCombo, true);
|
||||
pCombo->InsertString(0, NONE_STRING);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void TeamGeneric::_dictToScripts()
|
||||
{
|
||||
CWnd *pText = NULL;
|
||||
CComboBox *pCombo = NULL;
|
||||
|
||||
if (!m_teamDict) {
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
while (s_allControls[i][1]) {
|
||||
|
||||
pText = GetDlgItem(s_allControls[i][0]);
|
||||
if (!pText) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pCombo = (CComboBox*) GetDlgItem(s_allControls[i][1]);
|
||||
if (!pCombo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Bool exists;
|
||||
AsciiString scriptString;
|
||||
AsciiString keyName;
|
||||
keyName.format("%s%d", TheNameKeyGenerator->keyToName(TheKey_teamGenericScriptHook).str(), i);
|
||||
scriptString = m_teamDict->getAsciiString(NAMEKEY(keyName), &exists);
|
||||
|
||||
pText->ShowWindow(SW_SHOW);
|
||||
pCombo->ShowWindow(SW_SHOW);
|
||||
|
||||
if (exists) {
|
||||
Int selNdx = pCombo->FindStringExact(-1, scriptString.str());
|
||||
if (selNdx == LB_ERR) {
|
||||
pCombo->SetCurSel(0);
|
||||
} else {
|
||||
pCombo->SetCurSel(selNdx);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (!s_allControls[i][1]) {
|
||||
// We filled everything.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pCombo) {
|
||||
// We filled nothing, or there was an error.
|
||||
return;
|
||||
}
|
||||
|
||||
pCombo->SetCurSel(0);
|
||||
|
||||
++i;
|
||||
while (s_allControls[i][1]) {
|
||||
pText = GetDlgItem(s_allControls[i][0]);
|
||||
if (!pText) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pCombo = (CComboBox*) GetDlgItem(s_allControls[i][1]);
|
||||
if (!pCombo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pText->ShowWindow(SW_HIDE);
|
||||
pCombo->ShowWindow(SW_HIDE);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void TeamGeneric::_scriptsToDict()
|
||||
{
|
||||
if (!m_teamDict) {
|
||||
return;
|
||||
}
|
||||
|
||||
CWnd *pText = NULL;
|
||||
CComboBox *pCombo = NULL;
|
||||
|
||||
int scriptNum = 0;
|
||||
|
||||
int i = 0;
|
||||
while (s_allControls[i][1]) {
|
||||
|
||||
pText = GetDlgItem(s_allControls[i][0]);
|
||||
if (!pText) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pCombo = (CComboBox*) GetDlgItem(s_allControls[i][1]);
|
||||
if (!pCombo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// i should always be incremented, so just do it here.
|
||||
++i;
|
||||
|
||||
AsciiString keyName;
|
||||
keyName.format("%s%d", TheNameKeyGenerator->keyToName(TheKey_teamGenericScriptHook).str(), scriptNum);
|
||||
|
||||
int curSel = pCombo->GetCurSel();
|
||||
if (curSel == CB_ERR || curSel == 0) {
|
||||
if (m_teamDict->known(NAMEKEY(keyName), Dict::DICT_ASCIISTRING)) {
|
||||
// remove it if we know it.
|
||||
m_teamDict->remove(NAMEKEY(keyName));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
CString cstr;
|
||||
pCombo->GetLBText(curSel, cstr);
|
||||
|
||||
AsciiString scriptString = cstr;
|
||||
m_teamDict->setAsciiString(NAMEKEY(keyName), scriptString);
|
||||
++scriptNum;
|
||||
}
|
||||
|
||||
for ( ; s_allControls[scriptNum][1]; ++scriptNum ) {
|
||||
AsciiString keyName;
|
||||
keyName.format("%s%d", TheNameKeyGenerator->keyToName(TheKey_teamGenericScriptHook).str(), scriptNum);
|
||||
|
||||
if (m_teamDict->known(NAMEKEY(keyName), Dict::DICT_ASCIISTRING)) {
|
||||
m_teamDict->remove(NAMEKEY(keyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamGeneric::OnScriptAdjust()
|
||||
{
|
||||
_scriptsToDict();
|
||||
_dictToScripts();
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(TeamGeneric, CPropertyPage)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script1, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script2, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script3, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script4, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script5, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script6, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script7, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script8, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script9, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script10, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script11, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script12, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script13, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script14, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script15, OnScriptAdjust)
|
||||
ON_CBN_SELCHANGE(IDC_TeamGeneric_Script16, OnScriptAdjust)
|
||||
END_MESSAGE_MAP()
|
||||
590
GeneralsMD/Code/Tools/WorldBuilder/src/TeamIdentity.cpp
Normal file
590
GeneralsMD/Code/Tools/WorldBuilder/src/TeamIdentity.cpp
Normal file
@@ -0,0 +1,590 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TeamIdentity.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "TeamIdentity.h"
|
||||
#include "EditParameter.h"
|
||||
#include "PickUnitDialog.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingSort.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
|
||||
static const char* NEUTRAL_NAME_STR = "(neutral)";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TeamIdentity dialog
|
||||
|
||||
|
||||
TeamIdentity::TeamIdentity()
|
||||
: CPropertyPage(TeamIdentity::IDD)
|
||||
{
|
||||
//{{AFX_DATA_INIT(TeamIdentity)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void TeamIdentity::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CPropertyPage::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(TeamIdentity)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(TeamIdentity, CPropertyPage)
|
||||
//{{AFX_MSG_MAP(TeamIdentity)
|
||||
ON_BN_CLICKED(IDC_AI_RECRUITABLE, OnAiRecruitable)
|
||||
ON_BN_CLICKED(IDC_AUTO_REINFORCE, OnAutoReinforce)
|
||||
ON_EN_CHANGE(IDC_DESCRIPTION, OnChangeDescription)
|
||||
ON_EN_CHANGE(IDC_MAX, OnChangeMax)
|
||||
ON_EN_CHANGE(IDC_PRIORITY_DECREASE, OnChangePriorityDecrease)
|
||||
ON_EN_CHANGE(IDC_PRIORITY_INCREASE, OnChangePriorityIncrease)
|
||||
ON_CBN_SELCHANGE(IDC_PRODUCTION_CONDITION, OnSelchangeProductionCondition)
|
||||
ON_EN_CHANGE(IDC_PRODUCTION_PRIORITY, OnChangeProductionPriority)
|
||||
ON_CBN_SELCHANGE(IDC_HOME_WAYPOINT, OnSelchangeHomeWaypoint)
|
||||
ON_BN_CLICKED(IDC_UNIT_TYPE1_BUTTON, OnUnitType1Button)
|
||||
ON_BN_CLICKED(IDC_UNIT_TYPE2_BUTTON, OnUnitType2Button)
|
||||
ON_BN_CLICKED(IDC_UNIT_TYPE3_BUTTON, OnUnitType3Button)
|
||||
ON_BN_CLICKED(IDC_UNIT_TYPE4_BUTTON, OnUnitType4Button)
|
||||
ON_BN_CLICKED(IDC_UNIT_TYPE5_BUTTON, OnUnitType5Button)
|
||||
ON_BN_CLICKED(IDC_UNIT_TYPE6_BUTTON, OnUnitType6Button)
|
||||
ON_BN_CLICKED(IDC_UNIT_TYPE7_BUTTON, OnUnitType7Button)
|
||||
ON_BN_CLICKED(IDC_PRODUCTION_EXECUTEACTIONS, OnExecuteActions)
|
||||
ON_EN_CHANGE(IDC_TEAM_NAME, OnChangeTeamName)
|
||||
ON_BN_CLICKED(IDC_TEAM_SINGLETON, OnTeamSingleton)
|
||||
ON_EN_KILLFOCUS(IDC_TEAM_NAME, OnKillfocusTeamName)
|
||||
ON_CBN_SELENDOK(IDC_TEAMOWNER, OnSelendokTeamowner)
|
||||
ON_EN_CHANGE(IDC_TEAM_BUILD_FRAMES, OnChangeTeamBuildFrames)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TeamIdentity message handlers
|
||||
|
||||
BOOL TeamIdentity::OnInitDialog()
|
||||
{
|
||||
CPropertyPage::OnInitDialog();
|
||||
|
||||
loadUnitsInfo(IDC_MIN_UNIT1, TheKey_teamUnitMinCount1,
|
||||
IDC_MAX_UNIT1, TheKey_teamUnitMaxCount1,
|
||||
IDC_UNIT_TYPE1, TheKey_teamUnitType1);
|
||||
|
||||
loadUnitsInfo(IDC_MIN_UNIT2, TheKey_teamUnitMinCount2,
|
||||
IDC_MAX_UNIT2, TheKey_teamUnitMaxCount2,
|
||||
IDC_UNIT_TYPE2, TheKey_teamUnitType2);
|
||||
|
||||
loadUnitsInfo(IDC_MIN_UNIT3, TheKey_teamUnitMinCount3,
|
||||
IDC_MAX_UNIT3, TheKey_teamUnitMaxCount3,
|
||||
IDC_UNIT_TYPE3, TheKey_teamUnitType3);
|
||||
|
||||
loadUnitsInfo(IDC_MIN_UNIT4, TheKey_teamUnitMinCount4,
|
||||
IDC_MAX_UNIT4, TheKey_teamUnitMaxCount4,
|
||||
IDC_UNIT_TYPE4, TheKey_teamUnitType4);
|
||||
|
||||
loadUnitsInfo(IDC_MIN_UNIT5, TheKey_teamUnitMinCount5,
|
||||
IDC_MAX_UNIT5, TheKey_teamUnitMaxCount5,
|
||||
IDC_UNIT_TYPE5, TheKey_teamUnitType5);
|
||||
|
||||
loadUnitsInfo(IDC_MIN_UNIT6, TheKey_teamUnitMinCount6,
|
||||
IDC_MAX_UNIT6, TheKey_teamUnitMaxCount6,
|
||||
IDC_UNIT_TYPE6, TheKey_teamUnitType6);
|
||||
|
||||
loadUnitsInfo(IDC_MIN_UNIT7, TheKey_teamUnitMinCount7,
|
||||
IDC_MAX_UNIT7, TheKey_teamUnitMaxCount7,
|
||||
IDC_UNIT_TYPE7, TheKey_teamUnitType7);
|
||||
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(IDC_HOME_WAYPOINT);
|
||||
Bool exists;
|
||||
EditParameter::loadWaypoints(pCombo);
|
||||
AsciiString homeWaypoint = m_teamDict->getAsciiString(TheKey_teamHome, &exists);
|
||||
Int stringNdx = pCombo->AddString(NONE_STRING);
|
||||
if (exists) {
|
||||
Int ndx = pCombo->FindStringExact(-1, homeWaypoint.str());
|
||||
if (ndx != CB_ERR) {
|
||||
stringNdx = ndx;
|
||||
}
|
||||
}
|
||||
pCombo->SetCurSel(stringNdx);
|
||||
|
||||
CComboBox *owner = (CComboBox*)GetDlgItem(IDC_TEAMOWNER);
|
||||
owner->ResetContent();
|
||||
for (Int i = 0; i < TheSidesList->getNumSides(); i++)
|
||||
{
|
||||
AsciiString name = TheSidesList->getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName);
|
||||
if (name.isEmpty())
|
||||
name = NEUTRAL_NAME_STR;
|
||||
owner->AddString(name.str());
|
||||
}
|
||||
|
||||
// must re-find, since list is sorted
|
||||
AsciiString cur_oname = m_teamDict->getAsciiString(TheKey_teamOwner);
|
||||
Int myPlayerIndex = -1;
|
||||
TheSidesList->findSideInfo(cur_oname, &myPlayerIndex);
|
||||
AsciiString oname_ui = TheSidesList->getSideInfo(myPlayerIndex)->getDict()->getAsciiString(TheKey_playerName);
|
||||
int oindex_in_list = owner->FindStringExact(-1, oname_ui.str());
|
||||
DEBUG_ASSERTCRASH(oindex_in_list >= 0, ("hmm"));
|
||||
owner->SetCurSel(oindex_in_list);
|
||||
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_AUTO_REINFORCE);
|
||||
Bool autoReinf = m_teamDict->getBool(TheKey_teamAutoReinforce, &exists);
|
||||
pCheck->SetCheck(autoReinf?1:0);
|
||||
|
||||
pCheck = (CButton*)GetDlgItem(IDC_TEAM_SINGLETON);
|
||||
Bool singleton = m_teamDict->getBool(TheKey_teamIsSingleton, &exists);
|
||||
pCheck->SetCheck(singleton?1:0);
|
||||
|
||||
pCheck = (CButton *) GetDlgItem(IDC_AI_RECRUITABLE);
|
||||
|
||||
Bool aiRecruit = m_teamDict->getBool(TheKey_teamIsAIRecruitable, &exists);
|
||||
pCheck->SetCheck(aiRecruit?1:0);
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_DESCRIPTION);
|
||||
AsciiString description = m_teamDict->getAsciiString(TheKey_teamDescription, &exists);
|
||||
pWnd->SetWindowText(description.str());
|
||||
|
||||
pWnd = GetDlgItem(IDC_TEAM_NAME);
|
||||
description = m_teamDict->getAsciiString(TheKey_teamName, &exists);
|
||||
pWnd->SetWindowText(description.str());
|
||||
|
||||
pWnd = GetDlgItem(IDC_MAX);
|
||||
Int maxInstances = m_teamDict->getInt(TheKey_teamMaxInstances, &exists);
|
||||
if (!exists) maxInstances = 1;
|
||||
description.format("%d", maxInstances);
|
||||
pWnd->SetWindowText(description.str());
|
||||
|
||||
pWnd = GetDlgItem(IDC_PRODUCTION_PRIORITY);
|
||||
Int priority = m_teamDict->getInt(TheKey_teamProductionPriority, &exists);
|
||||
description.format("%d", priority);
|
||||
pWnd->SetWindowText(description.str());
|
||||
|
||||
pWnd = GetDlgItem(IDC_PRIORITY_INCREASE);
|
||||
Int increase = m_teamDict->getInt(TheKey_teamProductionPrioritySuccessIncrease, &exists);
|
||||
description.format("%d", increase);
|
||||
pWnd->SetWindowText(description.str());
|
||||
|
||||
pWnd = GetDlgItem(IDC_PRIORITY_DECREASE);
|
||||
Int decrease = m_teamDict->getInt(TheKey_teamProductionPriorityFailureDecrease, &exists);
|
||||
description.format("%d", decrease);
|
||||
pWnd->SetWindowText(description.str());
|
||||
|
||||
pWnd = GetDlgItem(IDC_TEAM_BUILD_FRAMES);
|
||||
Int idleFrames = m_teamDict->getInt(TheKey_teamInitialIdleFrames, &exists);
|
||||
description.format("%d", idleFrames);
|
||||
pWnd->SetWindowText(description.str());
|
||||
|
||||
pCombo = (CComboBox*)GetDlgItem(IDC_PRODUCTION_CONDITION);
|
||||
// Load the subroutine scripts into the combo box.
|
||||
EditParameter::loadScripts(pCombo, true);
|
||||
stringNdx = pCombo->AddString(NONE_STRING);
|
||||
pCombo->SetFocus();
|
||||
AsciiString script = m_teamDict->getAsciiString(TheKey_teamProductionCondition, &exists);
|
||||
if (exists) {
|
||||
Int ndx = pCombo->FindStringExact(-1, script.str());
|
||||
if (ndx != CB_ERR) {
|
||||
stringNdx = ndx;
|
||||
}
|
||||
}
|
||||
pCombo->SetCurSel(stringNdx);
|
||||
|
||||
CButton *pButton = (CButton*) GetDlgItem(IDC_PRODUCTION_EXECUTEACTIONS);
|
||||
if (pButton) {
|
||||
Bool executeActions = m_teamDict->getBool(TheKey_teamExecutesActionsOnCreate, &exists);
|
||||
if (!exists) {
|
||||
m_teamDict->setBool(TheKey_teamExecutesActionsOnCreate, false);
|
||||
executeActions = false;
|
||||
}
|
||||
pButton->SetCheck(executeActions ? 1 : 0);
|
||||
}
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void TeamIdentity::loadUnitsInfo(int idcMinUnit, NameKeyType keyMinUnit,
|
||||
int idcMaxUnit, NameKeyType keyMaxUnit,
|
||||
int idcUnitType, NameKeyType keyUnitType)
|
||||
{
|
||||
CEdit *pEdit = (CEdit *)GetDlgItem(idcMinUnit);
|
||||
CString text;
|
||||
Bool exists;
|
||||
text.Format("%d", m_teamDict->getInt(keyMinUnit, &exists));
|
||||
pEdit->SetWindowText(text);
|
||||
|
||||
pEdit = (CEdit *)GetDlgItem(idcMaxUnit);
|
||||
text.Format("%d", m_teamDict->getInt(keyMaxUnit, &exists));
|
||||
pEdit->SetWindowText(text);
|
||||
|
||||
AsciiString type = m_teamDict->getAsciiString(keyUnitType, &exists);
|
||||
if (type.isEmpty()) type = NONE_STRING;
|
||||
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(idcUnitType);
|
||||
pCombo->ResetContent();
|
||||
|
||||
Bool found = false;
|
||||
|
||||
// add entries from the thing factory as the available UNITS to use
|
||||
const ThingTemplate *tTemplate;
|
||||
for( tTemplate = TheThingFactory->firstTemplate();
|
||||
tTemplate;
|
||||
tTemplate = tTemplate->friend_getNextTemplate() ) {
|
||||
|
||||
// next tier uses the editor sorting bits that design can specify in the INI
|
||||
EditorSortingType sort = tTemplate->getEditorSorting();
|
||||
if (( sort != ES_VEHICLE ) && (sort != ES_INFANTRY)) continue;
|
||||
|
||||
Int ndx = pCombo->AddString(tTemplate->getName().str());
|
||||
if (type == tTemplate->getName()) {
|
||||
found = true;
|
||||
pCombo->SetCurSel(ndx);
|
||||
}
|
||||
}
|
||||
|
||||
Int ndx = pCombo->AddString(NONE_STRING);
|
||||
if (!found) {
|
||||
pCombo->SetCurSel(ndx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOL TeamIdentity::OnCommand(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
|
||||
Int wNotifyCode = HIWORD(wParam); // notification code
|
||||
Int wID = LOWORD(wParam); // item, control, or accelerator identifier
|
||||
NameKeyType key;
|
||||
if (wNotifyCode == EN_CHANGE) {
|
||||
Int editCtrl = wID;
|
||||
switch (editCtrl) {
|
||||
default: editCtrl = 0;
|
||||
case IDC_MIN_UNIT1: key = TheKey_teamUnitMinCount1; break;
|
||||
case IDC_MAX_UNIT1: key = TheKey_teamUnitMaxCount1; break;
|
||||
|
||||
case IDC_MIN_UNIT2: key = TheKey_teamUnitMinCount2; break;
|
||||
case IDC_MAX_UNIT2: key = TheKey_teamUnitMaxCount2; break;
|
||||
|
||||
case IDC_MIN_UNIT3: key = TheKey_teamUnitMinCount3; break;
|
||||
case IDC_MAX_UNIT3: key = TheKey_teamUnitMaxCount3; break;
|
||||
|
||||
case IDC_MIN_UNIT4: key = TheKey_teamUnitMinCount4; break;
|
||||
case IDC_MAX_UNIT4: key = TheKey_teamUnitMaxCount4; break;
|
||||
|
||||
case IDC_MIN_UNIT5: key = TheKey_teamUnitMinCount5; break;
|
||||
case IDC_MAX_UNIT5: key = TheKey_teamUnitMaxCount5; break;
|
||||
|
||||
case IDC_MIN_UNIT6: key = TheKey_teamUnitMinCount6; break;
|
||||
case IDC_MAX_UNIT6: key = TheKey_teamUnitMaxCount6; break;
|
||||
|
||||
case IDC_MIN_UNIT7: key = TheKey_teamUnitMinCount7; break;
|
||||
case IDC_MAX_UNIT7: key = TheKey_teamUnitMaxCount7; break;
|
||||
}
|
||||
if (editCtrl != 0) {
|
||||
CEdit *pEdit = (CEdit *)GetDlgItem(editCtrl);
|
||||
CString txt;
|
||||
pEdit->GetWindowText(txt);
|
||||
Int theInt;
|
||||
if (1==sscanf(txt, "%d", &theInt)) {
|
||||
m_teamDict->setInt(key, theInt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (wNotifyCode == CBN_SELCHANGE) {
|
||||
Int cmboCtrl = wID;
|
||||
switch (cmboCtrl) {
|
||||
case IDC_UNIT_TYPE1: key = TheKey_teamUnitType1; break;
|
||||
case IDC_UNIT_TYPE2: key = TheKey_teamUnitType2; break;
|
||||
case IDC_UNIT_TYPE3: key = TheKey_teamUnitType3; break;
|
||||
case IDC_UNIT_TYPE4: key = TheKey_teamUnitType4; break;
|
||||
case IDC_UNIT_TYPE5: key = TheKey_teamUnitType5; break;
|
||||
case IDC_UNIT_TYPE6: key = TheKey_teamUnitType6; break;
|
||||
case IDC_UNIT_TYPE7: key = TheKey_teamUnitType7; break;
|
||||
default: cmboCtrl = 0;
|
||||
}
|
||||
if (cmboCtrl != 0) {
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(cmboCtrl);
|
||||
CString txt;
|
||||
pCombo->GetWindowText(txt);
|
||||
AsciiString comboText = AsciiString(txt);
|
||||
m_teamDict->setAsciiString(key, comboText);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return CPropertyPage::OnCommand(wParam, lParam);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnAiRecruitable()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_AI_RECRUITABLE);
|
||||
Bool checked = pCheck->GetCheck()==1;
|
||||
m_teamDict->setBool(TheKey_teamIsAIRecruitable, checked);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnAutoReinforce()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_AUTO_REINFORCE);
|
||||
Bool checked = pCheck->GetCheck()==1;
|
||||
m_teamDict->setBool(TheKey_teamAutoReinforce, checked);
|
||||
}
|
||||
|
||||
|
||||
void TeamIdentity::OnChangeDescription()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_DESCRIPTION);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
AsciiString des = val;
|
||||
m_teamDict->setAsciiString(TheKey_teamDescription, des);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamIdentity::OnChangeMax()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_MAX);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
Int maxInstances = atoi(val);
|
||||
m_teamDict->setInt(TheKey_teamMaxInstances, maxInstances);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamIdentity::OnChangePriorityDecrease()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_PRIORITY_DECREASE);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
Int decrease = atoi(val);
|
||||
m_teamDict->setInt(TheKey_teamProductionPriorityFailureDecrease, decrease);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamIdentity::OnChangePriorityIncrease()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_PRIORITY_INCREASE);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
Int increase = atoi(val);
|
||||
m_teamDict->setInt(TheKey_teamProductionPrioritySuccessIncrease, increase);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamIdentity::OnSelchangeProductionCondition()
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_PRODUCTION_CONDITION);
|
||||
CString txt;
|
||||
Int curSel = pCombo->GetCurSel();
|
||||
pCombo->GetLBText(curSel, txt);
|
||||
AsciiString comboText = AsciiString(txt);
|
||||
if (comboText == NONE_STRING) {
|
||||
comboText.clear();
|
||||
}
|
||||
m_teamDict->setAsciiString(TheKey_teamProductionCondition, comboText);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnChangeProductionPriority()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_PRODUCTION_PRIORITY);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
Int priority = atoi(val);
|
||||
m_teamDict->setInt(TheKey_teamProductionPriority, priority);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamIdentity::OnSelchangeHomeWaypoint()
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(IDC_HOME_WAYPOINT);
|
||||
CString txt;
|
||||
Int curSel = pCombo->GetCurSel();
|
||||
pCombo->GetLBText(curSel, txt);
|
||||
AsciiString comboText = AsciiString(txt);
|
||||
if (comboText == NONE_STRING) {
|
||||
comboText.clear();
|
||||
}
|
||||
m_teamDict->setAsciiString(TheKey_teamHome, comboText);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnUnitTypeButton(Int idcUnitType)
|
||||
{
|
||||
PickUnitDialog dlg;
|
||||
dlg.SetAllowableType(ES_VEHICLE);
|
||||
dlg.SetAllowableType(ES_INFANTRY);
|
||||
if (dlg.DoModal() == IDOK) {
|
||||
AsciiString unit = dlg.getPickedUnit();
|
||||
NameKeyType keyUnitType;
|
||||
|
||||
switch (idcUnitType)
|
||||
{
|
||||
case IDC_UNIT_TYPE1:
|
||||
keyUnitType = TheKey_teamUnitType1;
|
||||
break;
|
||||
case IDC_UNIT_TYPE2:
|
||||
keyUnitType = TheKey_teamUnitType2;
|
||||
break;
|
||||
case IDC_UNIT_TYPE3:
|
||||
keyUnitType = TheKey_teamUnitType3;
|
||||
break;
|
||||
case IDC_UNIT_TYPE4:
|
||||
keyUnitType = TheKey_teamUnitType4;
|
||||
break;
|
||||
case IDC_UNIT_TYPE5:
|
||||
keyUnitType = TheKey_teamUnitType5;
|
||||
break;
|
||||
case IDC_UNIT_TYPE6:
|
||||
keyUnitType = TheKey_teamUnitType6;
|
||||
break;
|
||||
case IDC_UNIT_TYPE7:
|
||||
keyUnitType = TheKey_teamUnitType7;
|
||||
break;
|
||||
}
|
||||
m_teamDict->setAsciiString(keyUnitType, unit);
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(idcUnitType);
|
||||
pCombo->SelectString(-1, unit.str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TeamIdentity::OnUnitType1Button()
|
||||
{
|
||||
OnUnitTypeButton(IDC_UNIT_TYPE1);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnUnitType2Button()
|
||||
{
|
||||
OnUnitTypeButton(IDC_UNIT_TYPE2);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnUnitType3Button()
|
||||
{
|
||||
OnUnitTypeButton(IDC_UNIT_TYPE3);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnUnitType4Button()
|
||||
{
|
||||
OnUnitTypeButton(IDC_UNIT_TYPE4);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnUnitType5Button()
|
||||
{
|
||||
OnUnitTypeButton(IDC_UNIT_TYPE5);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnUnitType6Button()
|
||||
{
|
||||
OnUnitTypeButton(IDC_UNIT_TYPE6);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnUnitType7Button()
|
||||
{
|
||||
OnUnitTypeButton(IDC_UNIT_TYPE7);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnExecuteActions()
|
||||
{
|
||||
CButton *pButton = (CButton*) GetDlgItem(IDC_PRODUCTION_EXECUTEACTIONS);
|
||||
if (!pButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_teamDict->setBool(TheKey_teamExecutesActionsOnCreate, pButton->GetCheck() ? true : false);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnChangeTeamName()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TeamIdentity::OnTeamSingleton()
|
||||
{
|
||||
CButton *singleton = (CButton*)GetDlgItem(IDC_TEAM_SINGLETON);
|
||||
m_teamDict->setBool(TheKey_teamIsSingleton, singleton->GetCheck() != 0);
|
||||
}
|
||||
|
||||
void TeamIdentity::OnKillfocusTeamName()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_TEAM_NAME);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
AsciiString tnamenew = val;
|
||||
AsciiString tnamecur = m_teamDict->getAsciiString(TheKey_teamName);
|
||||
Bool set = true;
|
||||
if (tnamecur != tnamenew) {
|
||||
if (m_sides->findTeamInfo(tnamenew) || m_sides->findSideInfo(tnamenew))
|
||||
{
|
||||
::AfxMessageBox(IDS_NAME_IN_USE);
|
||||
set = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Int count = MapObject::countMapObjectsWithOwner(tnamecur);
|
||||
if (count > 0)
|
||||
{
|
||||
set = false;
|
||||
CString msg;
|
||||
msg.Format(IDS_RENAMING_INUSE_TEAM, count);
|
||||
if (::AfxMessageBox(msg, MB_YESNO) == IDYES)
|
||||
set = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (set) {
|
||||
m_teamDict->setAsciiString(TheKey_teamName, tnamenew);
|
||||
} else {
|
||||
Bool exists;
|
||||
AsciiString description = m_teamDict->getAsciiString(TheKey_teamName, &exists);
|
||||
pWnd->SetWindowText(description.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamIdentity::OnSelendokTeamowner()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_TEAMOWNER);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
AsciiString des = val;
|
||||
m_teamDict->setAsciiString(TheKey_teamOwner, des);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamIdentity::OnChangeTeamBuildFrames()
|
||||
{
|
||||
CWnd *pWnd = GetDlgItem(IDC_TEAM_BUILD_FRAMES);
|
||||
if (pWnd) {
|
||||
CString val;
|
||||
pWnd->GetWindowText(val);
|
||||
Int idleFrames = atoi(val);
|
||||
m_teamDict->setInt(TheKey_teamInitialIdleFrames, idleFrames);
|
||||
}
|
||||
|
||||
}
|
||||
685
GeneralsMD/Code/Tools/WorldBuilder/src/TeamObjectProperties.cpp
Normal file
685
GeneralsMD/Code/Tools/WorldBuilder/src/TeamObjectProperties.cpp
Normal file
@@ -0,0 +1,685 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TeamObjectProperties.cpp
|
||||
// Mike Lytle
|
||||
// January, 2003
|
||||
// (c) Electronic Arts 2003
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "TeamObjectProperties.h"
|
||||
#include "Common/MapObject.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TeamObjectProperties dialog
|
||||
|
||||
TeamObjectProperties::TeamObjectProperties(Dict* dictToEdit)
|
||||
: CPropertyPage(TeamObjectProperties::IDD),
|
||||
m_dictToEdit(dictToEdit)
|
||||
{
|
||||
//{{AFX_DATA_INIT(TeamObjectProperties)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
TeamObjectProperties::~TeamObjectProperties()
|
||||
{
|
||||
}
|
||||
|
||||
void TeamObjectProperties::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(TeamObjectProperties)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
#if 0
|
||||
BEGIN_MESSAGE_MAP(TeamObjectProperties, CPropertyPage)
|
||||
//{{AFX_MSG_MAP(TeamObjectProperties)
|
||||
ON_CBN_SELCHANGE(IDC_MAPOBJECT_StartingHealth, _HealthToDict)
|
||||
ON_CBN_SELENDOK(IDC_MAPOBJECT_HitPoints, _HPsToDict)
|
||||
ON_CBN_KILLFOCUS(IDC_MAPOBJECT_HitPoints, _HPsToDict)
|
||||
ON_BN_CLICKED(IDC_MAPOBJECT_Enabled, _EnabledToDict)
|
||||
ON_BN_CLICKED(IDC_MAPOBJECT_Indestructible, _IndestructibleToDict)
|
||||
ON_BN_CLICKED(IDC_MAPOBJECT_Unsellable, _UnsellableToDict)
|
||||
ON_BN_CLICKED(IDC_MAPOBJECT_Powered, _PoweredToDict)
|
||||
ON_CBN_SELCHANGE(IDC_MAPOBJECT_Aggressiveness, _AggressivenessToDict)
|
||||
ON_EN_KILLFOCUS(IDC_MAPOBJECT_VisionDistance, _VisibilityToDict)
|
||||
ON_EN_KILLFOCUS(IDC_MAPOBJECT_ShroudClearingDistance, _ShroudClearingDistanceToDict)
|
||||
ON_CBN_SELCHANGE(IDC_MAPOBJECT_Veterancy, _VeterancyToDict)
|
||||
ON_BN_CLICKED(IDC_MAPOBJECT_RecruitableAI, _RecruitableAIToDict)
|
||||
ON_BN_CLICKED(IDC_MAPOBJECT_Selectable, _SelectableToDict)
|
||||
ON_CBN_SELCHANGE(IDC_MAPOBJECT_Weather, _WeatherToDict)
|
||||
ON_CBN_SELCHANGE(IDC_MAPOBJECT_Time, _TimeToDict)
|
||||
ON_EN_KILLFOCUS(IDC_MAPOBJECT_StoppingDistance, _StoppingDistanceToDict)
|
||||
ON_EN_KILLFOCUS(IDC_MAPOBJECT_StartingHealthEdit, _HealthToDict)
|
||||
ON_BN_CLICKED(IDC_UPDATE_TEAM_MEMBERS, _UpdateTeamMembers)
|
||||
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TeamObjectProperties message handlers
|
||||
|
||||
BOOL TeamObjectProperties::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
updateTheUI();
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void TeamObjectProperties::OnOK()
|
||||
{
|
||||
_PropertiesToDict();
|
||||
}
|
||||
|
||||
void TeamObjectProperties::updateTheUI(void)
|
||||
{
|
||||
_DictToHealth();
|
||||
_DictToHPs();
|
||||
_DictToEnabled();
|
||||
_DictToDestructible();
|
||||
_DictToPowered();
|
||||
_DictToUnsellable();
|
||||
_DictToAggressiveness();
|
||||
_DictToVisibilityRange();
|
||||
_DictToVeterancy();
|
||||
_DictToWeather();
|
||||
_DictToTime();
|
||||
_DictToShroudClearingDistance();
|
||||
_DictToRecruitableAI();
|
||||
_DictToSelectable();
|
||||
_DictToStoppingDistance();
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToHealth(void)
|
||||
{
|
||||
Int value = 100;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectInitialHealth, &exists);
|
||||
}
|
||||
|
||||
CComboBox* pItem = (CComboBox*) GetDlgItem(IDC_MAPOBJECT_StartingHealth);
|
||||
CWnd* pItem2 = GetDlgItem(IDC_MAPOBJECT_StartingHealthEdit);
|
||||
if (pItem && pItem2) {
|
||||
if (value == 0) {
|
||||
pItem->SelectString(-1, "0%");
|
||||
pItem2->SetWindowText("");
|
||||
pItem2->EnableWindow(FALSE);
|
||||
} else if (value == 25) {
|
||||
pItem->SelectString(-1, "25%");
|
||||
pItem2->SetWindowText("");
|
||||
pItem2->EnableWindow(FALSE);
|
||||
} else if (value == 50) {
|
||||
pItem->SelectString(-1, "50%");
|
||||
pItem2->SetWindowText("");
|
||||
pItem2->EnableWindow(FALSE);
|
||||
} else if (value == 75) {
|
||||
pItem->SelectString(-1, "75%");
|
||||
pItem2->SetWindowText("");
|
||||
pItem2->EnableWindow(FALSE);
|
||||
} else if (value == 100) {
|
||||
pItem->SelectString(-1, "100%");
|
||||
pItem2->SetWindowText("");
|
||||
pItem2->EnableWindow(FALSE);
|
||||
} else {
|
||||
pItem->SelectString(-1, "Other");
|
||||
static char buff[12];
|
||||
sprintf(buff, "%d", value);
|
||||
pItem2->SetWindowText(buff);
|
||||
pItem2->EnableWindow(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToHPs(void)
|
||||
{
|
||||
Int value = -1;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectMaxHPs, &exists);
|
||||
if (!exists)
|
||||
value = -1;
|
||||
}
|
||||
|
||||
CComboBox* pItem = (CComboBox*) GetDlgItem(IDC_MAPOBJECT_HitPoints);
|
||||
pItem->ResetContent();
|
||||
CString str;
|
||||
str.Format("Default For Unit");
|
||||
pItem->InsertString(-1, str);
|
||||
|
||||
if (value != -1) {
|
||||
str.Format("%d", value);
|
||||
pItem->InsertString(-1, str);
|
||||
pItem->SetCurSel(1);
|
||||
} else {
|
||||
pItem->SetCurSel(0);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToEnabled(void)
|
||||
{
|
||||
Bool enabled = true;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
enabled = m_dictToEdit->getBool(TheKey_teamObjectEnabled, &exists);
|
||||
}
|
||||
|
||||
CButton* pItem = (CButton*) GetDlgItem(IDC_MAPOBJECT_Enabled);
|
||||
if (pItem) {
|
||||
pItem->SetCheck(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToDestructible(void)
|
||||
{
|
||||
Bool destructible = true;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
destructible = m_dictToEdit->getBool(TheKey_teamObjectIndestructible, &exists);
|
||||
}
|
||||
|
||||
CButton* pItem = (CButton*) GetDlgItem(IDC_MAPOBJECT_Indestructible);
|
||||
if (pItem) {
|
||||
pItem->SetCheck(destructible);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToUnsellable(void)
|
||||
{
|
||||
Bool unsellable = false;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
unsellable = m_dictToEdit->getBool(TheKey_teamObjectUnsellable, &exists);
|
||||
}
|
||||
|
||||
CButton* pItem = (CButton*) GetDlgItem(IDC_MAPOBJECT_Unsellable);
|
||||
if (pItem) {
|
||||
pItem->SetCheck(unsellable);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToPowered(void)
|
||||
{
|
||||
Bool powered = true;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
powered = m_dictToEdit->getBool(TheKey_teamObjectPowered, &exists);
|
||||
}
|
||||
|
||||
CButton* pItem = (CButton*) GetDlgItem(IDC_MAPOBJECT_Powered);
|
||||
if (pItem) {
|
||||
pItem->SetCheck(powered);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToAggressiveness(void)
|
||||
{
|
||||
Int value = 0;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectAggressiveness, &exists);
|
||||
}
|
||||
|
||||
CComboBox* pItem = (CComboBox*) GetDlgItem(IDC_MAPOBJECT_Aggressiveness);
|
||||
if (pItem) {
|
||||
if (value == -2) {
|
||||
pItem->SelectString(-1, "Sleep");
|
||||
} else if (value == -1) {
|
||||
pItem->SelectString(-1, "Passive");
|
||||
} else if (value == 0) {
|
||||
pItem->SelectString(-1, "Normal");
|
||||
} else if (value == 1) {
|
||||
pItem->SelectString(-1, "Alert");
|
||||
} else if (value == 2) {
|
||||
pItem->SelectString(-1, "Aggressive");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToVisibilityRange(void)
|
||||
{
|
||||
Int distance = 0;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
distance = m_dictToEdit->getInt(TheKey_teamObjectVisualRange, &exists);
|
||||
}
|
||||
|
||||
CWnd* pItem = GetDlgItem(IDC_MAPOBJECT_VisionDistance);
|
||||
if (pItem) {
|
||||
static char buff[12];
|
||||
sprintf(buff, "%d", distance);
|
||||
if (distance == 0) {
|
||||
pItem->SetWindowText("");
|
||||
} else {
|
||||
pItem->SetWindowText(buff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToVeterancy(void)
|
||||
{
|
||||
Int value = 0;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectVeterancy, &exists);
|
||||
}
|
||||
|
||||
CComboBox* pItem = (CComboBox*) GetDlgItem(IDC_MAPOBJECT_Veterancy);
|
||||
if (pItem) {
|
||||
pItem->SetCurSel(value);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToWeather(void)
|
||||
{
|
||||
Int value = 0;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectWeather, &exists);
|
||||
if (!exists)
|
||||
value = 0;
|
||||
}
|
||||
|
||||
CComboBox* pItem = (CComboBox*) GetDlgItem(IDC_MAPOBJECT_Weather);
|
||||
pItem->SetCurSel(value);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToTime(void)
|
||||
{
|
||||
Int value = 0;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectTime, &exists);
|
||||
if (!exists)
|
||||
value = 0;
|
||||
}
|
||||
|
||||
CComboBox* pItem = (CComboBox*) GetDlgItem(IDC_MAPOBJECT_Time);
|
||||
pItem->SetCurSel(value);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToShroudClearingDistance(void)
|
||||
{
|
||||
Int distance = 0;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
distance = m_dictToEdit->getInt(TheKey_teamObjectShroudClearingDistance, &exists);
|
||||
}
|
||||
|
||||
CWnd* pItem = GetDlgItem(IDC_MAPOBJECT_ShroudClearingDistance);
|
||||
if (pItem) {
|
||||
static char buff[12];
|
||||
sprintf(buff, "%d", distance);
|
||||
if (distance == 0) {
|
||||
pItem->SetWindowText("");
|
||||
} else {
|
||||
pItem->SetWindowText(buff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToRecruitableAI(void)
|
||||
{
|
||||
Bool recruitableAI = true;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
recruitableAI = m_dictToEdit->getBool(TheKey_teamObjectRecruitableAI, &exists);
|
||||
}
|
||||
|
||||
CButton* pItem = (CButton*) GetDlgItem(IDC_MAPOBJECT_RecruitableAI);
|
||||
if (pItem) {
|
||||
pItem->SetCheck(recruitableAI);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToSelectable(void)
|
||||
{
|
||||
Bool selectable = true;
|
||||
Bool exists;
|
||||
if (m_dictToEdit) {
|
||||
selectable = m_dictToEdit->getBool(TheKey_teamObjectSelectable, &exists);
|
||||
}
|
||||
|
||||
CButton* pItem = (CButton*) GetDlgItem(IDC_MAPOBJECT_Selectable);
|
||||
if (pItem) {
|
||||
pItem->SetCheck(selectable);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_DictToStoppingDistance(void)
|
||||
{
|
||||
Real stoppingDistance = 1.0f;
|
||||
Bool exists = false;
|
||||
if (m_dictToEdit) {
|
||||
stoppingDistance = m_dictToEdit->getReal(TheKey_teamObjectStoppingDistance, &exists);
|
||||
}
|
||||
|
||||
CWnd* pItem = GetDlgItem(IDC_MAPOBJECT_StoppingDistance);
|
||||
if (pItem) {
|
||||
static char buff[12];
|
||||
sprintf(buff, "%g", stoppingDistance);
|
||||
if (stoppingDistance == 0) {
|
||||
pItem->SetWindowText("");
|
||||
} else {
|
||||
pItem->SetWindowText(buff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_HealthToDict(void)
|
||||
{
|
||||
Int value;
|
||||
static char buf[1024];
|
||||
|
||||
CComboBox *owner = (CComboBox*)GetDlgItem(IDC_MAPOBJECT_StartingHealth);
|
||||
owner->GetWindowText(buf, sizeof(buf)-2);
|
||||
if (strcmp(buf, "Dead") == 0) {
|
||||
value = 0;
|
||||
} else if (strcmp(buf, "25%") == 0) {
|
||||
value = 25;
|
||||
} else if (strcmp(buf, "50%") == 0) {
|
||||
value = 50;
|
||||
} else if (strcmp(buf, "75%") == 0) {
|
||||
value = 75;
|
||||
} else if (strcmp(buf, "100%") == 0) {
|
||||
value = 100;
|
||||
} else if (strcmp(buf, "Other") == 0) {
|
||||
CWnd* edit = GetDlgItem(IDC_MAPOBJECT_StartingHealthEdit);
|
||||
edit->EnableWindow(TRUE);
|
||||
CString cstr;
|
||||
edit->GetWindowText(cstr);
|
||||
if (cstr.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
value = atoi(cstr.GetBuffer(0));
|
||||
}
|
||||
|
||||
m_dictToEdit->setInt(TheKey_teamObjectInitialHealth, value);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_EnabledToDict(void)
|
||||
{
|
||||
CButton *owner = (CButton*) GetDlgItem(IDC_MAPOBJECT_Enabled);
|
||||
Bool isChecked = (owner->GetCheck() != 0);
|
||||
|
||||
m_dictToEdit->setBool(TheKey_teamObjectEnabled, isChecked);
|
||||
}
|
||||
|
||||
|
||||
void TeamObjectProperties::_IndestructibleToDict(void)
|
||||
{
|
||||
CButton *owner = (CButton*) GetDlgItem(IDC_MAPOBJECT_Indestructible);
|
||||
Bool isChecked = (owner->GetCheck() != 0);
|
||||
|
||||
m_dictToEdit->setBool(TheKey_teamObjectIndestructible, isChecked);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_UnsellableToDict(void)
|
||||
{
|
||||
CButton *owner = (CButton*) GetDlgItem(IDC_MAPOBJECT_Unsellable);
|
||||
Bool isChecked = (owner->GetCheck() != 0);
|
||||
|
||||
m_dictToEdit->setBool(TheKey_teamObjectUnsellable, isChecked);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_PoweredToDict(void)
|
||||
{
|
||||
CButton *owner = (CButton*) GetDlgItem(IDC_MAPOBJECT_Powered);
|
||||
Bool isChecked = (owner->GetCheck() != 0);
|
||||
|
||||
m_dictToEdit->setBool(TheKey_teamObjectPowered, isChecked);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_AggressivenessToDict(void)
|
||||
{
|
||||
CComboBox *owner = (CComboBox*)GetDlgItem(IDC_MAPOBJECT_Aggressiveness);
|
||||
static char buf[1024];
|
||||
owner->GetWindowText(buf, sizeof(buf)-2);
|
||||
int value = 0;
|
||||
|
||||
if (strcmp(buf, "Sleep") == 0) {
|
||||
value = -2;
|
||||
} else if (strcmp(buf, "Passive") == 0) {
|
||||
value = -1;
|
||||
} else if (strcmp(buf, "Normal") == 0) {
|
||||
value = 0;
|
||||
} else if (strcmp(buf, "Alert") == 0) {
|
||||
value = 1;
|
||||
} else if (strcmp(buf, "Aggressive") == 0) {
|
||||
value = 2;
|
||||
}
|
||||
|
||||
m_dictToEdit->setInt(TheKey_teamObjectAggressiveness, value);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_VisibilityToDict(void)
|
||||
{
|
||||
int value = -1;
|
||||
CWnd* edit = GetDlgItem(IDC_MAPOBJECT_VisionDistance);
|
||||
edit->EnableWindow(TRUE);
|
||||
CString cstr;
|
||||
edit->GetWindowText(cstr);
|
||||
if (!cstr.IsEmpty()) {
|
||||
value = atoi(cstr.GetBuffer(0));
|
||||
}
|
||||
|
||||
if (value != -1) {
|
||||
m_dictToEdit->setInt(TheKey_teamObjectVisualRange, value);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_VeterancyToDict(void)
|
||||
{
|
||||
CComboBox *owner = (CComboBox*)GetDlgItem(IDC_MAPOBJECT_Veterancy);
|
||||
static char buf[1024];
|
||||
int curSel = owner->GetCurSel();
|
||||
int value = 0;
|
||||
if (curSel >= 0) {
|
||||
value=curSel;
|
||||
}
|
||||
|
||||
m_dictToEdit->setInt(TheKey_teamObjectVeterancy, value);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_WeatherToDict(void)
|
||||
{
|
||||
CComboBox *owner = (CComboBox*)GetDlgItem(IDC_MAPOBJECT_Weather);
|
||||
static char buf[1024];
|
||||
int curSel = owner->GetCurSel();
|
||||
|
||||
m_dictToEdit->setInt(TheKey_teamObjectWeather, curSel);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_TimeToDict(void)
|
||||
{
|
||||
CComboBox *owner = (CComboBox*)GetDlgItem(IDC_MAPOBJECT_Time);
|
||||
static char buf[1024];
|
||||
int curSel = owner->GetCurSel();
|
||||
|
||||
m_dictToEdit->setInt(TheKey_teamObjectTime, curSel);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_ShroudClearingDistanceToDict(void)
|
||||
{
|
||||
int value = -1;
|
||||
CWnd* edit = GetDlgItem(IDC_MAPOBJECT_ShroudClearingDistance);
|
||||
edit->EnableWindow(TRUE);
|
||||
CString cstr;
|
||||
edit->GetWindowText(cstr);
|
||||
if (!cstr.IsEmpty()) {
|
||||
value = atoi(cstr.GetBuffer(0));
|
||||
}
|
||||
|
||||
if (value != -1) {
|
||||
m_dictToEdit->setInt(TheKey_teamObjectShroudClearingDistance, value);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_RecruitableAIToDict(void)
|
||||
{
|
||||
CButton *owner = (CButton*) GetDlgItem(IDC_MAPOBJECT_RecruitableAI);
|
||||
Bool isChecked = (owner->GetCheck() != 0);
|
||||
|
||||
m_dictToEdit->setBool(TheKey_teamObjectRecruitableAI, isChecked);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_SelectableToDict(void)
|
||||
{
|
||||
CButton *owner = (CButton*) GetDlgItem(IDC_MAPOBJECT_Selectable);
|
||||
Bool isChecked = (owner->GetCheck() != 0);
|
||||
|
||||
m_dictToEdit->setBool(TheKey_teamObjectSelectable, isChecked);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_HPsToDict()
|
||||
{
|
||||
Int value;
|
||||
static char buf[1024];
|
||||
|
||||
CComboBox *owner = (CComboBox*)GetDlgItem(IDC_MAPOBJECT_HitPoints);
|
||||
owner->GetWindowText(buf, sizeof(buf)-2);
|
||||
value = atoi(buf);
|
||||
if (value == 0)
|
||||
value = -1;
|
||||
|
||||
m_dictToEdit->setInt(TheKey_teamObjectMaxHPs, value);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_StoppingDistanceToDict(void)
|
||||
{
|
||||
Real value;
|
||||
static char buf[1024];
|
||||
|
||||
CWnd* edit = GetDlgItem(IDC_MAPOBJECT_StoppingDistance);
|
||||
CString cstr;
|
||||
edit->GetWindowText(cstr);
|
||||
if (cstr.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
value = atof(cstr.GetBuffer(0));
|
||||
|
||||
m_dictToEdit->setReal(TheKey_teamObjectStoppingDistance, value);
|
||||
}
|
||||
|
||||
void TeamObjectProperties::_UpdateTeamMembers()
|
||||
{
|
||||
CString msg;
|
||||
msg.Format("Are you sure you want to change the team members?");
|
||||
if (::AfxMessageBox(msg, MB_YESNO) == IDNO) {
|
||||
return;
|
||||
}
|
||||
|
||||
AsciiString teamName = m_dictToEdit->getAsciiString(TheKey_teamName);
|
||||
|
||||
MapObject *pObj;
|
||||
for (pObj=MapObject::getFirstMapObject(); pObj; pObj=pObj->getNext()) {
|
||||
Dict* objectDict = pObj->getProperties();
|
||||
DEBUG_ASSERTCRASH(objectDict, ("objectDict shouldn't be NULL"));
|
||||
|
||||
AsciiString objectsTeam = objectDict->getAsciiString(TheKey_originalOwner);
|
||||
|
||||
if (teamName == objectsTeam) {
|
||||
DEBUG_ASSERTCRASH(m_dictToEdit, ("m_dictToEdit shouldn't be NULL"));
|
||||
Bool exists;
|
||||
|
||||
Int value = m_dictToEdit->getInt(TheKey_teamObjectInitialHealth, &exists);
|
||||
objectDict->setInt(TheKey_objectInitialHealth, value);
|
||||
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectMaxHPs, &exists);
|
||||
objectDict->setInt(TheKey_objectMaxHPs, value);
|
||||
|
||||
Bool enabled = m_dictToEdit->getBool(TheKey_teamObjectEnabled, &exists);
|
||||
objectDict->setBool(TheKey_objectEnabled, enabled);
|
||||
|
||||
enabled = m_dictToEdit->getBool(TheKey_teamObjectIndestructible, &exists);
|
||||
objectDict->setBool(TheKey_objectIndestructible, enabled);
|
||||
|
||||
enabled = m_dictToEdit->getBool(TheKey_teamObjectUnsellable, &exists);
|
||||
objectDict->setBool(TheKey_objectUnsellable, enabled);
|
||||
|
||||
enabled = m_dictToEdit->getBool(TheKey_teamObjectPowered, &exists);
|
||||
objectDict->setBool(TheKey_objectPowered, enabled);
|
||||
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectAggressiveness, &exists);
|
||||
objectDict->setInt(TheKey_objectAggressiveness, value);
|
||||
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectVisualRange, &exists);
|
||||
objectDict->setInt(TheKey_objectVisualRange, value);
|
||||
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectVeterancy, &exists);
|
||||
objectDict->setInt(TheKey_objectVeterancy, value);
|
||||
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectWeather, &exists);
|
||||
objectDict->setInt(TheKey_objectWeather, value);
|
||||
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectTime, &exists);
|
||||
objectDict->setInt(TheKey_objectTime, value);
|
||||
|
||||
value = m_dictToEdit->getInt(TheKey_teamObjectShroudClearingDistance, &exists);
|
||||
objectDict->setInt(TheKey_objectShroudClearingDistance, value);
|
||||
|
||||
enabled = m_dictToEdit->getBool(TheKey_teamObjectRecruitableAI, &exists);
|
||||
objectDict->setBool(TheKey_objectRecruitableAI, enabled);
|
||||
|
||||
enabled = m_dictToEdit->getBool(TheKey_teamObjectSelectable, &exists);
|
||||
objectDict->setBool(TheKey_objectSelectable, enabled);
|
||||
|
||||
Real dist = m_dictToEdit->getReal(TheKey_teamObjectStoppingDistance, &exists);
|
||||
objectDict->setReal(TheKey_objectStoppingDistance, dist);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TeamObjectProperties::_PropertiesToDict()
|
||||
{
|
||||
// Make sure that the attributes are in the dictionary.
|
||||
_HealthToDict();
|
||||
_HPsToDict();
|
||||
_EnabledToDict();
|
||||
_IndestructibleToDict();
|
||||
_UnsellableToDict();
|
||||
_PoweredToDict();
|
||||
_AggressivenessToDict();
|
||||
_VisibilityToDict();
|
||||
_ShroudClearingDistanceToDict();
|
||||
_VeterancyToDict();
|
||||
_RecruitableAIToDict();
|
||||
_SelectableToDict();
|
||||
_WeatherToDict();
|
||||
_TimeToDict();
|
||||
_StoppingDistanceToDict();
|
||||
_HealthToDict();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOL TeamObjectProperties::OnCommand(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return CPropertyPage::OnCommand(wParam, lParam);
|
||||
}
|
||||
|
||||
190
GeneralsMD/Code/Tools/WorldBuilder/src/TeamReinforcement.cpp
Normal file
190
GeneralsMD/Code/Tools/WorldBuilder/src/TeamReinforcement.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TeamReinforcement.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "TeamReinforcement.h"
|
||||
#include "EditParameter.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TeamReinforcement dialog
|
||||
|
||||
|
||||
TeamReinforcement::TeamReinforcement()
|
||||
: CPropertyPage(TeamReinforcement::IDD) ,
|
||||
m_teamDict(NULL)
|
||||
{
|
||||
//{{AFX_DATA_INIT(TeamReinforcement)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void TeamReinforcement::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CPropertyPage::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(TeamReinforcement)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(TeamReinforcement, CPropertyPage)
|
||||
//{{AFX_MSG_MAP(TeamReinforcement)
|
||||
ON_BN_CLICKED(IDC_DEPLOY_BY, OnDeployBy)
|
||||
ON_BN_CLICKED(IDC_TEAM_STARTS_FULL, OnTeamStartsFull)
|
||||
ON_CBN_SELCHANGE(IDC_TRANSPORT_COMBO, OnSelchangeTransportCombo)
|
||||
ON_BN_CLICKED(IDC_TRANSPORTS_EXIT, OnTransportsExit)
|
||||
ON_CBN_SELCHANGE(IDC_VETERANCY, OnSelchangeVeterancy)
|
||||
ON_CBN_SELCHANGE(IDC_WAYPOINT_COMBO, OnSelchangeWaypointCombo)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TeamReinforcement message handlers
|
||||
|
||||
BOOL TeamReinforcement::OnInitDialog()
|
||||
{
|
||||
CPropertyPage::OnInitDialog();
|
||||
Bool exists;
|
||||
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(IDC_TRANSPORT_COMBO);
|
||||
EditParameter::loadTransports(pCombo);
|
||||
AsciiString transport = m_teamDict->getAsciiString(TheKey_teamTransport, &exists);
|
||||
Int stringNdx = pCombo->AddString(NONE_STRING);
|
||||
Int noneNdx = stringNdx;
|
||||
if (exists && !transport.isEmpty()) {
|
||||
Int ndx = pCombo->FindStringExact(-1, transport.str());
|
||||
if (ndx != CB_ERR) {
|
||||
stringNdx = ndx;
|
||||
}
|
||||
}
|
||||
pCombo->SetCurSel(stringNdx);
|
||||
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_TEAM_STARTS_FULL);
|
||||
Bool teamStartsFull = m_teamDict->getBool(TheKey_teamStartsFull, &exists);
|
||||
pCheck->SetCheck(teamStartsFull?1:0);
|
||||
|
||||
pCheck = (CButton *) GetDlgItem(IDC_TRANSPORTS_EXIT);
|
||||
Bool transportsExit = m_teamDict->getBool(TheKey_teamTransportsExit, &exists);
|
||||
pCheck->SetCheck(transportsExit?1:0);
|
||||
Bool enable = !(stringNdx==noneNdx);
|
||||
pCheck->EnableWindow(enable);
|
||||
pCombo->EnableWindow(enable);
|
||||
|
||||
pCheck = (CButton *) GetDlgItem(IDC_DEPLOY_BY);
|
||||
pCheck->SetCheck( (stringNdx==noneNdx)?0:1);
|
||||
|
||||
pCombo = (CComboBox *)GetDlgItem(IDC_VETERANCY);
|
||||
Int value = m_teamDict->getInt(TheKey_teamVeterancy, &exists);
|
||||
pCombo->SetCurSel(value);
|
||||
|
||||
pCombo = (CComboBox *)GetDlgItem(IDC_WAYPOINT_COMBO);
|
||||
EditParameter::loadWaypoints(pCombo);
|
||||
AsciiString homeWaypoint = m_teamDict->getAsciiString(TheKey_teamReinforcementOrigin, &exists);
|
||||
stringNdx = pCombo->AddString(NONE_STRING);
|
||||
if (exists) {
|
||||
Int ndx = pCombo->FindStringExact(-1, homeWaypoint.str());
|
||||
if (ndx != CB_ERR) {
|
||||
stringNdx = ndx;
|
||||
}
|
||||
}
|
||||
pCombo->SetCurSel(stringNdx);
|
||||
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void TeamReinforcement::OnDeployBy()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_DEPLOY_BY);
|
||||
if (pCheck->GetCheck()) {
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(IDC_TRANSPORT_COMBO);
|
||||
pCombo->SetCurSel(0);
|
||||
pCombo->EnableWindow();
|
||||
pCheck = (CButton *) GetDlgItem(IDC_TRANSPORTS_EXIT);
|
||||
pCheck->EnableWindow();
|
||||
} else {
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(IDC_TRANSPORT_COMBO);
|
||||
pCombo->SetCurSel(-1);
|
||||
pCombo->EnableWindow(false);
|
||||
pCheck = (CButton *) GetDlgItem(IDC_TRANSPORTS_EXIT);
|
||||
pCheck->EnableWindow(false);
|
||||
m_teamDict->setAsciiString(TheKey_teamTransport, AsciiString::TheEmptyString);
|
||||
}
|
||||
}
|
||||
|
||||
void TeamReinforcement::OnTeamStartsFull()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_TEAM_STARTS_FULL);
|
||||
Bool checked = pCheck->GetCheck()==1;
|
||||
m_teamDict->setBool(TheKey_teamStartsFull, checked);
|
||||
}
|
||||
|
||||
void TeamReinforcement::OnSelchangeTransportCombo()
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_TRANSPORT_COMBO);
|
||||
CString txt;
|
||||
Int curSel = pCombo->GetCurSel();
|
||||
pCombo->GetLBText(curSel, txt);
|
||||
AsciiString comboText = AsciiString(txt);
|
||||
if (comboText == NONE_STRING) {
|
||||
comboText.clear();
|
||||
}
|
||||
m_teamDict->setAsciiString(TheKey_teamTransport, comboText);
|
||||
}
|
||||
|
||||
void TeamReinforcement::OnTransportsExit()
|
||||
{
|
||||
CButton *pCheck = (CButton *) GetDlgItem(IDC_TRANSPORTS_EXIT);
|
||||
Bool checked = pCheck->GetCheck()==1;
|
||||
m_teamDict->setBool(TheKey_teamTransportsExit, checked);
|
||||
}
|
||||
|
||||
void TeamReinforcement::OnSelchangeVeterancy()
|
||||
{
|
||||
CComboBox *owner = (CComboBox*)GetDlgItem(IDC_VETERANCY);
|
||||
static char buf[1024];
|
||||
int curSel = owner->GetCurSel();
|
||||
int value = 0;
|
||||
if (curSel >= 0) {
|
||||
value=curSel;
|
||||
}
|
||||
m_teamDict->setInt(TheKey_teamVeterancy, value);
|
||||
}
|
||||
|
||||
void TeamReinforcement::OnSelchangeWaypointCombo()
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_WAYPOINT_COMBO);
|
||||
CString txt;
|
||||
Int curSel = pCombo->GetCurSel();
|
||||
if (curSel >= 0) {
|
||||
pCombo->GetLBText(curSel, txt);
|
||||
}
|
||||
AsciiString comboText = AsciiString(txt);
|
||||
if (comboText == NONE_STRING) {
|
||||
comboText.clear();
|
||||
}
|
||||
m_teamDict->setAsciiString(TheKey_teamReinforcementOrigin, comboText);
|
||||
}
|
||||
|
||||
554
GeneralsMD/Code/Tools/WorldBuilder/src/TerrainMaterial.cpp
Normal file
554
GeneralsMD/Code/Tools/WorldBuilder/src/TerrainMaterial.cpp
Normal file
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TerrainMaterial.cpp : implementation file
|
||||
//
|
||||
|
||||
#define DEFINE_TERRAIN_TYPE_NAMES
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "terrainmaterial.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "TileTool.h"
|
||||
#include "WBView3D.h"
|
||||
#include "Common/TerrainTypes.h"
|
||||
#include "W3DDevice/GameClient/TerrainTex.h"
|
||||
#include "W3DDevice/GameClient/HeightMap.h"
|
||||
|
||||
TerrainMaterial *TerrainMaterial::m_staticThis = NULL;
|
||||
|
||||
static Int defaultMaterialIndex = 0;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TerrainMaterial dialog
|
||||
|
||||
Int TerrainMaterial::m_currentFgTexture(3);
|
||||
Int TerrainMaterial::m_currentBgTexture(6);
|
||||
|
||||
Bool TerrainMaterial::m_paintingPathingInfo;
|
||||
Bool TerrainMaterial::m_paintingPassable;
|
||||
|
||||
TerrainMaterial::TerrainMaterial(CWnd* pParent /*=NULL*/) :
|
||||
m_updating(false),
|
||||
m_currentWidth(3)
|
||||
{
|
||||
//{{AFX_DATA_INIT(TerrainMaterial)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void TerrainMaterial::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(TerrainMaterial)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(TerrainMaterial, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(TerrainMaterial)
|
||||
ON_BN_CLICKED(IDC_SWAP_TEXTURES, OnSwapTextures)
|
||||
ON_EN_CHANGE(IDC_SIZE_EDIT, OnChangeSizeEdit)
|
||||
ON_BN_CLICKED(IDC_IMPASSABLE, OnImpassable)
|
||||
ON_BN_CLICKED(IDC_PASSABLE_CHECK, OnPassableCheck)
|
||||
ON_BN_CLICKED(IDC_PASSABLE, OnPassable)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TerrainMaterial data access method.
|
||||
|
||||
/// Set foreground texture and invalidate swatches.
|
||||
void TerrainMaterial::setFgTexClass(Int texClass)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
m_staticThis->m_currentFgTexture=texClass;
|
||||
m_staticThis->m_terrainSwatches.Invalidate();
|
||||
updateTextureSelection();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Set backgroundground texture and invalidate swatches.
|
||||
void TerrainMaterial::setBgTexClass(Int texClass)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
m_staticThis->m_currentBgTexture=texClass;
|
||||
m_staticThis->m_terrainSwatches.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the setWidth value in the dialog.
|
||||
/** Update the value in the edit control and the slider. */
|
||||
void TerrainMaterial::setWidth(Int width)
|
||||
{
|
||||
CString buf;
|
||||
buf.Format("%d", width);
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
m_staticThis->m_currentWidth = width;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the tool option - single & multi tile use this panel,
|
||||
// and only multi tile uses the width.
|
||||
/** Update the ui for the tool. */
|
||||
void TerrainMaterial::setToolOptions(Bool singleCell)
|
||||
{
|
||||
CString buf;
|
||||
if (m_staticThis ) {
|
||||
m_staticThis->m_updating = true;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) {
|
||||
pEdit->EnableWindow(!singleCell);
|
||||
if (singleCell) {
|
||||
pEdit->SetWindowText("1");
|
||||
}
|
||||
}
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_SIZE_POPUP);
|
||||
if (pEdit) {
|
||||
pEdit->EnableWindow(!singleCell);
|
||||
}
|
||||
m_staticThis->m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainMaterial::updateLabel(void)
|
||||
{
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (!pDoc) return;
|
||||
|
||||
AsciiString name = pDoc->GetHeightMap()->getTexClassUiName(m_currentFgTexture);
|
||||
const char *tName = name.str();
|
||||
if (tName == NULL || tName[0] == 0) {
|
||||
tName = pDoc->GetHeightMap()->getTexClassUiName(m_currentFgTexture).str();
|
||||
}
|
||||
if (tName == NULL) {
|
||||
return;
|
||||
}
|
||||
const char *leaf = tName;
|
||||
while (*tName) {
|
||||
if ((tName[0] == '\\' || tName[0] == '/')&& tName[1]) {
|
||||
leaf = tName+1;
|
||||
}
|
||||
tName++;
|
||||
}
|
||||
CWnd *pLabel = GetDlgItem(IDC_TERRAIN_NAME);
|
||||
if (pLabel) {
|
||||
pLabel->SetWindowText(leaf);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainMaterial::updateTextureSelection(void)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
m_staticThis->setTerrainTreeViewSelection(TVI_ROOT, m_staticThis->m_currentFgTexture);
|
||||
m_staticThis->updateLabel();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the selected texture in the tree view.
|
||||
Bool TerrainMaterial::setTerrainTreeViewSelection(HTREEITEM parent, Int selection)
|
||||
{
|
||||
TVITEM item;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&item, 0, sizeof(item));
|
||||
HTREEITEM child = m_terrainTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM;
|
||||
item.hItem = child;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_terrainTreeView.GetItem(&item);
|
||||
if (item.lParam == selection) {
|
||||
m_terrainTreeView.SelectItem(child);
|
||||
return(true);
|
||||
}
|
||||
if (setTerrainTreeViewSelection(child, selection)) {
|
||||
return(true);
|
||||
}
|
||||
child = m_terrainTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TerrainMaterial message handlers
|
||||
|
||||
/// Setup the controls in the dialog.
|
||||
BOOL TerrainMaterial::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
CWnd *pWnd = GetDlgItem(IDC_TERRAIN_TREEVIEW);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_terrainTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_TERRAIN_TREEVIEW);
|
||||
m_terrainTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
pWnd = GetDlgItem(IDC_TERRAIN_SWATCHES);
|
||||
pWnd->GetWindowRect(&rect);
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_terrainSwatches.Create(NULL, "", WS_CHILD, rect, this, IDC_TERRAIN_SWATCHES);
|
||||
m_terrainSwatches.ShowWindow(SW_SHOW);
|
||||
|
||||
m_paintingPathingInfo = false;
|
||||
m_paintingPassable = false;
|
||||
CButton *button = (CButton *)GetDlgItem(IDC_PASSABLE_CHECK);
|
||||
button->SetCheck(false);
|
||||
button = (CButton *)GetDlgItem(IDC_PASSABLE);
|
||||
button->SetCheck(false);
|
||||
button->EnableWindow(false);
|
||||
button = (CButton *)GetDlgItem(IDC_IMPASSABLE);
|
||||
button->SetCheck(true);
|
||||
button->EnableWindow(false);
|
||||
|
||||
m_widthPopup.SetupPopSliderButton(this, IDC_SIZE_POPUP, this);
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
setWidth(m_currentWidth);
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
HTREEITEM TerrainMaterial::findOrAdd(HTREEITEM parent, char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = m_terrainTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
m_terrainTreeView.GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = m_terrainTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_LAST;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = m_terrainTreeView.InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
/** Add the terrain path to the tree view. */
|
||||
void TerrainMaterial::addTerrain(char *pPath, Int terrainNdx, HTREEITEM parent)
|
||||
{
|
||||
TerrainType *terrain = TheTerrainTypes->findTerrain( WorldHeightMapEdit::getTexClassName( terrainNdx ) );
|
||||
Bool doAdd = FALSE;
|
||||
char buffer[_MAX_PATH];
|
||||
//
|
||||
// if we have a 'terrain' entry, it means that our terrain index was properly defined
|
||||
// in an INI file, otherwise it was from eval textures. We will sort all of
|
||||
// the eval texture entries in a tree leaf all their own while the others are
|
||||
// sorted according to a field specified in INI
|
||||
//
|
||||
if( terrain )
|
||||
{
|
||||
if (terrain->isBlendEdge()) {
|
||||
return; // Don't add blend edges to the materials list.
|
||||
}
|
||||
for( TerrainClass i = TERRAIN_NONE; i < TERRAIN_NUM_CLASSES; i = (TerrainClass)(i + 1) )
|
||||
{
|
||||
|
||||
if( terrain->getClass() == i )
|
||||
{
|
||||
|
||||
parent = findOrAdd( parent, terrainTypeNames[ i ] );
|
||||
break; // exit for
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
// set the name in the tree view to that of the entry
|
||||
strcpy( buffer, terrain->getName().str() );
|
||||
|
||||
doAdd = TRUE;
|
||||
} // end if
|
||||
else if (!WorldHeightMapEdit::getTexClassIsBlendEdge(terrainNdx))
|
||||
{
|
||||
|
||||
// all these old entries we will put in a tree for eval textures
|
||||
parent = findOrAdd( parent, "**Eval" );
|
||||
Int i=0;
|
||||
while (pPath[i] && i<sizeof(buffer)) {
|
||||
if (pPath[i] == 0) {
|
||||
return;
|
||||
}
|
||||
if (pPath[i] == '\\' || pPath[i] == '/') {
|
||||
if (i>0 && (i>1 || buffer[0]!='.') ) { // skip the "." directory.
|
||||
buffer[i] = 0;
|
||||
parent = findOrAdd(parent, buffer);
|
||||
}
|
||||
pPath+= i+1;
|
||||
i = 0;
|
||||
}
|
||||
buffer[i] = pPath[i];
|
||||
buffer[i+1] = 0; // terminate at next character
|
||||
doAdd = TRUE;
|
||||
i++;
|
||||
}
|
||||
} // end else
|
||||
|
||||
Int tilesPerRow = TEXTURE_WIDTH/(2*TILE_PIXEL_EXTENT+TILE_OFFSET);
|
||||
Int availableTiles = 4 * tilesPerRow * tilesPerRow;
|
||||
Int percent = (WorldHeightMapEdit::getTexClassNumTiles(terrainNdx)*100 + availableTiles/2) / availableTiles;
|
||||
|
||||
char label[_MAX_PATH];
|
||||
sprintf(label, "%d%% %s", percent, buffer);
|
||||
|
||||
|
||||
if( doAdd )
|
||||
{
|
||||
if (percent<3 && defaultMaterialIndex==0) {
|
||||
defaultMaterialIndex = terrainNdx;
|
||||
}
|
||||
TVINSERTSTRUCT ins;
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_LAST;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = terrainNdx;
|
||||
ins.item.pszText = label;
|
||||
ins.item.cchTextMax = strlen(label)+2;
|
||||
m_terrainTreeView.InsertItem(&ins);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//* Create the tree view of textures from the textures in pMap. */
|
||||
void TerrainMaterial::updateTextures(WorldHeightMapEdit *pMap)
|
||||
{
|
||||
#if 1
|
||||
if (m_staticThis) {
|
||||
m_staticThis->m_updating = true;
|
||||
m_staticThis->m_terrainTreeView.DeleteAllItems();
|
||||
Int i;
|
||||
for (i=0; i<pMap->getNumTexClasses(); i++) {
|
||||
char path[_MAX_PATH];
|
||||
AsciiString uiName = pMap->getTexClassUiName(i);
|
||||
strncpy(path, uiName.str(), _MAX_PATH-2);
|
||||
m_staticThis->addTerrain(path, i, TVI_ROOT);
|
||||
}
|
||||
m_staticThis->m_updating = false;
|
||||
m_staticThis->m_currentFgTexture = defaultMaterialIndex;
|
||||
updateTextureSelection();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Swap the foreground and background textures. */
|
||||
void TerrainMaterial::OnSwapTextures()
|
||||
{
|
||||
|
||||
Int tmp = m_currentFgTexture;
|
||||
m_currentFgTexture = m_currentBgTexture;
|
||||
m_currentBgTexture = tmp;
|
||||
m_terrainSwatches.Invalidate();
|
||||
updateTextureSelection();
|
||||
}
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void TerrainMaterial::OnChangeSizeEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int width;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &width)) {
|
||||
m_currentWidth = width;
|
||||
BigTileTool::setWidth(m_currentWidth);
|
||||
sprintf(buffer, "%.1f FEET.", m_currentWidth*MAP_XY_FACTOR);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_WIDTH_LABEL);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainMaterial::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_SIZE_POPUP:
|
||||
*pMin = MIN_TILE_SIZE;
|
||||
*pMax = MAX_TILE_SIZE;
|
||||
*pInitial = m_currentWidth;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
|
||||
void TerrainMaterial::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit;
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_SIZE_POPUP:
|
||||
m_currentWidth = theVal;
|
||||
str.Format("%d",m_currentWidth);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
BigTileTool::setWidth(m_currentWidth);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void TerrainMaterial::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_SIZE_POPUP:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOL TerrainMaterial::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
if (pHdr->hdr.hwndFrom == m_terrainTreeView.m_hWnd) {
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
HTREEITEM hItem = m_terrainTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM;
|
||||
item.hItem = hItem;
|
||||
m_terrainTreeView.GetItem(&item);
|
||||
if (item.lParam >= 0) {
|
||||
Int texClass = item.lParam;
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (!pDoc) return 0;
|
||||
|
||||
WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
if (!pMap) return 0;
|
||||
if (m_updating) return 0;
|
||||
if (pMap->canFitTexture(texClass)) {
|
||||
m_currentFgTexture = texClass;
|
||||
updateLabel();
|
||||
m_terrainSwatches.Invalidate();
|
||||
} else {
|
||||
if (m_currentFgTexture != texClass) {
|
||||
// Tried to switch to a too large texture.
|
||||
::AfxMessageBox(IDS_TEXTURE_TOO_LARGE);
|
||||
::AfxGetMainWnd()->SetFocus();
|
||||
}
|
||||
m_currentFgTexture = texClass;
|
||||
updateLabel();
|
||||
m_terrainSwatches.Invalidate();
|
||||
}
|
||||
} else if (!(item.state & TVIS_EXPANDEDONCE) ) {
|
||||
HTREEITEM child = m_terrainTreeView.GetChildItem(hItem);
|
||||
while (child != NULL) {
|
||||
hItem = child;
|
||||
child = m_terrainTreeView.GetChildItem(hItem);
|
||||
}
|
||||
if (hItem != m_terrainTreeView.GetSelectedItem()) {
|
||||
m_terrainTreeView.SelectItem(hItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
void TerrainMaterial::OnImpassable()
|
||||
{
|
||||
m_paintingPassable = false;
|
||||
CButton *button = (CButton *)GetDlgItem(IDC_PASSABLE);
|
||||
button->SetCheck(0);
|
||||
}
|
||||
|
||||
void TerrainMaterial::OnPassableCheck()
|
||||
{
|
||||
CButton *owner = (CButton*) GetDlgItem(IDC_PASSABLE_CHECK);
|
||||
Bool isChecked = (owner->GetCheck() != 0);
|
||||
CButton *button = (CButton *)GetDlgItem(IDC_PASSABLE);
|
||||
button->EnableWindow(isChecked);
|
||||
button = (CButton *)GetDlgItem(IDC_IMPASSABLE);
|
||||
button->EnableWindow(isChecked);
|
||||
Bool showImpassable = false;
|
||||
if (TheTerrainRenderObject) {
|
||||
showImpassable = TheTerrainRenderObject->getShowImpassableAreas();
|
||||
}
|
||||
m_terrainSwatches.EnableWindow(!isChecked);
|
||||
m_terrainTreeView.EnableWindow(!isChecked);
|
||||
m_paintingPathingInfo = isChecked;
|
||||
if (showImpassable != isChecked) {
|
||||
TheTerrainRenderObject->setShowImpassableAreas(isChecked);
|
||||
// Force the entire terrain mesh to be rerendered.
|
||||
IRegion2D range = {0,0,0,0};
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc) {
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
if (p3View) {
|
||||
p3View->updateHeightMapInView(pDoc->GetHeightMap(), false, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainMaterial::OnPassable()
|
||||
{
|
||||
m_paintingPassable = true;
|
||||
CButton *button = (CButton *)GetDlgItem(IDC_IMPASSABLE);
|
||||
button->SetCheck(0);
|
||||
}
|
||||
300
GeneralsMD/Code/Tools/WorldBuilder/src/TerrainModal.cpp
Normal file
300
GeneralsMD/Code/Tools/WorldBuilder/src/TerrainModal.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TerrainModal.cpp : implementation file
|
||||
//
|
||||
|
||||
#define DEFINE_TERRAIN_TYPE_NAMES
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "TerrainModal.h"
|
||||
#include "terrainmaterial.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "Common/TerrainTypes.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TerrainModal dialog
|
||||
|
||||
|
||||
TerrainModal::TerrainModal(AsciiString path, WorldHeightMapEdit *pMap, CWnd* pParent /*=NULL*/)
|
||||
: CDialog(TerrainModal::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(TerrainModal)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
m_pathToReplace = path;
|
||||
m_map = pMap;
|
||||
}
|
||||
|
||||
|
||||
void TerrainModal::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(TerrainModal)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
void TerrainModal::updateLabel(void)
|
||||
{
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (!pDoc) return;
|
||||
|
||||
const char *tName = pDoc->GetHeightMap()->getTexClassUiName(m_currentFgTexture).str();
|
||||
if (tName == NULL || tName[0] == 0) {
|
||||
tName = pDoc->GetHeightMap()->getTexClassUiName(m_currentFgTexture).str();
|
||||
}
|
||||
if (tName == NULL) {
|
||||
return;
|
||||
}
|
||||
const char *leaf = tName;
|
||||
while (*tName) {
|
||||
if ((tName[0] == '\\' || tName[0] == '/')&& tName[1]) {
|
||||
leaf = tName+1;
|
||||
}
|
||||
tName++;
|
||||
}
|
||||
CWnd *pLabel = GetDlgItem(IDC_TERRAIN_NAME);
|
||||
if (pLabel) {
|
||||
pLabel->SetWindowText(leaf);
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup the controls in the dialog.
|
||||
BOOL TerrainModal::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CWnd *pWnd = GetDlgItem(IDC_TERRAIN_TREEVIEW);
|
||||
CRect rect;
|
||||
pWnd->GetWindowRect(&rect);
|
||||
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_terrainTreeView.Create(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|
|
||||
TVS_SHOWSELALWAYS|TVS_DISABLEDRAGDROP, rect, this, IDC_TERRAIN_TREEVIEW);
|
||||
m_terrainTreeView.ShowWindow(SW_SHOW);
|
||||
|
||||
pWnd = GetDlgItem(IDC_TERRAIN_SWATCHES);
|
||||
pWnd->GetWindowRect(&rect);
|
||||
ScreenToClient(&rect);
|
||||
rect.DeflateRect(2,2,2,2);
|
||||
m_terrainSwatches.Create(NULL, "", WS_CHILD, rect, this, IDC_TERRAIN_SWATCHES);
|
||||
m_terrainSwatches.ShowWindow(SW_SHOW);
|
||||
|
||||
pWnd = GetDlgItem(IDC_MISSING_NAME);
|
||||
if (pWnd) pWnd->SetWindowText(m_pathToReplace.str());
|
||||
m_currentFgTexture = 0;
|
||||
updateTextures();
|
||||
TerrainMaterial::setFgTexClass(m_currentFgTexture);
|
||||
SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/** Locate the child item in tree item parent with name pLabel. If not
|
||||
found, add it. Either way, return child. */
|
||||
HTREEITEM TerrainModal::findOrAdd(HTREEITEM parent, char *pLabel)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
HTREEITEM child = m_terrainTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
ins.item.mask = TVIF_HANDLE|TVIF_TEXT;
|
||||
ins.item.hItem = child;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = sizeof(buffer)-2;
|
||||
m_terrainTreeView.GetItem(&ins.item);
|
||||
if (strcmp(buffer, pLabel) == 0) {
|
||||
return(child);
|
||||
}
|
||||
child = m_terrainTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
|
||||
// not found, so add it.
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_LAST;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = -1;
|
||||
ins.item.pszText = pLabel;
|
||||
ins.item.cchTextMax = strlen(pLabel);
|
||||
child = m_terrainTreeView.InsertItem(&ins);
|
||||
return(child);
|
||||
}
|
||||
|
||||
/** Add the terrain path to the tree view. */
|
||||
void TerrainModal::addTerrain(char *pPath, Int terrainNdx, HTREEITEM parent)
|
||||
{
|
||||
TerrainType *terrain = TheTerrainTypes->findTerrain( WorldHeightMapEdit::getTexClassName( terrainNdx ) );
|
||||
Bool doAdd = FALSE;
|
||||
char buffer[_MAX_PATH];
|
||||
|
||||
//
|
||||
// if we have a 'terrain' entry, it means that our terrain index was properly defined
|
||||
// in an INI file, otherwise it was from legacy GDF stuff. We will sort all of
|
||||
// the legacy GDF terrain entries in a tree leaf all their own while the others are
|
||||
// sorted according to a field specified in INI
|
||||
//
|
||||
if( terrain )
|
||||
{
|
||||
|
||||
for( TerrainClass i = TERRAIN_NONE; i < TERRAIN_NUM_CLASSES; i = (TerrainClass)(i + 1) )
|
||||
{
|
||||
|
||||
if( terrain->getClass() == i )
|
||||
{
|
||||
|
||||
parent = findOrAdd( parent, terrainTypeNames[ i ] );
|
||||
break; // exit for
|
||||
|
||||
} // end if
|
||||
|
||||
} // end for i
|
||||
|
||||
strcpy( buffer, terrain->getName().str() );
|
||||
|
||||
doAdd = TRUE;
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
// all these old entries we will put in a tree for legacy GDF items
|
||||
parent = findOrAdd( parent, "**LegacyGDF" );
|
||||
|
||||
Int i=0;
|
||||
while (pPath[i] && i<sizeof(buffer)) {
|
||||
if (pPath[i] == 0) {
|
||||
return;
|
||||
}
|
||||
if (pPath[i] == '\\' || pPath[i] == '/') {
|
||||
if (i>0 && (i>1 || buffer[0]!='.') ) { // skip the "." directory.
|
||||
buffer[i] = 0;
|
||||
parent = findOrAdd(parent, buffer);
|
||||
}
|
||||
pPath+= i+1;
|
||||
i = 0;
|
||||
}
|
||||
buffer[i] = pPath[i];
|
||||
buffer[i+1] = 0; // terminate at next char
|
||||
i++;
|
||||
doAdd = TRUE;
|
||||
}
|
||||
|
||||
} // end else
|
||||
|
||||
if (doAdd)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
|
||||
::memset(&ins, 0, sizeof(ins));
|
||||
ins.hParent = parent;
|
||||
ins.hInsertAfter = TVI_LAST;
|
||||
ins.item.mask = TVIF_PARAM|TVIF_TEXT;
|
||||
ins.item.lParam = terrainNdx;
|
||||
ins.item.pszText = buffer;
|
||||
ins.item.cchTextMax = strlen(buffer)+2;
|
||||
m_terrainTreeView.InsertItem(&ins);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//* Create the tree view of textures from the textures in pMap. */
|
||||
void TerrainModal::updateTextures(void)
|
||||
{
|
||||
m_terrainTreeView.DeleteAllItems();
|
||||
Int i;
|
||||
for (i=0; i<WorldHeightMapEdit::getNumTexClasses(); i++) {
|
||||
if (m_map->isTexClassUsed(i)) {
|
||||
if (m_currentFgTexture == i) {
|
||||
m_currentFgTexture++;
|
||||
}
|
||||
// continue;
|
||||
}
|
||||
const char *tName = WorldHeightMapEdit::getTexClassName(i).str();
|
||||
char path[_MAX_PATH];
|
||||
strncpy(path, tName, _MAX_PATH-2);
|
||||
addTerrain(path, i, TVI_ROOT);
|
||||
}
|
||||
setTerrainTreeViewSelection(TVI_ROOT, m_currentFgTexture);
|
||||
updateLabel();
|
||||
}
|
||||
/// Set the selected texture in the tree view.
|
||||
Bool TerrainModal::setTerrainTreeViewSelection(HTREEITEM parent, Int selection)
|
||||
{
|
||||
TVITEM item;
|
||||
char buffer[_MAX_PATH];
|
||||
::memset(&item, 0, sizeof(item));
|
||||
HTREEITEM child = m_terrainTreeView.GetChildItem(parent);
|
||||
while (child != NULL) {
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM;
|
||||
item.hItem = child;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer)-2;
|
||||
m_terrainTreeView.GetItem(&item);
|
||||
if (item.lParam == selection) {
|
||||
m_terrainTreeView.SelectItem(child);
|
||||
return(true);
|
||||
}
|
||||
if (setTerrainTreeViewSelection(child, selection)) {
|
||||
return(true);
|
||||
}
|
||||
child = m_terrainTreeView.GetNextSiblingItem(child);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL TerrainModal::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||||
{
|
||||
NMTREEVIEW *pHdr = (NMTREEVIEW *)lParam;
|
||||
if (pHdr->hdr.hwndFrom == m_terrainTreeView.m_hWnd) {
|
||||
if (pHdr->hdr.code == TVN_SELCHANGED) {
|
||||
HTREEITEM hItem = m_terrainTreeView.GetSelectedItem();
|
||||
TVITEM item;
|
||||
::memset(&item, 0, sizeof(item));
|
||||
item.mask = TVIF_HANDLE|TVIF_PARAM;
|
||||
item.hItem = hItem;
|
||||
m_terrainTreeView.GetItem(&item);
|
||||
if (item.lParam >= 0) {
|
||||
m_currentFgTexture = item.lParam;
|
||||
TerrainMaterial::setFgTexClass(m_currentFgTexture);
|
||||
updateLabel();
|
||||
m_terrainSwatches.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CDialog::OnNotify(wParam, lParam, pResult);
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(TerrainModal, CDialog)
|
||||
//{{AFX_MSG_MAP(TerrainModal)
|
||||
// NOTE: the ClassWizard will add message map macros here
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TerrainModal message handlers
|
||||
116
GeneralsMD/Code/Tools/WorldBuilder/src/TerrainSwatches.cpp
Normal file
116
GeneralsMD/Code/Tools/WorldBuilder/src/TerrainSwatches.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TerrainSwatches.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "TerrainSwatches.h"
|
||||
#include "TerrainMaterial.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TerrainSwatches
|
||||
|
||||
TerrainSwatches::TerrainSwatches()
|
||||
{
|
||||
}
|
||||
|
||||
TerrainSwatches::~TerrainSwatches()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(TerrainSwatches, CWnd)
|
||||
//{{AFX_MSG_MAP(TerrainSwatches)
|
||||
ON_WM_PAINT()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
#define SWATCH_OFFSET 20
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TerrainSwatches message handlers
|
||||
|
||||
void TerrainSwatches::OnPaint()
|
||||
{
|
||||
CPaintDC dc(this); // device context for painting
|
||||
|
||||
CRect clientRect;
|
||||
GetClientRect(&clientRect);
|
||||
|
||||
CRect bgRect;
|
||||
bgRect = clientRect;
|
||||
bgRect.top = bgRect.bottom-TILE_PIXEL_EXTENT;
|
||||
bgRect.left = bgRect.right-TILE_PIXEL_EXTENT;
|
||||
|
||||
CRect fgRect = clientRect;
|
||||
fgRect.bottom = fgRect.top+TILE_PIXEL_EXTENT;
|
||||
fgRect.right = fgRect.left+TILE_PIXEL_EXTENT;
|
||||
|
||||
CBrush brush;
|
||||
brush.CreateSolidBrush(RGB(0,0,0));
|
||||
|
||||
Int fgTexClass = TerrainMaterial::getFgTexClass();
|
||||
Int bgTexClass = TerrainMaterial::getBgTexClass();
|
||||
UnsignedByte *pData;
|
||||
pData = WorldHeightMapEdit::getPointerToClassTileData(bgTexClass);
|
||||
if (pData) {
|
||||
DrawMyTexture(&dc, bgRect.top, bgRect.left, TILE_PIXEL_EXTENT, pData);
|
||||
} else {
|
||||
dc.FillSolidRect(&bgRect, RGB(0,128,0));
|
||||
}
|
||||
dc.FrameRect(&bgRect, &brush);
|
||||
pData = WorldHeightMapEdit::getPointerToClassTileData(fgTexClass);
|
||||
if (pData) {
|
||||
DrawMyTexture(&dc, fgRect.top, fgRect.left, TILE_PIXEL_EXTENT, pData);
|
||||
} else {
|
||||
dc.FillSolidRect(&fgRect, RGB(128,0,0));
|
||||
}
|
||||
dc.FrameRect(&fgRect, &brush);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void TerrainSwatches::DrawMyTexture(CDC *pDc, int top, int left, Int width, UnsignedByte *rgbData)
|
||||
{
|
||||
// Just blast about some dib bits.
|
||||
|
||||
LPBITMAPINFO pBI;
|
||||
// long bytes = sizeof(BITMAPINFO);
|
||||
pBI = new BITMAPINFO;
|
||||
pBI->bmiHeader.biSize = sizeof(pBI->bmiHeader);
|
||||
pBI->bmiHeader.biWidth = width;
|
||||
pBI->bmiHeader.biHeight = width; /* match display top left == 0,0 */
|
||||
pBI->bmiHeader.biPlanes = 1;
|
||||
pBI->bmiHeader.biBitCount = 32;
|
||||
pBI->bmiHeader.biCompression = BI_RGB;
|
||||
pBI->bmiHeader.biSizeImage = (width*width)*(pBI->bmiHeader.biBitCount/8);
|
||||
pBI->bmiHeader.biXPelsPerMeter = 1000;
|
||||
pBI->bmiHeader.biYPelsPerMeter = 1000;
|
||||
pBI->bmiHeader.biClrUsed = 0;
|
||||
pBI->bmiHeader.biClrImportant = 0;
|
||||
|
||||
//::Sleep(10);
|
||||
/*int val=*/::StretchDIBits(pDc->m_hDC, left, top, width, width, 0, 0, width, width, rgbData, pBI,
|
||||
DIB_RGB_COLORS, SRCCOPY);
|
||||
delete(pBI);
|
||||
}
|
||||
|
||||
|
||||
205
GeneralsMD/Code/Tools/WorldBuilder/src/TileTool.cpp
Normal file
205
GeneralsMD/Code/Tools/WorldBuilder/src/TileTool.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TileTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "TileTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "BrushTool.h"
|
||||
#include "DrawObject.h"
|
||||
//
|
||||
// TileTool class.
|
||||
//
|
||||
|
||||
|
||||
/// Constructor
|
||||
TileTool::TileTool(void) :
|
||||
Tool(ID_TILE_TOOL, IDC_TILE_CURSOR)
|
||||
{
|
||||
m_htMapEditCopy = NULL;
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
TileTool::~TileTool(void)
|
||||
{
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
}
|
||||
|
||||
/// Shows the terrain materials options panel.
|
||||
void TileTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_TERRAIN_MATERIAL);
|
||||
TerrainMaterial::setToolOptions(true);
|
||||
DrawObject::setDoBrushFeedback(true);
|
||||
DrawObject::setBrushFeedbackParms(true, 1, 0);
|
||||
}
|
||||
|
||||
|
||||
/// Common mouse down code for left and right clicks.
|
||||
void TileTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m == TRACK_L)
|
||||
m_textureClassToDraw = TerrainMaterial::getFgTexClass();
|
||||
else if (m == TRACK_R)
|
||||
m_textureClassToDraw = TerrainMaterial::getBgTexClass();
|
||||
else
|
||||
return;
|
||||
|
||||
// WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
// just in case, release it.
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
m_htMapEditCopy = pDoc->GetHeightMap()->duplicate();
|
||||
m_prevXIndex = -1;
|
||||
m_prevYIndex = -1;
|
||||
m_prevViewPt = viewPt;
|
||||
mouseMoved(m, viewPt, pView, pDoc);
|
||||
}
|
||||
|
||||
/// Common mouse up code for left and right clicks.
|
||||
void TileTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L && m != TRACK_R) return;
|
||||
#define DONT_DO_FULL_UPDATE
|
||||
#ifdef DO_FULL_UPDATE
|
||||
m_htMapEditCopy->optimizeTiles(); // force to optimize tileset
|
||||
IRegion2D partialRange = {0,0,0,0};
|
||||
pDoc->updateHeightMap(m_htMapEditCopy, false, partialRange);
|
||||
#endif
|
||||
WBDocUndoable *pUndo = new WBDocUndoable(pDoc, m_htMapEditCopy);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
REF_PTR_RELEASE(m_htMapEditCopy);
|
||||
}
|
||||
|
||||
/// Common mouse moved code for left and right clicks.
|
||||
void TileTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Bool didAnything = false;
|
||||
Bool fullUpdate = false;
|
||||
Coord3D cpt;
|
||||
|
||||
pView->viewToDocCoords(viewPt, &cpt);
|
||||
DrawObject::setFeedbackPos(cpt);
|
||||
if (m != TRACK_L && m != TRACK_R) return;
|
||||
Int dx = m_prevViewPt.x - viewPt.x;
|
||||
Int dy = m_prevViewPt.y - viewPt.y;
|
||||
Int count = sqrt(dx*dx + dy*dy);
|
||||
Int k;
|
||||
|
||||
Int totalMinX = m_htMapEditCopy->getXExtent();
|
||||
Int totalMinY = m_htMapEditCopy->getYExtent();
|
||||
Int totalMaxX = 0;
|
||||
Int totalMaxY = 0;
|
||||
|
||||
count /= 2;
|
||||
if (count<1) count = 1;
|
||||
for (k=0; k<=count; k++) {
|
||||
CPoint curViewPt;
|
||||
curViewPt.x = viewPt.x + ((count-k)*dx)/count;
|
||||
curViewPt.y = viewPt.y + ((count-k)*dy)/count;
|
||||
|
||||
if (k==0) {
|
||||
DEBUG_ASSERTCRASH(curViewPt.x == m_prevViewPt.x, ("Bad x"));
|
||||
DEBUG_ASSERTCRASH(curViewPt.y == m_prevViewPt.y, ("Bad y"));
|
||||
}
|
||||
if (k==count) {
|
||||
DEBUG_ASSERTCRASH(curViewPt.x == viewPt.x, ("Bad x"));
|
||||
DEBUG_ASSERTCRASH(curViewPt.y == viewPt.y, ("Bad y"));
|
||||
}
|
||||
CPoint ndx;
|
||||
Int width = getWidth();
|
||||
pView->viewToDocCoords(curViewPt, &cpt);
|
||||
getCenterIndex(&cpt, width, &ndx, pDoc);
|
||||
if (m_prevXIndex == ndx.x && m_prevYIndex == ndx.y) continue;
|
||||
|
||||
m_prevXIndex = ndx.x;
|
||||
m_prevYIndex = ndx.y;
|
||||
|
||||
Int i, j;
|
||||
Int minX = ndx.x - (width)/2;
|
||||
Int minY = ndx.y - (width)/2;
|
||||
for (i=minX; i<minX+width; i++) {
|
||||
if (i<0 || i>=m_htMapEditCopy->getXExtent()) continue;
|
||||
for (j=minY; j<minY+width; j++) {
|
||||
if (j<0 || j>=m_htMapEditCopy->getYExtent()) continue;
|
||||
if (TerrainMaterial::isPaintingPathingInfo()) {
|
||||
m_htMapEditCopy->setCliff(i, j, !TerrainMaterial::isPaintingPassable());
|
||||
} else {
|
||||
if (m_htMapEditCopy->setTileNdx(i, j, m_textureClassToDraw, width==1)) {
|
||||
fullUpdate = true;
|
||||
}
|
||||
}
|
||||
didAnything = true;
|
||||
if (totalMinX>i) totalMinX = i;
|
||||
if (totalMinY>j) totalMinY = j;
|
||||
if (totalMaxX<i) totalMaxX = i;
|
||||
if (totalMaxY<j) totalMaxY = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (didAnything) {
|
||||
IRegion2D partialRange;
|
||||
partialRange.lo.x = totalMinX;
|
||||
partialRange.hi.x = totalMaxX+1;
|
||||
partialRange.lo.y = totalMinY;
|
||||
partialRange.hi.y = totalMaxY+1;
|
||||
if (fullUpdate) {
|
||||
m_htMapEditCopy->optimizeTiles(); // force to optimize tileset
|
||||
}
|
||||
pDoc->updateHeightMap(m_htMapEditCopy, !fullUpdate, partialRange);
|
||||
}
|
||||
pView->UpdateWindow();
|
||||
m_prevViewPt = viewPt;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** BigTileTool
|
||||
***************************************************************************/
|
||||
Int BigTileTool::m_currentWidth = 0;
|
||||
|
||||
/// Constructor
|
||||
BigTileTool::BigTileTool(void)
|
||||
{
|
||||
m_toolID = ID_BIG_TILE_TOOL;
|
||||
}
|
||||
|
||||
/// Shows the terrain materials options panel.
|
||||
void BigTileTool::setWidth(Int width)
|
||||
{
|
||||
m_currentWidth = width;
|
||||
DrawObject::setBrushFeedbackParms(true, m_currentWidth, 0);
|
||||
}
|
||||
|
||||
/// Shows the terrain materials options panel.
|
||||
void BigTileTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_TERRAIN_MATERIAL);
|
||||
TerrainMaterial::setToolOptions(false);
|
||||
TerrainMaterial::setWidth(m_currentWidth);
|
||||
DrawObject::setDoBrushFeedback(true);
|
||||
DrawObject::setBrushFeedbackParms(true, m_currentWidth, 0);
|
||||
}
|
||||
151
GeneralsMD/Code/Tools/WorldBuilder/src/Tool.cpp
Normal file
151
GeneralsMD/Code/Tools/WorldBuilder/src/Tool.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Tool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
#include "MainFrm.h"
|
||||
#include "DrawObject.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "common/MapObject.h"
|
||||
|
||||
#include "Tool.h"
|
||||
//
|
||||
/// Tool class.
|
||||
//
|
||||
/// Constructor
|
||||
Tool::Tool(Int toolID, Int cursorID)
|
||||
{
|
||||
m_toolID = toolID;
|
||||
m_cursorID = cursorID;
|
||||
m_cursor = NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Destructor
|
||||
Tool::~Tool(void)
|
||||
{
|
||||
if (m_cursor) {
|
||||
::DestroyCursor(m_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Shows the "no options" options panel.
|
||||
void Tool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_NO_OPTIONS);
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
}
|
||||
|
||||
|
||||
void Tool::setCursor(void)
|
||||
{
|
||||
if (m_cursor == NULL) {
|
||||
m_cursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(m_cursorID));
|
||||
}
|
||||
::SetCursor(m_cursor);
|
||||
}
|
||||
|
||||
/// Calculate the round blend factor.
|
||||
/** Calculates the blend amount of the brush. 1.0 means the brush sets the
|
||||
height, 0.0 means no change, and between blends proportionally. */
|
||||
Real Tool::calcRoundBlendFactor(CPoint center, Int x, Int y, Int brushWidth, Int featherWidth)
|
||||
{
|
||||
Real offset = 0;
|
||||
if (brushWidth&1) offset = 0.5;
|
||||
const Real CLOSE_ENOUGH = 0.05f; // We are working on an integer grid.
|
||||
|
||||
Real dx = abs(center.x+offset-x);
|
||||
Real dy = abs(center.y+offset-y);
|
||||
|
||||
Real dist = (Real)sqrt(dx*dx+dy*dy);
|
||||
|
||||
if (dist <= (brushWidth/2.0f)+CLOSE_ENOUGH) return (1.0f);
|
||||
|
||||
dist -= (brushWidth/2.0f);
|
||||
|
||||
if (featherWidth < 1) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
if (dist <= featherWidth) {
|
||||
return (featherWidth-dist)/featherWidth;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/// Calculate the square blend factor.
|
||||
/** Calculates the blend amount of the brush. 1.0 means the brush sets the
|
||||
height, 0.0 means no change, and between blends proportionally. */
|
||||
Real Tool::calcSquareBlendFactor(CPoint center, Int x, Int y, Int brushWidth, Int featherWidth)
|
||||
{
|
||||
Real offset = 0;
|
||||
if (brushWidth&1) offset = 0.5;
|
||||
|
||||
Real dx = abs(center.x+offset-x);
|
||||
Real dy = abs(center.y+offset-y);
|
||||
|
||||
Real dist = dx;
|
||||
if (dy>dist) dist = dy;
|
||||
|
||||
if (dist <= (brushWidth/2.0f)) return (1.0f);
|
||||
|
||||
dist -= (brushWidth/2.0f);
|
||||
|
||||
if (featherWidth < 1) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
if (dist <= featherWidth) {
|
||||
return (featherWidth-dist)/featherWidth;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/// Gets the cell index for the center of the brush.
|
||||
/** Converts from document coordinates to cell index coordinates. */
|
||||
void Tool::getCenterIndex(Coord3D *docLocP, Int brushWidth, CPoint *center, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D cpt = *docLocP;
|
||||
// center on half pixel for even widths.
|
||||
if (!(brushWidth&1)) {
|
||||
cpt.x += MAP_XY_FACTOR/2;
|
||||
cpt.y += MAP_XY_FACTOR/2;
|
||||
}
|
||||
if (!pDoc->getCellIndexFromCoord(cpt, center)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Tool::getAllIndexesIn(const Coord3D *bl, const Coord3D *br,
|
||||
const Coord3D *tl, const Coord3D *tr,
|
||||
Int widthOutside, CWorldBuilderDoc *pDoc,
|
||||
VecHeightMapIndexes* allIndices)
|
||||
{
|
||||
if (!(bl && br && tl && tr && pDoc && allIndices)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pDoc->getAllIndexesInRect(bl, br, tl, tr, widthOutside, allIndices);
|
||||
}
|
||||
178
GeneralsMD/Code/Tools/WorldBuilder/src/WBFrameWnd.cpp
Normal file
178
GeneralsMD/Code/Tools/WorldBuilder/src/WBFrameWnd.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// WBFrameWnd.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "MainFrm.h"
|
||||
#include "WBFrameWnd.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WbView3d.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CWBFrameWnd
|
||||
|
||||
IMPLEMENT_DYNCREATE(CWBFrameWnd, CFrameWnd)
|
||||
|
||||
CWBFrameWnd::CWBFrameWnd()
|
||||
{
|
||||
}
|
||||
|
||||
CWBFrameWnd::~CWBFrameWnd()
|
||||
{
|
||||
}
|
||||
|
||||
BOOL CWBFrameWnd::LoadFrame(UINT nIDResource,
|
||||
DWORD dwDefaultStyle,
|
||||
CWnd* pParentWnd,
|
||||
CCreateContext* pContext) {
|
||||
//dwDefaultStyle &= ~(WS_SIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU);
|
||||
|
||||
BOOL ret = CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle, CMainFrame::GetMainFrame(), pContext);
|
||||
if (ret) {
|
||||
Int top = ::AfxGetApp()->GetProfileInt(TWO_D_WINDOW_SECTION, "Top", 10);
|
||||
Int left =::AfxGetApp()->GetProfileInt(TWO_D_WINDOW_SECTION, "Left", 10);
|
||||
this->SetWindowPos(NULL, left,
|
||||
top, 0, 0,
|
||||
SWP_NOZORDER|SWP_NOSIZE);
|
||||
if (!m_cellSizeToolBar.Create(this, IDD_CELL_SLIDER, CBRS_LEFT, IDD_CELL_SLIDER))
|
||||
{
|
||||
DEBUG_CRASH(("Failed to create toolbar\n"));
|
||||
}
|
||||
EnableDocking(CBRS_ALIGN_ANY);
|
||||
m_cellSizeToolBar.SetupSlider();
|
||||
m_cellSizeToolBar.EnableDocking(CBRS_ALIGN_ANY);
|
||||
DockControlBar(&m_cellSizeToolBar);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void CWBFrameWnd::OnMove(int x, int y)
|
||||
{
|
||||
CFrameWnd::OnMove(x, y);
|
||||
if (this->IsWindowVisible() && !this->IsIconic()) {
|
||||
CRect frameRect;
|
||||
GetWindowRect(&frameRect);
|
||||
::AfxGetApp()->WriteProfileInt(TWO_D_WINDOW_SECTION, "Top", frameRect.top);
|
||||
::AfxGetApp()->WriteProfileInt(TWO_D_WINDOW_SECTION, "Left", frameRect.left);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(CWBFrameWnd, CFrameWnd)
|
||||
//{{AFX_MSG_MAP(CWBFrameWnd)
|
||||
ON_WM_MOVE()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CWBFrameWnd message handlers
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CWB3dFrameWnd
|
||||
|
||||
IMPLEMENT_DYNCREATE(CWB3dFrameWnd, CMainFrame)
|
||||
|
||||
CWB3dFrameWnd::CWB3dFrameWnd()
|
||||
{
|
||||
}
|
||||
|
||||
CWB3dFrameWnd::~CWB3dFrameWnd()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(CWB3dFrameWnd, CMainFrame)
|
||||
//{{AFX_MSG_MAP(CWB3dFrameWnd)
|
||||
// NOTE - the ClassWizard will add and remove mapping macros here.
|
||||
ON_WM_MOVE()
|
||||
ON_COMMAND(ID_WINDOW_PREVIEW1024X768, OnWindowPreview1024x768)
|
||||
ON_UPDATE_COMMAND_UI(ID_WINDOW_PREVIEW1024X768, OnUpdateWindowPreview1024x768)
|
||||
ON_COMMAND(ID_WINDOW_PREVIEW640X480, OnWindowPreview640x480)
|
||||
ON_UPDATE_COMMAND_UI(ID_WINDOW_PREVIEW640X480, OnUpdateWindowPreview640x480)
|
||||
ON_COMMAND(ID_WINDOW_PREVIEW800X600, OnWindowPreview800x600)
|
||||
ON_UPDATE_COMMAND_UI(ID_WINDOW_PREVIEW800X600, OnUpdateWindowPreview800x600)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CWB3dFrameWnd message handlers
|
||||
BOOL CWB3dFrameWnd::LoadFrame(UINT nIDResource,
|
||||
DWORD dwDefaultStyle,
|
||||
CWnd* pParentWnd,
|
||||
CCreateContext* pContext) {
|
||||
dwDefaultStyle &= ~(WS_SIZEBOX);
|
||||
|
||||
BOOL ret = CMainFrame::LoadFrame(nIDResource, dwDefaultStyle, CMainFrame::GetMainFrame(), pContext);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void CWB3dFrameWnd::OnMove(int x, int y)
|
||||
{
|
||||
CFrameWnd::OnMove(x, y);
|
||||
if (this->IsWindowVisible() && !this->IsIconic()) {
|
||||
CRect frameRect;
|
||||
GetWindowRect(&frameRect);
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Top", frameRect.top);
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Left", frameRect.left);
|
||||
}
|
||||
}
|
||||
|
||||
void CWB3dFrameWnd::OnWindowPreview1024x768()
|
||||
{
|
||||
if (m_3dViewWidth == 1024) return;
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Width", 1024);
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Height", 768);
|
||||
adjustWindowSize();
|
||||
}
|
||||
|
||||
void CWB3dFrameWnd::OnUpdateWindowPreview1024x768(CCmdUI* pCmdUI)
|
||||
{
|
||||
pCmdUI->SetCheck(m_3dViewWidth==1024?1:0);
|
||||
}
|
||||
|
||||
void CWB3dFrameWnd::OnWindowPreview640x480()
|
||||
{
|
||||
if (m_3dViewWidth == 640) return;
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Width", 640);
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Height", 480);
|
||||
adjustWindowSize();
|
||||
}
|
||||
|
||||
void CWB3dFrameWnd::OnUpdateWindowPreview640x480(CCmdUI* pCmdUI)
|
||||
{
|
||||
pCmdUI->SetCheck(m_3dViewWidth==640?1:0);
|
||||
}
|
||||
|
||||
void CWB3dFrameWnd::OnWindowPreview800x600()
|
||||
{
|
||||
if (m_3dViewWidth == 800) return;
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Width", 800);
|
||||
::AfxGetApp()->WriteProfileInt(MAIN_FRAME_SECTION, "Height", 600);
|
||||
adjustWindowSize();
|
||||
}
|
||||
|
||||
void CWB3dFrameWnd::OnUpdateWindowPreview800x600(CCmdUI* pCmdUI)
|
||||
{
|
||||
pCmdUI->SetCheck(m_3dViewWidth==800?1:0);
|
||||
}
|
||||
317
GeneralsMD/Code/Tools/WorldBuilder/src/WBHeightMap.cpp
Normal file
317
GeneralsMD/Code/Tools/WorldBuilder/src/WBHeightMap.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FILE: Heightmap.cpp ////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: Heightmap.cpp
|
||||
//
|
||||
// Created: Mark W., John Ahlquist, April/May 2001
|
||||
//
|
||||
// Desc: Draw the terrain and scorchmarks in a scene.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "WBheightmap.h"
|
||||
#include "common/GlobalData.h"
|
||||
#include <tri.h>
|
||||
#include <colmath.h>
|
||||
#include <coltest.h>
|
||||
|
||||
#define dontUSE_FLAT_HEIGHT_MAP
|
||||
//-----------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//=============================================================================
|
||||
// WBHeightMap::WBHeightMap
|
||||
//=============================================================================
|
||||
WBHeightMap::WBHeightMap() :
|
||||
m_drawEntireMap(false),
|
||||
m_flattenHeights(false)
|
||||
{
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// WBHeightMap::Render
|
||||
//=============================================================================
|
||||
/** Renders (draws) the terrain. */
|
||||
//=============================================================================
|
||||
void WBHeightMap::setFlattenHeights(Bool flat)
|
||||
{
|
||||
if (m_flattenHeights != flat) {
|
||||
m_flattenHeights = flat;
|
||||
#ifndef USE_FLAT_HEIGHT_MAP
|
||||
m_originX = 0;
|
||||
m_originY = 0;
|
||||
updateBlock(0, 0, m_x-1, m_y-1, m_map, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// THE_Z is just above the water plane, so the flattened terrain doesn't draw
|
||||
// under water.
|
||||
#define THE_Z (10)
|
||||
//=============================================================================
|
||||
// WBHeightMap::flattenHeights
|
||||
//=============================================================================
|
||||
/** Flattens the terrain for the top down view.. */
|
||||
//=============================================================================
|
||||
void WBHeightMap::flattenHeights(void) {
|
||||
#ifndef USE_FLAT_HEIGHT_MAP
|
||||
Real theZ = THE_Z;
|
||||
Int i, j;
|
||||
for (j=0; j<m_numVBTilesY; j++)
|
||||
for (i=0; i<m_numVBTilesX; i++)
|
||||
{
|
||||
static int count = 0;
|
||||
count++;
|
||||
Int numVertex = (VERTEX_BUFFER_TILE_LENGTH*2)*(VERTEX_BUFFER_TILE_LENGTH*2);
|
||||
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexBufferTiles[j*m_numVBTilesX+i]);
|
||||
VERTEX_FORMAT *vbHardware = (VERTEX_FORMAT*)lockVtxBuffer.Get_Vertex_Array();
|
||||
Int vtx;
|
||||
for (vtx=0; vtx<numVertex; vtx++) {
|
||||
vbHardware->z = theZ;
|
||||
vbHardware++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// WBHeightMap::getMaxCellHeight
|
||||
//=============================================================================
|
||||
/** Returns maximum height of the 4 corners containing the given point */
|
||||
//=============================================================================
|
||||
Real WBHeightMap::getMaxCellHeight(Real x, Real y)
|
||||
{
|
||||
if (!m_flattenHeights) {
|
||||
return BaseHeightMapRenderObjClass::getMaxCellHeight(x,y);
|
||||
}
|
||||
// If we are flattening the height, all z values aret THE_Z. jba.
|
||||
return THE_Z;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// WBHeightMap::getHeight
|
||||
//=============================================================================
|
||||
/** return the height and normal of the triangle plane containing given location within heightmap. */
|
||||
//=============================================================================
|
||||
Real WBHeightMap::getHeightMapHeight(Real x, Real y, Coord3D* normal)
|
||||
{
|
||||
if (!m_flattenHeights) {
|
||||
return BaseHeightMapRenderObjClass::getHeightMapHeight(x,y,normal);
|
||||
}
|
||||
// If we are flattening the height, all z values aret THE_Z. jba.
|
||||
if (normal) {
|
||||
normal->x = 0;
|
||||
normal->y = 0;
|
||||
normal->z = 1;
|
||||
}
|
||||
return THE_Z;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// WBHeightMap::Cast_Ray
|
||||
//=============================================================================
|
||||
/** Return intersection of a ray with the heightmap mesh.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
Bool WBHeightMap::Cast_Ray(RayCollisionTestClass & raytest)
|
||||
{
|
||||
if (!m_flattenHeights) {
|
||||
return BaseHeightMapRenderObjClass::Cast_Ray(raytest);
|
||||
}
|
||||
Real theZ = THE_Z;
|
||||
TriClass tri;
|
||||
Bool hit = false;
|
||||
Int X,Y;
|
||||
Vector3 normal,P0,P1,P2,P3;
|
||||
|
||||
if (!m_map)
|
||||
return false; //need valid pointer to heightmap samples
|
||||
// HeightSampleType *pData = m_map->getDataPtr();
|
||||
//Clip ray to extents of heightfield
|
||||
AABoxClass hbox;
|
||||
LineSegClass lineseg,lineseg2;
|
||||
CastResultStruct result;
|
||||
Int StartCellX;
|
||||
Int EndCellX;
|
||||
Int StartCellY;
|
||||
Int EndCellY;
|
||||
const Int overhang = 2*32; // Allow picking past the edge for scrolling & objects.
|
||||
Vector3 minPt(MAP_XY_FACTOR*(-overhang), MAP_XY_FACTOR*(-overhang), -MAP_XY_FACTOR);
|
||||
Vector3 maxPt(MAP_XY_FACTOR*(m_map->getXExtent()+overhang),
|
||||
MAP_XY_FACTOR*(m_map->getYExtent()+overhang), MAP_HEIGHT_SCALE*m_map->getMaxHeightValue()+MAP_XY_FACTOR);
|
||||
MinMaxAABoxClass mmbox(minPt, maxPt);
|
||||
hbox.Init(mmbox);
|
||||
|
||||
lineseg=raytest.Ray;
|
||||
|
||||
//Set initial ray endpoints
|
||||
P0 = raytest.Ray.Get_P0();
|
||||
P1 = raytest.Ray.Get_P1();
|
||||
result.ComputeContactPoint=true;
|
||||
|
||||
Int p;
|
||||
for (p=0; p<3; p++) {
|
||||
//find intersection point of ray and terrain bounding box
|
||||
if (CollisionMath::Collide(lineseg,hbox,&result))
|
||||
{ //ray intersects terrain or starts inside the terrain.
|
||||
if (!result.StartBad) //check if start point inside terrain
|
||||
P0 = result.ContactPoint; //make intersection point the new start of the ray.
|
||||
|
||||
//reverse direction of original ray and clip again to extent of
|
||||
//heightmap
|
||||
result.Fraction=1.0f; //reset the result
|
||||
result.StartBad=false;
|
||||
lineseg2.Set(lineseg.Get_P1(),lineseg.Get_P0()); //reverse line segment
|
||||
if (CollisionMath::Collide(lineseg2,hbox,&result))
|
||||
{ if (!result.StartBad) //check if end point inside terrain
|
||||
P1 = result.ContactPoint; //make intersection point the new end pont of ray
|
||||
}
|
||||
} else {
|
||||
return(false);
|
||||
}
|
||||
|
||||
// Take the 2D bounding box of ray and check heights
|
||||
// inside this box for intersection.
|
||||
if (P0.X > P1.X) { //flip start/end points
|
||||
StartCellX = floor(P1.X/MAP_XY_FACTOR);
|
||||
EndCellX = ceil(P0.X/MAP_XY_FACTOR);
|
||||
} else {
|
||||
StartCellX = floor(P0.X/MAP_XY_FACTOR);
|
||||
EndCellX = ceil(P1.X/MAP_XY_FACTOR);
|
||||
}
|
||||
if (P0.Y > P1.Y) { //flip start/end points
|
||||
StartCellY=floor(P1.Y/MAP_XY_FACTOR);
|
||||
EndCellY=ceil(P0.Y/MAP_XY_FACTOR);
|
||||
} else {
|
||||
StartCellY = floor(P0.Y/MAP_XY_FACTOR);
|
||||
EndCellY = ceil(P1.Y/MAP_XY_FACTOR);
|
||||
}
|
||||
|
||||
Vector3 minPt(MAP_XY_FACTOR*(StartCellX-1), MAP_XY_FACTOR*(StartCellY-1), theZ-1);
|
||||
Vector3 maxPt(MAP_XY_FACTOR*(EndCellX+1), MAP_XY_FACTOR*(EndCellY+1), theZ+1);
|
||||
MinMaxAABoxClass mmbox(minPt, maxPt);
|
||||
hbox.Init(mmbox);
|
||||
}
|
||||
|
||||
raytest.Result->ComputeContactPoint=true; //tell CollisionMath that we need point.
|
||||
|
||||
Int offset;
|
||||
for (offset = 1; offset < 5; offset *= 3) {
|
||||
for (Y=StartCellY-offset; Y<=EndCellY+offset; Y++) {
|
||||
//if (Y<0) continue;
|
||||
//if (Y>=m_map->getYExtent()-1) continue;
|
||||
|
||||
for (X=StartCellX-offset; X<=EndCellX+offset; X++) {
|
||||
//test the 2 triangles in this cell
|
||||
// 3-----2
|
||||
// | /|
|
||||
// | / |
|
||||
// |/ |
|
||||
// 0-----1
|
||||
|
||||
//bottom triangle first
|
||||
P0.X=X*MAP_XY_FACTOR;
|
||||
P0.Y=Y*MAP_XY_FACTOR;
|
||||
P0.Z=THE_Z;
|
||||
|
||||
P1.X=(X+1)*MAP_XY_FACTOR;
|
||||
P1.Y=Y*MAP_XY_FACTOR;
|
||||
P1.Z=THE_Z;
|
||||
|
||||
P2.X=(X+1)*MAP_XY_FACTOR;
|
||||
P2.Y=(Y+1)*MAP_XY_FACTOR;
|
||||
P2.Z=THE_Z;
|
||||
|
||||
P3.X=X*MAP_XY_FACTOR;
|
||||
P3.Y=(Y+1)*MAP_XY_FACTOR;
|
||||
P3.Z=THE_Z;
|
||||
|
||||
|
||||
tri.V[0] = &P0;
|
||||
tri.V[1] = &P1;
|
||||
tri.V[2] = &P2;
|
||||
|
||||
tri.N = &normal;
|
||||
|
||||
tri.Compute_Normal();
|
||||
|
||||
hit = hit | CollisionMath::Collide(raytest.Ray, tri, raytest.Result);
|
||||
|
||||
if (raytest.Result->StartBad)
|
||||
return true;
|
||||
|
||||
//top triangle
|
||||
tri.V[0] = &P2;
|
||||
tri.V[1] = &P3;
|
||||
tri.V[2] = &P0;
|
||||
|
||||
tri.N = &normal;
|
||||
|
||||
tri.Compute_Normal();
|
||||
|
||||
hit = hit | CollisionMath::Collide(raytest.Ray, tri, raytest.Result);
|
||||
|
||||
if (hit)
|
||||
raytest.Result->SurfaceType = SURFACE_TYPE_DEFAULT; ///@todo: WW3D uses this to return dirt, grass, etc. Do we need this?
|
||||
}
|
||||
if (hit) break;
|
||||
}
|
||||
if (hit) break;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// WBHeightMap::Render
|
||||
//=============================================================================
|
||||
/** Renders (draws) the terrain. */
|
||||
//=============================================================================
|
||||
void WBHeightMap::Render(RenderInfoClass & rinfo)
|
||||
{
|
||||
if (m_flattenHeights) {
|
||||
flattenHeights();
|
||||
}
|
||||
#ifdef USE_FLAT_HEIGHT_MAP
|
||||
FlatHeightMapRenderObjClass::Render(rinfo);
|
||||
#else
|
||||
HeightMapRenderObjClass::Render(rinfo);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
594
GeneralsMD/Code/Tools/WorldBuilder/src/WBPopupSlider.cpp
Normal file
594
GeneralsMD/Code/Tools/WorldBuilder/src/WBPopupSlider.cpp
Normal file
@@ -0,0 +1,594 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// WBPopupSlider.cpp implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Lib/BaseType.h"
|
||||
#include "WBPopupSlider.h"
|
||||
#include "resource.h"
|
||||
#include "Common/Debug.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WBPopupSliderButton public member functions
|
||||
|
||||
void WBPopupSliderButton::SetupPopSliderButton
|
||||
(
|
||||
CWnd *pParentWnd,
|
||||
long controlID,
|
||||
PopupSliderOwner *pOwner
|
||||
)
|
||||
{
|
||||
SubclassWindow(pParentWnd->GetDlgItem(controlID)->GetSafeHwnd());
|
||||
|
||||
m_controlID = controlID;
|
||||
m_sliderStyle = SB_VERT;
|
||||
m_owner = pOwner;
|
||||
|
||||
HBITMAP hBm = (HBITMAP) ::LoadImage((HINSTANCE) AfxGetResourceHandle(),
|
||||
MAKEINTRESOURCE(IDB_DownArrow),
|
||||
IMAGE_BITMAP, 0, 0,
|
||||
LR_LOADMAP3DCOLORS);
|
||||
|
||||
HBITMAP hbmOld = (HBITMAP) SendMessage(BM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hBm);
|
||||
|
||||
if (hbmOld)
|
||||
::DeleteObject(hbmOld);
|
||||
hbmOld = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WBPopupSliderButton
|
||||
|
||||
WBPopupSliderButton::WBPopupSliderButton()
|
||||
{
|
||||
m_owner = NULL;
|
||||
}
|
||||
|
||||
WBPopupSliderButton::~WBPopupSliderButton()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(WBPopupSliderButton, CButton)
|
||||
//{{AFX_MSG_MAP(WBPopupSliderButton)
|
||||
ON_WM_LBUTTONDOWN()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WBPopupSliderButton message handlers
|
||||
|
||||
void WBPopupSliderButton::OnLButtonDown(UINT nFlags, CPoint point)
|
||||
{
|
||||
nFlags;
|
||||
point;
|
||||
|
||||
// just create the slider; it will delete itself when the user is done scrolling
|
||||
PopupSlider::New(this, m_sliderStyle, m_owner, m_controlID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// defines and typedefs
|
||||
|
||||
#define POPUP_SLIDER_SMALLDIMENSION 16
|
||||
#define POPUP_SLIDER_BIGDIMENSION 120
|
||||
#define SLIDER_INSET_AMOUNT 1
|
||||
|
||||
// two guesses what these are
|
||||
#define THUMB_ICON_WIDTH 5
|
||||
#define THUMB_ICON_HEIGHT 8
|
||||
|
||||
// the width between the end of the channel and the edge of the window
|
||||
#define CHANNEL_X_INSET_AMOUNT 5
|
||||
|
||||
// the height between the top of the thumb and the top of the window
|
||||
#define PIXELS_ABOVE_SLIDER 3
|
||||
|
||||
// the height between the top of the thumb and the top of the channel
|
||||
#define CHANNEL_BELOW_THUMB_TOP 1
|
||||
|
||||
// the height between the bottom of the channel and the bottom of the thumb
|
||||
#define CHANNEL_ABOVE_THUMB_BOTTOM 3
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PopupSlider static member variables
|
||||
|
||||
PopupSlider *PopupSlider::gPopupSlider = 0;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// internal routines
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PopupSlider private member functions
|
||||
|
||||
void PopupSlider::GetChannelRect(CRect* rect)
|
||||
{
|
||||
GetClientRect(rect);
|
||||
if(SB_HORZ == m_kind) {
|
||||
rect->InflateRect(-CHANNEL_X_INSET_AMOUNT, 0);
|
||||
rect->top = PIXELS_ABOVE_SLIDER + CHANNEL_BELOW_THUMB_TOP;
|
||||
rect->bottom = THUMB_ICON_HEIGHT - (CHANNEL_BELOW_THUMB_TOP + CHANNEL_ABOVE_THUMB_BOTTOM);
|
||||
rect->bottom += rect->top;
|
||||
} else {
|
||||
rect->InflateRect(0, -CHANNEL_X_INSET_AMOUNT);
|
||||
rect->left = PIXELS_ABOVE_SLIDER + CHANNEL_BELOW_THUMB_TOP + 2;
|
||||
rect->right = THUMB_ICON_HEIGHT - (CHANNEL_BELOW_THUMB_TOP + CHANNEL_ABOVE_THUMB_BOTTOM);
|
||||
rect->right += rect->left;
|
||||
}
|
||||
}
|
||||
|
||||
void PopupSlider::GetThumbIconRect(CRect* rect)
|
||||
{
|
||||
CRect channelRect;
|
||||
GetChannelRect(&channelRect);
|
||||
|
||||
int range = m_hi - m_lo;
|
||||
|
||||
int along = m_curValue - m_lo;
|
||||
|
||||
if(SB_HORZ == m_kind) {
|
||||
int thumbMiddle = along * channelRect.Width();
|
||||
thumbMiddle /= range;
|
||||
thumbMiddle += channelRect.left;
|
||||
|
||||
if (thumbMiddle < channelRect.left) thumbMiddle = channelRect.left;
|
||||
if (thumbMiddle > channelRect.right) thumbMiddle = channelRect.right;
|
||||
|
||||
rect->top = channelRect.top - CHANNEL_BELOW_THUMB_TOP;
|
||||
rect->bottom = channelRect.bottom + CHANNEL_ABOVE_THUMB_BOTTOM;
|
||||
rect->left = thumbMiddle - (THUMB_ICON_WIDTH / 2);
|
||||
rect->right = rect->left + THUMB_ICON_WIDTH;
|
||||
} else {
|
||||
int thumbMiddle = along * channelRect.Height();
|
||||
thumbMiddle /= range;
|
||||
thumbMiddle = channelRect.bottom - thumbMiddle;
|
||||
|
||||
if (thumbMiddle < channelRect.top) thumbMiddle = channelRect.top;
|
||||
if (thumbMiddle > channelRect.bottom) thumbMiddle = channelRect.bottom;
|
||||
|
||||
rect->top = thumbMiddle - (THUMB_ICON_WIDTH / 2);
|
||||
rect->bottom = thumbMiddle + (THUMB_ICON_WIDTH/2);
|
||||
rect->left = channelRect.left - CHANNEL_BELOW_THUMB_TOP;
|
||||
rect->right = channelRect.right + CHANNEL_ABOVE_THUMB_BOTTOM;
|
||||
}
|
||||
}
|
||||
|
||||
void PopupSlider::MoveThumbUnderMouse(int xNew)
|
||||
{
|
||||
CRect channelRect;
|
||||
GetChannelRect(&channelRect);
|
||||
|
||||
int newVal = m_hi - m_lo;
|
||||
if(SB_HORZ == m_kind) {
|
||||
int along = xNew - channelRect.left;
|
||||
|
||||
newVal *= along;
|
||||
newVal /= channelRect.Width();
|
||||
newVal += m_lo;
|
||||
} else {
|
||||
int along = channelRect.bottom - xNew;
|
||||
|
||||
newVal *= along;
|
||||
newVal /= channelRect.Height();
|
||||
newVal += m_lo;
|
||||
}
|
||||
if (newVal < m_lo) newVal = m_lo;
|
||||
if (newVal > m_hi) newVal = m_hi;
|
||||
|
||||
CRect iconRect;
|
||||
GetThumbIconRect(&iconRect);
|
||||
iconRect.InflateRect(3, 3);
|
||||
InvalidateRect(&iconRect, FALSE);
|
||||
|
||||
m_curValue = newVal;
|
||||
|
||||
GetThumbIconRect(&iconRect);
|
||||
iconRect.InflateRect(3, 3);
|
||||
InvalidateRect(&iconRect, FALSE);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PopupSlider static member functions
|
||||
|
||||
void PopupSlider::New(CWnd *pParentWnd, long kind,
|
||||
PopupSliderOwner *pSliderOwner,
|
||||
long sliderID)
|
||||
{
|
||||
PopupSlider * pPopupSlider;
|
||||
|
||||
DEBUG_ASSERTCRASH(((SB_HORZ == kind) || (SB_VERT == kind)),
|
||||
("PopupSlider - unexpected kind of slider!"));
|
||||
|
||||
DEBUG_ASSERTCRASH(pSliderOwner, ("slider owner is NULL!"));
|
||||
|
||||
try {
|
||||
CRect rect;
|
||||
|
||||
pParentWnd->GetWindowRect(&rect);
|
||||
|
||||
rect.left = rect.right;
|
||||
pPopupSlider = new PopupSlider();
|
||||
|
||||
pPopupSlider->mSliderOwner = pSliderOwner;
|
||||
pPopupSlider->mSliderID = sliderID;
|
||||
pPopupSlider->m_kind = kind;
|
||||
|
||||
pSliderOwner->GetPopSliderInfo(pPopupSlider->mSliderID,
|
||||
&(pPopupSlider->m_lo),
|
||||
&(pPopupSlider->m_hi),
|
||||
&(pPopupSlider->m_lineSize),
|
||||
&(pPopupSlider->m_curValue));
|
||||
|
||||
DEBUG_ASSERTCRASH(pPopupSlider->m_hi != pPopupSlider->m_lo, ("PopupSlider: endpoint values are the same!"));
|
||||
DEBUG_ASSERTCRASH(pPopupSlider->m_lineSize != 0, ("PopupSlider: line size is zero!"));
|
||||
|
||||
pPopupSlider->Create(rect, pParentWnd);
|
||||
|
||||
/* if the slider is successfully created, it will
|
||||
be deleted automatically by its PostNcDestroy
|
||||
member function */
|
||||
} catch (...) {
|
||||
// don't rethrow
|
||||
if (pPopupSlider) {
|
||||
delete pPopupSlider;
|
||||
pPopupSlider = NULL;
|
||||
}
|
||||
|
||||
} // catch
|
||||
|
||||
gPopupSlider = pPopupSlider;
|
||||
// gPopupSlider will be deleted when its PostNcDestroy method is called
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PopupSlider
|
||||
|
||||
PopupSlider::PopupSlider()
|
||||
{
|
||||
mSliderOwner = NULL;
|
||||
|
||||
mDraggingThumb = false;
|
||||
mClickThrough = false;
|
||||
mSetOrigPt = false;
|
||||
mEverMoved = false;
|
||||
mIcon = NULL;
|
||||
m_lo = m_hi = m_curValue = 0;
|
||||
|
||||
m_valOnLastFinished = 0;
|
||||
}
|
||||
|
||||
PopupSlider::~PopupSlider()
|
||||
{
|
||||
if (mIcon) {
|
||||
BOOL bRet = DestroyIcon(mIcon);
|
||||
|
||||
DEBUG_ASSERTCRASH(bRet != 0, ("Oops."));
|
||||
|
||||
mIcon = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(PopupSlider, CWnd)
|
||||
//{{AFX_MSG_MAP(PopupSlider)
|
||||
ON_WM_PAINT()
|
||||
ON_WM_LBUTTONDOWN()
|
||||
ON_WM_LBUTTONUP()
|
||||
ON_WM_DESTROY()
|
||||
ON_WM_MOUSEMOVE()
|
||||
ON_WM_KEYDOWN()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PopupSlider message handlers
|
||||
|
||||
BOOL PopupSlider::Create(const RECT& rect, CWnd* pParentWnd)
|
||||
{
|
||||
BOOL retVal;
|
||||
|
||||
try {
|
||||
if (FALSE == m_brush3dFaceColor.CreateSolidBrush(GetSysColor(COLOR_3DFACE))) {
|
||||
throw(-1);
|
||||
}
|
||||
|
||||
mIcon = (HICON) LoadImage(AfxGetResourceHandle(),
|
||||
MAKEINTRESOURCE(IDI_Thumb),
|
||||
IMAGE_ICON, 0, 0, LR_LOADMAP3DCOLORS);
|
||||
|
||||
|
||||
DWORD dwExStyle = WS_EX_TOPMOST;
|
||||
DWORD dwStyle = WS_POPUP;
|
||||
UINT nClassStyle = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT | CS_SAVEBITS;
|
||||
HCURSOR hCursor = ::LoadCursor(NULL, IDC_ARROW);
|
||||
CString className = AfxRegisterWndClass(nClassStyle, hCursor, (HBRUSH) m_brush3dFaceColor);
|
||||
|
||||
long winWidth, winHeight;
|
||||
|
||||
winWidth = ((SB_HORZ == m_kind) ? POPUP_SLIDER_BIGDIMENSION : POPUP_SLIDER_SMALLDIMENSION);
|
||||
winHeight = ((SB_HORZ == m_kind) ? POPUP_SLIDER_SMALLDIMENSION : POPUP_SLIDER_BIGDIMENSION);
|
||||
|
||||
CRect winRect(rect.right - winWidth, rect.bottom, rect.right, rect.bottom + winHeight);
|
||||
|
||||
// we'll just use "this" for the child ID
|
||||
if (FALSE == CWnd::CreateEx(dwExStyle, (LPCTSTR) className, "",
|
||||
dwStyle, winRect.left, winRect.top,
|
||||
winRect.Width(), winRect.Height(),
|
||||
pParentWnd->GetSafeHwnd(),
|
||||
NULL, NULL))
|
||||
throw(-1);
|
||||
|
||||
|
||||
// New code to center the slider's thumb under the parent window
|
||||
if(pParentWnd) {
|
||||
// Calculate the center of the parent window
|
||||
CPoint parentWindowCenter;
|
||||
GetCursorPos(&parentWindowCenter);
|
||||
|
||||
// Calculate the center of the thumb
|
||||
CRect iconRect;
|
||||
GetThumbIconRect(&iconRect);
|
||||
CPoint iconCenter = iconRect.CenterPoint();
|
||||
ClientToScreen(&iconCenter);
|
||||
|
||||
// from the centers, calculate how far to move the window
|
||||
int hAdjustToCenter = 0;
|
||||
int vAdjustToCenter = 0;
|
||||
if(SB_HORZ == m_kind)
|
||||
hAdjustToCenter = parentWindowCenter.x - iconCenter.x;
|
||||
// This may work for vertical sliders, but has not been tested
|
||||
if(SB_VERT == m_kind)
|
||||
vAdjustToCenter = parentWindowCenter.y - iconCenter.y;
|
||||
|
||||
// Move the window
|
||||
CRect myWindowRect;
|
||||
GetWindowRect(&myWindowRect);
|
||||
myWindowRect.OffsetRect(hAdjustToCenter, vAdjustToCenter);
|
||||
SetWindowPos(NULL, myWindowRect.left, myWindowRect.top, myWindowRect.Width(), myWindowRect.Height(), SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOREDRAW);
|
||||
}
|
||||
|
||||
// finally, make sure the window appears on screen
|
||||
//MakeSureWindowIsVisible(GetSafeHwnd());
|
||||
ShowWindow(SW_SHOW);
|
||||
|
||||
SetCapture();
|
||||
|
||||
// success (we'll set the capture after all the failable operations)
|
||||
retVal = TRUE;
|
||||
} catch (...) {
|
||||
// don't rethrow
|
||||
retVal = FALSE;
|
||||
} // catch
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void PopupSlider::PostNcDestroy()
|
||||
{
|
||||
if (mSliderOwner && (m_valOnLastFinished != m_curValue)) {
|
||||
mSliderOwner->PopSliderFinished(mSliderID, m_curValue);
|
||||
}
|
||||
|
||||
CWnd::PostNcDestroy();
|
||||
|
||||
// now that the window has gone away, delete ourselves
|
||||
if (gPopupSlider == this) {
|
||||
delete gPopupSlider;
|
||||
gPopupSlider = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void PopupSlider::OnPaint()
|
||||
{
|
||||
CPaintDC dc(this); // device context for painting
|
||||
CRect rc;
|
||||
CBrush cbr(GetSysColor(COLOR_BTNFACE));
|
||||
CBrush *pCbrOld;
|
||||
CPen cpen(PS_NULL, 0, RGB(0x00, 0x00, 0x00));
|
||||
CPen *pPenOld;
|
||||
COLORREF ulColor, brColor;
|
||||
|
||||
pCbrOld = dc.SelectObject(&cbr);
|
||||
pPenOld = dc.SelectObject(&cpen);
|
||||
|
||||
this->GetClientRect(&rc);
|
||||
|
||||
dc.Rectangle(&rc);
|
||||
|
||||
ulColor = GetSysColor(COLOR_3DHILIGHT);
|
||||
brColor = GetSysColor(COLOR_3DSHADOW);
|
||||
|
||||
dc.Draw3dRect(&rc, ulColor, brColor);
|
||||
|
||||
dc.SelectObject(pCbrOld);
|
||||
dc.SelectObject(pPenOld);
|
||||
|
||||
CRect channelRect;
|
||||
GetChannelRect(&channelRect);
|
||||
dc.Draw3dRect(&channelRect, GetSysColor(COLOR_3DSHADOW), ulColor);
|
||||
channelRect.InflateRect(-1, -1);
|
||||
dc.Draw3dRect(&channelRect, GetSysColor(COLOR_3DDKSHADOW), GetSysColor(COLOR_3DLIGHT));
|
||||
|
||||
if (mIcon) {
|
||||
CRect iconRect;
|
||||
GetThumbIconRect(&iconRect);
|
||||
::DrawIconEx(dc.GetSafeHdc(), iconRect.left, iconRect.top,
|
||||
mIcon, 0, 0, 0, NULL, DI_NORMAL);
|
||||
}
|
||||
// Do not call CWnd::OnPaint() for painting messages
|
||||
}
|
||||
|
||||
void PopupSlider::OnLButtonDown(UINT nFlags, CPoint point)
|
||||
{
|
||||
nFlags;
|
||||
CRect rc;
|
||||
GetClientRect(&rc);
|
||||
if (!mSetOrigPt) {
|
||||
mOrigPt = point;
|
||||
}
|
||||
mSetOrigPt = true;
|
||||
|
||||
|
||||
if (rc.PtInRect((POINT) point)) {
|
||||
CRect iconRect;
|
||||
GetThumbIconRect(&iconRect);
|
||||
|
||||
// 990909: inflate the rect a little to make it easier to grab the thumb
|
||||
if(SB_HORZ == m_kind) {
|
||||
iconRect.InflateRect(3, 6);
|
||||
} else {
|
||||
iconRect.InflateRect(6, 3);
|
||||
}
|
||||
|
||||
if (iconRect.PtInRect((POINT) point)) {
|
||||
mDraggingThumb = true;
|
||||
} else {
|
||||
CRect channelRect;
|
||||
GetChannelRect(&channelRect);
|
||||
|
||||
if (channelRect.PtInRect((POINT) point)) {
|
||||
if(SB_HORZ == m_kind) {
|
||||
MoveThumbUnderMouse(point.x);
|
||||
} else {
|
||||
MoveThumbUnderMouse(point.y);
|
||||
}
|
||||
mDraggingThumb = true;
|
||||
mEverMoved = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (mSliderOwner) {
|
||||
mSliderOwner->PopSliderFinished(mSliderID, m_curValue);
|
||||
}
|
||||
|
||||
m_valOnLastFinished = m_curValue;
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
// user clicked outside our area, close the windoid
|
||||
PostMessage(WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void PopupSlider::OnLButtonUp(UINT nFlags, CPoint point)
|
||||
{
|
||||
if (mDraggingThumb) {
|
||||
mDraggingThumb = false;
|
||||
|
||||
if (mSliderOwner) {
|
||||
try {
|
||||
mSliderOwner->PopSliderChanged(mSliderID, m_curValue);
|
||||
if (mClickThrough && mEverMoved) {
|
||||
mSliderOwner->PopSliderFinished(mSliderID, m_curValue);
|
||||
}
|
||||
|
||||
m_valOnLastFinished = m_curValue;
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
if (mClickThrough && mEverMoved) {
|
||||
PostMessage(WM_CLOSE, 0, 0);
|
||||
}
|
||||
} else {
|
||||
CWnd::OnLButtonUp(nFlags, point);
|
||||
}
|
||||
mClickThrough = false;
|
||||
}
|
||||
|
||||
void PopupSlider::OnDestroy()
|
||||
{
|
||||
mEverMoved = false;
|
||||
ReleaseCapture();
|
||||
|
||||
CWnd::OnDestroy();
|
||||
}
|
||||
|
||||
|
||||
void PopupSlider::OnMouseMove(UINT nFlags, CPoint point)
|
||||
{
|
||||
if (!mSetOrigPt) {
|
||||
mOrigPt = point;
|
||||
}
|
||||
mSetOrigPt = true;
|
||||
if (mDraggingThumb) {
|
||||
mEverMoved = true;
|
||||
if(SB_HORZ == m_kind) {
|
||||
MoveThumbUnderMouse(point.x);
|
||||
} else {
|
||||
MoveThumbUnderMouse(point.y);
|
||||
}
|
||||
|
||||
if (mSliderOwner) {
|
||||
mSliderOwner->PopSliderChanged(mSliderID, m_curValue);
|
||||
}
|
||||
} else if (nFlags & MK_LBUTTON) {
|
||||
CRect rc;
|
||||
GetChannelRect(&rc);
|
||||
if(SB_HORZ == m_kind) {
|
||||
rc.InflateRect(0, 6);
|
||||
} else {
|
||||
rc.InflateRect(6, 0);
|
||||
}
|
||||
|
||||
if (rc.PtInRect(point)) {
|
||||
// user just clicked thru, so mark the slider for click through
|
||||
mDraggingThumb = true;
|
||||
|
||||
if(SB_HORZ == m_kind) {
|
||||
if (mOrigPt.x != point.x) {
|
||||
MoveThumbUnderMouse(point.x);
|
||||
}
|
||||
mClickThrough = true;
|
||||
} else {
|
||||
if (mOrigPt.y != point.y) {
|
||||
MoveThumbUnderMouse(point.y);
|
||||
}
|
||||
mClickThrough = true;
|
||||
}
|
||||
|
||||
if (mSliderOwner) {
|
||||
mSliderOwner->PopSliderChanged(mSliderID, m_curValue);
|
||||
}
|
||||
} // if (PtInRect)
|
||||
}
|
||||
}
|
||||
|
||||
void PopupSlider::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||||
{
|
||||
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
|
||||
|
||||
if ((VK_RETURN == nChar) || (VK_ESCAPE == nChar)) {
|
||||
// close the window
|
||||
PostMessage(WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
3427
GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
Normal file
3427
GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
Normal file
File diff suppressed because it is too large
Load Diff
509
GeneralsMD/Code/Tools/WorldBuilder/src/WaterOptions.cpp
Normal file
509
GeneralsMD/Code/Tools/WorldBuilder/src/WaterOptions.cpp
Normal file
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// WaterOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "CUndoable.h"
|
||||
#include "WaterOptions.h"
|
||||
#include "WaypointOptions.h"
|
||||
#include "WorldBuilder.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WbView3d.h"
|
||||
#include "PolygonTool.h"
|
||||
#include "WaypointTool.h"
|
||||
#include "GameLogic/PolygonTrigger.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "LayersList.h"
|
||||
|
||||
WaterOptions *WaterOptions::m_staticThis = NULL;
|
||||
Int WaterOptions::m_waterHeight = 7;
|
||||
Int WaterOptions::m_waterPointSpacing = MAP_XY_FACTOR;
|
||||
Bool WaterOptions::m_creatingWaterAreas = false;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// WaterOptions dialog trivial construstor - Create does the real work.
|
||||
|
||||
|
||||
WaterOptions::WaterOptions(CWnd* pParent /*=NULL*/):
|
||||
m_moveUndoable(NULL)
|
||||
{
|
||||
//{{AFX_DATA_INIT(WaterOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
/// Windows default stuff.
|
||||
void WaterOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(WaterOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
void WaterOptions::setHeight(Int height)
|
||||
{
|
||||
char buffer[50];
|
||||
sprintf(buffer, "%d", height);
|
||||
m_waterHeight = height;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void WaterOptions::updateTheUI(void)
|
||||
{
|
||||
PolygonTrigger *theTrigger = WaypointOptions::getSingleSelectedPolygon();
|
||||
|
||||
CWnd *pWnd = this->GetDlgItem(IDC_WATERNAME_EDIT);
|
||||
|
||||
if (theTrigger && pWnd) {
|
||||
pWnd->SetWindowText(theTrigger->getTriggerName().str());
|
||||
setHeight(theTrigger->getPoint(0)->z);
|
||||
}
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_WATER_POLYGON);
|
||||
pButton->SetCheck(m_creatingWaterAreas ? 1:0);
|
||||
Bool isRiver = false;
|
||||
if (theTrigger) {
|
||||
isRiver = theTrigger->isRiver();
|
||||
}
|
||||
pButton = (CButton*)GetDlgItem(IDC_MAKE_RIVER);
|
||||
pButton->SetCheck(isRiver ? 1:0);
|
||||
pButton->EnableWindow(theTrigger!=NULL);
|
||||
|
||||
pWnd = m_staticThis->GetDlgItem(IDC_SPACING);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pWnd) {
|
||||
sprintf(buffer, "%d", m_waterPointSpacing);
|
||||
pWnd->SetWindowText(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void WaterOptions::update(void)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
m_staticThis->updateTheUI();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WaterOptions message handlers
|
||||
|
||||
/// Dialog UI initialization.
|
||||
/** Creates the slider controls, and sets the initial values for
|
||||
width and feather in the ui controls. */
|
||||
BOOL WaterOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
|
||||
m_waterHeightPopup.SetupPopSliderButton(this, IDC_HEIGHT_POPUP, this);
|
||||
m_waterPointSpacing = 2*MAP_XY_FACTOR;
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
setHeight(m_waterHeight);
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(WaterOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(WaterOptions)
|
||||
ON_CBN_KILLFOCUS(IDC_WATERNAME_EDIT, OnChangeWaterEdit)
|
||||
ON_EN_CHANGE(IDC_HEIGHT_EDIT, OnChangeHeightEdit)
|
||||
ON_EN_CHANGE(IDC_SPACING, OnChangeSpacingEdit)
|
||||
ON_BN_CLICKED(IDC_WATER_POLYGON, OnWaterPolygon)
|
||||
ON_BN_CLICKED(IDC_MAKE_RIVER, OnMakeRiver)
|
||||
ON_CBN_SELENDOK(IDC_WATERNAME_EDIT, OnChangeWaterEdit)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
void WaterOptions::OnChangeWaterEdit()
|
||||
{
|
||||
PolygonTrigger *theTrigger = WaypointOptions::getSingleSelectedPolygon();
|
||||
|
||||
// get the combo box
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_WATERNAME_EDIT);
|
||||
if (pCombo) {
|
||||
// get the text out of the combo. If it is user-typed, sel will be -1, otherwise it will be >=0
|
||||
CString theText;
|
||||
Int sel = pCombo->GetCurSel();
|
||||
if (sel >= 0) {
|
||||
pCombo->GetLBText(sel, theText);
|
||||
} else {
|
||||
pCombo->GetWindowText(theText);
|
||||
}
|
||||
AsciiString name((LPCTSTR)theText);
|
||||
|
||||
// check to see if the user-entered name is already in use.
|
||||
Bool didMatch = false;
|
||||
|
||||
// check trigger area objects
|
||||
PolygonTrigger *pTrig;
|
||||
for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); !didMatch && pTrig; pTrig = pTrig->getNext()) {
|
||||
if (pTrig==theTrigger) continue; // don't check against yourself.
|
||||
AsciiString trigName = pTrig->getTriggerName();
|
||||
if (name == trigName) {
|
||||
if (pTrig->isValid()) {
|
||||
didMatch = true;
|
||||
} else {
|
||||
PolygonTrigger::removePolygonTrigger(pTrig);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if there's a match, throw up a messagebox, otherwise set the name
|
||||
if (didMatch) {
|
||||
::AfxMessageBox("Name already in use");
|
||||
} else {
|
||||
if (theTrigger) {
|
||||
theTrigger->setTriggerName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WaterOptions::OnWaterPolygon()
|
||||
{
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_WATER_POLYGON);
|
||||
m_creatingWaterAreas = (pButton->GetCheck()==1);
|
||||
}
|
||||
|
||||
void WaterOptions::OnMakeRiver()
|
||||
{
|
||||
CButton *pButton = (CButton*)GetDlgItem(IDC_MAKE_RIVER);
|
||||
Bool river = (pButton->GetCheck()==1);
|
||||
PolygonTrigger *theTrigger = WaypointOptions::getSingleSelectedPolygon();
|
||||
if (theTrigger) {
|
||||
theTrigger->setRiver(river);
|
||||
if (river) {
|
||||
Int curPoint = PolygonTool::getSelectedPointNdx();
|
||||
if (curPoint >= 0) {
|
||||
Real endLen=0;
|
||||
Int newPoint = curPoint;
|
||||
if (curPoint>0) curPoint--;
|
||||
if (curPoint>0) curPoint--;
|
||||
Int i;
|
||||
for (i=curPoint; i<theTrigger->getNumPoints()-1 && i<curPoint+4; i++) {
|
||||
ICoord3D innerPt = *theTrigger->getPoint(i);
|
||||
ICoord3D outerPt = *theTrigger->getPoint(i+1);
|
||||
Real dx = innerPt.x-outerPt.x;
|
||||
Real dy = innerPt.y-outerPt.y;
|
||||
Real curLen = sqrt(dx*dx+dy*dy);
|
||||
if ( curLen>endLen) {
|
||||
newPoint = i;
|
||||
endLen = curLen;
|
||||
}
|
||||
}
|
||||
theTrigger->setRiverStart(newPoint);
|
||||
|
||||
// Now find the other end.
|
||||
// Real sourceWidth = endLen;
|
||||
|
||||
endLen=0;
|
||||
Int endPoint = 0;
|
||||
for (i=0; i<theTrigger->getNumPoints()-1; i++) {
|
||||
if (i>=newPoint-1 && i<=newPoint+1) continue;
|
||||
ICoord3D innerPt = *theTrigger->getPoint(i);
|
||||
ICoord3D outerPt = *theTrigger->getPoint(i+1);
|
||||
Real dx = innerPt.x-outerPt.x;
|
||||
Real dy = innerPt.y-outerPt.y;
|
||||
Real curLen = sqrt(dx*dx+dy*dy);
|
||||
if ( curLen>endLen) {
|
||||
endPoint = i;
|
||||
endLen = curLen;
|
||||
}
|
||||
}
|
||||
Int pointsOut = endPoint - newPoint;
|
||||
Int pointsIn = newPoint - endPoint;
|
||||
if (pointsOut<0) pointsOut += theTrigger->getNumPoints();
|
||||
if (pointsIn<0) pointsIn += theTrigger->getNumPoints();
|
||||
Int delta = pointsIn-pointsOut;
|
||||
if (delta<0) delta = -delta;
|
||||
if (delta>1) {
|
||||
PolygonTrigger *pNew;
|
||||
if (pointsOut<pointsIn) {
|
||||
pNew = adjustCount(theTrigger, newPoint+1, endPoint, pointsIn-1);
|
||||
theTrigger->setRiverStart(pointsIn);
|
||||
} else {
|
||||
pNew = adjustCount(theTrigger, endPoint+1, newPoint, pointsOut-1);
|
||||
theTrigger->setRiverStart(0);
|
||||
}
|
||||
while(theTrigger->getNumPoints()) theTrigger->deletePoint(theTrigger->getNumPoints()-1);
|
||||
for (i=0; i<pNew->getNumPoints(); i++) {
|
||||
theTrigger->addPoint(*pNew->getPoint(i));
|
||||
}
|
||||
pNew->deleteInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust the spacing.
|
||||
PolygonTrigger * WaterOptions::adjustCount(PolygonTrigger *trigger, Int firstPt, Int lastPt, Int desiredPointCount)
|
||||
{
|
||||
PolygonTrigger *pNew = newInstance(PolygonTrigger)(trigger->getNumPoints());
|
||||
// Real endLen=0;
|
||||
Real totalLen=0;
|
||||
Real curSpacingLen = 10;
|
||||
Int curPoint = lastPt;
|
||||
ICoord3D pt;
|
||||
while (curPoint != firstPt) {
|
||||
pt = *trigger->getPoint(curPoint);
|
||||
pNew->addPoint(pt);
|
||||
curPoint++;
|
||||
if (curPoint>=trigger->getNumPoints()) {
|
||||
curPoint = 0;
|
||||
}
|
||||
}
|
||||
|
||||
curPoint = firstPt;
|
||||
while (curPoint != lastPt) {
|
||||
Int nextPoint = curPoint;
|
||||
nextPoint++;
|
||||
if (nextPoint>=trigger->getNumPoints()) {
|
||||
nextPoint = 0;
|
||||
}
|
||||
ICoord3D curPt = *trigger->getPoint(curPoint);
|
||||
ICoord3D nextPt = *trigger->getPoint(nextPoint);
|
||||
Real dx = nextPt.x-curPt.x;
|
||||
Real dy = nextPt.y-curPt.y;
|
||||
Real curLen = sqrt(dx*dx+dy*dy);
|
||||
totalLen += curLen;
|
||||
curPoint = nextPoint;
|
||||
}
|
||||
Real spacing = totalLen/(desiredPointCount-1);
|
||||
|
||||
Bool didCurPoint = true;
|
||||
|
||||
curPoint = firstPt;
|
||||
pt = *trigger->getPoint(curPoint);
|
||||
pNew->addPoint(pt);
|
||||
while (curPoint != lastPt) {
|
||||
Int nextPoint = curPoint;
|
||||
nextPoint++;
|
||||
if (nextPoint>=trigger->getNumPoints()) {
|
||||
nextPoint = 0;
|
||||
}
|
||||
ICoord3D curPt = *trigger->getPoint(curPoint);
|
||||
ICoord3D nextPt = *trigger->getPoint(nextPoint);
|
||||
Real dx = nextPt.x-curPt.x;
|
||||
Real dy = nextPt.y-curPt.y;
|
||||
Real curLen = sqrt(dx*dx+dy*dy);
|
||||
if (curLen > 4*MAP_XY_FACTOR && curLen>2*spacing) {
|
||||
if (!didCurPoint) pNew->addPoint(curPt);
|
||||
else pNew->setPoint(curPt, pNew->getNumPoints()-1);
|
||||
pNew->addPoint(nextPt);
|
||||
didCurPoint = true;
|
||||
curSpacingLen = spacing;
|
||||
} else if (curSpacingLen>curLen) {
|
||||
curSpacingLen -= curLen;
|
||||
} else {
|
||||
while (curLen >= curSpacingLen) {
|
||||
// cur len > curSpacingLen.
|
||||
Real factor = curSpacingLen/curLen;
|
||||
curPt.x += dx*factor;
|
||||
curPt.y += dy*factor;
|
||||
pNew->addPoint(curPt);
|
||||
didCurPoint = false;
|
||||
dx = nextPt.x-curPt.x;
|
||||
dy = nextPt.y-curPt.y;
|
||||
curLen -= curSpacingLen;
|
||||
curSpacingLen = spacing;
|
||||
}
|
||||
curSpacingLen -= curLen;
|
||||
if ((curLen)<MAP_XY_FACTOR/2) {
|
||||
didCurPoint = true;
|
||||
}
|
||||
}
|
||||
curPoint = nextPoint;
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
void WaterOptions::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
|
||||
case IDC_HEIGHT_POPUP:
|
||||
*pMin = 0;
|
||||
*pMax = 255*MAP_HEIGHT_SCALE;
|
||||
*pInitial = m_waterHeight;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void WaterOptions::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit;
|
||||
switch (sliderID) {
|
||||
|
||||
|
||||
case IDC_HEIGHT_POPUP:
|
||||
m_waterHeight = theVal;
|
||||
str.Format("%d",m_waterHeight);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
m_updating = true;
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
startUpdateHeight();
|
||||
updateHeight();
|
||||
m_updating = false;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void WaterOptions::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_HEIGHT_POPUP:
|
||||
updateHeight();
|
||||
endUpdateHeight();
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
void WaterOptions::startUpdateHeight(void)
|
||||
{
|
||||
PolygonTrigger *theTrigger = WaypointOptions::getSingleSelectedPolygon();
|
||||
if (!theTrigger) {
|
||||
REF_PTR_RELEASE(m_moveUndoable);
|
||||
return;
|
||||
}
|
||||
if (!theTrigger->isWaterArea()) {
|
||||
REF_PTR_RELEASE(m_moveUndoable);
|
||||
return;
|
||||
}
|
||||
if (m_moveUndoable && theTrigger == m_moveUndoable->getTrigger()) {
|
||||
return;
|
||||
}
|
||||
m_originalHeight = theTrigger->getPoint(0)->z;
|
||||
Int i;
|
||||
for (i=0; i<theTrigger->getNumPoints(); i++) {
|
||||
ICoord3D loc = *theTrigger->getPoint(i);
|
||||
loc.z = m_originalHeight;
|
||||
theTrigger->setPoint(loc, i);
|
||||
}
|
||||
m_moveUndoable = new MovePolygonUndoable(theTrigger);
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
pDoc->AddAndDoUndoable(m_moveUndoable);
|
||||
}
|
||||
|
||||
|
||||
void WaterOptions::updateHeight(void)
|
||||
{
|
||||
PolygonTrigger *theTrigger = WaypointOptions::getSingleSelectedPolygon();
|
||||
if (!theTrigger || !m_moveUndoable) {
|
||||
REF_PTR_RELEASE(m_moveUndoable); // belongs to pDoc now.
|
||||
return;
|
||||
}
|
||||
if (!theTrigger->isWaterArea()) {
|
||||
REF_PTR_RELEASE(m_moveUndoable); // belongs to pDoc now.
|
||||
return;
|
||||
}
|
||||
if (theTrigger != m_moveUndoable->getTrigger()) {
|
||||
REF_PTR_RELEASE(m_moveUndoable); // belongs to pDoc now.
|
||||
return;
|
||||
}
|
||||
ICoord3D iLoc;
|
||||
Int dz = m_waterHeight - m_originalHeight;
|
||||
iLoc.x = 0;
|
||||
iLoc.y = 0;
|
||||
iLoc.z = dz;
|
||||
m_moveUndoable->SetOffset(iLoc);
|
||||
WbView3d *pView = CWorldBuilderDoc::GetActive3DView();
|
||||
pView->Invalidate();
|
||||
}
|
||||
|
||||
void WaterOptions::endUpdateHeight(void)
|
||||
{
|
||||
REF_PTR_RELEASE(m_moveUndoable); // belongs to pDoc now.
|
||||
}
|
||||
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void WaterOptions::OnChangeHeightEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int height;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &height)) {
|
||||
m_waterHeight = height;
|
||||
startUpdateHeight();
|
||||
updateHeight();
|
||||
endUpdateHeight();
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void WaterOptions::OnChangeSpacingEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SPACING);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int height;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &height)) {
|
||||
m_waterPointSpacing = height;
|
||||
} else {
|
||||
sprintf(buffer, "%d", m_waterPointSpacing);
|
||||
pEdit->SetWindowText(buffer);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
536
GeneralsMD/Code/Tools/WorldBuilder/src/WaterTool.cpp
Normal file
536
GeneralsMD/Code/Tools/WorldBuilder/src/WaterTool.cpp
Normal file
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// WaterTool.cpp
|
||||
// Water area tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "WaterTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "PointerTool.h"
|
||||
#include "TerrainMaterial.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "MainFrm.h"
|
||||
#include "DrawObject.h"
|
||||
#include "GameLogic/PolygonTrigger.h"
|
||||
#include "Common/GlobalData.h"
|
||||
|
||||
//
|
||||
// WaterTool class.
|
||||
//
|
||||
Bool WaterTool::m_water_isActive = false;
|
||||
|
||||
/// Constructor
|
||||
WaterTool::WaterTool(void)
|
||||
{
|
||||
m_toolID = ID_WATER_TOOL;
|
||||
m_cursorID = IDC_WATER;
|
||||
|
||||
m_currentZ = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Destructor
|
||||
WaterTool::~WaterTool(void)
|
||||
{
|
||||
if (m_poly_plusCursor) {
|
||||
::DestroyCursor(m_poly_plusCursor);
|
||||
}
|
||||
if (m_poly_moveCursor) {
|
||||
::DestroyCursor(m_poly_moveCursor);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears it's is active flag.
|
||||
void WaterTool::deactivate()
|
||||
{
|
||||
PolygonTool::deactivate();
|
||||
m_water_isActive = false;
|
||||
}
|
||||
|
||||
static Bool doIt = false;
|
||||
|
||||
/// Shows the no options panel.
|
||||
void WaterTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_WATER_OPTIONS);
|
||||
m_water_isActive = true;
|
||||
PointerTool::clearSelection();
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
m_poly_isActive = false;
|
||||
m_poly_isAdding = false;
|
||||
m_poly_mouseUpMove = false;
|
||||
m_poly_mouseUpPlus = false;
|
||||
m_currentZ = WaterOptions::getHeight();
|
||||
doIt = true;
|
||||
}
|
||||
|
||||
|
||||
#define WATER_FILL
|
||||
#define INTENSE_DEBUG
|
||||
|
||||
/// Perform the tool behavior on mouse down.
|
||||
void WaterTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
if (WaterOptions::getCreatingWaterAreas()) {
|
||||
fillTheArea(m, viewPt, pView, pDoc);
|
||||
return;
|
||||
}
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
m_poly_unsnappedMouseDownPt = docPt;
|
||||
poly_pickOnMouseDown(viewPt, pView);
|
||||
if (!poly_snapToPoly(&docPt)) {
|
||||
pView->snapPoint(&docPt);
|
||||
}
|
||||
docPt.z = m_currentZ;
|
||||
m_poly_mouseDownPt = docPt;
|
||||
if (m_poly_curSelectedPolygon) {
|
||||
DEBUG_ASSERTCRASH(m_poly_curSelectedPolygon->isWaterArea(), ("Should be water."));
|
||||
m_currentZ = m_poly_curSelectedPolygon->getPoint(0)->z;
|
||||
m_poly_mouseDownPt.z = m_currentZ;
|
||||
}
|
||||
startMouseDown(m, viewPt, pView, pDoc);
|
||||
m_poly_curSelectedPolygon->setWaterArea(true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Set the cursor. */
|
||||
void WaterTool::setCursor(void)
|
||||
{
|
||||
if (m_poly_mouseUpPlus || (m_poly_isAdding && m_poly_curSelectedPolygon)) {
|
||||
if (m_poly_plusCursor == NULL) {
|
||||
m_poly_plusCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_WATER_PLUS));
|
||||
}
|
||||
::SetCursor(m_poly_plusCursor);
|
||||
} else if (m_poly_mouseUpMove) {
|
||||
if (m_poly_moveCursor == NULL) {
|
||||
m_poly_moveCursor = AfxGetApp()->LoadCursor(MAKEINTRESOURCE(IDC_WATER_MOVE));
|
||||
}
|
||||
::SetCursor(m_poly_moveCursor);
|
||||
} else {
|
||||
Tool::setCursor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Left button move code.
|
||||
void WaterTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
if (m == TRACK_NONE) {
|
||||
PolygonTrigger *pCur = m_poly_curSelectedPolygon;
|
||||
Int curPt = m_poly_dragPointNdx;
|
||||
m_poly_unsnappedMouseDownPt = docPt;
|
||||
poly_pickOnMouseDown(viewPt, pView);
|
||||
m_poly_mouseUpPlus = false;
|
||||
m_poly_mouseUpMove = false;
|
||||
if (m_poly_curSelectedPolygon) {
|
||||
if (poly_pickPoint(m_poly_curSelectedPolygon, viewPt, pView) >= 0) {
|
||||
m_poly_mouseUpMove = true;
|
||||
} else {
|
||||
m_poly_mouseUpPlus = true;
|
||||
}
|
||||
}
|
||||
m_poly_curSelectedPolygon = pCur;
|
||||
m_poly_dragPointNdx = curPt;
|
||||
return; // setCursor will use the value of m_mouseUpRotate. jba.
|
||||
}
|
||||
|
||||
if (m != TRACK_L) return;
|
||||
if (!poly_snapToPoly(&docPt)) {
|
||||
pView->snapPoint(&docPt);
|
||||
}
|
||||
if (m_poly_moveUndoable) {
|
||||
ICoord3D iDocPt;
|
||||
iDocPt.x = floor(docPt.x+0.5f-m_poly_mouseDownPt.x);
|
||||
iDocPt.y = floor(docPt.y+0.5f-m_poly_mouseDownPt.y);
|
||||
iDocPt.z = 0;
|
||||
m_poly_moveUndoable->SetOffset(iDocPt);
|
||||
pView->Invalidate();
|
||||
return;
|
||||
}
|
||||
if (m_poly_dragPointNdx >= 0 && m_poly_curSelectedPolygon) {
|
||||
ICoord3D iDocPt;
|
||||
iDocPt.x = floor(docPt.x+0.5f);
|
||||
iDocPt.y = floor(docPt.y+0.5f);
|
||||
iDocPt.z = m_currentZ;
|
||||
m_poly_curSelectedPolygon->setPoint(iDocPt, m_poly_dragPointNdx);
|
||||
pView->Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/** Mouse up - not much. */
|
||||
void WaterTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
REF_PTR_RELEASE(m_poly_moveUndoable); // belongs to pDoc now.
|
||||
}
|
||||
|
||||
inline static Real mapZtoHeight(UnsignedByte mapZ) {
|
||||
return (mapZ * MAP_HEIGHT_SCALE)+0.01f;
|
||||
}
|
||||
|
||||
/// Perform the fill water area on mouse down.
|
||||
void WaterTool::fillTheArea(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
Int waterHeight = m_currentZ;
|
||||
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
|
||||
Int pointLimit = 3000; // In case we get lost.
|
||||
|
||||
PolygonTrigger *pNew = newInstance(PolygonTrigger)(100);
|
||||
AsciiString name;
|
||||
name.format("Water Area %d", pNew->getID());
|
||||
pNew->setTriggerName(name);
|
||||
|
||||
CPoint ndx;
|
||||
Int width = 1;
|
||||
getCenterIndex(&docPt, width, &ndx, pDoc);
|
||||
Int i, j;
|
||||
i = ndx.x;
|
||||
j = ndx.y;
|
||||
|
||||
WorldHeightMapEdit *pMap = pDoc->GetHeightMap();
|
||||
if (i<0 || i>=pMap->getXExtent()-1) return;
|
||||
if (j<0 || j>=pMap->getYExtent()-1) return;
|
||||
|
||||
Int intMapHeight = pMap->getHeight(i, j);
|
||||
if (waterHeight<mapZtoHeight(intMapHeight)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move left till we find an edge.
|
||||
while (i>0) {
|
||||
i--;
|
||||
intMapHeight = pMap->getHeight(i, j);
|
||||
if (waterHeight<mapZtoHeight(intMapHeight)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Bool bottom = true; // bottom left is dry, bottom right wet.
|
||||
Bool right = false; // right top is wet, right bottom dry.
|
||||
Bool left = false; // left bottom wet, left top dry.
|
||||
Bool top = false; // top left wet, top right dry.
|
||||
|
||||
Int startI = -1;
|
||||
Int startJ = -1;
|
||||
Bool firstTime = true;
|
||||
|
||||
// Build area in index coords for now, convert later.
|
||||
ICoord3D pt;
|
||||
ICoord3D tmpPt;
|
||||
Int curPoint = 0;
|
||||
while (pointLimit>0) {
|
||||
if (curPoint>2 && i==startI && j==startJ) break;
|
||||
pointLimit--;
|
||||
pt.x = i*MAP_XY_FACTOR;
|
||||
pt.y = j*MAP_XY_FACTOR;
|
||||
pt.z = waterHeight;
|
||||
intMapHeight = pMap->getHeight(i, j);
|
||||
#ifdef INTENSE_DEBUG
|
||||
if (bottom) {
|
||||
DEBUG_LOG(("Bottom %d,%d\n", i, j));
|
||||
} else if (left) {
|
||||
DEBUG_LOG(("Left %d,%d\n", i, j));
|
||||
} else if (right) {
|
||||
DEBUG_LOG(("Right %d,%d\n", i, j));
|
||||
} else if (top) {
|
||||
DEBUG_LOG(("Top %d,%d\n", i, j));
|
||||
}
|
||||
#endif
|
||||
if (bottom) {
|
||||
bottom = false;
|
||||
if (waterHeight<mapZtoHeight(intMapHeight)) {
|
||||
DEBUG_ASSERTCRASH(waterHeight>mapZtoHeight(pMap->getHeight(i+1, j)), ("Logic error. jba."));
|
||||
Real dx = (mapZtoHeight(pMap->getHeight(i,j))-waterHeight) / (mapZtoHeight(pMap->getHeight(i,j)-pMap->getHeight(i+1,j)));
|
||||
pt.x += dx*MAP_XY_FACTOR;
|
||||
Bool topLeftDry = (waterHeight<mapZtoHeight(pMap->getHeight(i, j+1)));
|
||||
Bool topRightDry = (waterHeight<mapZtoHeight(pMap->getHeight(i+1, j+1)));
|
||||
if (topRightDry) {
|
||||
i++;
|
||||
left = true;
|
||||
} else if (topLeftDry) {
|
||||
if (j<pMap->getYExtent()-2) {
|
||||
j++;
|
||||
bottom = true;
|
||||
} else {
|
||||
tmpPt = pt;
|
||||
pt.y += MAP_XY_FACTOR;
|
||||
tmpPt.x -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
tmpPt.y -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
pNew->addPoint(tmpPt);
|
||||
left = true;
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
if (i>0) {
|
||||
i--;
|
||||
right = true;
|
||||
} else {
|
||||
j++;
|
||||
bottom = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_ASSERTCRASH(i==0, ("Logic error. jba."));
|
||||
left = true;
|
||||
while (j<pMap->getYExtent()-2 && waterHeight>mapZtoHeight(pMap->getHeight(i, j+1))) {
|
||||
j++;
|
||||
}
|
||||
pt.y = (j)*MAP_XY_FACTOR;
|
||||
if (waterHeight>mapZtoHeight(pMap->getHeight(i, j+1))) {
|
||||
pt.y = (j+1)*MAP_XY_FACTOR;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
} else if (left) {
|
||||
left = false;
|
||||
if (waterHeight<mapZtoHeight(pMap->getHeight(i, j+1))) {
|
||||
DEBUG_ASSERTCRASH(waterHeight>mapZtoHeight(intMapHeight), ("Logic error. jba."));
|
||||
Real dy = (waterHeight-mapZtoHeight(pMap->getHeight(i,j))) / (mapZtoHeight(pMap->getHeight(i,j+1)-pMap->getHeight(i,j)));
|
||||
pt.y += dy*MAP_XY_FACTOR;
|
||||
Bool bottomRightDry = (waterHeight<mapZtoHeight(pMap->getHeight(i+1, j)));
|
||||
Bool topRightDry = (waterHeight<mapZtoHeight(pMap->getHeight(i+1, j+1)));
|
||||
if (bottomRightDry) {
|
||||
j--;
|
||||
top = true;
|
||||
} else if (topRightDry) {
|
||||
if (i<pMap->getXExtent()-2) {
|
||||
i++;
|
||||
left = true;
|
||||
} else {
|
||||
tmpPt = pt;
|
||||
pt.x += MAP_XY_FACTOR;
|
||||
tmpPt.x -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
tmpPt.y -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
pNew->addPoint(tmpPt);
|
||||
top = true;
|
||||
j--;
|
||||
}
|
||||
} else {
|
||||
if (j<pMap->getYExtent()-1) {
|
||||
j++;
|
||||
bottom = true;
|
||||
} else {
|
||||
i++;
|
||||
left = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pt.y = (j+1)*MAP_XY_FACTOR;
|
||||
DEBUG_ASSERTCRASH(j==pMap->getYExtent()-2, ("Logic error. jba."));
|
||||
while (i<pMap->getXExtent()-2 && waterHeight>mapZtoHeight(pMap->getHeight(i+1, j+1))) {
|
||||
i++;
|
||||
}
|
||||
top = true;
|
||||
pt.x = i*MAP_XY_FACTOR;
|
||||
if (waterHeight>mapZtoHeight(pMap->getHeight(i+1, j+1))) {
|
||||
pt.x = (i+1)*MAP_XY_FACTOR;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (right) {
|
||||
right = false;
|
||||
pt.x = (i+1)*MAP_XY_FACTOR;
|
||||
if (waterHeight<mapZtoHeight(pMap->getHeight(i+1, j))) {
|
||||
DEBUG_ASSERTCRASH(waterHeight>mapZtoHeight(pMap->getHeight(i+1,j+1)), ("Logic error. jba."));
|
||||
Real dy = (mapZtoHeight(pMap->getHeight(i+1,j))-waterHeight) / (mapZtoHeight(pMap->getHeight(i+1,j)-pMap->getHeight(i+1,j+1)));
|
||||
pt.y += dy*MAP_XY_FACTOR;
|
||||
Bool bottomLeftDry = (waterHeight<mapZtoHeight(pMap->getHeight(i, j)));
|
||||
Bool topLeftDry = (waterHeight<mapZtoHeight(pMap->getHeight(i, j+1)));
|
||||
if (topLeftDry) {
|
||||
j++;
|
||||
bottom = true;
|
||||
} else if (bottomLeftDry) {
|
||||
if (i>0) {
|
||||
i--;
|
||||
right = true;
|
||||
} else {
|
||||
tmpPt = pt;
|
||||
pt.x -= MAP_XY_FACTOR;
|
||||
tmpPt.x -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
tmpPt.y -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
pNew->addPoint(tmpPt);
|
||||
bottom = true;
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
if (j>0) {
|
||||
j--;
|
||||
top = true;
|
||||
} else {
|
||||
i--;
|
||||
right = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_ASSERTCRASH(j==0, ("Logic error. jba."));
|
||||
while (i>0 && waterHeight>mapZtoHeight(pMap->getHeight(i, j))) {
|
||||
i--;
|
||||
}
|
||||
bottom = true;
|
||||
pt.x = (i)*MAP_XY_FACTOR;
|
||||
if (waterHeight<mapZtoHeight(pMap->getHeight(i, j))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (top) {
|
||||
top = false;
|
||||
pt.y = (j+1)*MAP_XY_FACTOR;
|
||||
if (waterHeight<mapZtoHeight(pMap->getHeight(i+1, j+1))) {
|
||||
DEBUG_ASSERTCRASH(waterHeight>mapZtoHeight(pMap->getHeight(i, j+1)), ("Logic error. jba."));
|
||||
Real dx = (waterHeight-mapZtoHeight(pMap->getHeight(i,j+1))) / (mapZtoHeight(pMap->getHeight(i+1,j+1)-pMap->getHeight(i,j+1)));
|
||||
pt.x += dx*MAP_XY_FACTOR;
|
||||
Bool bottomLeftDry = (waterHeight<mapZtoHeight(pMap->getHeight(i, j)));
|
||||
Bool bottomRightDry = (waterHeight<mapZtoHeight(pMap->getHeight(i+1, j)));
|
||||
if (bottomLeftDry) {
|
||||
i--;
|
||||
right = true;
|
||||
} else if (bottomRightDry) {
|
||||
if (j>0) {
|
||||
j--;
|
||||
top = true;
|
||||
} else {
|
||||
tmpPt = pt;
|
||||
pt.y -= MAP_XY_FACTOR;
|
||||
tmpPt.x -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
tmpPt.y -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
pNew->addPoint(tmpPt);
|
||||
i--;
|
||||
right = true;
|
||||
}
|
||||
} else {
|
||||
if (i<pMap->getXExtent()-2) {
|
||||
i++;
|
||||
left = true;
|
||||
} else {
|
||||
j--;
|
||||
top = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_ASSERTCRASH(i==pMap->getXExtent()-2, ("Logic error. jba."));
|
||||
while (j>0 && waterHeight>mapZtoHeight(pMap->getHeight(i+1, j))) {
|
||||
j--;
|
||||
}
|
||||
right = true;
|
||||
pt.y = (j+1)*MAP_XY_FACTOR;
|
||||
if (waterHeight>mapZtoHeight(pMap->getHeight(i, j))) {
|
||||
pt.y = (j)*MAP_XY_FACTOR;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
pt.x = (i+1)*MAP_XY_FACTOR;
|
||||
}
|
||||
} else {
|
||||
DEBUG_CRASH(("Logic error. jba.")); // shouldn't get here.
|
||||
}
|
||||
pt.x -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
pt.y -= pMap->getBorderSize()*MAP_XY_FACTOR;
|
||||
pNew->addPoint(pt);
|
||||
curPoint++;
|
||||
if (firstTime) {
|
||||
startI = i;
|
||||
startJ = j;
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (pNew->getNumPoints()>2) {
|
||||
PolygonTrigger *pBetter = adjustSpacing(pNew, WaterOptions::getSpacing());
|
||||
pNew->deleteInstance();
|
||||
pNew = pBetter;
|
||||
pNew->setWaterArea(true);
|
||||
AddPolygonUndoable *pUndo = new AddPolygonUndoable(pNew);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
m_poly_curSelectedPolygon = pNew;
|
||||
m_poly_dragPointNdx = -1;
|
||||
WaterOptions::update();
|
||||
} else {
|
||||
pNew->deleteInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Adjust the spacing.
|
||||
PolygonTrigger * WaterTool::adjustSpacing(PolygonTrigger *trigger, Real spacing)
|
||||
{
|
||||
PolygonTrigger *pNew = newInstance(PolygonTrigger)(trigger->getNumPoints());
|
||||
// Real endLen=0;
|
||||
Int i;
|
||||
Real curSpacingLen = spacing;
|
||||
ICoord3D pt = *trigger->getPoint(0);
|
||||
pNew->addPoint(pt);
|
||||
Bool didCurPoint = true;
|
||||
for (i=0; i<trigger->getNumPoints()-1; i++) {
|
||||
ICoord3D curPt = *trigger->getPoint(i);
|
||||
ICoord3D nextPt = *trigger->getPoint(i+1);
|
||||
Real dx = nextPt.x-curPt.x;
|
||||
Real dy = nextPt.y-curPt.y;
|
||||
Real curLen = sqrt(dx*dx+dy*dy);
|
||||
if (curLen > 4*MAP_XY_FACTOR && curLen>2*spacing) {
|
||||
if (!didCurPoint) pNew->addPoint(curPt);
|
||||
else pNew->setPoint(curPt, pNew->getNumPoints()-1);
|
||||
pNew->addPoint(nextPt);
|
||||
didCurPoint = true;
|
||||
curSpacingLen = spacing;
|
||||
} else if (curSpacingLen>curLen) {
|
||||
curSpacingLen -= curLen;
|
||||
} else {
|
||||
while (curLen >= curSpacingLen) {
|
||||
// cur len > curSpacingLen.
|
||||
Real factor = curSpacingLen/curLen;
|
||||
curPt.x += dx*factor;
|
||||
curPt.y += dy*factor;
|
||||
pNew->addPoint(curPt);
|
||||
didCurPoint = false;
|
||||
dx = nextPt.x-curPt.x;
|
||||
dy = nextPt.y-curPt.y;
|
||||
curLen -= curSpacingLen;
|
||||
curSpacingLen = spacing;
|
||||
}
|
||||
curSpacingLen -= curLen;
|
||||
if ((curLen)<MAP_XY_FACTOR/2) {
|
||||
didCurPoint = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
pNew->setTriggerName(trigger->getTriggerName());
|
||||
return pNew;
|
||||
}
|
||||
571
GeneralsMD/Code/Tools/WorldBuilder/src/WaypointOptions.cpp
Normal file
571
GeneralsMD/Code/Tools/WorldBuilder/src/WaypointOptions.cpp
Normal file
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// WaypointOptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "CUndoable.h"
|
||||
#include "WaypointOptions.h"
|
||||
#include "WorldBuilder.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WbView3d.h"
|
||||
#include "PolygonTool.h"
|
||||
#include "WaypointTool.h"
|
||||
#include "GameLogic/PolygonTrigger.h"
|
||||
#include "GameLogic/Scripts.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "LayersList.h"
|
||||
|
||||
WaypointOptions *WaypointOptions::m_staticThis = NULL;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// WaypointOptions dialog trivial construstor - Create does the real work.
|
||||
|
||||
|
||||
WaypointOptions::WaypointOptions(CWnd* pParent /*=NULL*/):
|
||||
m_moveUndoable(NULL)
|
||||
{
|
||||
//{{AFX_DATA_INIT(WaypointOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
/// Windows default stuff.
|
||||
void WaypointOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(WaypointOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
MapObject *WaypointOptions::getSingleSelectedWaypoint(void)
|
||||
{
|
||||
MapObject *theMapObj = NULL;
|
||||
// Bool found = false;
|
||||
Int selCount=0;
|
||||
MapObject *pMapObj;
|
||||
for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) {
|
||||
if (pMapObj->isSelected()) {
|
||||
if (pMapObj->isWaypoint()) {
|
||||
theMapObj = pMapObj;
|
||||
}
|
||||
selCount++;
|
||||
}
|
||||
}
|
||||
if (selCount==1 && theMapObj) {
|
||||
return theMapObj;
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
PolygonTrigger *WaypointOptions::getSingleSelectedPolygon(void)
|
||||
{
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc==NULL) return NULL;
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
Bool showPoly = false;
|
||||
if (p3View) {
|
||||
showPoly = p3View->isPolygonTriggerVisible();
|
||||
}
|
||||
if (showPoly || PolygonTool::isActive()) {
|
||||
for (PolygonTrigger *pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
|
||||
if (PolygonTool::isSelected(pTrig)) {
|
||||
return pTrig;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void WaypointOptions::updateTheUI(void)
|
||||
{
|
||||
Tool *curTool = ((CWorldBuilderApp*)AfxGetApp())->getCurTool();
|
||||
|
||||
Bool isWaypointTool = (curTool && (curTool->getToolID() == ID_WAYPOINT_TOOL));
|
||||
MapObject *theMapObj = getSingleSelectedWaypoint();
|
||||
PolygonTrigger *theTrigger = WaypointOptions::getSingleSelectedPolygon();
|
||||
|
||||
CWnd *pWnd = this->GetDlgItem(IDC_WAYPOINTNAME_EDIT);
|
||||
CWnd *pCaption1 = this->GetDlgItem(IDC_WAYPOINT_CAPTION1);
|
||||
CWnd *pCaption2 = this->GetDlgItem(IDC_WAYPOINT_PATHLABELS);
|
||||
CWnd *pCaption3 = this->GetDlgItem(65535);
|
||||
CWnd *pCaption4 = this->GetDlgItem(65534);
|
||||
CWnd *pCaption5 = this->GetDlgItem(IDC_LIST_WAYPOINTS);
|
||||
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_WAYPOINTNAME_EDIT);
|
||||
CComboBox *pListWayptNames = (CComboBox*)GetDlgItem(IDC_LIST_OF_WAYPOINT_NAMES);
|
||||
|
||||
CWnd *pWaypointLabel1 = GetDlgItem(IDC_WAYPOINTLABEL1_EDIT);
|
||||
CWnd *pWaypointLabel2 = GetDlgItem(IDC_WAYPOINTLABEL2_EDIT);
|
||||
CWnd *pWaypointLabel3 = GetDlgItem(IDC_WAYPOINTLABEL3_EDIT);
|
||||
|
||||
CWnd *pWaypointLocation = this->GetDlgItem(IDC_WAYPOINT_LOCATION);
|
||||
CWnd *pWaypointX = GetDlgItem(IDC_WAYPOINT_LOCATIONX);
|
||||
CWnd *pWaypointY = GetDlgItem(IDC_WAYPOINT_LOCATIONY);
|
||||
CButton *pBiDirCheck = (CButton *)GetDlgItem(IDC_WAYPOINT_BIDIRECTIONAL);
|
||||
|
||||
if (theTrigger) {
|
||||
pCaption1->ShowWindow(SW_SHOW);
|
||||
pWnd->ShowWindow(SW_SHOW);
|
||||
} else {
|
||||
pCaption1->ShowWindow(SW_HIDE);
|
||||
pCaption2->ShowWindow(SW_HIDE);
|
||||
pWnd->ShowWindow(SW_HIDE);
|
||||
}
|
||||
|
||||
if (pCombo && !theTrigger) {
|
||||
pCombo->ResetContent();
|
||||
pCombo->AddString((TheNameKeyGenerator->keyToName(TheKey_InitialCameraPosition)).str());
|
||||
pCombo->AddString((TheNameKeyGenerator->keyToName(TheKey_Player_1_Start)).str());
|
||||
pCombo->AddString((TheNameKeyGenerator->keyToName(TheKey_Player_2_Start)).str());
|
||||
pCombo->AddString((TheNameKeyGenerator->keyToName(TheKey_Player_3_Start)).str());
|
||||
pCombo->AddString((TheNameKeyGenerator->keyToName(TheKey_Player_4_Start)).str());
|
||||
pCombo->AddString((TheNameKeyGenerator->keyToName(TheKey_Player_5_Start)).str());
|
||||
pCombo->AddString((TheNameKeyGenerator->keyToName(TheKey_Player_6_Start)).str());
|
||||
pCombo->AddString((TheNameKeyGenerator->keyToName(TheKey_Player_7_Start)).str());
|
||||
pCombo->AddString((TheNameKeyGenerator->keyToName(TheKey_Player_8_Start)).str());
|
||||
pCombo->ShowWindow(SW_SHOW);
|
||||
} else if (pCombo && theTrigger) {
|
||||
pCombo->ResetContent();
|
||||
AsciiString trigger;
|
||||
trigger = INNER_PERIMETER;
|
||||
trigger.concat("1");
|
||||
pCombo->AddString(trigger.str());
|
||||
trigger = OUTER_PERIMETER;
|
||||
trigger.concat("1");
|
||||
pCombo->AddString(trigger.str());
|
||||
trigger = INNER_PERIMETER;
|
||||
trigger.concat("2");
|
||||
pCombo->AddString(trigger.str());
|
||||
trigger = OUTER_PERIMETER;
|
||||
trigger.concat("2");
|
||||
pCombo->AddString(trigger.str());
|
||||
trigger = INNER_PERIMETER;
|
||||
trigger.concat("3");
|
||||
pCombo->AddString(trigger.str());
|
||||
trigger = OUTER_PERIMETER;
|
||||
trigger.concat("3");
|
||||
pCombo->AddString(trigger.str());
|
||||
trigger = INNER_PERIMETER;
|
||||
trigger.concat("4");
|
||||
pCombo->AddString(trigger.str());
|
||||
trigger = OUTER_PERIMETER;
|
||||
trigger.concat("4");
|
||||
pCombo->AddString(trigger.str());
|
||||
pCombo->ShowWindow(SW_SHOW);
|
||||
}
|
||||
|
||||
// display the list of waypoint names drop down menu
|
||||
if (pListWayptNames && !theTrigger) {
|
||||
|
||||
// reset everything and start fresh again
|
||||
pListWayptNames->ResetContent();
|
||||
|
||||
// get the first map object, and then cycle through the rest
|
||||
MapObject *tempObj = MapObject::getFirstMapObject();
|
||||
while (true) {
|
||||
|
||||
if (!tempObj)
|
||||
break;
|
||||
|
||||
// if it is a waypoint, add its name to the combo box
|
||||
if (tempObj->isWaypoint())
|
||||
pListWayptNames->AddString(tempObj->getWaypointName().str());
|
||||
|
||||
tempObj = tempObj->getNext();
|
||||
}
|
||||
|
||||
// make sure the window is displayed
|
||||
pCombo->ShowWindow(SW_SHOW);
|
||||
}
|
||||
|
||||
if ((theMapObj || isWaypointTool) && pWnd ) {
|
||||
if (theMapObj) {
|
||||
Bool exists;
|
||||
pWnd->EnableWindow();
|
||||
pWnd->SetWindowText(theMapObj->getProperties()->getAsciiString(TheKey_waypointName).str());
|
||||
pCaption1->SetWindowText("Waypoint name:");
|
||||
SetWindowText("Waypoint Options");
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
|
||||
/* display location of waypoint */
|
||||
|
||||
pWaypointLocation->ShowWindow(SW_SHOW);
|
||||
pWaypointY->ShowWindow(SW_SHOW);
|
||||
pWaypointX->ShowWindow(SW_SHOW);
|
||||
pListWayptNames->ShowWindow(SW_SHOW);
|
||||
pCaption5->ShowWindow(SW_SHOW);
|
||||
const Coord3D *waypointLocation = getSingleSelectedWaypoint()->getLocation();
|
||||
AsciiString locX, locY;
|
||||
|
||||
// convert the location coordinates to strings
|
||||
locX.format("%f", waypointLocation->x);
|
||||
locY.format("%f", waypointLocation->y);
|
||||
|
||||
// set the window text to reflect the current position of the waypoint
|
||||
pWaypointX->SetWindowText(locX.str());
|
||||
pWaypointY->SetWindowText(locY.str());
|
||||
|
||||
if (pDoc->isWaypointLinked(theMapObj)) {
|
||||
pCaption2->ShowWindow(SW_SHOW);
|
||||
AsciiString name;
|
||||
name = theMapObj->getProperties()->getAsciiString(TheKey_waypointPathLabel1, &exists);
|
||||
pWaypointLabel1->ShowWindow(SW_SHOW);
|
||||
pWaypointLabel1->SetWindowText(name.str());
|
||||
name = theMapObj->getProperties()->getAsciiString(TheKey_waypointPathLabel2, &exists);
|
||||
pWaypointLabel2->ShowWindow(SW_SHOW);
|
||||
pWaypointLabel2->SetWindowText(name.str());
|
||||
name = theMapObj->getProperties()->getAsciiString(TheKey_waypointPathLabel3, &exists);
|
||||
pWaypointLabel3->ShowWindow(SW_SHOW);
|
||||
pWaypointLabel3->SetWindowText(name.str());
|
||||
} else {
|
||||
pCaption2->ShowWindow(SW_HIDE);
|
||||
pWaypointLabel1->ShowWindow(SW_HIDE);
|
||||
pWaypointLabel2->ShowWindow(SW_HIDE);
|
||||
pWaypointLabel3->ShowWindow(SW_HIDE);
|
||||
}
|
||||
if (pBiDirCheck) {
|
||||
pBiDirCheck->ShowWindow(SW_SHOW);
|
||||
Bool checked = theMapObj->getProperties()->getBool(TheKey_waypointPathBiDirectional, &exists);
|
||||
pBiDirCheck->SetCheck(checked);
|
||||
}
|
||||
}
|
||||
} else if (theTrigger && pWnd) {
|
||||
pListWayptNames->ShowWindow(SW_HIDE);
|
||||
pWaypointLocation->ShowWindow(SW_HIDE);
|
||||
pWaypointY->ShowWindow(SW_HIDE);
|
||||
pWaypointX->ShowWindow(SW_HIDE);
|
||||
pCaption3->ShowWindow(SW_HIDE);
|
||||
pCaption4->ShowWindow(SW_HIDE);
|
||||
pCaption5->ShowWindow(SW_HIDE);
|
||||
pWaypointLabel1->ShowWindow(SW_HIDE);
|
||||
pWaypointLabel2->ShowWindow(SW_HIDE);
|
||||
pWaypointLabel3->ShowWindow(SW_HIDE);
|
||||
pCaption2->ShowWindow(SW_HIDE);
|
||||
pBiDirCheck->ShowWindow(SW_HIDE);
|
||||
pCaption1->SetWindowText("Area name:");
|
||||
SetWindowText("Area Trigger Options");
|
||||
pWnd->SetWindowText(theTrigger->getTriggerName().str());
|
||||
pWnd->EnableWindow();
|
||||
} else if (pWnd) {
|
||||
pCaption2->ShowWindow(SW_HIDE);
|
||||
pWnd->EnableWindow(false);
|
||||
pWnd->SetWindowText("");
|
||||
}
|
||||
}
|
||||
|
||||
void WaypointOptions::update(void)
|
||||
{
|
||||
if (m_staticThis) {
|
||||
m_staticThis->updateTheUI();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WaypointOptions message handlers
|
||||
|
||||
/// Dialog UI initialization.
|
||||
/** Creates the slider controls, and sets the initial values for
|
||||
width and feather in the ui controls. */
|
||||
BOOL WaypointOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(WaypointOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(WaypointOptions)
|
||||
ON_CBN_KILLFOCUS(IDC_WAYPOINTNAME_EDIT, OnChangeWaypointnameEdit)
|
||||
ON_CBN_KILLFOCUS(IDC_LIST_OF_WAYPOINT_NAMES, OnChangeSelectedWaypoint)
|
||||
ON_EN_CHANGE(IDC_WAYPOINT_LOCATIONX, OnEditWaypointLocationX)
|
||||
ON_EN_CHANGE(IDC_WAYPOINT_LOCATIONY, OnEditWaypointLocationY)
|
||||
ON_EN_CHANGE(IDC_WAYPOINTLABEL1_EDIT, OnEditchangeWaypointlabel1Edit)
|
||||
ON_EN_CHANGE(IDC_WAYPOINTLABEL2_EDIT, OnEditchangeWaypointlabel2Edit)
|
||||
ON_EN_CHANGE(IDC_WAYPOINTLABEL3_EDIT, OnEditchangeWaypointlabel3Edit)
|
||||
ON_CBN_SELENDOK(IDC_LIST_OF_WAYPOINT_NAMES, OnChangeSelectedWaypoint)
|
||||
ON_CBN_SELENDOK(IDC_WAYPOINTNAME_EDIT, OnChangeWaypointnameEdit)
|
||||
ON_BN_CLICKED(IDC_WAYPOINT_BIDIRECTIONAL, OnWaypointBidirectional)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
void WaypointOptions::OnChangeSelectedWaypoint()
|
||||
{
|
||||
// deselect whatever is currently selected
|
||||
MapObject *currentlySelected = getSingleSelectedWaypoint(), *pMapObj, *waypt;
|
||||
if (!currentlySelected)
|
||||
return;
|
||||
currentlySelected->setSelected(false);
|
||||
|
||||
// retrieve information from dialog box, if user-typed -- sel will be -1, otherwise it will be >=0
|
||||
CString theText;
|
||||
CComboBox *pListWayptNames = (CComboBox*)GetDlgItem(IDC_LIST_OF_WAYPOINT_NAMES);
|
||||
Int sel = pListWayptNames->GetCurSel();
|
||||
if (sel >= 0) {
|
||||
pListWayptNames->GetLBText(sel, theText);
|
||||
} else {
|
||||
pListWayptNames->GetWindowText(theText);
|
||||
}
|
||||
AsciiString name((LPCTSTR)theText);
|
||||
|
||||
// find and store the waypoint that corresponds to the information in the dialog box
|
||||
Bool foundWaypoint = false;
|
||||
for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) {
|
||||
if (pMapObj->isWaypoint()) {
|
||||
if (pMapObj->getWaypointName() != AsciiString.TheEmptyString) {
|
||||
if (pMapObj->getWaypointName() == name) {
|
||||
foundWaypoint = true;
|
||||
waypt = pMapObj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no waypoint corresponds to whatever is in the dialog box, then don't do anything
|
||||
if (!foundWaypoint)
|
||||
return;
|
||||
|
||||
// select the waypoint and set camera position to position of waypoint
|
||||
waypt->setSelected(true);
|
||||
Coord3D pos = *waypt->getLocation();
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc) {
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
if (p3View) {
|
||||
p3View->setCenterInView(pos.x/MAP_XY_FACTOR, pos.y/MAP_XY_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void WaypointOptions::OnEditWaypointLocationX()
|
||||
{
|
||||
CString tempName;
|
||||
AsciiString name;
|
||||
|
||||
//make sure that only one waypoint is being selected currently
|
||||
MapObject *waypt = getSingleSelectedWaypoint();
|
||||
if (!waypt)
|
||||
return;
|
||||
|
||||
// get old waypoint information
|
||||
const Coord3D *waypointLocation = waypt->getLocation();
|
||||
Coord3D newWaypointLocation;
|
||||
|
||||
// retrieve information from dialog box
|
||||
CWnd *pWnd = GetDlgItem(IDC_WAYPOINT_LOCATIONX);
|
||||
pWnd->GetWindowText(tempName);
|
||||
name = ((LPCTSTR)tempName);
|
||||
|
||||
// make sure that there is a value in the field, otherwise funky stuff would happen
|
||||
if (name.isEmpty())
|
||||
return;
|
||||
|
||||
// determine new values of waypoint location
|
||||
newWaypointLocation.x = atof(name.str());
|
||||
newWaypointLocation.y = waypointLocation->y;
|
||||
newWaypointLocation.z = 0;
|
||||
|
||||
// set the new information into both the waypointa and the window
|
||||
waypt->setLocation(&newWaypointLocation);
|
||||
}
|
||||
|
||||
void WaypointOptions::OnEditWaypointLocationY()
|
||||
{
|
||||
CString tempName;
|
||||
AsciiString name;
|
||||
|
||||
//make sure that only one waypoint is being selected currently
|
||||
MapObject *waypt = getSingleSelectedWaypoint();
|
||||
if (!waypt)
|
||||
return;
|
||||
|
||||
// get old waypoint information
|
||||
const Coord3D *waypointLocation = waypt->getLocation();
|
||||
Coord3D newWaypointLocation;
|
||||
|
||||
// retrieve information from dialog box
|
||||
CWnd *pWnd = GetDlgItem(IDC_WAYPOINT_LOCATIONY);
|
||||
pWnd->GetWindowText(tempName);
|
||||
name = ((LPCTSTR)tempName);
|
||||
|
||||
// make sure that there is a value in the field, otherwise funky stuff would happen
|
||||
if (name.isEmpty())
|
||||
return;
|
||||
|
||||
// determine new values of waypoint location
|
||||
newWaypointLocation.y = atof(name.str());
|
||||
newWaypointLocation.x = waypointLocation->x;
|
||||
newWaypointLocation.z = 0;
|
||||
|
||||
// set the new information into both the waypointa and the window
|
||||
waypt->setLocation(&newWaypointLocation);
|
||||
}
|
||||
|
||||
Bool WaypointOptions::isUnique(AsciiString name, MapObject* theMapObj)
|
||||
{
|
||||
MapObject *pMapObj;
|
||||
Bool didMatch = false;
|
||||
for (pMapObj = MapObject::getFirstMapObject(); !didMatch && pMapObj; pMapObj = pMapObj->getNext()) {
|
||||
if (pMapObj->isWaypoint()) {
|
||||
if (pMapObj == theMapObj) continue; // don't check against self.
|
||||
AsciiString wayName = pMapObj->getWaypointName();
|
||||
if (name == wayName) {
|
||||
didMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (didMatch == false);
|
||||
}
|
||||
|
||||
AsciiString WaypointOptions::GenerateUniqueName(Int id)
|
||||
{
|
||||
AsciiString name;
|
||||
name.format("Waypoint %d", id);
|
||||
while (!isUnique(name)) {
|
||||
id++;
|
||||
name.format("Waypoint %d", id);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
void WaypointOptions::OnChangeWaypointnameEdit()
|
||||
{
|
||||
MapObject *theMapObj = getSingleSelectedWaypoint();
|
||||
PolygonTrigger *theTrigger = WaypointOptions::getSingleSelectedPolygon();
|
||||
|
||||
// get the combo box
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_WAYPOINTNAME_EDIT);
|
||||
if (pCombo) {
|
||||
// get the text out of the combo. If it is user-typed, sel will be -1, otherwise it will be >=0
|
||||
CString theText;
|
||||
Int sel = pCombo->GetCurSel();
|
||||
if (sel >= 0) {
|
||||
pCombo->GetLBText(sel, theText);
|
||||
} else {
|
||||
pCombo->GetWindowText(theText);
|
||||
}
|
||||
AsciiString name((LPCTSTR)theText);
|
||||
|
||||
// check to see if the user-entered name is already in use.
|
||||
Bool didMatch = false;
|
||||
|
||||
// check waypoint objects.
|
||||
didMatch = !isUnique(name, theMapObj);
|
||||
|
||||
// check trigger area objects
|
||||
PolygonTrigger *pTrig;
|
||||
for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); !didMatch && pTrig; pTrig = pTrig->getNext()) {
|
||||
if (pTrig==theTrigger) continue; // don't check against yourself.
|
||||
AsciiString trigName = pTrig->getTriggerName();
|
||||
if (name == trigName) {
|
||||
if (pTrig->isValid()) {
|
||||
didMatch = true;
|
||||
} else {
|
||||
PolygonTrigger::removePolygonTrigger(pTrig);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if there's a match, throw up a messagebox, otherwise set the name
|
||||
if (didMatch) {
|
||||
::AfxMessageBox("Name already in use");
|
||||
} else {
|
||||
if (theMapObj) {
|
||||
AsciiString layerName = TheLayersList->removeMapObjectFromLayersList(theMapObj);
|
||||
theMapObj->setWaypointName(name);
|
||||
theMapObj->validate();
|
||||
TheLayersList->addMapObjectToLayersList(theMapObj, layerName);
|
||||
} else if (theTrigger) {
|
||||
theTrigger->setTriggerName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WaypointOptions::OnEditchangeWaypointlabel1Edit()
|
||||
{
|
||||
changeWaypointLabel(IDC_WAYPOINTLABEL1_EDIT, TheKey_waypointPathLabel1);
|
||||
}
|
||||
|
||||
void WaypointOptions::changeWaypointLabel(Int editControlID, NameKeyType key)
|
||||
{
|
||||
MapObject *theMapObj = getSingleSelectedWaypoint();
|
||||
if (theMapObj==NULL) return;
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (!pDoc->isWaypointLinked(theMapObj)) {
|
||||
return;
|
||||
}
|
||||
// get the edit box
|
||||
CWnd *pWnd = GetDlgItem(editControlID);
|
||||
if (pWnd) {
|
||||
// get the text
|
||||
CString theText;
|
||||
pWnd->GetWindowText(theText);
|
||||
AsciiString name((LPCTSTR)theText);
|
||||
theMapObj->getProperties()->setAsciiString(key, name);
|
||||
pDoc->updateLinkedWaypointLabels(theMapObj);
|
||||
}
|
||||
}
|
||||
|
||||
void WaypointOptions::OnEditchangeWaypointlabel2Edit()
|
||||
{
|
||||
changeWaypointLabel(IDC_WAYPOINTLABEL2_EDIT, TheKey_waypointPathLabel2);
|
||||
}
|
||||
|
||||
void WaypointOptions::OnEditchangeWaypointlabel3Edit()
|
||||
{
|
||||
changeWaypointLabel(IDC_WAYPOINTLABEL3_EDIT, TheKey_waypointPathLabel3);
|
||||
}
|
||||
|
||||
void WaypointOptions::OnWaypointBidirectional()
|
||||
{
|
||||
MapObject *theMapObj = getSingleSelectedWaypoint();
|
||||
if (theMapObj==NULL) return;
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (!pDoc->isWaypointLinked(theMapObj)) {
|
||||
return;
|
||||
}
|
||||
// get the edit box
|
||||
CButton *pWnd = (CButton *)GetDlgItem(IDC_WAYPOINT_BIDIRECTIONAL);
|
||||
if (pWnd) {
|
||||
Bool checked = pWnd->GetCheck()==1;
|
||||
// get the text
|
||||
CString theText;
|
||||
pWnd->GetWindowText(theText);
|
||||
AsciiString name((LPCTSTR)theText);
|
||||
theMapObj->getProperties()->setBool(TheKey_waypointPathBiDirectional, checked);
|
||||
pDoc->updateLinkedWaypointLabels(theMapObj);
|
||||
}
|
||||
}
|
||||
208
GeneralsMD/Code/Tools/WorldBuilder/src/WaypointTool.cpp
Normal file
208
GeneralsMD/Code/Tools/WorldBuilder/src/WaypointTool.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// WaypointTool.cpp
|
||||
// Texture tiling tool for worldbuilder.
|
||||
// Author: John Ahlquist, April 2001
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include "WaypointTool.h"
|
||||
#include "PointerTool.h"
|
||||
#include "CUndoable.h"
|
||||
#include "TerrainMaterial.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "MainFrm.h"
|
||||
#include "DrawObject.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
|
||||
//
|
||||
// WaypointTool class.
|
||||
//
|
||||
|
||||
Bool WaypointTool::m_isActive = false;
|
||||
|
||||
|
||||
/// Constructor
|
||||
WaypointTool::WaypointTool(void) :
|
||||
Tool(ID_WAYPOINT_TOOL, IDC_WAYPOINT)
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
WaypointTool::~WaypointTool(void)
|
||||
{
|
||||
}
|
||||
|
||||
/// Clears it's is active flag.
|
||||
void WaypointTool::deactivate()
|
||||
{
|
||||
m_isActive = false;
|
||||
}
|
||||
|
||||
/// Shows the terrain materials options panel.
|
||||
void WaypointTool::activate()
|
||||
{
|
||||
CMainFrame::GetMainFrame()->showOptionsDialog(IDD_WAYPOINT_OPTIONS);
|
||||
WaypointOptions::update();
|
||||
DrawObject::setDoBrushFeedback(false);
|
||||
m_isActive = true;
|
||||
}
|
||||
|
||||
// Pick a waypoint.
|
||||
MapObject *WaypointTool::pickWaypoint(Coord3D loc){
|
||||
// Tight check first.
|
||||
MapObject *pObj;
|
||||
for (pObj = MapObject::getFirstMapObject(); pObj; pObj = pObj->getNext()) {
|
||||
if (!pObj->isWaypoint()) {
|
||||
continue;
|
||||
}
|
||||
Coord3D cloc = *pObj->getLocation();
|
||||
// Check and see if we are within 1/2 cell size of the center.
|
||||
Coord3D cpt = loc;
|
||||
cpt.x -= cloc.x;
|
||||
cpt.y -= cloc.y;
|
||||
cpt.z = 0;
|
||||
if (cpt.length() < 0.5f*MAP_XY_FACTOR) {
|
||||
return pObj;
|
||||
}
|
||||
}
|
||||
// Loose check
|
||||
for (pObj = MapObject::getFirstMapObject(); pObj; pObj = pObj->getNext()) {
|
||||
if (!pObj->isWaypoint()) {
|
||||
continue;
|
||||
}
|
||||
Coord3D cloc = *pObj->getLocation();
|
||||
// Check and see if we are within 1 & 1/2 cell size of the center.
|
||||
Coord3D cpt = loc;
|
||||
cpt.x -= cloc.x;
|
||||
cpt.y -= cloc.y;
|
||||
cpt.z = 0;
|
||||
if (cpt.length() < 1.5f*MAP_XY_FACTOR) {
|
||||
return pObj;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Perform the tool behavior on mouse down.
|
||||
void WaypointTool::mouseDown(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
m_downWaypointID = 0;
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
MapObject *pObj = pickWaypoint(docPt);
|
||||
if (pObj) {
|
||||
pObj->setSelected(true);
|
||||
m_downWaypointID = pObj->getWaypointID();
|
||||
docPt = *pObj->getLocation();
|
||||
WaypointOptions::update();
|
||||
} else {
|
||||
pView->snapPoint(&docPt);
|
||||
MapObject *pNew = newInstance( MapObject)(docPt, AsciiString("*Waypoints/Waypoint"), 0, 0, NULL, NULL );
|
||||
Int id = pDoc->getNextWaypointID();
|
||||
AsciiString name = WaypointOptions::GenerateUniqueName(id);
|
||||
pNew->setSelected(true);
|
||||
pNew->setIsWaypoint();
|
||||
pNew->setWaypointID(id);
|
||||
pNew->setWaypointName(name);
|
||||
pNew->getProperties()->setAsciiString(TheKey_originalOwner, AsciiString("team"));
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, pNew);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
pNew = NULL; // undoable owns it now.
|
||||
m_downWaypointID = id;
|
||||
WaypointOptions::update();
|
||||
}
|
||||
m_mouseDownPt = docPt;
|
||||
}
|
||||
|
||||
/// Left button move code.
|
||||
void WaypointTool::mouseMoved(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
if (m != TRACK_L) return;
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
MapObject *pObj = pickWaypoint(docPt);
|
||||
if (pObj) {
|
||||
docPt = *pObj->getLocation();
|
||||
} else {
|
||||
pView->snapPoint(&docPt);
|
||||
}
|
||||
DrawObject::setWaypointDragFeedback(m_mouseDownPt, docPt);
|
||||
pView->Invalidate();
|
||||
}
|
||||
|
||||
/** Execute the tool on mouse up - Place an object. */
|
||||
void WaypointTool::mouseUp(TTrackingMode m, CPoint viewPt, WbView* pView, CWorldBuilderDoc *pDoc)
|
||||
{
|
||||
DrawObject::stopWaypointDragFeedback();
|
||||
if (m != TRACK_L) return;
|
||||
|
||||
Coord3D docPt;
|
||||
pView->viewToDocCoords(viewPt, &docPt);
|
||||
MapObject *pObj;
|
||||
PointerTool::clearSelection();
|
||||
pObj = pickWaypoint(docPt);
|
||||
if (pObj == NULL) {
|
||||
pView->snapPoint(&docPt);
|
||||
MapObject *pNew = newInstance( MapObject)(docPt, AsciiString("*Waypoints/Waypoint"), 0, 0, NULL, NULL );
|
||||
Int id = pDoc->getNextWaypointID();
|
||||
AsciiString name;
|
||||
name.format("Waypoint %d", id);
|
||||
pNew->setSelected(true);
|
||||
pNew->setIsWaypoint();
|
||||
pNew->setWaypointID(id);
|
||||
pNew->setWaypointName(name);
|
||||
pNew->getProperties()->setAsciiString(TheKey_originalOwner, AsciiString("team"));
|
||||
AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, pNew);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
pObj = pNew;
|
||||
pNew = NULL; // undoable owns it now.
|
||||
}
|
||||
if (pObj)
|
||||
{
|
||||
pObj->setSelected(true);
|
||||
if (m_downWaypointID && (pObj->getWaypointID() != m_downWaypointID))
|
||||
{
|
||||
Int waypointID = pObj->getWaypointID();
|
||||
// If a link
|
||||
if (pDoc->waypointLinkExists(m_downWaypointID, waypointID))
|
||||
{
|
||||
pDoc->removeWaypointLink(m_downWaypointID, waypointID);
|
||||
}
|
||||
else if (m_downWaypointID != waypointID)
|
||||
{
|
||||
pDoc->addWaypointLink(m_downWaypointID, waypointID);
|
||||
}
|
||||
MapObject *pDown = pDoc->getWaypointByID(m_downWaypointID);
|
||||
if (pDown) {
|
||||
pDoc->updateLinkedWaypointLabels(pDown);
|
||||
}
|
||||
}
|
||||
WaypointOptions::update();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
730
GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp
Normal file
730
GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp
Normal file
@@ -0,0 +1,730 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// WorldBuilder.cpp : Defines the class behaviors for the application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <eh.h>
|
||||
#include "WorldBuilder.h"
|
||||
#include "EulaDialog.h"
|
||||
#include "MainFrm.h"
|
||||
#include "OpenMap.h"
|
||||
#include "SplashScreen.h"
|
||||
#include "Textureloader.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "WBFrameWnd.h"
|
||||
#include "WbView3d.h"
|
||||
|
||||
//#include <wsys/StdFileSystem.h>
|
||||
#include "W3DDevice/GameClient/W3DFileSystem.h"
|
||||
#include "common/GlobalData.h"
|
||||
#include "WHeightMapEdit.h"
|
||||
//#include "Common/GameFileSystem.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "Common/ArchiveFileSystem.h"
|
||||
#include "Common/LocalFileSystem.h"
|
||||
#include "Common/CDManager.h"
|
||||
#include "Common/Debug.h"
|
||||
#include "Common/StackDump.h"
|
||||
#include "Common/GameMemory.h"
|
||||
#include "Common/Science.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/SpecialPower.h"
|
||||
#include "Common/TerrainTypes.h"
|
||||
#include "Common/DamageFX.h"
|
||||
#include "Common/Upgrade.h"
|
||||
#include "Common/ModuleFactory.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
|
||||
#include "GameLogic/Armor.h"
|
||||
#include "GameLogic/CaveSystem.h"
|
||||
#include "GameLogic/CrateSystem.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
#include "GameLogic/RankInfo.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
#include "GameLogic/ScriptActions.h"
|
||||
#include "GameClient/Anim2D.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/ParticleSys.h"
|
||||
#include "GameClient/Water.h"
|
||||
#include "GameClient/TerrainRoads.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/VideoPlayer.h"
|
||||
#include "GameLogic/Locomotor.h"
|
||||
|
||||
#include "W3DDevice/Common/W3DModuleFactory.h"
|
||||
#include "W3DDevice/GameClient/W3DParticleSys.h"
|
||||
#include "MilesAudioDevice/MilesAudioManager.h"
|
||||
|
||||
#include <io.h>
|
||||
#include "win32device/GameClient/Win32Mouse.h"
|
||||
#include "Win32Device/Common/Win32LocalFileSystem.h"
|
||||
#include "Win32Device/Common/Win32BIGFileSystem.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
static SubsystemInterfaceList TheSubsystemListRecord;
|
||||
|
||||
template<class SUBSYSTEM>
|
||||
void initSubsystem(SUBSYSTEM*& sysref, SUBSYSTEM* sys, const char* path1 = NULL, const char* path2 = NULL, const char* dirpath = NULL)
|
||||
{
|
||||
sysref = sys;
|
||||
TheSubsystemListRecord.initSubsystem(sys, path1, path2, dirpath, NULL);
|
||||
}
|
||||
|
||||
|
||||
#define APP_SECTION "WorldbuilderApp"
|
||||
#define OPEN_FILE_DIR "OpenDirectory"
|
||||
|
||||
Win32Mouse *TheWin32Mouse = NULL;
|
||||
char *gAppPrefix = "wb_"; /// So WB can have a different debug log file name.
|
||||
const Char *g_strFile = "data\\Generals.str";
|
||||
const Char *g_csfFile = "data\\%s\\Generals.csf";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WBGameFileClass - extends the file system a bit so we can get at some
|
||||
// wb only data. jba.
|
||||
|
||||
class WBGameFileClass : public GameFileClass
|
||||
{
|
||||
|
||||
public:
|
||||
WBGameFileClass(char const *filename):GameFileClass(filename){};
|
||||
virtual char const * Set_Name(char const *filename);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Sets the file name, and finds the GDI asset if present. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
char const * WBGameFileClass::Set_Name( char const *filename )
|
||||
{
|
||||
char const *pChar = GameFileClass::Set_Name(filename);
|
||||
if (this->Is_Available()) {
|
||||
return pChar; // it was found by the parent class.
|
||||
}
|
||||
|
||||
if (TheFileSystem->doesFileExist(filename)) {
|
||||
strcpy( m_filePath, filename );
|
||||
m_fileExists = true;
|
||||
}
|
||||
return m_filename;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WB_W3DFileSystem - extends the file system a bit so we can get at some
|
||||
// wb only data. jba.
|
||||
|
||||
class WB_W3DFileSystem : public W3DFileSystem {
|
||||
virtual FileClass * Get_File( char const *filename );
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Gets a file with the specified filename. */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FileClass * WB_W3DFileSystem::Get_File( char const *filename )
|
||||
{
|
||||
WBGameFileClass *pFile = new WBGameFileClass( filename );
|
||||
if (!pFile->Is_Available()) {
|
||||
pFile->Set_Name(filename);
|
||||
}
|
||||
return pFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// The one and only CWorldBuilderApp object
|
||||
|
||||
static CWorldBuilderApp theApp;
|
||||
HWND ApplicationHWnd = NULL;
|
||||
|
||||
/**
|
||||
* The ApplicationHInstance is needed for the WOL code,
|
||||
* which needs it for the COM initialization of WOLAPI.DLL.
|
||||
* Of course, the WOL code is in gameengine, while the
|
||||
* HINSTANCE is only in the various projects' main files.
|
||||
* So, we need to create the HINSTANCE, even if it always
|
||||
* stays NULL. Just to make COM happy. Whee.
|
||||
*/
|
||||
HINSTANCE ApplicationHInstance = NULL;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CWorldBuilderApp
|
||||
|
||||
BEGIN_MESSAGE_MAP(CWorldBuilderApp, CWinApp)
|
||||
//{{AFX_MSG_MAP(CWorldBuilderApp)
|
||||
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
|
||||
ON_COMMAND(IDM_RESET_WINDOWS, OnResetWindows)
|
||||
ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
|
||||
ON_COMMAND(ID_TEXTURESIZING_MAPCLIFFTEXTURES, OnTexturesizingMapclifftextures)
|
||||
ON_UPDATE_COMMAND_UI(ID_TEXTURESIZING_MAPCLIFFTEXTURES, OnUpdateTexturesizingMapclifftextures)
|
||||
//}}AFX_MSG_MAP
|
||||
// Standard file based document commands
|
||||
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
|
||||
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
|
||||
// Standard print setup command
|
||||
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
static Int gFirstCP = 0;
|
||||
|
||||
// CWorldBuilderApp construction
|
||||
|
||||
CWorldBuilderApp::CWorldBuilderApp() :
|
||||
m_curTool(NULL),
|
||||
m_selTool(NULL),
|
||||
m_lockCurTool(0),
|
||||
m_3dtemplate(NULL),
|
||||
m_pasteMapObjList(NULL)
|
||||
{
|
||||
|
||||
for (Int i=0; i<NUM_VIEW_TOOLS; i++) {
|
||||
m_tools[i] = NULL;
|
||||
|
||||
}
|
||||
m_tools[0] = &m_brushTool;
|
||||
m_tools[1] = &m_tileTool;
|
||||
m_tools[2] = &m_featherTool;
|
||||
m_tools[3] = &m_autoEdgeOutTool;
|
||||
m_tools[4] = &m_bigTileTool;
|
||||
m_tools[5] = &m_floodFillTool;
|
||||
m_tools[6] = &m_moundTool;
|
||||
m_tools[7] = &m_digTool;
|
||||
m_tools[8] = &m_eyedropperTool;
|
||||
m_tools[9] = &m_objectTool;
|
||||
m_tools[10] = &m_pointerTool;
|
||||
m_tools[11] = &m_blendEdgeTool;
|
||||
m_tools[12] = &m_groveTool;
|
||||
m_tools[13] = &m_meshMoldTool;
|
||||
m_tools[14] = &m_roadTool;
|
||||
m_tools[15] = &m_handScrollTool;
|
||||
m_tools[16] = &m_waypointTool;
|
||||
m_tools[17] = &m_polygonTool;
|
||||
m_tools[18] = &m_buildListTool;
|
||||
m_tools[19] = &m_fenceTool;
|
||||
m_tools[20] = &m_waterTool;
|
||||
m_tools[21] = &m_rampTool;
|
||||
m_tools[22] = &m_scorchTool;
|
||||
m_tools[23] = &m_borderTool;
|
||||
m_tools[24] = &m_rulerTool;
|
||||
|
||||
// set up initial values.
|
||||
m_brushTool.setHeight(16);
|
||||
m_brushTool.setWidth(3);
|
||||
m_brushTool.setFeather(3);
|
||||
m_moundTool.setMoundHeight(3);
|
||||
m_moundTool.setWidth(3);
|
||||
m_moundTool.setFeather(3);
|
||||
m_featherTool.setFeather(3);
|
||||
m_featherTool.setRadius(1);
|
||||
m_featherTool.setRate(2);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CWorldBuilderApp destructor
|
||||
|
||||
CWorldBuilderApp::~CWorldBuilderApp()
|
||||
{
|
||||
m_curTool = NULL;
|
||||
m_selTool = NULL;
|
||||
|
||||
for (Int i=0; i<NUM_VIEW_TOOLS; i++) {
|
||||
if (m_tools[i]) {
|
||||
m_tools[i] = NULL;
|
||||
}
|
||||
}
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CWorldBuilderApp initialization
|
||||
|
||||
BOOL CWorldBuilderApp::InitInstance()
|
||||
{
|
||||
//#ifdef _RELEASE
|
||||
EulaDialog eulaDialog;
|
||||
if( eulaDialog.DoModal() == IDCANCEL )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
//#endif
|
||||
|
||||
ApplicationHWnd = GetDesktopWindow();
|
||||
|
||||
// initialization
|
||||
_set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
|
||||
|
||||
// start the log
|
||||
DEBUG_INIT(DEBUG_FLAGS_DEFAULT);
|
||||
|
||||
#ifdef DEBUG_LOGGING
|
||||
// Turn on console output jba [3/20/2003]
|
||||
DebugSetFlags(DebugGetFlags() | DEBUG_FLAG_LOG_TO_CONSOLE);
|
||||
#endif
|
||||
|
||||
DEBUG_LOG(("starting Worldbuilder.\n"));
|
||||
|
||||
#ifdef _INTERNAL
|
||||
DEBUG_LOG(("_INTERNAL defined.\n"));
|
||||
#endif
|
||||
#ifdef _DEBUG
|
||||
DEBUG_LOG(("_DEBUG defined.\n"));
|
||||
#endif
|
||||
initMemoryManager();
|
||||
#ifdef MEMORYPOOL_CHECKPOINTING
|
||||
gFirstCP = TheMemoryPoolFactory->debugSetCheckpoint();
|
||||
#endif
|
||||
|
||||
SplashScreen loadWindow;
|
||||
loadWindow.Create(IDD_LOADING, loadWindow.GetDesktopWindow());
|
||||
loadWindow.SetWindowText("Loading Worldbuilder");
|
||||
loadWindow.ShowWindow(SW_SHOW);
|
||||
loadWindow.UpdateWindow();
|
||||
|
||||
CRect rect(15, 315, 230, 333);
|
||||
loadWindow.setTextOutputLocation(rect);
|
||||
loadWindow.outputText(IDS_SPLASH_LOADING);
|
||||
|
||||
// not part of the subsystem list, because it should normally never be reset!
|
||||
TheNameKeyGenerator = new NameKeyGenerator;
|
||||
TheNameKeyGenerator->init();
|
||||
|
||||
#ifdef _AFXDLL
|
||||
Enable3dControls(); // Call this when using MFC in a shared DLL
|
||||
#else
|
||||
Enable3dControlsStatic(); // Call this when linking to MFC statically
|
||||
#endif
|
||||
|
||||
// Set the current directory to the app directory.
|
||||
char buf[_MAX_PATH];
|
||||
GetModuleFileName(NULL, buf, sizeof(buf));
|
||||
char *pEnd = buf + strlen(buf);
|
||||
while (pEnd != buf) {
|
||||
if (*pEnd == '\\') {
|
||||
*pEnd = 0;
|
||||
break;
|
||||
}
|
||||
pEnd--;
|
||||
}
|
||||
::SetCurrentDirectory(buf);
|
||||
|
||||
TheFileSystem = new FileSystem;
|
||||
|
||||
initSubsystem(TheLocalFileSystem, (LocalFileSystem*)new Win32LocalFileSystem);
|
||||
initSubsystem(TheArchiveFileSystem, (ArchiveFileSystem*)new Win32BIGFileSystem);
|
||||
|
||||
// Just for kicks, get the HINSTANCE that WOL would need
|
||||
// if we were going to use it, which we aren't.
|
||||
ApplicationHInstance = AfxGetInstanceHandle();
|
||||
|
||||
INI ini;
|
||||
|
||||
initSubsystem(TheWritableGlobalData, new GlobalData(), "Data\\INI\\Default\\GameData.ini", "Data\\INI\\GameData.ini");
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
ini.load( AsciiString( "Data\\INI\\GameDataDebug.ini" ), INI_LOAD_MULTIFILE, NULL );
|
||||
TheWritableGlobalData->m_debugIgnoreAsserts = false;
|
||||
#endif
|
||||
|
||||
#if defined(_INTERNAL)
|
||||
// leave on asserts for a while. jba. [4/15/2003] TheWritableGlobalData->m_debugIgnoreAsserts = true;
|
||||
#endif
|
||||
DEBUG_LOG(("TheWritableGlobalData %x\n", TheWritableGlobalData));
|
||||
#if 1
|
||||
// srj sez: put INI into our user data folder, not the ap dir
|
||||
free((void*)m_pszProfileName);
|
||||
strcpy(buf, TheGlobalData->getPath_UserData().str());
|
||||
strcat(buf, "WorldBuilder.ini");
|
||||
#else
|
||||
strcat(buf, "//");
|
||||
strcat(buf, m_pszProfileName);
|
||||
free((void*)m_pszProfileName);
|
||||
#endif
|
||||
m_pszProfileName = (const char *)malloc(strlen(buf)+2);
|
||||
strcpy((char*)m_pszProfileName, buf);
|
||||
|
||||
// ensure the user maps dir exists
|
||||
sprintf(buf, "%sMaps\\", TheGlobalData->getPath_UserData().str());
|
||||
CreateDirectory(buf, NULL);
|
||||
|
||||
// read the water settings from INI (must do prior to initing GameClient, apparently)
|
||||
ini.load( AsciiString( "Data\\INI\\Default\\Water.ini" ), INI_LOAD_OVERWRITE, NULL );
|
||||
ini.load( AsciiString( "Data\\INI\\Water.ini" ), INI_LOAD_OVERWRITE, NULL );
|
||||
|
||||
initSubsystem(TheGameText, CreateGameTextInterface());
|
||||
initSubsystem(TheScienceStore, new ScienceStore(), "Data\\INI\\Default\\Science.ini", "Data\\INI\\Science.ini");
|
||||
initSubsystem(TheMultiplayerSettings, new MultiplayerSettings(), "Data\\INI\\Default\\Multiplayer.ini", "Data\\INI\\Multiplayer.ini");
|
||||
initSubsystem(TheTerrainTypes, new TerrainTypeCollection(), "Data\\INI\\Default\\Terrain.ini", "Data\\INI\\Terrain.ini");
|
||||
initSubsystem(TheTerrainRoads, new TerrainRoadCollection(), "Data\\INI\\Default\\Roads.ini", "Data\\INI\\Roads.ini");
|
||||
|
||||
WorldHeightMapEdit::init();
|
||||
|
||||
initSubsystem(TheScriptEngine, (ScriptEngine*)(new ScriptEngine()));
|
||||
|
||||
TheScriptEngine->turnBreezeOff(); // stop the tree sway.
|
||||
|
||||
// [2/11/2003]
|
||||
ini.load( AsciiString( "Data\\Scripts\\Scripts.ini" ), INI_LOAD_OVERWRITE, NULL );
|
||||
|
||||
// need this before TheAudio in case we're running off of CD - TheAudio can try to open Music.big on the CD...
|
||||
initSubsystem(TheCDManager, CreateCDManager(), NULL);
|
||||
initSubsystem(TheAudio, (AudioManager*)new MilesAudioManager());
|
||||
if (!TheAudio->isMusicAlreadyLoaded())
|
||||
return FALSE;
|
||||
|
||||
initSubsystem(TheVideoPlayer, (VideoPlayerInterface*)(new VideoPlayer()));
|
||||
initSubsystem(TheModuleFactory, (ModuleFactory*)(new W3DModuleFactory()));
|
||||
initSubsystem(TheSidesList, new SidesList());
|
||||
initSubsystem(TheCaveSystem, new CaveSystem());
|
||||
initSubsystem(TheRankInfoStore, new RankInfoStore(), NULL, "Data\\INI\\Rank.ini");
|
||||
initSubsystem(ThePlayerTemplateStore, new PlayerTemplateStore(), "Data\\INI\\Default\\PlayerTemplate.ini", "Data\\INI\\PlayerTemplate.ini");
|
||||
initSubsystem(TheSpecialPowerStore, new SpecialPowerStore(), "Data\\INI\\Default\\SpecialPower.ini", "Data\\INI\\SpecialPower.ini" );
|
||||
initSubsystem(TheParticleSystemManager, (ParticleSystemManager*)(new W3DParticleSystemManager()));
|
||||
initSubsystem(TheFXListStore, new FXListStore(), "Data\\INI\\Default\\FXList.ini", "Data\\INI\\FXList.ini");
|
||||
initSubsystem(TheWeaponStore, new WeaponStore(), NULL, "Data\\INI\\Weapon.ini");
|
||||
initSubsystem(TheObjectCreationListStore, new ObjectCreationListStore(), "Data\\INI\\Default\\ObjectCreationList.ini", "Data\\INI\\ObjectCreationList.ini");
|
||||
initSubsystem(TheLocomotorStore, new LocomotorStore(), NULL, "Data\\INI\\Locomotor.ini");
|
||||
initSubsystem(TheDamageFXStore, new DamageFXStore(), NULL, "Data\\INI\\DamageFX.ini");
|
||||
initSubsystem(TheArmorStore, new ArmorStore(), NULL, "Data\\INI\\Armor.ini");
|
||||
initSubsystem(TheThingFactory, new ThingFactory(), "Data\\INI\\Default\\Object.ini", NULL, "Data\\INI\\Object");
|
||||
initSubsystem(TheCrateSystem, new CrateSystem(), "Data\\INI\\Default\\Crate.ini", "Data\\INI\\Crate.ini");
|
||||
initSubsystem(TheUpgradeCenter, new UpgradeCenter, "Data\\INI\\Default\\Upgrade.ini", "Data\\INI\\Upgrade.ini");
|
||||
initSubsystem(TheAnim2DCollection, new Anim2DCollection ); //Init's itself.
|
||||
|
||||
TheSubsystemListRecord.postProcessLoadAll();
|
||||
|
||||
TheW3DFileSystem = new WB_W3DFileSystem;
|
||||
|
||||
// Just to be sure - wb doesn't do well with half res terrain.
|
||||
DEBUG_ASSERTCRASH(!TheGlobalData->m_useHalfHeightMap, ("TheGlobalData->m_useHalfHeightMap : Don't use this setting in WB."));
|
||||
TheWritableGlobalData->m_useHalfHeightMap = false;
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
// WB never uses the shroud.
|
||||
TheWritableGlobalData->m_shroudOn = FALSE;
|
||||
#endif
|
||||
|
||||
TheWritableGlobalData->m_isWorldBuilder = TRUE;
|
||||
|
||||
// Change the registry key under which our settings are stored.
|
||||
// TODO: You should modify this string to be something appropriate
|
||||
// such as the name of your company or organization.
|
||||
//SetRegistryKey(_T("Local AppWizard-Generated Applications"));
|
||||
|
||||
LoadStdProfileSettings(); // Load standard INI file options (including MRU)
|
||||
|
||||
// Register the application's document templates. Document templates
|
||||
// serve as the connection between documents, frame windows and views.
|
||||
|
||||
m_3dtemplate = new CSingleDocTemplate(
|
||||
IDR_MAPDOC,
|
||||
RUNTIME_CLASS(CWorldBuilderDoc),
|
||||
RUNTIME_CLASS(CWB3dFrameWnd),
|
||||
RUNTIME_CLASS(WbView3d));
|
||||
|
||||
AddDocTemplate(m_3dtemplate);
|
||||
|
||||
#ifdef MDI
|
||||
CMainFrame* pMainFrame = new CMainFrame;
|
||||
if (!pMainFrame->LoadFrame(IDR_MAPDOC))
|
||||
return FALSE;
|
||||
m_pMainWnd = pMainFrame;
|
||||
#endif
|
||||
|
||||
// Parse command line for standard shell commands, DDE, file open
|
||||
CCommandLineInfo cmdInfo;
|
||||
ParseCommandLine(cmdInfo);
|
||||
|
||||
// Dispatch commands specified on the command line
|
||||
if (!ProcessShellCommand(cmdInfo))
|
||||
return FALSE;
|
||||
|
||||
// The one and only window has been initialized, so show and update it.
|
||||
m_pMainWnd->ShowWindow(SW_SHOW);
|
||||
m_pMainWnd->UpdateWindow();
|
||||
|
||||
// Parse command line for standard shell commands, DDE, file open
|
||||
// CCommandLineInfo cmdInfo;
|
||||
// ParseCommandLine(cmdInfo);
|
||||
|
||||
// Dispatch commands specified on the command line
|
||||
// if (!ProcessShellCommand(cmdInfo))
|
||||
// return FALSE;
|
||||
|
||||
selectPointerTool();
|
||||
|
||||
CString openDir = this->GetProfileString(APP_SECTION, OPEN_FILE_DIR);
|
||||
m_currentDirectory = openDir;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CWorldBuilderApp message handlers
|
||||
|
||||
BOOL CWorldBuilderApp::OnCmdMsg(UINT nID, int nCode, void* pExtra,
|
||||
AFX_CMDHANDLERINFO* pHandlerInfo)
|
||||
{
|
||||
// If pHandlerInfo is NULL, then handle the message
|
||||
if (pHandlerInfo == NULL)
|
||||
{
|
||||
for (Int i=0; i<NUM_VIEW_TOOLS; i++) {
|
||||
Tool *pTool = m_tools[i];
|
||||
if (pTool==NULL) continue;
|
||||
if ((Int)nID == pTool->getToolID()) {
|
||||
if (nCode == CN_COMMAND)
|
||||
{
|
||||
// Handle WM_COMMAND message
|
||||
setActiveTool(pTool);
|
||||
}
|
||||
else if (nCode == CN_UPDATE_COMMAND_UI)
|
||||
{
|
||||
// Update UI element state
|
||||
CCmdUI *pUI = (CCmdUI*)pExtra;
|
||||
pUI->SetCheck(m_curTool == pTool?1:0);
|
||||
pUI->Enable(true);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't process the command, call the base class
|
||||
// version of OnCmdMsg so the message-map can handle the message
|
||||
return CWinApp::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// CWorldBuilderApp::selectPointerTool
|
||||
//=============================================================================
|
||||
/** Sets the active tool to the pointer, and clears the selection. */
|
||||
//=============================================================================
|
||||
void CWorldBuilderApp::selectPointerTool(void)
|
||||
{
|
||||
setActiveTool(&m_pointerTool);
|
||||
// Clear selection.
|
||||
m_pointerTool.clearSelection();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// CWorldBuilderApp::setActiveTool
|
||||
//=============================================================================
|
||||
/** Sets the active tool, and activates it after deactivating the current tool. */
|
||||
//=============================================================================
|
||||
void CWorldBuilderApp::setActiveTool(Tool *pNewTool)
|
||||
{
|
||||
if (m_curTool == pNewTool) {
|
||||
// same tool
|
||||
return;
|
||||
}
|
||||
if (m_selTool && m_selTool != pNewTool) {
|
||||
m_selTool->deactivate();
|
||||
}
|
||||
if (pNewTool) {
|
||||
pNewTool->activate();
|
||||
}
|
||||
m_curTool = pNewTool;
|
||||
m_selTool = pNewTool;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// CWorldBuilderApp::updateCurTool
|
||||
//=============================================================================
|
||||
/** Checks to see if any key modifiers (ctrl or alt) are pressed. If so,
|
||||
selectes the appropriate tool, else uses the normal tool. */
|
||||
//=============================================================================
|
||||
void CWorldBuilderApp::updateCurTool(Bool forceHand)
|
||||
{
|
||||
Tool *curTool = m_curTool;
|
||||
DEBUG_ASSERTCRASH((m_lockCurTool>=0),("oops"));
|
||||
if (!m_lockCurTool) { // don't change tools that are doing something.
|
||||
if (forceHand || (0x8000 & ::GetAsyncKeyState(VK_SPACE))) {
|
||||
// Space bar gives scroll hand.
|
||||
m_curTool = &m_handScrollTool;
|
||||
} else if (0x8000 & ::GetAsyncKeyState(VK_MENU)) {
|
||||
// Alt key gives eyedropper.
|
||||
m_curTool = &m_eyedropperTool;
|
||||
} else if (0x8000 & ::GetAsyncKeyState(VK_CONTROL)) {
|
||||
// Control key gives pointer.
|
||||
m_curTool = &m_pointerTool;
|
||||
} else {
|
||||
// Else the tool selected in the tool palette.
|
||||
m_curTool = m_selTool;
|
||||
}
|
||||
}
|
||||
if (curTool != m_curTool) {
|
||||
m_curTool->activate();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CAboutDlg dialog used for App About
|
||||
|
||||
class CAboutDlg : public CDialog
|
||||
{
|
||||
public:
|
||||
CAboutDlg();
|
||||
|
||||
// Dialog Data
|
||||
//{{AFX_DATA(CAboutDlg)
|
||||
enum { IDD = IDD_ABOUTBOX };
|
||||
//}}AFX_DATA
|
||||
|
||||
// ClassWizard generated virtual function overrides
|
||||
//{{AFX_VIRTUAL(CAboutDlg)
|
||||
protected:
|
||||
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
|
||||
//}}AFX_VIRTUAL
|
||||
|
||||
// Implementation
|
||||
protected:
|
||||
//{{AFX_MSG(CAboutDlg)
|
||||
// No message handlers
|
||||
//}}AFX_MSG
|
||||
DECLARE_MESSAGE_MAP()
|
||||
};
|
||||
|
||||
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
|
||||
{
|
||||
//{{AFX_DATA_INIT(CAboutDlg)
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(CAboutDlg)
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
|
||||
//{{AFX_MSG_MAP(CAboutDlg)
|
||||
// No message handlers
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
// App command to run the dialog
|
||||
void CWorldBuilderApp::OnAppAbout()
|
||||
{
|
||||
CAboutDlg aboutDlg;
|
||||
aboutDlg.DoModal();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CWorldBuilderApp message handlers
|
||||
|
||||
int CWorldBuilderApp::ExitInstance()
|
||||
{
|
||||
|
||||
WriteProfileString(APP_SECTION, OPEN_FILE_DIR, m_currentDirectory.str());
|
||||
m_currentDirectory.clear();
|
||||
|
||||
ScriptList::reset();
|
||||
|
||||
TheSubsystemListRecord.shutdownAll();
|
||||
|
||||
WorldHeightMapEdit::shutdown();
|
||||
|
||||
delete TheFileSystem;
|
||||
TheFileSystem = NULL;
|
||||
|
||||
delete TheW3DFileSystem;
|
||||
TheW3DFileSystem = NULL;
|
||||
|
||||
delete TheNameKeyGenerator;
|
||||
TheNameKeyGenerator = NULL;
|
||||
|
||||
#ifdef MEMORYPOOL_CHECKPOINTING
|
||||
Int lastCP = TheMemoryPoolFactory->debugSetCheckpoint();
|
||||
#endif
|
||||
#ifdef MEMORYPOOL_CHECKPOINTING
|
||||
TheMemoryPoolFactory->debugMemoryReport(REPORT_FACTORYINFO | REPORT_CP_LEAKS | REPORT_CP_STACKTRACE, gFirstCP, lastCP);
|
||||
#endif
|
||||
#ifdef MEMORYPOOL_DEBUG
|
||||
TheMemoryPoolFactory->debugMemoryReport(REPORT_POOLINFO | REPORT_POOL_OVERFLOW | REPORT_SIMPLE_LEAKS, 0, 0);
|
||||
#endif
|
||||
shutdownMemoryManager();
|
||||
DEBUG_SHUTDOWN();
|
||||
|
||||
return CWinApp::ExitInstance();
|
||||
}
|
||||
|
||||
void CWorldBuilderApp::OnResetWindows()
|
||||
{
|
||||
if (CMainFrame::GetMainFrame()) {
|
||||
CMainFrame::GetMainFrame()->ResetWindowPositions();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CWorldBuilderApp::OnFileOpen()
|
||||
{
|
||||
#ifdef DO_MAPS_IN_DIRECTORIES
|
||||
TOpenMapInfo info;
|
||||
OpenMap mapDlg(&info);
|
||||
if (mapDlg.DoModal() == IDOK) {
|
||||
if (!info.browse) {
|
||||
OpenDocumentFile(info.filename);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// cancelled so return.
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
CFileStatus status;
|
||||
if (m_currentDirectory != AsciiString("")) try {
|
||||
if (CFile::GetStatus(m_currentDirectory.str(), status)) {
|
||||
if (status.m_attribute & CFile::directory) {
|
||||
::SetCurrentDirectory(m_currentDirectory.str());
|
||||
}
|
||||
}
|
||||
} catch(...) {}
|
||||
|
||||
CWinApp::OnFileOpen();
|
||||
}
|
||||
|
||||
void CWorldBuilderApp::OnTexturesizingMapclifftextures()
|
||||
{
|
||||
setActiveTool(&m_floodFillTool);
|
||||
m_floodFillTool.setAdjustCliffs(true);
|
||||
|
||||
}
|
||||
|
||||
void CWorldBuilderApp::OnUpdateTexturesizingMapclifftextures(CCmdUI* pCmdUI)
|
||||
{
|
||||
// TODO: Add your command update UI handler code here
|
||||
|
||||
}
|
||||
2673
GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp
Normal file
2673
GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1083
GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderView.cpp
Normal file
1083
GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderView.cpp
Normal file
File diff suppressed because it is too large
Load Diff
132
GeneralsMD/Code/Tools/WorldBuilder/src/addplayerdialog.cpp
Normal file
132
GeneralsMD/Code/Tools/WorldBuilder/src/addplayerdialog.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// addplayerdialog.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "addplayerdialog.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "CUndoable.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// AddPlayerDialog dialog
|
||||
|
||||
|
||||
AddPlayerDialog::AddPlayerDialog(AsciiString side, CWnd* pParent /*=NULL*/)
|
||||
: CDialog(AddPlayerDialog::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(AddPlayerDialog)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
|
||||
m_side = side;
|
||||
m_addedSide.clear();
|
||||
}
|
||||
|
||||
|
||||
void AddPlayerDialog::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(AddPlayerDialog)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(AddPlayerDialog, CDialog)
|
||||
//{{AFX_MSG_MAP(AddPlayerDialog)
|
||||
ON_CBN_EDITCHANGE(IDC_COMBO1, OnEditchangeCombo1)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// AddPlayerDialog message handlers
|
||||
|
||||
void AddPlayerDialog::OnOK()
|
||||
{
|
||||
CComboBox *faction = (CComboBox*)GetDlgItem(IDC_COMBO1);
|
||||
|
||||
if (faction)
|
||||
{
|
||||
// get the text out of the combo. If it is user-typed, sel will be -1, otherwise it will be >=0
|
||||
CString theText;
|
||||
Int sel = faction->GetCurSel();
|
||||
if (sel >= 0) {
|
||||
faction->GetLBText(sel, theText);
|
||||
} else {
|
||||
faction->GetWindowText(theText);
|
||||
}
|
||||
AsciiString name((LPCTSTR)theText);
|
||||
|
||||
const PlayerTemplate* pt = ThePlayerTemplateStore->findPlayerTemplate(NAMEKEY(name));
|
||||
if (pt)
|
||||
{
|
||||
m_addedSide = pt ? pt->getName() : AsciiString::TheEmptyString;
|
||||
SidesList newSides = *TheSidesList;
|
||||
newSides.addPlayerByTemplate(m_addedSide);
|
||||
Bool modified = newSides.validateSides();
|
||||
DEBUG_ASSERTLOG(!modified,("had to clean up sides in AddPlayerDialog::OnOK"));
|
||||
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
SidesListUndoable *pUndo = new SidesListUndoable(newSides, pDoc);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
}
|
||||
}
|
||||
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
void AddPlayerDialog::OnCancel()
|
||||
{
|
||||
m_addedSide.clear();
|
||||
CDialog::OnCancel();
|
||||
}
|
||||
|
||||
BOOL AddPlayerDialog::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CComboBox *factions = (CComboBox*)GetDlgItem(IDC_COMBO1);
|
||||
factions->ResetContent();
|
||||
if (ThePlayerTemplateStore)
|
||||
{
|
||||
for (int i = 0; i < ThePlayerTemplateStore->getPlayerTemplateCount(); i++)
|
||||
{
|
||||
const PlayerTemplate* pt = ThePlayerTemplateStore->getNthPlayerTemplate(i);
|
||||
if (!pt)
|
||||
continue;
|
||||
if (m_side.isEmpty() || m_side == pt->getSide())
|
||||
factions->AddString(pt->getName().str());
|
||||
}
|
||||
}
|
||||
factions->SetCurSel(0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void AddPlayerDialog::OnEditchangeCombo1()
|
||||
{
|
||||
// TODO: Add your control notification handler code here
|
||||
|
||||
}
|
||||
283
GeneralsMD/Code/Tools/WorldBuilder/src/brushoptions.cpp
Normal file
283
GeneralsMD/Code/Tools/WorldBuilder/src/brushoptions.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// brushoptions.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "resource.h"
|
||||
#include "Lib\BaseType.h"
|
||||
#include "brushoptions.h"
|
||||
#include "WorldBuilderView.h"
|
||||
#include "BrushTool.h"
|
||||
|
||||
BrushOptions *BrushOptions::m_staticThis = NULL;
|
||||
Int BrushOptions::m_currentWidth = 0;
|
||||
Int BrushOptions::m_currentHeight = 0;
|
||||
Int BrushOptions::m_currentFeather = 0;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// BrushOptions dialog trivial construstor - Create does the real work.
|
||||
|
||||
|
||||
BrushOptions::BrushOptions(CWnd* pParent /*=NULL*/)
|
||||
{
|
||||
//{{AFX_DATA_INIT(BrushOptions)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
/// Windows default stuff.
|
||||
void BrushOptions::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(BrushOptions)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
/// Sets the feather value in the dialog.
|
||||
/** Update the value in the edit control and the slider. */
|
||||
void BrushOptions::setFeather(Int feather)
|
||||
{
|
||||
CString buf;
|
||||
buf.Format("%d", feather);
|
||||
m_currentFeather = feather;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_FEATHER_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the brush width value in the dialog.
|
||||
/** Update the value in the edit control and the slider. */
|
||||
void BrushOptions::setWidth(Int width)
|
||||
{
|
||||
CString buf;
|
||||
buf.Format("%d", width);
|
||||
m_currentWidth = width;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void BrushOptions::setHeight(Int height)
|
||||
{
|
||||
char buffer[50];
|
||||
sprintf(buffer, "%d", height);
|
||||
m_currentHeight = height;
|
||||
if (m_staticThis && !m_staticThis->m_updating) {
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// BrushOptions message handlers
|
||||
|
||||
/// Dialog UI initialization.
|
||||
/** Creates the slider controls, and sets the initial values for
|
||||
width and feather in the ui controls. */
|
||||
BOOL BrushOptions::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = true;
|
||||
m_brushWidthPopup.SetupPopSliderButton(this, IDC_SIZE_POPUP, this);
|
||||
m_brushFeatherPopup.SetupPopSliderButton(this, IDC_FEATHER_POPUP, this);
|
||||
m_brushHeightPopup.SetupPopSliderButton(this, IDC_HEIGHT_POPUP, this);
|
||||
|
||||
|
||||
m_staticThis = this;
|
||||
m_updating = false;
|
||||
setFeather(m_currentFeather);
|
||||
setWidth(m_currentWidth);
|
||||
setHeight(m_currentHeight);
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
/// Handles feather edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void BrushOptions::OnChangeFeatherEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_FEATHER_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int feather;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &feather)) {
|
||||
m_currentFeather = feather;
|
||||
BrushTool::setFeather(m_currentFeather);
|
||||
sprintf(buffer, "%.1f FEET.", m_currentFeather*MAP_XY_FACTOR);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_FEATHER_LABEL);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void BrushOptions::OnChangeSizeEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int width;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &width)) {
|
||||
m_currentWidth = width;
|
||||
BrushTool::setWidth(m_currentWidth);
|
||||
sprintf(buffer, "%.1f FEET.", m_currentWidth*MAP_XY_FACTOR);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_WIDTH_LABEL);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles width edit ui messages.
|
||||
/** Gets the new edit control text, converts it to an int, then updates
|
||||
the slider and brush tool. */
|
||||
void BrushOptions::OnChangeHeightEdit()
|
||||
{
|
||||
if (m_updating) return;
|
||||
CWnd *pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
char buffer[_MAX_PATH];
|
||||
if (pEdit) {
|
||||
pEdit->GetWindowText(buffer, sizeof(buffer));
|
||||
Int height;
|
||||
m_updating = true;
|
||||
if (1==sscanf(buffer, "%d", &height)) {
|
||||
m_currentHeight = height;
|
||||
BrushTool::setHeight(m_currentHeight);
|
||||
sprintf(buffer, "%.1f FEET.", m_currentHeight*MAP_HEIGHT_SCALE);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_LABEL);
|
||||
if (pEdit) pEdit->SetWindowText(buffer);
|
||||
}
|
||||
m_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void BrushOptions::GetPopSliderInfo(const long sliderID, long *pMin, long *pMax, long *pLineSize, long *pInitial)
|
||||
{
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_SIZE_POPUP:
|
||||
*pMin = 1;
|
||||
*pMax = 30;
|
||||
*pInitial = m_currentWidth;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_HEIGHT_POPUP:
|
||||
*pMin = 0;
|
||||
*pMax = 255;
|
||||
*pInitial = m_currentHeight;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
case IDC_FEATHER_POPUP:
|
||||
*pMin = 0;
|
||||
*pMax = 20;
|
||||
*pInitial = m_currentFeather;
|
||||
*pLineSize = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void BrushOptions::PopSliderChanged(const long sliderID, long theVal)
|
||||
{
|
||||
CString str;
|
||||
CWnd *pEdit;
|
||||
switch (sliderID) {
|
||||
|
||||
case IDC_SIZE_POPUP:
|
||||
m_currentWidth = theVal;
|
||||
str.Format("%d",m_currentWidth);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_SIZE_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
BrushTool::setWidth(m_currentWidth);
|
||||
break;
|
||||
|
||||
case IDC_HEIGHT_POPUP:
|
||||
m_currentHeight = theVal;
|
||||
str.Format("%d",m_currentHeight);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_HEIGHT_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
BrushTool::setHeight(m_currentHeight);
|
||||
break;
|
||||
|
||||
case IDC_FEATHER_POPUP:
|
||||
m_currentFeather = theVal;
|
||||
str.Format("%d",m_currentFeather);
|
||||
pEdit = m_staticThis->GetDlgItem(IDC_FEATHER_EDIT);
|
||||
if (pEdit) pEdit->SetWindowText(str);
|
||||
BrushTool::setFeather(m_currentFeather);
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void BrushOptions::PopSliderFinished(const long sliderID, long theVal)
|
||||
{
|
||||
switch (sliderID) {
|
||||
case IDC_SIZE_POPUP:
|
||||
break;
|
||||
case IDC_HEIGHT_POPUP:
|
||||
break;
|
||||
case IDC_FEATHER_POPUP:
|
||||
break;
|
||||
|
||||
default:
|
||||
// uh-oh!
|
||||
DEBUG_CRASH(("Slider message from unknown control"));
|
||||
break;
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(BrushOptions, COptionsPanel)
|
||||
//{{AFX_MSG_MAP(BrushOptions)
|
||||
ON_EN_CHANGE(IDC_FEATHER_EDIT, OnChangeFeatherEdit)
|
||||
ON_EN_CHANGE(IDC_SIZE_EDIT, OnChangeSizeEdit)
|
||||
ON_EN_CHANGE(IDC_HEIGHT_EDIT, OnChangeHeightEdit)
|
||||
ON_WM_HSCROLL()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
2889
GeneralsMD/Code/Tools/WorldBuilder/src/mapobjectprops.cpp
Normal file
2889
GeneralsMD/Code/Tools/WorldBuilder/src/mapobjectprops.cpp
Normal file
File diff suppressed because it is too large
Load Diff
883
GeneralsMD/Code/Tools/WorldBuilder/src/playerlistdlg.cpp
Normal file
883
GeneralsMD/Code/Tools/WorldBuilder/src/playerlistdlg.cpp
Normal file
@@ -0,0 +1,883 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// playerlistdlg.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "playerlistdlg.h"
|
||||
#include "MapObjectProps.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "cundoable.h"
|
||||
#include "AddPlayerDialog.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "Common/UnicodeString.h"
|
||||
|
||||
static const char* NEUTRAL_NAME_STR = "(neutral)";
|
||||
|
||||
static Int thePrevCurPlyr = 0;
|
||||
|
||||
static Bool islegalplayernamechar(char c)
|
||||
{
|
||||
// note, spaces are NOT allowed.
|
||||
return ::isalnum(c) || c == '_';
|
||||
}
|
||||
|
||||
static void fixDefaultTeamName(SidesList& sides, AsciiString oldpname, AsciiString newpname)
|
||||
{
|
||||
AsciiString tname;
|
||||
tname.set("team");
|
||||
tname.concat(oldpname);
|
||||
TeamsInfo *ti = sides.findTeamInfo(tname);
|
||||
if (ti)
|
||||
{
|
||||
tname.set("team");
|
||||
tname.concat(newpname);
|
||||
DEBUG_LOG(("rename team %s -> %s\n",ti->getDict()->getAsciiString(TheKey_teamName).str(),tname.str()));
|
||||
ti->getDict()->setAsciiString(TheKey_teamName, tname);
|
||||
ti->getDict()->setAsciiString(TheKey_teamOwner, newpname);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CRASH(("team not found"));
|
||||
}
|
||||
}
|
||||
|
||||
static void updateAllTeams(SidesList& sides, AsciiString oldpname, AsciiString newpname)
|
||||
{
|
||||
Int numTeams = sides.getNumTeams();
|
||||
for (Int i = 0; i < numTeams; ++i) {
|
||||
TeamsInfo *teamInfo = sides.getTeamInfo(i);
|
||||
if (teamInfo) {
|
||||
Bool exists;
|
||||
Dict *dict = teamInfo->getDict();
|
||||
|
||||
AsciiString teamOwner = dict->getAsciiString(TheKey_teamOwner, &exists);
|
||||
|
||||
if (exists && teamOwner.compare(oldpname) == 0) {
|
||||
dict->setAsciiString(TheKey_teamOwner, newpname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ensureValidPlayerName(Dict *d)
|
||||
{
|
||||
// ensure there are no illegal chars in it. (in particular, no spaces!)
|
||||
char buf[1024];
|
||||
strcpy(buf, d->getAsciiString(TheKey_playerName).str());
|
||||
for (char* p = buf; *p; ++p)
|
||||
if (!islegalplayernamechar(*p))
|
||||
*p = '_';
|
||||
d->setAsciiString(TheKey_playerName, AsciiString(buf));
|
||||
}
|
||||
|
||||
static AsciiString playerNameForUI(SidesList& sides, int i)
|
||||
{
|
||||
AsciiString b = sides.getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName);
|
||||
if (b.isEmpty())
|
||||
b = NEUTRAL_NAME_STR;
|
||||
return b;
|
||||
}
|
||||
|
||||
static AsciiString UIToInternal(SidesList& sides, const AsciiString& n)
|
||||
{
|
||||
Int i;
|
||||
for (i = 0; i < sides.getNumSides(); i++)
|
||||
{
|
||||
if (playerNameForUI(sides, i) == n)
|
||||
return sides.getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName);
|
||||
}
|
||||
|
||||
DEBUG_CRASH(("ui name not found"));
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
static Bool containsToken(const AsciiString& cur_allies, const AsciiString& tokenIn)
|
||||
{
|
||||
AsciiString name, token;
|
||||
|
||||
name = cur_allies;
|
||||
while (name.nextToken(&token))
|
||||
{
|
||||
if (token == tokenIn)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static AsciiString removeDupsFromEnemies(const AsciiString& cur_allies, const AsciiString& cur_enemies)
|
||||
{
|
||||
AsciiString new_enemies, tmp, token;
|
||||
|
||||
tmp = cur_enemies;
|
||||
while (tmp.nextToken(&token))
|
||||
{
|
||||
if (containsToken(cur_allies, token))
|
||||
continue;
|
||||
if (!new_enemies.isEmpty())
|
||||
new_enemies.concat(" ");
|
||||
new_enemies.concat(token);
|
||||
}
|
||||
return new_enemies;
|
||||
}
|
||||
|
||||
static AsciiString extractFromAlliesList(CListBox *alliesList, SidesList& sides)
|
||||
{
|
||||
char buffer[1024];
|
||||
AsciiString allies;
|
||||
for (Int i = 0; i < alliesList->GetCount(); i++)
|
||||
{
|
||||
if (alliesList->GetSel(i) > 0)
|
||||
{
|
||||
alliesList->GetText(i, buffer);
|
||||
AsciiString nm(buffer);
|
||||
if (!allies.isEmpty())
|
||||
allies.concat(" ");
|
||||
allies.concat(UIToInternal(sides, nm));
|
||||
}
|
||||
}
|
||||
//DEBUG_LOG(("a/e is (%s)\n",allies.str()));
|
||||
return allies;
|
||||
}
|
||||
|
||||
static void buildAlliesList(CListBox *alliesList, SidesList& sides, const AsciiString& omitPlayer)
|
||||
{
|
||||
Int i;
|
||||
AsciiString name, token, oname;
|
||||
|
||||
alliesList->ResetContent();
|
||||
for (i = 0; i < sides.getNumSides(); i++)
|
||||
{
|
||||
name = sides.getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName);
|
||||
if (name == omitPlayer || name.isEmpty())
|
||||
continue;
|
||||
name = playerNameForUI(sides, i);
|
||||
alliesList->AddString(name.str());
|
||||
}
|
||||
}
|
||||
|
||||
static void selectAlliesList(CListBox *alliesList, SidesList& sides, const AsciiString& cur_allies)
|
||||
{
|
||||
Int oindex_in_list;
|
||||
AsciiString name, token, oname;
|
||||
|
||||
if (extractFromAlliesList(alliesList, sides) == cur_allies)
|
||||
return;
|
||||
|
||||
alliesList->SetSel(-1, false);
|
||||
name = cur_allies;
|
||||
while (name.nextToken(&token))
|
||||
{
|
||||
Int i;
|
||||
SidesInfo *si = sides.findSideInfo(token, &i);
|
||||
if (!si)
|
||||
{
|
||||
DEBUG_CRASH(("player %s not found",token.str()));
|
||||
continue;
|
||||
}
|
||||
// must re-find, since list is sorted
|
||||
oindex_in_list = alliesList->FindStringExact(-1, playerNameForUI(sides, i).str());
|
||||
if (oindex_in_list == -1)
|
||||
{
|
||||
DEBUG_CRASH(("hmm, should not happen"));
|
||||
continue;
|
||||
}
|
||||
alliesList->SetSel(oindex_in_list, true);
|
||||
}
|
||||
}
|
||||
|
||||
// returns a str describing how t1 considers t2. (implies nothing about how t2 considers t1)
|
||||
static const char* calcRelationStr(SidesList& sides, int t1, int t2)
|
||||
{
|
||||
const char* allied = "Ally";
|
||||
const char* enemies = "Enemy";
|
||||
const char* neutral = "Neutral";
|
||||
|
||||
SidesInfo* ti1;
|
||||
SidesInfo* ti2;
|
||||
AsciiString t2name;
|
||||
|
||||
|
||||
// we use the relationship between our player's default teams.
|
||||
ti1 = sides.getSideInfo(t1);
|
||||
ti2 = sides.getSideInfo(t2);
|
||||
t2name = ti2->getDict()->getAsciiString(TheKey_playerName);
|
||||
if (containsToken(ti1->getDict()->getAsciiString(TheKey_playerAllies), t2name))
|
||||
return allied;
|
||||
else if (containsToken(ti1->getDict()->getAsciiString(TheKey_playerEnemies), t2name))
|
||||
return enemies;
|
||||
|
||||
// no relation, so assume neutral
|
||||
return neutral;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PlayerListDlg dialog
|
||||
|
||||
|
||||
PlayerListDlg::PlayerListDlg(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(PlayerListDlg::IDD, pParent), m_updating(0)
|
||||
{
|
||||
//{{AFX_DATA_INIT(PlayerListDlg)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void PlayerListDlg::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(PlayerListDlg)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(PlayerListDlg, CDialog)
|
||||
//{{AFX_MSG_MAP(PlayerListDlg)
|
||||
ON_BN_CLICKED(IDC_NEWPLAYER, OnNewplayer)
|
||||
ON_BN_CLICKED(IDC_EDITPLAYER, OnEditplayer)
|
||||
ON_BN_CLICKED(IDC_REMOVEPLAYER, OnRemoveplayer)
|
||||
ON_LBN_SELCHANGE(IDC_PLAYERS, OnSelchangePlayers)
|
||||
ON_LBN_DBLCLK(IDC_PLAYERS, OnDblclkPlayers)
|
||||
ON_LBN_SELCHANGE(IDC_ALLIESLIST, OnSelchangeAllieslist)
|
||||
ON_LBN_SELCHANGE(IDC_ENEMIESLIST, OnSelchangeEnemieslist)
|
||||
ON_BN_CLICKED(IDC_PLAYERISCOMPUTER, OnPlayeriscomputer)
|
||||
ON_CBN_SELENDOK(IDC_PLAYERFACTION, OnEditchangePlayerfaction)
|
||||
ON_BN_CLICKED(IDC_CHANGE_NAME, OnChangePlayername)
|
||||
ON_EN_CHANGE(IDC_PLAYERDISPLAYNAME, OnChangePlayerdisplayname)
|
||||
ON_BN_CLICKED(IDC_PlayerColor, OnColorPress)
|
||||
ON_CBN_SELENDOK(IDC_PlayerColorCombo, OnSelectPlayerColor)
|
||||
ON_BN_CLICKED(IDC_ADDSKIRMISHPLAYERS, OnAddskirmishplayers)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
//ON_EN_CHANGE(IDC_PLAYERNAME, OnChangePlayername)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PlayerListDlg message handlers
|
||||
|
||||
void PlayerListDlg::OnNewplayer()
|
||||
{
|
||||
if (m_sides.getNumSides() >= MAX_PLAYER_COUNT - 1) ///Added -1 so we can always have an observer even for Single player games.
|
||||
return;
|
||||
|
||||
AddPlayerDialog addPlyr("");
|
||||
if (addPlyr.DoModal() != IDOK)
|
||||
return;
|
||||
|
||||
AsciiString addedPTName = addPlyr.getAddedSide();
|
||||
|
||||
AsciiString pname;
|
||||
UnicodeString pnameu;
|
||||
Int num = 1;
|
||||
do {
|
||||
pname.format("player%04d",num);
|
||||
pnameu.format(L"Player %04d's Display Name",num);
|
||||
num++;
|
||||
} while (m_sides.findSideInfo(pname));
|
||||
|
||||
Dict newPlayerDict;
|
||||
newPlayerDict.setAsciiString(TheKey_playerName, pname);
|
||||
newPlayerDict.setBool(TheKey_playerIsHuman, true);
|
||||
newPlayerDict.setUnicodeString(TheKey_playerDisplayName, pnameu);
|
||||
newPlayerDict.setAsciiString(TheKey_playerFaction, addedPTName);
|
||||
newPlayerDict.setAsciiString(TheKey_playerEnemies, AsciiString(""));
|
||||
newPlayerDict.setAsciiString(TheKey_playerAllies, AsciiString(""));
|
||||
|
||||
#ifdef NOT_IN_USE
|
||||
// auto-open the advanced prop editor
|
||||
MapObjectProps editor(&newPlayerDict, "Create New Player", this);
|
||||
if (editor.DoModal() == IDOK)
|
||||
#endif
|
||||
{
|
||||
if (newPlayerDict.getAsciiString(TheKey_playerName).isEmpty())
|
||||
{
|
||||
// sorry, no more than one neutral
|
||||
}
|
||||
else
|
||||
{
|
||||
ensureValidPlayerName(&newPlayerDict);
|
||||
m_sides.addSide(&newPlayerDict);
|
||||
|
||||
Bool modified = m_sides.validateSides();
|
||||
DEBUG_ASSERTLOG(!modified,("had to clean up sides in PlayerListDlg::OnNewplayer"));
|
||||
m_curPlayerIdx = m_sides.getNumSides()-1;
|
||||
updateTheUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnEditplayer()
|
||||
{
|
||||
// TODO: the dialog referenced here has no ok or cancel buttons, so locks the editor
|
||||
// re-enable this routine once it is not a guaranteed hang
|
||||
AfxMessageBox("Implement me. (Sorry.)");
|
||||
return;
|
||||
|
||||
#if 0
|
||||
Dict *playerDict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
AsciiString pnameold = playerDict->getAsciiString(TheKey_playerName);
|
||||
Bool isneutral = pnameold.isEmpty();
|
||||
if (isneutral)
|
||||
return;
|
||||
|
||||
Dict playerDictCopy = *playerDict;
|
||||
MapObjectProps editor(&playerDictCopy, "Edit Player", this);
|
||||
if (editor.DoModal() == IDOK)
|
||||
{
|
||||
ensureValidPlayerName(&playerDictCopy);
|
||||
|
||||
if (playerDict->getAsciiString(TheKey_playerName) != playerDictCopy.getAsciiString(TheKey_playerName))
|
||||
{
|
||||
AsciiString tname;
|
||||
tname.set("team");
|
||||
tname.concat(playerDict->getAsciiString(TheKey_playerName));
|
||||
Int count = MapObject::countMapObjectsWithOwner(tname);
|
||||
if (count > 0)
|
||||
{
|
||||
CString msg;
|
||||
msg.Format(IDS_RENAMING_INUSE_TEAM, count);
|
||||
if (::AfxMessageBox(msg, MB_YESNO) == IDNO)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*m_sides.getSideInfo(m_curPlayerIdx)->getDict() = playerDictCopy;
|
||||
|
||||
AsciiString pnamenew = playerDictCopy.getAsciiString(TheKey_playerName);
|
||||
fixDefaultTeamName(m_sides, pnameold, pnamenew);
|
||||
|
||||
Bool modified = m_sides.validateSides();
|
||||
DEBUG_ASSERTLOG(!modified,("had to clean up sides in PlayerListDlg::OnEditplayer"));
|
||||
|
||||
updateTheUI();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnRemoveplayer()
|
||||
{
|
||||
Dict *playerDict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
AsciiString pname = playerDict->getAsciiString(TheKey_playerName);
|
||||
Bool isneutral = pname.isEmpty();
|
||||
if (isneutral)
|
||||
return;
|
||||
|
||||
Int i;
|
||||
Int count = 0;
|
||||
for (i = 0; i < m_sides.getNumTeams(); i++)
|
||||
{
|
||||
Dict *tdict = m_sides.getTeamInfo(i)->getDict();
|
||||
if (tdict->getAsciiString(TheKey_teamOwner) == pname)
|
||||
{
|
||||
count += MapObject::countMapObjectsWithOwner(tdict->getAsciiString(TheKey_teamName));
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
CString msg;
|
||||
msg.Format(IDS_REMOVING_INUSE_TEAM, count);
|
||||
if (::AfxMessageBox(msg, MB_YESNO) == IDNO)
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_sides.getNumSides() <= 1)
|
||||
return;
|
||||
|
||||
m_sides.removeSide(m_curPlayerIdx);
|
||||
try_again:
|
||||
for (i = 0; i < m_sides.getNumTeams(); i++)
|
||||
{
|
||||
Dict *tdict = m_sides.getTeamInfo(i)->getDict();
|
||||
if (tdict->getAsciiString(TheKey_teamOwner) == pname)
|
||||
{
|
||||
m_sides.removeTeam(i);
|
||||
goto try_again;
|
||||
}
|
||||
}
|
||||
|
||||
Bool modified = m_sides.validateSides();
|
||||
DEBUG_ASSERTLOG(!modified,("had to clean up sides in PlayerListDlg::OnRemoveplayer"));
|
||||
updateTheUI();
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnSelchangePlayers()
|
||||
{
|
||||
CListBox *list = (CListBox*)GetDlgItem(IDC_PLAYERS);
|
||||
m_curPlayerIdx = list->GetCurSel();
|
||||
updateTheUI();
|
||||
}
|
||||
|
||||
void PlayerListDlg::updateTheUI(void)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
if (m_updating)
|
||||
return;
|
||||
|
||||
++m_updating;
|
||||
|
||||
// make sure everything is canonical.
|
||||
Bool modified = m_sides.validateSides();
|
||||
DEBUG_ASSERTLOG(!modified,("had to clean up sides in PlayerListDlg::updateTheUI! (caller should do this)"));
|
||||
|
||||
if (m_curPlayerIdx < 0) m_curPlayerIdx = 0;
|
||||
if (m_curPlayerIdx >= m_sides.getNumSides())
|
||||
m_curPlayerIdx = m_sides.getNumSides()-1;
|
||||
|
||||
// update player list
|
||||
CListBox *list = (CListBox*)GetDlgItem(IDC_PLAYERS);
|
||||
list->ResetContent();
|
||||
|
||||
Int len = m_sides.getNumSides();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Dict *d = m_sides.getSideInfo(i)->getDict();
|
||||
AsciiString name = d->getAsciiString(TheKey_playerName);
|
||||
UnicodeString uni = d->getUnicodeString(TheKey_playerDisplayName);
|
||||
AsciiString fmt;
|
||||
if (name.isEmpty())
|
||||
fmt = NEUTRAL_NAME_STR;
|
||||
else
|
||||
fmt.format("%s=\"%ls\"",name.str(),uni.str());
|
||||
list->AddString(fmt.str());
|
||||
}
|
||||
|
||||
Dict *pdict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
AsciiString cur_pname = pdict->getAsciiString(TheKey_playerName);
|
||||
UnicodeString cur_pdname = pdict->getUnicodeString(TheKey_playerDisplayName);
|
||||
Bool isNeutral = cur_pname.isEmpty();
|
||||
|
||||
// update player name
|
||||
{
|
||||
CWnd *playername = GetDlgItem(IDC_PLAYERNAME);
|
||||
playername->EnableWindow(!isNeutral); // neutral names are not editable
|
||||
playername->GetWindowText(buffer, sizeof(buffer)-2);
|
||||
if (strcmp(cur_pname.str(), buffer) != 0)
|
||||
playername->SetWindowText(cur_pname.str());
|
||||
}
|
||||
|
||||
// update display name
|
||||
{
|
||||
CWnd *playerdname = GetDlgItem(IDC_PLAYERDISPLAYNAME);
|
||||
playerdname->EnableWindow(!isNeutral); // neutral names are not editable
|
||||
playerdname->GetWindowText(buffer, sizeof(buffer)-2);
|
||||
AsciiString cur_pdnamea;
|
||||
cur_pdnamea.translate(cur_pdname);
|
||||
if (strcmp(cur_pdnamea.str(), buffer) != 0)
|
||||
playerdname->SetWindowText(cur_pdnamea.str());
|
||||
}
|
||||
|
||||
// update color button
|
||||
{
|
||||
RGBColor rgb;
|
||||
Bool hasColor = false;
|
||||
Int color = pdict->getInt(TheKey_playerColor, &hasColor);
|
||||
if (hasColor) {
|
||||
rgb.setFromInt(color);
|
||||
} else {
|
||||
AsciiString tmplname = pdict->getAsciiString(TheKey_playerFaction);
|
||||
const PlayerTemplate* pt = ThePlayerTemplateStore->findPlayerTemplate(NAMEKEY(tmplname));
|
||||
if (pt) {
|
||||
rgb = *pt->getPreferredColor();
|
||||
}
|
||||
}
|
||||
m_colorButton.setColor(rgb);
|
||||
SelectColor(rgb);
|
||||
|
||||
}
|
||||
|
||||
// update control button
|
||||
{
|
||||
Bool isHuman = pdict->getBool(TheKey_playerIsHuman);
|
||||
CButton *controller = (CButton*)GetDlgItem(IDC_PLAYERISCOMPUTER);
|
||||
controller->SetCheck(isHuman ? 0 : 1);
|
||||
controller->EnableWindow(!isNeutral);
|
||||
}
|
||||
|
||||
// update factions popup
|
||||
{
|
||||
CComboBox *factions = (CComboBox*)GetDlgItem(IDC_PLAYERFACTION);
|
||||
factions->ResetContent();
|
||||
if (ThePlayerTemplateStore)
|
||||
{
|
||||
for (i = 0; i < ThePlayerTemplateStore->getPlayerTemplateCount(); i++)
|
||||
{
|
||||
AsciiString nm = ThePlayerTemplateStore->getNthPlayerTemplate(i)->getName();
|
||||
factions->AddString(nm.str());
|
||||
}
|
||||
}
|
||||
i = factions->FindStringExact(-1, pdict->getAsciiString(TheKey_playerFaction).str());
|
||||
factions->SetCurSel(i);
|
||||
}
|
||||
|
||||
// update allies & enemies
|
||||
CListBox *allieslist = (CListBox*)GetDlgItem(IDC_ALLIESLIST);
|
||||
CListBox *enemieslist = (CListBox*)GetDlgItem(IDC_ENEMIESLIST);
|
||||
CListBox *regardOthers = (CListBox*)GetDlgItem(IDC_PLAYER_ATTITUDE_OUT);
|
||||
CListBox *regardMe = (CListBox*)GetDlgItem(IDC_PLAYER_ATTITUDE_IN);
|
||||
|
||||
AsciiString cur_allies = m_sides.getSideInfo(m_curPlayerIdx)->getDict()->getAsciiString(TheKey_playerAllies);
|
||||
buildAlliesList(allieslist, m_sides, cur_pname);
|
||||
selectAlliesList(allieslist, m_sides, cur_allies);
|
||||
allieslist->EnableWindow(!isNeutral);
|
||||
|
||||
AsciiString cur_enemies = m_sides.getSideInfo(m_curPlayerIdx)->getDict()->getAsciiString(TheKey_playerEnemies);
|
||||
buildAlliesList(enemieslist, m_sides, cur_pname);
|
||||
selectAlliesList(enemieslist, m_sides, cur_enemies);
|
||||
enemieslist->EnableWindow(!isNeutral);
|
||||
|
||||
regardOthers->ResetContent();
|
||||
regardMe->ResetContent();
|
||||
const char* rstr;
|
||||
AsciiString pname;
|
||||
for (i = 0; i < m_sides.getNumSides(); i++)
|
||||
{
|
||||
pname = m_sides.getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName);
|
||||
if (pname.isEmpty() || pname == cur_pname)
|
||||
continue; // skip neutral and self
|
||||
pname = playerNameForUI(m_sides, i);
|
||||
|
||||
rstr = calcRelationStr(m_sides, m_curPlayerIdx, i);
|
||||
sprintf(buffer, "%s: %s",pname.str(),rstr);
|
||||
regardOthers->AddString(buffer);
|
||||
|
||||
rstr = calcRelationStr(m_sides, i, m_curPlayerIdx);
|
||||
sprintf(buffer, "%s: %s",pname.str(),rstr);
|
||||
regardMe->AddString(buffer);
|
||||
}
|
||||
|
||||
list->SetCurSel(m_curPlayerIdx);
|
||||
|
||||
CWnd *newbtn = GetDlgItem(IDC_NEWPLAYER);
|
||||
CWnd *editbtn = GetDlgItem(IDC_EDITPLAYER);
|
||||
CWnd *rmvbtn = GetDlgItem(IDC_REMOVEPLAYER);
|
||||
Dict *playerDict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
Bool isneutral = playerDict->getAsciiString(TheKey_playerName).isEmpty();
|
||||
if( newbtn )
|
||||
newbtn->EnableWindow(m_sides.getNumSides() < MAX_PLAYER_COUNT);
|
||||
if( editbtn )
|
||||
editbtn->EnableWindow(!isneutral);
|
||||
if( rmvbtn )
|
||||
rmvbtn->EnableWindow(m_sides.getNumSides() > 1 && !isneutral);
|
||||
|
||||
Invalidate();
|
||||
UpdateWindow();
|
||||
|
||||
--m_updating;
|
||||
}
|
||||
|
||||
|
||||
BOOL PlayerListDlg::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_updating = 0;
|
||||
m_sides = *TheSidesList;
|
||||
m_curPlayerIdx = thePrevCurPlyr;
|
||||
|
||||
CRect rect;
|
||||
CWnd *item = GetDlgItem(IDC_PlayerColor);
|
||||
if (item) {
|
||||
item->GetWindowRect(&rect);
|
||||
ScreenToClient(&rect);
|
||||
DWORD style = item->GetStyle();
|
||||
m_colorButton.Create("", style, rect, this, IDC_PlayerColor);
|
||||
item->DestroyWindow();
|
||||
}
|
||||
|
||||
updateTheUI();
|
||||
PopulateColorComboBox();
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnDblclkPlayers()
|
||||
{
|
||||
OnEditplayer();
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnColorPress()
|
||||
{
|
||||
Dict *playerDict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
CColorDialog dlg;
|
||||
if (dlg.DoModal() == IDOK) {
|
||||
m_colorButton.setColor(CButtonShowColor::BGRtoRGB(dlg.GetColor()));
|
||||
RGBColor color = m_colorButton.getColor();
|
||||
playerDict->setInt(TheKey_playerColor, color.getAsInt());
|
||||
}
|
||||
updateTheUI();
|
||||
}
|
||||
|
||||
void PlayerListDlg::PopulateColorComboBox()
|
||||
{
|
||||
Int numColors = TheMultiplayerSettings->getNumColors();
|
||||
AsciiString colorName;
|
||||
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_PlayerColorCombo);
|
||||
if (pCombo) {
|
||||
for (Int c=0; c<numColors; ++c)
|
||||
{
|
||||
MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(c);
|
||||
if (!def)
|
||||
continue;
|
||||
UnicodeString colorName = TheGameText->fetch(def->getTooltipName().str());
|
||||
AsciiString str;
|
||||
str.translate(colorName);
|
||||
pCombo->AddString(str.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerListDlg::SelectColor(RGBColor rgb)
|
||||
{
|
||||
Int numColors = TheMultiplayerSettings->getNumColors();
|
||||
AsciiString colorName;
|
||||
Bool selected = false;
|
||||
|
||||
CComboBox *pCombo = (CComboBox*)GetDlgItem(IDC_PlayerColorCombo);
|
||||
if (pCombo) {
|
||||
for (Int c=0; c<numColors; ++c)
|
||||
{
|
||||
MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(c);
|
||||
if (!def)
|
||||
continue;
|
||||
if (rgb.getAsInt() == def->getRGBValue().getAsInt()) {
|
||||
pCombo->SetCurSel(c);
|
||||
selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!selected) {
|
||||
pCombo->SetCurSel(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnSelectPlayerColor()
|
||||
{
|
||||
CComboBox *pCombo = (CComboBox *)GetDlgItem(IDC_PlayerColorCombo);
|
||||
Dict *playerDict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
if (pCombo && playerDict) {
|
||||
CString str;
|
||||
pCombo->GetWindowText(str);
|
||||
Int index = -1;
|
||||
Int numColors = TheMultiplayerSettings->getNumColors();
|
||||
for (Int c=0; c<numColors; ++c)
|
||||
{
|
||||
MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(c);
|
||||
if (!def)
|
||||
continue;
|
||||
UnicodeString colorName = TheGameText->fetch(def->getTooltipName().str());
|
||||
AsciiString asciiColor;
|
||||
asciiColor.translate(colorName);
|
||||
|
||||
if (str == asciiColor.str()) {
|
||||
index = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
Int color = TheMultiplayerSettings->getColor(c)->getColor();
|
||||
playerDict->setInt(TheKey_playerColor, color);
|
||||
}
|
||||
}
|
||||
updateTheUI();
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnSelchangeAllieslist()
|
||||
{
|
||||
Dict *playerDict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
AsciiString pname = playerDict->getAsciiString(TheKey_playerName);
|
||||
Bool isneutral = pname.isEmpty();
|
||||
if (isneutral)
|
||||
return;
|
||||
|
||||
CListBox *allieslist = (CListBox*)GetDlgItem(IDC_ALLIESLIST);
|
||||
AsciiString allies = extractFromAlliesList(allieslist, m_sides);
|
||||
|
||||
CListBox *enemieslist = (CListBox*)GetDlgItem(IDC_ENEMIESLIST);
|
||||
AsciiString enemies = extractFromAlliesList(enemieslist, m_sides);
|
||||
|
||||
enemies = removeDupsFromEnemies(allies, enemies);
|
||||
|
||||
m_sides.getSideInfo(m_curPlayerIdx)->getDict()->setAsciiString(TheKey_playerAllies, allies);
|
||||
m_sides.getSideInfo(m_curPlayerIdx)->getDict()->setAsciiString(TheKey_playerEnemies, enemies);
|
||||
|
||||
updateTheUI();
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnSelchangeEnemieslist()
|
||||
{
|
||||
OnSelchangeAllieslist();
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnOK()
|
||||
{
|
||||
Bool modified = m_sides.validateSides();
|
||||
DEBUG_ASSERTLOG(!modified,("had to clean up sides in CTeamsDialog::OnOK"));
|
||||
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
SidesListUndoable *pUndo = new SidesListUndoable(m_sides, pDoc);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
|
||||
thePrevCurPlyr = m_curPlayerIdx;
|
||||
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnCancel()
|
||||
{
|
||||
CDialog::OnCancel();
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnPlayeriscomputer()
|
||||
{
|
||||
CButton *b = (CButton*)GetDlgItem(IDC_PLAYERISCOMPUTER);
|
||||
m_sides.getSideInfo(m_curPlayerIdx)->getDict()->setBool(TheKey_playerIsHuman, b->GetCheck() == 0);
|
||||
|
||||
updateTheUI();
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnEditchangePlayerfaction()
|
||||
{
|
||||
CComboBox *faction = (CComboBox*)GetDlgItem(IDC_PLAYERFACTION);
|
||||
|
||||
if (faction) {
|
||||
// get the text out of the combo. If it is user-typed, sel will be -1, otherwise it will be >=0
|
||||
CString theText;
|
||||
Int sel = faction->GetCurSel();
|
||||
if (sel >= 0) {
|
||||
faction->GetLBText(sel, theText);
|
||||
} else {
|
||||
faction->GetWindowText(theText);
|
||||
}
|
||||
AsciiString name((LPCTSTR)theText);
|
||||
|
||||
Dict *pdict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
pdict->setAsciiString(TheKey_playerFaction, name);
|
||||
|
||||
updateTheUI();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnChangePlayername()
|
||||
{
|
||||
CWnd *playername = GetDlgItem(IDC_PLAYERNAME);
|
||||
char buf[1024];
|
||||
playername->GetWindowText(buf, sizeof(buf)-2);
|
||||
|
||||
Dict *pdict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
AsciiString pnamenew(buf);
|
||||
AsciiString pnameold = pdict->getAsciiString(TheKey_playerName);
|
||||
|
||||
if (pnameold == pnamenew)
|
||||
return; // hmm, no change, so just punt.
|
||||
|
||||
if (m_sides.findSideInfo(pnamenew))
|
||||
{
|
||||
::AfxMessageBox(IDS_NAME_IN_USE);
|
||||
}
|
||||
else
|
||||
{
|
||||
pdict->setAsciiString(TheKey_playerName, pnamenew);
|
||||
ensureValidPlayerName(pdict);
|
||||
|
||||
updateAllTeams(m_sides, pnameold, pnamenew);
|
||||
fixDefaultTeamName(m_sides, pnameold, pnamenew);
|
||||
}
|
||||
|
||||
updateTheUI();
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnChangePlayerdisplayname()
|
||||
{
|
||||
CWnd *playername = GetDlgItem(IDC_PLAYERDISPLAYNAME);
|
||||
char buf[1024];
|
||||
playername->GetWindowText(buf, sizeof(buf)-2);
|
||||
|
||||
Dict *pdict = m_sides.getSideInfo(m_curPlayerIdx)->getDict();
|
||||
|
||||
AsciiString tmp(buf);
|
||||
UnicodeString pnamenew;
|
||||
pnamenew.translate(tmp);
|
||||
UnicodeString pnameold = pdict->getUnicodeString(TheKey_playerDisplayName);
|
||||
|
||||
if (pnameold == pnamenew)
|
||||
return; // hmm, no change, so just punt.
|
||||
|
||||
pdict->setUnicodeString(TheKey_playerDisplayName, pnamenew);
|
||||
|
||||
updateTheUI();
|
||||
}
|
||||
|
||||
static void addSide(SidesList *sides, AsciiString faction,
|
||||
AsciiString playerName, UnsignedShort *playerUName)
|
||||
{
|
||||
if (!sides->findSideInfo(playerName)) {
|
||||
|
||||
Dict newPlayerDict;
|
||||
UnicodeString playerUStr;
|
||||
playerUStr = playerUName;
|
||||
newPlayerDict.setAsciiString(TheKey_playerName, playerName);
|
||||
newPlayerDict.setBool(TheKey_playerIsHuman, false);
|
||||
newPlayerDict.setUnicodeString(TheKey_playerDisplayName, playerUStr);
|
||||
newPlayerDict.setAsciiString(TheKey_playerFaction, faction);
|
||||
newPlayerDict.setAsciiString(TheKey_playerEnemies, AsciiString(""));
|
||||
newPlayerDict.setAsciiString(TheKey_playerAllies, AsciiString(""));
|
||||
|
||||
ensureValidPlayerName(&newPlayerDict);
|
||||
sides->addSide(&newPlayerDict);
|
||||
|
||||
Bool modified = sides->validateSides();
|
||||
DEBUG_ASSERTLOG(!modified,("had to clean up sides in PlayerListDlg::OnNewplayer"));
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerListDlg::OnAddskirmishplayers()
|
||||
{
|
||||
// PlyrCivilian
|
||||
|
||||
addSide(&m_sides, "FactionCivilian", "PlyrCivilian", L"PlyrCivilian");
|
||||
addSide(&m_sides, "FactionAmerica", "SkirmishAmerica", L"SkirmishAmerica");
|
||||
addSide(&m_sides, "FactionChina", "SkirmishChina", L"SkirmishChina");
|
||||
addSide(&m_sides, "FactionGLA", "SkirmishGLA", L"SkirmishGLA");
|
||||
|
||||
addSide(&m_sides, "FactionAmericaAirForceGeneral", "SkirmishAmericaAirForceGeneral", L"SkirmishAmericaAirForceGeneral");
|
||||
addSide(&m_sides, "FactionAmericaLaserGeneral", "SkirmishAmericaLaserGeneral", L"SkirmishAmericaLaserGeneral");
|
||||
addSide(&m_sides, "FactionAmericaSuperWeaponGeneral", "SkirmishAmericaSuperWeaponGeneral", L"SkirmishAmericaSuperWeaponGeneral");
|
||||
addSide(&m_sides, "FactionChinaTankGeneral", "SkirmishChinaTankGeneral", L"SkirmishChinaTankGeneral");
|
||||
addSide(&m_sides, "FactionChinaNukeGeneral", "SkirmishChinaNukeGeneral", L"SkirmishChinaNukeGeneral");
|
||||
addSide(&m_sides, "FactionChinaInfantryGeneral", "SkirmishChinaInfantryGeneral", L"SkirmishChinaInfantryGeneral");
|
||||
addSide(&m_sides, "FactionGLADemolitionGeneral", "SkirmishGLADemolitionGeneral", L"SkirmishGLADemolitionGeneral");
|
||||
addSide(&m_sides, "FactionGLAToxinGeneral", "SkirmishGLAToxinGeneral", L"SkirmishGLAToxinGeneral");
|
||||
addSide(&m_sides, "FactionGLAStealthGeneral", "SkirmishGLAStealthGeneral", L"SkirmishGLAStealthGeneral");
|
||||
updateTheUI();
|
||||
}
|
||||
176
GeneralsMD/Code/Tools/WorldBuilder/src/propedit.cpp
Normal file
176
GeneralsMD/Code/Tools/WorldBuilder/src/propedit.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// propedit.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "..\include\propedit.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PropEdit dialog
|
||||
|
||||
#define NO_RESTRICT_KEYS
|
||||
|
||||
PropEdit::PropEdit(AsciiString* key, Dict::DataType* type, AsciiString* value, Bool valueOnly, CWnd *parent)
|
||||
: CDialog(PropEdit::IDD, parent), m_key(key), m_type(type), m_value(value), m_updating(0), m_valueOnly(valueOnly)
|
||||
{
|
||||
//{{AFX_DATA_INIT(PropEdit)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void PropEdit::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(PropEdit)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(PropEdit, CDialog)
|
||||
//{{AFX_MSG_MAP(PropEdit)
|
||||
ON_EN_CHANGE(IDC_KEYNAME, OnChangeKeyname)
|
||||
ON_CBN_EDITCHANGE(IDC_KEYTYPE, OnEditchangeKeytype)
|
||||
ON_CBN_CLOSEUP(IDC_KEYTYPE, OnCloseupKeytype)
|
||||
ON_CBN_SELCHANGE(IDC_KEYTYPE, OnSelchangeKeytype)
|
||||
ON_EN_CHANGE(IDC_VALUE, OnChangeValue)
|
||||
ON_BN_CLICKED(IDC_PROPBOOL, OnPropbool)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
void PropEdit::validate()
|
||||
{
|
||||
if (m_updating > 0)
|
||||
return;
|
||||
|
||||
Bool enabOK = true;
|
||||
CWnd *ok = GetDlgItem(IDOK);
|
||||
CWnd *keyname = GetDlgItem(IDC_KEYNAME);
|
||||
CWnd *valuename = GetDlgItem(IDC_VALUE);
|
||||
CComboBox *keytype = (CComboBox*)GetDlgItem(IDC_KEYTYPE);
|
||||
CButton *valuebool = (CButton*)GetDlgItem(IDC_PROPBOOL);
|
||||
*m_type = (Dict::DataType)keytype->GetCurSel();
|
||||
|
||||
valuename->ShowWindow(*m_type != Dict::DICT_BOOL ? SW_SHOW : SW_HIDE);
|
||||
valuebool->ShowWindow(*m_type == Dict::DICT_BOOL ? SW_SHOW : SW_HIDE);
|
||||
|
||||
keyname->EnableWindow(!m_valueOnly);
|
||||
keytype->EnableWindow(!m_valueOnly);
|
||||
|
||||
CString tmp;
|
||||
|
||||
keyname->GetWindowText(tmp);
|
||||
#ifdef RESTRICT_KEYS
|
||||
tmp.MakeLower(); // we force user-entered keys to all-lowercase, to prevent case issues
|
||||
#endif
|
||||
*m_key = (LPCTSTR)tmp;
|
||||
|
||||
if (!m_valueOnly)
|
||||
{
|
||||
int len = tmp.GetLength();
|
||||
if (len <= 0)
|
||||
enabOK = false;
|
||||
|
||||
#ifdef RESTRICT_KEYS
|
||||
// only letters, numbers, and underline allowed
|
||||
for (int i = 0; i < len; i++)
|
||||
if (!isalnum(tmp[i]) && tmp[i] != '_')
|
||||
enabOK = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
valuename->GetWindowText(tmp);
|
||||
*m_value = (LPCTSTR)tmp;
|
||||
switch (*m_type)
|
||||
{
|
||||
case Dict::DICT_BOOL:
|
||||
*m_value = valuebool->GetCheck() ? "true" : "false";
|
||||
break;
|
||||
case Dict::DICT_INT:
|
||||
break;
|
||||
case Dict::DICT_REAL:
|
||||
break;
|
||||
case Dict::DICT_ASCIISTRING:
|
||||
break;
|
||||
case Dict::DICT_UNICODESTRING:
|
||||
break;
|
||||
default:
|
||||
enabOK = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ok->EnableWindow(enabOK);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// PropEdit message handlers
|
||||
|
||||
void PropEdit::OnChangeKeyname()
|
||||
{
|
||||
validate();
|
||||
}
|
||||
|
||||
void PropEdit::OnEditchangeKeytype()
|
||||
{
|
||||
validate();
|
||||
}
|
||||
|
||||
void PropEdit::OnCloseupKeytype()
|
||||
{
|
||||
validate();
|
||||
}
|
||||
|
||||
void PropEdit::OnSelchangeKeytype()
|
||||
{
|
||||
validate();
|
||||
}
|
||||
|
||||
void PropEdit::OnChangeValue()
|
||||
{
|
||||
validate();
|
||||
}
|
||||
|
||||
|
||||
BOOL PropEdit::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
CWnd *keyname = GetDlgItem(IDC_KEYNAME);
|
||||
CWnd *valuename = GetDlgItem(IDC_VALUE);
|
||||
CComboBox *keytype = (CComboBox*)GetDlgItem(IDC_KEYTYPE);
|
||||
CButton *valuebool = (CButton*)GetDlgItem(IDC_PROPBOOL);
|
||||
|
||||
++m_updating;
|
||||
keytype->SetCurSel((Int)*m_type);
|
||||
keyname->SetWindowText(m_key->str());
|
||||
valuename->SetWindowText(m_value->str());
|
||||
valuebool->SetCheck(strcmp(m_value->str(), "true")==0 ? 1 : 0);
|
||||
--m_updating;
|
||||
validate();
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
}
|
||||
|
||||
void PropEdit::OnPropbool()
|
||||
{
|
||||
validate();
|
||||
}
|
||||
641
GeneralsMD/Code/Tools/WorldBuilder/src/teamsdialog.cpp
Normal file
641
GeneralsMD/Code/Tools/WorldBuilder/src/teamsdialog.cpp
Normal file
@@ -0,0 +1,641 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// teamsdialog.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "worldbuilder.h"
|
||||
#include "teamsdialog.h"
|
||||
#include "CFixTeamOwnerDialog.h"
|
||||
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "TeamBehavior.h"
|
||||
#include "TeamGeneric.h"
|
||||
#include "TeamIdentity.h"
|
||||
#include "TeamReinforcement.h"
|
||||
#include "TeamObjectProperties.h"
|
||||
#include "WorldBuilderDoc.h"
|
||||
#include "cundoable.h"
|
||||
#include "WBView3d.h"
|
||||
|
||||
static Int thePrevCurTeam = 0;
|
||||
|
||||
static const char* NEUTRAL_NAME_STR = "(neutral)";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CTeamsDialog dialog
|
||||
|
||||
|
||||
CTeamsDialog::CTeamsDialog(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(CTeamsDialog::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(CTeamsDialog)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
}
|
||||
|
||||
|
||||
void CTeamsDialog::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(CTeamsDialog)
|
||||
// NOTE: the ClassWizard will add DDX and DDV calls here
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(CTeamsDialog, CDialog)
|
||||
//{{AFX_MSG_MAP(CTeamsDialog)
|
||||
ON_BN_CLICKED(IDC_NEWTEAM, OnNewteam)
|
||||
ON_BN_CLICKED(IDC_DELETETEAM, OnDeleteteam)
|
||||
ON_LBN_SELCHANGE(IDC_PLAYER_LIST, OnSelchangePlayerList)
|
||||
ON_NOTIFY(NM_CLICK, IDC_TEAMS_LIST, OnClickTeamsList)
|
||||
ON_NOTIFY(NM_DBLCLK, IDC_TEAMS_LIST, OnDblclkTeamsList)
|
||||
ON_BN_CLICKED(IDC_COPYTEAM, OnCopyteam)
|
||||
ON_BN_CLICKED(IDC_SelectTeamMembers, OnSelectTeamMembers)
|
||||
ON_BN_CLICKED(IDC_MOVEDOWNTEAM, OnMoveDownTeam)
|
||||
ON_BN_CLICKED(IDC_MOVEUPTEAM, OnMoveUpTeam)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CTeamsDialog message handlers
|
||||
|
||||
static Bool isPlayerDefaultTeamIndex(SidesList& sides, Int i)
|
||||
{
|
||||
return sides.isPlayerDefaultTeam(sides.getTeamInfo(i));
|
||||
}
|
||||
|
||||
static AsciiString playerNameForUI(SidesList& sides, int i)
|
||||
{
|
||||
AsciiString b = sides.getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName);
|
||||
if (b.isEmpty())
|
||||
b = NEUTRAL_NAME_STR;
|
||||
return b;
|
||||
}
|
||||
|
||||
static AsciiString teamNameForUI(SidesList& sides, int i)
|
||||
{
|
||||
TeamsInfo *ti = sides.getTeamInfo(i);
|
||||
if (sides.isPlayerDefaultTeam(ti))
|
||||
{
|
||||
AsciiString ostr = ti->getDict()->getAsciiString(TheKey_teamOwner);
|
||||
if (ostr.isEmpty())
|
||||
ostr = NEUTRAL_NAME_STR;
|
||||
AsciiString n;
|
||||
n.format("(default team)");
|
||||
return n;
|
||||
}
|
||||
|
||||
return ti->getDict()->getAsciiString(TheKey_teamName);
|
||||
}
|
||||
|
||||
static AsciiString UIToInternal(SidesList& sides, const AsciiString& n)
|
||||
{
|
||||
Int i;
|
||||
for (i = 0; i < sides.getNumSides(); i++)
|
||||
{
|
||||
if (playerNameForUI(sides, i) == n)
|
||||
return sides.getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName);
|
||||
}
|
||||
|
||||
DEBUG_CRASH(("ui name not found"));
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
static Int findTeamParentIndex(SidesList& sides, Int i)
|
||||
{
|
||||
AsciiString oname = sides.getTeamInfo(i)->getDict()->getAsciiString(TheKey_teamOwner);
|
||||
|
||||
for (int j = 0; j < sides.getNumSides(); j++)
|
||||
{
|
||||
if (oname == sides.getSideInfo(j)->getDict()->getAsciiString(TheKey_playerName))
|
||||
{
|
||||
return -(j+1);
|
||||
}
|
||||
}
|
||||
DEBUG_CRASH(("hmm"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CTeamsDialog::updateUI(Int whatToRebuild)
|
||||
{
|
||||
if (m_updating)
|
||||
return;
|
||||
|
||||
++m_updating;
|
||||
|
||||
// make sure everything is canonical.
|
||||
Bool modified = m_sides.validateSides();
|
||||
DEBUG_ASSERTLOG(!modified,("had to clean up sides in CTeamsDialog::updateUI! (caller should do this)"));
|
||||
if (modified)
|
||||
{
|
||||
whatToRebuild = REBUILD_ALL; // assume the worst.
|
||||
}
|
||||
|
||||
// constrain team index.
|
||||
if (m_curTeam < 0) m_curTeam = 0;
|
||||
if (m_curTeam >= m_sides.getNumTeams())
|
||||
m_curTeam = m_sides.getNumTeams()-1;
|
||||
|
||||
if (whatToRebuild & REBUILD_TEAMS)
|
||||
{
|
||||
UpdateTeamsList();
|
||||
}
|
||||
|
||||
Bool isDefault = true;
|
||||
|
||||
if (m_curTeam >= 0) {
|
||||
isDefault = isPlayerDefaultTeamIndex(m_sides, m_curTeam);
|
||||
}
|
||||
|
||||
// update delete button
|
||||
CButton *del = (CButton*)GetDlgItem(IDC_DELETETEAM);
|
||||
del->EnableWindow(!isDefault); // toplevel team names are delete-able, but "default" teams are not
|
||||
CButton *copyteam = (CButton*)GetDlgItem(IDC_COPYTEAM);
|
||||
copyteam->EnableWindow(!isDefault); // toplevel team names are delete-able, but "default" teams are not
|
||||
|
||||
//update move up and move down buttons
|
||||
CButton *moveup = (CButton*)GetDlgItem(IDC_MOVEUPTEAM);
|
||||
moveup->EnableWindow(!isDefault);
|
||||
CButton *movedown = (CButton*)GetDlgItem(IDC_MOVEDOWNTEAM);
|
||||
movedown->EnableWindow(!isDefault);
|
||||
|
||||
CListBox *players = (CListBox*)GetDlgItem(IDC_PLAYER_LIST);
|
||||
Int whichPlayer = players->GetCurSel();
|
||||
|
||||
CButton *newteam = (CButton*)GetDlgItem(IDC_NEWTEAM);
|
||||
newteam->EnableWindow(whichPlayer>0); // toplevel team names are delete-able, but "default" teams are not
|
||||
|
||||
|
||||
--m_updating;
|
||||
}
|
||||
|
||||
BOOL CTeamsDialog::OnInitDialog()
|
||||
{
|
||||
CRect rect;
|
||||
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
// default values for our vars
|
||||
m_updating = 0;
|
||||
m_sides = *TheSidesList;
|
||||
m_curTeam = thePrevCurTeam;
|
||||
|
||||
CListCtrl *pList = (CListCtrl *)GetDlgItem(IDC_TEAMS_LIST);
|
||||
pList->InsertColumn(0, "Team Name", LVCFMT_LEFT, 150, 0);
|
||||
pList->InsertColumn(1, "Origin", LVCFMT_LEFT, 150, 1);
|
||||
pList->InsertColumn(2, "Priority", LVCFMT_LEFT, 50, 2);
|
||||
pList->InsertColumn(3, "Script", LVCFMT_LEFT, 150, 3);
|
||||
pList->InsertColumn(4, "Trigger", LVCFMT_LEFT, 150, 4);
|
||||
|
||||
|
||||
CListBox *players = (CListBox*)GetDlgItem(IDC_PLAYER_LIST);
|
||||
players->ResetContent();
|
||||
for (int i = 0; i < m_sides.getNumSides(); i++)
|
||||
{
|
||||
players->AddString(playerNameForUI(m_sides, i).str());
|
||||
}
|
||||
|
||||
validateTeamOwners();
|
||||
|
||||
updateUI(REBUILD_ALL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnOK()
|
||||
{
|
||||
Bool modified = m_sides.validateSides();
|
||||
DEBUG_ASSERTLOG(!modified,("had to clean up sides in CTeamsDialog::OnOK"));
|
||||
|
||||
CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
SidesListUndoable *pUndo = new SidesListUndoable(m_sides, pDoc);
|
||||
pDoc->AddAndDoUndoable(pUndo);
|
||||
REF_PTR_RELEASE(pUndo); // belongs to pDoc now.
|
||||
|
||||
thePrevCurTeam = m_curTeam;
|
||||
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnCancel()
|
||||
{
|
||||
CDialog::OnCancel();
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnNewteam()
|
||||
{
|
||||
Int num = 1;
|
||||
AsciiString tname;
|
||||
do
|
||||
{
|
||||
tname.format("team%04d",num++);
|
||||
}
|
||||
while (m_sides.findTeamInfo(tname));
|
||||
|
||||
AsciiString oname = m_sides.getTeamInfo(m_curTeam)->getDict()->getAsciiString(TheKey_teamOwner);
|
||||
|
||||
Dict d;
|
||||
d.setAsciiString(TheKey_teamName, tname);
|
||||
d.setAsciiString(TheKey_teamOwner, oname); // owned by the parent of whatever is selected.
|
||||
d.setBool(TheKey_teamIsSingleton, false);
|
||||
|
||||
m_sides.addTeam(&d);
|
||||
Int i;
|
||||
if (m_sides.findTeamInfo(tname, &i)) {
|
||||
m_curTeam = i;
|
||||
OnEditTemplate();
|
||||
}
|
||||
updateUI(REBUILD_ALL);
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnDeleteteam()
|
||||
{
|
||||
if (m_curTeam < 0)
|
||||
return;
|
||||
|
||||
Bool isDefault = isPlayerDefaultTeamIndex(m_sides, m_curTeam);
|
||||
if (isDefault)
|
||||
{
|
||||
DEBUG_CRASH(("should not be allowed"));
|
||||
return;
|
||||
}
|
||||
|
||||
AsciiString tname = m_sides.getTeamInfo(m_curTeam)->getDict()->getAsciiString(TheKey_teamName);
|
||||
Int count = MapObject::countMapObjectsWithOwner(tname);
|
||||
if (count > 0)
|
||||
{
|
||||
CString msg;
|
||||
msg.Format(IDS_REMOVING_INUSE_TEAM, count);
|
||||
if (::AfxMessageBox(msg, MB_YESNO) == IDNO)
|
||||
return;
|
||||
}
|
||||
|
||||
m_sides.removeTeam(m_curTeam);
|
||||
updateUI(REBUILD_ALL);
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnEditTemplate()
|
||||
{
|
||||
CPropertySheet editDialog;
|
||||
editDialog.Construct("Edit Team Template.");
|
||||
TeamIdentity identity;
|
||||
identity.setTeamDict(m_sides.getTeamInfo(m_curTeam)->getDict());
|
||||
identity.setSidesList(&m_sides);
|
||||
TeamReinforcement reinforcements;
|
||||
reinforcements.setTeamDict(m_sides.getTeamInfo(m_curTeam)->getDict());
|
||||
TeamBehavior behavior;
|
||||
behavior.setTeamDict(m_sides.getTeamInfo(m_curTeam)->getDict());
|
||||
|
||||
TeamGeneric generic;
|
||||
generic.setTeamDict(m_sides.getTeamInfo(m_curTeam)->getDict());
|
||||
|
||||
TeamObjectProperties object(m_sides.getTeamInfo(m_curTeam)->getDict());
|
||||
|
||||
editDialog.AddPage(&identity);
|
||||
editDialog.AddPage(&reinforcements);
|
||||
editDialog.AddPage(&behavior);
|
||||
editDialog.AddPage(&generic);
|
||||
editDialog.AddPage(&object);
|
||||
|
||||
if (IDOK == editDialog.DoModal()) {
|
||||
}
|
||||
updateUI(REBUILD_ALL);
|
||||
}
|
||||
|
||||
void CTeamsDialog::UpdateTeamsList()
|
||||
{
|
||||
CListCtrl *pList = (CListCtrl *)GetDlgItem(IDC_TEAMS_LIST);
|
||||
pList->DeleteAllItems();
|
||||
CListBox *players = (CListBox*)GetDlgItem(IDC_PLAYER_LIST);
|
||||
|
||||
Int which = players->GetCurSel();
|
||||
if (which < 0)
|
||||
return;
|
||||
|
||||
Int numTeams = m_sides.getNumTeams();
|
||||
Bool selected = false;
|
||||
|
||||
for (Int i=0, inserted = 0; i<numTeams; i++)
|
||||
{
|
||||
TeamsInfo *ti = m_sides.getTeamInfo(i);
|
||||
if (ti->getDict()->getAsciiString(TheKey_teamOwner) == playerNameForUI(m_sides, which).str())
|
||||
{
|
||||
Bool exists;
|
||||
AsciiString teamName = teamNameForUI(m_sides, i);
|
||||
AsciiString waypoint = ti->getDict()->getAsciiString(TheKey_teamHome, &exists);
|
||||
AsciiString script = ti->getDict()->getAsciiString(TheKey_teamOnCreateScript, &exists);
|
||||
CString pri;
|
||||
pri.Format(TEXT("%d"), ti->getDict()->getInt(TheKey_teamProductionPriority, &exists));
|
||||
AsciiString trigger = ti->getDict()->getAsciiString(TheKey_teamProductionCondition, &exists);
|
||||
|
||||
pList->InsertItem(LVIF_TEXT, inserted, teamName.str(), 0, 0, 0, 0);
|
||||
pList->SetItemText(inserted, 1, waypoint.str());
|
||||
pList->SetItemText(inserted, 2, pri);
|
||||
pList->SetItemText(inserted, 3, script.str());
|
||||
pList->SetItemText(inserted, 4, trigger.str());
|
||||
|
||||
pList->SetItemData(inserted, i);
|
||||
if (m_curTeam == i) {
|
||||
selected = true;
|
||||
pList->SetItemState(inserted, LVIS_SELECTED, LVIS_SELECTED);
|
||||
pList->EnsureVisible(inserted, false);
|
||||
}
|
||||
inserted++;
|
||||
}
|
||||
}
|
||||
if (!selected) {
|
||||
m_curTeam = -1;
|
||||
if (inserted > 0) {
|
||||
m_curTeam = pList->GetItemData(0);
|
||||
pList->SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
|
||||
pList->EnsureVisible(0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnSelchangePlayerList()
|
||||
{
|
||||
updateUI(REBUILD_ALL);
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnClickTeamsList(NMHDR* pNMHDR, LRESULT* pResult)
|
||||
{
|
||||
CListCtrl* pList = (CListCtrl*) GetDlgItem(IDC_TEAMS_LIST);
|
||||
|
||||
int nItem = pList->GetNextItem(-1, LVNI_SELECTED);
|
||||
if (nItem >= 0)
|
||||
{
|
||||
m_curTeam = pList->GetItemData(nItem);
|
||||
pList->SetItemState(nItem, LVIS_SELECTED, LVIS_SELECTED);
|
||||
}
|
||||
updateUI(REBUILD_ALL);
|
||||
|
||||
*pResult = 0;
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnDblclkTeamsList(NMHDR* pNMHDR, LRESULT* pResult)
|
||||
{
|
||||
CListCtrl* pList = (CListCtrl*) GetDlgItem(IDC_TEAMS_LIST);
|
||||
|
||||
int nItem = pList->GetNextItem(-1, LVNI_SELECTED);
|
||||
if (nItem >= 0)
|
||||
{
|
||||
m_curTeam = pList->GetItemData(nItem);
|
||||
}
|
||||
if (m_curTeam >= 0 && !isPlayerDefaultTeamIndex(m_sides, m_curTeam)) {
|
||||
OnEditTemplate();
|
||||
}
|
||||
|
||||
*pResult = 0;
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnCopyteam()
|
||||
{
|
||||
Dict d = *m_sides.getTeamInfo(m_curTeam)->getDict();
|
||||
AsciiString origName = d.getAsciiString(TheKey_teamName);
|
||||
|
||||
Int num = 1;
|
||||
AsciiString tname;
|
||||
do
|
||||
{
|
||||
tname.format("%s.%2d",origName.str(), num++);
|
||||
}
|
||||
while (m_sides.findTeamInfo(tname));
|
||||
|
||||
d.setAsciiString(TheKey_teamName, tname);
|
||||
m_sides.addTeam(&d);
|
||||
|
||||
updateUI(REBUILD_ALL);
|
||||
}
|
||||
|
||||
void CTeamsDialog::OnSelectTeamMembers()
|
||||
{
|
||||
Int count = 0;
|
||||
Dict d = *m_sides.getTeamInfo(m_curTeam)->getDict();
|
||||
AsciiString teamName = d.getAsciiString(TheKey_teamName);
|
||||
Coord3D pos;
|
||||
MapObject *pObj;
|
||||
for (pObj=MapObject::getFirstMapObject(); pObj; pObj=pObj->getNext()) {
|
||||
pObj->setSelected(false);
|
||||
AsciiString objectsTeam = pObj->getProperties()->getAsciiString(TheKey_originalOwner);
|
||||
if (teamName==objectsTeam) {
|
||||
pObj->setSelected(true);
|
||||
pos = *pObj->getLocation();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
CString info;
|
||||
info.Format(IDS_NUM_SELECTED_ON_TEAM, teamName.str(), count);
|
||||
if (count>0) {
|
||||
CWorldBuilderDoc *pDoc = CWorldBuilderDoc::GetActiveDoc();
|
||||
if (pDoc) {
|
||||
WbView3d *p3View = pDoc->GetActive3DView();
|
||||
if (p3View) {
|
||||
p3View->setCenterInView(pos.x/MAP_XY_FACTOR, pos.y/MAP_XY_FACTOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
::AfxMessageBox(info, MB_OK);
|
||||
}
|
||||
|
||||
/** This function moves a team up the list in the teams list dialog */
|
||||
void CTeamsDialog::OnMoveUpTeam()
|
||||
{
|
||||
// don't move up if already at top of list
|
||||
if (m_curTeam <= 1)
|
||||
return;
|
||||
|
||||
Dict temp, current;
|
||||
int startRemove;
|
||||
int totalTeams = m_sides.getNumTeams();
|
||||
|
||||
// iterates through all modified entries in the teams list
|
||||
for (int i=m_curTeam-1; i<totalTeams; i++)
|
||||
{
|
||||
|
||||
/* saves the one right above the selected team, then deletes it
|
||||
from the list */
|
||||
if (i == (m_curTeam-1)) {
|
||||
temp = *m_sides.getTeamInfo(i)->getDict();
|
||||
m_sides.removeTeam(i);
|
||||
startRemove = i;
|
||||
}
|
||||
|
||||
/* saves the selected item, deletes from the list, then adds it
|
||||
to the bottom of the list -- then adds the saved "temp" item
|
||||
to the bottom of the list */
|
||||
else if (i == m_curTeam) {
|
||||
current = *m_sides.getTeamInfo(startRemove)->getDict();
|
||||
m_sides.removeTeam(startRemove);
|
||||
m_sides.addTeam(¤t);
|
||||
m_sides.addTeam(&temp);
|
||||
}
|
||||
|
||||
/* saves each following item, deletes from the list, and then
|
||||
adds it to the bottom of the list */
|
||||
else if (i > m_curTeam) {
|
||||
current = *m_sides.getTeamInfo(startRemove)->getDict();
|
||||
m_sides.removeTeam(startRemove);
|
||||
m_sides.addTeam(¤t);
|
||||
}
|
||||
}
|
||||
|
||||
// modify cursor
|
||||
m_curTeam--;
|
||||
|
||||
// rebuild user interface to reflect changes
|
||||
/*
|
||||
LVITEM *pItem = NULL;
|
||||
CListCtrl* pList = (CListCtrl*) GetDlgItem(IDC_TEAMS_LIST);
|
||||
Bool result = pList->GetItem(pItem);
|
||||
pList->DeleteItem(m_curTeam);
|
||||
pList->InsertItem(pItem);
|
||||
for (i=0; i<m_sides.getNumTeams(); i++)
|
||||
pList->Update(i);
|
||||
pList->SetItemState(m_curTeam, LVIS_SELECTED, LVIS_SELECTED);
|
||||
*/
|
||||
updateUI(REBUILD_ALL);
|
||||
}
|
||||
|
||||
/// This function moves a team down the list in the teams list dialog
|
||||
void CTeamsDialog::OnMoveDownTeam()
|
||||
{
|
||||
// don't move down if already at bottom of list
|
||||
if (m_curTeam >= m_sides.getNumTeams()-1)
|
||||
return;
|
||||
|
||||
Dict temp, current;
|
||||
int startRemove;
|
||||
int totalTeams = m_sides.getNumTeams();
|
||||
|
||||
// iterates through all modified entries in the teams list
|
||||
for (int i=m_curTeam; i<totalTeams; i++)
|
||||
{
|
||||
|
||||
/* saves the selected team, then deletes it
|
||||
from the list */
|
||||
if (i == m_curTeam) {
|
||||
temp = *m_sides.getTeamInfo(i)->getDict();
|
||||
m_sides.removeTeam(i);
|
||||
startRemove = i;
|
||||
}
|
||||
|
||||
/* saves the one right after the selected item, deletes it from the list,
|
||||
then adds it to the bottom of the list -- then adds the saved "temp" item
|
||||
to the bottom of the list */
|
||||
else if (i == (m_curTeam+1)) {
|
||||
current = *m_sides.getTeamInfo(startRemove)->getDict();
|
||||
m_sides.removeTeam(startRemove);
|
||||
m_sides.addTeam(¤t);
|
||||
m_sides.addTeam(&temp);
|
||||
}
|
||||
|
||||
/* saves each following item, deletes from the list, and then
|
||||
adds it to the bottom of the list */
|
||||
else if (i > m_curTeam+1) {
|
||||
current = *m_sides.getTeamInfo(startRemove)->getDict();
|
||||
m_sides.removeTeam(startRemove);
|
||||
m_sides.addTeam(¤t);
|
||||
}
|
||||
}
|
||||
|
||||
// modify cursor
|
||||
m_curTeam++;
|
||||
|
||||
// rebuild user interface to reflect changes
|
||||
/* LVITEM *pItem = NULL;
|
||||
CListCtrl* pList = (CListCtrl*) GetDlgItem(IDC_TEAMS_LIST);
|
||||
Bool result = pList->GetItem(pItem);
|
||||
pList->DeleteItem(m_curTeam);
|
||||
pList->InsertItem(pItem);
|
||||
for (i=0; i<m_sides.getNumTeams(); i++)
|
||||
pList->Update(i);
|
||||
pList->SetItemState(m_curTeam, LVIS_SELECTED, LVIS_SELECTED);
|
||||
*/
|
||||
updateUI(REBUILD_ALL);
|
||||
}
|
||||
|
||||
void CTeamsDialog::validateTeamOwners( void )
|
||||
{
|
||||
Int numTeams = m_sides.getNumTeams();
|
||||
for (Int i = 0; i < numTeams; ++i) {
|
||||
TeamsInfo *ti = m_sides.getTeamInfo(i);
|
||||
if (!ti) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Bool exists;
|
||||
AsciiString owner = ti->getDict()->getAsciiString(TheKey_teamOwner, &exists);
|
||||
|
||||
if (exists) {
|
||||
if (isValidTeamOwner(owner)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
doCorrectTeamOwnerDialog(ti);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Bool CTeamsDialog::isValidTeamOwner( AsciiString ownerName )
|
||||
{
|
||||
Int numOwners = m_sides.getNumSides();
|
||||
for (Int i = 0; i < numOwners; ++i) {
|
||||
SidesInfo *side = m_sides.getSideInfo(i);
|
||||
if (!side) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Bool exists;
|
||||
AsciiString sideOwnerName = side->getDict()->getAsciiString(TheKey_playerName, &exists);
|
||||
|
||||
if (!exists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ownerName == sideOwnerName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sideOwnerName.isEmpty()) {
|
||||
sideOwnerName = NEUTRAL_NAME_STR;
|
||||
}
|
||||
|
||||
if (ownerName == sideOwnerName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CTeamsDialog::doCorrectTeamOwnerDialog( TeamsInfo *ti )
|
||||
{
|
||||
CFixTeamOwnerDialog fix(ti, &m_sides);
|
||||
if (fix.DoModal() == IDOK) {
|
||||
if (fix.pickedValidTeam()) {
|
||||
ti->getDict()->setAsciiString(TheKey_teamOwner, fix.getSelectedOwner());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1090
GeneralsMD/Code/Tools/WorldBuilder/src/wbview.cpp
Normal file
1090
GeneralsMD/Code/Tools/WorldBuilder/src/wbview.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user