Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA
2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
6072 changed files with 2283311 additions and 0 deletions

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

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

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

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

View 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, &current);
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, &current);
return;
}
if (m_modifyBorderNdx >= 0) {
ICoord2D currentBorder;
pDoc->getBoundary(m_modifyBorderNdx, &currentBorder);
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, &currentBorder);
}
}
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
}
}

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

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

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

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

View 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_ */

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

File diff suppressed because it is too large Load Diff

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

View File

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

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

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

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

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

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

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

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

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

View 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

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

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

File diff suppressed because it is too large Load Diff

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

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

View 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 //////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

View 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"

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

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

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

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

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

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

View 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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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