ui_node_base.c

Go to the documentation of this file.
00001 
00005 /*
00006 Copyright (C) 2002-2010 UFO: Alien Invasion.
00007 
00008 This program is free software; you can redistribute it and/or
00009 modify it under the terms of the GNU General Public License
00010 as published by the Free Software Foundation; either version 2
00011 of the License, or (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016 
00017 See the GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License
00020 along with this program; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022 
00023 */
00024 
00025 #include "../ui_main.h"
00026 #include "../ui_parse.h"
00027 #include "../ui_tooltip.h"
00028 #include "../ui_nodes.h"
00029 #include "../ui_render.h"
00030 #include "ui_node_base.h"
00031 #include "ui_node_abstractnode.h"
00032 
00033 #include "../../client.h"
00034 #include "../../campaign/cp_campaign.h"
00035 
00036 #define EXTRADATA_TYPE baseExtraData_t
00037 #define EXTRADATA(node) UI_EXTRADATA(node, baseExtraData_t)
00038 
00042 static void UI_AbstractBaseNodeLoaded (uiNode_t * node)
00043 {
00044     const int id = EXTRADATA(node).baseid;
00045     if (id < 0 || id >= MAX_BASES) {
00046         Com_Printf("UI_AbstractBaseNodeLoaded: Invalid baseid given %i", id);
00047     }
00048 }
00049 
00053 static void UI_BaseLayoutNodeDraw (uiNode_t * node)
00054 {
00055     base_t *base;
00056     int height, width, y;
00057     int row, col;
00058     const vec4_t c_gray = {0.5, 0.5, 0.5, 1.0};
00059     vec2_t nodepos;
00060     int totalMarge;
00061 
00062     if (EXTRADATA(node).baseid >= MAX_BASES || EXTRADATA(node).baseid < 0)
00063         return;
00064 
00065     totalMarge = node->padding * (BASE_SIZE + 1);
00066     width = (node->size[0] - totalMarge) / BASE_SIZE;
00067     height = (node->size[1] - totalMarge) / BASE_SIZE;
00068 
00069     UI_GetNodeAbsPos(node, nodepos);
00070 
00071     base = B_GetBaseByIDX(EXTRADATA(node).baseid);
00072 
00073     y = nodepos[1] + node->padding;
00074     for (row = 0; row < BASE_SIZE; row++) {
00075         int x = nodepos[0] + node->padding;
00076         for (col = 0; col < BASE_SIZE; col++) {
00077             if (base->map[row][col].blocked) {
00078                 UI_DrawFill(x, y, width, height, c_gray);
00079             } else if (base->map[row][col].building) {
00080                 /* maybe destroyed in the meantime */
00081                 if (base->founded)
00082                     UI_DrawFill(x, y, width, height, node->color);
00083             }
00084             x += width + node->padding;
00085         }
00086         y += height + node->padding;
00087     }
00088 }
00089 
00091 #define BASE_IMAGE_OVERLAY 20
00092 
00101 static void UI_BaseMapGetCellAtPos (const uiNode_t *node, int x, int y, int *col, int *row)
00102 {
00103     assert(col);
00104     assert(row);
00105     UI_NodeAbsoluteToRelativePos(node, &x, &y);
00106     if (x < 0 || y < 0 || x >= node->size[0] || y >= node->size[1]) {
00107         *col = -1;
00108         *row = -1;
00109         return;
00110     }
00111     *col = x / (node->size[0] / BASE_SIZE);
00112     *row = y / (node->size[0] / BASE_SIZE);
00113     assert(*col >= 0 && *col < BASE_SIZE);
00114     assert(*row >= 0 && *row < BASE_SIZE);
00115 }
00116 
00121 static inline qboolean UI_BaseMapIsCellFree (const base_t *base, int col, int row)
00122 {
00123     return col >= 0 && col < BASE_SIZE
00124      && row >= 0 && row < BASE_SIZE
00125      && base->map[row][col].building == NULL
00126      && !base->map[row][col].blocked;
00127 }
00128 
00132 static void UI_BaseMapNodeDraw (uiNode_t * node)
00133 {
00134     int width, height, row, col;
00135     char image[MAX_QPATH];      
00136     const building_t *building;
00137     const building_t *secondBuilding = NULL;
00138     const base_t *base = B_GetCurrentSelectedBase();
00139     qboolean used[MAX_BUILDINGS];
00140 
00141     if (!base) {
00142         UI_PopWindow(qfalse);
00143         return;
00144     }
00145 
00146     /* reset the used flag */
00147     memset(used, 0, sizeof(used));
00148 
00149     width = node->size[0] / BASE_SIZE;
00150     height = node->size[1] / BASE_SIZE + BASE_IMAGE_OVERLAY;
00151 
00152     for (row = 0; row < BASE_SIZE; row++) {
00153         for (col = 0; col < BASE_SIZE; col++) {
00154             vec2_t pos;
00155             UI_GetNodeAbsPos(node, pos);
00156             pos[0] += col * width;
00157             pos[1] += row * (height - BASE_IMAGE_OVERLAY);
00158 
00159             /* base tile */
00160             image[0] = '\0';
00161             if (base->map[row][col].blocked) {
00162                 building = NULL;
00163                 Q_strncpyz(image, "base/invalid", sizeof(image));
00164             } else if (!base->map[row][col].building) {
00165                 building = NULL;
00166                 Q_strncpyz(image, "base/grid", sizeof(image));
00167             } else {
00168                 building = base->map[row][col].building;
00169                 secondBuilding = NULL;
00170                 assert(building);
00171 
00172                 /* some buildings are drawn with two tiles - e.g. the hangar is no square map tile.
00173                  * These buildings have the needs parameter set to the second building part which has
00174                  * its own image set, too. We are searching for this second building part here. */
00175                 if (!B_BuildingGetUsed(used, building->idx)) {
00176                     if (building->needs)
00177                         B_BuildingSetUsed(used, building->idx);
00178                     if (building->image)
00179                         Q_strncpyz(image, building->image, sizeof(image));
00180                 } else {
00181                     secondBuilding = B_GetBuildingTemplate(building->needs);
00182                     if (!secondBuilding)
00183                         Com_Error(ERR_DROP, "Error in ufo-scriptfile - could not find the needed building");
00184                     Q_strncpyz(image, secondBuilding->image, sizeof(image));
00185                 }
00186             }
00187 
00188             /* draw tile */
00189             if (image[0] != '\0')
00190                 UI_DrawNormImageByName(pos[0], pos[1], width, height, 0, 0, 0, 0, image);
00191 
00192             /* only draw for first part of building */
00193             if (building && !secondBuilding) {
00194                 switch (building->buildingStatus) {
00195                 case B_STATUS_DOWN:
00196                 case B_STATUS_CONSTRUCTION_FINISHED:
00197                     break;
00198                 case B_STATUS_UNDER_CONSTRUCTION:
00199                 {
00200                     const int time = building->buildTime - (ccs.date.day - building->timeStart);
00201                     UI_DrawString("f_small", ALIGN_UL, pos[0] + 10, pos[1] + 10, pos[0] + 10, node->size[0], 0, va(ngettext("%i day left", "%i days left", time), time), 0, 0, NULL, qfalse, 0);
00202                     break;
00203                 }
00204                 default:
00205                     break;
00206                 }
00207             }
00208         }
00209     }
00210 
00211     if (!node->state)
00212         return;
00213 
00214     UI_BaseMapGetCellAtPos(node, mousePosX, mousePosY, &col, &row);
00215     if (col == -1)
00216         return;
00217 
00218     /* if we are building */
00219     if (ccs.baseAction == BA_NEWBUILDING) {
00220         qboolean isLarge;
00221         assert(base->buildingCurrent);
00223         if (!UI_BaseMapIsCellFree(base, col, row))
00224             return;
00225 
00226         isLarge = base->buildingCurrent->needs != NULL;
00227 
00228         /* large building */
00229         if (isLarge) {
00230             if (UI_BaseMapIsCellFree(base, col + 1, row)) {
00231                 /* ok */
00232             } else if (UI_BaseMapIsCellFree(base, col - 1, row)) {
00233                 /* fix col at the left cell */
00234                 col--;
00235             } else {
00236                 col = -1;
00237             }
00238         }
00239         if (col != -1) {
00240             vec2_t pos;
00241             UI_GetNodeAbsPos(node, pos);
00242             if (isLarge) {
00243                 UI_DrawNormImageByName(pos[0] + col * width, pos[1] + row * (height - BASE_IMAGE_OVERLAY), width + width, height, 0, 0, 0, 0, "base/hover2");
00244             } else
00245                 UI_DrawNormImageByName(pos[0] + col * width, pos[1] + row * (height - BASE_IMAGE_OVERLAY), width, height, 0, 0, 0, 0, "base/hover");
00246         }
00247     }
00248 }
00249 
00256 static void UI_BaseMapNodeDrawTooltip (uiNode_t *node, int x, int y)
00257 {
00258     int col, row;
00259     building_t *building;
00260     const int itemToolTipWidth = 250;
00261     char *tooltipText;
00262     base_t *base = B_GetCurrentSelectedBase();
00263 
00264     UI_BaseMapGetCellAtPos(node, x, y, &col, &row);
00265     if (col == -1)
00266         return;
00267 
00268     building = base->map[row][col].building;
00269     if (!building)
00270         return;
00271 
00272     tooltipText = _(building->name);
00273     if (!B_CheckBuildingDependencesStatus(base, building))
00274         tooltipText = va("%s\n%s %s", tooltipText, _("not operational, depends on"), _(building->dependsBuilding->name));
00275     UI_DrawTooltip(tooltipText, x, y, itemToolTipWidth);
00276 }
00277 
00285 static void UI_BaseMapNodeClick (uiNode_t *node, int x, int y)
00286 {
00287     int row, col;
00288     base_t *base = B_GetCurrentSelectedBase();
00289 
00290     assert(base);
00291     assert(node);
00292     assert(node->root);
00293 
00294     UI_BaseMapGetCellAtPos(node, x, y, &col, &row);
00295     if (col == -1)
00296         return;
00297 
00298     if (ccs.baseAction == BA_NEWBUILDING) {
00299         assert(base->buildingCurrent);
00300         if (!base->map[row][col].building && !base->map[row][col].blocked) {
00301             if (!base->buildingCurrent->needs
00302              || (col < BASE_SIZE - 1 && !base->map[row][col + 1].building && !base->map[row][col + 1].blocked)
00303              || (col > 0 && !base->map[row][col - 1].building && !base->map[row][col - 1].blocked))
00304             /* Set position for a new building */
00305             B_SetBuildingByClick(base, base->buildingCurrent, row, col);
00306         }
00307         return;
00308     }
00309 
00310     if (base->map[row][col].building) {
00311         const building_t *entry = base->map[row][col].building;
00312         if (!entry)
00313             Com_Error(ERR_DROP, "UI_BaseMapNodeClick: no entry at %i:%i", x, y);
00314 
00315         assert(!base->map[row][col].blocked);
00316 
00317         B_BuildingOpenAfterClick(base, entry);
00318         ccs.baseAction = BA_NONE;
00319         return;
00320     }
00321 }
00322 
00330 static void UI_BaseMapNodeRightClick (uiNode_t *node, int x, int y)
00331 {
00332     int row, col;
00333     base_t *base = B_GetCurrentSelectedBase();
00334 
00335     assert(base);
00336     assert(node);
00337     assert(node->root);
00338 
00339     UI_BaseMapGetCellAtPos(node, x, y, &col, &row);
00340     if (col == -1)
00341         return;
00342 
00343     if (base->map[row][col].building) {
00344         building_t *entry = base->map[row][col].building;
00345         if (!entry)
00346             Sys_Error("UI_BaseMapNodeRightClick: no entry at %i:%i\n", x, y);
00347 
00348         assert(!base->map[row][col].blocked);
00349         B_MarkBuildingDestroy(base, entry);
00350         return;
00351     }
00352 }
00353 
00362 static void UI_BaseMapNodeMiddleClick (uiNode_t *node, int x, int y)
00363 {
00364     int row, col;
00365     base_t *base = B_GetCurrentSelectedBase();
00366 
00367     assert(base);
00368     assert(node);
00369     assert(node->root);
00370 
00371     UI_BaseMapGetCellAtPos(node, x, y, &col, &row);
00372     if (col == -1)
00373         return;
00374 
00375     if (base->map[row][col].building) {
00376         building_t *entry = base->map[row][col].building;
00377         if (!entry)
00378             Com_Error(ERR_DROP, "UI_BaseMapNodeMiddleClick: no entry at %i:%i", x, y);
00379 
00380         assert(!base->map[row][col].blocked);
00381         B_DrawBuilding(base, entry);
00382         return;
00383     }
00384 }
00385 
00389 static void UI_BaseLayoutNodeLoading (uiNode_t *node)
00390 {
00391     node->padding = 3;
00392     Vector4Set(node->color, 1, 1, 1, 1);
00393 }
00394 
00395 static const value_t properties[] = {
00396     /* Identify the base, from a base ID, the node use. */
00397     {"baseid", V_INT, UI_EXTRADATA_OFFSETOF(baseExtraData_t, baseid), MEMBER_SIZEOF(baseExtraData_t, baseid)},
00398     {NULL, V_NULL, 0, 0}
00399 };
00400 
00401 void UI_RegisterAbstractBaseNode (uiBehaviour_t *behaviour)
00402 {
00403     behaviour->name = "abstractbase";
00404     behaviour->isAbstract = qtrue;
00405     behaviour->properties = properties;
00406     behaviour->loaded = UI_AbstractBaseNodeLoaded;
00407     behaviour->extraDataSize = sizeof(EXTRADATA_TYPE);
00408 }
00409 
00410 void UI_RegisterBaseMapNode (uiBehaviour_t *behaviour)
00411 {
00412     behaviour->name = "basemap";
00413     behaviour->extends = "abstractbase";
00414     behaviour->draw = UI_BaseMapNodeDraw;
00415     behaviour->leftClick = UI_BaseMapNodeClick;
00416     behaviour->rightClick = UI_BaseMapNodeRightClick;
00417     behaviour->drawTooltip = UI_BaseMapNodeDrawTooltip;
00418     behaviour->middleClick = UI_BaseMapNodeMiddleClick;
00419 }
00420 
00421 void UI_RegisterBaseLayoutNode (uiBehaviour_t *behaviour)
00422 {
00423     behaviour->name = "baselayout";
00424     behaviour->extends = "abstractbase";
00425     behaviour->draw = UI_BaseLayoutNodeDraw;
00426     behaviour->loading = UI_BaseLayoutNodeLoading;
00427 }

Generated by  doxygen 1.6.2