If you have comments or questions concerning this source file, discuss them in the forum.
/*
Copyright (c) 2002 Nicolai Haehnle
See the license.txt for details. If that file was not included in the
source distributions, please email <prefect@rtts.org>
*/
// ed_tilesets.cpp
#include "editor.h"
#include "rttspanels.h"
#include "gui_listbox.h"
#include "gui_grid.h"
/*
==============================================================================
EdTilePool IMPLEMENTATION
==============================================================================
*/
/*
==============
EdTilePool::EdTilePool
Initialize the pool
==============
*/
EdTilePool::EdTilePool(const char *pszName)
{
m_pszName = L_Strdup(pszName, TAG_EDITOR);
}
/*
==============
EdTilePool::~EdTilePool
Free allocate resources
==============
*/
EdTilePool::~EdTilePool()
{
L_Free(m_pszName);
}
/*
==============================================================================
EdTilesets IMPLEMENTATION
==============================================================================
*/
/*
==============
EdTilesets::EdTilesets
Start out with no tilesets
==============
*/
EdTilesets::EdTilesets()
{
m_iNumTypesEx = 0;
m_pTypesEx = 0;
m_iNumBorderNames = 0;
m_pBorderNames = 0;
m_iNumTilesets = 0;
m_ppTilesets = 0;
m_pCurrent = 0;
m_iCurIdx = -1;
m_iCurTile = ~0;
}
/*
==============
EdTilesets::~EdTilesets
Free all allocated data
==============
*/
EdTilesets::~EdTilesets()
{
int i;
if (m_pTypesEx)
L_Free(m_pTypesEx);
if (m_iNumBorderNames) {
for(i = 0; i < m_iNumBorderNames; i++)
L_Free(m_pBorderNames[i]);
L_Free(m_pBorderNames);
}
if (m_iNumTilesets) {
for(i = 0; i < m_iNumTilesets; i++)
delete m_ppTilesets[i];
L_Free(m_ppTilesets);
}
}
/*
==============
EdTilesets::LoadTileset
Parse the tileset definition file
==============
*/
void EdTilesets::LoadTileset(const char *name)
{
char path[MAX_OSPATH];
LParser p;
EdTilePool *tp;
ed_tiletype_t *ett;
const char *token;
unsigned id;
bool valid;
int dir, border;
snprintf(path, sizeof(path), "tiles/%s.ts", name);
p.AddFile(path);
// Create the new pool
tp = new EdTilePool(name);
m_iNumTilesets++;
m_ppTilesets = (EdTilePool **)L_Realloc(m_ppTilesets, m_iNumTilesets*sizeof(void *),
TAG_EDITOR);
m_ppTilesets[m_iNumTilesets-1] = tp;
// Parse the file
for(;;) {
// token is always the tilename
token = p.TryString();
if (!token)
break;
id = m_tiletypes.Load(token);
valid = m_tiletypes.ValidId(id);
if (valid) {
tp->m_ids.Add(id);
ett = GetTileTypeEx(id);
} else
ett = 0;
// parse border types
p.Expect("(");
for(;;) {
p.NextToken();
if (p.tok.type == ')')
break;
if (p.tok.type != TOK_STRING)
throw p.Error("'(' or keyword expected");
if (!strcmp(p.tok.string, "noauto")) {
if (ett)
ett->noauto = true;
} else {
if (!strcmp(p.tok.string, "left"))
dir = ttb_left;
else if (!strcmp(p.tok.string, "right"))
dir = ttb_right;
else if (!strcmp(p.tok.string, "bottom"))
dir = ttb_bottom;
else if (!strcmp(p.tok.string, "top"))
dir = ttb_top;
else
throw p.Error("unknown keyword %s", p.tok.string);
token = p.AnyString();
if (ett) {
border = GetBorderByName(token);
if (ett->border[dir] < 0)
ett->border[dir] = border;
else if (ett->border[dir] != border)
L_Printf("%s: conflicting border %i: %s\n",
m_tiletypes.m_pTT[id].pic->m_szName, dir, token);
}
}
}
p.Expect(";");
}
}
/*
==============
EdTilesets::Load
Scan the tiles directory for available tiles
==============
*/
void EdTilesets::Load()
{
char **names, *p;
int count, i;
// Load all tiles that are available
count = L_FindFiles("tiles", "*.bmp", &names);
for(i = 0; i < count; i++) {
p = strstr(names[i], ".bmp");
if (p)
*p = 0;
m_tiletypes.Load(names[i]);
}
L_FreeFindFiles(names);
// Load tileset descriptions
count = L_FindFiles("tiles", "*.ts", &names);
for(i = 0; i < count; i++) {
try {
p = strstr(names[i], ".ts");
if (p)
*p = 0;
LoadTileset(names[i]);
} catch(LError &err) {
L_Printf("Error loading %s.ts: %s\n", names[i], err.Get());
}
}
L_FreeFindFiles(names);
}
/*
==============
EdTilesets::GetBorderByName
Return the border ID used for auto-placing, create a new ID if the name's unique.
==============
*/
int EdTilesets::GetBorderByName(const char *name)
{
int i;
for(i = 0; i < m_iNumBorderNames; i++) {
if (!strcmp(m_pBorderNames[i], name))
return i;
}
i = m_iNumBorderNames++;
m_pBorderNames = (char **)L_Realloc(m_pBorderNames, sizeof(char *)*(m_iNumBorderNames),
TAG_EDITOR);
m_pBorderNames[i] = L_Strdup(name, TAG_EDITOR);
return i;
}
/*
==============
EdTilesets::GetTileTypeEx
Return the extended, editor-only information for the given tile.
If necessary, the information is created using default values
==============
*/
ed_tiletype_t *EdTilesets::GetTileTypeEx(unsigned id)
{
ed_tiletype_t *ett;
lassert(m_tiletypes.ValidId(id));
if (id >= m_iNumTypesEx) {
m_pTypesEx = (ed_tiletype_t *)L_Realloc(m_pTypesEx, sizeof(ed_tiletype_t)*(id+1),
TAG_EDITOR);
for(ett = &m_pTypesEx[m_iNumTypesEx]; m_iNumTypesEx <= id; m_iNumTypesEx++, ett++) {
ett->border[0] = ett->border[1] = ett->border[2] = ett->border[3] = -1;
ett->noauto = false;
}
}
return &m_pTypesEx[id];
}
/*
==============
EdTilesets::isCurTileValid
Returns true if the currently selected tile is valid for painting
==============
*/
bool EdTilesets::isCurTileValid()
{
return m_tiletypes.ValidId(m_iCurTile);
}
/*
==============
EdTilesets::SetTileset
Sets the current tileset, based on ID (-1 means all tiles)
==============
*/
void EdTilesets::SetTileset(int id)
{
if (id < 0 || id >= m_iNumTilesets)
m_pCurrent = 0;
else
m_pCurrent = m_ppTilesets[id];
}
/*
==============
EdTilesets::SetCurTile
Set the current tile; adjust m_iCurIdx if possible
==============
*/
void EdTilesets::SetCurTile(unsigned id)
{
lassert(m_tiletypes.ValidId(id));
m_iCurTile = id;
if (!m_pCurrent)
m_iCurIdx = id;
else
m_iCurIdx = m_pCurrent->m_ids.Find(id);
}
/*
==============
EdTilesets::SetCurIdx
Sets the current tile, where idx is relative to the currently selected
tileset
==============
*/
void EdTilesets::SetCurIdx(int idx)
{
m_iCurIdx = idx;
if (!m_pCurrent)
m_iCurTile = idx;
else
m_iCurTile = m_pCurrent->m_ids.items[idx];
}
/*
==============
EdTilesets::NextTile
EdTilesets::PrevTile
Cycle the selected tile
==============
*/
void EdTilesets::NextTile()
{
m_iCurIdx++;
if (!m_pCurrent) {
if ((unsigned)m_iCurIdx >= m_tiletypes.m_iNumTT)
m_iCurIdx = 0;
m_iCurTile = m_iCurIdx;
} else {
if (m_iCurIdx >= m_pCurrent->m_ids.size)
m_iCurIdx = 0;
m_iCurTile = m_pCurrent->m_ids.items[m_iCurIdx];
}
}
void EdTilesets::PrevTile()
{
m_iCurIdx--;
if (!m_pCurrent) {
if (m_iCurIdx < 0)
m_iCurIdx = m_tiletypes.m_iNumTT - 1;
m_iCurTile = m_iCurIdx;
} else {
if (m_iCurIdx < 0)
m_iCurIdx = m_pCurrent->m_ids.size - 1;
m_iCurTile = m_pCurrent->m_ids.items[m_iCurIdx];
}
}
/*
==============================================================================
ChooseTilesetDlg IMPLEMENTATION
==============================================================================
*/
/*
Returns -1 for cancel, and the selected tileset (1-based) when run;
A return value of 0 means "all" has been selected.
*/
class ChooseTilesetDlg : public RttsDialog {
public:
GListBox *m_pListBox;
GButton *m_pOk;
public:
ChooseTilesetDlg(GPanel *pParent, EdTilesets *pTilesets);
void Refresh();
void Ok(int id);
};
ChooseTilesetDlg::ChooseTilesetDlg(GPanel *pParent, EdTilesets *pTilesets)
: RttsDialog(pParent, 50, 50, 540, 380)
{
GButton *btn;
int i;
m_pListBox = new GListBox(this, 30, 30, 330, 320);
m_pListBox->SetColor(192, 192, 192);
m_pListBox->SetSelectedColor(255, 255, 0);
m_pListBox->AddString("<all>");
for(i = 0; i < pTilesets->m_iNumTilesets; i++)
m_pListBox->AddString(pTilesets->m_ppTilesets[i]->m_pszName);
m_pListBox->SelectChanged.Connect(this, &ChooseTilesetDlg::Refresh);
m_pOk = new RttsButton(this, 450, 30, 0, 30, "Ok", halign_center);
m_pOk->Clicked.Connect(this, &ChooseTilesetDlg::Ok);
btn = new RttsButton(this, 450, 60, 0, 30, "Cancel", halign_center, -1);
btn->Clicked.Connect(this, &ChooseTilesetDlg::EndModal);
Refresh();
}
void ChooseTilesetDlg::Refresh()
{
bool valid;
valid = true;
if (!m_pListBox->GetSelection())
valid = false;
m_pOk->SetOblivious(!valid);
}
void ChooseTilesetDlg::Ok(int id)
{
EndModal(m_pListBox->GetSelectionId());
}
/*
==============================================================================
PickTileDlg IMPLEMENTATION
==============================================================================
*/
class PickTileDlg : public RttsDialog {
public:
EdTilesets *m_pTilesets;
GGrid *m_pGrid;
public:
PickTileDlg(GPanel *pParent, EdTilesets *pTilesets);
void Refresh();
void ChooseTileset(int id);
};
PickTileDlg::PickTileDlg(GPanel *pParent, EdTilesets *pTilesets)
: RttsDialog(pParent, 30, 30, 580, 420)
{
GButton *btn;
btn = new RttsButton(this, 290, 30, 0, 0, "Choose tileset", halign_center);
btn->Clicked.Connect(this, &PickTileDlg::ChooseTileset);
m_pTilesets = pTilesets;
m_pGrid = 0;
Refresh();
}
void PickTileDlg::Refresh()
{
GButton *btn;
int count, i;
unsigned id;
if (m_pGrid)
delete m_pGrid;
m_pGrid = new GGrid(this, 30, 60, 520, 330);
if (m_pTilesets->m_pCurrent)
count = m_pTilesets->m_pCurrent->m_ids.size;
else
count = m_pTilesets->m_tiletypes.m_iNumTT;
for(i = 0; i < count; i++) {
if (m_pTilesets->m_pCurrent)
id = m_pTilesets->m_pCurrent->m_ids.items[i];
else
id = i;
btn = new GButton(m_pGrid, 1, 1, 40, 40, 0, halign_center|valign_center, i);
btn->SetPicture(m_pTilesets->m_tiletypes.m_pTT[id].pic);
btn->SetClickedColor(255, 0, 0);
btn->Clicked.Connect(this, &PickTileDlg::EndModal);
}
m_pGrid->Arrange(true, true);
}
void PickTileDlg::ChooseTileset(int id)
{
ChooseTilesetDlg dlg(this, m_pTilesets);
int code;
code = dlg.Run();
if (code >= 0)
m_pTilesets->SetTileset(code-1);
Refresh();
}
/*
==============================================================================
Editor
==============================================================================
*/
/*
==============
Editor::Tile_Pick
Open the pick tile dialog
==============
*/
void Editor::Tile_Pick()
{
lassert(m_iMode == tlm_tile);
int code;
PickTileDlg dlg(this, &m_tilesets);
code = dlg.Run();
if (code < 0)
return;
m_tilesets.SetCurIdx(code);
}