1 /* $Id$ */ |
|
2 |
|
3 /** @file company_cmd.cpp Handling of companies. */ |
|
4 |
|
5 #include "stdafx.h" |
|
6 #include "openttd.h" |
|
7 #include "engine_func.h" |
|
8 #include "engine_base.h" |
|
9 #include "player_func.h" |
|
10 #include "player_gui.h" |
|
11 #include "town.h" |
|
12 #include "news_func.h" |
|
13 #include "saveload.h" |
|
14 #include "command_func.h" |
|
15 #include "network/network.h" |
|
16 #include "network/network_func.h" |
|
17 #include "variables.h" |
|
18 #include "cheat_func.h" |
|
19 #include "ai/ai.h" |
|
20 #include "player_face.h" |
|
21 #include "group.h" |
|
22 #include "window_func.h" |
|
23 #include "tile_map.h" |
|
24 #include "strings_func.h" |
|
25 #include "gfx_func.h" |
|
26 #include "functions.h" |
|
27 #include "date_func.h" |
|
28 #include "vehicle_func.h" |
|
29 #include "sound_func.h" |
|
30 #include "core/alloc_func.hpp" |
|
31 #include "core/sort_func.hpp" |
|
32 #include "autoreplace_func.h" |
|
33 #include "autoreplace_gui.h" |
|
34 #include "string_func.h" |
|
35 #include "ai/default/default.h" |
|
36 #include "ai/trolly/trolly.h" |
|
37 #include "road_func.h" |
|
38 #include "rail.h" |
|
39 #include "sprite.h" |
|
40 #include "debug.h" |
|
41 #include "oldpool_func.h" |
|
42 |
|
43 #include "table/strings.h" |
|
44 #include "table/sprites.h" |
|
45 |
|
46 CompanyByte _local_company; |
|
47 CompanyByte _current_company; |
|
48 /* NOSAVE: can be determined from company structs */ |
|
49 byte _company_colours[MAX_COMPANIES]; |
|
50 CompanyManagerFace _company_manager_face; ///< for company manager face storage in openttd.cfg |
|
51 HighScore _highscore_table[5][5]; // 4 difficulty-settings (+ network); top 5 |
|
52 |
|
53 DEFINE_OLD_POOL_GENERIC(Company, Company) |
|
54 |
|
55 Company::Company(uint16 name_1, bool is_ai) : name_1(name_1), is_ai(is_ai) |
|
56 { |
|
57 for (uint j = 0; j < 4; j++) this->share_owners[j] = COMPANY_SPECTATOR; |
|
58 } |
|
59 |
|
60 Company::~Company() |
|
61 { |
|
62 free(this->name); |
|
63 free(this->president_name); |
|
64 free(this->num_engines); |
|
65 |
|
66 if (CleaningPool()) return; |
|
67 |
|
68 DeleteCompanyWindows(this->index); |
|
69 this->name_1 = 0; |
|
70 } |
|
71 |
|
72 /** |
|
73 * Sets the local company and updates the patch settings that are set on a |
|
74 * per-company basis to reflect the core's state in the GUI. |
|
75 * @param new_company the new company |
|
76 * @pre IsValidCompanyID(new_company) || new_company == COMPANY_SPECTATOR || new_company == OWNER_NONE |
|
77 */ |
|
78 void SetLocalCompany(CompanyID new_company) |
|
79 { |
|
80 /* company could also be COMPANY_SPECTATOR or OWNER_NONE */ |
|
81 assert(IsValidCompanyID(new_company) || new_company == COMPANY_SPECTATOR || new_company == OWNER_NONE); |
|
82 |
|
83 _local_company = new_company; |
|
84 |
|
85 /* Do not update the patches if we are in the intro GUI */ |
|
86 if (IsValidCompanyID(new_company) && _game_mode != GM_MENU) { |
|
87 const Company *p = GetCompany(new_company); |
|
88 _settings_client.gui.autorenew = p->engine_renew; |
|
89 _settings_client.gui.autorenew_months = p->engine_renew_months; |
|
90 _settings_client.gui.autorenew_money = p->engine_renew_money; |
|
91 InvalidateWindow(WC_GAME_OPTIONS, 0); |
|
92 } |
|
93 } |
|
94 |
|
95 bool IsHumanCompany(CompanyID company) |
|
96 { |
|
97 return !GetCompany(company)->is_ai; |
|
98 } |
|
99 |
|
100 |
|
101 uint16 GetDrawStringCompanyColor(CompanyID company) |
|
102 { |
|
103 /* Get the color for DrawString-subroutines which matches the color |
|
104 * of the company */ |
|
105 if (!IsValidCompanyID(company)) return _colour_gradient[COLOUR_WHITE][4] | IS_PALETTE_COLOR; |
|
106 return (_colour_gradient[_company_colours[company]][4]) | IS_PALETTE_COLOR; |
|
107 } |
|
108 |
|
109 void DrawCompanyIcon(CompanyID p, int x, int y) |
|
110 { |
|
111 DrawSprite(SPR_PLAYER_ICON, COMPANY_SPRITE_COLOR(p), x, y); |
|
112 } |
|
113 |
|
114 /** |
|
115 * Converts an old company manager's face format to the new company manager's face format |
|
116 * |
|
117 * Meaning of the bits in the old face (some bits are used in several times): |
|
118 * - 4 and 5: chin |
|
119 * - 6 to 9: eyebrows |
|
120 * - 10 to 13: nose |
|
121 * - 13 to 15: lips (also moustache for males) |
|
122 * - 16 to 19: hair |
|
123 * - 20 to 22: eye color |
|
124 * - 20 to 27: tie, ear rings etc. |
|
125 * - 28 to 30: glasses |
|
126 * - 19, 26 and 27: race (bit 27 set and bit 19 equal to bit 26 = black, otherwise white) |
|
127 * - 31: gender (0 = male, 1 = female) |
|
128 * |
|
129 * @param face the face in the old format |
|
130 * @return the face in the new format |
|
131 */ |
|
132 CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face) |
|
133 { |
|
134 CompanyManagerFace cmf = 0; |
|
135 GenderEthnicity ge = GE_WM; |
|
136 |
|
137 if (HasBit(face, 31)) SetBit(ge, GENDER_FEMALE); |
|
138 if (HasBit(face, 27) && (HasBit(face, 26) == HasBit(face, 19))) SetBit(ge, ETHNICITY_BLACK); |
|
139 |
|
140 SetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, ge, ge); |
|
141 SetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge, GB(face, 28, 3) <= 1); |
|
142 SetCompanyManagerFaceBits(cmf, CMFV_EYE_COLOUR, ge, HasBit(ge, ETHNICITY_BLACK) ? 0 : ClampU(GB(face, 20, 3), 5, 7) - 5); |
|
143 SetCompanyManagerFaceBits(cmf, CMFV_CHIN, ge, ScaleCompanyManagerFaceValue(CMFV_CHIN, ge, GB(face, 4, 2))); |
|
144 SetCompanyManagerFaceBits(cmf, CMFV_EYEBROWS, ge, ScaleCompanyManagerFaceValue(CMFV_EYEBROWS, ge, GB(face, 6, 4))); |
|
145 SetCompanyManagerFaceBits(cmf, CMFV_HAIR, ge, ScaleCompanyManagerFaceValue(CMFV_HAIR, ge, GB(face, 16, 4))); |
|
146 SetCompanyManagerFaceBits(cmf, CMFV_JACKET, ge, ScaleCompanyManagerFaceValue(CMFV_JACKET, ge, GB(face, 20, 2))); |
|
147 SetCompanyManagerFaceBits(cmf, CMFV_COLLAR, ge, ScaleCompanyManagerFaceValue(CMFV_COLLAR, ge, GB(face, 22, 2))); |
|
148 SetCompanyManagerFaceBits(cmf, CMFV_GLASSES, ge, GB(face, 28, 1)); |
|
149 |
|
150 uint lips = GB(face, 10, 4); |
|
151 if (!HasBit(ge, GENDER_FEMALE) && lips < 4) { |
|
152 SetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge, true); |
|
153 SetCompanyManagerFaceBits(cmf, CMFV_MOUSTACHE, ge, max(lips, 1U) - 1); |
|
154 } else { |
|
155 if (!HasBit(ge, GENDER_FEMALE)) { |
|
156 lips = lips * 15 / 16; |
|
157 lips -= 3; |
|
158 if (HasBit(ge, ETHNICITY_BLACK) && lips > 8) lips = 0; |
|
159 } else { |
|
160 lips = ScaleCompanyManagerFaceValue(CMFV_LIPS, ge, lips); |
|
161 } |
|
162 SetCompanyManagerFaceBits(cmf, CMFV_LIPS, ge, lips); |
|
163 |
|
164 uint nose = GB(face, 13, 3); |
|
165 if (ge == GE_WF) { |
|
166 nose = (nose * 3 >> 3) * 3 >> 2; // There is 'hole' in the nose sprites for females |
|
167 } else { |
|
168 nose = ScaleCompanyManagerFaceValue(CMFV_NOSE, ge, nose); |
|
169 } |
|
170 SetCompanyManagerFaceBits(cmf, CMFV_NOSE, ge, nose); |
|
171 } |
|
172 |
|
173 uint tie_earring = GB(face, 24, 4); |
|
174 if (!HasBit(ge, GENDER_FEMALE) || tie_earring < 3) { // Not all females have an earring |
|
175 if (HasBit(ge, GENDER_FEMALE)) SetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge, true); |
|
176 SetCompanyManagerFaceBits(cmf, CMFV_TIE_EARRING, ge, HasBit(ge, GENDER_FEMALE) ? tie_earring : ScaleCompanyManagerFaceValue(CMFV_TIE_EARRING, ge, tie_earring / 2)); |
|
177 } |
|
178 |
|
179 return cmf; |
|
180 } |
|
181 |
|
182 /** |
|
183 * Checks whether a company manager's face is a valid encoding. |
|
184 * Unused bits are not enforced to be 0. |
|
185 * @param cmf the fact to check |
|
186 * @return true if and only if the face is valid |
|
187 */ |
|
188 bool IsValidCompanyManagerFace(CompanyManagerFace cmf) |
|
189 { |
|
190 if (!AreCompanyManagerFaceBitsValid(cmf, CMFV_GEN_ETHN, GE_WM)) return false; |
|
191 |
|
192 GenderEthnicity ge = (GenderEthnicity)GetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, GE_WM); |
|
193 bool has_moustache = !HasBit(ge, GENDER_FEMALE) && GetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge) != 0; |
|
194 bool has_tie_earring = !HasBit(ge, GENDER_FEMALE) || GetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge) != 0; |
|
195 bool has_glasses = GetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge) != 0; |
|
196 |
|
197 if (!AreCompanyManagerFaceBitsValid(cmf, CMFV_EYE_COLOUR, ge)) return false; |
|
198 for (CompanyManagerFaceVariable cmfv = CMFV_CHEEKS; cmfv < CMFV_END; cmfv++) { |
|
199 switch (cmfv) { |
|
200 case CMFV_MOUSTACHE: if (!has_moustache) continue; break; |
|
201 case CMFV_LIPS: /* FALL THROUGH */ |
|
202 case CMFV_NOSE: if (has_moustache) continue; break; |
|
203 case CMFV_TIE_EARRING: if (!has_tie_earring) continue; break; |
|
204 case CMFV_GLASSES: if (!has_glasses) continue; break; |
|
205 default: break; |
|
206 } |
|
207 if (!AreCompanyManagerFaceBitsValid(cmf, cmfv, ge)) return false; |
|
208 } |
|
209 |
|
210 return true; |
|
211 } |
|
212 |
|
213 void InvalidateCompanyWindows(const Company *company) |
|
214 { |
|
215 CompanyID cid = company->index; |
|
216 |
|
217 if (cid == _local_company) InvalidateWindow(WC_STATUS_BAR, 0); |
|
218 InvalidateWindow(WC_FINANCES, cid); |
|
219 } |
|
220 |
|
221 bool CheckCompanyHasMoney(CommandCost cost) |
|
222 { |
|
223 if (cost.GetCost() > 0) { |
|
224 CompanyID company = _current_company; |
|
225 if (IsValidCompanyID(company) && cost.GetCost() > GetCompany(company)->money) { |
|
226 SetDParam(0, cost.GetCost()); |
|
227 _error_message = STR_0003_NOT_ENOUGH_CASH_REQUIRES; |
|
228 return false; |
|
229 } |
|
230 } |
|
231 return true; |
|
232 } |
|
233 |
|
234 static void SubtractMoneyFromAnyCompany(Company *c, CommandCost cost) |
|
235 { |
|
236 if (cost.GetCost() == 0) return; |
|
237 assert(cost.GetExpensesType() != INVALID_EXPENSES); |
|
238 |
|
239 c->money -= cost.GetCost(); |
|
240 c->yearly_expenses[0][cost.GetExpensesType()] += cost.GetCost(); |
|
241 |
|
242 if (HasBit(1 << EXPENSES_TRAIN_INC | |
|
243 1 << EXPENSES_ROADVEH_INC | |
|
244 1 << EXPENSES_AIRCRAFT_INC | |
|
245 1 << EXPENSES_SHIP_INC, cost.GetExpensesType())) { |
|
246 c->cur_economy.income -= cost.GetCost(); |
|
247 } else if (HasBit(1 << EXPENSES_TRAIN_RUN | |
|
248 1 << EXPENSES_ROADVEH_RUN | |
|
249 1 << EXPENSES_AIRCRAFT_RUN | |
|
250 1 << EXPENSES_SHIP_RUN | |
|
251 1 << EXPENSES_PROPERTY | |
|
252 1 << EXPENSES_LOAN_INT, cost.GetExpensesType())) { |
|
253 c->cur_economy.expenses -= cost.GetCost(); |
|
254 } |
|
255 |
|
256 InvalidateCompanyWindows(c); |
|
257 } |
|
258 |
|
259 void SubtractMoneyFromCompany(CommandCost cost) |
|
260 { |
|
261 CompanyID cid = _current_company; |
|
262 |
|
263 if (IsValidCompanyID(cid)) SubtractMoneyFromAnyCompany(GetCompany(cid), cost); |
|
264 } |
|
265 |
|
266 void SubtractMoneyFromCompanyFract(CompanyID company, CommandCost cst) |
|
267 { |
|
268 Company *c = GetCompany(company); |
|
269 byte m = c->money_fraction; |
|
270 Money cost = cst.GetCost(); |
|
271 |
|
272 c->money_fraction = m - (byte)cost; |
|
273 cost >>= 8; |
|
274 if (c->money_fraction > m) cost++; |
|
275 if (cost != 0) SubtractMoneyFromAnyCompany(c, CommandCost(cst.GetExpensesType(), cost)); |
|
276 } |
|
277 |
|
278 void GetNameOfOwner(Owner owner, TileIndex tile) |
|
279 { |
|
280 SetDParam(2, owner); |
|
281 |
|
282 if (owner != OWNER_TOWN) { |
|
283 if (!IsValidCompanyID(owner)) { |
|
284 SetDParam(0, STR_0150_SOMEONE); |
|
285 } else { |
|
286 SetDParam(0, STR_COMPANY_NAME); |
|
287 SetDParam(1, owner); |
|
288 } |
|
289 } else { |
|
290 const Town *t = ClosestTownFromTile(tile, (uint)-1); |
|
291 |
|
292 SetDParam(0, STR_TOWN); |
|
293 SetDParam(1, t->index); |
|
294 } |
|
295 } |
|
296 |
|
297 |
|
298 bool CheckOwnership(Owner owner) |
|
299 { |
|
300 assert(owner < OWNER_END); |
|
301 |
|
302 if (owner == _current_company) return true; |
|
303 _error_message = STR_013B_OWNED_BY; |
|
304 GetNameOfOwner(owner, 0); |
|
305 return false; |
|
306 } |
|
307 |
|
308 bool CheckTileOwnership(TileIndex tile) |
|
309 { |
|
310 Owner owner = GetTileOwner(tile); |
|
311 |
|
312 assert(owner < OWNER_END); |
|
313 |
|
314 if (owner == _current_company) return true; |
|
315 _error_message = STR_013B_OWNED_BY; |
|
316 |
|
317 /* no need to get the name of the owner unless we're the local company (saves some time) */ |
|
318 if (IsLocalCompany()) GetNameOfOwner(owner, tile); |
|
319 return false; |
|
320 } |
|
321 |
|
322 static void GenerateCompanyName(Company *c) |
|
323 { |
|
324 TileIndex tile; |
|
325 Town *t; |
|
326 StringID str; |
|
327 Company *cc; |
|
328 uint32 strp; |
|
329 char buffer[100]; |
|
330 |
|
331 if (c->name_1 != STR_SV_UNNAMED) return; |
|
332 |
|
333 tile = c->last_build_coordinate; |
|
334 if (tile == 0) return; |
|
335 |
|
336 t = ClosestTownFromTile(tile, (uint)-1); |
|
337 |
|
338 if (t->name == NULL && IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1)) { |
|
339 str = t->townnametype - SPECSTR_TOWNNAME_START + SPECSTR_PLAYERNAME_START; |
|
340 strp = t->townnameparts; |
|
341 |
|
342 verify_name:; |
|
343 /* No companies must have this name already */ |
|
344 FOR_ALL_COMPANIES(cc) { |
|
345 if (cc->name_1 == str && cc->name_2 == strp) goto bad_town_name; |
|
346 } |
|
347 |
|
348 GetString(buffer, str, lastof(buffer)); |
|
349 if (strlen(buffer) >= MAX_LENGTH_COMPANY_NAME_BYTES) goto bad_town_name; |
|
350 |
|
351 set_name:; |
|
352 c->name_1 = str; |
|
353 c->name_2 = strp; |
|
354 |
|
355 MarkWholeScreenDirty(); |
|
356 |
|
357 if (!IsHumanCompany(c->index)) { |
|
358 CompanyNewsInformation *cni = MallocT<CompanyNewsInformation>(1); |
|
359 cni->FillData(c); |
|
360 SetDParam(0, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED); |
|
361 SetDParam(1, STR_705F_STARTS_CONSTRUCTION_NEAR); |
|
362 SetDParamStr(2, cni->company_name); |
|
363 SetDParam(3, t->index); |
|
364 AddNewsItem(STR_02B6, NS_COMPANY_NEW, c->last_build_coordinate, 0, cni); |
|
365 } |
|
366 return; |
|
367 } |
|
368 bad_town_name:; |
|
369 |
|
370 if (c->president_name_1 == SPECSTR_PRESIDENT_NAME) { |
|
371 str = SPECSTR_ANDCO_NAME; |
|
372 strp = c->president_name_2; |
|
373 goto set_name; |
|
374 } else { |
|
375 str = SPECSTR_ANDCO_NAME; |
|
376 strp = Random(); |
|
377 goto verify_name; |
|
378 } |
|
379 } |
|
380 |
|
381 static const byte _colour_sort[COLOUR_END] = {2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 2, 3, 1, 1, 1}; |
|
382 static const Colours _similar_colour[COLOUR_END][2] = { |
|
383 { COLOUR_BLUE, COLOUR_LIGHT_BLUE }, // COLOUR_DARK_BLUE |
|
384 { COLOUR_GREEN, COLOUR_DARK_GREEN }, // COLOUR_PALE_GREEN |
|
385 { INVALID_COLOUR, INVALID_COLOUR }, // COLOUR_PINK |
|
386 { COLOUR_ORANGE, INVALID_COLOUR }, // COLOUR_YELLOW |
|
387 { INVALID_COLOUR, INVALID_COLOUR }, // COLOUR_RED |
|
388 { COLOUR_DARK_BLUE, COLOUR_BLUE }, // COLOUR_LIGHT_BLUE |
|
389 { COLOUR_PALE_GREEN, COLOUR_DARK_GREEN }, // COLOUR_GREEN |
|
390 { COLOUR_PALE_GREEN, COLOUR_GREEN }, // COLOUR_DARK_GREEN |
|
391 { COLOUR_DARK_BLUE, COLOUR_LIGHT_BLUE }, // COLOUR_BLUE |
|
392 { COLOUR_BROWN, COLOUR_ORANGE }, // COLOUR_CREAM |
|
393 { COLOUR_PURPLE, INVALID_COLOUR }, // COLOUR_MAUVE |
|
394 { COLOUR_MAUVE, INVALID_COLOUR }, // COLOUR_PURPLE |
|
395 { COLOUR_YELLOW, COLOUR_CREAM }, // COLOUR_ORANGE |
|
396 { COLOUR_CREAM, INVALID_COLOUR }, // COLOUR_BROWN |
|
397 { COLOUR_WHITE, INVALID_COLOUR }, // COLOUR_GREY |
|
398 { COLOUR_GREY, INVALID_COLOUR }, // COLOUR_WHITE |
|
399 }; |
|
400 |
|
401 static byte GenerateCompanyColour() |
|
402 { |
|
403 Colours colours[COLOUR_END]; |
|
404 |
|
405 /* Initialize array */ |
|
406 for (uint i = 0; i < COLOUR_END; i++) colours[i] = (Colours)i; |
|
407 |
|
408 /* And randomize it */ |
|
409 for (uint i = 0; i < 100; i++) { |
|
410 uint r = Random(); |
|
411 Swap(colours[GB(r, 0, 4)], colours[GB(r, 4, 4)]); |
|
412 } |
|
413 |
|
414 /* Bubble sort it according to the values in table 1 */ |
|
415 for (uint i = 0; i < COLOUR_END; i++) { |
|
416 for (uint j = 1; j < COLOUR_END; j++) { |
|
417 if (_colour_sort[colours[j - 1]] < _colour_sort[colours[j]]) { |
|
418 Swap(colours[j - 1], colours[j]); |
|
419 } |
|
420 } |
|
421 }; |
|
422 |
|
423 /* Move the colors that look similar to each company's color to the side */ |
|
424 Company *c; |
|
425 FOR_ALL_COMPANIES(c) { |
|
426 Colours pcolour = (Colours)c->colour; |
|
427 |
|
428 for (uint i = 0; i < COLOUR_END; i++) { |
|
429 if (colours[i] == pcolour) { |
|
430 colours[i] = INVALID_COLOUR; |
|
431 break; |
|
432 } |
|
433 } |
|
434 |
|
435 for (uint j = 0; j < 2; j++) { |
|
436 Colours similar = _similar_colour[pcolour][j]; |
|
437 if (similar == INVALID_COLOUR) break; |
|
438 |
|
439 for (uint i = 1; i < COLOUR_END; i++) { |
|
440 if (colours[i - 1] == similar) Swap(colours[i - 1], colours[i]); |
|
441 } |
|
442 } |
|
443 } |
|
444 |
|
445 /* Return the first available color */ |
|
446 for (uint i = 0; i < COLOUR_END; i++) { |
|
447 if (colours[i] != INVALID_COLOUR) return colours[i]; |
|
448 } |
|
449 |
|
450 NOT_REACHED(); |
|
451 } |
|
452 |
|
453 static void GeneratePresidentName(Company *c) |
|
454 { |
|
455 Company *cc; |
|
456 char buffer[100], buffer2[40]; |
|
457 |
|
458 for (;;) { |
|
459 restart:; |
|
460 |
|
461 c->president_name_2 = Random(); |
|
462 c->president_name_1 = SPECSTR_PRESIDENT_NAME; |
|
463 |
|
464 SetDParam(0, c->index); |
|
465 GetString(buffer, STR_PLAYER_NAME, lastof(buffer)); |
|
466 if (strlen(buffer) >= 32 || GetStringBoundingBox(buffer).width >= 94) |
|
467 continue; |
|
468 |
|
469 FOR_ALL_COMPANIES(cc) { |
|
470 if (c != cc) { |
|
471 SetDParam(0, cc->index); |
|
472 GetString(buffer2, STR_PLAYER_NAME, lastof(buffer2)); |
|
473 if (strcmp(buffer2, buffer) == 0) |
|
474 goto restart; |
|
475 } |
|
476 } |
|
477 return; |
|
478 } |
|
479 } |
|
480 |
|
481 void ResetCompanyLivery(Company *c) |
|
482 { |
|
483 for (LiveryScheme scheme = LS_BEGIN; scheme < LS_END; scheme++) { |
|
484 c->livery[scheme].in_use = false; |
|
485 c->livery[scheme].colour1 = c->colour; |
|
486 c->livery[scheme].colour2 = c->colour; |
|
487 } |
|
488 } |
|
489 |
|
490 /** |
|
491 * Create a new company and sets all company variables default values |
|
492 * |
|
493 * @param is_ai is a ai company? |
|
494 * @return the company struct |
|
495 */ |
|
496 Company *DoStartupNewCompany(bool is_ai) |
|
497 { |
|
498 if (!Company::CanAllocateItem()) return NULL; |
|
499 |
|
500 Company *c = new Company(STR_SV_UNNAMED, is_ai); |
|
501 |
|
502 memset(&_companies_ai[c->index], 0, sizeof(CompanyAI)); |
|
503 memset(&_companies_ainew[c->index], 0, sizeof(CompanyAiNew)); |
|
504 |
|
505 /* Make a color */ |
|
506 c->colour = GenerateCompanyColour(); |
|
507 ResetCompanyLivery(c); |
|
508 _company_colours[c->index] = c->colour; |
|
509 |
|
510 c->money = c->current_loan = 100000; |
|
511 |
|
512 _companies_ai[c->index].state = 5; // AIS_WANT_NEW_ROUTE |
|
513 c->share_owners[0] = c->share_owners[1] = c->share_owners[2] = c->share_owners[3] = INVALID_OWNER; |
|
514 |
|
515 c->avail_railtypes = GetCompanyRailtypes(c->index); |
|
516 c->avail_roadtypes = GetCompanyRoadtypes(c->index); |
|
517 c->inaugurated_year = _cur_year; |
|
518 RandomCompanyManagerFaceBits(c->face, (GenderEthnicity)Random(), false); // create a random company manager face |
|
519 |
|
520 /* Engine renewal settings */ |
|
521 c->engine_renew_list = NULL; |
|
522 c->renew_keep_length = false; |
|
523 c->engine_renew = _settings_client.gui.autorenew; |
|
524 c->engine_renew_months = _settings_client.gui.autorenew_months; |
|
525 c->engine_renew_money = _settings_client.gui.autorenew_money; |
|
526 |
|
527 GeneratePresidentName(c); |
|
528 |
|
529 InvalidateWindow(WC_GRAPH_LEGEND, 0); |
|
530 InvalidateWindow(WC_TOOLBAR_MENU, 0); |
|
531 InvalidateWindow(WC_CLIENT_LIST, 0); |
|
532 |
|
533 if (is_ai && (!_networking || _network_server) && _ai.enabled) |
|
534 AI_StartNewAI(c->index); |
|
535 |
|
536 c->num_engines = CallocT<uint16>(GetEnginePoolSize()); |
|
537 |
|
538 return c; |
|
539 } |
|
540 |
|
541 void StartupCompanies() |
|
542 { |
|
543 /* The AI starts like in the setting with +2 month max */ |
|
544 _next_competitor_start = _settings_game.difficulty.competitor_start_time * 90 * DAY_TICKS + RandomRange(60 * DAY_TICKS) + 1; |
|
545 } |
|
546 |
|
547 static void MaybeStartNewCompany() |
|
548 { |
|
549 uint n; |
|
550 Company *c; |
|
551 |
|
552 /* count number of competitors */ |
|
553 n = 0; |
|
554 FOR_ALL_COMPANIES(c) { |
|
555 if (c->is_ai) n++; |
|
556 } |
|
557 |
|
558 /* when there's a lot of computers in game, the probability that a new one starts is lower */ |
|
559 if (n < (uint)_settings_game.difficulty.max_no_competitors && |
|
560 n < (_network_server ? |
|
561 InteractiveRandomRange(_settings_game.difficulty.max_no_competitors + 2) : |
|
562 RandomRange(_settings_game.difficulty.max_no_competitors + 2) |
|
563 )) { |
|
564 /* Send a command to all clients to start up a new AI. |
|
565 * Works fine for Multiplayer and Singleplayer */ |
|
566 DoCommandP(0, 1, 0, NULL, CMD_COMPANY_CTRL); |
|
567 } |
|
568 |
|
569 /* The next AI starts like the difficulty setting said, with +2 month max */ |
|
570 _next_competitor_start = _settings_game.difficulty.competitor_start_time * 90 * DAY_TICKS + 1; |
|
571 _next_competitor_start += _network_server ? InteractiveRandomRange(60 * DAY_TICKS) : RandomRange(60 * DAY_TICKS); |
|
572 } |
|
573 |
|
574 void InitializeCompanies() |
|
575 { |
|
576 _Company_pool.CleanPool(); |
|
577 _Company_pool.AddBlockToPool(); |
|
578 _cur_company_tick_index = 0; |
|
579 } |
|
580 |
|
581 void OnTick_Companies() |
|
582 { |
|
583 if (_game_mode == GM_EDITOR) return; |
|
584 |
|
585 if (IsValidCompanyID((CompanyID)_cur_company_tick_index)) { |
|
586 Company *c = GetCompany((CompanyID)_cur_company_tick_index); |
|
587 if (c->name_1 != 0) GenerateCompanyName(c); |
|
588 |
|
589 if (AI_AllowNewAI() && _game_mode != GM_MENU && !--_next_competitor_start) { |
|
590 MaybeStartNewCompany(); |
|
591 } |
|
592 } |
|
593 |
|
594 _cur_company_tick_index = (_cur_company_tick_index + 1) % MAX_COMPANIES; |
|
595 } |
|
596 |
|
597 void CompaniesYearlyLoop() |
|
598 { |
|
599 Company *c; |
|
600 |
|
601 /* Copy statistics */ |
|
602 FOR_ALL_COMPANIES(c) { |
|
603 memmove(&c->yearly_expenses[1], &c->yearly_expenses[0], sizeof(c->yearly_expenses) - sizeof(c->yearly_expenses[0])); |
|
604 memset(&c->yearly_expenses[0], 0, sizeof(c->yearly_expenses[0])); |
|
605 InvalidateWindow(WC_FINANCES, c->index); |
|
606 } |
|
607 |
|
608 if (_settings_client.gui.show_finances && _local_company != COMPANY_SPECTATOR) { |
|
609 ShowCompanyFinances(_local_company); |
|
610 c = GetCompany(_local_company); |
|
611 if (c->num_valid_stat_ent > 5 && c->old_economy[0].performance_history < c->old_economy[4].performance_history) { |
|
612 SndPlayFx(SND_01_BAD_YEAR); |
|
613 } else { |
|
614 SndPlayFx(SND_00_GOOD_YEAR); |
|
615 } |
|
616 } |
|
617 } |
|
618 |
|
619 /** Change engine renewal parameters |
|
620 * @param tile unused |
|
621 * @param flags operation to perform |
|
622 * @param p1 bits 0-3 command |
|
623 * - p1 = 0 - change auto renew bool |
|
624 * - p1 = 1 - change auto renew months |
|
625 * - p1 = 2 - change auto renew money |
|
626 * - p1 = 3 - change auto renew array |
|
627 * - p1 = 4 - change bool, months & money all together |
|
628 * - p1 = 5 - change renew_keep_length |
|
629 * @param p2 value to set |
|
630 * if p1 = 0, then: |
|
631 * - p2 = enable engine renewal |
|
632 * if p1 = 1, then: |
|
633 * - p2 = months left before engine expires to replace it |
|
634 * if p1 = 2, then |
|
635 * - p2 = minimum amount of money available |
|
636 * if p1 = 3, then: |
|
637 * - p1 bits 16-31 = engine group |
|
638 * - p2 bits 0-15 = old engine type |
|
639 * - p2 bits 16-31 = new engine type |
|
640 * if p1 = 4, then: |
|
641 * - p1 bit 15 = enable engine renewal |
|
642 * - p1 bits 16-31 = months left before engine expires to replace it |
|
643 * - p2 bits 0-31 = minimum amount of money available |
|
644 * if p1 = 5, then |
|
645 * - p2 = enable renew_keep_length |
|
646 */ |
|
647 CommandCost CmdSetAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
648 { |
|
649 if (!IsValidCompanyID(_current_company)) return CMD_ERROR; |
|
650 |
|
651 Company *c = GetCompany(_current_company); |
|
652 switch (GB(p1, 0, 3)) { |
|
653 case 0: |
|
654 if (c->engine_renew == HasBit(p2, 0)) return CMD_ERROR; |
|
655 |
|
656 if (flags & DC_EXEC) { |
|
657 c->engine_renew = HasBit(p2, 0); |
|
658 if (IsLocalCompany()) { |
|
659 _settings_client.gui.autorenew = c->engine_renew; |
|
660 InvalidateWindow(WC_GAME_OPTIONS, 0); |
|
661 } |
|
662 } |
|
663 break; |
|
664 |
|
665 case 1: |
|
666 if (Clamp((int16)p2, -12, 12) != (int16)p2) return CMD_ERROR; |
|
667 if (c->engine_renew_months == (int16)p2) return CMD_ERROR; |
|
668 |
|
669 if (flags & DC_EXEC) { |
|
670 c->engine_renew_months = (int16)p2; |
|
671 if (IsLocalCompany()) { |
|
672 _settings_client.gui.autorenew_months = c->engine_renew_months; |
|
673 InvalidateWindow(WC_GAME_OPTIONS, 0); |
|
674 } |
|
675 } |
|
676 break; |
|
677 |
|
678 case 2: |
|
679 if (ClampU(p2, 0, 2000000) != p2) return CMD_ERROR; |
|
680 if (c->engine_renew_money == p2) return CMD_ERROR; |
|
681 |
|
682 if (flags & DC_EXEC) { |
|
683 c->engine_renew_money = p2; |
|
684 if (IsLocalCompany()) { |
|
685 _settings_client.gui.autorenew_money = c->engine_renew_money; |
|
686 InvalidateWindow(WC_GAME_OPTIONS, 0); |
|
687 } |
|
688 } |
|
689 break; |
|
690 |
|
691 case 3: { |
|
692 EngineID old_engine_type = GB(p2, 0, 16); |
|
693 EngineID new_engine_type = GB(p2, 16, 16); |
|
694 GroupID id_g = GB(p1, 16, 16); |
|
695 CommandCost cost; |
|
696 |
|
697 if (!IsValidGroupID(id_g) && !IsAllGroupID(id_g) && !IsDefaultGroupID(id_g)) return CMD_ERROR; |
|
698 if (new_engine_type != INVALID_ENGINE) { |
|
699 if (!CheckAutoreplaceValidity(old_engine_type, new_engine_type, _current_company)) return CMD_ERROR; |
|
700 |
|
701 cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, flags); |
|
702 } else { |
|
703 cost = RemoveEngineReplacementForCompany(c, old_engine_type, id_g, flags); |
|
704 } |
|
705 |
|
706 if (IsLocalCompany()) InvalidateAutoreplaceWindow(old_engine_type, id_g); |
|
707 |
|
708 return cost; |
|
709 } |
|
710 |
|
711 case 4: |
|
712 if (Clamp((int16)GB(p1, 16, 16), -12, 12) != (int16)GB(p1, 16, 16)) return CMD_ERROR; |
|
713 if (ClampU(p2, 0, 2000000) != p2) return CMD_ERROR; |
|
714 |
|
715 if (flags & DC_EXEC) { |
|
716 c->engine_renew = HasBit(p1, 15); |
|
717 c->engine_renew_months = (int16)GB(p1, 16, 16); |
|
718 c->engine_renew_money = p2; |
|
719 |
|
720 if (IsLocalCompany()) { |
|
721 _settings_client.gui.autorenew = c->engine_renew; |
|
722 _settings_client.gui.autorenew_months = c->engine_renew_months; |
|
723 _settings_client.gui.autorenew_money = c->engine_renew_money; |
|
724 InvalidateWindow(WC_GAME_OPTIONS, 0); |
|
725 } |
|
726 } |
|
727 break; |
|
728 |
|
729 case 5: |
|
730 if (c->renew_keep_length == HasBit(p2, 0)) return CMD_ERROR; |
|
731 |
|
732 if (flags & DC_EXEC) { |
|
733 c->renew_keep_length = HasBit(p2, 0); |
|
734 if (IsLocalCompany()) { |
|
735 InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN); |
|
736 } |
|
737 } |
|
738 break; |
|
739 } |
|
740 |
|
741 return CommandCost(); |
|
742 } |
|
743 |
|
744 /** |
|
745 * Fill the CompanyNewsInformation struct with the required data. |
|
746 * @param p the current company. |
|
747 * @param other the other company. |
|
748 */ |
|
749 void CompanyNewsInformation::FillData(const Company *c, const Company *other) |
|
750 { |
|
751 SetDParam(0, c->index); |
|
752 GetString(this->company_name, STR_COMPANY_NAME, lastof(this->company_name)); |
|
753 |
|
754 if (other == NULL) { |
|
755 *this->other_company_name = '\0'; |
|
756 } else { |
|
757 SetDParam(0, other->index); |
|
758 GetString(this->other_company_name, STR_COMPANY_NAME, lastof(this->other_company_name)); |
|
759 c = other; |
|
760 } |
|
761 |
|
762 SetDParam(0, c->index); |
|
763 GetString(this->president_name, STR_7058_PRESIDENT, lastof(this->president_name)); |
|
764 |
|
765 this->colour = c->colour; |
|
766 this->face = c->face; |
|
767 |
|
768 } |
|
769 |
|
770 /** Control the companies: add, delete, etc. |
|
771 * @param tile unused |
|
772 * @param flags operation to perform |
|
773 * @param p1 various functionality |
|
774 * - p1 = 0 - create a new company, Which company (network) it will be is in p2 |
|
775 * - p1 = 1 - create a new AI company |
|
776 * - p1 = 2 - delete a company. Company is identified by p2 |
|
777 * - p1 = 3 - merge two companies together. merge #1 with #2. Identified by p2 |
|
778 * @param p2 various functionality, dictated by p1 |
|
779 * - p1 = 0 - ClientID of the newly created client |
|
780 * - p1 = 2 - CompanyID of the that is getting deleted |
|
781 * - p1 = 3 - #1 p2 = (bit 0-15) - company to merge (p2 & 0xFFFF) |
|
782 * - #2 p2 = (bit 16-31) - company to be merged into ((p2>>16)&0xFFFF) |
|
783 * @todo In the case of p1=0, create new company, the clientID of the new client is in parameter |
|
784 * p2. This parameter is passed in at function DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) |
|
785 * on the server itself. First of all this is unbelievably ugly; second of all, well, |
|
786 * it IS ugly! <b>Someone fix this up :)</b> So where to fix?@n |
|
787 * @arg - network_server.c:838 DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)@n |
|
788 * @arg - network_client.c:536 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) from where the map has been received |
|
789 */ |
|
790 CommandCost CmdCompanyCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
791 { |
|
792 if (flags & DC_EXEC) _current_company = OWNER_NONE; |
|
793 |
|
794 InvalidateWindowData(WC_COMPANY_LEAGUE, 0, 0); |
|
795 |
|
796 switch (p1) { |
|
797 case 0: { /* Create a new company */ |
|
798 /* This command is only executed in a multiplayer game */ |
|
799 if (!_networking) return CMD_ERROR; |
|
800 |
|
801 #ifdef ENABLE_NETWORK |
|
802 |
|
803 /* Joining Client: |
|
804 * _local_company: COMPANY_SPECTATOR |
|
805 * _network_playas/cid = requested company/clientid |
|
806 * |
|
807 * Other client(s)/server: |
|
808 * _local_company/_network_playas: what they play as |
|
809 * cid = requested company/company of joining client */ |
|
810 uint16 cid = p2; // ClientID |
|
811 |
|
812 /* Has the network client a correct ClientID? */ |
|
813 if (!(flags & DC_EXEC)) return CommandCost(); |
|
814 if (cid >= MAX_CLIENT_INFO) return CommandCost(); |
|
815 |
|
816 /* Delete multiplayer progress bar */ |
|
817 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); |
|
818 |
|
819 Company *c = DoStartupNewCompany(false); |
|
820 |
|
821 /* A new company could not be created, revert to being a spectator */ |
|
822 if (c == NULL) { |
|
823 if (_network_server) { |
|
824 NetworkClientInfo *ci = &_network_client_info[cid]; |
|
825 ci->client_playas = COMPANY_SPECTATOR; |
|
826 NetworkUpdateClientInfo(ci->client_index); |
|
827 } else if (_local_company == COMPANY_SPECTATOR) { |
|
828 _network_playas = COMPANY_SPECTATOR; |
|
829 } |
|
830 break; |
|
831 } |
|
832 |
|
833 /* This is the joining client who wants a new company */ |
|
834 if (_local_company != _network_playas && _network_playas == c->index) { |
|
835 assert(_local_company == COMPANY_SPECTATOR); |
|
836 SetLocalCompany(c->index); |
|
837 if (!StrEmpty(_settings_client.network.default_company_pass)) { |
|
838 char *password = _settings_client.network.default_company_pass; |
|
839 NetworkChangeCompanyPassword(1, &password); |
|
840 } |
|
841 |
|
842 _current_company = _local_company; |
|
843 |
|
844 /* Now that we have a new company, broadcast our autorenew settings to |
|
845 * all clients so everything is in sync */ |
|
846 NetworkSend_Command(0, |
|
847 (_settings_client.gui.autorenew << 15 ) | (_settings_client.gui.autorenew_months << 16) | 4, |
|
848 _settings_client.gui.autorenew_money, |
|
849 CMD_SET_AUTOREPLACE, |
|
850 NULL |
|
851 ); |
|
852 |
|
853 MarkWholeScreenDirty(); |
|
854 } |
|
855 |
|
856 if (_network_server) { |
|
857 /* XXX - UGLY! p2 (pid) is mis-used to fetch the client-id, done at |
|
858 * server-side in network_server.c:838, function |
|
859 * DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) */ |
|
860 NetworkClientInfo *ci = &_network_client_info[cid]; |
|
861 ci->client_playas = c->index; |
|
862 NetworkUpdateClientInfo(ci->client_index); |
|
863 |
|
864 if (IsValidCompanyID(ci->client_playas)) { |
|
865 CompanyID company_backup = _local_company; |
|
866 _network_company_info[c->index].months_empty = 0; |
|
867 |
|
868 /* XXX - When a client joins, we automatically set its name to the |
|
869 * client's name (for some reason). As it stands now only the server |
|
870 * knows the client's name, so it needs to send out a "broadcast" to |
|
871 * do this. To achieve this we send a network command. However, it |
|
872 * uses _local_company to execute the command as. To prevent abuse |
|
873 * (eg. only yourself can change your name/company), we 'cheat' by |
|
874 * impersonation _local_company as the server. Not the best solution; |
|
875 * but it works. |
|
876 * TODO: Perhaps this could be improved by when the client is ready |
|
877 * with joining to let it send itself the command, and not the server? |
|
878 * For example in network_client.c:534? */ |
|
879 _cmd_text = ci->client_name; |
|
880 _local_company = ci->client_playas; |
|
881 NetworkSend_Command(0, 0, 0, CMD_RENAME_PRESIDENT, NULL); |
|
882 _local_company = company_backup; |
|
883 } |
|
884 } |
|
885 #endif /* ENABLE_NETWORK */ |
|
886 } break; |
|
887 |
|
888 case 1: /* Make a new AI company */ |
|
889 if (!(flags & DC_EXEC)) return CommandCost(); |
|
890 |
|
891 DoStartupNewCompany(true); |
|
892 break; |
|
893 |
|
894 case 2: { /* Delete a company */ |
|
895 Company *c; |
|
896 |
|
897 if (!IsValidCompanyID((CompanyID)p2)) return CMD_ERROR; |
|
898 |
|
899 if (!(flags & DC_EXEC)) return CommandCost(); |
|
900 |
|
901 c = GetCompany((CompanyID)p2); |
|
902 |
|
903 /* Only allow removal of HUMAN companies */ |
|
904 if (IsHumanCompany(c->index)) { |
|
905 /* Delete any open window of the company */ |
|
906 DeleteCompanyWindows(c->index); |
|
907 |
|
908 CompanyNewsInformation *cni = MallocT<CompanyNewsInformation>(1); |
|
909 cni->FillData(c); |
|
910 |
|
911 /* Show the bankrupt news */ |
|
912 SetDParam(0, STR_705C_BANKRUPT); |
|
913 SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY); |
|
914 SetDParamStr(2, cni->company_name); |
|
915 AddNewsItem(STR_02B6, NS_COMPANY_BANKRUPT, 0, 0, cni); |
|
916 |
|
917 /* Remove the company */ |
|
918 ChangeOwnershipOfCompanyItems(c->index, INVALID_OWNER); |
|
919 |
|
920 delete c; |
|
921 } |
|
922 } break; |
|
923 |
|
924 case 3: { /* Merge a company (#1) into another company (#2), elimination company #1 */ |
|
925 CompanyID cid_old = (CompanyID)GB(p2, 0, 16); |
|
926 CompanyID cid_new = (CompanyID)GB(p2, 16, 16); |
|
927 |
|
928 if (!IsValidCompanyID(cid_old) || !IsValidCompanyID(cid_new)) return CMD_ERROR; |
|
929 |
|
930 if (!(flags & DC_EXEC)) return CMD_ERROR; |
|
931 |
|
932 ChangeOwnershipOfCompanyItems(cid_old, cid_new); |
|
933 delete GetCompany(cid_old); |
|
934 } break; |
|
935 |
|
936 default: return CMD_ERROR; |
|
937 } |
|
938 |
|
939 return CommandCost(); |
|
940 } |
|
941 |
|
942 static const StringID _endgame_perf_titles[] = { |
|
943 STR_0213_BUSINESSMAN, |
|
944 STR_0213_BUSINESSMAN, |
|
945 STR_0213_BUSINESSMAN, |
|
946 STR_0213_BUSINESSMAN, |
|
947 STR_0213_BUSINESSMAN, |
|
948 STR_0214_ENTREPRENEUR, |
|
949 STR_0214_ENTREPRENEUR, |
|
950 STR_0215_INDUSTRIALIST, |
|
951 STR_0215_INDUSTRIALIST, |
|
952 STR_0216_CAPITALIST, |
|
953 STR_0216_CAPITALIST, |
|
954 STR_0217_MAGNATE, |
|
955 STR_0217_MAGNATE, |
|
956 STR_0218_MOGUL, |
|
957 STR_0218_MOGUL, |
|
958 STR_0219_TYCOON_OF_THE_CENTURY |
|
959 }; |
|
960 |
|
961 StringID EndGameGetPerformanceTitleFromValue(uint value) |
|
962 { |
|
963 value = minu(value / 64, lengthof(_endgame_perf_titles) - 1); |
|
964 |
|
965 return _endgame_perf_titles[value]; |
|
966 } |
|
967 |
|
968 /** Save the highscore for the company */ |
|
969 int8 SaveHighScoreValue(const Company *c) |
|
970 { |
|
971 HighScore *hs = _highscore_table[_settings_game.difficulty.diff_level]; |
|
972 uint i; |
|
973 uint16 score = c->old_economy[0].performance_history; |
|
974 |
|
975 /* Exclude cheaters from the honour of being in the highscore table */ |
|
976 if (CheatHasBeenUsed()) return -1; |
|
977 |
|
978 for (i = 0; i < lengthof(_highscore_table[0]); i++) { |
|
979 /* You are in the TOP5. Move all values one down and save us there */ |
|
980 if (hs[i].score <= score) { |
|
981 /* move all elements one down starting from the replaced one */ |
|
982 memmove(&hs[i + 1], &hs[i], sizeof(HighScore) * (lengthof(_highscore_table[0]) - i - 1)); |
|
983 SetDParam(0, c->index); |
|
984 SetDParam(1, c->index); |
|
985 GetString(hs[i].company, STR_HIGHSCORE_NAME, lastof(hs[i].company)); // get manager/company name string |
|
986 hs[i].score = score; |
|
987 hs[i].title = EndGameGetPerformanceTitleFromValue(score); |
|
988 return i; |
|
989 } |
|
990 } |
|
991 |
|
992 return -1; // too bad; we did not make it into the top5 |
|
993 } |
|
994 |
|
995 /** Sort all companies given their performance */ |
|
996 static int CDECL HighScoreSorter(const Company* const *a, const Company* const *b) |
|
997 { |
|
998 return (*b)->old_economy[0].performance_history - (*a)->old_economy[0].performance_history; |
|
999 } |
|
1000 |
|
1001 /* Save the highscores in a network game when it has ended */ |
|
1002 #define LAST_HS_ITEM lengthof(_highscore_table) - 1 |
|
1003 int8 SaveHighScoreValueNetwork() |
|
1004 { |
|
1005 const Company *c; |
|
1006 const Company *cl[MAX_COMPANIES]; |
|
1007 uint count = 0; |
|
1008 int8 company = -1; |
|
1009 |
|
1010 /* Sort all active companies with the highest score first */ |
|
1011 FOR_ALL_COMPANIES(c) cl[count++] = c; |
|
1012 |
|
1013 GSortT(cl, count, &HighScoreSorter); |
|
1014 |
|
1015 { |
|
1016 uint i; |
|
1017 |
|
1018 memset(_highscore_table[LAST_HS_ITEM], 0, sizeof(_highscore_table[0])); |
|
1019 |
|
1020 /* Copy over Top5 companies */ |
|
1021 for (i = 0; i < lengthof(_highscore_table[LAST_HS_ITEM]) && i < count; i++) { |
|
1022 HighScore* hs = &_highscore_table[LAST_HS_ITEM][i]; |
|
1023 |
|
1024 SetDParam(0, cl[i]->index); |
|
1025 SetDParam(1, cl[i]->index); |
|
1026 GetString(hs->company, STR_HIGHSCORE_NAME, lastof(hs->company)); // get manager/company name string |
|
1027 hs->score = cl[i]->old_economy[0].performance_history; |
|
1028 hs->title = EndGameGetPerformanceTitleFromValue(hs->score); |
|
1029 |
|
1030 /* get the ranking of the local company */ |
|
1031 if (cl[i]->index == _local_company) company = i; |
|
1032 } |
|
1033 } |
|
1034 |
|
1035 /* Add top5 companys to highscore table */ |
|
1036 return company; |
|
1037 } |
|
1038 |
|
1039 /** Save HighScore table to file */ |
|
1040 void SaveToHighScore() |
|
1041 { |
|
1042 FILE *fp = fopen(_highscore_file, "wb"); |
|
1043 |
|
1044 if (fp != NULL) { |
|
1045 uint i; |
|
1046 HighScore *hs; |
|
1047 |
|
1048 for (i = 0; i < LAST_HS_ITEM; i++) { // don't save network highscores |
|
1049 for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) { |
|
1050 /* First character is a command character, so strlen will fail on that */ |
|
1051 byte length = min(sizeof(hs->company), StrEmpty(hs->company) ? 0 : (int)strlen(&hs->company[1]) + 1); |
|
1052 |
|
1053 if (fwrite(&length, sizeof(length), 1, fp) != 1 || // write away string length |
|
1054 fwrite(hs->company, length, 1, fp) > 1 || // Yes... could be 0 bytes too |
|
1055 fwrite(&hs->score, sizeof(hs->score), 1, fp) != 1 || |
|
1056 fwrite(" ", 2, 1, fp) != 1) { // XXX - placeholder for hs->title, not saved anymore; compatibility |
|
1057 DEBUG(misc, 1, "Could not save highscore."); |
|
1058 i = LAST_HS_ITEM; |
|
1059 break; |
|
1060 } |
|
1061 } |
|
1062 } |
|
1063 fclose(fp); |
|
1064 } |
|
1065 } |
|
1066 |
|
1067 /** Initialize the highscore table to 0 and if any file exists, load in values */ |
|
1068 void LoadFromHighScore() |
|
1069 { |
|
1070 FILE *fp = fopen(_highscore_file, "rb"); |
|
1071 |
|
1072 memset(_highscore_table, 0, sizeof(_highscore_table)); |
|
1073 |
|
1074 if (fp != NULL) { |
|
1075 uint i; |
|
1076 HighScore *hs; |
|
1077 |
|
1078 for (i = 0; i < LAST_HS_ITEM; i++) { // don't load network highscores |
|
1079 for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) { |
|
1080 byte length; |
|
1081 if (fread(&length, sizeof(length), 1, fp) != 1 || |
|
1082 fread(hs->company, length, 1, fp) > 1 || // Yes... could be 0 bytes too |
|
1083 fread(&hs->score, sizeof(hs->score), 1, fp) != 1 || |
|
1084 fseek(fp, 2, SEEK_CUR) == -1) { // XXX - placeholder for hs->title, not saved anymore; compatibility |
|
1085 DEBUG(misc, 1, "Highscore corrupted"); |
|
1086 i = LAST_HS_ITEM; |
|
1087 break; |
|
1088 } |
|
1089 *lastof(hs->company) = '\0'; |
|
1090 hs->title = EndGameGetPerformanceTitleFromValue(hs->score); |
|
1091 } |
|
1092 } |
|
1093 fclose(fp); |
|
1094 } |
|
1095 |
|
1096 /* Initialize end of game variable (when to show highscore chart) */ |
|
1097 _settings_client.gui.ending_year = 2051; |
|
1098 } |
|
1099 |
|
1100 /* Save/load of companies */ |
|
1101 static const SaveLoad _company_desc[] = { |
|
1102 SLE_VAR(Company, name_2, SLE_UINT32), |
|
1103 SLE_VAR(Company, name_1, SLE_STRINGID), |
|
1104 SLE_CONDSTR(Company, name, SLE_STR, 0, 84, SL_MAX_VERSION), |
|
1105 |
|
1106 SLE_VAR(Company, president_name_1, SLE_UINT16), |
|
1107 SLE_VAR(Company, president_name_2, SLE_UINT32), |
|
1108 SLE_CONDSTR(Company, president_name, SLE_STR, 0, 84, SL_MAX_VERSION), |
|
1109 |
|
1110 SLE_VAR(Company, face, SLE_UINT32), |
|
1111 |
|
1112 /* money was changed to a 64 bit field in savegame version 1. */ |
|
1113 SLE_CONDVAR(Company, money, SLE_VAR_I64 | SLE_FILE_I32, 0, 0), |
|
1114 SLE_CONDVAR(Company, money, SLE_INT64, 1, SL_MAX_VERSION), |
|
1115 |
|
1116 SLE_CONDVAR(Company, current_loan, SLE_VAR_I64 | SLE_FILE_I32, 0, 64), |
|
1117 SLE_CONDVAR(Company, current_loan, SLE_INT64, 65, SL_MAX_VERSION), |
|
1118 |
|
1119 SLE_VAR(Company, colour, SLE_UINT8), |
|
1120 SLE_VAR(Company, money_fraction, SLE_UINT8), |
|
1121 SLE_CONDVAR(Company, avail_railtypes, SLE_UINT8, 0, 57), |
|
1122 SLE_VAR(Company, block_preview, SLE_UINT8), |
|
1123 |
|
1124 SLE_CONDVAR(Company, cargo_types, SLE_FILE_U16 | SLE_VAR_U32, 0, 93), |
|
1125 SLE_CONDVAR(Company, cargo_types, SLE_UINT32, 94, SL_MAX_VERSION), |
|
1126 SLE_CONDVAR(Company, location_of_HQ, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), |
|
1127 SLE_CONDVAR(Company, location_of_HQ, SLE_UINT32, 6, SL_MAX_VERSION), |
|
1128 SLE_CONDVAR(Company, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), |
|
1129 SLE_CONDVAR(Company, last_build_coordinate, SLE_UINT32, 6, SL_MAX_VERSION), |
|
1130 SLE_CONDVAR(Company, inaugurated_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), |
|
1131 SLE_CONDVAR(Company, inaugurated_year, SLE_INT32, 31, SL_MAX_VERSION), |
|
1132 |
|
1133 SLE_ARR(Company, share_owners, SLE_UINT8, 4), |
|
1134 |
|
1135 SLE_VAR(Company, num_valid_stat_ent, SLE_UINT8), |
|
1136 |
|
1137 SLE_VAR(Company, quarters_of_bankrupcy, SLE_UINT8), |
|
1138 SLE_VAR(Company, bankrupt_asked, SLE_UINT8), |
|
1139 SLE_VAR(Company, bankrupt_timeout, SLE_INT16), |
|
1140 SLE_CONDVAR(Company, bankrupt_value, SLE_VAR_I64 | SLE_FILE_I32, 0, 64), |
|
1141 SLE_CONDVAR(Company, bankrupt_value, SLE_INT64, 65, SL_MAX_VERSION), |
|
1142 |
|
1143 /* yearly expenses was changed to 64-bit in savegame version 2. */ |
|
1144 SLE_CONDARR(Company, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, 0, 1), |
|
1145 SLE_CONDARR(Company, yearly_expenses, SLE_INT64, 3 * 13, 2, SL_MAX_VERSION), |
|
1146 |
|
1147 SLE_CONDVAR(Company, is_ai, SLE_BOOL, 2, SL_MAX_VERSION), |
|
1148 SLE_CONDNULL(1, 4, 99), |
|
1149 |
|
1150 /* Engine renewal settings */ |
|
1151 SLE_CONDNULL(512, 16, 18), |
|
1152 SLE_CONDREF(Company, engine_renew_list, REF_ENGINE_RENEWS, 19, SL_MAX_VERSION), |
|
1153 SLE_CONDVAR(Company, engine_renew, SLE_BOOL, 16, SL_MAX_VERSION), |
|
1154 SLE_CONDVAR(Company, engine_renew_months, SLE_INT16, 16, SL_MAX_VERSION), |
|
1155 SLE_CONDVAR(Company, engine_renew_money, SLE_UINT32, 16, SL_MAX_VERSION), |
|
1156 SLE_CONDVAR(Company, renew_keep_length, SLE_BOOL, 2, SL_MAX_VERSION), // added with 16.1, but was blank since 2 |
|
1157 |
|
1158 /* reserve extra space in savegame here. (currently 63 bytes) */ |
|
1159 SLE_CONDNULL(63, 2, SL_MAX_VERSION), |
|
1160 |
|
1161 SLE_END() |
|
1162 }; |
|
1163 |
|
1164 static const SaveLoad _company_economy_desc[] = { |
|
1165 /* these were changed to 64-bit in savegame format 2 */ |
|
1166 SLE_CONDVAR(CompanyEconomyEntry, income, SLE_FILE_I32 | SLE_VAR_I64, 0, 1), |
|
1167 SLE_CONDVAR(CompanyEconomyEntry, income, SLE_INT64, 2, SL_MAX_VERSION), |
|
1168 SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_FILE_I32 | SLE_VAR_I64, 0, 1), |
|
1169 SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_INT64, 2, SL_MAX_VERSION), |
|
1170 SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_FILE_I32 | SLE_VAR_I64, 0, 1), |
|
1171 SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, 2, SL_MAX_VERSION), |
|
1172 |
|
1173 SLE_VAR(CompanyEconomyEntry, delivered_cargo, SLE_INT32), |
|
1174 SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32), |
|
1175 |
|
1176 SLE_END() |
|
1177 }; |
|
1178 |
|
1179 static const SaveLoad _company_livery_desc[] = { |
|
1180 SLE_CONDVAR(Livery, in_use, SLE_BOOL, 34, SL_MAX_VERSION), |
|
1181 SLE_CONDVAR(Livery, colour1, SLE_UINT8, 34, SL_MAX_VERSION), |
|
1182 SLE_CONDVAR(Livery, colour2, SLE_UINT8, 34, SL_MAX_VERSION), |
|
1183 SLE_END() |
|
1184 }; |
|
1185 |
|
1186 static void SaveLoad_PLYR(Company *c) |
|
1187 { |
|
1188 int i; |
|
1189 |
|
1190 SlObject(c, _company_desc); |
|
1191 |
|
1192 /* Write AI? */ |
|
1193 if (!IsHumanCompany(c->index)) { |
|
1194 SaveLoad_AI(c->index); |
|
1195 } |
|
1196 |
|
1197 /* Write economy */ |
|
1198 SlObject(&c->cur_economy, _company_economy_desc); |
|
1199 |
|
1200 /* Write old economy entries. */ |
|
1201 for (i = 0; i < c->num_valid_stat_ent; i++) { |
|
1202 SlObject(&c->old_economy[i], _company_economy_desc); |
|
1203 } |
|
1204 |
|
1205 /* Write each livery entry. */ |
|
1206 int num_liveries = CheckSavegameVersion(63) ? LS_END - 4 : (CheckSavegameVersion(85) ? LS_END - 2: LS_END); |
|
1207 for (i = 0; i < num_liveries; i++) { |
|
1208 SlObject(&c->livery[i], _company_livery_desc); |
|
1209 } |
|
1210 |
|
1211 if (num_liveries < LS_END) { |
|
1212 /* We want to insert some liveries somewhere in between. This means some have to be moved. */ |
|
1213 memmove(&c->livery[LS_FREIGHT_WAGON], &c->livery[LS_PASSENGER_WAGON_MONORAIL], (LS_END - LS_FREIGHT_WAGON) * sizeof(c->livery[0])); |
|
1214 c->livery[LS_PASSENGER_WAGON_MONORAIL] = c->livery[LS_MONORAIL]; |
|
1215 c->livery[LS_PASSENGER_WAGON_MAGLEV] = c->livery[LS_MAGLEV]; |
|
1216 } |
|
1217 |
|
1218 if (num_liveries == LS_END - 4) { |
|
1219 /* Copy bus/truck liveries over to trams */ |
|
1220 c->livery[LS_PASSENGER_TRAM] = c->livery[LS_BUS]; |
|
1221 c->livery[LS_FREIGHT_TRAM] = c->livery[LS_TRUCK]; |
|
1222 } |
|
1223 } |
|
1224 |
|
1225 static void Save_PLYR() |
|
1226 { |
|
1227 Company *c; |
|
1228 FOR_ALL_COMPANIES(c) { |
|
1229 SlSetArrayIndex(c->index); |
|
1230 SlAutolength((AutolengthProc*)SaveLoad_PLYR, c); |
|
1231 } |
|
1232 } |
|
1233 |
|
1234 static void Load_PLYR() |
|
1235 { |
|
1236 int index; |
|
1237 while ((index = SlIterateArray()) != -1) { |
|
1238 Company *c = new (index) Company(); |
|
1239 SaveLoad_PLYR(c); |
|
1240 _company_colours[index] = c->colour; |
|
1241 |
|
1242 /* This is needed so an AI is attached to a loaded AI */ |
|
1243 if (c->is_ai && (!_networking || _network_server) && _ai.enabled) { |
|
1244 /* Clear the memory of the new AI, otherwise we might be doing wrong things. */ |
|
1245 memset(&_companies_ainew[index], 0, sizeof(CompanyAiNew)); |
|
1246 AI_StartNewAI(c->index); |
|
1247 } |
|
1248 } |
|
1249 } |
|
1250 |
|
1251 extern const ChunkHandler _company_chunk_handlers[] = { |
|
1252 { 'PLYR', Save_PLYR, Load_PLYR, CH_ARRAY | CH_LAST}, |
|
1253 }; |
|