Index: ps/trunk/binaries/data/mods/public/gui/credits/texts/programming.json
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/credits/texts/programming.json (revision 25814)
+++ ps/trunk/binaries/data/mods/public/gui/credits/texts/programming.json (revision 25815)
@@ -1,293 +1,294 @@
{
"Title": "Programming",
"Content": [
{
"Title": "Programming managers",
"List": [
{ "nick": "Acumen", "name": "Stuart Walpole" },
{ "nick": "Dak Lozar", "name": "Dave Loeser" },
{ "nick": "h20", "name": "Daniel Wilhelm" },
{ "nick": "Janwas", "name": "Jan Wassenberg" },
{ "nick": "Raj", "name": "Raj Sharma" }
]
},
{
"Subtitle": "Special thanks to",
"List": [
{ "nick": "leper", "name": "Georg Kilzer" },
{ "nick": "Ykkrosh", "name": "Philip Taylor" }
]
},
{
"List": [
{ "nick": "01d55" },
{ "nick": "aBothe", "name": "Alexander Bothe" },
{ "nick": "Acumen", "name": "Stuart Walpole" },
{ "nick": "adrian", "name": "Adrian Boguszewszki" },
{ "name": "Adrian Fatol" },
{ "nick": "AI-Amsterdam" },
{ "nick": "Alan", "name": "Alan Kemp" },
{ "nick": "Alex", "name": "Alexander Yakobovich" },
{ "nick": "alpha123", "name": "Peter P. Cannici" },
{ "nick": "Ampaex", "name": "Antonio Vazquez" },
{ "name": "André Puel" },
{ "nick": "andy5995", "name": "Andy Alt" },
{ "nick": "Angen" },
{ "nick": "Arfrever", "name": "Arfrever Frehtes Taifersar Arahesis" },
{ "nick": "ArnH", "name": "Arno Hemelhof" },
{ "nick": "Aurium", "name": "Aurélio Heckert" },
{ "nick": "azayrahmad", "name": "Aziz Rahmad" },
{ "nick": "badmadblacksad", "name": "Martin F" },
{ "nick": "badosu", "name": "Amadeus Folego" },
{ "nick": "bb", "name": "Bouke Jansen" },
{ "nick": "Bellaz89", "name": "Andrea Bellandi" },
{ "nick": "Ben", "name": "Ben Vinegar" },
{ "nick": "Bird" },
{ "nick": "Blue", "name": "Richard Welsh" },
{ "nick": "bmwiedemann" },
{ "nick": "boeseRaupe", "name": "Michael Kluge" },
{ "nick": "bog_dan_ro", "name": "BogDan Vatra" },
{ "nick": "Bonk", "name": "Christopher Ebbert" },
{ "nick": "Boudica" },
{ "nick": "Caius", "name": "Lars Kemmann" },
{ "nick": "Calefaction", "name": "Matt Holmes" },
{ "nick": "Calvinh", "name": "Carl-Johan Höiby" },
{ "nick": "causative", "name": "Bart Parkis" },
{ "name": "Cédric Houbart" },
{ "nick": "Chakakhan", "name": "Kenny Long" },
{ "nick": "Clockwork-Muse", "name": "Stephen A. Imhoff" },
{ "nick": "cpc", "name": "Clément Pit-Claudel" },
{ "nick": "Cracker78", "name": "Chad Heim" },
{ "nick": "Crynux", "name": "Stephen J. Fewer" },
{ "nick": "cwprogger" },
{ "nick": "cygal", "name": "Quentin Pradet" },
{ "nick": "Dak Lozar", "name": "Dave Loeser" },
{ "nick": "dalerank", "name": "Sergey Kushnirenko" },
{ "nick": "dan", "name": "Dan Strandberg" },
{ "nick": "DanCar", "name": "Daniel Cardenas" },
{ "nick": "danger89", "name": "Melroy van den Berg" },
{ "name": "Daniel Trevitz" },
{ "nick": "Dariost", "name": "Dario Ostuni" },
{ "nick": "Dave", "name": "David Protasowski" },
{ "name": "David Marshall" },
{ "nick": "dax", "name": "Dacian Fiordean" },
{ "nick": "deebee", "name": "Deepak Anthony" },
{ "nick": "Deiz" },
{ "nick": "Dietger", "name": "Dietger van Antwerpen" },
{ "nick": "DigitalSeraphim", "name": "Nick Owens" },
{ "nick": "dp304" },
{ "nick": "dpiquet", "name": "Damien Piquet" },
{ "nick": "dumbo" },
{ "nick": "Dunedan", "name": "Daniel Roschka" },
{ "nick": "dvangennip", "name": "Doménique" },
{ "nick": "DynamoFox" },
{ "nick": "Echelon9", "name": "Rhys Kidd" },
{ "nick": "echotangoecho" },
{ "nick": "eihrul", "name": "Lee Salzman" },
{ "nick": "elexis", "name": "Alexander Heinsius" },
{ "nick": "EmjeR", "name": "Matthijs de Rijk" },
{ "nick": "EMontana" },
{ "nick": "ericb" },
{ "nick": "evanssthomas", "name": "Evans Thomas" },
{ "nick": "Evulant", "name": "Alexander S." },
{ "nick": "fabio", "name": "Fabio Pedretti" },
{ "nick": "falsevision", "name": "Mahdi Khodadadifard" },
{ "nick": "fatherbushido", "name": "Nicolas Tisserand" },
{ "nick": "fcxSanya", "name": "Alexander Olkhovskiy" },
{ "nick": "FeXoR", "name": "Florian Finke" },
{ "nick": "Fire Giant", "name": "Malte Schwarzkopf" },
{ "name": "Fork AD" },
{ "nick": "fpre", "name": "Frederick Stallmeyer" },
{ "nick": "Freagarach" },
{ "nick": "freenity", "name": "Anton Galitch" },
{ "nick": "Gallaecio", "name": "Adrián Chaves" },
{ "nick": "gbish (aka Iny)", "name": "Grant Bishop" },
{ "nick": "Gee", "name": "Gustav Larsson" },
{ "nick": "Gentz", "name": "Hal Gentz" },
{ "nick": "gerbilOFdoom" },
{ "nick": "godlikeldh" },
{ "nick": "greybeard", "name": "Joe Cocovich" },
{ "nick": "grillaz" },
{ "nick": "Grugnas", "name": "Giuseppe Tranchese" },
{ "nick": "gudo" },
{ "nick": "Guuts", "name": "Matthew Guttag" },
{ "nick": "h20", "name": "Daniel Wilhelm" },
{ "nick": "Hannibal_Barca", "name": "Clive Juhász S." },
{ "nick": "Haommin" },
{ "nick": "happyconcepts", "name": "Ben Bird" },
{ "nick": "historic_bruno", "name": "Ben Brian" },
{ "nick": "idanwin" },
{ "nick": "Imarok", "name": "J. S." },
{ "nick": "Inari" },
{ "nick": "infyquest", "name": "Vijay Kiran Kamuju" },
{ "nick": "irishninja", "name": "Brian Broll" },
{ "nick": "IronNerd", "name": "Matthew McMullan" },
{ "nick": "Itms", "name": "Nicolas Auvray" },
{ "nick": "Jaison", "name": "Marco tom Suden" },
{ "nick": "jammus", "name": "James Scott" },
{ "nick": "Janwas", "name": "Jan Wassenberg" },
{ "nick": "javiergodas", "name": "Javier Godas Vieitez" },
{ "nick": "JCWasmx86" },
{ "nick": "Jgwman" },
{ "nick": "JonBaer", "name": "Jon Baer" },
{ "nick": "Josh", "name": "Joshua J. Bakita" },
{ "nick": "joskar", "name": "Johnny Oskarsson" },
{ "nick": "jP_wanN", "name": "Jonas Platte" },
{ "nick": "Jubalbarca", "name": "James Baillie" },
{ "nick": "JubJub", "name": "Sebastian Vetter" },
{ "nick": "jurgemaister" },
{ "nick": "kabzerek", "name": "Grzegorz Kabza" },
{ "nick": "Kai", "name": "Kai Chen" },
{ "name": "Kareem Ergawy" },
{ "nick": "kevmo", "name": "Kevin Caffrey" },
{ "nick": "kezz", "name": "Graeme Kerry" },
{ "nick": "kingadami", "name": "Adam Winsor" },
{ "nick": "kingbasil", "name": "Giannis Fafalios" },
{ "nick": "Krinkle", "name": "Timo Tijhof" },
{ "nick": "Kuba386", "name": "Jakub Kośmicki" },
{ "nick": "lafferjm", "name": "Justin Lafferty" },
{ "nick": "Langbart" },
{ "nick": "LeanderH", "name": "Leander Hemelhof" },
{ "nick": "leper", "name": "Georg Kilzer" },
{ "nick": "Link Mauve", "name": "Emmanuel Gil Peyrot" },
{ "nick": "LittleDev" },
{ "nick": "livingaftermidnight", "name": "Will Dull" },
{ "nick": "lonehawk", "name": "Vignesh Krishnan" },
{ "nick": "Louhike" },
{ "nick": "lsdh" },
{ "nick": "Ludovic", "name": "Ludovic Rousseau" },
{ "nick": "luiko", "name": "Luis Carlos Garcia Barajas" },
{ "nick": "m0l0t0ph", "name": "Christoph Gielisch" },
{ "nick": "madmax", "name": "Abhijit Nandy" },
{ "nick": "madpilot", "name": "Guido Falsi" },
{ "nick": "mammadori", "name": "Marco Amadori" },
{ "nick": "marder", "name": "Stefan R. F." },
{ "nick": "markcho" },
{ "nick": "MarkT", "name": "Mark Thompson" },
{ "nick": "Markus" },
{ "nick": "Mate-86", "name": "Mate Kovacs" },
{ "nick": "Matei", "name": "Matei Zaharia" },
+ { "nick": "MatSharrow" },
{ "nick": "MattDoerksen", "name": "Matt Doerksen" },
{ "nick": "mattlott", "name": "Matt Lott" },
{ "nick": "maveric", "name": "Anton Protko" },
{ "nick": "Micnasty", "name": "Travis Gorkin" },
{ "name": "Mikołaj \"Bajter\" Korcz" },
{ "nick": "mimo" },
{ "nick": "mk12", "name": "Mitchell Kember" },
{ "nick": "mmayfield45", "name": "Michael Mayfield" },
{ "nick": "mmoanis", "name": "Mohamed Moanis" },
{ "nick": "Molotov", "name": "Dario Alvarez" },
{ "nick": "mpmoreti", "name": "Marcos Paulo Moreti" },
{ "nick": "mreiland", "name": "Michael Reiland" },
{ "nick": "myconid" },
{ "nick": "n1xc0d3r", "name": "Luis Guerrero" },
{ "nick": "nani", "name": "S. N." },
{ "nick": "nd3c3nt", "name": "Gavin Fowler" },
{ "nick": "nephele" },
{ "nick": "Nescio" },
{ "nick": "niektb", "name": "Niek ten Brinke" },
{ "nick": "nikagra", "name": "Mikita Hradovich" },
{ "nick": "njm" },
{ "nick": "NoMonkey", "name": "John Mena" },
{ "nick": "norsnor" },
{ "nick": "notpete", "name": "Rich Cross" },
{ "nick": "nwtour" },
{ "nick": "odoaker", "name": "Ágoston Sipos" },
{ "nick": "Offensive ePeen", "name": "Jared Ryan Bills" },
{ "nick": "Ols", "name": "Oliver Whiteman" },
{ "nick": "olsner", "name": "Simon Brenner" },
{ "nick": "OptimusShepard", "name": "Pirmin Stanglmeier" },
{ "nick": "otero" },
{ "nick": "Palaxin", "name": "David A. Freitag" },
{ "name": "Paul Withers" },
{ "nick": "paulobezerr", "name": "Paulo George Gomes Bezerra" },
{ "nick": "pcpa", "name": "Paulo Andrade" },
{ "nick": "Pendingchaos" },
{ "nick": "PeteVasi", "name": "Pete Vasiliauskas" },
{ "nick": "pilino1234" },
{ "nick": "PingvinBetyar", "name": "Schronk Tamás" },
{ "nick": "plugwash", "name": "Peter Michael Green" },
{ "nick": "Polakrity" },
{ "nick": "Poya", "name": "Poya Manouchehri" },
{ "nick": "prefect", "name": "Nicolai Hähnle" },
{ "nick": "Prodigal Son" },
{ "nick": "pstumpf", "name": "Pascal Stumpf" },
{ "nick": "pyrolink", "name": "Andrew Decker" },
{ "nick": "quantumstate", "name": "Jonathan Waller" },
{ "nick": "QuickShot", "name": "Walter Krawec" },
{ "nick": "quonter" },
{ "nick": "qwertz" },
{ "nick": "Radagast" },
{ "nick": "Raj", "name": "Raj Sharma" },
{ "nick": "ramtzok1", "name": "Ram" },
{ "nick": "rapidelectron", "name": "Christian Weihsbach" },
{ "nick": "r-a-sattarov", "name": "Ramil Sattarov" },
{ "nick": "RedFox", "name": "Jorma Rebane" },
{ "nick": "RefinedCode" },
{ "nick": "Riemer" },
{ "name": "Rolf Sievers" },
{ "nick": "s0600204", "name": "Matthew Norwood" },
{ "nick": "sacha_vrand", "name": "Sacha Vrand" },
{ "nick": "SafaAlfulaij" },
{ "name": "Samuel Guarnieri" },
{ "nick": "Samulis", "name": "Sam Gossner" },
{ "nick": "Sandarac" },
{ "nick": "sanderd17", "name": "Sander Deryckere" },
{ "nick": "sathyam", "name": "Sathyam Vellal" },
{ "nick": "sbirmi", "name": "Sharad Birmiwal" },
{ "nick": "sbte", "name": "Sven Baars" },
{ "nick": "scroogie", "name": "André Gemünd" },
{ "nick": "scythetwirler", "name": "Casey X." },
{ "nick": "serveurix" },
{ "nick": "Shane", "name": "Shane Grant" },
{ "nick": "shh" },
{ "nick": "Silk", "name": "Josh Godsiff" },
{ "nick": "silure" },
{ "nick": "Simikolon", "name": "Yannick & Simon" },
{ "nick": "smiley", "name": "M. L." },
{ "nick": "Spahbod", "name": "Omid Davoodi" },
{ "nick": "Stan", "name": "Stanislas Dolcini" },
{ "nick": "Stefan" },
{ "nick": "StefanBruens", "name": "Stefan Brüns" },
{ "nick": "stilz", "name": "Sławomir Zborowski" },
{ "nick": "stwf", "name": "Steven Fuchs" },
{ "nick": "svott", "name": "Sven Ott" },
{ "nick": "t4nk004" },
{ "nick": "tau" },
{ "nick": "tbm", "name": "Martin Michlmayr" },
{ "nick": "Teiresias" },
{ "nick": "temple" },
{ "nick": "texane" },
{ "nick": "thamlett", "name": "Timothy Hamlett" },
{ "nick": "thedrunkyak", "name": "Dan Fuhr" },
{ "nick": "Tobbi" },
{ "nick": "Toonijn", "name": "Toon Baeyens" },
{ "nick": "TrinityDeath", "name": "Jethro Lu" },
{ "nick": "triumvir", "name": "Corin Schedler" },
{ "nick": "trompetin17", "name": "Juan Guillermo" },
{ "nick": "tpearson", "name": "Timothy Pearson" },
{ "nick": "user1", "name": "A. C." },
{ "nick": "usey11" },
{ "nick": "vincent_c", "name": "Vincent Cheng" },
{ "nick": "vinhig", "name": "Vincent Higginson" },
{ "nick": "vladislavbelov", "name": "Vladislav Belov" },
{ "nick": "voroskoi" },
{ "nick": "vts", "name": "Jeroen DR" },
{ "nick": "wacko", "name": "Andrew Spiering" },
{ "nick": "WhiteTreePaladin", "name": "Brian Ashley" },
{ "nick": "wraitii", "name": "Lancelot de Ferrière le Vayer" },
{ "nick": "Xentelian", "name": "Mark Strawson" },
{ "nick": "Xienen", "name": "Dayle Flowers" },
{ "nick": "xtizer", "name": "Matt Green" },
{ "nick": "yashi", "name": "Yasushi Shoji" },
{ "nick": "Ykkrosh", "name": "Philip Taylor" },
{ "nick": "Yves" },
{ "nick": "Zeusthor", "name": "Jeffrey Tavares" },
{ "nick": "zoot" },
{ "nick": "zsol", "name": "Zsolt Dollenstein" },
{ "nick": "ztamas", "name": "Tamas Zolnai" },
{ "nick": "Zyi", "name": "Charles De Meulenaer" }
]
}
]
}
Index: ps/trunk/source/tools/atlas/AtlasUI/ActorEditor/ActorEditor.cpp
===================================================================
--- ps/trunk/source/tools/atlas/AtlasUI/ActorEditor/ActorEditor.cpp (revision 25814)
+++ ps/trunk/source/tools/atlas/AtlasUI/ActorEditor/ActorEditor.cpp (revision 25815)
@@ -1,451 +1,451 @@
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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 0 A.D. If not, see .
*/
#include "precompiled.h"
#include
#include "ActorEditor.h"
#include "ActorEditorListCtrl.h"
#include "AtlasObject/AtlasObject.h"
#include "AtlasObject/AtlasObjectText.h"
#include "General/Datafile.h"
#ifdef __WXMAC__
#include
#endif
#include "wx/file.h"
BEGIN_EVENT_TABLE(ActorEditor, AtlasWindow)
EVT_MENU(ID_CreateEntity, ActorEditor::OnCreateEntity)
END_EVENT_TABLE()
ActorEditor::ActorEditor(wxWindow* parent)
: AtlasWindow(parent, _("Actor Editor"), wxSize(1024, 450))
{
SetIcon(wxIcon(_T("ICON_ActorEditor")));
#ifdef __WXMAC__
ProcessSerialNumber PSN;
GetCurrentProcess(&PSN);
TransformProcessType(&PSN,kProcessTransformToForegroundApplication);
#endif
wxMenu* menu = new wxMenu;
menu->Append(ID_CreateEntity, _("Create &entity..."));
AddCustomMenu(menu, _("&Actor"));
//////////////////////////////////////////////////////////////////////////
wxPanel* mainPanel = new wxPanel(this);
m_ActorEditorListCtrl = new ActorEditorListCtrl(mainPanel);
wxBoxSizer* vertSizer = new wxBoxSizer(wxVERTICAL);
mainPanel->SetSizer(vertSizer);
wxBoxSizer* topSizer = new wxBoxSizer(wxHORIZONTAL);
vertSizer->Add(topSizer,
wxSizerFlags().Border(wxLEFT|wxRIGHT, 5));
vertSizer->Add(
m_ActorEditorListCtrl,
wxSizerFlags().Proportion(1).Expand().Border(wxALL, 10));
//////////////////////////////////////////////////////////////////////////
// Properties panel:
wxPanel* propertiesPanel = new wxPanel(mainPanel);
topSizer->Add(propertiesPanel, wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT, 5));
wxSizer* propertiesSizer = new wxStaticBoxSizer(
new wxStaticBox(propertiesPanel, wxID_ANY, _("Actor properties")),
wxHORIZONTAL);
propertiesPanel->SetSizer(propertiesSizer);
m_CastShadows = new wxCheckBox(propertiesPanel, wxID_ANY, _("Cast shadow"));
propertiesSizer->Add(m_CastShadows, wxSizerFlags().Border(wxALL, 5));
m_Float = new wxCheckBox(propertiesPanel, wxID_ANY, _("Float on water"));
propertiesSizer->Add(m_Float, wxSizerFlags().Border(wxALL, 5));
// TODO: Orientation property.
//////////////////////////////////////////////////////////////////////////
// Materials box:
wxPanel* materialsPanel = new wxPanel(mainPanel);
topSizer->Add(materialsPanel, wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT, 5));
wxSizer* materialsSizer = new wxStaticBoxSizer(
new wxStaticBox(materialsPanel, wxID_ANY, _("Material")),
wxHORIZONTAL);
materialsPanel->SetSizer(materialsSizer);
// Get the list of XML materials
wxArrayString materials = Datafile::EnumerateDataFiles(_T("mods/public/art/materials/"), _T("*.xml"));
// Extract the filenames and discard the path
for (size_t i = 0; i < materials.Count(); ++i)
materials[i] = wxFileName(materials[i]).GetFullName();
m_Material = new wxComboBox(materialsPanel, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, materials);
materialsSizer->Add(m_Material, wxSizerFlags().Border(wxALL, 2));
}
void ActorEditor::ThawData(AtObj& in)
{
AtObj actor (*in["actor"]);
m_ActorEditorListCtrl->ThawData(actor);
m_CastShadows->SetValue(actor["castshadow"].defined());
m_Float->SetValue(actor["float"].defined());
m_Material->SetValue((wxString)actor["material"]);
}
AtObj ActorEditor::FreezeData()
{
AtObj actor (m_ActorEditorListCtrl->FreezeData());
if (m_CastShadows->IsChecked())
actor.set("castshadow", "");
if (m_Float->IsChecked())
actor.set("float", "");
if (m_Material->GetValue().length())
- actor.set("material", m_Material->GetValue());
+ actor.set("material", m_Material->GetValue().utf8_str());
AtObj out;
out.set("actor", actor);
return out;
}
static AtObj ConvertToLatestFormat(AtObj in)
{
if (! in.defined())
{
// 'Importing' a new blank file. Fill it in with default values:
AtObj actor;
actor.add("@version", "1");
in.add("actor", actor);
}
// Determine the file format version
long version;
if (in["Object"].defined())
{
// old-style actor format
version = -1;
}
else if (in["qualitylevels"].defined())
{
// New-style, multiple-quality-levels actor.
wxLogError(_("Cannot edit actors with multiple quality levels. If you want to use the actor editor, use the `` format and edit the referenced files."));
return AtObj();
}
else if (in["actor"].defined())
version = (in["actor"]["@version"].defined()) ? (*in["actor"]["@version"]).getLong() : 0;
else
{
wxLogError(_("Failed to determine actor file format version"));
return AtObj();
}
// Do any necessary conversions into the most recent format:
if (version == -1)
{
AtObj actor;
AtIter castShadows = in["Object"]["Properties"]["@castshadows"];
// Handle the global actor properties
if (castShadows.defined() && (*castShadows).getInt() == 1)
actor.set("castshadow", "");
// Things to strip leading strings (for converting filenames, since the
// new format doesn't store the entire path)
#define THING1(out,outname, in,inname, prefix) \
wxASSERT( wxString::FromUTF8(in["Object"][inname]).StartsWith(_T(prefix)) ); \
- out.add(outname, wxString::FromUTF8(in["Object"][inname]).Mid(wxString(_T(prefix)).Length()))
+ out.add(outname, wxString::FromUTF8(in["Object"][inname]).Mid(wxString(_T(prefix)).Length()).utf8_str())
#define THING2(out,outname, in,inname, prefix) \
wxASSERT( wxString::FromUTF8(in[inname]).StartsWith(_T(prefix)) ); \
- out.add(outname, wxString::FromUTF8(in[inname]).Mid(wxString(_T(prefix)).Length()))
+ out.add(outname, wxString::FromUTF8(in[inname]).Mid(wxString(_T(prefix)).Length()).utf8_str())
if (wxString::FromUTF8(in["Object"]["Material"]).Len())
{
THING1(actor,"material", in,"Material", "art/materials/");
}
// Create a single variant to contain all the old data
AtObj var;
var.add("@name", "Base");
var.add("@frequency", "100"); // 100 == default frequency
THING1(var,"mesh", in,"ModelName", "art/meshes/");
// XXX
std::string textureName(in["Object"]["TextureName"]);
if (boost::algorithm::starts_with(textureName, "art/textures/ui/session/portraits/ui_portrait_sheet_civ_"))
var.add("texture", ("temp/" + textureName.substr(strlen("art/textures/ui/session/portraits/"))).c_str());
else
THING1(var,"texture", in,"TextureName", "art/textures/skins/");
AtObj anims;
for (AtIter animit = in["Object"]["Animations"]["Animation"]; animit.defined(); ++animit)
{
if (strlen(animit["@file"]))
{
AtObj anim;
anim.add("@name", animit["@name"]);
anim.add("@speed", animit["@speed"]);
THING2(anim,"@file", animit,"@file", "art/animation/");
anims.add("animation", anim);
}
}
var.add("animations", anims);
AtObj props;
for (AtIter propit = in["Object"]["Props"]["Prop"]; propit.defined(); ++propit)
{
AtObj prop;
prop.add("@attachpoint", propit["@attachpoint"]);
prop.add("@actor", propit["@model"]);
props.add("prop", prop);
}
var.add("props", props);
AtObj grp;
grp.add("variant", var);
actor.add("group", grp);
#undef THING1
#undef THING2
actor.set("@version", "1");
in = AtObj();
in.set("actor", actor);
}
else if (version == 0)
{
AtObj actor;
if (in["actor"]["castshadow"].defined()) actor.add("castshadow", in["actor"]["castshadow"]);
if (in["actor"]["material"].defined()) actor.add("material", in["actor"]["material"]);
for (AtIter grpit = in["actor"]["group"]; grpit.defined(); ++grpit)
{
AtObj grp;
for (AtIter varit = grpit["variant"]; varit.defined(); ++varit)
{
AtObj var;
var.add("@name", varit["name"]);
var.add("@frequency", varit["frequency"]);
var.add("mesh", varit["mesh"]);
var.add("texture", varit["texture"]);
AtObj anims;
for (AtIter animit = varit["animations"]["animation"]; animit.defined(); ++animit)
{
AtObj anim;
anim.add("@name", animit["name"]);
anim.add("@file", animit["file"]);
anim.add("@speed", animit["speed"]);
anims.add("animation", anim);
}
var.add("animations", anims);
AtObj props;
for (AtIter propit = varit["props"]["prop"]; propit.defined(); ++propit)
{
AtObj prop;
prop.add("@attachpoint", propit["attachpoint"]);
prop.add("@actor", propit["model"]);
props.add("prop", prop);
}
var.add("props", props);
grp.add("variant", var);
}
actor.add("group", grp);
}
actor.set("@version", "1");
in.set("actor", actor);
}
else if (version == 1)
{
// current format
}
else
{
// ??? unknown format - this should have been noticed earlier
wxFAIL_MSG(_T("Invalid actor format"));
}
return in;
}
void ActorEditor::ImportData(AtObj& in)
{
AtObj data = ConvertToLatestFormat(in);
if (! data.defined())
return;
// Copy the data into the appropriate UI controls:
AtObj actor (*data["actor"]);
m_ActorEditorListCtrl->ImportData(actor);
m_Actor = actor;
m_CastShadows->SetValue(actor["castshadow"].defined());
m_Float->SetValue(actor["float"].defined());
m_Material->SetValue((wxString)actor["material"]);
}
AtObj ActorEditor::ExportData()
{
// Export the group/variant/etc data
AtObj actor (m_ActorEditorListCtrl->ExportData());
actor.set("@version", "1");
AtObj castShadow = *m_Actor["castshadow"];
if (m_CastShadows->IsChecked() && castShadow.defined())
actor.set("castshadow", castShadow);
else if (m_CastShadows->IsChecked())
actor.set("castshadow", "");
AtObj floatObj = *m_Actor["float"];
if (m_Float->IsChecked() && floatObj)
actor.set("float", floatObj);
else if (m_Float->IsChecked())
actor.set("float", "");
AtObj material = *m_Actor["material"];
actor.set("material", material);
if (m_Material->GetValue().length())
- actor.set("material", m_Material->GetValue());
+ actor.set("material", m_Material->GetValue().utf8_str());
AtObj out;
out.set("actor", actor);
return out;
}
void ActorEditor::OnCreateEntity(wxCommandEvent& WXUNUSED(event))
{
// Create a very basic entity for this actor.
// The output should be an XML file like:
//
//
//
// units/celt_csw_a.xml
//
//
// 'Actor' comes from this actor's filename.
// 'Parent' comes from the filename of a user-selected entity.
// The file will be saved into a user-selected location.
// Get the entity's expected name
wxFileName currentFilename (GetCurrentFilename());
if (! currentFilename.IsOk())
{
wxMessageDialog(this, _("Please save this actor before attempting to create an entity for it."),
_("Gentle reminder"), wxOK | wxICON_INFORMATION).ShowModal();
return;
}
wxString entityName = currentFilename.GetName();
// Work out where the entities are stored
wxFileName entityPath (_T("mods/public/entities/"));
entityPath.MakeAbsolute(Datafile::GetDataDirectory());
// Make sure the user knows what's going on
static bool instructed = false; // only tell them once per session
if (! instructed)
{
instructed = true;
if (wxMessageBox(_("To create an entity, you will first be asked to choose a parent entity, and then asked where save the new entity."),
_("Usage instructions"), wxICON_INFORMATION|wxOK|wxCANCEL, this) != wxOK)
return; // cancelled by user
}
wxString parentEntityFilename
(wxFileSelector(_("Choose a parent entity"), entityPath.GetPath(), _T(""),
_T("xml"), _("XML files (*.xml)|*.xml|All files (*.*)|*.*"), wxFD_OPEN, this));
if (! parentEntityFilename.Length())
return; // cancelled by user
// Get the parent's name
wxString parentName (wxFileName(parentEntityFilename).GetName());
wxString outputEntityFilename
(wxFileSelector(_("Choose a filename to save as"), entityPath.GetPath(), entityName,
_T("xml"), _("XML files (*.xml)|*.xml|All files (*.*)|*.*"), wxFD_SAVE|wxFD_OVERWRITE_PROMPT, this));
if (! outputEntityFilename.Length())
return; // cancelled by user
// Get this actor's filename, relative to actors/
wxFileName actorPath (_T("mods/public/art/actors/"));
actorPath.MakeAbsolute(Datafile::GetDataDirectory());
wxFileName actorFilename (currentFilename);
actorFilename.MakeRelativeTo(actorPath.GetFullPath());
// Create the XML data to be written
// TODO: Native line endings
wxString xml =
_T("\r\n")
_T("\r\n")
_T("\r\n")
_T("\t") + actorFilename.GetFullPath(wxPATH_UNIX) + _T("\r\n")
_T("\r\n");
wxFile file (outputEntityFilename.c_str(), wxFile::write);
if (! file.IsOpened())
{
wxLogError(_("Failed to open file"));
return;
}
if (! file.Write(xml))
{
wxLogError(_("Failed to write XML data to file"));
return;
}
}
wxString ActorEditor::GetDefaultOpenDirectory()
{
wxFileName dir (_T("mods/public/art/actors/"), wxPATH_UNIX);
dir.MakeAbsolute(Datafile::GetDataDirectory());
return dir.GetPath();
}
Index: ps/trunk/source/tools/atlas/AtlasUI/CustomControls/EditableListCtrl/EditableListCtrl.cpp
===================================================================
--- ps/trunk/source/tools/atlas/AtlasUI/CustomControls/EditableListCtrl/EditableListCtrl.cpp (revision 25814)
+++ ps/trunk/source/tools/atlas/AtlasUI/CustomControls/EditableListCtrl/EditableListCtrl.cpp (revision 25815)
@@ -1,319 +1,319 @@
-/* Copyright (C) 2019 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "EditableListCtrl.h"
#include "EditableListCtrlCommands.h"
#include "FieldEditCtrl.h"
#include "General/AtlasWindowCommandProc.h"
#include "AtlasObject/AtlasObject.h"
#include "AtlasObject/AtlasObjectText.h"
#include "General/AtlasClipboard.h"
const int BlanksAtEnd = 2;
EditableListCtrl::EditableListCtrl(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
: wxListCtrl(parent, id, pos, size, style | wxLC_VIRTUAL, validator, name)
{
m_ListItemAttr[0].SetBackgroundColour(wxColor(0xff, 0xff, 0xff));
m_ListItemAttr[1].SetBackgroundColour(wxColor(0xee, 0xee, 0xee));
wxASSERT_MSG(style & wxLC_REPORT, _T("EditableListCtrl must be LC_REPORT"));
UpdateDisplay();
}
EditableListCtrl::~EditableListCtrl()
{
size_t count = m_ColumnTypes.size();
for (size_t n = 0; n < count; ++n)
delete (FieldEditCtrl*)m_ColumnTypes[n].ctrl;
m_ColumnTypes.clear();
}
void EditableListCtrl::AddColumnType(const wxString& title, int width, const char* objectkey, FieldEditCtrl* ctrl)
{
int n = GetColumnCount();
wxASSERT(m_ColumnTypes.size() == (size_t) n); // check internal consistency
InsertColumn(n, title, wxLIST_FORMAT_LEFT, width);
m_ColumnTypes.push_back(ColumnData(objectkey, ctrl));
}
void EditableListCtrl::OnMouseEvent(wxMouseEvent& event)
{
// Double-clicking/right-clicking on a cell lets the user edit it.
// The editing method depends on what column the cell is in.
if (event.LeftDClick() || event.RightDown())
{
// Work out what cell was clicked on:
wxPoint pt = event.GetPosition();
int col = GetColumnAtPosition(pt);
if (col < 0 || col >= static_cast(m_ColumnTypes.size()))
return;
int flags;
long row = HitTest(pt, flags);
if (row != wxNOT_FOUND && (flags & wxLIST_HITTEST_ONITEM))
{
// Calculate the exact positioning of the clicked cell
wxRect rect;
GetCellRect(row, col, rect);
// Execute the appropriate FieldEditCtrl
FieldEditCtrl* editor = (FieldEditCtrl*)m_ColumnTypes[col].ctrl;
editor->StartEdit(this, rect, row, col);
}
}
}
void EditableListCtrl::OnKeyDown(wxKeyEvent& event)
{
// TODO: Don't use magic key-code numbers
// Check for Copy
if ((event.GetKeyCode() == 3) || // ctrl+c
(event.GetKeyCode() == WXK_INSERT && event.ControlDown())) // ctrl+insert
{
AtObj row;
long selection = GetSelection();
if (selection >= 0 && selection < (long)m_ListData.size())
row = m_ListData[selection];
AtlasClipboard::SetClipboard(row);
}
// Check for Paste
else
if ((event.GetKeyCode() == 22) || // ctrl+v
(event.GetKeyCode() == WXK_INSERT && event.ShiftDown())) // shift+insert
{
AtObj row;
if (AtlasClipboard::GetClipboard(row))
{
long selection = GetSelection();
AtlasWindowCommandProc* commandProc = AtlasWindowCommandProc::GetFromParentFrame(this);
commandProc->Submit(new PasteCommand(this, selection, row));
}
}
else
event.Skip();
}
int EditableListCtrl::GetColumnAtPosition(wxPoint& pos)
{
// Find the column which pos is in.
// Get the origin of the table, in case it's scrolled horizontally
wxRect rect;
GetItemRect(0, rect);
int x = rect.GetX();
// Loop through each column
int numCols = GetColumnCount();
for (int i = 0; i < numCols; ++i)
{
// Calculate the position of this column's right-hand edge
x += GetColumnWidth(i);
// Test if pos was within this column (and assume it wasn't in an earlier one)
if (pos.x <= x)
return i;
}
// Point is outside the table's right edge
return -1;
}
void EditableListCtrl::GetCellRect(long row, int col, wxRect& rect)
{
wxASSERT(col >= 0 && col < GetColumnCount());
wxASSERT(row >= 0 && row < GetItemCount());
GetItemRect(row, rect);
for (int i = 0; i < col; ++i)
rect.x += GetColumnWidth(i);
rect.width = GetColumnWidth(col);
}
bool EditableListCtrl::IsRowBlank(int n)
{
return ! m_ListData[n].hasContent();
}
void EditableListCtrl::TrimBlankEnds()
{
while (m_ListData.size() && !m_ListData.back().defined())
m_ListData.pop_back();
}
void EditableListCtrl::SetSelection(long item)
{
SetItemState(item, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
}
long EditableListCtrl::GetSelection()
{
for (long item = 0; item < GetItemCount(); ++item)
if (GetItemState(item, wxLIST_STATE_SELECTED))
return item;
return 0;
}
void EditableListCtrl::MakeSizeAtLeast(int n)
{
if ((int)m_ListData.size() < n)
m_ListData.resize(n);
}
void EditableListCtrl::AddRow(AtObj& obj)
{
m_ListData.push_back(obj);
}
void EditableListCtrl::AddRow(AtIter& iter)
{
AtObj obj = *iter;
AddRow(obj);
}
void EditableListCtrl::UpdateDisplay()
{
TrimBlankEnds();
SetItemCount((int)m_ListData.size() + BlanksAtEnd);
Refresh();
}
void EditableListCtrl::CloneListData(std::vector& out)
{
out = m_ListData;
}
void EditableListCtrl::SetListData(std::vector& in)
{
m_ListData = in;
}
void EditableListCtrl::DeleteData()
{
m_ListData.clear();
}
wxString EditableListCtrl::GetCellString(long item, long column) const
{
wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), _T(""));
if (item >= (int)m_ListData.size())
return _T("");
AtObj cell = *m_ListData[item][m_ColumnTypes[column].key];
return AtlasObject::ConvertToString(cell).c_str();
}
AtObj EditableListCtrl::GetCellObject(long item, long column) const
{
wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), AtObj());
if (item >= (int)m_ListData.size())
return AtObj();
return *m_ListData[item][m_ColumnTypes[column].key];
}
void EditableListCtrl::SetCellString(long item, long column, wxString& str)
{
wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), );
MakeSizeAtLeast(item+1);
- m_ListData[item].set(m_ColumnTypes[column].key, str);
+ m_ListData[item].set(m_ColumnTypes[column].key, str.utf8_str());
}
void EditableListCtrl::SetCellObject(long item, long column, AtObj& obj)
{
wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), );
MakeSizeAtLeast(item+1);
m_ListData[item].set(m_ColumnTypes[column].key, obj);
}
wxString EditableListCtrl::OnGetItemText(long item, long column) const
{
return GetCellString(item, column);
}
wxListItemAttr* EditableListCtrl::OnGetItemAttr(long item) const
{
// Make the last two rows white
if (item >= (long)m_ListData.size())
return const_cast(&m_ListItemAttr[0]);
// Make the background colors of rows alternate
else
return const_cast(&m_ListItemAttr[item%2]);
}
void EditableListCtrl::ImportData(AtObj& in)
{
return DoImport(in);
}
AtObj EditableListCtrl::ExportData()
{
return DoExport();
}
void EditableListCtrl::ThawData(AtObj& in)
{
m_ListData.clear();
for (AtIter it = in["item"]; it.defined(); ++it)
m_ListData.push_back(*it);
UpdateDisplay();
}
AtObj EditableListCtrl::FreezeData()
{
AtObj out;
for (std::vector::iterator it = m_ListData.begin(); it != m_ListData.end(); ++it)
out.add("item", *it);
return out;
}
BEGIN_EVENT_TABLE(EditableListCtrl, wxListCtrl)
EVT_LEFT_DCLICK(EditableListCtrl::OnMouseEvent)
EVT_RIGHT_DOWN(EditableListCtrl::OnMouseEvent)
EVT_CHAR(EditableListCtrl::OnKeyDown)
END_EVENT_TABLE()
Index: ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp
===================================================================
--- ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp (revision 25814)
+++ ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp (revision 25815)
@@ -1,747 +1,747 @@
-/* Copyright (C) 2020 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "Map.h"
#include "AtlasObject/AtlasObject.h"
#include "AtlasObject/JSONSpiritInclude.h"
#include "GameInterface/Messages.h"
#include "MapResizeDialog/MapResizeDialog.h"
#include "ScenarioEditor/ScenarioEditor.h"
#include "ScenarioEditor/Tools/Common/Tools.h"
#include
#include
#include
#define CREATE_CHECKBOX(window, parentSizer, name, description, ID) \
parentSizer->Add(new wxStaticText(window, wxID_ANY, _(name)), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); \
parentSizer->Add(Tooltipped(new wxCheckBox(window, ID, wxEmptyString), _(description)));
enum
{
ID_MapName,
ID_MapDescription,
ID_MapReveal,
ID_MapType,
ID_MapPreview,
ID_MapTeams,
ID_MapKW_Demo,
ID_MapKW_Naval,
ID_MapKW_New,
ID_MapKW_Trigger,
ID_RandomScript,
ID_RandomSize,
ID_RandomNomad,
ID_RandomSeed,
ID_RandomReseed,
ID_RandomGenerate,
ID_ResizeMap,
ID_SimPlay,
ID_SimFast,
ID_SimSlow,
ID_SimPause,
ID_SimReset,
ID_OpenPlayerPanel
};
enum
{
SimInactive,
SimPlaying,
SimPlayingFast,
SimPlayingSlow,
SimPaused
};
bool IsPlaying(int s) { return (s == SimPlaying || s == SimPlayingFast || s == SimPlayingSlow); }
// TODO: Some of these helper things should be moved out of this file
// and into shared locations
// Helper function for adding tooltips
static wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
{
window->SetToolTip(tip);
return window;
}
// Helper class for storing AtObjs
class AtObjClientData : public wxClientData
{
public:
AtObjClientData(const AtObj& obj) : obj(obj) {}
virtual ~AtObjClientData() {}
AtObj GetValue() { return obj; }
private:
AtObj obj;
};
class MapSettingsControl : public wxPanel
{
public:
MapSettingsControl(wxWindow* parent, ScenarioEditor& scenarioEditor);
void CreateWidgets();
void ReadFromEngine();
void SetMapSettings(const AtObj& obj);
AtObj UpdateSettingsObject();
private:
void SendToEngine();
void OnVictoryConditionChanged(long controlId);
void OnEdit(wxCommandEvent& evt)
{
long id = static_cast(evt.GetId());
if (std::any_of(m_VictoryConditions.begin(), m_VictoryConditions.end(), [id](const std::pair& vc) {
return vc.first == id;
}))
OnVictoryConditionChanged(id);
SendToEngine();
}
std::map m_VictoryConditions;
std::set m_MapSettingsKeywords;
std::set m_MapSettingsVictoryConditions;
std::vector m_PlayerCivChoices;
Observable& m_MapSettings;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(MapSettingsControl, wxPanel)
EVT_TEXT(ID_MapName, MapSettingsControl::OnEdit)
EVT_TEXT(ID_MapDescription, MapSettingsControl::OnEdit)
EVT_TEXT(ID_MapPreview, MapSettingsControl::OnEdit)
EVT_CHECKBOX(wxID_ANY, MapSettingsControl::OnEdit)
EVT_CHOICE(wxID_ANY, MapSettingsControl::OnEdit)
END_EVENT_TABLE();
MapSettingsControl::MapSettingsControl(wxWindow* parent, ScenarioEditor& scenarioEditor)
: wxPanel(parent, wxID_ANY), m_MapSettings(scenarioEditor.GetMapSettings())
{
wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Map settings"));
SetSizer(sizer);
}
void MapSettingsControl::CreateWidgets()
{
wxSizer* sizer = GetSizer();
/////////////////////////////////////////////////////////////////////////
// Map settings
wxBoxSizer* nameSizer = new wxBoxSizer(wxHORIZONTAL);
nameSizer->Add(new wxStaticText(this, wxID_ANY, _("Name")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
nameSizer->Add(8, 0);
nameSizer->Add(Tooltipped(new wxTextCtrl(this, ID_MapName),
_("Displayed name of the map")), wxSizerFlags().Proportion(1));
sizer->Add(nameSizer, wxSizerFlags().Expand());
sizer->Add(0, 2);
sizer->Add(new wxStaticText(this, wxID_ANY, _("Description")));
sizer->Add(Tooltipped(new wxTextCtrl(this, ID_MapDescription, wxEmptyString, wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE),
_("Short description used on the map selection screen")), wxSizerFlags().Expand());
sizer->AddSpacer(5);
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5);
gridSizer->AddGrowableCol(1);
// TODO: have preview selector tool?
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Preview")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(new wxTextCtrl(this, ID_MapPreview, wxEmptyString),
_("Texture used for map preview")), wxSizerFlags().Expand());
CREATE_CHECKBOX(this, gridSizer, "Reveal map", "If checked, players won't need to explore", ID_MapReveal);
CREATE_CHECKBOX(this, gridSizer, "Lock teams", "If checked, teams will be locked", ID_MapTeams);
sizer->Add(gridSizer, wxSizerFlags().Expand());
sizer->AddSpacer(5);
wxStaticBoxSizer* victoryConditionSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Victory Conditions"));
wxFlexGridSizer* vcGridSizer = new wxFlexGridSizer(2, 0, 5);
vcGridSizer->AddGrowableCol(1);
AtlasMessage::qGetVictoryConditionData qryVictoryCondition;
qryVictoryCondition.Post();
std::vector victoryConditionData = *qryVictoryCondition.data;
for (const std::string& victoryConditionJson : victoryConditionData)
{
AtObj victoryCondition = AtlasObject::LoadFromJSON(victoryConditionJson);
long index = wxWindow::NewControlId();
wxString title = wxString::FromUTF8(victoryCondition["Data"]["Title"]);
- std::string escapedTitle = wxString::FromUTF8(title).Lower().ToStdString();
+ std::string escapedTitle = title.Lower().ToStdString();
std::replace(escapedTitle.begin(), escapedTitle.end(), ' ', '_');
AtObj updateCondition = *(victoryCondition["Data"]["Title"]);
updateCondition.setString(escapedTitle.c_str());
m_VictoryConditions.insert(std::pair(index, victoryCondition));
CREATE_CHECKBOX(this, vcGridSizer, title, "Select " + title + " victory condition.", index);
}
victoryConditionSizer->Add(vcGridSizer);
sizer->Add(victoryConditionSizer, wxSizerFlags().Expand());
sizer->AddSpacer(5);
wxStaticBoxSizer* keywordsSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Keywords"));
wxFlexGridSizer* kwGridSizer = new wxFlexGridSizer(4, 5, 15);
CREATE_CHECKBOX(this, kwGridSizer, "Demo", "If checked, map will only be visible using filters in game setup", ID_MapKW_Demo);
CREATE_CHECKBOX(this, kwGridSizer, "Naval", "If checked, map will only be visible using filters in game setup", ID_MapKW_Naval);
CREATE_CHECKBOX(this, kwGridSizer, "New", "If checked, the map will appear in the list of new maps", ID_MapKW_New);
CREATE_CHECKBOX(this, kwGridSizer, "Trigger", "If checked, the map will appear in the list of maps with trigger scripts", ID_MapKW_Trigger);
keywordsSizer->Add(kwGridSizer);
sizer->Add(keywordsSizer, wxSizerFlags().Expand());
}
void MapSettingsControl::ReadFromEngine()
{
AtlasMessage::qGetMapSettings qry;
qry.Post();
if (!(*qry.settings).empty())
{
// Prevent error if there's no map settings to parse
m_MapSettings = AtlasObject::LoadFromJSON(*qry.settings);
}
// map name
wxDynamicCast(FindWindow(ID_MapName), wxTextCtrl)->ChangeValue(wxString::FromUTF8(m_MapSettings["Name"]));
// map description
wxDynamicCast(FindWindow(ID_MapDescription), wxTextCtrl)->ChangeValue(wxString::FromUTF8(m_MapSettings["Description"]));
// map preview
wxDynamicCast(FindWindow(ID_MapPreview), wxTextCtrl)->ChangeValue(wxString::FromUTF8(m_MapSettings["Preview"]));
// reveal map
wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->SetValue(wxString::FromUTF8(m_MapSettings["RevealMap"]) == "true");
// victory conditions
m_MapSettingsVictoryConditions.clear();
for (AtIter victoryCondition = m_MapSettings["VictoryConditions"]["item"]; victoryCondition.defined(); ++victoryCondition)
m_MapSettingsVictoryConditions.insert(std::string(victoryCondition));
// Clear Checkboxes before loading data. We don't update victory condition just yet because it might reenable some of the checkboxes.
for (const std::pair& vc : m_VictoryConditions)
{
wxCheckBox* checkBox = wxDynamicCast(FindWindow(vc.first), wxCheckBox);
if (!checkBox)
continue;
checkBox->SetValue(false);
checkBox->Enable(true);
}
for (const std::pair& vc : m_VictoryConditions)
{
std::string escapedTitle = wxString::FromUTF8(vc.second["Data"]["Title"]).Lower().ToStdString();
std::replace(escapedTitle.begin(), escapedTitle.end(), ' ', '_');
if (m_MapSettingsVictoryConditions.find(escapedTitle) == m_MapSettingsVictoryConditions.end())
continue;
wxCheckBox* checkBox = wxDynamicCast(FindWindow(vc.first), wxCheckBox);
if (!checkBox)
continue;
checkBox->SetValue(true);
OnVictoryConditionChanged(vc.first);
}
// lock teams
wxDynamicCast(FindWindow(ID_MapTeams), wxCheckBox)->SetValue(wxString::FromUTF8(m_MapSettings["LockTeams"]) == "true");
// keywords
{
m_MapSettingsKeywords.clear();
for (AtIter keyword = m_MapSettings["Keywords"]["item"]; keyword.defined(); ++keyword)
m_MapSettingsKeywords.insert(std::string(keyword));
wxWindow* window;
#define INIT_CHECKBOX(ID, mapSettings, value) \
window = FindWindow(ID); \
if (window != nullptr) \
wxDynamicCast(window, wxCheckBox)->SetValue(mapSettings.count(value) != 0);
INIT_CHECKBOX(ID_MapKW_Demo, m_MapSettingsKeywords, "demo");
INIT_CHECKBOX(ID_MapKW_Naval, m_MapSettingsKeywords, "naval");
INIT_CHECKBOX(ID_MapKW_New, m_MapSettingsKeywords, "new");
INIT_CHECKBOX(ID_MapKW_Trigger, m_MapSettingsKeywords, "trigger");
#undef INIT_CHECKBOX
}
}
void MapSettingsControl::SetMapSettings(const AtObj& obj)
{
m_MapSettings = obj;
m_MapSettings.NotifyObservers();
SendToEngine();
}
void MapSettingsControl::OnVictoryConditionChanged(long controlId)
{
AtObj victoryCondition;
wxCheckBox* modifiedCheckbox = wxDynamicCast(FindWindow(controlId), wxCheckBox);
for (const std::pair& vc : m_VictoryConditions)
{
if (vc.first != controlId)
continue;
victoryCondition = vc.second;
break;
}
if (modifiedCheckbox->GetValue())
{
for (AtIter victoryConditionPair = victoryCondition["Data"]["ChangeOnChecked"]; victoryConditionPair.defined(); ++victoryConditionPair)
{
for (const std::pair& vc : m_VictoryConditions)
{
std::string escapedTitle = wxString::FromUTF8(vc.second["Data"]["Title"]).Lower().ToStdString();
std::replace(escapedTitle.begin(), escapedTitle.end(), ' ', '_');
if (victoryConditionPair[escapedTitle.c_str()].defined())
{
wxCheckBox* victoryConditionCheckBox = wxDynamicCast(FindWindow(vc.first), wxCheckBox);
victoryConditionCheckBox->SetValue(wxString::FromUTF8(victoryConditionPair[escapedTitle.c_str()]).Lower().ToStdString() == "true");
}
}
}
}
for (const std::pair& vc : m_VictoryConditions)
{
if (vc.first == controlId)
continue;
wxCheckBox* otherCheckbox = wxDynamicCast(FindWindow(vc.first), wxCheckBox);
otherCheckbox->Enable(true);
for (const std::pair& vc2 : m_VictoryConditions)
{
for (AtIter victoryConditionTitle = vc2.second["Data"]["DisabledWhenChecked"]; victoryConditionTitle.defined(); ++victoryConditionTitle)
{
std::string escapedTitle = wxString::FromUTF8(vc.second["Data"]["Title"]).Lower().ToStdString();
std::replace(escapedTitle.begin(), escapedTitle.end(), ' ', '_');
if (escapedTitle == wxString::FromUTF8(victoryConditionTitle["item"]).ToStdString() && wxDynamicCast(FindWindow(vc2.first), wxCheckBox)->GetValue())
{
otherCheckbox->Enable(false);
otherCheckbox->SetValue(false);
break;
}
}
}
}
}
AtObj MapSettingsControl::UpdateSettingsObject()
{
// map name
m_MapSettings.set("Name", wxDynamicCast(FindWindow(ID_MapName), wxTextCtrl)->GetValue().utf8_str());
// map description
m_MapSettings.set("Description", wxDynamicCast(FindWindow(ID_MapDescription), wxTextCtrl)->GetValue().utf8_str());
// map preview
m_MapSettings.set("Preview", wxDynamicCast(FindWindow(ID_MapPreview), wxTextCtrl)->GetValue().utf8_str());
// reveal map
m_MapSettings.setBool("RevealMap", wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->GetValue());
// victory conditions
#define INSERT_VICTORY_CONDITION_CHECKBOX(name, ID) \
if (wxDynamicCast(FindWindow(ID), wxCheckBox)->GetValue()) \
m_MapSettingsVictoryConditions.insert(name); \
else \
m_MapSettingsVictoryConditions.erase(name);
for (const std::pair& vc : m_VictoryConditions)
{
std::string escapedTitle = wxString::FromUTF8(vc.second["Data"]["Title"]).Lower().ToStdString();
std::replace(escapedTitle.begin(), escapedTitle.end(), ' ', '_');
INSERT_VICTORY_CONDITION_CHECKBOX(escapedTitle, vc.first)
}
#undef INSERT_VICTORY_CONDITION_CHECKBOX
AtObj victoryConditions;
victoryConditions.set("@array", "");
for (const std::string& victoryCondition : m_MapSettingsVictoryConditions)
victoryConditions.add("item", victoryCondition.c_str());
m_MapSettings.set("VictoryConditions", victoryConditions);
// keywords
{
#define INSERT_KEYWORDS_CHECKBOX(name, ID) \
if (wxDynamicCast(FindWindow(ID), wxCheckBox)->GetValue()) \
m_MapSettingsKeywords.insert(name); \
else \
m_MapSettingsKeywords.erase(name);
INSERT_KEYWORDS_CHECKBOX("demo", ID_MapKW_Demo);
INSERT_KEYWORDS_CHECKBOX("naval", ID_MapKW_Naval);
INSERT_KEYWORDS_CHECKBOX("new", ID_MapKW_New);
INSERT_KEYWORDS_CHECKBOX("trigger", ID_MapKW_Trigger);
#undef INSERT_KEYWORDS_CHECKBOX
AtObj keywords;
keywords.set("@array", "");
for (const std::string& keyword : m_MapSettingsKeywords)
keywords.add("item", keyword.c_str());
m_MapSettings.set("Keywords", keywords);
}
// teams locked
m_MapSettings.setBool("LockTeams", wxDynamicCast(FindWindow(ID_MapTeams), wxCheckBox)->GetValue());
// default AI RNG seed
m_MapSettings.setInt("AISeed", 0);
return m_MapSettings;
}
void MapSettingsControl::SendToEngine()
{
UpdateSettingsObject();
std::string json = AtlasObject::SaveToJSON(m_MapSettings);
// TODO: would be nice if we supported undo for settings changes
POST_COMMAND(SetMapSettings, (json));
}
MapSidebar::MapSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer), m_SimState(SimInactive)
{
wxSizer* scrollSizer = new wxBoxSizer(wxVERTICAL);
wxScrolledWindow* scrolledWindow = new wxScrolledWindow(this);
scrolledWindow->SetScrollRate(10, 10);
scrolledWindow->SetSizer(scrollSizer);
m_MainSizer->Add(scrolledWindow, wxSizerFlags().Expand().Proportion(1));
m_MapSettingsCtrl = new MapSettingsControl(scrolledWindow, m_ScenarioEditor);
scrollSizer->Add(m_MapSettingsCtrl, wxSizerFlags().Expand());
{
/////////////////////////////////////////////////////////////////////////
// Random map settings
wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _("Random map"));
scrollSizer->Add(sizer, wxSizerFlags().Expand());
sizer->Add(new wxChoice(scrolledWindow, ID_RandomScript), wxSizerFlags().Expand());
sizer->AddSpacer(5);
sizer->Add(new wxButton(scrolledWindow, ID_OpenPlayerPanel, _T("Change players")), wxSizerFlags().Expand());
sizer->AddSpacer(5);
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5);
gridSizer->AddGrowableCol(1);
wxChoice* sizeChoice = new wxChoice(scrolledWindow, ID_RandomSize);
gridSizer->Add(new wxStaticText(scrolledWindow, wxID_ANY, _("Map size")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(sizeChoice, wxSizerFlags().Expand());
CREATE_CHECKBOX(scrolledWindow, gridSizer, "Nomad", "Place only some units instead of starting bases.", ID_RandomNomad);
gridSizer->Add(new wxStaticText(scrolledWindow, wxID_ANY, _("Random seed")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
wxBoxSizer* seedSizer = new wxBoxSizer(wxHORIZONTAL);
seedSizer->Add(Tooltipped(new wxTextCtrl(scrolledWindow, ID_RandomSeed, _T("0"), wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_NUMERIC)),
_("Seed value for random map")), wxSizerFlags(1).Expand());
seedSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_RandomReseed, _("R"), wxDefaultPosition, wxSize(40, -1)),
_("New random seed")));
gridSizer->Add(seedSizer, wxSizerFlags().Expand());
sizer->Add(gridSizer, wxSizerFlags().Expand());
sizer->AddSpacer(5);
sizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_RandomGenerate, _("Generate map")),
_("Run selected random map script")), wxSizerFlags().Expand());
}
{
/////////////////////////////////////////////////////////////////////////
// Misc tools
wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _("Misc tools"));
sizer->Add(new wxButton(scrolledWindow, ID_ResizeMap, _("Resize/Recenter map")), wxSizerFlags().Expand());
scrollSizer->Add(sizer, wxSizerFlags().Expand().Border(wxTOP, 10));
}
{
/////////////////////////////////////////////////////////////////////////
// Simulation buttons
wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _("Simulation test"));
scrollSizer->Add(sizer, wxSizerFlags().Expand().Border(wxTOP, 8));
wxGridSizer* gridSizer = new wxGridSizer(5);
gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimPlay, _("Play"), wxDefaultPosition, wxSize(48, -1)),
_("Run the simulation at normal speed")), wxSizerFlags().Expand());
gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimFast, _("Fast"), wxDefaultPosition, wxSize(48, -1)),
_("Run the simulation at 8x speed")), wxSizerFlags().Expand());
gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimSlow, _("Slow"), wxDefaultPosition, wxSize(48, -1)),
_("Run the simulation at 1/8x speed")), wxSizerFlags().Expand());
gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimPause, _("Pause"), wxDefaultPosition, wxSize(48, -1)),
_("Pause the simulation")), wxSizerFlags().Expand());
gridSizer->Add(Tooltipped(new wxButton(scrolledWindow, ID_SimReset, _("Reset"), wxDefaultPosition, wxSize(48, -1)),
_("Reset the editor to initial state")), wxSizerFlags().Expand());
sizer->Add(gridSizer, wxSizerFlags().Expand());
UpdateSimButtons();
}
}
void MapSidebar::OnCollapse(wxCollapsiblePaneEvent& WXUNUSED(evt))
{
Freeze();
// Toggling the collapsing doesn't seem to update the sidebar layout
// automatically, so do it explicitly here
Layout();
Refresh(); // fixes repaint glitch on Windows
Thaw();
}
void MapSidebar::OnFirstDisplay()
{
// We do this here becase messages are used which requires simulation to be init'd
m_MapSettingsCtrl->CreateWidgets();
m_MapSettingsCtrl->ReadFromEngine();
// Load the map sizes list
AtlasMessage::qGetMapSizes qrySizes;
qrySizes.Post();
AtObj sizes = AtlasObject::LoadFromJSON(*qrySizes.sizes);
wxChoice* sizeChoice = wxDynamicCast(FindWindow(ID_RandomSize), wxChoice);
for (AtIter s = sizes["Data"]["item"]; s.defined(); ++s)
sizeChoice->Append(wxString::FromUTF8(s["Name"]), reinterpret_cast((*s["Tiles"]).getLong()));
sizeChoice->SetSelection(0);
// Load the RMS script list
AtlasMessage::qGetRMSData qry;
qry.Post();
std::vector scripts = *qry.data;
wxChoice* scriptChoice = wxDynamicCast(FindWindow(ID_RandomScript), wxChoice);
scriptChoice->Clear();
for (size_t i = 0; i < scripts.size(); ++i)
{
AtObj data = AtlasObject::LoadFromJSON(scripts[i]);
wxString name = wxString::FromUTF8(data["settings"]["Name"]);
if (!name.IsEmpty())
scriptChoice->Append(name, new AtObjClientData(*data["settings"]));
}
scriptChoice->SetSelection(0);
Layout();
}
void MapSidebar::OnMapReload()
{
m_MapSettingsCtrl->ReadFromEngine();
// Reset sim test buttons
POST_MESSAGE(SimPlay, (0.f, false));
POST_MESSAGE(SimStopMusic, ());
POST_MESSAGE(GuiSwitchPage, (L"page_atlas.xml"));
m_SimState = SimInactive;
UpdateSimButtons();
}
void MapSidebar::UpdateSimButtons()
{
wxButton* button;
button = wxDynamicCast(FindWindow(ID_SimPlay), wxButton);
wxCHECK(button, );
button->Enable(m_SimState != SimPlaying);
button = wxDynamicCast(FindWindow(ID_SimFast), wxButton);
wxCHECK(button, );
button->Enable(m_SimState != SimPlayingFast);
button = wxDynamicCast(FindWindow(ID_SimSlow), wxButton);
wxCHECK(button, );
button->Enable(m_SimState != SimPlayingSlow);
button = wxDynamicCast(FindWindow(ID_SimPause), wxButton);
wxCHECK(button, );
button->Enable(IsPlaying(m_SimState));
button = wxDynamicCast(FindWindow(ID_SimReset), wxButton);
wxCHECK(button, );
button->Enable(m_SimState != SimInactive);
}
void MapSidebar::OnSimPlay(wxCommandEvent& event)
{
float speed = 1.f;
int newState = SimPlaying;
if (event.GetId() == ID_SimFast)
{
speed = 8.f;
newState = SimPlayingFast;
}
else if (event.GetId() == ID_SimSlow)
{
speed = 0.125f;
newState = SimPlayingSlow;
}
if (m_SimState == SimInactive)
{
// Force update of player settings
POST_MESSAGE(LoadPlayerSettings, (false));
POST_MESSAGE(SimStateSave, (L"default"));
POST_MESSAGE(GuiSwitchPage, (L"page_session.xml"));
POST_MESSAGE(SimPlay, (speed, true));
m_SimState = newState;
}
else // paused or already playing at a different speed
{
POST_MESSAGE(SimPlay, (speed, true));
m_SimState = newState;
}
UpdateSimButtons();
}
void MapSidebar::OnSimPause(wxCommandEvent& WXUNUSED(event))
{
if (IsPlaying(m_SimState))
{
POST_MESSAGE(SimPlay, (0.f, true));
m_SimState = SimPaused;
}
UpdateSimButtons();
}
void MapSidebar::OnSimReset(wxCommandEvent& WXUNUSED(event))
{
if (IsPlaying(m_SimState))
{
POST_MESSAGE(SimPlay, (0.f, true));
POST_MESSAGE(SimStateRestore, (L"default"));
POST_MESSAGE(SimStopMusic, ());
POST_MESSAGE(SimPlay, (0.f, false));
POST_MESSAGE(GuiSwitchPage, (L"page_atlas.xml"));
m_SimState = SimInactive;
}
else if (m_SimState == SimPaused)
{
POST_MESSAGE(SimPlay, (0.f, true));
POST_MESSAGE(SimStateRestore, (L"default"));
POST_MESSAGE(SimStopMusic, ());
POST_MESSAGE(SimPlay, (0.f, false));
POST_MESSAGE(GuiSwitchPage, (L"page_atlas.xml"));
m_SimState = SimInactive;
}
UpdateSimButtons();
}
void MapSidebar::OnRandomReseed(wxCommandEvent& WXUNUSED(evt))
{
// Pick a shortish randomish value
wxString seed;
seed << (int)floor((rand() / (float)RAND_MAX) * 10000.f);
wxDynamicCast(FindWindow(ID_RandomSeed), wxTextCtrl)->SetValue(seed);
}
void MapSidebar::OnRandomGenerate(wxCommandEvent& WXUNUSED(evt))
{
if (m_ScenarioEditor.DiscardChangesDialog())
return;
wxChoice* scriptChoice = wxDynamicCast(FindWindow(ID_RandomScript), wxChoice);
if (scriptChoice->GetSelection() < 0)
return;
// TODO: this settings thing seems a bit of a mess,
// since it's mixing data from three different sources
AtObj settings = m_MapSettingsCtrl->UpdateSettingsObject();
AtObj scriptSettings = dynamic_cast(scriptChoice->GetClientObject(scriptChoice->GetSelection()))->GetValue();
settings.addOverlay(scriptSettings);
wxChoice* sizeChoice = wxDynamicCast(FindWindow(ID_RandomSize), wxChoice);
wxString size;
size << (intptr_t)sizeChoice->GetClientData(sizeChoice->GetSelection());
settings.setInt("Size", wxAtoi(size));
settings.setBool("Nomad", wxDynamicCast(FindWindow(ID_RandomNomad), wxCheckBox)->GetValue());
settings.setInt("Seed", wxAtoi(wxDynamicCast(FindWindow(ID_RandomSeed), wxTextCtrl)->GetValue()));
std::string json = AtlasObject::SaveToJSON(settings);
wxBusyInfo busy(_("Generating map"));
wxBusyCursor busyc;
wxString scriptName = wxString::FromUTF8(settings["Script"]);
// Copy the old map settings, so we don't lose them if the map generation fails
AtObj oldSettings = settings;
AtlasMessage::qGenerateMap qry((std::wstring)scriptName.wc_str(), json);
qry.Post();
if (qry.status < 0)
{
// Display error message and revert to old map settings
wxLogError(_("Random map script '%s' failed"), scriptName.c_str());
m_MapSettingsCtrl->SetMapSettings(oldSettings);
}
m_ScenarioEditor.NotifyOnMapReload();
}
void MapSidebar::OnOpenPlayerPanel(wxCommandEvent& WXUNUSED(evt))
{
m_ScenarioEditor.SelectPage(_T("PlayerSidebar"));
}
void MapSidebar::OnResizeMap(wxCommandEvent& WXUNUSED(evt))
{
MapResizeDialog dlg(this);
if (dlg.ShowModal() != wxID_OK)
return;
wxPoint offset = dlg.GetOffset();
POST_COMMAND(ResizeMap, (dlg.GetNewSize(), offset.x, offset.y));
}
BEGIN_EVENT_TABLE(MapSidebar, Sidebar)
EVT_COLLAPSIBLEPANE_CHANGED(wxID_ANY, MapSidebar::OnCollapse)
EVT_BUTTON(ID_SimPlay, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimFast, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimSlow, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimPause, MapSidebar::OnSimPause)
EVT_BUTTON(ID_SimReset, MapSidebar::OnSimReset)
EVT_BUTTON(ID_RandomReseed, MapSidebar::OnRandomReseed)
EVT_BUTTON(ID_RandomGenerate, MapSidebar::OnRandomGenerate)
EVT_BUTTON(ID_ResizeMap, MapSidebar::OnResizeMap)
EVT_BUTTON(ID_OpenPlayerPanel, MapSidebar::OnOpenPlayerPanel)
END_EVENT_TABLE();
Index: ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp
===================================================================
--- ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp (revision 25814)
+++ ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp (revision 25815)
@@ -1,1005 +1,1002 @@
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "Player.h"
#include "AtlasObject/AtlasObject.h"
#include "CustomControls/ColorDialog/ColorDialog.h"
#include "ScenarioEditor/ScenarioEditor.h"
#include "wx/choicebk.h"
enum
{
ID_NumPlayers,
ID_PlayerFood,
ID_PlayerWood,
ID_PlayerMetal,
ID_PlayerStone,
ID_PlayerPop,
ID_PlayerColor,
ID_DefaultName,
ID_DefaultCiv,
ID_DefaultColor,
ID_DefaultAI,
ID_DefaultFood,
ID_DefaultWood,
ID_DefaultMetal,
ID_DefaultStone,
ID_DefaultPop,
ID_DefaultTeam,
ID_CameraSet,
ID_CameraView,
ID_CameraClear
};
// TODO: Some of these helper things should be moved out of this file
// and into shared locations
// Helper function for adding tooltips
static wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
{
window->SetToolTip(tip);
return window;
}
//////////////////////////////////////////////////////////////////////////
class DefaultCheckbox : public wxCheckBox
{
public:
DefaultCheckbox(wxWindow* parent, wxWindowID id, wxWindow* control, bool initialValue = false)
: wxCheckBox(parent, id, wxEmptyString), m_Control(control)
{
SetValue(initialValue);
}
virtual void SetValue(bool value)
{
m_Control->Enable(value);
wxCheckBox::SetValue(value);
}
void OnChecked(wxCommandEvent& evt)
{
m_Control->Enable(evt.IsChecked());
evt.Skip();
}
private:
wxWindow* m_Control;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(DefaultCheckbox, wxCheckBox)
EVT_CHECKBOX(wxID_ANY, DefaultCheckbox::OnChecked)
END_EVENT_TABLE();
class PlayerNotebookPage : public wxPanel
{
public:
PlayerNotebookPage(wxWindow* parent, const wxString& name, size_t playerID)
: wxPanel(parent, wxID_ANY), m_Name(name), m_PlayerID(playerID)
{
m_Controls.page = this;
Freeze();
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(sizer);
{
/////////////////////////////////////////////////////////////////////////
// Player Info
wxStaticBoxSizer* playerInfoSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Player info"));
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(3, 5, 5);
gridSizer->AddGrowableCol(2);
wxTextCtrl* nameCtrl = new wxTextCtrl(this, wxID_ANY);
gridSizer->Add(new DefaultCheckbox(this, ID_DefaultName, nameCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Name")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(nameCtrl, wxSizerFlags(1).Expand().Align(wxALIGN_RIGHT));
m_Controls.name = nameCtrl;
wxChoice* civChoice = new wxChoice(this, wxID_ANY);
gridSizer->Add(new DefaultCheckbox(this, ID_DefaultCiv, civChoice), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Civilisation")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(civChoice, wxSizerFlags(1).Expand().Align(wxALIGN_RIGHT));
m_Controls.civ = civChoice;
wxButton* colorButton = new wxButton(this, ID_PlayerColor);
gridSizer->Add(new DefaultCheckbox(this, ID_DefaultColor, colorButton), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Color")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(colorButton,
_("Set player color")), wxSizerFlags(1).Expand().Align(wxALIGN_RIGHT));
m_Controls.color = colorButton;
wxChoice* aiChoice = new wxChoice(this, wxID_ANY);
gridSizer->Add(new DefaultCheckbox(this, ID_DefaultAI, aiChoice), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("AI")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(aiChoice,
_("Select AI")), wxSizerFlags(1).Expand().Align(wxALIGN_RIGHT));
m_Controls.ai = aiChoice;
playerInfoSizer->Add(gridSizer, wxSizerFlags(1).Expand());
sizer->Add(playerInfoSizer, wxSizerFlags().Expand().Border(wxTOP, 10));
}
{
/////////////////////////////////////////////////////////////////////////
// Resources
wxStaticBoxSizer* resourceSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Resources"));
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(3, 5, 5);
gridSizer->AddGrowableCol(2);
wxSpinCtrl* foodCtrl = new wxSpinCtrl(this, ID_PlayerFood, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(this, ID_DefaultFood, foodCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Food")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(foodCtrl,
_("Initial value of food resource")), wxSizerFlags().Expand());
m_Controls.food = foodCtrl;
wxSpinCtrl* woodCtrl = new wxSpinCtrl(this, ID_PlayerWood, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(this, ID_DefaultWood, woodCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Wood")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(woodCtrl,
_("Initial value of wood resource")), wxSizerFlags().Expand());
m_Controls.wood = woodCtrl;
wxSpinCtrl* metalCtrl = new wxSpinCtrl(this, ID_PlayerMetal, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(this, ID_DefaultMetal, metalCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Metal")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(metalCtrl,
_("Initial value of metal resource")), wxSizerFlags().Expand());
m_Controls.metal = metalCtrl;
wxSpinCtrl* stoneCtrl = new wxSpinCtrl(this, ID_PlayerStone, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(this, ID_DefaultStone, stoneCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Stone")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(stoneCtrl,
_("Initial value of stone resource")), wxSizerFlags().Expand());
m_Controls.stone = stoneCtrl;
wxSpinCtrl* popCtrl = new wxSpinCtrl(this, ID_PlayerPop, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(this, ID_DefaultPop, popCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Pop limit")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(popCtrl,
_("Population limit for this player")), wxSizerFlags().Expand());
m_Controls.pop = popCtrl;
resourceSizer->Add(gridSizer, wxSizerFlags(1).Expand());
sizer->Add(resourceSizer, wxSizerFlags().Expand().Border(wxTOP, 10));
}
{
/////////////////////////////////////////////////////////////////////////
// Diplomacy
wxStaticBoxSizer* diplomacySizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Diplomacy"));
wxBoxSizer* boxSizer = new wxBoxSizer(wxHORIZONTAL);
wxChoice* teamCtrl = new wxChoice(this, wxID_ANY);
boxSizer->Add(new DefaultCheckbox(this, ID_DefaultTeam, teamCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
boxSizer->AddSpacer(5);
boxSizer->Add(new wxStaticText(this, wxID_ANY, _("Team")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
boxSizer->AddSpacer(5);
teamCtrl->Append(_("None"));
teamCtrl->Append(_T("1"));
teamCtrl->Append(_T("2"));
teamCtrl->Append(_T("3"));
teamCtrl->Append(_T("4"));
boxSizer->Add(teamCtrl);
m_Controls.team = teamCtrl;
diplomacySizer->Add(boxSizer, wxSizerFlags(1).Expand());
// TODO: possibly have advanced panel where each player's diplomacy can be set?
// Advanced panel
/*wxCollapsiblePane* advPane = new wxCollapsiblePane(this, wxID_ANY, _("Advanced"));
wxWindow* pane = advPane->GetPane();
diplomacySizer->Add(advPane, 0, wxGROW | wxALL, 2);*/
sizer->Add(diplomacySizer, wxSizerFlags().Expand().Border(wxTOP, 10));
}
{
/////////////////////////////////////////////////////////////////////////
// Camera
wxStaticBoxSizer* cameraSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Starting Camera"));
wxGridSizer* gridSizer = new wxGridSizer(3);
wxButton* cameraSet = new wxButton(this, ID_CameraSet, _("Set"), wxDefaultPosition, wxSize(48, -1));
gridSizer->Add(Tooltipped(cameraSet,
_("Set player camera to this view")), wxSizerFlags().Expand());
wxButton* cameraView = new wxButton(this, ID_CameraView, _("View"), wxDefaultPosition, wxSize(48, -1));
cameraView->Enable(false);
gridSizer->Add(Tooltipped(cameraView,
_("View the player camera")), wxSizerFlags().Expand());
wxButton* cameraClear = new wxButton(this, ID_CameraClear, _("Clear"), wxDefaultPosition, wxSize(48, -1));
cameraClear->Enable(false);
gridSizer->Add(Tooltipped(cameraClear,
_("Clear player camera")), wxSizerFlags().Expand());
cameraSizer->Add(gridSizer, wxSizerFlags().Expand());
sizer->Add(cameraSizer, wxSizerFlags().Expand().Border(wxTOP, 10));
}
Layout();
Thaw();
}
void OnDisplay()
{
}
PlayerPageControls GetControls()
{
return m_Controls;
}
wxString GetPlayerName()
{
return m_Name;
}
size_t GetPlayerID()
{
return m_PlayerID;
}
bool IsCameraDefined()
{
return m_CameraDefined;
}
sCameraInfo GetCamera()
{
return m_Camera;
}
void SetCamera(sCameraInfo info, bool isDefined = true)
{
m_Camera = info;
m_CameraDefined = isDefined;
// Enable/disable controls
wxDynamicCast(FindWindow(ID_CameraView), wxButton)->Enable(isDefined);
wxDynamicCast(FindWindow(ID_CameraClear), wxButton)->Enable(isDefined);
}
private:
void OnColor(wxCommandEvent& evt)
{
// Show color dialog
ColorDialog colorDlg(this, _T("Scenario Editor/PlayerColor"), m_Controls.color->GetBackgroundColour());
if (colorDlg.ShowModal() == wxID_OK)
{
m_Controls.color->SetBackgroundColour(colorDlg.GetColourData().GetColour());
// Pass event on to next handler
evt.Skip();
}
}
void OnCameraSet(wxCommandEvent& evt)
{
AtlasMessage::qGetView qryView;
qryView.Post();
SetCamera(qryView.info, true);
// Pass event on to next handler
evt.Skip();
}
void OnCameraView(wxCommandEvent& WXUNUSED(evt))
{
POST_MESSAGE(SetView, (m_Camera));
}
void OnCameraClear(wxCommandEvent& evt)
{
SetCamera(sCameraInfo(), false);
// Pass event on to next handler
evt.Skip();
}
sCameraInfo m_Camera;
bool m_CameraDefined;
wxString m_Name;
size_t m_PlayerID;
PlayerPageControls m_Controls;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(PlayerNotebookPage, wxPanel)
EVT_BUTTON(ID_PlayerColor, PlayerNotebookPage::OnColor)
EVT_BUTTON(ID_CameraSet, PlayerNotebookPage::OnCameraSet)
EVT_BUTTON(ID_CameraView, PlayerNotebookPage::OnCameraView)
EVT_BUTTON(ID_CameraClear, PlayerNotebookPage::OnCameraClear)
END_EVENT_TABLE();
//////////////////////////////////////////////////////////////////////////
class PlayerNotebook : public wxChoicebook
{
public:
PlayerNotebook(wxWindow *parent)
: wxChoicebook(parent, wxID_ANY/*, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH*/)
{
}
PlayerPageControls AddPlayer(wxString name, size_t player)
{
PlayerNotebookPage* playerPage = new PlayerNotebookPage(this, name, player);
AddPage(playerPage, name);
m_Pages.push_back(playerPage);
return playerPage->GetControls();
}
void ResizePlayers(size_t numPlayers)
{
wxASSERT(numPlayers <= m_Pages.size());
// We don't really want to destroy the windows corresponding
// to the tabs, so we've kept them in a vector and will
// only remove and add them to the notebook as needed
int selection = GetSelection();
size_t pageCount = GetPageCount();
if (numPlayers > pageCount)
{
// Add previously removed pages
for (size_t i = pageCount; i < numPlayers; ++i)
{
AddPage(m_Pages[i], m_Pages[i]->GetPlayerName());
}
}
else
{
// Remove previously added pages
// we have to manually hide them or they remain visible
for (size_t i = pageCount - 1; i >= numPlayers; --i)
{
m_Pages[i]->Hide();
RemovePage(i);
}
}
// Workaround for bug on wxGTK 2.8: wxChoice selection doesn't update
// (in fact it loses its selection when adding/removing pages)
GetChoiceCtrl()->SetSelection(selection);
}
protected:
void OnPageChanged(wxChoicebookEvent& evt)
{
if (evt.GetSelection() >= 0 && evt.GetSelection() < (int)GetPageCount())
{
static_cast(GetPage(evt.GetSelection()))->OnDisplay();
}
evt.Skip();
}
private:
std::vector m_Pages;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(PlayerNotebook, wxChoicebook)
EVT_CHOICEBOOK_PAGE_CHANGED(wxID_ANY, PlayerNotebook::OnPageChanged)
END_EVENT_TABLE();
//////////////////////////////////////////////////////////////////////////
class PlayerSettingsControl : public wxPanel
{
public:
PlayerSettingsControl(wxWindow* parent, ScenarioEditor& scenarioEditor);
void CreateWidgets();
void LoadDefaults();
void ReadFromEngine();
AtObj UpdateSettingsObject();
private:
void SendToEngine();
void OnEdit(wxCommandEvent& WXUNUSED(evt))
{
if (!m_InGUIUpdate)
{
SendToEngine();
}
}
void OnEditSpin(wxSpinEvent& WXUNUSED(evt))
{
if (!m_InGUIUpdate)
{
SendToEngine();
}
}
void OnPlayerColor(wxCommandEvent& WXUNUSED(evt))
{
if (!m_InGUIUpdate)
{
SendToEngine();
// Update player settings, to show new color
POST_MESSAGE(LoadPlayerSettings, (false));
}
}
void OnNumPlayersText(wxCommandEvent& WXUNUSED(evt))
{ // Ignore because it will also trigger EVT_SPINCTRL
// and we don't want to handle the same event twice
}
void OnNumPlayersSpin(wxSpinEvent& evt)
{
if (!m_InGUIUpdate)
{
wxASSERT(evt.GetInt() > 0);
// When wxMessageBox pops up, wxSpinCtrl loses focus, which
// forces another EVT_SPINCTRL event, which we don't want
// to handle, so we check here for a change
if (evt.GetInt() == (int)m_NumPlayers)
{
return; // No change
}
size_t oldNumPlayers = m_NumPlayers;
m_NumPlayers = evt.GetInt();
if (m_NumPlayers < oldNumPlayers)
{
// Remove players, but check if they own any entities
bool notified = false;
for (size_t i = oldNumPlayers; i > m_NumPlayers; --i)
{
qGetPlayerObjects objectsQry(i);
objectsQry.Post();
std::vector ids = *objectsQry.ids;
if (ids.size() > 0)
{
if (!notified)
{
// TODO: Add option to reassign objects?
if (wxMessageBox(_("WARNING: All objects belonging to the removed players will be deleted. Continue anyway?"), _("Remove player confirmation"), wxICON_EXCLAMATION | wxYES_NO) != wxYES)
{
// Restore previous player count
m_NumPlayers = oldNumPlayers;
wxDynamicCast(FindWindow(ID_NumPlayers), wxSpinCtrl)->SetValue(m_NumPlayers);
return;
}
notified = true;
}
// Delete objects
// TODO: Merge multiple commands?
POST_COMMAND(DeleteObjects, (ids));
}
}
}
m_Players->ResizePlayers(m_NumPlayers);
SendToEngine();
// Reload players, notify observers
POST_MESSAGE(LoadPlayerSettings, (true));
m_MapSettings.NotifyObservers();
}
}
// TODO: we shouldn't hardcode this, but instead dynamically create
// new player notebook pages on demand; of course the default data
// will be limited by the entries in player_defaults.json
static const size_t MAX_NUM_PLAYERS = 8;
bool m_InGUIUpdate;
AtObj m_PlayerDefaults;
PlayerNotebook* m_Players;
std::vector m_PlayerControls;
Observable& m_MapSettings;
size_t m_NumPlayers;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(PlayerSettingsControl, wxPanel)
EVT_BUTTON(ID_PlayerColor, PlayerSettingsControl::OnPlayerColor)
EVT_BUTTON(ID_CameraSet, PlayerSettingsControl::OnEdit)
EVT_BUTTON(ID_CameraClear, PlayerSettingsControl::OnEdit)
EVT_CHECKBOX(wxID_ANY, PlayerSettingsControl::OnEdit)
EVT_CHOICE(wxID_ANY, PlayerSettingsControl::OnEdit)
EVT_TEXT(ID_NumPlayers, PlayerSettingsControl::OnNumPlayersText)
EVT_TEXT(wxID_ANY, PlayerSettingsControl::OnEdit)
EVT_SPINCTRL(ID_NumPlayers, PlayerSettingsControl::OnNumPlayersSpin)
EVT_SPINCTRL(ID_PlayerFood, PlayerSettingsControl::OnEditSpin)
EVT_SPINCTRL(ID_PlayerWood, PlayerSettingsControl::OnEditSpin)
EVT_SPINCTRL(ID_PlayerMetal, PlayerSettingsControl::OnEditSpin)
EVT_SPINCTRL(ID_PlayerStone, PlayerSettingsControl::OnEditSpin)
EVT_SPINCTRL(ID_PlayerPop, PlayerSettingsControl::OnEditSpin)
END_EVENT_TABLE();
PlayerSettingsControl::PlayerSettingsControl(wxWindow* parent, ScenarioEditor& scenarioEditor)
: wxPanel(parent, wxID_ANY), m_InGUIUpdate(false), m_MapSettings(scenarioEditor.GetMapSettings()), m_NumPlayers(0)
{
// To prevent recursion, don't handle GUI events right now
m_InGUIUpdate = true;
wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Player settings"));
SetSizer(sizer);
wxBoxSizer* boxSizer = new wxBoxSizer(wxHORIZONTAL);
boxSizer->Add(new wxStaticText(this, wxID_ANY, _("Num players")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
wxSpinCtrl* numPlayersSpin = new wxSpinCtrl(this, ID_NumPlayers, wxEmptyString, wxDefaultPosition, wxSize(40, -1));
numPlayersSpin->SetValue(MAX_NUM_PLAYERS);
numPlayersSpin->SetRange(1, MAX_NUM_PLAYERS);
boxSizer->Add(numPlayersSpin);
sizer->Add(boxSizer, wxSizerFlags().Expand().Proportion(0));
sizer->AddSpacer(5);
m_Players = new PlayerNotebook(this);
sizer->Add(m_Players, wxSizerFlags().Expand().Proportion(1));
m_InGUIUpdate = false;
}
void PlayerSettingsControl::CreateWidgets()
{
// To prevent recursion, don't handle GUI events right now
m_InGUIUpdate = true;
// Load default civ and player data
wxArrayString civNames;
wxArrayString civCodes;
AtlasMessage::qGetCivData qryCiv;
qryCiv.Post();
std::vector civData = *qryCiv.data;
for (size_t i = 0; i < civData.size(); ++i)
{
AtObj civ = AtlasObject::LoadFromJSON(civData[i]);
civNames.Add(wxString::FromUTF8(civ["Name"]));
civCodes.Add(wxString::FromUTF8(civ["Code"]));
}
// Load AI data
ArrayOfAIData ais(AIData::CompareAIData);
AtlasMessage::qGetAIData qryAI;
qryAI.Post();
AtObj aiData = AtlasObject::LoadFromJSON(*qryAI.data);
for (AtIter a = aiData["AIData"]["item"]; a.defined(); ++a)
{
ais.Add(new AIData(wxString::FromUTF8(a["id"]), wxString::FromUTF8(a["data"]["name"])));
}
// Create player pages
AtIter playerDefs = m_PlayerDefaults["item"];
if (playerDefs.defined())
++playerDefs; // Skip gaia
for (size_t i = 0; i < MAX_NUM_PLAYERS; ++i)
{
// Create new player tab and get controls
wxString name(_("Unknown"));
if (playerDefs["Name"].defined())
name = playerDefs["Name"];
PlayerPageControls controls = m_Players->AddPlayer(name, i);
m_PlayerControls.push_back(controls);
// Populate civ choice box
wxChoice* civChoice = controls.civ;
for (size_t j = 0; j < civNames.Count(); ++j)
civChoice->Append(civNames[j], new wxStringClientData(civCodes[j]));
civChoice->SetSelection(0);
// Populate ai choice box
wxChoice* aiChoice = controls.ai;
aiChoice->Append(_(""), new wxStringClientData());
for (size_t j = 0; j < ais.Count(); ++j)
aiChoice->Append(ais[j]->GetName(), new wxStringClientData(ais[j]->GetID()));
aiChoice->SetSelection(0);
if (playerDefs.defined())
++playerDefs;
}
m_InGUIUpdate = false;
}
void PlayerSettingsControl::LoadDefaults()
{
AtlasMessage::qGetPlayerDefaults qryPlayers;
qryPlayers.Post();
AtObj playerData = AtlasObject::LoadFromJSON(*qryPlayers.defaults);
m_PlayerDefaults = *playerData["PlayerData"];
}
void PlayerSettingsControl::ReadFromEngine()
{
AtlasMessage::qGetMapSettings qry;
qry.Post();
if (!(*qry.settings).empty())
{
// Prevent error if there's no map settings to parse
m_MapSettings = AtlasObject::LoadFromJSON(*qry.settings);
}
else
{
// Use blank object, it will be created next
m_MapSettings = AtObj();
}
AtIter player = m_MapSettings["PlayerData"]["item"];
if (!m_MapSettings.defined() || !player.defined() || player.count() == 0)
{
// Player data missing - set number of players to max
m_NumPlayers = MAX_NUM_PLAYERS;
}
else
{
++player; // skip gaia
m_NumPlayers = player.count();
}
wxASSERT(m_NumPlayers <= MAX_NUM_PLAYERS && m_NumPlayers != 0);
// To prevent recursion, don't handle GUI events right now
m_InGUIUpdate = true;
wxDynamicCast(FindWindow(ID_NumPlayers), wxSpinCtrl)->SetValue(m_NumPlayers);
// Remove / add extra player pages as needed
m_Players->ResizePlayers(m_NumPlayers);
// Update player controls with player data
AtIter playerDefs = m_PlayerDefaults["item"];
if (playerDefs.defined())
++playerDefs; // skip gaia
for (size_t i = 0; i < MAX_NUM_PLAYERS; ++i)
{
const PlayerPageControls& controls = m_PlayerControls[i];
// name
wxString name(_("Unknown"));
bool defined = player["Name"].defined();
if (defined)
name = wxString::FromUTF8(player["Name"]);
else if (playerDefs["Name"].defined())
name = wxString::FromUTF8(playerDefs["Name"]);
controls.name->SetValue(name);
wxDynamicCast(FindWindowById(ID_DefaultName, controls.page), DefaultCheckbox)->SetValue(defined);
// civ
wxChoice* choice = controls.civ;
defined = player["Civ"].defined();
wxString civCode = wxString::FromUTF8(defined ? player["Civ"] : playerDefs["Civ"]);
for (size_t j = 0; j < choice->GetCount(); ++j)
{
wxStringClientData* str = dynamic_cast(choice->GetClientObject(j));
if (str->GetData() == civCode)
{
choice->SetSelection(j);
break;
}
}
wxDynamicCast(FindWindowById(ID_DefaultCiv, controls.page), DefaultCheckbox)->SetValue(defined);
// color
wxColor color;
AtObj clrObj = *player["Color"];
defined = clrObj.defined();
if (!defined)
clrObj = *playerDefs["Color"];
color = wxColor((*clrObj["r"]).getInt(), (*clrObj["g"]).getInt(), (*clrObj["b"]).getInt());
controls.color->SetBackgroundColour(color);
wxDynamicCast(FindWindowById(ID_DefaultColor, controls.page), DefaultCheckbox)->SetValue(defined);
// player type
defined = player["AI"].defined();
wxString aiID = wxString::FromUTF8(defined ? player["AI"] : playerDefs["AI"]);
choice = controls.ai;
if (!aiID.empty())
{
// AI
for (size_t j = 0; j < choice->GetCount(); ++j)
{
wxStringClientData* str = dynamic_cast(choice->GetClientObject(j));
if (str->GetData() == aiID)
{
choice->SetSelection(j);
break;
}
}
}
else // Human
choice->SetSelection(0);
wxDynamicCast(FindWindowById(ID_DefaultAI, controls.page), DefaultCheckbox)->SetValue(defined);
// resources
AtObj resObj = *player["Resources"];
defined = resObj.defined() && resObj["food"].defined();
if (defined)
controls.food->SetValue((*resObj["food"]).getInt());
else
controls.food->SetValue(0);
wxDynamicCast(FindWindowById(ID_DefaultFood, controls.page), DefaultCheckbox)->SetValue(defined);
defined = resObj.defined() && resObj["wood"].defined();
if (defined)
controls.wood->SetValue((*resObj["wood"]).getInt());
else
controls.wood->SetValue(0);
wxDynamicCast(FindWindowById(ID_DefaultWood, controls.page), DefaultCheckbox)->SetValue(defined);
defined = resObj.defined() && resObj["metal"].defined();
if (defined)
controls.metal->SetValue((*resObj["metal"]).getInt());
else
controls.metal->SetValue(0);
wxDynamicCast(FindWindowById(ID_DefaultMetal, controls.page), DefaultCheckbox)->SetValue(defined);
defined = resObj.defined() && resObj["stone"].defined();
if (defined)
controls.stone->SetValue((*resObj["stone"]).getInt());
else
controls.stone->SetValue(0);
wxDynamicCast(FindWindowById(ID_DefaultStone, controls.page), DefaultCheckbox)->SetValue(defined);
// population limit
defined = player["PopulationLimit"].defined();
if (defined)
controls.pop->SetValue((*player["PopulationLimit"]).getInt());
else
controls.pop->SetValue(0);
wxDynamicCast(FindWindowById(ID_DefaultPop, controls.page), DefaultCheckbox)->SetValue(defined);
// team
defined = player["Team"].defined();
if (defined)
controls.team->SetSelection((*player["Team"]).getInt() + 1);
else
controls.team->SetSelection(0);
wxDynamicCast(FindWindowById(ID_DefaultTeam, controls.page), DefaultCheckbox)->SetValue(defined);
// camera
if (player["StartingCamera"].defined())
{
sCameraInfo info;
// Don't use wxAtof because it depends on locales which
// may cause problems with decimal points
// see: http://www.wxwidgets.org/docs/faqgtk.htm#locale
AtObj camPos = *player["StartingCamera"]["Position"];
info.pX = (float)(*camPos["x"]).getDouble();
info.pY = (float)(*camPos["y"]).getDouble();
info.pZ = (float)(*camPos["z"]).getDouble();
AtObj camRot = *player["StartingCamera"]["Rotation"];
info.rX = (float)(*camRot["x"]).getDouble();
info.rY = (float)(*camRot["y"]).getDouble();
info.rZ = (float)(*camRot["z"]).getDouble();
controls.page->SetCamera(info, true);
}
else
controls.page->SetCamera(sCameraInfo(), false);
if (player.defined())
++player;
if (playerDefs.defined())
++playerDefs;
}
// Send default properties to engine, since they might not be set
SendToEngine();
m_InGUIUpdate = false;
}
AtObj PlayerSettingsControl::UpdateSettingsObject()
{
// Update player data in the map settings
AtObj players;
players.set("@array", "");
wxASSERT(m_NumPlayers <= MAX_NUM_PLAYERS);
AtIter playerDefs = m_PlayerDefaults["item"];
if (playerDefs.defined())
++playerDefs; // Skip gaia
for (size_t i = 0; i < m_NumPlayers; ++i)
{
PlayerPageControls controls = m_PlayerControls[i];
AtObj player;
// name
wxTextCtrl* text = controls.name;
if (text->IsEnabled())
player.set("Name", text->GetValue().utf8_str());
// civ
wxChoice* choice = controls.civ;
if (choice->IsEnabled() && choice->GetSelection() >= 0)
{
wxStringClientData* str = dynamic_cast(choice->GetClientObject(choice->GetSelection()));
- player.set("Civ", str->GetData());
+ player.set("Civ", str->GetData().utf8_str());
}
else
player.unset("Civ");
// color
if (controls.color->IsEnabled())
{
wxColor color = controls.color->GetBackgroundColour();
AtObj clrObj;
clrObj.setInt("r", (int)color.Red());
clrObj.setInt("g", (int)color.Green());
clrObj.setInt("b", (int)color.Blue());
player.set("Color", clrObj);
}
// player type
choice = controls.ai;
- if (choice->IsEnabled())
+ if (choice->IsEnabled() && choice->GetSelection() > 0)
{
- if (choice->GetSelection() > 0)
- {
- // ai - get id
- wxStringClientData* str = dynamic_cast(choice->GetClientObject(choice->GetSelection()));
- player.set("AI", str->GetData());
- }
- else // human
- player.set("AI", _(""));
+ // ai - get id
+ wxStringClientData* str = dynamic_cast(choice->GetClientObject(choice->GetSelection()));
+ player.set("AI", str->GetData().utf8_str());
}
+ else // human
+ player.unset("AI");
// resources
AtObj resObj;
if (controls.food->IsEnabled())
resObj.setInt("food", controls.food->GetValue());
if (controls.wood->IsEnabled())
resObj.setInt("wood", controls.wood->GetValue());
if (controls.metal->IsEnabled())
resObj.setInt("metal", controls.metal->GetValue());
if (controls.stone->IsEnabled())
resObj.setInt("stone", controls.stone->GetValue());
if (resObj.defined())
player.set("Resources", resObj);
// population limit
if (controls.pop->IsEnabled())
player.setInt("PopulationLimit", controls.pop->GetValue());
// team
choice = controls.team;
if (choice->IsEnabled() && choice->GetSelection() >= 0)
player.setInt("Team", choice->GetSelection() - 1);
// camera
AtObj camObj;
if (controls.page->IsCameraDefined())
{
sCameraInfo cam = controls.page->GetCamera();
AtObj camPos;
camPos.setDouble("x", cam.pX);
camPos.setDouble("y", cam.pY);
camPos.setDouble("z", cam.pZ);
camObj.set("Position", camPos);
AtObj camRot;
camRot.setDouble("x", cam.rX);
camRot.setDouble("y", cam.rY);
camRot.setDouble("z", cam.rZ);
camObj.set("Rotation", camRot);
}
player.set("StartingCamera", camObj);
players.add("item", player);
if (playerDefs.defined())
++playerDefs;
}
m_MapSettings.set("PlayerData", players);
return m_MapSettings;
}
void PlayerSettingsControl::SendToEngine()
{
UpdateSettingsObject();
std::string json = AtlasObject::SaveToJSON(m_MapSettings);
// TODO: would be nice if we supported undo for settings changes
POST_COMMAND(SetMapSettings, (json));
}
//////////////////////////////////////////////////////////////////////////
PlayerSidebar::PlayerSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer), m_Loaded(false)
{
wxSizer* scrollSizer = new wxBoxSizer(wxVERTICAL);
wxScrolledWindow* scrolledWindow = new wxScrolledWindow(this);
scrolledWindow->SetScrollRate(10, 10);
scrolledWindow->SetSizer(scrollSizer);
m_MainSizer->Add(scrolledWindow, wxSizerFlags().Proportion(1).Expand());
m_PlayerSettingsCtrl = new PlayerSettingsControl(scrolledWindow, m_ScenarioEditor);
scrollSizer->Add(m_PlayerSettingsCtrl, wxSizerFlags().Expand());
}
void PlayerSidebar::OnCollapse(wxCollapsiblePaneEvent& WXUNUSED(evt))
{
Freeze();
// Toggling the collapsing doesn't seem to update the sidebar layout
// automatically, so do it explicitly here
Layout();
Refresh(); // fixes repaint glitch on Windows
Thaw();
}
void PlayerSidebar::OnFirstDisplay()
{
// We do this here becase messages are used which requires simulation to be init'd
m_PlayerSettingsCtrl->LoadDefaults();
m_PlayerSettingsCtrl->CreateWidgets();
m_PlayerSettingsCtrl->ReadFromEngine();
m_Loaded = true;
Layout();
}
void PlayerSidebar::OnMapReload()
{
// Make sure we've loaded the controls
if (m_Loaded)
{
m_PlayerSettingsCtrl->ReadFromEngine();
}
}
BEGIN_EVENT_TABLE(PlayerSidebar, Sidebar)
EVT_COLLAPSIBLEPANE_CHANGED(wxID_ANY, PlayerSidebar::OnCollapse)
END_EVENT_TABLE();