/** Move to the next news item */ static void MoveToNextItem() { InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar DeleteWindowById(WC_NEWS_WINDOW, 0); // close the newspapers window if shown _forced_news = NULL; _statusbar_news_item = NULL; /* if we're not at the last item, then move on */ if (_current_news != _latest_news) { _current_news = (_current_news == NULL) ? _oldest_news : _current_news->next; const NewsItem *ni = _current_news; const NewsType type = ni->type; /* check the date, don't show too old items */ if (_date - _news_type_data[type].age > ni->date) return; switch (_news_type_data[type].GetDisplay()) { default: NOT_REACHED(); case ND_OFF: // Off - show nothing only a small reminder in the status bar InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_REMINDER); break; case ND_SUMMARY: // Summary - show ticker ShowTicker(ni); break; case ND_FULL: // Full - show newspaper ShowNewspaper(ni); break; } } }
/** * Change the configured music set and reset playback * @param index Index of music set to switch to */ void ChangeMusicSet(int index) { if (BaseMusic::GetIndexOfUsedSet() == index) return; /* Resume playback after switching? * Always if music is already playing, and also if the user is switching * away from an empty music set. * If the user switches away from an empty set, assume it's because they * want to hear music now. */ bool shouldplay = _song_is_active || (BaseMusic::GetUsedSet()->num_available == 0); StopMusic(); const char *name = BaseMusic::GetSet(index)->name; BaseMusic::SetSet(name); free(BaseMusic::ini_set); BaseMusic::ini_set = stredup(name); InitializeMusic(); ResetPlaylist(); _settings_client.music.playing = shouldplay; InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); InvalidateWindowData(WC_MUSIC_WINDOW, 0); InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true); }
/** * Some data on this window has become invalid. * @param data Information about the changed data. * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details. */ virtual void OnInvalidateData(int data = 0, bool gui_scope = true) { if (!gui_scope) return; /* Forward the message to the appropriate toolbar (ingame or scenario editor) */ InvalidateWindowData(WC_MAIN_TOOLBAR, 0, data, true); InvalidateWindowData(WC_MAIN_TOOLBAR_RIGHT, 0, data, true); }
/** * Rename a sign. If the new name of the sign is empty, we assume * the user wanted to delete it. So delete it. Ownership of signs * has no meaning/effect whatsoever except for eyecandy * @param tile unused * @param flags type of operation * @param p1 index of the sign to be renamed/removed * @param p2 unused * @param text the new name or an empty string when resetting to the default * @return the cost of this operation or an error */ CommandCost CmdRenameSign(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Sign *si = Sign::GetIfValid(p1); if (si == NULL) return CMD_ERROR; /* Rename the signs when empty, otherwise remove it */ if (!StrEmpty(text)) { if (Utf8StringLength(text) >= MAX_LENGTH_SIGN_NAME_CHARS) return CMD_ERROR; if (flags & DC_EXEC) { /* Delete the old name */ free(si->name); /* Assign the new one */ si->name = strdup(text); si->owner = _current_company; si->UpdateVirtCoord(); InvalidateWindowData(WC_SIGN_LIST, 0, 1); } } else { // Delete sign if (flags & DC_EXEC) { si->sign.MarkDirty(); delete si; InvalidateWindowData(WC_SIGN_LIST, 0, 0); } } return CommandCost(); }
/** * Invalidating some stuff after removing item from the pool. * @param index index of deleted item */ void Company::PostDestructor(size_t index) { InvalidateWindowData(WC_GRAPH_LEGEND, 0, (int)index); InvalidateWindowData(WC_PERFORMANCE_DETAIL, 0, (int)index); InvalidateWindowData(WC_COMPANY_LEAGUE, 0, 0); /* If the currently shown error message has this company in it, then close it. */ InvalidateWindowData(WC_ERRMSG, 0); }
/** * Build a buoy. * @param tile tile where to place the buoy * @param flags operation to perform * @param p1 unused * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (tile == 0) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); WaterClass wc; if ((flags & DC_PASTE) && !(flags & DC_EXEC)) { /* When pasting a buoy, there may be no water yet (a canal will be placed when DC_EXE'ing). * Ignore that there is no water so we can calculate the cost more precisely. */ wc = WATER_CLASS_INVALID; } else { if (!HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); wc = GetWaterClass(tile); } if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); if (!IsTileFlat(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); /* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */ Waypoint *wp = FindDeletedWaypointCloseTo(tile, STR_SV_STNAME_BUOY, OWNER_NONE); if (wp == NULL && !Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING); CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_WAYPOINT_BUOY]); if (!IsWaterTile(tile)) { CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); } if (flags & DC_EXEC) { if (wp == NULL) { wp = new Waypoint(tile); } else { /* Move existing (recently deleted) buoy to the new location */ wp->xy = tile; InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index); } wp->rect.BeforeAddTile(tile, StationRect::ADD_TRY); wp->string_id = STR_SV_STNAME_BUOY; wp->facilities |= FACIL_DOCK; wp->owner = OWNER_NONE; wp->build_date = _date; if (wp->town == NULL) MakeDefaultName(wp); assert(wc != WATER_CLASS_INVALID); MakeBuoy(tile, wp->index, wc); wp->UpdateVirtCoord(); InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index); } return cost; }
/** Free whatever we've allocated */ ~NetworkContentDownloadStatusWindow() { /* Tell all the backends about what we've downloaded */ for (ContentType *iter = this->receivedTypes.Begin(); iter != this->receivedTypes.End(); iter++) { switch (*iter) { case CONTENT_TYPE_AI: case CONTENT_TYPE_AI_LIBRARY: AI::Rescan(); SetWindowClassesDirty(WC_AI_DEBUG); break; case CONTENT_TYPE_BASE_GRAPHICS: BaseGraphics::FindSets(); SetWindowDirty(WC_GAME_OPTIONS, 0); break; case CONTENT_TYPE_BASE_SOUNDS: BaseSounds::FindSets(); SetWindowDirty(WC_GAME_OPTIONS, 0); break; case CONTENT_TYPE_BASE_MUSIC: BaseMusic::FindSets(); SetWindowDirty(WC_GAME_OPTIONS, 0); break; case CONTENT_TYPE_NEWGRF: ScanNewGRFFiles(); /* Yes... these are the NewGRF windows */ InvalidateWindowClassesData(WC_SAVELOAD); InvalidateWindowData(WC_GAME_OPTIONS, 0, 1); break; case CONTENT_TYPE_SCENARIO: case CONTENT_TYPE_HEIGHTMAP: extern void ScanScenarios(); ScanScenarios(); InvalidateWindowData(WC_SAVELOAD, 0, 0); break; default: break; } } /* Always invalidate the download window; tell it we are going to be gone */ InvalidateWindowData(WC_NETWORK_WINDOW, 1, 2); _network_content_client.RemoveCallback(this); }
/** Show news item in the ticker */ static void ShowTicker(const NewsItem *ni) { if (_settings_client.sound.news_ticker) SndPlayFx(SND_16_MORSE); _statusbar_news_item = ni; InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_TICKER); }
static void CheckShipLeaveDepot(Ship *v) { if (!v->IsInDepot()) return; TileIndex tile = v->tile; Axis axis = GetShipDepotAxis(tile); /* Check first (north) side */ if (DiagdirReachesTracks((DiagDirection)axis) & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) { v->direction = ReverseDir(AxisToDirection(axis)); /* Check second (south) side */ } else if (DiagdirReachesTracks((DiagDirection)(axis + 2)) & GetTileShipTrackStatus(TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis])))) { v->direction = AxisToDirection(axis); } else { return; } v->state = AxisToTrackBits(axis); v->vehstatus &= ~VS_HIDDEN; v->cur_speed = 0; RecalcShipStuff(v); PlayShipSound(v); VehicleServiceInDepot(v); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); SetWindowClassesDirty(WC_SHIPS_LIST); }
/** * Create a new story page. * @param tile unused. * @param flags type of operation * @param p1 various bitstuffed elements * - p1 = (bit 0 - 7) - Company for which this story page belongs to. * @param p2 unused. * @param text Title of the story page. Null is allowed in wich case a generic page title is provided by OpenTTD. * @return the cost of this operation or an error */ CommandCost CmdCreateStoryPage(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (!StoryPage::CanAllocateItem()) return CMD_ERROR; CompanyID company = (CompanyID)GB(p1, 0, 8); if (_current_company != OWNER_DEITY) return CMD_ERROR; if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR; if (flags & DC_EXEC) { if (_story_page_pool.items == 0) { /* Initialize the next sort value variable. */ _story_page_next_sort_value = 0; } StoryPage *s = new StoryPage(); s->sort_value = _story_page_next_sort_value; s->date = _date; s->company = company; if (StrEmpty(text)) { s->title = NULL; } else { s->title = strdup(text); } InvalidateWindowClassesData(WC_STORY_BOOK, -1); if (StoryPage::GetNumItems() == 1) InvalidateWindowData(WC_MAIN_TOOLBAR, 0); _new_story_page_id = s->index; _story_page_next_sort_value++; } return CommandCost(); }
/** * Marks subsidy as awarded, creates news and AI event * @param company awarded company */ void Subsidy::AwardTo(CompanyID company) { assert(!this->IsAwarded()); this->awarded = company; this->remaining = SUBSIDY_CONTRACT_MONTHS; char company_name[MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH]; SetDParam(0, company); GetString(company_name, STR_COMPANY_NAME, lastof(company_name)); char *cn = strdup(company_name); /* Add a news item */ Pair reftype = SetupSubsidyDecodeParam(this, false); InjectDParam(1); SetDParamStr(0, cn); AddNewsItem( STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, this->src, (NewsReferenceType)reftype.b, this->dst, cn ); AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index)); Game::NewEvent(new ScriptEventSubsidyAwarded(this->index)); InvalidateWindowData(WC_SUBSIDIES_LIST, 0); }
/** * Remove a song from a custom playlist. * @param song_index Index in the custom playlist to remove. */ void MusicSystem::PlaylistRemove(size_t song_index) { if (!this->IsCustomPlaylist()) return; Playlist &pl = this->standard_playlists[this->selected_playlist]; if (song_index >= pl.size()) return; /* Remove from "simple" playlists */ PlaylistEntry song = pl[song_index]; pl.erase(pl.begin() + song_index); this->displayed_playlist.erase(this->displayed_playlist.begin() + song_index); /* Find in actual active playlist (may be shuffled) and remove, * if it's the current song restart playback */ for (size_t i = 0; i < this->active_playlist.size(); i++) { Playlist::iterator s2 = this->active_playlist.begin() + i; if (s2->filename == song.filename && s2->cat_index == song.cat_index) { this->active_playlist.erase(s2); if ((int)i == this->playlist_position && this->IsPlaying()) this->Play(); break; } } this->SaveCustomPlaylist(this->selected_playlist); InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); }
/** Perform the monthly update of open subsidies, and try to create a new one. */ void SubsidyMonthlyLoop() { bool modified = false; Subsidy *s; FOR_ALL_SUBSIDIES(s) { if (--s->remaining == 0) { if (!s->IsAwarded()) { Pair reftype = SetupSubsidyDecodeParam(s, true); AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index)); } else { if (s->awarded == _local_company) { Pair reftype = SetupSubsidyDecodeParam(s, true); AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); } AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyExpired(s->index)); } delete s; modified = true; } } if (modified) RebuildSubsidisedSourceAndDestinationCache(); bool passenger_subsidy = false; bool town_subsidy = false; bool industry_subsidy = false; int random_chance = RandomRange(16); if (random_chance < 2) { /* There is a 1/8 chance each month of generating a passenger subsidy. */ int n = 1000; do { passenger_subsidy = FindSubsidyPassengerRoute(); } while (!passenger_subsidy && n--); } else if (random_chance == 2) { /* Cargo subsidies with a town as a source have a 1/16 chance. */ int n = 1000; do { town_subsidy = FindSubsidyTownCargoRoute(); } while (!town_subsidy && n--); } else if (random_chance == 3) { /* Cargo subsidies with an industry as a source have a 1/16 chance. */ int n = 1000; do { industry_subsidy = FindSubsidyIndustryCargoRoute(); } while (!industry_subsidy && n--); } modified |= passenger_subsidy || town_subsidy || industry_subsidy; if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0); }
/** * Remove a buoy * @param tile TileIndex been queried * @param flags operation to perform * @pre IsBuoyTile(tile) * @return cost or failure of operation */ CommandCost RemoveBuoy(TileIndex tile, DoCommandFlag flags) { /* XXX: strange stuff, allow clearing as invalid company when clearing landscape */ if (!Company::IsValidID(_current_company) && !(flags & DC_BANKRUPT)) return_cmd_error(INVALID_STRING_ID); Waypoint *wp = Waypoint::GetByTile(tile); if (HasStationInUse(wp->index, false, _current_company)) return_cmd_error(STR_ERROR_BUOY_IS_IN_USE); /* remove the buoy if there is a ship on tile when company goes bankrupt... */ if (!(flags & DC_BANKRUPT)) { CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; } if (flags & DC_EXEC) { wp->facilities &= ~FACIL_DOCK; InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index); /* We have to set the water tile's state to the same state as before the * buoy was placed. Otherwise one could plant a buoy on a canal edge, * remove it and flood the land (if the canal edge is at level 0) */ MakeWaterKeepingClass(tile, GetTileOwner(tile)); wp->rect.AfterRemoveTile(wp, tile); wp->UpdateVirtCoord(); wp->delete_ctr = 0; } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WAYPOINT_BUOY]); }
/** * Marks subsidy as awarded, creates news and AI event * @param company awarded company */ void Subsidy::AwardTo(CompanyID company) { assert(!this->IsAwarded()); this->awarded = company; this->remaining = SUBSIDY_CONTRACT_MONTHS; char *company_name = MallocT<char>(MAX_LENGTH_COMPANY_NAME_BYTES); SetDParam(0, company); GetString(company_name, STR_COMPANY_NAME, company_name + MAX_LENGTH_COMPANY_NAME_BYTES - 1); /* Add a news item */ Pair reftype = SetupSubsidyDecodeParam(this, 0); InjectDParam(1); SetDParamStr(0, company_name); AddNewsItem( STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier, NS_SUBSIDIES, (NewsReferenceType)reftype.a, this->src, (NewsReferenceType)reftype.b, this->dst, company_name ); AI::BroadcastNewEvent(new AIEventSubsidyAwarded(this->index)); InvalidateWindowData(WC_SUBSIDIES_LIST, 0); }
/** Skip to previous track */ void MusicSystem::Prev() { this->ChangePlaylistPosition(-1); if (_settings_client.music.playing) this->Play(); InvalidateWindowData(WC_MUSIC_WINDOW, 0); }
/** * Append a song to a custom playlist. * Always adds to the currently active playlist. * @param song_index Index of song in the current music set to add */ void MusicSystem::PlaylistAdd(size_t song_index) { if (!this->IsCustomPlaylist()) return; /* Pick out song from the music set */ if (song_index >= this->music_set.size()) return; PlaylistEntry entry = this->music_set[song_index]; /* Check for maximum length */ if (this->standard_playlists[this->selected_playlist].size() >= NUM_SONGS_PLAYLIST) return; /* Add it to the appropriate playlist, and the display */ this->standard_playlists[this->selected_playlist].push_back(entry); this->displayed_playlist.push_back(entry); /* Add it to the active playlist, if playback is shuffled select a random position to add at */ if (this->active_playlist.empty()) { this->active_playlist.push_back(entry); if (this->IsPlaying()) this->Play(); } else if (this->IsShuffle()) { /* Generate a random position between 0 and n (inclusive, new length) to insert at */ size_t maxpos = this->displayed_playlist.size(); size_t newpos = InteractiveRandom() % maxpos; this->active_playlist.insert(this->active_playlist.begin() + newpos, entry); /* Make sure to shift up the current playback position if the song was inserted before it */ if ((int)newpos <= this->playlist_position) this->playlist_position++; } else { this->active_playlist.push_back(entry); } this->SaveCustomPlaylist(this->selected_playlist); InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); }
/** Stop playback and set flag that we don't intend to play music */ void MusicSystem::Stop() { MusicDriver::GetInstance()->StopSong(); _settings_client.music.playing = false; InvalidateWindowData(WC_MUSIC_WINDOW, 0); }
/** * Place a sign at the given coordinates. Ownership of sign has * no effect whatsoever except for the colour the sign gets for easy recognition, * but everybody is able to rename/remove it. * @param tile tile to place sign at * @param flags type of operation * @param p1 unused * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdPlaceSign(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { /* Try to locate a new sign */ if (!Sign::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_SIGNS); /* Check sign text length if any */ if (!StrEmpty(text) && Utf8StringLength(text) >= MAX_LENGTH_SIGN_NAME_CHARS) return CMD_ERROR; /* When we execute, really make the sign */ if (flags & DC_EXEC) { Sign *si = new Sign(_current_company); int x = TileX(tile) * TILE_SIZE; int y = TileY(tile) * TILE_SIZE; si->x = x; si->y = y; si->z = GetSlopeZ(x, y); if (!StrEmpty(text)) { si->name = strdup(text); } si->UpdateVirtCoord(); InvalidateWindowData(WC_SIGN_LIST, 0, 0); _new_sign_id = si->index; } return CommandCost(); }
/* static */ void AI::StartNew(CompanyID company, bool rerandomise_ai) { assert(Company::IsValidID(company)); /* Clients shouldn't start AIs */ if (_networking && !_network_server) return; AIConfig *config = AIConfig::GetConfig(company, AIConfig::SSS_FORCE_GAME); AIInfo *info = config->GetInfo(); if (info == NULL || (rerandomise_ai && config->IsRandom())) { info = AI::scanner_info->SelectRandomAI(); assert(info != NULL); /* Load default data and store the name in the settings */ config->Change(info->GetName(), -1, false, true); } Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE); Company *c = Company::Get(company); c->ai_info = info; assert(c->ai_instance == NULL); c->ai_instance = new AIInstance(); c->ai_instance->Initialize(info); cur_company.Restore(); InvalidateWindowData(WC_AI_DEBUG, 0, -1); return; }
/** * Create a new company and sets all company variables default values * * @param is_ai is an AI company? * @param company CompanyID to use for the new company * @return the company struct */ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY) { if (!Company::CanAllocateItem()) return NULL; /* we have to generate colour before this company is valid */ Colours colour = GenerateCompanyColour(); Company *c; if (company == INVALID_COMPANY) { c = new Company(STR_SV_UNNAMED, is_ai); } else { if (Company::IsValidID(company)) return NULL; c = new (company) Company(STR_SV_UNNAMED, is_ai); } c->colour = colour; ResetCompanyLivery(c); _company_colours[c->index] = (Colours)c->colour; c->money = c->current_loan = (100000ll * _economy.inflation_prices >> 16) / 50000 * 50000; c->share_owners[0] = c->share_owners[1] = c->share_owners[2] = c->share_owners[3] = INVALID_OWNER; c->avail_railtypes = GetCompanyRailtypes(c->index); c->avail_roadtypes = GetCompanyRoadtypes(c->index); c->inaugurated_year = _cur_year; RandomCompanyManagerFaceBits(c->face, (GenderEthnicity)Random(), false, false); // create a random company manager face SetDefaultCompanySettings(c->index); GeneratePresidentName(c); SetWindowDirty(WC_GRAPH_LEGEND, 0); SetWindowClassesDirty(WC_CLIENT_LIST_POPUP); SetWindowDirty(WC_CLIENT_LIST, 0); InvalidateWindowData(WC_LINKGRAPH_LEGEND, 0); BuildOwnerLegend(); InvalidateWindowData(WC_SMALLMAP, 0, 1); if (is_ai && (!_networking || _network_server)) AI::StartNew(c->index); AI::BroadcastNewEvent(new ScriptEventCompanyNew(c->index), c->index); Game::NewEvent(new ScriptEventCompanyNew(c->index)); return c; }
static bool CheckShipLeaveDepot(Ship *v) { if (!v->IsChainInDepot()) return false; /* We are leaving a depot, but have to go to the exact same one; re-enter */ if (v->current_order.IsType(OT_GOTO_DEPOT) && IsShipDepotTile(v->tile) && GetDepotIndex(v->tile) == v->current_order.GetDestination()) { VehicleEnterDepot(v); return true; } TileIndex tile = v->tile; Axis axis = GetShipDepotAxis(tile); DiagDirection north_dir = ReverseDiagDir(AxisToDiagDir(axis)); TileIndex north_neighbour = TILE_ADD(tile, TileOffsByDiagDir(north_dir)); DiagDirection south_dir = AxisToDiagDir(axis); TileIndex south_neighbour = TILE_ADD(tile, 2 * TileOffsByDiagDir(south_dir)); TrackBits north_tracks = DiagdirReachesTracks(north_dir) & GetTileShipTrackStatus(north_neighbour); TrackBits south_tracks = DiagdirReachesTracks(south_dir) & GetTileShipTrackStatus(south_neighbour); if (north_tracks && south_tracks) { /* Ask pathfinder for best direction */ bool reverse = false; bool path_found; switch (_settings_game.pf.pathfinder_for_ships) { case VPF_OPF: reverse = OPFShipChooseTrack(v, north_neighbour, north_dir, north_tracks, path_found) == INVALID_TRACK; break; // OPF always allows reversing case VPF_NPF: reverse = NPFShipCheckReverse(v); break; case VPF_YAPF: reverse = YapfShipCheckReverse(v); break; default: NOT_REACHED(); } if (reverse) north_tracks = TRACK_BIT_NONE; } if (north_tracks) { /* Leave towards north */ v->direction = DiagDirToDir(north_dir); } else if (south_tracks) { /* Leave towards south */ v->direction = DiagDirToDir(south_dir); } else { /* Both ways blocked */ return false; } v->state = AxisToTrackBits(axis); v->vehstatus &= ~VS_HIDDEN; v->cur_speed = 0; v->UpdateViewport(true, true); SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); PlayShipSound(v); VehicleServiceInDepot(v); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); SetWindowClassesDirty(WC_SHIPS_LIST); return false; }
Company::Company(uint16 name_1, bool is_ai) { this->name_1 = name_1; this->location_of_HQ = INVALID_TILE; this->is_ai = is_ai; for (uint j = 0; j < 4; j++) this->share_owners[j] = COMPANY_SPECTATOR; InvalidateWindowData(WC_PERFORMANCE_DETAIL, 0, INVALID_COMPANY); }
/** * Update the virtual coords needed to draw the waypoint sign. */ void Waypoint::UpdateVirtCoord() { Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE); SetDParam(0, this->index); this->sign.UpdatePosition(pt.x, pt.y - 32 * ZOOM_LVL_BASE, STR_VIEWPORT_WAYPOINT); /* Recenter viewport */ InvalidateWindowData(WC_WAYPOINT_VIEW, this->index); }
virtual void DrawWidget(const Rect &r, int widget) const { StringID str = INVALID_STRING_ID; switch (widget) { case WID_S_LEFT: /* Draw the date */ SetDParam(0, _date); str = STR_WHITE_DATE_LONG; break; case WID_S_RIGHT: { /* Draw company money, if any */ const Company *c = Company::GetIfValid(_local_company); if (c != NULL) { SetDParam(0, c->money); str = STR_COMPANY_MONEY; } break; } case WID_S_MIDDLE: /* Draw status bar */ if (this->saving) { // true when saving is active str = STR_STATUSBAR_SAVING_GAME; } else if (_do_autosave) { str = STR_STATUSBAR_AUTOSAVE; } else if (_pause_mode != PM_UNPAUSED) { str = STR_STATUSBAR_PAUSED; } else if (this->ticker_scroll < TICKER_STOP && FindWindowById(WC_NEWS_WINDOW, 0) == NULL && _statusbar_news_item != NULL && _statusbar_news_item->string_id != 0) { /* Draw the scrolling news text */ if (!DrawScrollingStatusText(_statusbar_news_item, this->ticker_scroll, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom)) { InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); if (Company::IsValidID(_local_company)) { /* This is the default text */ SetDParam(0, _local_company); str = STR_STATUSBAR_COMPANY_NAME; } } } else { if (Company::IsValidID(_local_company)) { /* This is the default text */ SetDParam(0, _local_company); str = STR_STATUSBAR_COMPANY_NAME; } } break; } int center_top = Center(r.top + WD_FRAMERECT_TOP, r.bottom - r.top); if (str != INVALID_STRING_ID) DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, center_top, str, TC_FROMSTRING, SA_HOR_CENTER); if (widget == WID_S_MIDDLE && this->reminder_timeout > 0) { Dimension icon_size = GetSpriteSize(SPR_UNREAD_NEWS); center_top = Center(r.top + WD_FRAMERECT_TOP, r.bottom - r.top, icon_size.height); DrawSprite(SPR_UNREAD_NEWS, PAL_NONE, r.right - WD_FRAMERECT_RIGHT - icon_size.width, center_top); } }
/* static */ void AILog::Log(AILog::AILogType level, const char *message) { if (AIObject::GetLogPointer() == NULL) { AIObject::GetLogPointer() = new LogData(); LogData *log = (LogData *)AIObject::GetLogPointer(); log->lines = CallocT<char *>(80); log->type = CallocT<AILog::AILogType>(80); log->count = 80; log->pos = log->count - 1; log->used = 0; } LogData *log = (LogData *)AIObject::GetLogPointer(); /* Go to the next log-line */ log->pos = (log->pos + 1) % log->count; if (log->used != log->count) log->used++; /* Free last message, and write new message */ free(log->lines[log->pos]); log->lines[log->pos] = strdup(message); log->type[log->pos] = level; /* Cut string after first \n */ char *p; while ((p = strchr(log->lines[log->pos], '\n')) != NULL) { *p = '\0'; break; } char logc; switch (level) { case LOG_SQ_ERROR: logc = 'S'; break; case LOG_ERROR: logc = 'E'; break; case LOG_SQ_INFO: logc = 'P'; break; case LOG_WARNING: logc = 'W'; break; case LOG_INFO: logc = 'I'; break; default: logc = '?'; break; } /* Also still print to debug window */ DEBUG(ai, level, "[%d] [%c] %s", (uint)_current_company, logc, log->lines[log->pos]); InvalidateWindowData(WC_AI_DEBUG, 0, _current_company); }
/** * Build a buoy. * @param tile tile where to place the buoy * @param flags operation to perform * @param p1 unused * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (tile == 0 || !HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); if (GetTileSlope(tile) != SLOPE_FLAT) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); /* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */ Waypoint *wp = FindDeletedWaypointCloseTo(tile, STR_SV_STNAME_BUOY, OWNER_NONE); if (wp == NULL && !Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING); CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_WAYPOINT_BUOY]); if (!IsWaterTile(tile)) { CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); } if (flags & DC_EXEC) { if (wp == NULL) { wp = new Waypoint(tile); } else { /* Move existing (recently deleted) buoy to the new location */ wp->xy = tile; InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index); } wp->rect.BeforeAddTile(tile, StationRect::ADD_TRY); wp->string_id = STR_SV_STNAME_BUOY; wp->facilities |= FACIL_DOCK; wp->owner = OWNER_NONE; wp->build_date = _date; if (wp->town == NULL) MakeDefaultName(wp); MakeBuoy(tile, wp->index, GetWaterClass(tile)); wp->UpdateVirtCoord(); InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index); } return cost; }
/** * Rebuild the left autoreplace list if an engine is removed or added * @param e Engine to check if it is removed or added * @param id_g The group the engine belongs to * Note: this function only works if it is called either * - when a new vehicle is build, but before it's counted in num_engines * - when a vehicle is deleted and after it's subtracted from num_engines * - when not changing the count (used when changing replace orders) */ void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g) { if (GetGroupNumEngines(_local_company, id_g, e) == 0 || GetGroupNumEngines(_local_company, ALL_GROUP, e) == 0) { /* We don't have any of this engine type. * Either we just sold the last one, we build a new one or we stopped replacing it. * In all cases, we need to update the left list */ InvalidateWindowData(WC_REPLACE_VEHICLE, Engine::Get(e)->type, 1); } }
/** Disable shuffle and restart playback */ void MusicSystem::Unshuffle() { _settings_client.music.shuffle = false; this->active_playlist = this->displayed_playlist; if (_settings_client.music.playing) this->Play(); InvalidateWindowData(WC_MUSIC_WINDOW, 0); }
/** * Constructor. * @param name_1 Name of the company. * @param is_ai A computer program is running for this company. */ Company::Company(uint16 name_1, bool is_ai) { this->name_1 = name_1; this->location_of_HQ = INVALID_TILE; this->is_ai = is_ai; this->terraform_limit = _settings_game.construction.terraform_frame_burst << 16; this->clear_limit = _settings_game.construction.clear_frame_burst << 16; for (uint j = 0; j < 4; j++) this->share_owners[j] = COMPANY_SPECTATOR; InvalidateWindowData(WC_PERFORMANCE_DETAIL, 0, INVALID_COMPANY); }