static void CampaignIntroDraw(void *data) { // This will only draw once const CampaignSetting *c = data; GraphicsBlitBkg(&gGraphicsDevice); const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; const int y = h / 4; // Display title + author char *buf; CMALLOC(buf, strlen(c->Title) + strlen(c->Author) + 16); sprintf(buf, "%s by %s", c->Title, c->Author); FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad.y = y - 25; FontStrOpt(buf, Vec2iZero(), opts); CFREE(buf); // Display campaign description // allow some slack for newlines if (strlen(c->Description) > 0) { CMALLOC(buf, strlen(c->Description) * 2); // Pad about 1/6th of the screen width total (1/12th left and right) FontSplitLines(c->Description, buf, w * 5 / 6); FontStr(buf, Vec2iNew(w / 12, y)); CFREE(buf); } }
bool ScreenMissionBriefing(const struct MissionOptions *m) { const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; const int y = h / 4; MissionBriefingData mData; memset(&mData, 0, sizeof mData); mData.IsOK = true; // Title CMALLOC(mData.Title, strlen(m->missionData->Title) + 32); sprintf(mData.Title, "Mission %d: %s", m->index + 1, m->missionData->Title); mData.TitleOpts = FontOptsNew(); mData.TitleOpts.HAlign = ALIGN_CENTER; mData.TitleOpts.Area = gGraphicsDevice.cachedConfig.Res; mData.TitleOpts.Pad.y = y - 25; // Password if (m->index > 0) { sprintf( mData.Password, "Password: %s", gAutosave.LastMission.Password); mData.PasswordOpts = FontOptsNew(); mData.PasswordOpts.HAlign = ALIGN_CENTER; mData.PasswordOpts.Area = gGraphicsDevice.cachedConfig.Res; mData.PasswordOpts.Pad.y = y - 15; } // Split the description, and prepare it for typewriter effect mData.TypewriterCount = 0; // allow some slack for newlines CMALLOC(mData.Description, strlen(m->missionData->Description) * 2 + 1); CCALLOC(mData.TypewriterBuf, strlen(m->missionData->Description) * 2 + 1); // Pad about 1/6th of the screen width total (1/12th left and right) FontSplitLines(m->missionData->Description, mData.Description, w * 5 / 6); mData.DescriptionPos = Vec2iNew(w / 12, y); // Objectives mData.ObjectiveDescPos = Vec2iNew(w / 6, y + FontStrH(mData.Description) + h / 10); mData.ObjectiveInfoPos = Vec2iNew(w - (w / 6), mData.ObjectiveDescPos.y + FontH()); mData.ObjectiveHeight = h / 12; mData.MissionOptions = m; GameLoopData gData = GameLoopDataNew( &mData, MissionBriefingUpdate, &mData, MissionBriefingDraw); GameLoop(&gData); if (mData.IsOK) { SoundPlay(&gSoundDevice, StrSound("mg")); } CFREE(mData.Title); CFREE(mData.Description); CFREE(mData.TypewriterBuf); return mData.IsOK; }
void DrawBufferInit(DrawBuffer *b, Vec2i size, GraphicsDevice *g) { debug(D_MAX, "Initialising draw buffer %dx%d\n", size.x, size.y); b->OrigSize = size; CMALLOC(b->tiles, size.x * sizeof *b->tiles); CMALLOC(b->tiles[0], size.x * size.y * sizeof *b->tiles[0]); for (int i = 1; i < size.x; i++) { b->tiles[i] = b->tiles[0] + i * size.y; } b->g = g; debug(D_MAX, "Initialised draw buffer %dx%d\n", size.x, size.y); }
void DrawBufferInit(DrawBuffer *b, Vec2i size, GraphicsDevice *g) { debug(D_MAX, "Initialising draw buffer %dx%d\n", size.x, size.y); b->OrigSize = size; CMALLOC(b->tiles, size.x * sizeof *b->tiles); CMALLOC(b->tiles[0], size.x * size.y * sizeof *b->tiles[0]); for (int i = 1; i < size.x; i++) { b->tiles[i] = b->tiles[0] + i * size.y; } b->g = g; CArrayInit(&b->displaylist, sizeof(TTileItem *)); CArrayReserve(&b->displaylist, 32); debug(D_MAX, "Initialised draw buffer %dx%d\n", size.x, size.y); }
void PicLoad( Pic *p, const Vec2i size, const Vec2i offset, const SDL_Surface *image, const SDL_Surface *s) { p->size = size; p->offset = Vec2iZero(); CMALLOC(p->Data, size.x * size.y * sizeof *((Pic *)0)->Data); // Manually copy the pixels and replace the alpha component, // since our gfx device format has no alpha int srcI = offset.y*image->w + offset.x; for (int i = 0; i < size.x * size.y; i++, srcI++) { const Uint32 alpha = ((Uint32 *)image->pixels)[srcI] >> image->format->Ashift; // If completely transparent, replace rgb with black (0) too // This is because transparency blitting checks entire pixel if (alpha == 0) { p->Data[i] = 0; } else { const Uint32 pixel = ((Uint32 *)s->pixels)[srcI]; const Uint32 rgbMask = s->format->Rmask | s->format->Gmask | s->format->Bmask; p->Data[i] = (pixel & rgbMask) | (alpha << gGraphicsDevice.Ashift); } if ((i + 1) % size.x == 0) { srcI += image->w - size.x; } } }
static UIObject *CreateSetKeyObjs(Vec2i pos, EditorBrush *brush) { UIObject *o2; UIObject *c = UIObjectCreate(UITYPE_CONTEXT_MENU, 0, pos, Vec2iZero()); UIObject *o = UIObjectCreate( UITYPE_CUSTOM, 0, Vec2iZero(), Vec2iNew(TILE_WIDTH + 4, TILE_HEIGHT + 4)); o->ChangeFunc = BrushSetBrushTypeSetKey; o->u.CustomDrawFunc = DrawKey; o->OnFocusFunc = ActivateIndexedEditorBrush; o->OnUnfocusFunc = DeactivateIndexedEditorBrush; pos = Vec2iZero(); for (int i = -1; i < KEY_COUNT; i++) { o2 = UIObjectCopy(o); o2->IsDynamicData = 1; CMALLOC(o2->Data, sizeof(IndexedEditorBrush)); ((IndexedEditorBrush *)o2->Data)->Brush = brush; ((IndexedEditorBrush *)o2->Data)->ItemIndex = i; o2->Pos = pos; if (i == -1) { // -1 means no key CSTRDUP(o2->Tooltip, "no key"); } UIObjectAddChild(c, o2); pos.x += o->Size.x; } UIObjectDestroy(o); return c; }
void PicLoad( Pic *p, const Vec2i size, const Vec2i offset, const SDL_Surface *image) { p->size = size; p->offset = Vec2iZero(); CMALLOC(p->Data, size.x * size.y * sizeof *((Pic *)0)->Data); // Manually copy the pixels and replace the alpha component, // since our gfx device format has no alpha int srcI = offset.y*image->w + offset.x; for (int i = 0; i < size.x * size.y; i++, srcI++) { const Uint32 pixel = ((Uint32 *)image->pixels)[srcI]; color_t c; SDL_GetRGBA(pixel, image->format, &c.r, &c.g, &c.b, &c.a); // If completely transparent, replace rgb with black (0) too // This is because transparency blitting checks entire pixel if (c.a == 0) { p->Data[i] = 0; } else { p->Data[i] = COLOR2PIXEL(c); } if ((i + 1) % size.x == 0) { srcI += image->w - size.x; } } }
static UIObject *CreateAddKeyObjs(Vec2i pos, EditorBrush *brush) { UIObject *o2; UIObject *c = UIObjectCreate(UITYPE_CONTEXT_MENU, 0, pos, Vec2iZero()); UIObject *o = UIObjectCreate( UITYPE_CUSTOM, 0, Vec2iZero(), Vec2iNew(TILE_WIDTH + 4, TILE_HEIGHT + 4)); o->ChangeFunc = BrushSetBrushTypeAddKey; o->u.CustomDrawFunc = DrawKey; o->OnFocusFunc = ActivateIndexedEditorBrush; o->OnUnfocusFunc = DeactivateIndexedEditorBrush; pos = Vec2iZero(); int width = 4; for (int i = 0; i < KEY_COUNT; i++) { o2 = UIObjectCopy(o); o2->IsDynamicData = 1; CMALLOC(o2->Data, sizeof(IndexedEditorBrush)); ((IndexedEditorBrush *)o2->Data)->Brush = brush; ((IndexedEditorBrush *)o2->Data)->u.ItemIndex = i; o2->Pos = pos; UIObjectAddChild(c, o2); pos.x += o->Size.x; if (((i + 1) % width) == 0) { pos.x = 0; pos.y += o->Size.y; } } UIObjectDestroy(o); return c; }
static PlayerList *PlayerListNew( GameLoopResult (*updateFunc)(GameLoopData *, LoopRunner *), void (*drawFunc)(void *), void *data, const bool hasMenu, const bool showWinners) { PlayerList *pl; CMALLOC(pl, sizeof *pl); pl->pos = svec2i_zero(); pl->size = gGraphicsDevice.cachedConfig.Res; pl->scroll = 0; pl->updateFunc = updateFunc; pl->drawFunc = drawFunc; pl->data = data; pl->hasMenu = hasMenu; pl->showWinners = showWinners; CArrayInit(&pl->playerUIDs, sizeof(int)); // Collect all players, then order by score descending int playersAlive = 0; CA_FOREACH(const PlayerData, p, gPlayerDatas) CArrayPushBack(&pl->playerUIDs, &p->UID); if (p->Lives > 0) { playersAlive++; } CA_FOREACH_END() qsort( pl->playerUIDs.data, pl->playerUIDs.size, pl->playerUIDs.elemSize, ComparePlayerScores); pl->showLastMan = playersAlive == 1; return pl; }
void AddSong(struct SongDef **songList, const char *path) { struct SongDef *s; CMALLOC(s, sizeof(struct SongDef)); strcpy(s->path, path); s->next = *songList; *songList = s; }
Pic PicCopy(const Pic *src) { Pic p = *src; const size_t size = p.size.x * p.size.y * sizeof *p.Data; CMALLOC(p.Data, size); memcpy(p.Data, src->Data, size); return p; }
menu_t *MenuCreate(const char *name, menu_type_e type) { menu_t *menu; CMALLOC(menu, sizeof(menu_t)); strcpy(menu->name, name); menu->type = type; menu->parentMenu = NULL; return menu; }
UDPpacket NetInputNewPacket(NetInputChannel *n, size_t len) { UDPpacket packet; SDLNet_Write32(n->otherHost, &packet.address.host); SDLNet_Write16(n->otherPort, &packet.address.port); packet.maxlen = packet.len = len; CMALLOC(packet.data, len); return packet; }
void PicTrim(Pic *pic, const bool xTrim, const bool yTrim) { // Scan all pixels looking for the min/max of x and y struct vec2i min = pic->size; struct vec2i max = svec2i_zero(); for (struct vec2i pos = svec2i_zero(); pos.y < pic->size.y; pos.y++) { for (pos.x = 0; pos.x < pic->size.x; pos.x++) { const Uint32 pixel = *(pic->Data + pos.x + pos.y * pic->size.x); if (pixel > 0) { min.x = MIN(min.x, pos.x); min.y = MIN(min.y, pos.y); max.x = MAX(max.x, pos.x); max.y = MAX(max.y, pos.y); } } } // If no opaque pixels found, don't trim struct vec2i newSize = pic->size; struct vec2i offset = svec2i_zero(); if (min.x < max.x && min.y < max.y) { if (xTrim) { newSize.x = max.x - min.x + 1; offset.x = min.x; } if (yTrim) { newSize.y = max.y - min.y + 1; offset.y = min.y; } } // Trim by copying pixels Uint32 *newData; CMALLOC(newData, newSize.x * newSize.y * sizeof *newData); for (struct vec2i pos = svec2i_zero(); pos.y < newSize.y; pos.y++) { for (pos.x = 0; pos.x < newSize.x; pos.x++) { Uint32 *target = newData + pos.x + pos.y * newSize.x; const int srcIdx = pos.x + offset.x + (pos.y + offset.y) * pic->size.x; *target = *(pic->Data + srcIdx); } } // Replace the old data CFREE(pic->Data); pic->Data = newData; pic->size = newSize; pic->offset = svec2i_zero(); PicTryMakeTex(pic); }
static UIObject *CreateAddMapItemObjsImpl( struct vec2i pos, CreateAddMapItemObjsImplData data) { UIObject *c = UIObjectCreate(UITYPE_CONTEXT_MENU, 0, pos, svec2i_zero()); c->OnFocusFunc = CreateAddMapItemSubObjs; c->IsDynamicData = true; CMALLOC(c->Data, sizeof(CreateAddMapItemObjsImplData)); memcpy(c->Data, &data, sizeof data); return c; }
ASPath ASPathCopy(ASPath path) { if (path) { const size_t size = sizeof(struct __ASPath) + (path->count * path->nodeSize); ASPath newPath; CMALLOC(newPath, size); memcpy(newPath, path, size); return newPath; } else { return NULL; } }
static UIObject *CreateAddCharacterObjs( Vec2i pos, EditorBrush *brush, CampaignOptions *co) { UIObject *c = UIObjectCreate(UITYPE_CONTEXT_MENU, 0, pos, Vec2iZero()); // Need to update UI objects dynamically as new characters can be // added and removed c->OnFocusFunc = CreateAddCharacterSubObjs; c->IsDynamicData = 1; CMALLOC(c->Data, sizeof(EditorBrushAndCampaign)); ((EditorBrushAndCampaign *)c->Data)->Brush.Brush = brush; ((EditorBrushAndCampaign *)c->Data)->Campaign = co; return c; }
static void CreateAddCharacterSubObjs(UIObject *c, void *vData) { EditorBrushAndCampaign *data = vData; CharacterStore *store = &data->Campaign->Setting.characters; if (c->Children.size == store->OtherChars.size) { return; } // Recreate the child UI objects c->Highlighted = NULL; UIObject **objs = c->Children.data; for (int i = 0; i < (int)c->Children.size; i++, objs++) { UIObjectDestroy(*objs); } CArrayTerminate(&c->Children); CArrayInit(&c->Children, sizeof c); UIObject *o = UIObjectCreate( UITYPE_CUSTOM, 0, Vec2iZero(), Vec2iNew(TILE_WIDTH + 4, TILE_HEIGHT * 2 + 4)); o->ChangeFunc = BrushSetBrushTypeAddCharacter; o->u.CustomDrawFunc = DrawCharacter; o->OnFocusFunc = ActivateEditorBrushAndCampaignBrush; o->OnUnfocusFunc = DeactivateEditorBrushAndCampaignBrush; Vec2i pos = Vec2iZero(); int width = 8; for (int i = 0; i < (int)store->OtherChars.size; i++) { UIObject *o2 = UIObjectCopy(o); Character *ch = CArrayGet(&store->OtherChars, i); CSTRDUP(o2->Tooltip, ch->Gun->name); o2->IsDynamicData = 1; CMALLOC(o2->Data, sizeof(EditorBrushAndCampaign)); ((EditorBrushAndCampaign *)o2->Data)->Brush.Brush = data->Brush.Brush; ((EditorBrushAndCampaign *)o2->Data)->Campaign = data->Campaign; ((EditorBrushAndCampaign *)o2->Data)->Brush.u.ItemIndex = i; o2->Pos = pos; UIObjectAddChild(c, o2); pos.x += o->Size.x; if (((i + 1) % width) == 0) { pos.x = 0; pos.y += o->Size.y; } } UIObjectDestroy(o); }
void PicFromPicPaletted(GraphicsDevice *g, Pic *pic, PicPaletted *picP) { pic->size = Vec2iNew(picP->w, picP->h); pic->offset = Vec2iZero(); CMALLOC(pic->Data, pic->size.x * pic->size.y * sizeof *pic->Data); for (int i = 0; i < pic->size.x * pic->size.y; i++) { unsigned char palette = *(picP->data + i); pic->Data[i] = PixelFromColor(g, PaletteToColor(palette)); // Special case: if the palette colour is 0, it's transparent if (palette == 0) { pic->Data[i] = 0; } } }
static UIObject *CreateAddObjectiveObjs( Vec2i pos, EditorBrush *brush, CampaignOptions *co) { UIObject *c = UIObjectCreate(UITYPE_CONTEXT_MENU, 0, pos, Vec2iZero()); // Need to update UI objects dynamically as new objectives can be // added and removed c->OnFocusFunc = CreateAddObjectiveSubObjs; CSTRDUP(c->Tooltip, "Manually place objectives\nThe rest will be randomly placed"); c->IsDynamicData = 1; CMALLOC(c->Data, sizeof(EditorBrushAndCampaign)); ((EditorBrushAndCampaign *)c->Data)->Brush.Brush = brush; ((EditorBrushAndCampaign *)c->Data)->Campaign = co; return c; }
GameLoopData *ScreenVictory(CampaignOptions *c) { SoundPlay(&gSoundDevice, StrSound("victory")); VictoryData *data; CMALLOC(data, sizeof *data); data->Campaign = c; const char *finalWordsSingle[] = { "Ha, next time I'll use my good hand", "Over already? I was just warming up...", "There's just no good opposition to be found these days!", "Well, maybe I'll just do my monthly reload then", "Woof woof", "I'll just bury the bones in the back yard, he-he", "I just wish they'd let me try bare-handed", "Rambo? Who's Rambo?", "<in Austrian accent:> I'll be back", "Gee, my trigger finger is sore", "I need more practice. I think I missed a few shots at times" }; const char *finalWordsMulti[] = { "United we stand, divided we conquer", "Nothing like good teamwork, is there?", "Which way is the camera?", "We eat bullets for breakfast and have grenades as dessert", "We're so cool we have to wear mittens", }; if (GetNumPlayers(PLAYER_ANY, false, true) == 1) { const int numWords = sizeof finalWordsSingle / sizeof(char *); data->FinalWords = finalWordsSingle[rand() % numWords]; } else { const int numWords = sizeof finalWordsMulti / sizeof(char *); data->FinalWords = finalWordsMulti[rand() % numWords]; } PlayerList *pl = PlayerListNew( PlayerListUpdate, VictoryDraw, data, true, false); pl->pos.y = 75; pl->size.y -= pl->pos.y; return PlayerListLoop(pl); }
static bool ScreenWait( const char *message, void (*checkFunc)(menu_t *, void *), void *data) { MenuSystem ms; MenuSystemInit( &ms, &gEventHandlers, &gGraphicsDevice, Vec2iZero(), gGraphicsDevice.cachedConfig.Res); ms.allowAborts = true; ms.root = ms.current = MenuCreateNormal("", message, MENU_TYPE_NORMAL, 0); MenuAddExitType(&ms, MENU_TYPE_RETURN); ScreenWaitData *swData; CMALLOC(swData, sizeof *swData); swData->ms = &ms; swData->data = data; MenuSetPostUpdateFunc(ms.root, checkFunc, swData, true); MenuLoop(&ms); const bool ok = !ms.hasAbort; MenuSystemTerminate(&ms); return ok; }
static UIObject *CreateAddWreckObjs(Vec2i pos, EditorBrush *brush) { UIObject *o2; UIObject *c = UIObjectCreate(UITYPE_CONTEXT_MENU, 0, pos, Vec2iZero()); UIObject *o = UIObjectCreate( UITYPE_CUSTOM, 0, Vec2iZero(), Vec2iNew(TILE_WIDTH + 4, TILE_HEIGHT + 4)); o->ChangeFunc = BrushSetBrushTypeAddWreck; o->u.CustomDrawFunc = DrawWreck; o->OnFocusFunc = ActivateIndexedEditorBrush; o->OnUnfocusFunc = DeactivateIndexedEditorBrush; pos = Vec2iZero(); const int width = 8; for (int i = 0; i < (int)gMapObjects.Destructibles.size; i++) { o2 = UIObjectCopy(o); o2->IsDynamicData = 1; CMALLOC(o2->Data, sizeof(IndexedEditorBrush)); ((IndexedEditorBrush *)o2->Data)->Brush = brush; ((IndexedEditorBrush *)o2->Data)->u.ItemIndex = i; o2->Pos = pos; UIObjectAddChild(c, o2); pos.x += o->Size.x; if (((i + 1) % width) == 0) { pos.x = 0; pos.y += o->Size.y; } const char **name = CArrayGet(&gMapObjects.Destructibles, i); const MapObject *mo = StrMapObject(*name); CSTRDUP(o2->Tooltip, mo->Name); } UIObjectDestroy(o); return c; }
ASPath ASPathCreate(const ASPathNodeSource *source, void *context, void *startNodeKey, void *goalNodeKey) { VisitedNodes visitedNodes; ASNeighborList neighborList; Node current; Node goalNode; ASPath path = NULL; if (!startNodeKey || !source || !source->nodeNeighbors || source->nodeSize == 0) { return NULL; } visitedNodes = VisitedNodesCreate(source, context); neighborList = NeighborListCreate(source); current = GetNode(visitedNodes, startNodeKey); goalNode = GetNode(visitedNodes, goalNodeKey); // mark the goal node as the goal SetNodeIsGoal(goalNode); // set the starting node's estimate cost to the goal and add it to the open set SetNodeEstimatedCost(current, GetPathCostHeuristic(current, goalNode)); AddNodeToOpenSet(current, 0, NodeNull); // perform the A* algorithm while (HasOpenNode(visitedNodes) && !NodeIsGoal((current = GetOpenNode(visitedNodes)))) { size_t n; if (source->earlyExit) { const int shouldExit = source->earlyExit(visitedNodes->nodeRecordsCount, GetNodeKey(current), goalNodeKey, context); if (shouldExit > 0) { SetNodeIsGoal(current); break; } else if (shouldExit < 0) { break; } } RemoveNodeFromOpenSet(current); AddNodeToClosedSet(current); neighborList->count = 0; source->nodeNeighbors(neighborList, GetNodeKey(current), context); for (n=0; n<neighborList->count; n++) { const float cost = GetNodeCost(current) + NeighborListGetEdgeCost(neighborList, n); Node neighbor = GetNode(visitedNodes, NeighborListGetNodeKey(neighborList, n)); if (!NodeHasEstimatedCost(neighbor)) { SetNodeEstimatedCost(neighbor, GetPathCostHeuristic(neighbor, goalNode)); } if (NodeIsInOpenSet(neighbor) && cost < GetNodeCost(neighbor)) { RemoveNodeFromOpenSet(neighbor); } if (NodeIsInClosedSet(neighbor) && cost < GetNodeCost(neighbor)) { RemoveNodeFromClosedSet(neighbor); } if (!NodeIsInOpenSet(neighbor) && !NodeIsInClosedSet(neighbor)) { AddNodeToOpenSet(neighbor, cost, current); } } } if (NodeIsNull(goalNode)) { SetNodeIsGoal(current); } if (NodeIsGoal(current)) { size_t count = 0; Node n = current; size_t i; while (!NodeIsNull(n)) { count++; n = GetParentNode(n); } CMALLOC(path, sizeof(struct __ASPath) + (count * source->nodeSize)); path->nodeSize = source->nodeSize; path->count = count; path->cost = GetNodeCost(current); n = current; for (i=count; i>0; i--) { memcpy(path->nodeKeys + ((i - 1) * source->nodeSize), GetNodeKey(n), source->nodeSize); n = GetParentNode(n); } } NeighborListDestroy(neighborList); VisitedNodesDestroy(visitedNodes); return path; }
void WeaponMenuCreate( WeaponMenu *menu, int numPlayers, int player, const int playerUID, EventHandlers *handlers, GraphicsDevice *graphics) { MenuSystem *ms = &menu->ms; WeaponMenuData *data = &menu->data; Vec2i pos, size; int w = graphics->cachedConfig.Res.x; int h = graphics->cachedConfig.Res.y; data->display.PlayerUID = playerUID; data->display.currentMenu = &ms->current; data->display.Dir = DIRECTION_DOWN; data->PlayerUID = playerUID; switch (numPlayers) { case 1: // Single menu, entire screen pos = Vec2iNew(w / 2, 0); size = Vec2iNew(w / 2, h); break; case 2: // Two menus, side by side pos = Vec2iNew(player * w / 2 + w / 4, 0); size = Vec2iNew(w / 4, h); break; case 3: case 4: // Four corners pos = Vec2iNew((player & 1) * w / 2 + w / 4, (player / 2) * h / 2); size = Vec2iNew(w / 4, h / 2); break; default: CASSERT(false, "not implemented"); pos = Vec2iNew(w / 2, 0); size = Vec2iNew(w / 2, h); break; } MenuSystemInit(ms, handlers, graphics, pos, size); ms->align = MENU_ALIGN_LEFT; ms->root = ms->current = MenuCreateNormal( "", "", MENU_TYPE_NORMAL, 0); ms->root->u.normal.maxItems = 11; const CArray *weapons = &gMission.Weapons; for (int i = 0; i < (int)weapons->size; i++) { const GunDescription **g = CArrayGet(weapons, i); menu_t *gunMenu; if ((*g)->Description != NULL) { // Gun description menu gunMenu = MenuCreateNormal((*g)->name, "", MENU_TYPE_NORMAL, 0); char *buf; CMALLOC(buf, strlen((*g)->Description) * 2); FontSplitLines((*g)->Description, buf, size.x * 5 / 6); MenuAddSubmenu(gunMenu, MenuCreateBack(buf)); CFREE(buf); gunMenu->u.normal.isSubmenusAlt = true; MenuSetCustomDisplay(gunMenu, DisplayDescriptionGunIcon, *g); } else { gunMenu = MenuCreate((*g)->name, MENU_TYPE_BASIC); } MenuAddSubmenu(ms->root, gunMenu); } MenuSetPostInputFunc(ms->root, WeaponSelect, &data->display); // Disable menu items where the player already has the weapon PlayerData *pData = PlayerDataGetByUID(playerUID); for (int i = 0; i < pData->weaponCount; i++) { for (int j = 0; j < (int)weapons->size; j++) { const GunDescription **g = CArrayGet(weapons, j); if (pData->weapons[i] == *g) { MenuDisableSubmenu(ms->root, j); } } } MenuAddSubmenu(ms->root, MenuCreateSeparator("")); MenuAddSubmenu( ms->root, MenuCreateNormal("(End)", "", MENU_TYPE_NORMAL, 0)); // Select "(End)" ms->root->u.normal.index = (int)ms->root->u.normal.subMenus.size - 1; // Disable "Done" if no weapons selected if (pData->weaponCount == 0) { MenuDisableSubmenu(ms->root, (int)ms->root->u.normal.subMenus.size - 1); } MenuSetCustomDisplay(ms->root, DisplayGunIcon, NULL); MenuSystemAddCustomDisplay(ms, MenuDisplayPlayer, &data->display); MenuSystemAddCustomDisplay(ms, DisplayEquippedWeapons, data); MenuSystemAddCustomDisplay( ms, MenuDisplayPlayerControls, &data->PlayerUID); }
void SetupQuickPlayCampaign(CampaignSetting *setting) { Mission *m; CMALLOC(m, sizeof *m); MissionInit(m); m->WallStyle = rand() % WALL_STYLE_COUNT; m->FloorStyle = rand() % FLOOR_STYLE_COUNT; m->RoomStyle = rand() % FLOOR_STYLE_COUNT; m->ExitStyle = rand() % GetExitCount(); m->KeyStyle = rand() % KEYSTYLE_COUNT; strcpy( m->DoorStyle, DoorStyleStr(rand() % gPicManager.doorStyleNames.size)); m->Size = GenerateQuickPlayMapSize( ConfigGetEnum(&gConfig, "QuickPlay.MapSize")); m->Type = MAPTYPE_CLASSIC; // TODO: generate different map types switch (m->Type) { case MAPTYPE_CLASSIC: m->u.Classic.Walls = GenerateQuickPlayParam( ConfigGetEnum(&gConfig, "QuickPlay.WallCount"), 0, 5, 15, 30); m->u.Classic.WallLength = GenerateQuickPlayParam( ConfigGetEnum(&gConfig, "QuickPlay.WallLength"), 1, 3, 6, 12); m->u.Classic.CorridorWidth = rand() % 3 + 1; m->u.Classic.Rooms.Count = GenerateQuickPlayParam( ConfigGetEnum(&gConfig, "QuickPlay.RoomCount"), 0, 2, 5, 12); m->u.Classic.Rooms.Min = rand() % 10 + 5; m->u.Classic.Rooms.Max = rand() % 10 + m->u.Classic.Rooms.Min; m->u.Classic.Rooms.Edge = 1; m->u.Classic.Rooms.Overlap = 1; m->u.Classic.Rooms.Walls = rand() % 5; m->u.Classic.Rooms.WallLength = rand() % 6 + 1; m->u.Classic.Rooms.WallPad = rand() % 4 + 1; m->u.Classic.Squares = GenerateQuickPlayParam( ConfigGetEnum(&gConfig, "QuickPlay.SquareCount"), 0, 1, 3, 6); m->u.Classic.Doors.Enabled = rand() % 2; m->u.Classic.Doors.Min = 1; m->u.Classic.Doors.Max = 6; m->u.Classic.Pillars.Count = rand() % 5; m->u.Classic.Pillars.Min = rand() % 3 + 1; m->u.Classic.Pillars.Max = rand() % 3 + m->u.Classic.Pillars.Min; break; default: assert(0 && "unknown map type"); break; } CharacterStoreTerminate(&setting->characters); CharacterStoreInit(&setting->characters); int c = GenerateQuickPlayParam( ConfigGetEnum(&gConfig, "QuickPlay.EnemyCount"), 3, 5, 8, 12); SetupQuickPlayEnemies(m, c, &setting->characters); c = GenerateQuickPlayParam( ConfigGetEnum(&gConfig, "QuickPlay.ItemCount"), 0, 2, 5, 10); for (int i = 0; i < c; i++) { MapObjectDensity mop; mop.M = IndexMapObject(rand() % MapObjectsCount(&gMapObjects)); mop.Density = GenerateQuickPlayParam( ConfigGetEnum(&gConfig, "QuickPlay.ItemCount"), 0, 5, 10, 20); CArrayPushBack(&m->MapObjectDensities, &mop); } m->EnemyDensity = (40 + (rand() % 20)) / m->Enemies.size; CA_FOREACH(const GunDescription, g, gGunDescriptions.Guns) if (g->IsRealGun) { CArrayPushBack(&m->Weapons, &g); } CA_FOREACH_END() m->WallMask = RandomBGColor(); m->FloorMask = RandomBGColor(); m->RoomMask = RandomBGColor(); m->AltMask = RandomBGColor(); CFREE(setting->Title); CSTRDUP(setting->Title, "Quick play"); CFREE(setting->Author); CSTRDUP(setting->Author, ""); CFREE(setting->Description); CSTRDUP(setting->Description, ""); CArrayPushBack(&setting->Missions, m); CFREE(m); }
static void CreateAddObjectiveSubObjs(UIObject *c, void *vData) { EditorBrushAndCampaign *data = vData; Mission *m = CampaignGetCurrentMission(data->Campaign); // Check if the data is still the same; if so don't recreate the // child UI objects // This is because during the course of UI operations, this element // could be highlighted again; if we recreate then we invalidate // UI pointers. bool needToRecreate = false; int childIndex = 0; CA_FOREACH(const Objective, obj, m->Objectives) int secondaryCount = 1; const CharacterStore *store = &data->Campaign->Setting.characters; switch (obj->Type) { case OBJECTIVE_KILL: secondaryCount = (int)store->specialIds.size; break; case OBJECTIVE_COLLECT: break; case OBJECTIVE_DESTROY: break; case OBJECTIVE_RESCUE: secondaryCount = (int)store->prisonerIds.size; break; default: continue; } for (int j = 0; j < (int)secondaryCount; j++) { if ((int)c->Children.size <= childIndex) { needToRecreate = true; break; } UIObject *o2 = *(UIObject **)CArrayGet(&c->Children, childIndex); if (((EditorBrushAndCampaign *)o2->Data)->Brush.u.ItemIndex != _ca_index || ((EditorBrushAndCampaign *)o2->Data)->Brush.Index2 != j) { needToRecreate = true; break; } childIndex++; } if (needToRecreate) { break; } CA_FOREACH_END() if (!needToRecreate) { return; } // Recreate the child UI objects c->Highlighted = NULL; CA_FOREACH(UIObject *, obj, c->Children) UIObjectDestroy(*obj); CA_FOREACH_END() CArrayTerminate(&c->Children); CArrayInit(&c->Children, sizeof c); UIObject *o = UIObjectCreate( UITYPE_CUSTOM, 0, Vec2iZero(), Vec2iNew(TILE_WIDTH + 4, TILE_HEIGHT * 2 + 4)); o->ChangeFunc = BrushSetBrushTypeAddObjective; o->u.CustomDrawFunc = DrawObjective; o->OnFocusFunc = ActivateEditorBrushAndCampaignBrush; o->OnUnfocusFunc = DeactivateEditorBrushAndCampaignBrush; Vec2i pos = Vec2iZero(); CA_FOREACH(const Objective, obj, m->Objectives) int secondaryCount = 1; const CharacterStore *store = &data->Campaign->Setting.characters; switch (obj->Type) { case OBJECTIVE_KILL: secondaryCount = (int)store->specialIds.size; o->Size.y = TILE_HEIGHT * 2 + 4; break; case OBJECTIVE_COLLECT: o->Size.y = TILE_HEIGHT + 4; break; case OBJECTIVE_DESTROY: o->Size.y = TILE_HEIGHT * 2 + 4; break; case OBJECTIVE_RESCUE: secondaryCount = (int)store->prisonerIds.size; o->Size.y = TILE_HEIGHT * 2 + 4; break; default: continue; } for (int j = 0; j < (int)secondaryCount; j++) { UIObject *o2 = UIObjectCopy(o); CSTRDUP(o2->Tooltip, ObjectiveTypeStr(obj->Type)); o2->IsDynamicData = true; CMALLOC(o2->Data, sizeof(EditorBrushAndCampaign)); ((EditorBrushAndCampaign *)o2->Data)->Brush.Brush = data->Brush.Brush; ((EditorBrushAndCampaign *)o2->Data)->Campaign = data->Campaign; ((EditorBrushAndCampaign *)o2->Data)->Brush.u.ItemIndex = _ca_index; ((EditorBrushAndCampaign *)o2->Data)->Brush.Index2 = j; o2->Pos = pos; UIObjectAddChild(c, o2); pos.x += o->Size.x; } pos.x = 0; pos.y += o->Size.y; CA_FOREACH_END() UIObjectDestroy(o); }
static void MenuDisplaySubmenus(const MenuSystem *ms) { int x = 0, yStart = 0; const menu_t *menu = ms->current; switch (menu->type) { // TODO: refactor the three menu types (normal, options, campaign) into one case MENU_TYPE_NORMAL: case MENU_TYPE_OPTIONS: { int iStart = 0; int iEnd = (int)menu->u.normal.subMenus.size; int numMenuLines = 0; int maxIEnd = (int)menu->u.normal.subMenus.size; if (menu->u.normal.maxItems > 0) { // Calculate first/last indices if (menu->u.normal.scroll != 0) { iStart = menu->u.normal.scroll; } maxIEnd = iStart + menu->u.normal.maxItems; } // Count the number of menu items that can fit // This is to account for multi-line items for (iEnd = iStart; iEnd < maxIEnd && iEnd < (int)menu->u.normal.subMenus.size; iEnd++) { const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, iEnd); const int numLines = FontStrNumLines(subMenu->name); if (menu->u.normal.maxItems > 0 && numMenuLines + numLines > menu->u.normal.maxItems) { break; } numMenuLines += numLines; } int maxWidth = 0; for (int i = 0; i < (int)menu->u.normal.subMenus.size; i++) { const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, i); const int width = FontStrW(subMenu->name); if (width > maxWidth) { maxWidth = width; } } // Limit max width if it is larger than the menu system size maxWidth = MIN(ms->size.x, maxWidth); const bool isCentered = menu->type == MENU_TYPE_NORMAL; switch (ms->align) { case MENU_ALIGN_CENTER: x = MS_CENTER_X(*ms, maxWidth); if (!isCentered) { x -= 20; } break; case MENU_ALIGN_LEFT: x = ms->pos.x; break; default: assert(0 && "unknown alignment"); break; } yStart = MS_CENTER_Y(*ms, numMenuLines * FontH()); if (menu->u.normal.maxItems > 0) { // Display scroll arrows if (menu->u.normal.scroll != 0) { DisplayMenuItem( Vec2iNew( MS_CENTER_X(*ms, FontW('^')), yStart - 2 - FontH()), "^", 0, 0, colorBlack); } if (iEnd < (int)menu->u.normal.subMenus.size - 1) { DisplayMenuItem( Vec2iNew( MS_CENTER_X(*ms, FontW('v')), yStart + numMenuLines*FontH() + 2), "v", 0, 0, colorBlack); } } const int xOptions = x + maxWidth + 10; // Display normal menu items Vec2i pos = Vec2iNew(x, yStart); for (int i = iStart; i < iEnd; i++) { const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, i); char *nameBuf; CMALLOC(nameBuf, strlen(subMenu->name) + 3); if (subMenu->type == MENU_TYPE_NORMAL && subMenu->u.normal.isSubmenusAlt) { sprintf(nameBuf, "%s >", subMenu->name); } else { strcpy(nameBuf, subMenu->name); } switch (menu->u.normal.align) { case MENU_ALIGN_CENTER: pos.x = MS_CENTER_X(*ms, FontStrW(nameBuf)); break; case MENU_ALIGN_LEFT: // Do nothing break; default: assert(0 && "unknown alignment"); break; } const int yNext = DisplayMenuItem( pos, nameBuf, i == menu->u.normal.index, subMenu->isDisabled, subMenu->color).y + FontH(); // display option value if (subMenu->type == MENU_TYPE_SET_OPTION_TOGGLE || subMenu->type == MENU_TYPE_SET_OPTION_RANGE || subMenu->type == MENU_TYPE_SET_OPTION_SEED || subMenu->type == MENU_TYPE_SET_OPTION_UP_DOWN_VOID_FUNC_VOID || subMenu->type == MENU_TYPE_SET_OPTION_RANGE_GET_SET) { const int optionInt = MenuOptionGetIntValue(subMenu); const Vec2i value_pos = Vec2iNew(xOptions, pos.y); switch (subMenu->u.option.displayStyle) { case MENU_OPTION_DISPLAY_STYLE_INT: { char buf[32]; sprintf(buf, "%d", optionInt); FontStr(buf, value_pos); } break; case MENU_OPTION_DISPLAY_STYLE_YES_NO: FontStr(optionInt ? "Yes" : "No", value_pos); break; case MENU_OPTION_DISPLAY_STYLE_ON_OFF: FontStr(optionInt ? "On" : "Off", value_pos); break; case MENU_OPTION_DISPLAY_STYLE_STR_FUNC: FontStr(subMenu->u.option.uFunc.str(), value_pos); break; case MENU_OPTION_DISPLAY_STYLE_INT_TO_STR_FUNC: FontStr( subMenu->u.option.uFunc.intToStr(optionInt), value_pos); break; default: break; } } pos.y = yNext; } } break; case MENU_TYPE_KEYS: { int xKeys; x = MS_CENTER_X(*ms, (FontW('a') * 10)) / 2; xKeys = x * 3; yStart = (gGraphicsDevice.cachedConfig.Res.y / 2) - (FontH() * 10); for (int i = 0; i < (int)menu->u.normal.subMenus.size; i++) { int y = yStart + i * FontH(); int isSelected = i == menu->u.normal.index; const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, i); const char *name = subMenu->name; if (isSelected && subMenu->type != MENU_TYPE_SET_OPTION_CHANGE_KEY) { FontStrMask(name, Vec2iNew(x, y), colorRed); } else { FontStr(name, Vec2iNew(x, y)); } if (subMenu->type == MENU_TYPE_SET_OPTION_CHANGE_KEY) { const char *keyName; if (menu->u.normal.changeKeyMenu == subMenu) { keyName = "Press a key"; } else if (subMenu->u.changeKey.code == KEY_CODE_MAP) { keyName = SDL_GetKeyName(gConfig.Input.PlayerKeys[0].Keys.map); } else { keyName = SDL_GetKeyName(InputGetKey( subMenu->u.changeKey.keys, subMenu->u.changeKey.code)); } DisplayMenuItem( Vec2iNew(xKeys, y), keyName, isSelected, 0, colorBlack); } } } break; default: // No submenus, don't display anything break; } }