/** * Handle changing of the current year. * @param p1 Unused. * @param p2 +1 (increase) or -1 (decrease). * @return New year. */ static int32 ClickChangeDateCheat(int32 p1, int32 p2) { YearMonthDay ymd; ConvertDateToYMD(_date, &ymd); p1 = Clamp(p1, MIN_YEAR, MAX_YEAR); if (p1 == _cur_year) return _cur_year; SetDate(ConvertYMDToDate(p1, ymd.month, ymd.day), _date_fract); EnginesMonthlyLoop(); SetWindowDirty(WC_STATUS_BAR, 0); InvalidateWindowClassesData(WC_BUILD_STATION, 0); InvalidateWindowClassesData(WC_BUILD_OBJECT, 0); ResetSignalVariant(); return _cur_year; }
/** Refits a ship to the specified cargo type. * @param tile unused * @param flags type of operation * @param p1 vehicle ID of the ship to refit * @param p2 various bitstuffed elements * - p2 = (bit 0-7) - the new cargo type to refit to (p2 & 0xFF) * - p2 = (bit 8-15) - the new cargo subtype to refit to * - p2 = (bit 16) - refit only this vehicle (ignored) * @param text unused * @return the cost of this operation or an error */ CommandCost CmdRefitShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { CargoID new_cid = GB(p2, 0, 8); // gets the cargo number byte new_subtype = GB(p2, 8, 8); Ship *v = Ship::GetIfValid(p1); if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR; if (!v->IsStoppedInDepot()) return_cmd_error(STR_ERROR_SHIP_MUST_BE_STOPPED_IN_DEPOT); if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_CAN_T_REFIT_DESTROYED_VEHICLE); /* Check cargo */ if (new_cid >= NUM_CARGO) return CMD_ERROR; CommandCost cost = RefitVehicle(v, true, new_cid, new_subtype, flags); if (flags & DC_EXEC) { v->colourmap = PAL_NONE; // invalidate vehicle colour map SetWindowDirty(WC_VEHICLE_DETAILS, v->index); SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowClassesData(WC_SHIPS_LIST, 0); } v->InvalidateNewGRFCacheOfChain(); // always invalidate; querycost might have filled it return cost; }
/** * Runs various procedures that have to be done yearly */ static void OnNewYear() { CompaniesYearlyLoop(); VehiclesYearlyLoop(); TownsYearlyLoop(); InvalidateWindowClassesData(WC_BUILD_STATION); #ifdef ENABLE_NETWORK if (_network_server) NetworkServerYearlyLoop(); #endif /* ENABLE_NETWORK */ if (_cur_year == _settings_client.gui.semaphore_build_before) ResetSignalVariant(); /* check if we reached end of the game */ if (_cur_year == ORIGINAL_END_YEAR) { ShowEndGameChart(); /* check if we reached the maximum year, decrement dates by a year */ } else if (_cur_year == MAX_YEAR + 1) { Vehicle *v; uint days_this_year; _cur_year--; days_this_year = IsLeapYear(_cur_year) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR; _date -= days_this_year; FOR_ALL_VEHICLES(v) v->date_of_last_service -= days_this_year; #ifdef ENABLE_NETWORK /* Because the _date wraps here, and text-messages expire by game-days, we have to clean out * all of them if the date is set back, else those messages will hang for ever */ NetworkInitChatMessage(); #endif /* ENABLE_NETWORK */ } if (_settings_client.gui.auto_euro) CheckSwitchToEuro(); }
/** * 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(); }
/** * 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. */ InvalidateWindowClassesData( WC_WATCH_COMPANY, 0 ); InvalidateWindowData(WC_ERRMSG, 0); }
/* static */ void Game::Rescan() { TarScanner::DoScan(TarScanner::GAME); Game::scanner_info->RescanDir(); Game::scanner_library->RescanDir(); ResetConfig(); InvalidateWindowData(WC_AI_LIST, 0, 1); SetWindowClassesDirty(WC_AI_DEBUG); InvalidateWindowClassesData(WC_AI_SETTINGS); }
/** * Really perform the scan for all NewGRFs. * @param callback The callback to call after the scanning is complete. */ void DoScanNewGRFFiles(void *callback) { _modal_progress_work_mutex->BeginCritical(); ClearGRFConfigList(&_all_grfs); TarScanner::DoScan(TarScanner::NEWGRF); DEBUG(grf, 1, "Scanning for NewGRFs"); uint num = GRFFileScanner::DoScan(); DEBUG(grf, 1, "Scan complete, found %d files", num); if (num != 0 && _all_grfs != NULL) { /* Sort the linked list using quicksort. * For that we first have to make an array, then sort and * then remake the linked list. */ GRFConfig **to_sort = MallocT<GRFConfig*>(num); uint i = 0; for (GRFConfig *p = _all_grfs; p != NULL; p = p->next, i++) { to_sort[i] = p; } /* Number of files is not necessarily right */ num = i; QSortT(to_sort, num, &GRFSorter); for (i = 1; i < num; i++) { to_sort[i - 1]->next = to_sort[i]; } to_sort[num - 1]->next = NULL; _all_grfs = to_sort[0]; free(to_sort); #ifdef ENABLE_NETWORK NetworkAfterNewGRFScan(); #endif } _modal_progress_work_mutex->EndCritical(); _modal_progress_paint_mutex->BeginCritical(); /* Yes... these are the NewGRF windows */ InvalidateWindowClassesData(WC_SAVELOAD, 0, true); InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_NEWGRF_STATE, GOID_NEWGRF_RESCANNED, true); if (callback != NULL) ((NewGRFScanCallback*)callback)->OnNewGRFsScanned(); DeleteWindowByClass(WC_MODAL_PROGRESS); SetModalProgress(false); MarkWholeScreenDirty(); _modal_progress_paint_mutex->EndCritical(); }
/** * 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; this->tree_limit = _settings_game.construction.tree_frame_burst << 16; for (uint j = 0; j < 4; j++) this->share_owners[j] = COMPANY_SPECTATOR; InvalidateWindowData(WC_PERFORMANCE_DETAIL, 0, INVALID_COMPANY); InvalidateWindowClassesData( WC_WATCH_COMPANY, 0 ); }
/** * Clean up a station by clearing vehicle orders and invalidating windows. * Aircraft-Hangar orders need special treatment here, as the hangars are * actually part of a station (tiletype is STATION), but the order type * is OT_GOTO_DEPOT. */ Station::~Station() { if (CleaningPool()) { for (CargoID c = 0; c < NUM_CARGO; c++) { this->goods[c].cargo.OnCleanPool(); } return; } while (!this->loading_vehicles.empty()) { this->loading_vehicles.front()->LeaveStation(); } Aircraft *a; FOR_ALL_AIRCRAFT(a) { if (!a->IsNormalAircraft()) continue; if (a->targetairport == this->index) a->targetairport = INVALID_STATION; } Vehicle *v; FOR_ALL_VEHICLES(v) { /* Forget about this station if this station is removed */ if (v->last_station_visited == this->index) { v->last_station_visited = INVALID_STATION; } } /* Clear the persistent storage. */ delete this->airport.psa; if (this->owner == OWNER_NONE) { /* Invalidate all in case of oil rigs. */ InvalidateWindowClassesData(WC_STATION_LIST, 0); } else { InvalidateWindowData(WC_STATION_LIST, this->owner, 0); } DeleteWindowById(WC_STATION_VIEW, index); /* Now delete all orders that go to the station */ RemoveOrderFromAllVehicles(OT_GOTO_STATION, this->index); /* Remove all news items */ DeleteStationNews(this->index); for (CargoID c = 0; c < NUM_CARGO; c++) { this->goods[c].cargo.Truncate(0); } CargoPacket::InvalidateAllFrom(this->index); }
/** 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); }
/** * Update date of a story page. * @param tile unused. * @param flags type of operation * @param p1 = (bit 0 - 15) - StoryPageID to update. * @param p2 = (bit 0 - 31) - date * @param text unused * @return the cost of this operation or an error */ CommandCost CmdSetStoryPageDate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (_current_company != OWNER_DEITY) return CMD_ERROR; StoryPageID page_id = (StoryPageID)GB(p1, 0, 16); if (!StoryPage::IsValidID(page_id)) return CMD_ERROR; Date date = (Date)p2; if (flags & DC_EXEC) { StoryPage *p = StoryPage::Get(page_id); p->date = date; InvalidateWindowClassesData(WC_STORY_BOOK, page_id); } return CommandCost(); }
/** * Pause/Unpause the game (server-only). * Set or unset a bit in the pause mode. If pause mode is zero the game is * unpaused. A bitset is used instead of a boolean value/counter to have * more control over the game when saving/loading, etc. * @param tile unused * @param flags operation to perform * @param p1 the pause mode to change * @param p2 1 pauses, 0 unpauses this mode * @param text unused * @return the cost of this operation or an error */ CommandCost CmdPause(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { switch (p1) { case PM_PAUSED_SAVELOAD: case PM_PAUSED_ERROR: case PM_PAUSED_NORMAL: break; #ifdef ENABLE_NETWORK case PM_PAUSED_JOIN: case PM_PAUSED_ACTIVE_CLIENTS: if (!_networking) return CMD_ERROR; break; #endif /* ENABLE_NETWORK */ default: return CMD_ERROR; } if (flags & DC_EXEC) { if (p1 == PM_PAUSED_NORMAL && _pause_mode & PM_PAUSED_ERROR) { ShowQuery( STR_NEWGRF_UNPAUSE_WARNING_TITLE, STR_NEWGRF_UNPAUSE_WARNING, NULL, AskUnsafeUnpauseCallback ); } else { #ifdef ENABLE_NETWORK PauseMode prev_mode = _pause_mode; #endif /* ENABLE_NETWORK */ if (p2 == 0) { _pause_mode = _pause_mode & ~p1; } else { _pause_mode = _pause_mode | p1; } InvalidateWindowClassesData(WC_AI_DEBUG, -2); #ifdef ENABLE_NETWORK NetworkHandlePauseChange(prev_mode, (PauseMode)p1); #endif /* ENABLE_NETWORK */ } SetWindowDirty(WC_STATUS_BAR, 0); SetWindowDirty(WC_MAIN_TOOLBAR, 0); } return CommandCost(); }
/** * Update completed state of a goal. * @param tile unused. * @param flags type of operation * @param p1 GoalID to update. * @param p2 completed state. If goal is completed, set to 1, otherwise 0. * @param text unused * @return the cost of this operation or an error */ CommandCost CmdSetGoalCompleted(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (_current_company != OWNER_DEITY) return CMD_ERROR; if (!Goal::IsValidID(p1)) return CMD_ERROR; if (flags & DC_EXEC) { Goal *g = Goal::Get(p1); g->completed = p2 == 1; if (g->company == INVALID_COMPANY) { InvalidateWindowClassesData(WC_GOALS_LIST); } else { InvalidateWindowData(WC_GOALS_LIST, g->company); } } return CommandCost(); }
/** * Update a new story page element. * @param tile Tile location if it is a location page element, otherwise unused. * @param flags type of operation * @param p1 various bitstuffed elements * - p1 = (bit 0 - 15) - The page element to update. * (bit 16 - 31) - unused * @param p2 Id of referenced object * @param text Text content in case it is a text or location page element * @return the cost of this operation or an error */ CommandCost CmdUpdateStoryPageElement(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { StoryPageElementID page_element_id = (StoryPageElementID)GB(p1, 0, 16); if (_current_company != OWNER_DEITY) return CMD_ERROR; if (!StoryPageElement::IsValidID(page_element_id)) return CMD_ERROR; StoryPageElement *pe = StoryPageElement::Get(page_element_id); StoryPageID page_id = pe->page; StoryPageElementType type = pe->type; if (!VerifyElementContentParameters(page_id, type, tile, p2, text)) return CMD_ERROR; if (flags & DC_EXEC) { UpdateElement(*pe, tile, p2, text); InvalidateWindowClassesData(WC_STORY_BOOK, pe->page); } return CommandCost(); }
/** * Update title of a story page. * @param tile unused. * @param flags type of operation * @param p1 = (bit 0 - 15) - StoryPageID to update. * @param p2 unused * @param text title text of the story page. * @return the cost of this operation or an error */ CommandCost CmdSetStoryPageTitle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (_current_company != OWNER_DEITY) return CMD_ERROR; StoryPageID page_id = (StoryPageID)GB(p1, 0, 16); if (!StoryPage::IsValidID(page_id)) return CMD_ERROR; if (flags & DC_EXEC) { StoryPage *p = StoryPage::Get(page_id); free(p->title); if (StrEmpty(text)) { p->title = NULL; } else { p->title = strdup(text); } InvalidateWindowClassesData(WC_STORY_BOOK, page_id); } return CommandCost(); }
/** * Remove a goal. * @param tile unused. * @param flags type of operation * @param p1 GoalID to remove. * @param p2 unused. * @param text unused. * @return the cost of this operation or an error */ CommandCost CmdRemoveGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (_current_company != OWNER_DEITY) return CMD_ERROR; if (!Goal::IsValidID(p1)) return CMD_ERROR; if (flags & DC_EXEC) { Goal *g = Goal::Get(p1); CompanyID c = g->company; delete g; if (c == INVALID_COMPANY) { InvalidateWindowClassesData(WC_GOALS_LIST); } else { InvalidateWindowData(WC_GOALS_LIST, c); } if (Goal::GetNumItems() == 0) InvalidateWindowData(WC_MAIN_TOOLBAR, 0); } return CommandCost(); }
/** * Remove a story page and associated story page elements. * @param tile unused. * @param flags type of operation * @param p1 = (bit 0 - 15) - StoryPageID to remove. * @param p2 unused. * @param text unused. * @return the cost of this operation or an error */ CommandCost CmdRemoveStoryPage(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (_current_company != OWNER_DEITY) return CMD_ERROR; StoryPageID page_id = (StoryPageID)p1; if (!StoryPage::IsValidID(page_id)) return CMD_ERROR; if (flags & DC_EXEC) { StoryPage *p = StoryPage::Get(page_id); StoryPageElement *pe; FOR_ALL_STORY_PAGE_ELEMENTS(pe) { if (pe->page == p->index) { delete pe; } } delete p; InvalidateWindowClassesData(WC_STORY_BOOK, -1); if (StoryPage::GetNumItems() == 0) InvalidateWindowData(WC_MAIN_TOOLBAR, 0); }
/** * Sets the local company and updates the settings that are set on a * per-company basis to reflect the core's state in the GUI. * @param new_company the new company * @pre Company::IsValidID(new_company) || new_company == COMPANY_SPECTATOR || new_company == OWNER_NONE */ void SetLocalCompany(CompanyID new_company) { /* company could also be COMPANY_SPECTATOR or OWNER_NONE */ assert(Company::IsValidID(new_company) || new_company == COMPANY_SPECTATOR || new_company == OWNER_NONE); #ifdef ENABLE_NETWORK /* Delete the chat window, if you were team chatting. */ InvalidateWindowData(WC_SEND_NETWORK_MSG, DESTTYPE_TEAM, _local_company); #endif assert(IsLocalCompany()); _current_company = _local_company = new_company; /* Delete any construction windows... */ DeleteConstructionWindows(); /* ... and redraw the whole screen. */ MarkWholeScreenDirty(); InvalidateWindowClassesData(WC_SIGN_LIST, -1); }
/** Give a custom name to your vehicle * @param tile unused * @param flags type of operation * @param p1 vehicle ID to name * @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 CmdRenameVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Vehicle *v = Vehicle::GetIfValid(p1); if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR; bool reset = StrEmpty(text); if (!reset) { if (strlen(text) >= MAX_LENGTH_VEHICLE_NAME_BYTES) return CMD_ERROR; if (!(flags & DC_AUTOREPLACE) && !IsUniqueVehicleName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE); } if (flags & DC_EXEC) { free(v->name); v->name = reset ? NULL : strdup(text); InvalidateWindowClassesData(WC_TRAINS_LIST, 1); MarkWholeScreenDirty(); } return CommandCost(); }
/** * Callback for building wagons. * @param result The result of the command. * @param tile The tile the command was executed on. * @param p1 Additional data for the command (for the #CommandProc) * @param p2 Additional data for the command (for the #CommandProc) */ void CcBuildWagon(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) { if (result.Failed()) return; /* find a locomotive in the depot. */ const Vehicle *found = NULL; const Train *t; FOR_ALL_TRAINS(t) { if (t->IsFrontEngine() && t->tile == tile && t->IsStoppedInDepot()) { if (found != NULL) return; // must be exactly one. found = t; } } /* if we found a loco, */ if (found != NULL) { found = found->Last(); /* put the new wagon at the end of the loco. */ DoCommandP(0, _new_vehicle_id, found->index, CMD_MOVE_RAIL_VEHICLE); InvalidateWindowClassesData(WC_TRAINS_LIST, 0); } }
/** * Create a new story page element. * @param tile Tile location if it is a location page element, otherwise unused. * @param flags type of operation * @param p1 various bitstuffed elements * - p1 = (bit 0 - 15) - The page which the element belongs to. * (bit 16 - 23) - Page element type * @param p2 Id of referenced object * @param text Text content in case it is a text or location page element * @return the cost of this operation or an error */ CommandCost CmdCreateStoryPageElement(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (!StoryPageElement::CanAllocateItem()) return CMD_ERROR; StoryPageID page_id = (CompanyID)GB(p1, 0, 16); StoryPageElementType type = Extract<StoryPageElementType, 16, 8>(p1); /* Allow at most 128 elements per page. */ uint16 element_count = 0; StoryPageElement *iter; FOR_ALL_STORY_PAGE_ELEMENTS(iter) { if (iter->page == page_id) element_count++; } if (element_count >= 128) return CMD_ERROR; if (_current_company != OWNER_DEITY) return CMD_ERROR; if (!StoryPage::IsValidID(page_id)) return CMD_ERROR; if (!VerifyElementContentParameters(page_id, type, tile, p2, text)) return CMD_ERROR; if (flags & DC_EXEC) { if (_story_page_element_pool.items == 0) { /* Initialize the next sort value variable. */ _story_page_element_next_sort_value = 0; } StoryPageElement *pe = new StoryPageElement(); pe->sort_value = _story_page_element_next_sort_value; pe->type = type; pe->page = page_id; UpdateElement(*pe, tile, p2, text); InvalidateWindowClassesData(WC_STORY_BOOK, page_id); _new_story_page_element_id = pe->index; _story_page_element_next_sort_value++; } return CommandCost(); }
/** * Update progress text of a goal. * @param tile unused. * @param flags type of operation * @param p1 GoalID to update. * @param p2 unused * @param text Progress text of the goal. * @return the cost of this operation or an error */ CommandCost CmdSetGoalProgress(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (_current_company != OWNER_DEITY) return CMD_ERROR; if (!Goal::IsValidID(p1)) return CMD_ERROR; if (flags & DC_EXEC) { Goal *g = Goal::Get(p1); free(g->progress); if (StrEmpty(text)) { g->progress = NULL; } else { g->progress = stredup(text); } if (g->company == INVALID_COMPANY) { InvalidateWindowClassesData(WC_GOALS_LIST); } else { InvalidateWindowData(WC_GOALS_LIST, g->company); } } return CommandCost(); }
void CcBuildWagon(bool success, TileIndex tile, uint32 p1, uint32 p2) { if (!success) return; /* find a locomotive in the depot. */ const Vehicle *found = NULL; const Train *t; FOR_ALL_TRAINS(t) { if (t->IsFrontEngine() && t->tile == tile && t->track == TRACK_BIT_DEPOT) { if (found != NULL) return; // must be exactly one. found = t; } } /* if we found a loco, */ if (found != NULL) { found = found->Last(); /* put the new wagon at the end of the loco. */ DoCommandP(0, _new_vehicle_id | (found->index << 16), 0, CMD_MOVE_RAIL_VEHICLE); InvalidateWindowClassesData(WC_TRAINS_LIST, 0); } }
/** * Give a custom name to your vehicle * @param tile unused * @param flags type of operation * @param p1 vehicle ID to name * @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 CmdRenameVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Vehicle *v = Vehicle::GetIfValid(p1); if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR; CommandCost ret = CheckOwnership(v->owner); if (ret.Failed()) return ret; bool reset = StrEmpty(text); if (!reset) { if (Utf8StringLength(text) >= MAX_LENGTH_VEHICLE_NAME_CHARS) return CMD_ERROR; if (!(flags & DC_AUTOREPLACE) && !IsUniqueVehicleName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE); } if (flags & DC_EXEC) { free(v->name); v->name = reset ? NULL : strdup(text); InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 1); MarkWholeScreenDirty(); } return CommandCost(); }
/** * Rebuild the GRFConfig's of the servers in the game list as we did * a rescan and might have found new NewGRFs. */ void NetworkAfterNewGRFScan() { for (NetworkGameList *item = _network_game_list; item != NULL; item = item->next) { /* Reset compatability state */ item->info.compatible = item->info.version_compatible; for (GRFConfig *c = item->info.grfconfig; c != NULL; c = c->next) { assert(HasBit(c->flags, GCF_COPY)); const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, c->ident.md5sum); if (f == NULL) { /* Don't know the GRF, so mark game incompatible and the (possibly) * already resolved name for this GRF (another server has sent the * name of the GRF already */ c->name->Release(); c->name = FindUnknownGRFName(c->ident.grfid, c->ident.md5sum, true); c->name->AddRef(); c->status = GCS_NOT_FOUND; /* If we miss a file, we're obviously incompatible */ item->info.compatible = false; } else { c->filename = f->filename; c->name->Release(); c->name = f->name; c->name->AddRef(); c->info->Release(); c->info = f->info; c->info->AddRef(); c->status = GCS_UNKNOWN; } } } InvalidateWindowClassesData(WC_NETWORK_WINDOW); }
/** * Build a vehicle. * @param tile tile of depot where the vehicle is built * @param flags for command * @param p1 various bitstuffed data * bits 0-15: vehicle type being built. * bits 16-31: vehicle type specific bits passed on to the vehicle build functions. * @param p2 User * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { /* Elementary check for valid location. */ if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR; VehicleType type; switch (GetTileType(tile)) { case MP_RAILWAY: type = VEH_TRAIN; break; case MP_ROAD: type = VEH_ROAD; break; case MP_WATER: type = VEH_SHIP; break; case MP_STATION: type = VEH_AIRCRAFT; break; default: NOT_REACHED(); // Safe due to IsDepotTile() } /* Validate the engine type. */ EngineID eid = GB(p1, 0, 16); if (!IsEngineBuildable(eid, type, _current_company)) return_cmd_error(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + type); const Engine *e = Engine::Get(eid); CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost()); /* Engines without valid cargo should not be available */ if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR; /* Check whether the number of vehicles we need to build can be built according to pool space. */ uint num_vehicles; switch (type) { case VEH_TRAIN: num_vehicles = (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); break; case VEH_ROAD: num_vehicles = 1 + CountArticulatedParts(eid, false); break; case VEH_SHIP: num_vehicles = 1; break; case VEH_AIRCRAFT: num_vehicles = e->u.air.subtype & AIR_CTOL ? 2 : 3; break; default: NOT_REACHED(); // Safe due to IsDepotTile() } if (!Vehicle::CanAllocateItem(num_vehicles)) return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME); /* Check whether we can allocate a unit number. Autoreplace does not allocate * an unit number as it will (always) reuse the one of the replaced vehicle * and (train) wagons don't have an unit number in any scenario. */ UnitID unit_num = (flags & DC_AUTOREPLACE || (type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON)) ? 0 : GetFreeUnitNumber(type); if (unit_num == UINT16_MAX) return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME); Vehicle *v; switch (type) { case VEH_TRAIN: value.AddCost(CmdBuildRailVehicle(tile, flags, e, GB(p1, 16, 16), &v)); break; case VEH_ROAD: value.AddCost(CmdBuildRoadVehicle(tile, flags, e, GB(p1, 16, 16), &v)); break; case VEH_SHIP: value.AddCost(CmdBuildShip (tile, flags, e, GB(p1, 16, 16), &v)); break; case VEH_AIRCRAFT: value.AddCost(CmdBuildAircraft (tile, flags, e, GB(p1, 16, 16), &v)); break; default: NOT_REACHED(); // Safe due to IsDepotTile() } if (value.Succeeded() && flags & DC_EXEC) { v->unitnumber = unit_num; v->value = value.GetCost(); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowClassesData(GetWindowClassForVehicleType(type), 0); SetWindowDirty(WC_COMPANY, _current_company); if (IsLocalCompany()) { InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the auto replace window (must be called before incrementing num_engines) } GroupStatistics::CountEngine(v, 1); GroupStatistics::UpdateAutoreplace(_current_company); if (v->IsPrimaryVehicle()) { GroupStatistics::CountVehicle(v, 1); OrderBackup::Restore(v, p2); } } return value; }
/** * When an engine is made buildable or is removed from being buildable, add/remove it from the build/autoreplace lists * @param type The type of engine */ void AddRemoveEngineFromAutoreplaceAndBuildWindows(VehicleType type) { InvalidateWindowData(WC_REPLACE_VEHICLE, type, 0); // Update the autoreplace window InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well }
/** * Clean up a station by clearing vehicle orders, invalidating windows and * removing link stats. * Aircraft-Hangar orders need special treatment here, as the hangars are * actually part of a station (tiletype is STATION), but the order type * is OT_GOTO_DEPOT. */ Station::~Station() { if (CleaningPool()) { for (CargoID c = 0; c < NUM_CARGO; c++) { this->goods[c].cargo.OnCleanPool(); } return; } while (!this->loading_vehicles.empty()) { this->loading_vehicles.front()->LeaveStation(); } Aircraft *a; FOR_ALL_AIRCRAFT(a) { if (!a->IsNormalAircraft()) continue; if (a->targetairport == this->index) a->targetairport = INVALID_STATION; } for (CargoID c = 0; c < NUM_CARGO; ++c) { LinkGraph *lg = LinkGraph::GetIfValid(this->goods[c].link_graph); if (lg == NULL) continue; for (NodeID node = 0; node < lg->Size(); ++node) { Station *st = Station::Get((*lg)[node].Station()); st->goods[c].flows.erase(this->index); if ((*lg)[node][this->goods[c].node].LastUpdate() != INVALID_DATE) { st->goods[c].flows.DeleteFlows(this->index); RerouteCargo(st, c, this->index, st->index); } } lg->RemoveNode(this->goods[c].node); if (lg->Size() == 0) { LinkGraphSchedule::instance.Unqueue(lg); delete lg; } } Vehicle *v; FOR_ALL_VEHICLES(v) { /* Forget about this station if this station is removed */ if (v->last_station_visited == this->index) { v->last_station_visited = INVALID_STATION; } if (v->last_loading_station == this->index) { v->last_loading_station = INVALID_STATION; } } /* Clear the persistent storage. */ delete this->airport.psa; if (this->owner == OWNER_NONE) { /* Invalidate all in case of oil rigs. */ InvalidateWindowClassesData(WC_STATION_LIST, 0); } else { InvalidateWindowData(WC_STATION_LIST, this->owner, 0); } if (Overlays::Instance()->HasStation(Station::Get(this->index))) { Overlays::Instance()->ToggleStation(Station::Get(this->index)); }; DeleteWindowById(WC_STATION_VIEW, index); /* Now delete all orders that go to the station */ RemoveOrderFromAllVehicles(OT_GOTO_STATION, this->index); /* Remove all news items */ DeleteStationNews(this->index); for (CargoID c = 0; c < NUM_CARGO; c++) { this->goods[c].cargo.Truncate(); } CargoPacket::InvalidateAllFrom(this->index); }
/** * Control the companies: add, delete, etc. * @param tile unused * @param flags operation to perform * @param p1 various functionality * - bits 0..15: * = 0 - create a new company * = 1 - create a new AI company * = 2 - delete a company * - bits 16..24: CompanyID * @param p2 ClientID * @param text unused * @return the cost of this operation or an error */ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { InvalidateWindowData(WC_COMPANY_LEAGUE, 0, 0); CompanyID company_id = (CompanyID)GB(p1, 16, 8); #ifdef ENABLE_NETWORK ClientID client_id = (ClientID)p2; #endif /* ENABLE_NETWORK */ switch (GB(p1, 0, 16)) { case 0: { // Create a new company /* This command is only executed in a multiplayer game */ if (!_networking) return CMD_ERROR; #ifdef ENABLE_NETWORK /* Has the network client a correct ClientIndex? */ if (!(flags & DC_EXEC)) return CommandCost(); NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id); #ifndef DEBUG_DUMP_COMMANDS /* When replaying the client ID is not a valid client; there * are actually no clients at all. However, the company has to * be created, otherwise we cannot rerun the game properly. * So only allow a NULL client info in that case. */ if (ci == NULL) return CommandCost(); #endif /* NOT DEBUG_DUMP_COMMANDS */ /* Delete multiplayer progress bar */ DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); Company *c = DoStartupNewCompany(false); /* A new company could not be created, revert to being a spectator */ if (c == NULL) { if (_network_server) { ci->client_playas = COMPANY_SPECTATOR; NetworkUpdateClientInfo(ci->client_id); } break; } /* This is the client (or non-dedicated server) who wants a new company */ if (client_id == _network_own_client_id) { assert(_local_company == COMPANY_SPECTATOR); SetLocalCompany(c->index); if (!StrEmpty(_settings_client.network.default_company_pass)) { NetworkChangeCompanyPassword(_local_company, _settings_client.network.default_company_pass); } /* Now that we have a new company, broadcast our company settings to * all clients so everything is in sync */ SyncCompanySettings(); MarkWholeScreenDirty(); } if (_network_server) { if (ci != NULL) { /* ci is only NULL when replaying. * When replaying no client is actually in need of an update. */ ci->client_playas = c->index; NetworkUpdateClientInfo(ci->client_id); } if (Company::IsValidID(c->index)) { _network_company_states[c->index].months_empty = 0; _network_company_states[c->index].password[0] = '\0'; NetworkServerUpdateCompanyPassworded(c->index, false); /* XXX - When a client joins, we automatically set its name to the * client's name (for some reason). As it stands now only the server * knows the client's name, so it needs to send out a "broadcast" to * do this. To achieve this we send a network command. However, it * uses _local_company to execute the command as. To prevent abuse * (eg. only yourself can change your name/company), we 'cheat' by * impersonation _local_company as the server. Not the best solution; * but it works. * TODO: Perhaps this could be improved by when the client is ready * with joining to let it send itself the command, and not the server? * For example in network_client.c:534? */ if (ci != NULL) { /* ci is only NULL when replaying. * When replaying, the command to rename the president will * automatically be ran, so this is not even needed to get * the exact same state. */ NetworkSendCommand(0, 0, 0, CMD_RENAME_PRESIDENT, NULL, ci->client_name, c->index); } } /* Announce new company on network. */ NetworkAdminCompanyInfo(c, true); if (ci != NULL) { /* ci is only NULL when replaying. * When replaying, the message that someone started a new company * is not interesting at all. */ NetworkServerSendChat(NETWORK_ACTION_COMPANY_NEW, DESTTYPE_BROADCAST, 0, "", ci->client_id, c->index + 1); } } #endif /* ENABLE_NETWORK */ break; } case 1: // Make a new AI company if (!(flags & DC_EXEC)) return CommandCost(); if (company_id != INVALID_COMPANY && (company_id >= MAX_COMPANIES || Company::IsValidID(company_id))) return CMD_ERROR; DoStartupNewCompany(true, company_id); break; case 2: { // Delete a company CompanyRemoveReason reason = (CompanyRemoveReason)GB(p2, 0, 2); if (reason >= CRR_END) return CMD_ERROR; Company *c = Company::GetIfValid(company_id); if (c == NULL) return CMD_ERROR; if (!(flags & DC_EXEC)) return CommandCost(); /* Delete any open window of the company */ DeleteCompanyWindows(c->index); CompanyNewsInformation *cni = MallocT<CompanyNewsInformation>(1); cni->FillData(c); /* Show the bankrupt news */ SetDParam(0, STR_NEWS_COMPANY_BANKRUPT_TITLE); SetDParam(1, STR_NEWS_COMPANY_BANKRUPT_DESCRIPTION); SetDParamStr(2, cni->company_name); AddCompanyNewsItem(STR_MESSAGE_NEWS_FORMAT, cni); /* Remove the company */ ChangeOwnershipOfCompanyItems(c->index, INVALID_OWNER); if (c->is_ai) AI::Stop(c->index); CompanyID c_index = c->index; delete c; AI::BroadcastNewEvent(new ScriptEventCompanyBankrupt(c_index)); Game::NewEvent(new ScriptEventCompanyBankrupt(c_index)); CompanyAdminRemove(c_index, (CompanyRemoveReason)reason); break; } default: return CMD_ERROR; } InvalidateWindowClassesData(WC_GAME_OPTIONS); InvalidateWindowClassesData(WC_AI_SETTINGS); InvalidateWindowClassesData(WC_AI_LIST); return CommandCost(); }
/** * Refits a vehicle to the specified cargo type. * @param tile unused * @param flags type of operation * @param p1 vehicle ID to refit * @param p2 various bitstuffed elements * - p2 = (bit 0-4) - New cargo type to refit to. * - p2 = (bit 6) - Automatic refitting. * - p2 = (bit 7) - Refit only this vehicle. Used only for cloning vehicles. * - p2 = (bit 8-15) - New cargo subtype to refit to. 0xFF means to try keeping the same subtype according to GetBestFittingSubType(). * - p2 = (bit 16-23) - Number of vehicles to refit (not counting articulated parts). Zero means all vehicles. * Only used if "refit only this vehicle" is false. * @param text unused * @return the cost of this operation or an error */ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Vehicle *v = Vehicle::GetIfValid(p1); if (v == NULL) return CMD_ERROR; /* Don't allow disasters and sparks and such to be refitted. * We cannot check for IsPrimaryVehicle as autoreplace also refits in free wagon chains. */ if (!IsCompanyBuildableVehicleType(v->type)) return CMD_ERROR; Vehicle *front = v->First(); CommandCost ret = CheckOwnership(front->owner); if (ret.Failed()) return ret; bool auto_refit = HasBit(p2, 6); /* Don't allow shadows and such to be refitted. */ if (v != front && (v->type == VEH_SHIP || v->type == VEH_AIRCRAFT)) return CMD_ERROR; /* Allow auto-refitting only during loading and normal refitting only in a depot. */ if ((!auto_refit || !front->current_order.IsType(OT_LOADING)) && !front->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type); if (front->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED); /* Check cargo */ CargoID new_cid = GB(p2, 0, 5); byte new_subtype = GB(p2, 8, 8); if (new_cid >= NUM_CARGO) return CMD_ERROR; /* For ships and aircrafts there is always only one. */ bool only_this = HasBit(p2, 7) || front->type == VEH_SHIP || front->type == VEH_AIRCRAFT; uint8 num_vehicles = GB(p2, 16, 8); CommandCost cost = RefitVehicle(v, only_this, num_vehicles, new_cid, new_subtype, flags, auto_refit); if (flags & DC_EXEC) { /* Update the cached variables */ switch (v->type) { case VEH_TRAIN: Train::From(front)->ConsistChanged(auto_refit); break; case VEH_ROAD: RoadVehUpdateCache(RoadVehicle::From(front), auto_refit); if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) RoadVehicle::From(front)->CargoChanged(); break; case VEH_SHIP: v->InvalidateNewGRFCacheOfChain(); v->colourmap = PAL_NONE; // invalidate vehicle colour map Ship::From(v)->UpdateCache(); break; case VEH_AIRCRAFT: v->InvalidateNewGRFCacheOfChain(); v->colourmap = PAL_NONE; // invalidate vehicle colour map UpdateAircraftCache(Aircraft::From(v), true); break; default: NOT_REACHED(); } InvalidateWindowData(WC_VEHICLE_DETAILS, front->index); SetWindowDirty(WC_VEHICLE_DEPOT, front->tile); InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); } else { /* Always invalidate the cache; querycost might have filled it. */ v->InvalidateNewGRFCacheOfChain(); } return cost; }