static void CampaignListTerminate(campaign_list_t *list) { CFREE(list->Name); CA_FOREACH(campaign_list_t, sublist, list->subFolders) CampaignListTerminate(sublist); CA_FOREACH_END() CArrayTerminate(&list->subFolders); CA_FOREACH(CampaignEntry, e, list->list) CampaignEntryTerminate(e); CA_FOREACH_END() CArrayTerminate(&list->list); }
void MapObjectsTerminate(MapObjects *classes) { MapObjectsClear(&classes->Classes); CArrayTerminate(&classes->Classes); MapObjectsClear(&classes->CustomClasses); CArrayTerminate(&classes->CustomClasses); CA_FOREACH(char *, s, classes->Destructibles) CFREE(*s); CA_FOREACH_END() CArrayTerminate(&classes->Destructibles); CA_FOREACH(char *, s, classes->Bloods) CFREE(*s); CA_FOREACH_END() CArrayTerminate(&classes->Bloods); }
void JoyTerminate(CArray *joys) { CA_FOREACH(Joystick, j, *joys) JoyTerminateOne(j); CA_FOREACH_END() CArrayTerminate(joys); }
void UIObjectDestroy(UIObject *o) { CFREE(o->Tooltip); CA_FOREACH(UIObject *, obj, o->Children) UIObjectDestroy(*obj); CA_FOREACH_END() CArrayTerminate(&o->Children); if (o->IsDynamicData) { CFREE(o->Data); } switch (o->Type) { case UITYPE_TEXTBOX: CFREE(o->u.Textbox.Hint); break; case UITYPE_TAB: CArrayTerminate(&o->u.Tab.Labels); break; default: // do nothing break; } CFREE(o); }
static void DrawDebris(DrawBuffer *b, struct vec2i offset) { Tile *tile = &b->tiles[0][0]; for (int y = 0; y < Y_TILES; y++) { CArrayClear(&b->displaylist); for (int x = 0; x < b->Size.x; x++, tile++) { if (tile->outOfSight) { continue; } CA_FOREACH(ThingId, tid, tile->things) const Thing *ti = ThingIdGetThing(tid); if (ThingDrawLast(ti)) { CArrayPushBack(&b->displaylist, &ti); } CA_FOREACH_END() } DrawBufferSortDisplayList(b); CA_FOREACH(const Thing *, tp, b->displaylist) DrawThing(b, *tp, offset); CA_FOREACH_END() tile += X_TILES - b->Size.x; } }
static GameLoopResult DogfightScoresUpdate(GameLoopData *data, LoopRunner *l) { PlayerList *pl = data->Data; const GameLoopResult result = MenuUpdate(&pl->ms); if (result == UPDATE_RESULT_OK) { // Calculate PVP rounds won int maxScore = 0; CA_FOREACH(PlayerData, p, gPlayerDatas) if (IsPlayerAlive(p)) { p->Totals.Score++; maxScore = MAX(maxScore, p->Totals.Score); } CA_FOREACH_END() gCampaign.IsComplete = maxScore == ModeMaxRoundsWon(gCampaign.Entry.Mode); CASSERT(maxScore <= ModeMaxRoundsWon(gCampaign.Entry.Mode), "score exceeds max rounds won"); if (gCampaign.IsComplete) { LoopRunnerChange(l, ScreenDogfightFinalScores()); } else { LoopRunnerChange( l, HighScoresScreen(&gCampaign, &gGraphicsDevice)); } }
void AutosaveTerminate(Autosave *autosave) { CA_FOREACH(MissionSave, m, autosave->Missions) CampaignEntryTerminate(&m->Campaign); CA_FOREACH_END() CArrayTerminate(&autosave->Missions); }
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; }
static void DrawWallsAndThings(DrawBuffer *b, Vec2i offset) { Vec2i pos; Tile *tile = &b->tiles[0][0]; pos.y = b->dy + cWallOffset.dy + offset.y; for (int y = 0; y < Y_TILES; y++, pos.y += TILE_HEIGHT) { CArrayClear(&b->displaylist); pos.x = b->dx + cWallOffset.dx + offset.x; for (int x = 0; x < b->Size.x; x++, tile++, pos.x += TILE_WIDTH) { if (tile->flags & MAPTILE_IS_WALL) { if (!(tile->flags & MAPTILE_DELAY_DRAW)) { DrawWallColumn(y, pos, tile); } } else if (tile->flags & MAPTILE_OFFSET_PIC) { // Drawing doors // Doors may be offset; vertical doors are drawn centered // horizontal doors are bottom aligned Vec2i doorPos = pos; doorPos.x += (TILE_WIDTH - tile->picAlt->pic.size.x) / 2; if (tile->picAlt->pic.size.y > 16) { doorPos.y += TILE_HEIGHT - (tile->picAlt->pic.size.y % TILE_HEIGHT); } BlitMasked( &gGraphicsDevice, &tile->picAlt->pic, doorPos, GetTileLOSMask(tile), 0); } // Draw the items that are in LOS if (tile->flags & MAPTILE_OUT_OF_SIGHT) { continue; } CA_FOREACH(ThingId, tid, tile->things) const TTileItem *ti = ThingIdGetTileItem(tid); // Don't draw debris, they are drawn later if (TileItemIsDebris(ti)) { continue; } CArrayPushBack(&b->displaylist, &ti); CA_FOREACH_END() } DrawBufferSortDisplayList(b); CA_FOREACH(const TTileItem *, tp, b->displaylist) DrawThing(b, *tp, offset); CA_FOREACH_END() tile += X_TILES - b->Size.x; } }
static void DrawDebris(DrawBuffer *b, Vec2i offset) { Tile *tile = &b->tiles[0][0]; for (int y = 0; y < Y_TILES; y++) { CArrayClear(&b->displaylist); for (int x = 0; x < b->Size.x; x++, tile++) { if (tile->flags & MAPTILE_OUT_OF_SIGHT) { continue; } CA_FOREACH(ThingId, tid, tile->things) const TTileItem *ti = ThingIdGetTileItem(tid); if (TileItemIsDebris(ti)) { CArrayPushBack(&b->displaylist, &ti); } CA_FOREACH_END() } DrawBufferSortDisplayList(b); CA_FOREACH(const TTileItem *, tp, b->displaylist) DrawThing(b, *tp, offset); CA_FOREACH_END() tile += X_TILES - b->Size.x; } }
static void AddMissionNodes(Autosave *a, json_t *root, const char *nodeName) { json_t *missions = json_new_array(); CA_FOREACH(MissionSave, m, a->Missions) json_insert_child(missions, CreateMissionNode(m)); CA_FOREACH_END() json_insert_pair_into_object(root, nodeName, missions); }
static void ReloadDestructibles(MapObjects *mo) { CA_FOREACH(char *, s, mo->Destructibles) CFREE(*s); CA_FOREACH_END() CArrayClear(&mo->Destructibles); AddDestructibles(mo, &mo->Classes); AddDestructibles(mo, &mo->CustomClasses); }
void AmmoClassesClear(CArray *ammo) { CA_FOREACH(Ammo, a, *ammo) CFREE(a->Name); CFREE(a->Sound); CFREE(a->DefaultGun); CA_FOREACH_END() CArrayClear(ammo); }
void ConfigDestroy(Config *c) { CFREE(c->Name); if (c->Type == CONFIG_TYPE_GROUP) { CA_FOREACH(Config, child, c->u.Group) ConfigDestroy(child); CA_FOREACH_END() CArrayTerminate(&c->u.Group); } }
static void DrawWallsAndThings(DrawBuffer *b, struct vec2i offset) { struct vec2i pos; Tile *tile = &b->tiles[0][0]; pos.y = b->dy + WALL_OFFSET_Y + offset.y; const bool useFog = ConfigGetBool(&gConfig, "Game.Fog"); for (int y = 0; y < Y_TILES; y++, pos.y += TILE_HEIGHT) { CArrayClear(&b->displaylist); pos.x = b->dx + offset.x; for (int x = 0; x < b->Size.x; x++, tile++, pos.x += TILE_WIDTH) { if (tile->Class->Type == TILE_CLASS_WALL) { DrawLOSPic(tile, tile->Class->Pic, pos, useFog); } else if (tile->Class->Type == TILE_CLASS_DOOR && tile->ClassAlt && tile->ClassAlt->Pic) { // Drawing doors // Doors may be offset; vertical doors are drawn centered // horizontal doors are bottom aligned struct vec2i doorPos = pos; const Pic *pic = tile->ClassAlt->Pic; doorPos.x += (TILE_WIDTH - pic->size.x) / 2; if (pic->size.y > 16) { doorPos.y += TILE_HEIGHT - (pic->size.y % TILE_HEIGHT); } DrawLOSPic(tile, pic, doorPos, useFog); } // Draw the items that are in LOS if (tile->outOfSight) { continue; } CA_FOREACH(ThingId, tid, tile->things) const Thing *ti = ThingIdGetThing(tid); // Drawn later if (ThingDrawLast(ti)) { continue; } CArrayPushBack(&b->displaylist, &ti); CA_FOREACH_END() } DrawBufferSortDisplayList(b); CA_FOREACH(const Thing *, tp, b->displaylist) DrawThing(b, *tp, offset); CA_FOREACH_END() tile += X_TILES - b->Size.x; } }
bool RunGame(const CampaignOptions *co, struct MissionOptions *m, Map *map) { // Clear the background DrawRectangle( &gGraphicsDevice, Vec2iZero(), gGraphicsDevice.cachedConfig.Res, colorBlack, 0); SDL_UpdateTexture( gGraphicsDevice.bkg, NULL, gGraphicsDevice.buf, gGraphicsDevice.cachedConfig.Res.x * sizeof(Uint32)); MapLoad(map, m, co); // Seed random if PVP mode (otherwise players will always spawn in same // position) if (IsPVP(co->Entry.Mode)) { srand((unsigned int)time(NULL)); } if (!co->IsClient) { MapLoadDynamic(map, m, &co->Setting.characters); // For PVP modes, mark all map as explored if (IsPVP(co->Entry.Mode)) { MapMarkAllAsVisited(map); } // Reset players for the mission CA_FOREACH(const PlayerData, p, gPlayerDatas) // Only reset for local players; for remote ones wait for the // client ready message if (!p->IsLocal) continue; GameEvent e = GameEventNew(GAME_EVENT_PLAYER_DATA); e.u.PlayerData = PlayerDataMissionReset(p); GameEventsEnqueue(&gGameEvents, e); CA_FOREACH_END() // Process the events to force add the players HandleGameEvents(&gGameEvents, NULL, NULL, NULL); // Note: place players first, // as bad guys are placed away from players Vec2i firstPos = Vec2iZero(); CA_FOREACH(const PlayerData, p, gPlayerDatas) if (!p->Ready) continue; firstPos = PlacePlayer(&gMap, p, firstPos, true); CA_FOREACH_END() if (!IsPVP(co->Entry.Mode)) { InitializeBadGuys(); CreateEnemies(); } }
// TODO: use map structure? BulletClass *StrBulletClass(const char *s) { if (s == NULL || strlen(s) == 0) { return NULL; } CA_FOREACH(BulletClass, b, gBulletClasses.CustomClasses) if (strcmp(s, b->Name) == 0) { return b; } CA_FOREACH_END() CA_FOREACH(BulletClass, b, gBulletClasses.Classes) if (strcmp(s, b->Name) == 0) { return b; } CA_FOREACH_END() CASSERT(false, "cannot parse bullet name"); return NULL; }
void CampaignSettingTerminate(CampaignSetting *setting) { CFREE(setting->Title); CFREE(setting->Author); CFREE(setting->Description); CA_FOREACH(Mission, m, setting->Missions) MissionTerminate(m); CA_FOREACH_END() CArrayTerminate(&setting->Missions); CharacterStoreTerminate(&setting->characters); memset(setting, 0, sizeof *setting); }
int StrAmmoId(const char *s) { if (s == NULL || strlen(s) == 0) { return 0; } CA_FOREACH(Ammo, a, gAmmo.CustomAmmo) if (strcmp(s, a->Name) == 0) { return _ca_index + (int)gAmmo.Ammo.size; } CA_FOREACH_END() CA_FOREACH(Ammo, a, gAmmo.Ammo) if (strcmp(s, a->Name) == 0) { return _ca_index; } CA_FOREACH_END() CASSERT(false, "cannot parse ammo name"); return 0; }
int DestructibleMapObjectIndex(const MapObject *mo) { if (mo == NULL) { return 0; } CA_FOREACH(const char *, name, gMapObjects.Destructibles) const MapObject *d = StrMapObject(*name); if (d == mo) { return _ca_index; } CA_FOREACH_END() CASSERT(false, "cannot find destructible map object"); return -1; }
void MissionOptionsTerminate(struct MissionOptions *mo) { ActorsTerminate(); ObjsTerminate(); MobObjsTerminate(); PickupsTerminate(); ParticlesTerminate(&gParticles); WatchesTerminate(); CA_FOREACH(PlayerData, p, gPlayerDatas) p->ActorUID = -1; CA_FOREACH_END() gMission.HasStarted = false; gMission.HasBegun = false; CArrayTerminate(&mo->Weapons); memset(mo, 0, sizeof *mo); }
Mix_Chunk *StrSound(const char *s) { if (s == NULL || strlen(s) == 0) { return NULL; } CA_FOREACH(SoundData, sound, gSoundDevice.customSounds) if (strcmp(sound->Name, s) == 0) { return sound->data; } CA_FOREACH_END() CA_FOREACH(SoundData, sound, gSoundDevice.sounds) if (strcmp(sound->Name, s) == 0) { return sound->data; } CA_FOREACH_END() return NULL; }
MapObject *StrMapObject(const char *s) { if (s == NULL || strlen(s) == 0) { return NULL; } CA_FOREACH(MapObject, c, gMapObjects.CustomClasses) if (strcmp(s, c->Name) == 0) { return c; } CA_FOREACH_END() CA_FOREACH(MapObject, c, gMapObjects.Classes) if (strcmp(s, c->Name) == 0) { return c; } CA_FOREACH_END() return NULL; }
static void MissionSummaryDraw( const menu_t *menu, GraphicsDevice *g, const Vec2i p, const Vec2i size, const void *data) { UNUSED(menu); UNUSED(p); UNUSED(size); const struct MissionOptions *m = data; const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; // Display password if (strlen(gAutosave.LastMission.Password) > 0) { char s[64]; sprintf(s, "Last password: %s", gAutosave.LastMission.Password); FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.VAlign = ALIGN_END; opts.Area = g->cachedConfig.Res; opts.Pad.y = opts.Area.y / 12; FontStrOpt(s, Vec2iZero(), opts); } // Display objectives and bonuses Vec2i pos = Vec2iNew(w / 6, h / 2 + h / 10); int idx = 1; CA_FOREACH(const Objective, o, m->missionData->Objectives) // Do not mention optional objectives with none completed if (o->done == 0 && !ObjectiveIsRequired(o)) { continue; } // Objective icon DrawObjectiveInfo(o, Vec2iAdd(pos, Vec2iNew(-26, FontH()))); // Objective completion text char s[100]; sprintf(s, "Objective %d: %d of %d, %d required", idx, o->done, o->Count, o->Required); FontOpts opts = FontOptsNew(); opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = pos; if (!ObjectiveIsRequired(o)) { // Show optional objectives in purple opts.Mask = colorPurple; } FontStrOpt(s, Vec2iZero(), opts); // Objective status text opts = FontOptsNew(); opts.HAlign = ALIGN_END; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = pos; if (!ObjectiveIsComplete(o)) { opts.Mask = colorRed; FontStrOpt("Failed", Vec2iZero(), opts); } else if (ObjectiveIsPerfect(o) && AreAnySurvived()) { opts.Mask = colorGreen; char buf[16]; sprintf(buf, "Perfect: %d", PERFECT_BONUS); FontStrOpt(buf, Vec2iZero(), opts); } else if (ObjectiveIsRequired(o)) { FontStrOpt("Done", Vec2iZero(), opts); } else { FontStrOpt("Bonus!", Vec2iZero(), opts); } pos.y += 15; idx++; CA_FOREACH_END() // Draw other bonuses if (AreAnySurvived()) { char s[64]; sprintf(s, "Access bonus: %d", GetAccessBonus(m)); FontStr(s, pos); pos.y += FontH() + 1; int seconds; const int timeBonus = GetTimeBonus(m, &seconds); sprintf(s, "Time bonus: %d secs x 25 = %d", seconds, timeBonus); FontStr(s, pos); } // Draw per-player summaries PlayerData *pds[MAX_LOCAL_PLAYERS]; idx = 0; CA_FOREACH(PlayerData, pd, gPlayerDatas) if (!pd->IsLocal) { continue; } pds[idx] = pd; idx++; CA_FOREACH_END() Vec2i playerSize; switch (idx) { case 1: playerSize = Vec2iNew(w, h / 2); DrawPlayerSummary(Vec2iZero(), playerSize, pds[0]); break; case 2: // side by side playerSize = Vec2iNew(w / 2, h / 2); DrawPlayerSummary(Vec2iZero(), playerSize, pds[0]); DrawPlayerSummary(Vec2iNew(w / 2, 0), playerSize, pds[1]); break; case 3: // fallthrough case 4: // 2x2 playerSize = Vec2iNew(w / 2, h / 4); DrawPlayerSummary(Vec2iZero(), playerSize, pds[0]); DrawPlayerSummary(Vec2iNew(w / 2, 0), playerSize, pds[1]); DrawPlayerSummary(Vec2iNew(0, h / 4), playerSize, pds[2]); if (idx == 4) { DrawPlayerSummary(Vec2iNew(w / 2, h / 4), playerSize, pds[3]); } break; default: CASSERT(false, "not implemented"); break; } }
bool ScreenMissionSummary( CampaignOptions *c, struct MissionOptions *m, const bool completed) { if (completed) { // Save password MissionSave ms; MissionSaveInit(&ms); ms.Campaign = c->Entry; // Don't make password for next level if there is none int passwordIndex = m->index + 1; if (passwordIndex == c->Entry.NumMissions) { passwordIndex--; } strcpy(ms.Password, MakePassword(passwordIndex, 0)); ms.MissionsCompleted = m->index + 1; AutosaveAddMission(&gAutosave, &ms); AutosaveSave(&gAutosave, GetConfigFilePath(AUTOSAVE_FILE)); } // Calculate bonus scores // Bonuses only apply if at least one player has lived if (AreAnySurvived()) { int bonus = 0; // Objective bonuses CA_FOREACH(const Objective, o, m->missionData->Objectives) if (ObjectiveIsPerfect(o)) { bonus += PERFECT_BONUS; } CA_FOREACH_END() bonus += GetAccessBonus(m); bonus += GetTimeBonus(m, NULL); CA_FOREACH(PlayerData, p, gPlayerDatas) ApplyBonuses(p, bonus); CA_FOREACH_END() } MenuSystem ms; const int h = FontH() * 10; MenuSystemInit( &ms, &gEventHandlers, &gGraphicsDevice, Vec2iNew(0, gGraphicsDevice.cachedConfig.Res.y - h), Vec2iNew(gGraphicsDevice.cachedConfig.Res.x, h)); ms.current = ms.root = MenuCreateNormal("", "", MENU_TYPE_NORMAL, 0); // Use return code 0 for whether to continue the game if (completed) { MenuAddSubmenu(ms.root, MenuCreateReturn("Continue", 0)); } else { MenuAddSubmenu(ms.root, MenuCreateReturn("Replay mission", 0)); MenuAddSubmenu(ms.root, MenuCreateReturn("Back to menu", 1)); } ms.allowAborts = true; MenuAddExitType(&ms, MENU_TYPE_RETURN); MenuSystemAddCustomDisplay(&ms, MissionSummaryDraw, m); MenuLoop(&ms); return ms.current->u.returnCode == 0; }
static void DrawWallsAndThings(DrawBuffer *b, Vec2i offset) { Vec2i pos; Tile *tile = &b->tiles[0][0]; pos.y = b->dy + WALL_OFFSET_Y + offset.y; const bool useFog = ConfigGetBool(&gConfig, "Game.Fog"); for (int y = 0; y < Y_TILES; y++, pos.y += TILE_HEIGHT) { CArrayClear(&b->displaylist); pos.x = b->dx + offset.x; for (int x = 0; x < b->Size.x; x++, tile++, pos.x += TILE_WIDTH) { if (tile->flags & MAPTILE_IS_WALL) { if (!(tile->flags & MAPTILE_DELAY_DRAW)) { DrawWallColumn(y, pos, tile); } } else if (tile->flags & MAPTILE_OFFSET_PIC) { // Drawing doors // Doors may be offset; vertical doors are drawn centered // horizontal doors are bottom aligned Vec2i doorPos = pos; doorPos.x += (TILE_WIDTH - tile->picAlt->pic.size.x) / 2; if (tile->picAlt->pic.size.y > 16) { doorPos.y += TILE_HEIGHT - (tile->picAlt->pic.size.y % TILE_HEIGHT); } switch (GetTileLOS(tile, useFog)) { case TILE_LOS_NORMAL: Blit(&gGraphicsDevice, &tile->picAlt->pic, doorPos); break; case TILE_LOS_FOG: BlitMasked( &gGraphicsDevice, &tile->picAlt->pic, doorPos, colorFog, false); break; case TILE_LOS_NONE: default: // don't draw anything break; } } // Draw the items that are in LOS if (tile->flags & MAPTILE_OUT_OF_SIGHT) { continue; } CA_FOREACH(ThingId, tid, tile->things) const TTileItem *ti = ThingIdGetTileItem(tid); // Drawn later if (TileItemDrawLast(ti)) { continue; } CArrayPushBack(&b->displaylist, &ti); CA_FOREACH_END() } DrawBufferSortDisplayList(b); CA_FOREACH(const TTileItem *, tp, b->displaylist) DrawThing(b, *tp, offset); CA_FOREACH_END() tile += X_TILES - b->Size.x; } }
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); }