diff options
-rw-r--r-- | docs/landscape.html | 666 | ||||
-rw-r--r-- | docs/landscape_externals.html | 616 | ||||
-rw-r--r-- | docs/landscape_grid.html | 41 | ||||
-rw-r--r-- | projects/openttd.vcproj | 12 | ||||
-rw-r--r-- | projects/openttd_vs80.vcproj | 20 | ||||
-rw-r--r-- | source.list | 4 | ||||
-rw-r--r-- | src/map.cpp | 13 | ||||
-rw-r--r-- | src/map.h | 7 | ||||
-rw-r--r-- | src/misc.cpp | 31 | ||||
-rw-r--r-- | src/misc_gui.cpp | 1 | ||||
-rw-r--r-- | src/newgrf.cpp | 426 | ||||
-rw-r--r-- | src/newgrf.h | 3 | ||||
-rw-r--r-- | src/newgrf_callbacks.h | 61 | ||||
-rw-r--r-- | src/newgrf_house.cpp | 609 | ||||
-rw-r--r-- | src/newgrf_house.h | 71 | ||||
-rw-r--r-- | src/newgrf_sound.cpp | 9 | ||||
-rw-r--r-- | src/newgrf_sound.h | 1 | ||||
-rw-r--r-- | src/newgrf_spritegroup.cpp | 6 | ||||
-rw-r--r-- | src/newgrf_spritegroup.h | 13 | ||||
-rw-r--r-- | src/newgrf_town.cpp | 91 | ||||
-rw-r--r-- | src/newgrf_town.h | 13 | ||||
-rw-r--r-- | src/openttd.cpp | 45 | ||||
-rw-r--r-- | src/table/sprites.h | 3 | ||||
-rw-r--r-- | src/table/town_land.h | 810 | ||||
-rw-r--r-- | src/town.h | 116 | ||||
-rw-r--r-- | src/town_cmd.cpp | 282 | ||||
-rw-r--r-- | src/town_map.h | 303 | ||||
-rw-r--r-- | src/void_map.h | 1 |
28 files changed, 3027 insertions, 1247 deletions
diff --git a/docs/landscape.html b/docs/landscape.html index 4ef9815d7..a89643654 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -568,621 +568,67 @@ <td valign=top nowrap> </td> <td> <ul> - <li>m2: Index into the array of towns</li> - <li>m3 bits 7..6: stage of construction (<tt>3</tt> = completed)</li> - <li>m4: <a name="HouseTypes">town building type</a>:<br> - <small>Note: In the climate list, 'sub-arctic' means below the snow line, and 'snow' means above the snow line in the sub-arctic climate.</small> - <table> - <tr> - <th align=left>Type </th> - <th align=left>Size </th> - <th align=left>Climates </th> - <th align=left>Description</th> - </tr> - - <tr> - <td nowrap valign=top><tt>00</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>01</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>02</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>small block of flats</td> - </tr> - - <tr> - <td nowrap valign=top><tt>03</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>church</td> - </tr> - - <tr> - <td nowrap valign=top><tt>04</tt> </td> - <td>1×1</td> - <td>temperate, sub-arctic, sub-tropical</td> - <td align=left>large office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>05</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>large office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>06</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>town houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>07</tt>..<tt>08</tt> </td> - <td>1×2</td> - <td>temperate</td> - <td align=left>hotel</td> - </tr> - - <tr> - <td nowrap valign=top><tt>09</tt> </td> - <td>1×1</td> - <td>temperate, sub-arctic, sub-tropical </td> - <td align=left>statue</td> - </tr> - - <tr> - <td nowrap valign=top><tt>0A</tt> </td> - <td>1×1</td> - <td>temperate, sub-arctic, sub-tropical</td> - <td align=left>fountain</td> - </tr> - - <tr> - <td nowrap valign=top><tt>0B</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>park (with a pond)</td> - </tr> - - <tr> - <td nowrap valign=top><tt>0C</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>park (with an alley)</td> - </tr> - - <tr> - <td nowrap valign=top><tt>0D</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>0E</tt>..<tt>10</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>various types of shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>11</tt> </td> - <td>1×1</td> - <td>temperate, sub-arctic, sub-tropical</td> - <td align=left>modern office building</td> - </tr> - - <tr> - <td nowrap valign=top><tt>12</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>warehouse</td> - </tr> - - <tr> - <td nowrap valign=top><tt>13</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>office block (with spiral stairway on the side)</td> - </tr> - - <tr> - <td nowrap valign=top><tt>14</tt>..<tt>17</tt> </td> - <td>2×2</td> - <td>temperate</td> - <td align=left>stadium</td> - </tr> - - <tr> - <td nowrap valign=top><tt>18</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>old houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>19</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>cottages</td> - </tr> - - <tr> - <td nowrap valign=top><tt>1A</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>1B</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>flats</td> - </tr> - - <tr> - <td nowrap valign=top><tt>1C</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>1D</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>1E</tt> </td> - <td>1×1</td> - <td>temperate, sub-tropical</td> - <td align=left>shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>1F</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>theatre</td> - </tr> - - <tr> - <td nowrap valign=top><tt>20</tt>..<tt>23</tt> </td> - <td>2×2</td> - <td>temperate, sub-arctic, sub-tropical</td> - <td align=left>stadium (modern style)</td> - </tr> - - <tr> - <td nowrap valign=top><tt>24</tt> </td> - <td>1×1</td> - <td>temperate, sub-arctic, sub-tropical</td> - <td align=left>offices (the modern 'vertical tube' style)</td> - </tr> - - <tr> - <td nowrap valign=top><tt>25</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>26</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>27</tt> </td> - <td>1×1</td> - <td>temperate</td> - <td align=left>cinema</td> - </tr> - - <tr> - <td nowrap valign=top><tt>28</tt>..<tt>2B</tt> </td> - <td>2×2</td> - <td>temperate</td> - <td align=left>shopping mall</td> - </tr> - - <tr> - <td nowrap valign=top><tt>2C</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>flats</td> - </tr> - - <tr> - <td nowrap valign=top><tt>2D</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>flats</td> - </tr> - - <tr> - <td nowrap valign=top><tt>2E</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>2F</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>30</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>31</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>32</tt> </td> - <td>1×1</td> - <td>sub-arctic, sub-tropical</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>33</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>34</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>35</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>36</tt> </td> - <td>1×1</td> - <td>sub-arctic, sub-tropical</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>37</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>38</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>39</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>3A</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>3B</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>3C</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>church</td> - </tr> - - <tr> - <td nowrap valign=top><tt>3D</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>church</td> - </tr> - - <tr> - <td nowrap valign=top><tt>3E</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>3F</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>40</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>41</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>42</tt>..<tt>43</tt> </td> - <td>1×2</td> - <td>sub-arctic</td> - <td align=left>hotel</td> - </tr> - - <tr> - <td nowrap valign=top><tt>44</tt>..<tt>45</tt> </td> - <td>1×2</td> - <td>snow</td> - <td align=left>hotel</td> - </tr> - - <tr> - <td nowrap valign=top><tt>46</tt> </td> - <td>1×1</td> - <td>sub-arctic, sub-tropical</td> - <td align=left>shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>47</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>48</tt> </td> - <td>1×1</td> - <td>sub-arctic</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>49</tt> </td> - <td>1×1</td> - <td>snow</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>4A</tt>..<tt>4B</tt> </td> - <td>2×1</td> - <td>sub-arctic</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>4C</tt>..<tt>4D</tt> </td> - <td>2×1</td> - <td>snow</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>4E</tt> </td> - <td>1×1</td> - <td>sub-tropical</td> - <td align=left>houses (with a tree in a corner)</td> - </tr> - - <tr> - <td nowrap valign=top><tt>4F</tt>, <tt>50</tt> </td> - <td>1×1</td> - <td>sub-tropical</td> - <td align=left>houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>51</tt> </td> - <td>1×1</td> - <td>sub-tropical</td> - <td align=left>houses (suburb-type)</td> - </tr> - - <tr> - <td nowrap valign=top><tt>52</tt> </td> - <td>1×1</td> - <td>sub-tropical</td> - <td align=left>flats</td> - </tr> - - <tr> - <td nowrap valign=top><tt>53</tt> </td> - <td>1×1</td> - <td>sub-tropical</td> - <td align=left>church</td> - </tr> - - <tr> - <td nowrap valign=top><tt>54</tt> </td> - <td>1×1</td> - <td>sub-tropical</td> - <td align=left>houses (with two trees in front)</td> - </tr> - - <tr> - <td nowrap valign=top><tt>55</tt>, <tt>56</tt> </td> - <td>1×1</td> - <td>sub-tropical</td> - <td align=left>flats</td> - </tr> - - <tr> - <td nowrap valign=top><tt>57</tt>..<tt>58</tt> </td> - <td>2×1</td> - <td>sub-tropical</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>59</tt> </td> - <td>1×1</td> - <td>sub-tropical</td> - <td align=left>flats</td> - </tr> - - <tr> - <td nowrap valign=top><tt>5A</tt> </td> - <td>1×1</td> - <td>sub-tropical</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>5B</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>church</td> - </tr> - - <tr> - <td nowrap valign=top><tt>5C</tt>..<tt>61</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>various types of toyland houses</td> - </tr> - - <tr> - <td nowrap valign=top><tt>62</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>63</tt>..<tt>64</tt> </td> - <td>1×2</td> - <td>toyland</td> - <td align=left>houses ('shoe' style)</td> - </tr> - - <tr> - <td nowrap valign=top><tt>65</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>66</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>igloo</td> - </tr> - - <tr> - <td nowrap valign=top><tt>67</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>tepees</td> - </tr> - - <tr> - <td nowrap valign=top><tt>68</tt>, <tt>69</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>shops and offices</td> - </tr> - - <tr> - <td nowrap valign=top><tt>6A</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>tall office block</td> - </tr> - - <tr> - <td nowrap valign=top><tt>6B</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>statue</td> - </tr> - - <tr> - <td nowrap valign=top><tt>6C</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>teapot-house</td> - </tr> - - <tr> - <td nowrap valign=top><tt>6D</tt> </td> - <td>1×1</td> - <td>toyland</td> - <td align=left>piggy-bank</td> - </tr> - </table> - </li> - <li>m5 bits 2..0: construction counter, for buildings under construction incremented on every periodic tile processing<br> - On wraparound the stage of construction in m3 is increased + <li>m1 : Random bits <a href="#newhouses">(newhouses)</a> </li> + <li>m2 : index into the array of industries</li> + <li>m3 bit 7 : + <ul> + <li> set : House is complete + <ul> + <li>m5 : age of House. Maximum been 255, which means 255 years and more</li> + </ul> + </li> + <li> clear : House is in construction + <ul> + <li>m5 bits 7..5 : free</li> + <li>m5 bits 4..3 : construction stage</li> + <li>m5 bits 2..0 : construction counter</li> + </ul> + </li> + </ul> + <li>m3 bit 6 : 9th bit of house type (m4), allowing 512 different types.</li> + <li>m3 bits 5..0 : triggers activated <a href="#newhouses">(newhouses)</a></li> + <li>m4 : <a href="landscape_externals.html">town building type</a> (with m3[6] bit)</li> + <li>m5 : see m3 bit 7</li> + <li>m6 : + <ul> + <li>If <a href="#newhouses">newhouses</a> is activated + <ul> + <li>bits 7..3 : Current animation frame</li> + <li>bit 2 : free</li> + </ul> + </li> + <li>Standard behaviour + <ul> + <li>bits 7..2 : lift position (for houses type 04 and 05)</li> + </ul> + </li> + <li>bits 1..0 : tropic zone specifier</li> + </ul> </li> - <li>for large office blocks (types <tt>04</tt> and <tt>05</tt>): + <li>m7 : <ul> - <li>m1 bits 6..0: position of the lift</li> - <li>m1 bit 7: if set the lift is moving</li> - <li>m5 bit 7: if set then m5 bits 5..0 hold the destination floor of the lift, which could be 0..6, except 1.<br> - So the building has 6 effective floors. This is due to the fact that the first floor is 2 'normal' floors high.<br> - One 'normal' floor has a height of 6 lift positions.</li> + <li>If <a href="#newhouses">newhouses</a> is activated + <ul> + <li>Periodic processing time remaining</li> + </ul> + </li> + <li>Standard behaviour (only for houses type 04 and 05) + <ul> + <li>bits 7..4 : free</li> + <li>bits 3..1 : lift destination. Values can be 0..6, except 1.<br> + So the building has 6 effective floors. This is due to the fact that the first floor is 2 'normal' floors high.<br> + One 'normal' floor has a height of 6 lift positions. + </li> + <li>bit 0 : Lift has destination when set</li> + </ul> + </li> </ul> </li> - <li>m6 bits 1..0 : <a href="#tropic_zone">Tropic zone definition</a></li> </ul> + <small><a name="newhouses">Newhouses is the name englobing a newGRF feature developped by TTDPatch devs (mainly Csaboka).<br> + It allows the replacement of the properties as well as the graphics of houses in the game.<br> + To distinguish between the standard behaviour and the newGRF one, HouseID (m4 + m3[6]) is tested for anything above 110.<br> + 110 is the count of standard houses. So above 110 means there is a new definition of at least one house</small> </td> </tr> diff --git a/docs/landscape_externals.html b/docs/landscape_externals.html new file mode 100644 index 000000000..16f130532 --- /dev/null +++ b/docs/landscape_externals.html @@ -0,0 +1,616 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> + +<head> + <meta name="Author" content="Marcin Grzegorczyk"> + <meta name="Description" content="Structure of OpenTTD (OTTD) landscape arrays"> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>OpenTTD Landscape externals</title> +</head> + +<body> + <h3><a name="Landscape">Landscape</a></h3> + <p> + These are the different house types available on standard game.<br> + <small>Note: In the climate list, 'sub-arctic' means below the snow line, and 'snow' means above the snow line in the sub-arctic climate.</small> + </p> + + <table> + <tr> + <th align=left>Type </th> + <th align=left>Size </th> + <th align=left>Climates </th> + <th align=left>Description</th> + </tr> + + <tr> + <td nowrap valign=top><tt>00</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>01</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>02</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>small block of flats</td> + </tr> + + <tr> + <td nowrap valign=top><tt>03</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>church</td> + </tr> + + <tr> + <td nowrap valign=top><tt>04</tt> </td> + <td>1×1</td> + <td>temperate, sub-arctic, sub-tropical</td> + <td align=left>large office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>05</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>large office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>06</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>town houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>07</tt>..<tt>08</tt> </td> + <td>1×2</td> + <td>temperate</td> + <td align=left>hotel</td> + </tr> + + <tr> + <td nowrap valign=top><tt>09</tt> </td> + <td>1×1</td> + <td>temperate, sub-arctic, sub-tropical </td> + <td align=left>statue</td> + </tr> + + <tr> + <td nowrap valign=top><tt>0A</tt> </td> + <td>1×1</td> + <td>temperate, sub-arctic, sub-tropical</td> + <td align=left>fountain</td> + </tr> + + <tr> + <td nowrap valign=top><tt>0B</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>park (with a pond)</td> + </tr> + + <tr> + <td nowrap valign=top><tt>0C</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>park (with an alley)</td> + </tr> + + <tr> + <td nowrap valign=top><tt>0D</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>0E</tt>..<tt>10</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>various types of shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>11</tt> </td> + <td>1×1</td> + <td>temperate, sub-arctic, sub-tropical</td> + <td align=left>modern office building</td> + </tr> + + <tr> + <td nowrap valign=top><tt>12</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>warehouse</td> + </tr> + + <tr> + <td nowrap valign=top><tt>13</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>office block (with spiral stairway on the side)</td> + </tr> + + <tr> + <td nowrap valign=top><tt>14</tt>..<tt>17</tt> </td> + <td>2×2</td> + <td>temperate</td> + <td align=left>stadium</td> + </tr> + + <tr> + <td nowrap valign=top><tt>18</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>old houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>19</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>cottages</td> + </tr> + + <tr> + <td nowrap valign=top><tt>1A</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>1B</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>flats</td> + </tr> + + <tr> + <td nowrap valign=top><tt>1C</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>1D</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>1E</tt> </td> + <td>1×1</td> + <td>temperate, sub-tropical</td> + <td align=left>shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>1F</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>theatre</td> + </tr> + + <tr> + <td nowrap valign=top><tt>20</tt>..<tt>23</tt> </td> + <td>2×2</td> + <td>temperate, sub-arctic, sub-tropical</td> + <td align=left>stadium (modern style)</td> + </tr> + + <tr> + <td nowrap valign=top><tt>24</tt> </td> + <td>1×1</td> + <td>temperate, sub-arctic, sub-tropical</td> + <td align=left>offices (the modern 'vertical tube' style)</td> + </tr> + + <tr> + <td nowrap valign=top><tt>25</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>26</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>27</tt> </td> + <td>1×1</td> + <td>temperate</td> + <td align=left>cinema</td> + </tr> + + <tr> + <td nowrap valign=top><tt>28</tt>..<tt>2B</tt> </td> + <td>2×2</td> + <td>temperate</td> + <td align=left>shopping mall</td> + </tr> + + <tr> + <td nowrap valign=top><tt>2C</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>flats</td> + </tr> + + <tr> + <td nowrap valign=top><tt>2D</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>flats</td> + </tr> + + <tr> + <td nowrap valign=top><tt>2E</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>2F</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>30</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>31</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>32</tt> </td> + <td>1×1</td> + <td>sub-arctic, sub-tropical</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>33</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>34</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>35</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>36</tt> </td> + <td>1×1</td> + <td>sub-arctic, sub-tropical</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>37</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>38</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>39</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>3A</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>3B</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>3C</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>church</td> + </tr> + + <tr> + <td nowrap valign=top><tt>3D</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>church</td> + </tr> + + <tr> + <td nowrap valign=top><tt>3E</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>3F</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>40</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>41</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>42</tt>..<tt>43</tt> </td> + <td>1×2</td> + <td>sub-arctic</td> + <td align=left>hotel</td> + </tr> + + <tr> + <td nowrap valign=top><tt>44</tt>..<tt>45</tt> </td> + <td>1×2</td> + <td>snow</td> + <td align=left>hotel</td> + </tr> + + <tr> + <td nowrap valign=top><tt>46</tt> </td> + <td>1×1</td> + <td>sub-arctic, sub-tropical</td> + <td align=left>shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>47</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>48</tt> </td> + <td>1×1</td> + <td>sub-arctic</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>49</tt> </td> + <td>1×1</td> + <td>snow</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>4A</tt>..<tt>4B</tt> </td> + <td>2×1</td> + <td>sub-arctic</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>4C</tt>..<tt>4D</tt> </td> + <td>2×1</td> + <td>snow</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>4E</tt> </td> + <td>1×1</td> + <td>sub-tropical</td> + <td align=left>houses (with a tree in a corner)</td> + </tr> + + <tr> + <td nowrap valign=top><tt>4F</tt>, <tt>50</tt> </td> + <td>1×1</td> + <td>sub-tropical</td> + <td align=left>houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>51</tt> </td> + <td>1×1</td> + <td>sub-tropical</td> + <td align=left>houses (suburb-type)</td> + </tr> + + <tr> + <td nowrap valign=top><tt>52</tt> </td> + <td>1×1</td> + <td>sub-tropical</td> + <td align=left>flats</td> + </tr> + + <tr> + <td nowrap valign=top><tt>53</tt> </td> + <td>1×1</td> + <td>sub-tropical</td> + <td align=left>church</td> + </tr> + + <tr> + <td nowrap valign=top><tt>54</tt> </td> + <td>1×1</td> + <td>sub-tropical</td> + <td align=left>houses (with two trees in front)</td> + </tr> + + <tr> + <td nowrap valign=top><tt>55</tt>, <tt>56</tt> </td> + <td>1×1</td> + <td>sub-tropical</td> + <td align=left>flats</td> + </tr> + + <tr> + <td nowrap valign=top><tt>57</tt>..<tt>58</tt> </td> + <td>2×1</td> + <td>sub-tropical</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>59</tt> </td> + <td>1×1</td> + <td>sub-tropical</td> + <td align=left>flats</td> + </tr> + + <tr> + <td nowrap valign=top><tt>5A</tt> </td> + <td>1×1</td> + <td>sub-tropical</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>5B</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>church</td> + </tr> + + <tr> + <td nowrap valign=top><tt>5C</tt>..<tt>61</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>various types of toyland houses</td> + </tr> + + <tr> + <td nowrap valign=top><tt>62</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>63</tt>..<tt>64</tt> </td> + <td>1×2</td> + <td>toyland</td> + <td align=left>houses ('shoe' style)</td> + </tr> + + <tr> + <td nowrap valign=top><tt>65</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>66</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>igloo</td> + </tr> + + <tr> + <td nowrap valign=top><tt>67</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>tepees</td> + </tr> + + <tr> + <td nowrap valign=top><tt>68</tt>, <tt>69</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>shops and offices</td> + </tr> + + <tr> + <td nowrap valign=top><tt>6A</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>tall office block</td> + </tr> + + <tr> + <td nowrap valign=top><tt>6B</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>statue</td> + </tr> + + <tr> + <td nowrap valign=top><tt>6C</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>teapot-house</td> + </tr> + + <tr> + <td nowrap valign=top><tt>6D</tt> </td> + <td>1×1</td> + <td>toyland</td> + <td align=left>piggy-bank</td> + </tr> + </table> +</body> + +</html>
\ No newline at end of file diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index 2d9196f3b..6539dfc57 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -33,6 +33,7 @@ the array so you can quickly see what is used and what is not. <li><span style="font-weight: bold;">m4</span> - 8 bits in size, is used for general storage</li> <li><span style="font-weight: bold;">m5</span> - 8 bits in size, is used for general storage</li> <li><span style="font-weight: bold;">m6</span> - 8 bits in size, special meaning : lower 2 bits only valid in tropic climate, upper 2 bits for bridges</li> + <li><span style="font-weight: bold;">m7</span> - 8 bits in size, is used for general storage</li> </ul> <table align=center border="1" cellpadding="2" cellspacing="2"> @@ -46,6 +47,7 @@ the array so you can quickly see what is used and what is not. <th>m4 (8)</th> <th>m5 (8)</th> <th>m6 (8)</th> + <th>m7 (8)</th> </tr> <tr> <td colspan=2 class="caption">bits</td> @@ -56,6 +58,7 @@ the array so you can quickly see what is used and what is not. <td class="bits">7654 3210</td> <td class="bits">7654 3210</td> <td class="bits">7654 3210</td> + <td class="bits">7654 3210</td> </tr> <tr> <td rowspan="2">0</td> @@ -67,6 +70,7 @@ the array so you can quickly see what is used and what is not. <td class="bits">XXXX XX<span class="free">OO</span></td> <td class="bits">XXXX XXXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td class="caption">farmland</td> @@ -77,6 +81,7 @@ the array so you can quickly see what is used and what is not. <td class="bits">-inherit-</td> <td class="bits"><span class="free">OOO</span>X XXXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td rowspan=3>1</td> @@ -88,6 +93,7 @@ the array so you can quickly see what is used and what is not. <td class="bits"><span class="free">OOOO O</span>XXX</td> <td class="bits">XXXX XXXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td class="caption">depot</td> @@ -98,6 +104,7 @@ the array so you can quickly see what is used and what is not. <td class="bits"><span class="free">OOOO</span> XXXX</td> <td class="bits">XX<span class="free">OO O</span>XXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td class="caption">waypoint</td> @@ -108,6 +115,7 @@ the array so you can quickly see what is used and what is not. <td class="bits"><span class="free">OOOO</span> XXXX</td> <td class="bits">XX<span class="free">OO O</span>XXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td rowspan=3>2</td> @@ -119,6 +127,7 @@ the array so you can quickly see what is used and what is not. <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XXXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td class="caption">level crossing</td> @@ -129,6 +138,7 @@ the array so you can quickly see what is used and what is not. <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XX<span class="free">OO</span></td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td class="caption">road depot</td> @@ -139,17 +149,19 @@ the array so you can quickly see what is used and what is not. <td class="bits"><span class="free">OOOO OOOO</span></td> <td class="bits">XXXX XXXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td>3</td> - <td class="caption">town</td> + <td class="caption">house</td> + <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XXXX</td> - <td class="bits"><span class="abuse">XXXX XXXX</span></td> <td class="bits">XXXX XXXX XXXX XXXX</td> - <td class="bits">XX<span class="free">OO OOOO</span></td> + <td class="bits">XX<span class="free">O</span>X XXXX</td> <td class="bits">XXXX XXXX</td> - <td class="bits">X<span class="free">O</span>XX XXXX</td> - <td class="bits"><span class="free">OOOO OO</span>XX</td> + <td class="bits">XX<span class="abuse">XX XXXX</span></td> + <td class="bits"><span class="abuse">XXXX XX</span>XX</td> + <td class="bits"><span class="abuse">X</span>XX<span class="abuse">X XXX</span>X</td> </tr> <tr> <td>4</td> @@ -161,6 +173,7 @@ the array so you can quickly see what is used and what is not. <td class="bits">XXXX XX<span class="free">OO</span></td> <td class="bits">XX<span class="free">OO O</span>XXX</td> <td class="bits"><span class="free">OOOO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td>5</td> @@ -172,6 +185,7 @@ the array so you can quickly see what is used and what is not. <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XXXX</td> <td class="bits"><span class="free">OOOO O</span>XXX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td>6</td> @@ -183,20 +197,25 @@ the array so you can quickly see what is used and what is not. <td class="bits"><span class="free">OOOO OOOO</span></td> <td class="bits"><span class="free">OOOO OO</span>XX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td rowspan=3>8</td> <td class="caption">industry</td> <td class="bits">XXXX XXXX</td> - <td class="bits"><span class="abuse">X</span><span class="free">OO</span><span class="abuse">X XXXX</span></td> + <td class="bits"><span class="abuse">X</span><span class="free">OO</span><span class="abuse">X + XXXX</span></td> <td class="bits">XXXX XXXX XXXX XXXX</td> <td class="bits"><span class="free">OOOO OOOO</span></td> <td class="bits"><span class="free">OOOO OOOO</span></td> <td class="bits">XXXX XXXX</td> <td class="bits"><span class="free">OOOO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> - <td>bubble/sugar/toffee,<BR>gold/copper/coal,<BR>oil wells, power station</td> + <td>bubble/sugar/toffee,<BR> + gold/copper/coal,<BR> + oil wells, power station</td> <td class="bits">-inherit-</td> <td class="bits"><span class="abuse">X</span><span class="free">OOO OOOO</span></td> <td class="bits">-inherit-</td> @@ -204,6 +223,7 @@ the array so you can quickly see what is used and what is not. <td class="bits"><span class="free">OOOO OOOO</span></td> <td class="bits">-inherit-</td> <td class="bits">-inherit-</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td>toy factory</td> @@ -214,6 +234,7 @@ the array so you can quickly see what is used and what is not. <td class="bits">XXXX XXXX</td> <td class="bits">-inherit-</td> <td class="bits">-inherit-</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td rowspan=2>9</td> @@ -225,16 +246,19 @@ the array so you can quickly see what is used and what is not. <td class="bits">X<span class="free">OOO OOOO</span></td> <td class="bits">X<span class="free">OOO</span> XXXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td>bridge ramp</td> <td class="bits">XXXX XXXX</td> <td class="bits">XXXX XXXX</td> - <td class="bits"><span class="free">OOOO OOOO</span> <span class="abuse">XXXX</span> <span class="free">OOOO</span></td> + <td class="bits"><span class="free">OOOO OOOO</span> <span class="abuse">XXXX</span> + <span class="free">OOOO</span></td> <td class="bits"><span class="free">OOOO</span> XXXX</td> <td class="bits">X<span class="free">OOO OOOO</span></td> <td class="bits">X<span class="free">OOO</span> XXXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> <tr> <td>A</td> @@ -246,6 +270,7 @@ the array so you can quickly see what is used and what is not. <td class="bits"><span class="free">OOOO OOOO</span></td> <td class="bits">XXXX XXXX</td> <td class="bits">XX<span class="free">OO OO</span>XX</td> + <td class="bits"><span class="free">OOOO OOOO</span></td> </tr> </tbody> </table> diff --git a/projects/openttd.vcproj b/projects/openttd.vcproj index db9fa427c..2e01dfaca 100644 --- a/projects/openttd.vcproj +++ b/projects/openttd.vcproj @@ -519,6 +519,9 @@ RelativePath=".\..\src\newgrf_engine.h"> </File> <File + RelativePath=".\..\src\newgrf_house.h"> + </File> + <File RelativePath=".\..\src\newgrf_sound.h"> </File> <File @@ -531,6 +534,9 @@ RelativePath=".\..\src\newgrf_text.h"> </File> <File + RelativePath=".\..\src\newgrf_town.h"> + </File> + <File RelativePath=".\..\src\news.h"> </File> <File @@ -926,6 +932,9 @@ RelativePath=".\..\src\newgrf_engine.cpp"> </File> <File + RelativePath=".\..\src\newgrf_house.cpp"> + </File> + <File RelativePath=".\..\src\newgrf_sound.cpp"> </File> <File @@ -937,6 +946,9 @@ <File RelativePath=".\..\src\newgrf_text.cpp"> </File> + <File + RelativePath=".\..\src\newgrf_town.cpp"> + </File> </Filter> <Filter Name="Map Accessors" diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 86c6b1f35..668094f1a 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -448,11 +448,11 @@ > </File> <File - RelativePath=".\..\src\aystar.cpp" + RelativePath=".\..\src\autoreplace_cmd.cpp" > </File> <File - RelativePath=".\..\src\autoreplace_cmd.cpp" + RelativePath=".\..\src\aystar.cpp" > </File> <File @@ -920,6 +920,10 @@ > </File> <File + RelativePath=".\..\src\newgrf_house.h" + > + </File> + <File RelativePath=".\..\src\newgrf_sound.h" > </File> @@ -936,6 +940,10 @@ > </File> <File + RelativePath=".\..\src\newgrf_town.h" + > + </File> + <File RelativePath=".\..\src\news.h" > </File> @@ -1456,6 +1464,10 @@ > </File> <File + RelativePath=".\..\src\newgrf_house.cpp" + > + </File> + <File RelativePath=".\..\src\newgrf_sound.cpp" > </File> @@ -1471,6 +1483,10 @@ RelativePath=".\..\src\newgrf_text.cpp" > </File> + <File + RelativePath=".\..\src\newgrf_town.cpp" + > + </File> </Filter> <Filter Name="Map Accessors" diff --git a/source.list b/source.list index 7c0c1da74..36347e9d3 100644 --- a/source.list +++ b/source.list @@ -140,10 +140,12 @@ newgrf_callbacks.h newgrf_cargo.h newgrf_config.h newgrf_engine.h +newgrf_house.h newgrf_sound.h newgrf_spritegroup.h newgrf_station.h newgrf_text.h +newgrf_town.h news.h npf.h music/null_m.h @@ -279,10 +281,12 @@ ai/trolly/trolly.cpp newgrf.cpp newgrf_config.cpp newgrf_engine.cpp +newgrf_house.cpp newgrf_sound.cpp newgrf_spritegroup.cpp newgrf_station.cpp newgrf_text.cpp +newgrf_town.cpp # Map Accessors bridge_map.cpp diff --git a/src/map.cpp b/src/map.cpp index 4ae44930a..00655a42e 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -22,7 +22,8 @@ uint _map_size_y; uint _map_tile_mask; uint _map_size; -Tile* _m = NULL; +Tile *_m = NULL; +TileExtended *_me = NULL; void AllocateMap(uint size_x, uint size_y) @@ -44,10 +45,16 @@ void AllocateMap(uint size_x, uint size_y) _map_tile_mask = _map_size - 1; free(_m); + free(_me); + _m = CallocT<Tile>(_map_size); + _me = CallocT<TileExtended>(_map_size); - /* XXX TODO handle memory shortage more gracefully */ - if (_m == NULL) error("Failed to allocate memory for the map"); + /* XXX @todo handle memory shortage more gracefully + * Maybe some attemps could be made to try with smaller maps down to 64x64 + * Maybe check for available memory before doing the calls, after all, we know how big + * the map is */ + if ((_m == NULL) || (_me == NULL)) error("Failed to allocate memory for the map"); } @@ -28,7 +28,12 @@ struct Tile { byte m6; }; -extern Tile* _m; +struct TileExtended { + byte m7; +}; + +extern Tile *_m; +extern TileExtended *_me; void AllocateMap(uint size_x, uint size_y); diff --git a/src/misc.cpp b/src/misc.cpp index 01b73fee2..fdd35ad56 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -18,6 +18,7 @@ #include "vehicle_gui.h" #include "variables.h" #include "ai/ai.h" +#include "newgrf_house.h" #include "date.h" #include "cargotype.h" @@ -132,6 +133,7 @@ void InitializeGame(int mode, uint size_x, uint size_y) InitializeSigns(); InitializeStations(); InitializeIndustries(); + InitializeBuildingCounts(); InitializeMainGui(); InitializeNameMgr(); @@ -578,6 +580,34 @@ static void Save_MAP6() } } +static void Load_MAP7() +{ + uint size = MapSize(); + uint i; + + for (i = 0; i != size;) { + uint8 buf[4096]; + uint j; + + SlArray(buf, lengthof(buf), SLE_UINT8); + for (j = 0; j != lengthof(buf); j++) _me[i++].m7 = buf[j]; + } +} + +static void Save_MAP7() +{ + uint size = MapSize(); + uint i; + + SlSetLength(size); + for (i = 0; i != size;) { + uint8 buf[4096]; + uint j; + + for (j = 0; j != lengthof(buf); j++) buf[j] = _me[i++].m7; + SlArray(buf, lengthof(buf), SLE_UINT8); + } +} static void Save_CHTS() { @@ -614,6 +644,7 @@ extern const ChunkHandler _misc_chunk_handlers[] = { { 'M3HI', Save_MAP4, Load_MAP4, CH_RIFF }, { 'MAP5', Save_MAP5, Load_MAP5, CH_RIFF }, { 'MAPE', Save_MAP6, Load_MAP6, CH_RIFF }, + { 'MAP7', Save_MAP7, Load_MAP7, CH_RIFF }, { 'NAME', Save_NAME, Load_NAME, CH_ARRAY}, { 'DATE', SaveLoad_DATE, SaveLoad_DATE, CH_RIFF}, diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index c2c0ff120..792afe302 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -188,6 +188,7 @@ static void Place_LandInfo(TileIndex tile) DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4); DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5); DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _m[tile].m6); + DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7); #undef LANDINFOD_LEVEL } diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 09e63d6ee..7323a0107 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -18,6 +18,7 @@ #include "string.h" #include "table/strings.h" #include "bridge.h" +#include "town.h" #include "economy.h" #include "newgrf_engine.h" #include "vehicle.h" @@ -28,9 +29,11 @@ #include "currency.h" #include "sound.h" #include "newgrf_config.h" +#include "newgrf_house.h" #include "newgrf_sound.h" #include "newgrf_spritegroup.h" #include "helpers.hpp" +#include "table/town_land.h" #include "cargotype.h" /* TTDPatch extended GRF format codec @@ -68,6 +71,9 @@ static byte *_preload_sprite = NULL; /* Set if any vehicle is loaded which uses 2cc (two company colours) */ bool _have_2cc = false; +/* Set if there are any newhouses loaded. */ +bool _have_newhouses = false; + /* Default cargo translation table. By default there are 27 possible cargo types */ static const uint _default_cargo_max = 27; static CargoLabel _default_cargo_list[_default_cargo_max]; @@ -227,6 +233,32 @@ static GRFFile *GetFileByFilename(const char *filename) } +/** Used when setting an object's property to map to the GRF's strings + * while taking in consideration the "drift" between TTDPatch string system and OpenTTD's one + * @param str StringID that we want to have the equivalent in OoenTTD + * @return the properly adjusted StringID + */ +static StringID MapGRFStringID(StringID str) +{ + /* 0xD0 and 0xDC stand for all the TextIDs in the range + * of 0xD000 (misc graphics texts) and 0xDC00 (misc persistent texts). + * These strings are unique to each grf file, and thus require to be used with the + * grfid in which they are declared */ + if (GB(str, 8, 8) == 0xD0 || GB(str, 8, 8) == 0xDC) { + return GetGRFStringID(_cur_grffile->grfid, str); + } + + /* Map building names according to our lang file changes + * 0x200F = Tall Office Block, first house name in the original data, the one that TTDPatch stil uses + * 0x201F = Old houses is the last house name. + * OpenTTD does not have exactly the same order aymore, so, the code below allows + * to compensate for the difference */ + if (str >= 0x200F && str <= 0x201F) return str + (STR_200F_TALL_OFFICE_BLOCK - 0x200F); + + return str; +} + + typedef bool (*VCI_Handler)(uint engine, int numinfo, int prop, byte **buf, int len); #define FOR_EACH_OBJECT for (i = 0; i < numinfo; i++) @@ -1168,6 +1200,206 @@ static bool BridgeChangeInfo(uint brid, int numinfo, int prop, byte **bufp, int return ret; } +static bool TownHouseChangeInfo(uint hid, int numinfo, int prop, byte **bufp, int len) +{ + HouseSpec **housespec; + byte *buf = *bufp; + int i; + bool ret = false; + + if (hid + numinfo >= HOUSE_MAX) { + grfmsg(1, "TownHouseChangeInfo: Too many houses loaded (%u), max (%u). Ignoring.", hid + numinfo, HOUSE_MAX-1); + return false; + } + + /* Allocate house specs if they haven't been allocated already. */ + if (_cur_grffile->housespec == NULL) { + _cur_grffile->housespec = CallocT<HouseSpec*>(HOUSE_MAX); + + /* Reset any overrides that have been set. */ + ResetHouseOverrides(); + } + + housespec = &_cur_grffile->housespec[hid]; + + if (prop != 0x08) { + /* Check that all the houses being modified have been defined. */ + FOR_EACH_OBJECT { + if (housespec[i] == NULL) { + grfmsg(2, "TownHouseChangeInfo: Attempt to modify undefined house %u. Ignoring.", hid + i); + return false; + } + } + } + + switch (prop) { + case 0x08: // Substitute building type, and definition of a new house + FOR_EACH_OBJECT { + byte subs_id = grf_load_byte(&buf); + + if (subs_id == 0xFF) { + /* Instead of defining a new house, a substitute house id + * of 0xFF disables the old house with the current id. */ + _house_specs[hid + i].enabled = false; + continue; + } else if (subs_id >= NEW_HOUSE_OFFSET) { + /* The substitute id must be one of the original houses. */ + grfmsg(2, "TownHouseChangeInfo: Attempt to use new house %u as substitute house for %u. Ignoring.", subs_id, hid + i); + return false; + } + + /* Allocate space for this house. */ + if (housespec[i] == NULL) housespec[i] = CallocT<HouseSpec>(1); + + memcpy(housespec[i], &_house_specs[subs_id], sizeof(_house_specs[subs_id])); + + housespec[i]->enabled = true; + housespec[i]->local_id = hid + i; + housespec[i]->substitute_id = subs_id; + housespec[i]->random_colour[0] = 0x04; // those 4 random colours are the base colour + housespec[i]->random_colour[1] = 0x08; // for all new houses + housespec[i]->random_colour[2] = 0x0C; // they stand for red, blue, orange and green + housespec[i]->random_colour[3] = 0x06; + + /* New houses do not (currently) expect to have a default start + * date before 1930, as this breaks the build date stuff. See + * FinaliseHouseArray() for more details. */ + if (housespec[i]->min_date < 1930) housespec[i]->min_date = 1930; + } + _have_newhouses = true; + break; + + case 0x09: // Building flags + FOR_EACH_OBJECT { + byte state = grf_load_byte(&buf); + housespec[i]->building_flags = (BuildingFlags)state; + } + break; + + case 0x0A: // Availability years + FOR_EACH_OBJECT { + uint16 years = grf_load_word(&buf); + housespec[i]->min_date = GB(years, 0, 8) > 150 ? MAX_YEAR : ORIGINAL_BASE_YEAR + GB(years, 0, 8); + housespec[i]->max_date = GB(years, 8, 8) > 150 ? MAX_YEAR : ORIGINAL_BASE_YEAR + GB(years, 8, 8); + } + break; + + case 0x0B: // Population + FOR_EACH_OBJECT housespec[i]->population = grf_load_byte(&buf); + break; + + case 0x0C: // Mail generation multiplier + FOR_EACH_OBJECT housespec[i]->mail_generation = grf_load_byte(&buf); + break; + + case 0x0D: // Passenger acceptance + FOR_EACH_OBJECT housespec[i]->passenger_acceptance = grf_load_byte(&buf); + break; + + case 0x0E: // Mail acceptance + FOR_EACH_OBJECT housespec[i]->mail_acceptance = grf_load_byte(&buf); + break; + + case 0x0F: // Goods, food or fizzy drinks acceptance + FOR_EACH_OBJECT { + int8 goods = grf_load_byte(&buf); + if (goods > 0) { + housespec[i]->goods_acceptance = goods; + } else { + housespec[i]->food_acceptance = -goods; + } + } + break; + + case 0x10: // Local authority rating decrease on removal + FOR_EACH_OBJECT housespec[i]->remove_rating_decrease = grf_load_word(&buf); + break; + + case 0x11: // Removal cost multiplier + FOR_EACH_OBJECT housespec[i]->removal_cost = grf_load_byte(&buf); + break; + + case 0x12: // Building name ID + FOR_EACH_OBJECT housespec[i]->building_name = MapGRFStringID(grf_load_word(&buf)); + break; + + case 0x13: // Building availability mask + FOR_EACH_OBJECT { + uint16 avail = grf_load_word(&buf); + housespec[i]->building_availability = (HouseZones)avail; + } + break; + + case 0x14: // House callback flags + FOR_EACH_OBJECT housespec[i]->callback_mask = grf_load_byte(&buf); + break; + + case 0x15: // House override byte + FOR_EACH_OBJECT { + byte override = grf_load_byte(&buf); + + /* The house being overridden must be an original house. */ + if (override >= NEW_HOUSE_OFFSET) { + grfmsg(2, "TownHouseChangeInfo: Attempt to override new house %u with house id %u. Ignoring.", override, hid); + return false; + } + + AddHouseOverride(hid, override); + } + break; + + case 0x16: // Periodic refresh multiplier + FOR_EACH_OBJECT housespec[i]->processing_time = grf_load_byte(&buf); + break; + + case 0x17: // Four random colours to use + FOR_EACH_OBJECT { + uint j; + for (j = 0; j < 4; j++) housespec[i]->random_colour[j] = grf_load_byte(&buf); + } + break; + + case 0x18: // Relative probability of appearing + FOR_EACH_OBJECT housespec[i]->probability = grf_load_byte(&buf); + break; + + case 0x19: // Extra flags + FOR_EACH_OBJECT { + byte flags = grf_load_byte(&buf); + housespec[i]->extra_flags = (HouseExtraFlags)flags; + } + break; + + case 0x1A: // Animation frames + FOR_EACH_OBJECT housespec[i]->animation_frames = grf_load_byte(&buf); + break; + + case 0x1B: // Animation speed + FOR_EACH_OBJECT housespec[i]->animation_speed = clamp(grf_load_byte(&buf), 2, 16); + break; + + case 0x1C: // Class of the building type + FOR_EACH_OBJECT housespec[i]->class_id = AllocateHouseClassID(grf_load_byte(&buf), _cur_grffile->grfid); + break; + + case 0x1D: // Callback flags 2 + FOR_EACH_OBJECT housespec[i]->callback_mask |= (grf_load_byte(&buf) << 8); + break; + + case 0x1E: // Accepted cargo types + FOR_EACH_OBJECT grf_load_dword(&buf); + ret = true; + break; + + default: + ret = true; + break; + } + + *bufp = buf; + return ret; +} + static bool GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, int len) { byte *buf = *bufp; @@ -1375,7 +1607,7 @@ static void FeatureChangeInfo(byte *buf, int len) /* GSF_STATION */ StationChangeInfo, /* GSF_CANAL */ NULL, /* GSF_BRIDGE */ BridgeChangeInfo, - /* GSF_TOWNHOUSE */ NULL, + /* GSF_TOWNHOUSE */ TownHouseChangeInfo, /* GSF_GLOBALVAR */ GlobalVarChangeInfo, /* GSF_INDUSTRYTILES */NULL, /* GSF_INDUSTRIES */ NULL, @@ -1853,6 +2085,86 @@ static void NewSpriteGroup(byte *buf, int len) break; } + case GSF_TOWNHOUSE: { + byte sprites = _cur_grffile->spriteset_numents; + byte num_sprites = max((uint8)1, type); + uint i; + + group = AllocateSpriteGroup(); + group->type = SGT_TILELAYOUT; + group->g.layout.num_sprites = sprites; + group->g.layout.dts = CallocT<DrawTileSprites>(1); + + /* Groundsprite */ + group->g.layout.dts->ground_sprite = grf_load_word(&buf); + group->g.layout.dts->ground_pal = grf_load_word(&buf); + /* Remap transparent/colour modifier bits */ + if (HASBIT(group->g.layout.dts->ground_sprite, 14)) { + CLRBIT(group->g.layout.dts->ground_sprite, 14); + SETBIT(group->g.layout.dts->ground_sprite, PALETTE_MODIFIER_TRANSPARENT); + } + if (HASBIT(group->g.layout.dts->ground_sprite, 15)) { + CLRBIT(group->g.layout.dts->ground_sprite, 15); + SETBIT(group->g.layout.dts->ground_sprite, PALETTE_MODIFIER_COLOR); + } + if (HASBIT(group->g.layout.dts->ground_pal, 14)) { + CLRBIT(group->g.layout.dts->ground_pal, 14); + SETBIT(group->g.layout.dts->ground_sprite, SPRITE_MODIFIER_OPAQUE); + } + if (HASBIT(group->g.layout.dts->ground_pal, 15)) { + /* Bit 31 set means this is a custom sprite, so rewrite it to the + * last spriteset defined. */ + SpriteID sprite = _cur_grffile->spriteset_start + GB(group->g.layout.dts->ground_sprite, 0, 14) * sprites; + SB(group->g.layout.dts->ground_sprite, 0, SPRITE_WIDTH, sprite); + CLRBIT(group->g.layout.dts->ground_pal, 15); + } + + group->g.layout.dts->seq = CallocT<DrawTileSeqStruct>(num_sprites + 1); + + for (i = 0; i < num_sprites; i++) { + DrawTileSeqStruct *seq = (DrawTileSeqStruct*)&group->g.layout.dts->seq[i]; + + seq->image = grf_load_word(&buf); + seq->pal = grf_load_word(&buf); + seq->delta_x = grf_load_byte(&buf); + seq->delta_y = grf_load_byte(&buf); + + if (HASBIT(seq->image, 14)) { + CLRBIT(seq->image, 14); + SETBIT(seq->image, PALETTE_MODIFIER_TRANSPARENT); + } + if (HASBIT(seq->image, 15)) { + CLRBIT(seq->image, 15); + SETBIT(seq->image, PALETTE_MODIFIER_COLOR); + } + if (HASBIT(seq->pal, 14)) { + CLRBIT(seq->pal, 14); + SETBIT(seq->image, SPRITE_MODIFIER_OPAQUE); + } + if (HASBIT(seq->pal, 15)) { + /* Bit 31 set means this is a custom sprite, so rewrite it to the + * last spriteset defined. */ + SpriteID sprite = _cur_grffile->spriteset_start + GB(seq->image, 0, 14) * sprites; + SB(seq->image, 0, SPRITE_WIDTH, sprite); + CLRBIT(seq->pal, 15); + } + + if (type > 0) { + seq->delta_z = grf_load_byte(&buf); + if ((byte)seq->delta_z == 0x80) continue; + } + + seq->size_x = grf_load_byte(&buf); + seq->size_y = grf_load_byte(&buf); + seq->size_z = grf_load_byte(&buf); + } + + /* Set the terminator value. */ + ((DrawTileSeqStruct*)group->g.layout.dts->seq)[i].delta_x = (byte)0x80; + + break; + } + /* Loading of Tile Layout and Production Callback groups would happen here */ default: grfmsg(1, "NewSpriteGroup: Unsupported feature %d, skipping", feature); } @@ -1934,7 +2246,7 @@ static void FeatureMapSpriteGroup(byte *buf, int len) grfmsg(6, "FeatureMapSpriteGroup: Feature %d, %d ids, %d cids, wagon override %d", feature, idcount, cidcount, wagover); - if (feature > GSF_STATION) { + if (feature > GSF_STATION && feature != GSF_TOWNHOUSE) { grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature %d, skipping", feature); return; } @@ -1986,6 +2298,29 @@ static void FeatureMapSpriteGroup(byte *buf, int len) } } return; + } else if (feature == GSF_TOWNHOUSE) { + byte *bp = &buf[4 + idcount + cidcount * 3]; + uint16 groupid = grf_load_word(&bp); + + if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) { + grfmsg(1, "FeatureMapSpriteGroup: Spriteset 0x%04X out of range 0x%X or empty, skipping.", + groupid, _cur_grffile->spritegroups_count); + return; + } + + for (uint i = 0; i < idcount; i++) { + uint8 hid = buf[3 + i]; + HouseSpec *hs = _cur_grffile->housespec[hid]; + + if (hs == NULL) { + grfmsg(1, "FeatureMapSpriteGroup: Too many houses defined, skipping"); + return; + } + + hs->spritegroup = _cur_grffile->spritegroups[groupid]; + hs->grffile = _cur_grffile; + } + return; } // FIXME: Tropicset contains things like: @@ -2148,6 +2483,7 @@ static void FeatureNewName(byte *buf, int len) break; } + case GSF_TOWNHOUSE: default: switch (GB(id, 8, 8)) { case 0xC4: /* Station class name */ @@ -2167,7 +2503,15 @@ static void FeatureNewName(byte *buf, int len) } break; - case 0xC9: + case 0xC9: { /* House name */ + if (_cur_grffile->housespec == NULL || _cur_grffile->housespec[GB(id, 0, 8)] == NULL) { + grfmsg(1, "FeatureNewName: Attempt to name undefined house 0x%X, ignoring.", GB(id, 0, 8)); + } else { + _cur_grffile->housespec[GB(id, 0, 8)]->building_name = AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED); + } + break; + } + case 0xD0: case 0xDC: AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED); @@ -2182,7 +2526,6 @@ static void FeatureNewName(byte *buf, int len) #if 0 case GSF_CANAL : case GSF_BRIDGE : - case GSF_TOWNHOUSE : AddGRFString(_cur_spriteid, id, lang, name); switch (GB(id, 8,8)) { case 0xC9: /* House name */ @@ -3478,7 +3821,7 @@ static void InitializeGRFSpecial() | (1 << 0x16) // canals | (1 << 0x17) // newstartyear | (0 << 0x18) // freighttrains - | (0 << 0x19) // newhouses + | (1 << 0x19) // newhouses | (1 << 0x1A) // newbridges | (0 << 0x1B) // newtownnames | (0 << 0x1C) // moreanimations @@ -3550,6 +3893,20 @@ static void ResetCustomStations() } } +static void ResetCustomHouses() +{ + GRFFile *file; + uint i; + + for (file = _first_grffile; file != NULL; file = file->next) { + if (file->housespec == NULL) continue; + for (i = 0; i < HOUSE_MAX; i++) free(file->housespec[i]); + + free(file->housespec); + file->housespec = NULL; + } +} + static void ResetNewGRF() { GRFFile *next; @@ -3610,6 +3967,10 @@ static void ResetNewGRFData() /* Reset the curencies array */ ResetCurrencies(); + /* Reset the house array */ + ResetCustomHouses(); + ResetHouses(); + // Reset station classes ResetStationClasses(); ResetCustomStations(); @@ -3635,6 +3996,7 @@ static void ResetNewGRFData() _traininfo_vehicle_pitch = 0; _traininfo_vehicle_width = 29; _have_2cc = false; + _have_newhouses = false; _signal_base = 0; _coast_base = 0; @@ -3838,6 +4200,41 @@ static void CalculateRefitMasks() } } +/** Add all new houses to the house array. House properties can be set at any + * time in the GRF file, so we can only add a house spec to the house array + * after the file has finished loading. We also need to check the dates, due to + * the TTDPatch behaviour described below that we need to emulate. */ +static void FinaliseHouseArray() +{ + /* If there are no houses with start dates before 1930, then all houses + * with start dates of 1930 have them reset to 0. This is in order to be + * compatible with TTDPatch, where if no houses have start dates before + * 1930 and the date is before 1930, the game pretends that this is 1930. + * If there have been any houses defined with start dates before 1930 then + * the dates are left alone. */ + bool reset_dates = true; + + for (GRFFile *file = _first_grffile; file != NULL; file = file->next) { + if (file->housespec == NULL) continue; + + for (int i = 0; i < HOUSE_MAX; i++) { + HouseSpec *hs = file->housespec[i]; + if (hs != NULL) { + SetHouseSpec(hs); + if (hs->min_date < 1930) reset_dates = false; + } + } + } + + if (reset_dates) { + for (int i = NEW_HOUSE_OFFSET; i < HOUSE_MAX; i++) { + HouseSpec *hs = GetHouseSpecs(i); + + if (hs->enabled && hs->min_date == 1930) hs->min_date = 0; + } + } +} + /* Here we perform initial decoding of some special sprites (as are they * described at http://www.ttdpatch.net/src/newgrf.txt, but this is only a very * partial implementation yet). */ @@ -4003,6 +4400,18 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage) void InitDepotWindowBlockSizes(); +static void AfterLoadGRFs() +{ + /* Pre-calculate all refit masks after loading GRF files. */ + CalculateRefitMasks(); + + /* Set the block size in the depot windows based on vehicle sprite sizes */ + InitDepotWindowBlockSizes(); + + /* Add all new houses to the house array. */ + FinaliseHouseArray(); +} + void LoadNewGRF(uint load_index, uint file_index) { InitializeGRFSpecial(); @@ -4033,9 +4442,6 @@ void LoadNewGRF(uint load_index, uint file_index) } } - // Pre-calculate all refit masks after loading GRF files - CalculateRefitMasks(); - - /* Set the block size in the depot windows based on vehicle sprite sizes */ - InitDepotWindowBlockSizes(); + /* Call any functions that should be run after GRFs have been loaded. */ + AfterLoadGRFs(); } diff --git a/src/newgrf.h b/src/newgrf.h index 963145f65..e9e5418b0 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -4,6 +4,7 @@ #define NEWGRF_H #include "station.h" +#include "town.h" #include "newgrf_config.h" #include "helpers.hpp" #include "cargotype.h" @@ -56,6 +57,7 @@ struct GRFFile { uint sound_offset; StationSpec **stations; + HouseSpec **housespec; uint32 param[0x80]; uint param_end; /// one more than the highest set parameter @@ -72,6 +74,7 @@ extern GRFFile *_first_grffile; extern SpriteID _signal_base; extern SpriteID _coast_base; extern bool _have_2cc; +extern bool _have_newhouses; void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage); void LoadNewGRF(uint load_index, uint file_index); diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h index 22aecc649..348bfb6cc 100644 --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -37,10 +37,42 @@ enum CallbackID { CBID_TRAIN_ARTIC_ENGINE = 0x16, + /* Called (if appropriate bit in callback mask is set) to determine whether + * the house can be built on the specified tile. */ + CBID_HOUSE_ALLOW_CONSTRUCTION = 0x17, + CBID_VEHICLE_CARGO_SUFFIX = 0x19, + /* Called (if appropriate bit in callback mask is set) to determine + * the next animation frame. */ + CBID_HOUSE_ANIMATION_NEXT_FRAME = 0x1A, + + /* Called (if appropriate bit in callback mask is set) for periodically + * starting or stopping the animation. */ + CBID_HOUSE_ANIMATION_START_STOP = 0x1B, + + /* Called (if appropriate bit in callback mask is set) whenever the + * construction state of a house changes. */ + CBID_CONSTRUCTION_STATE_CHANGE = 0x1C, + CBID_TRAIN_ALLOW_WAGON_ATTACH = 0x1D, + /* Called (if appropriate bit in callback mask is set) to determine the + * colour of a town building. */ + CBID_BUILDING_COLOUR = 0x1E, + + /* Called (if appropriate bit in callback mask is set) to decide how much + * cargo a town building can accept. */ + CBID_HOUSE_CARGO_ACCEPTANCE = 0x1F, // not yet implemented + + /* Called (if appropriate bit in callback mask is set) to indicate + * how long the current animation frame should last. */ + CBID_HOUSE_ANIMATION_SPEED = 0x20, + + /* Called (if appropriate bit in callback mask is set) periodically to + * determine if a house should be destroyed. */ + CBID_HOUSE_DESTRUCTION = 0x21, + /* This callback is called from vehicle purchase lists. It returns a value to be * used as a custom string ID in the 0xD000 range. */ CBID_VEHICLE_ADDITIONAL_TEXT = 0x23, @@ -48,16 +80,28 @@ enum CallbackID { /* Called when building a station to customize the tile layout */ CBID_STATION_TILE_LAYOUT = 0x24, + /* Called (if appropriate bit in callback mask is set) to determine which + * cargoes a town building should accept. */ + CBID_HOUSE_ACCEPT_CARGO = 0x2A, // not yet implemented + /* Called to determine if a specific colour map should be used for a vehicle * instead of the default livery */ CBID_VEHICLE_COLOUR_MAPPING = 0x2D, + /* Called (if appropriate bit in callback mask is set) to determine how much + * cargo a town building produces. */ + CBID_HOUSE_PRODUCE_CARGO = 0x2E, // not yet implemented + /* Called when the player (or AI) tries to start or stop a vehicle. Mainly * used for preventing a vehicle from leaving the depot. */ CBID_VEHICLE_START_STOP_CHECK = 0x31, /* Called to play a special sound effect */ CBID_VEHICLE_SOUND_EFFECT = 0x33, + + /* Called (if appropriate bit in callback mask set) to determine whether a + * town building can be destroyed. */ + CBID_HOUSE_DENY_DESTRUCTION = 0x143, }; /** @@ -84,6 +128,23 @@ enum StationCallbackMask { }; /** + * Callback masks for houses. + */ +enum HouseCallbackMask { + CBM_HOUSE_ALLOW_CONSTRUCTION = 0, + CBM_ANIMATION_NEXT_FRAME = 1, + CBM_ANIMATION_START_STOP = 2, + CBM_CONSTRUCTION_STATE_CHANGE = 3, + CBM_BUILDING_COLOUR = 4, + CBM_CARGO_ACCEPTANCE = 5, + CBM_ANIMATION_SPEED = 6, + CBM_HOUSE_DESTRUCTION = 7, + CBM_HOUSE_ACCEPT_CARGO = 8, + CBM_HOUSE_PRODUCE_CARGO = 9, + CBM_HOUSE_DENY_DESTRUCTION = 10, +}; + +/** * Result of a failed callback. */ enum { diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp new file mode 100644 index 000000000..707825341 --- /dev/null +++ b/src/newgrf_house.cpp @@ -0,0 +1,609 @@ +/* $Id$ */ + +/** @file newgrf_house.cpp */ + +#include "stdafx.h" +#include "openttd.h" +#include "functions.h" +#include "variables.h" +#include "debug.h" +#include "viewport.h" +#include "date.h" +#include "town.h" +#include "town_map.h" +#include "sound.h" +#include "sprite.h" +#include "strings.h" +#include "table/strings.h" +#include "table/sprites.h" +#include "table/town_land.h" +#include "newgrf.h" +#include "newgrf_house.h" +#include "newgrf_spritegroup.h" +#include "newgrf_callbacks.h" +#include "newgrf_town.h" +#include "newgrf_sound.h" + +static BuildingCounts _building_counts; +static HouseClassMapping _class_mapping[HOUSE_CLASS_MAX]; +HouseIDMapping _house_id_mapping[HOUSE_MAX]; + +/* Since the house IDs defined by the GRF file don't necessarily correlate + * to those used by the game, the IDs used for overriding old houses must be + * translated when the house spec is set. */ +static uint16 _house_overrides[NEW_HOUSE_OFFSET]; + +void AddHouseOverride(uint8 local_id, uint house_type) +{ + assert(house_type < NEW_HOUSE_OFFSET); + _house_overrides[house_type] = local_id; +} + +void ResetHouseOverrides() +{ + for (int i = 0; i != lengthof(_house_overrides); i++) { + _house_overrides[i] = INVALID_HOUSE_ID; + } +} + +static HouseID GetHouseID(byte grf_local_id, uint32 grfid) +{ + const HouseIDMapping *map; + + for (HouseID house_id = NEW_HOUSE_OFFSET; house_id != lengthof(_house_id_mapping); house_id++) { + map = &_house_id_mapping[house_id]; + if (map->house_id == grf_local_id && map->grfid == grfid) return house_id; + } + return INVALID_HOUSE_ID; +} + +static HouseID AddHouseID(byte grf_local_id, uint32 grfid, byte substitute_id) +{ + HouseID house_id; + HouseIDMapping *map; + + /* Look to see if this house has already been added. This is done + * separately from the loop below in case a GRF has been deleted, and there + * are any gaps in the array. */ + house_id = GetHouseID(grf_local_id, grfid); + if (house_id != INVALID_HOUSE_ID) return house_id; + + /* This house hasn't been defined before, so give it an ID now. */ + for (house_id = NEW_HOUSE_OFFSET; house_id != lengthof(_house_id_mapping); house_id++) { + map = &_house_id_mapping[house_id]; + + if (map->house_id == 0 && map->grfid == 0) { + map->house_id = grf_local_id; + map->grfid = grfid; + map->substitute_id = substitute_id; + return house_id; + } + } + + return INVALID_HOUSE_ID; +} + +void SetHouseSpec(const HouseSpec *hs) +{ + HouseID house_id = AddHouseID(hs->local_id, hs->grffile->grfid, hs->substitute_id); + + if (house_id == INVALID_HOUSE_ID) { + grfmsg(1, "SetHouseSpec: Too many houses allocated. Ignoring."); + return; + } + + memcpy(&_house_specs[house_id], hs, sizeof(*hs)); + + /* Now add the overrides. */ + for (int i = 0; i != lengthof(_house_overrides); i++) { + HouseSpec *overridden_hs = GetHouseSpecs(i); + + if (_house_overrides[i] != hs->local_id) continue; + + overridden_hs->override = house_id; + _house_overrides[i] = INVALID_HOUSE_ID; + } +} + +void ResetHouseIDMapping() +{ + memset(&_house_id_mapping, 0, sizeof(_house_id_mapping)); +} + +void CheckHouseIDs() +{ + for (TileIndex t = 0; t < MapSize(); t++) { + HouseID house_id; + + if (!IsTileType(t, MP_HOUSE)) continue; + + house_id = GetHouseType(t); + if (!GetHouseSpecs(house_id)->enabled && house_id >= NEW_HOUSE_OFFSET) { + /* The specs for this type of house are not available any more, so + * replace it with the substitute original house type. */ + SetHouseType(t, _house_id_mapping[house_id].substitute_id); + } + } + + InitializeBuildingCounts(); + AfterLoadCountBuildings(); + +} + +HouseClassID AllocateHouseClassID(byte grf_class_id, uint32 grfid) +{ + /* Start from 1 because 0 means that no class has been assigned. */ + for (int i = 1; i != lengthof(_class_mapping); i++) { + HouseClassMapping *map = &_class_mapping[i]; + + if (map->class_id == grf_class_id && map->grfid == grfid) return (HouseClassID)i; + + if (map->class_id == 0 && map->grfid == 0) { + map->class_id = grf_class_id; + map->grfid = grfid; + return (HouseClassID)i; + } + } + return HOUSE_NO_CLASS; +} + +void InitializeBuildingCounts() +{ + memset(&_building_counts, 0, sizeof(_building_counts)); +} + +/** + * IncreaseBuildingCount() + * Increase the count of a building when it has been added by a town. + * @param t The town that the building is being built in + * @param house_id The id of the house being added + */ +void IncreaseBuildingCount(Town *t, HouseID house_id) +{ + HouseClassID class_id = GetHouseSpecs(house_id)->class_id; + + if (!_have_newhouses) return; + + /* If there are 255 buildings of this type in this town, there are also + * at least that many houses of the same class in the town, and + * therefore on the map as well. */ + if (t->building_counts.id_count[house_id] == 255) return; + + t->building_counts.id_count[house_id]++; + if (_building_counts.id_count[house_id] < 255) _building_counts.id_count[house_id]++; + + /* Similarly, if there are 255 houses of this class in this town, there + * must be at least that number on the map too. */ + if (class_id == HOUSE_NO_CLASS || t->building_counts.class_count[class_id] == 255) return; + + t->building_counts.class_count[class_id]++; + if (_building_counts.class_count[class_id] < 255) _building_counts.class_count[class_id]++; +} + +/** + * DecreaseBuildingCount() + * Decrease the number of a building when it is deleted. + * @param t The town that the building was built in + * @param house_id The id of the house being removed + */ +void DecreaseBuildingCount(Town *t, HouseID house_id) +{ + HouseClassID class_id = GetHouseSpecs(house_id)->class_id; + + if (!_have_newhouses) return; + + if (t->building_counts.id_count[house_id] > 0) t->building_counts.id_count[house_id]--; + if (_building_counts.id_count[house_id] > 0) _building_counts.id_count[house_id]--; + + if (class_id == HOUSE_NO_CLASS) return; + + if (t->building_counts.class_count[class_id] > 0) t->building_counts.class_count[class_id]--; + if (_building_counts.class_count[class_id] > 0) _building_counts.class_count[class_id]--; +} + +/** + * AfterLoadCountBuildings() + * + * After a savegame has been loaded, count the number of buildings on the map. + */ +void AfterLoadCountBuildings() +{ + if (!_have_newhouses) return; + + for (TileIndex t = 0; t < MapSize(); t++) { + if (!IsTileType(t, MP_HOUSE)) continue; + IncreaseBuildingCount(GetTownByTile(t), GetHouseType(t)); + } +} + + +static uint32 HouseGetRandomBits(const ResolverObject *object) +{ + const TileIndex tile = object->u.house.tile; + return (tile == INVALID_TILE || !IsTileType(tile, MP_HOUSE)) ? 0 : GetHouseRandomBits(tile); +} + +static uint32 HouseGetTriggers(const ResolverObject *object) +{ + const TileIndex tile = object->u.house.tile; + return (tile == INVALID_TILE || !IsTileType(tile, MP_HOUSE)) ? 0 : GetHouseTriggers(tile); +} + +static void HouseSetTriggers(const ResolverObject *object, int triggers) +{ + const TileIndex tile = object->u.house.tile; + if (IsTileType(tile, MP_HOUSE)) SetHouseTriggers(tile, triggers); +} + +static uint32 GetNumHouses(HouseID house_id, const Town *town) +{ + uint8 map_id_count, town_id_count, map_class_count, town_class_count; + HouseClassID class_id = GetHouseSpecs(house_id)->class_id; + + map_id_count = _building_counts.id_count[house_id]; + map_class_count = _building_counts.class_count[class_id]; + town_id_count = town->building_counts.id_count[house_id]; + town_class_count = town->building_counts.class_count[class_id]; + + return map_class_count << 24 | town_class_count << 16 | map_id_count << 8 | town_id_count; +} + +static uint32 GetTerrainType(TileIndex tile) +{ + switch (_opt.landscape) { + case LT_DESERT: return GetTropicZone(tile) == TROPICZONE_DESERT ? 1 : 2; + case LT_HILLY: return GetTileZ(tile) >= _opt.snow_line ? 4 : 0; + default: return 0; + } +} + +static uint32 GetGRFParameter(HouseID house_id, byte parameter) +{ + const HouseSpec *hs = GetHouseSpecs(house_id); + const GRFFile *file = hs->grffile; + + if (parameter >= file->param_end) return 0; + return file->param[parameter]; +} + +/** + * HouseGetVariable(): + * + * Used by the resolver to get values for feature 07 deterministic spritegroups. + */ +static uint32 HouseGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available) +{ + const Town *town = object->u.house.town; + TileIndex tile = object->u.house.tile; + HouseID house_id = object->u.house.house_id; + + if (object->scope == VSG_SCOPE_PARENT) { + return TownGetVariable(variable, parameter, available, town); + } + + switch (variable) { + /* Construction stage. */ + case 0x40: return (IsTileType(tile, MP_HOUSE) ? GetHouseBuildingStage(tile) : 0) | OriginalTileRandomiser(TileX(tile), TileY(tile)) << 2; + + /* Building age. */ + case 0x41: return clamp(_cur_year - GetHouseConstructionYear(tile), 0, 0xFF); + + /* Town zone */ + case 0x42: return GetTownRadiusGroup(town, tile); + + /* Terrain type */ + case 0x43: return GetTerrainType(tile); + + /* Number of this type of building on the map. */ + case 0x44: return GetNumHouses(house_id, town); + + /* Whether the town is being created or just expanded. */ + case 0x45: return _generating_world ? 1 : 0; + + /* Current animation frame. */ + case 0x46: return IsTileType(tile, MP_HOUSE) ? GetHouseAnimationFrame(tile) : 0; + + + /* Building counts for old houses with id = parameter. */ + case 0x60: return GetNumHouses(parameter, town); + + /* Building counts for new houses with id = parameter. */ + case 0x61: { + const HouseSpec *hs = GetHouseSpecs(house_id); + if (hs->grffile == NULL) return 0; + + HouseID new_house = GetHouseID(parameter, hs->grffile->grfid); + return new_house == INVALID_HOUSE_ID ? 0 : GetNumHouses(new_house, town); + } + + /* Land info for nearby tiles. */ + case 0x62: { + int8 x = GB(parameter, 0, 4); + int8 y = GB(parameter, 4, 4); + byte tile_type; + + if (x >= 8) x -= 16; + if (y >= 8) y -= 16; + + tile += TileDiffXY(x, y); + + tile_type = GetTerrainType(tile) << 2 | (IsTileType(tile, MP_WATER) ? 1 : 0) << 1; + + return GetTileType(tile) << 24 | (TileHeight(tile) * 8) << 16 | tile_type << 8 | GetTileSlope(tile, NULL); + } + + /* Read GRF parameter */ + case 0x7F: return GetGRFParameter(object->u.house.house_id, parameter); + } + + DEBUG(grf, 1, "Unhandled house property 0x%X", variable); + + *available = false; + return UINT_MAX; +} + +static const SpriteGroup *HouseResolveReal(const ResolverObject *object, const SpriteGroup *group) +{ + /* Houses do not have 'real' groups */ + return NULL; +} + +/** + * NewHouseResolver(): + * + * Returns a resolver object to be used with feature 07 spritegroups. + */ +static void NewHouseResolver(ResolverObject *res, HouseID house_id, TileIndex tile, Town *town) +{ + res->GetRandomBits = HouseGetRandomBits; + res->GetTriggers = HouseGetTriggers; + res->SetTriggers = HouseSetTriggers; + res->GetVariable = HouseGetVariable; + res->ResolveReal = HouseResolveReal; + + res->u.house.tile = tile; + res->u.house.town = town; + res->u.house.house_id = house_id; + + res->callback = 0; + res->callback_param1 = 0; + res->callback_param2 = 0; + res->last_value = 0; + res->trigger = 0; + res->reseed = 0; +} + +uint16 GetHouseCallback(uint16 callback, uint32 param1, HouseID house_id, Town *town, TileIndex tile) +{ + ResolverObject object; + const SpriteGroup *group; + + NewHouseResolver(&object, house_id, tile, town); + object.callback = callback; + object.callback_param1 = param1; + object.callback_param2 = 0; + + group = Resolve(GetHouseSpecs(house_id)->spritegroup, &object); + if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED; + + return group->g.callback.result; +} + +void DrawTileLayout(const TileInfo *ti, const SpriteGroup *group, byte stage, HouseID house_id) +{ + const DrawTileSprites *dts = group->g.layout.dts; + const DrawTileSeqStruct *dtss; + + SpriteID image = dts->ground_sprite; + SpriteID pal = dts->ground_pal; + + if (GB(image, 0, SPRITE_WIDTH) != 0) DrawGroundSprite(image, pal); + + foreach_draw_tile_seq(dtss, dts->seq) { + if (GB(dtss->image, 0, SPRITE_WIDTH) == 0) continue; + + image = dtss->image + stage; + pal = dtss->pal; + + if (!HASBIT(image, SPRITE_MODIFIER_OPAQUE) && ((_display_opt & DO_TRANS_BUILDINGS))) { + SETBIT(image, PALETTE_MODIFIER_TRANSPARENT); + pal = PALETTE_TO_TRANSPARENT; + } else if (HASBIT(image, PALETTE_MODIFIER_COLOR)) { + if (pal == 0) { + const HouseSpec *hs = GetHouseSpecs(house_id); + if (HASBIT(hs->callback_mask, CBM_BUILDING_COLOUR)) { + uint16 callback = GetHouseCallback(CBID_BUILDING_COLOUR, 0, house_id, GetTownByTile(ti->tile), ti->tile); + if (callback != CALLBACK_FAILED) { + /* If bit 14 is set, we should use a 2cc colour map, else use the callback value. */ + pal = HASBIT(callback, 14) ? GB(callback, 0, 8) + SPR_2CCMAP_BASE : callback; + } + } else { + pal = hs->random_colour[OriginalTileRandomiser(ti->x, ti->y)] + PALETTE_RECOLOR_START; + } + } + } else { + pal = PAL_NONE; + } + + if ((byte)dtss->delta_z != 0x80) { + AddSortableSpriteToDraw( + image, pal, + ti->x + dtss->delta_x, ti->y + dtss->delta_y, + dtss->size_x, dtss->size_y, + dtss->size_z, ti->z + dtss->delta_z + ); + } else { + AddChildSpriteScreen(image, pal, dtss->delta_x, dtss->delta_y); + } + } +} + +void DrawNewHouseTile(TileInfo *ti, HouseID house_id) +{ + const HouseSpec *hs = GetHouseSpecs(house_id); + const SpriteGroup *group; + ResolverObject object; + + if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh); + + NewHouseResolver(&object, house_id, ti->tile, GetTownByTile(ti->tile)); + + group = Resolve(hs->spritegroup, &object); + if (group == NULL || group->type != SGT_TILELAYOUT) { + /* XXX: This is for debugging purposes really, and shouldn't stay. */ + DrawGroundSprite(SPR_SHADOW_CELL, PAL_NONE); + } else { + /* Limit the building stage to the number of stages supplied. */ + byte stage = GetHouseBuildingStage(ti->tile); + stage = clamp(stage - 4 + group->g.layout.num_sprites, 0, group->g.layout.num_sprites - 1); + DrawTileLayout(ti, group, stage, house_id); + } +} + +void AnimateNewHouseTile(TileIndex tile) +{ + const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); + byte animation_speed = hs->animation_speed; + bool frame_set_by_callback = false; + + if (HASBIT(hs->callback_mask, CBM_ANIMATION_SPEED)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_SPEED, 0, GetHouseType(tile), GetTownByTile(tile), tile); + if (callback_res != CALLBACK_FAILED) animation_speed = clamp(callback_res & 0xFF, 2, 16); + } + + /* An animation speed of 2 means the animation frame changes 4 ticks, and + * increasing this value by one doubles the wait. 2 is the minimum value + * allowed for animation_speed, which corresponds to 120ms, and 16 is the + * maximum, corresponding to around 33 minutes. */ + if (_tick_counter % (1 << animation_speed) != 0) return; + + byte frame = GetHouseAnimationFrame(tile); + byte num_frames = GB(hs->animation_frames, 0, 7); + + if (HASBIT(hs->callback_mask, CBM_ANIMATION_NEXT_FRAME)) { + uint32 param = (hs->extra_flags & CALLBACK_1A_RANDOM_BITS) ? Random() : 0; + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_NEXT_FRAME, param, GetHouseType(tile), GetTownByTile(tile), tile); + + if (callback_res != CALLBACK_FAILED) { + frame_set_by_callback = true; + + switch (callback_res & 0xFF) { + case 0xFF: + DeleteAnimatedTile(tile); + break; + case 0xFE: + /* Carry on as normal. */ + frame_set_by_callback = false; + break; + default: + frame = callback_res & 0xFF; + break; + } + + /* If the lower 7 bits of the upper byte of the callback + * result are not empty, it is a sound effect. */ + if (GB(callback_res, 8, 7) != 0) PlayHouseSound(GB(callback_res, 8, 7), tile); + } + } + + if (!frame_set_by_callback) { + if (frame < num_frames) { + frame++; + } else if (frame == num_frames && HASBIT(hs->animation_frames, 7)) { + /* This animation loops, so start again from the beginning */ + frame = 0; + } else { + /* This animation doesn't loop, so stay here */ + DeleteAnimatedTile(tile); + } + } + + SetHouseAnimationFrame(tile, frame); + MarkTileDirtyByTile(tile); +} + +void ChangeHouseAnimationFrame(TileIndex tile, uint16 callback_result) +{ + switch (callback_result & 0xFF) { + case 0xFD: /* Do nothing. */ break; + case 0xFE: AddAnimatedTile(tile); break; + case 0xFF: DeleteAnimatedTile(tile); break; + default: + SetHouseAnimationFrame(tile, callback_result & 0xFF); + AddAnimatedTile(tile); + break; + } + /* If the lower 7 bits of the upper byte of the callback + * result are not empty, it is a sound effect. */ + if (GB(callback_result, 8, 7) != 0) PlayHouseSound(GB(callback_result, 8, 7), tile); +} + +bool CanDeleteHouse(TileIndex tile) +{ + const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); + + /* Human players are always allowed to remove buildings, as is water and + * anyone using the scenario editor. */ + if ((IsValidPlayer(_current_player) && IsHumanPlayer(_current_player)) + || _current_player == OWNER_WATER || _current_player == OWNER_NONE) return true; + + if (HASBIT(hs->callback_mask, CBM_HOUSE_DENY_DESTRUCTION)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_DENY_DESTRUCTION, 0, GetHouseType(tile), GetTownByTile(tile), tile); + return (callback_res == CALLBACK_FAILED || callback_res == 0); + } else { + return !(hs->extra_flags & BUILDING_IS_PROTECTED); + } +} + +static void AnimationControl(TileIndex tile, uint16 random_bits) +{ + const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); + + if (HASBIT(hs->callback_mask, CBM_ANIMATION_START_STOP)) { + uint32 param = (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) ? (GB(Random(), 0, 16) | random_bits << 16) : Random(); + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_START_STOP, param, GetHouseType(tile), GetTownByTile(tile), tile); + + if (callback_res != CALLBACK_FAILED) ChangeHouseAnimationFrame(tile, callback_res); + } +} + +bool NewHouseTileLoop(TileIndex tile) +{ + const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); + + if (GetHouseProcessingTime(tile) > 0) { + DecHouseProcessingTime(tile); + return true; + } + + /* @todo: Magic with triggers goes here. Got to implement that, one day. .. */ + + if (HASBIT(hs->callback_mask, CBM_ANIMATION_START_STOP)) { + /* If this house is marked as having a synchronised callback, all the + * tiles will have the callback called at once, rather than when the + * tile loop reaches them. This should only be enabled for the northern + * tile, or strange things will happen (here, and in TTDPatch). */ + if (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) { + uint16 random = GB(Random(), 0, 16); + + if (hs->building_flags & BUILDING_HAS_1_TILE) AnimationControl(tile, random); + if (hs->building_flags & BUILDING_2_TILES_Y) AnimationControl(TILE_ADDXY(tile, 0, 1), random); + if (hs->building_flags & BUILDING_2_TILES_X) AnimationControl(TILE_ADDXY(tile, 1, 0), random); + if (hs->building_flags & BUILDING_HAS_4_TILES) AnimationControl(TILE_ADDXY(tile, 1, 1), random); + } else { + AnimationControl(tile, 0); + } + } + + /* Check callback 21, which determines if a house should be destroyed. */ + if (HASBIT(hs->callback_mask, CBM_HOUSE_DESTRUCTION)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_DESTRUCTION, 0, GetHouseType(tile), GetTownByTile(tile), tile); + if (callback_res != CALLBACK_FAILED && callback_res > 0) { + ClearTownHouse(GetTownByTile(tile), tile); + return false; + } + } + + SetHouseProcessingTime(tile, hs->processing_time); + return true; +} diff --git a/src/newgrf_house.h b/src/newgrf_house.h new file mode 100644 index 000000000..513b46094 --- /dev/null +++ b/src/newgrf_house.h @@ -0,0 +1,71 @@ +/* $Id$ */ + +/** @file newgrf_house.h */ + +#ifndef NEWGRF_HOUSE_H +#define NEWGRF_HOUSE_H + +#include "town.h" + +/** + * Maps a house id stored on the map to a GRF file. + * House IDs are stored on the map, so there needs to be a way to tie them to + * GRF files. An array of HouseIDMapping structs is saved with the savegame so + * that house GRFs can be loaded in a different order, or removed safely. The + * index in the array is the house ID stored on the map. + * + * The substitute ID is the ID of an original house that should be used instead + * if the GRF containing the new house is not available. + */ +struct HouseIDMapping { + uint32 grfid; ///< The GRF ID of the file this house belongs to + uint8 house_id; ///< The house ID within the GRF file + uint8 substitute_id; ///< The (original) house ID to use if this GRF is not available +}; + +/** + * Makes class IDs unique to each GRF file. + * Houses can be assigned class IDs which are only comparable within the GRF + * file they were defined in. This mapping ensures that if two houses have the + * same class as defined by the GRF file, the classes are different within the + * game. An array of HouseClassMapping structs is created, and the array index + * of the struct that matches both the GRF ID and the class ID is the class ID + * used in the game. + * + * Although similar to the HouseIDMapping struct above, this serves a different + * purpose. Since the class ID is not saved anywhere, this mapping does not + * need to be persistent; it just needs to keep class ids unique. + */ +struct HouseClassMapping { + uint32 grfid; ////< The GRF ID of the file this class belongs to + uint8 class_id; ////< The class id within the grf file +}; + +extern HouseIDMapping _house_id_mapping[HOUSE_MAX]; ///< Declared in newgrf_house.cpp + +void AddHouseOverride(uint8 local_id, uint house_type); +void ResetHouseOverrides(); + +void SetHouseSpec(const HouseSpec *hs); + +void CheckHouseIDs(); +void ResetHouseIDMapping(); + +HouseClassID AllocateHouseClassID(byte grf_class_id, uint32 grfid); + +void InitializeBuildingCounts(); +void IncreaseBuildingCount(Town *t, HouseID house_id); +void DecreaseBuildingCount(Town *t, HouseID house_id); +void AfterLoadCountBuildings(); + +void DrawNewHouseTile(TileInfo *ti, HouseID house_id); +void AnimateNewHouseTile(TileIndex tile); +void ChangeHouseAnimationFrame(TileIndex tile, uint16 callback_result); + +uint16 GetHouseCallback(uint16 callback, uint32 param1, HouseID house_id, Town *town, TileIndex tile); + +bool CanDeleteHouse(TileIndex tile); + +bool NewHouseTileLoop(TileIndex tile); + +#endif /* NEWGRF_HOUSE_H */ diff --git a/src/newgrf_sound.cpp b/src/newgrf_sound.cpp index 136ea84a3..8064546fe 100644 --- a/src/newgrf_sound.cpp +++ b/src/newgrf_sound.cpp @@ -66,3 +66,12 @@ bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event) if (callback < GetNumSounds()) SndPlayVehicleFx((SoundFx)callback, v); return true; } + +bool PlayHouseSound(uint16 sound_id, TileIndex tile) +{ + if (sound_id < GetNumOriginalSounds()) { + SndPlayTileFx((SoundFx)sound_id, tile); + return true; + } + return false; +} diff --git a/src/newgrf_sound.h b/src/newgrf_sound.h index 61f7df12e..3b52d68cb 100644 --- a/src/newgrf_sound.h +++ b/src/newgrf_sound.h @@ -21,5 +21,6 @@ void InitializeSoundPool(); FileEntry *GetSound(uint index); uint GetNumSounds(); bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event); +bool PlayHouseSound(uint16 sound_id, TileIndex tile); #endif /* NEWGRF_SOUND_H */ diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index b73377dd0..fcde3cc28 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -8,6 +8,7 @@ #include "newgrf_callbacks.h" #include "newgrf_spritegroup.h" #include "date.h" +#include "sprite.h" static void SpriteGroupPoolCleanBlock(uint start_item, uint end_item); @@ -33,6 +34,11 @@ static void DestroySpriteGroup(SpriteGroup *group) free((SpriteGroup**)group->g.random.groups); break; + case SGT_TILELAYOUT: + free((void*)group->g.layout.dts->seq); + free(group->g.layout.dts); + break; + default: break; } diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index cbd79a97d..bf1c54fe5 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -3,6 +3,7 @@ #ifndef NEWGRF_SPRITEGROUP_H #define NEWGRF_SPRITEGROUP_H +#include "town.h" struct SpriteGroup; @@ -128,6 +129,11 @@ struct ResultSpriteGroup { byte num_sprites; }; +struct TileLayoutSpriteGroup { + byte num_sprites; /* Number of sprites in the spriteset, used for loading stages */ + struct DrawTileSprites *dts; +}; + /* List of different sprite group types */ enum SpriteGroupType { SGT_INVALID, @@ -136,6 +142,7 @@ enum SpriteGroupType { SGT_RANDOMIZED, SGT_CALLBACK, SGT_RESULT, + SGT_TILELAYOUT, }; /* Common wrapper for all the different sprite group types */ @@ -148,6 +155,7 @@ struct SpriteGroup { RandomizedSpriteGroup random; CallbackResultSpriteGroup callback; ResultSpriteGroup result; + TileLayoutSpriteGroup layout; } g; }; @@ -180,6 +188,11 @@ struct ResolverObject { const struct StationSpec *statspec; CargoID cargo_type; } station; + struct { + TileIndex tile; + Town *town; + HouseID house_id; + } house; } u; uint32 (*GetRandomBits)(const struct ResolverObject*); diff --git a/src/newgrf_town.cpp b/src/newgrf_town.cpp new file mode 100644 index 000000000..497684e59 --- /dev/null +++ b/src/newgrf_town.cpp @@ -0,0 +1,91 @@ +/* $Id$ */ + +/** @file newgrf_town.cpp */ + +#include "stdafx.h" +#include "openttd.h" +#include "debug.h" +#include "functions.h" +#include "town.h" + +/** This function implements the town variables that newGRF defines. + * @param variable that is queried + * @param parameter unused + * @param available will return false if ever the variable asked for does not exist + * @param t is of course the town we are inquiring + * @return the value stored in the corresponding variable*/ +uint32 TownGetVariable(byte variable, byte parameter, bool *available, const Town *t) +{ + switch (variable) { + /* Larger towns */ + case 0x40: return 1; + + /* Town index */ + case 0x41: return t->index; + + /* Town properties */ + case 0x80: return t->xy; + case 0x81: return GB(t->xy, 8, 8); + case 0x82: return t->population; + case 0x83: return GB(t->population, 8, 8); + case 0x8A: return t->grow_counter; + case 0x92: return t->flags12; // In original game, 0x92 and 0x93 are really one word. Since flags12 is a byte, this is to adjust + case 0x93: return 0; + case 0x94: return t->radius[0]; + case 0x95: return GB(t->radius[0], 8, 8); + case 0x96: return t->radius[1]; + case 0x97: return GB(t->radius[1], 8, 8); + case 0x98: return t->radius[2]; + case 0x99: return GB(t->radius[2], 8, 8); + case 0x9A: return t->radius[3]; + case 0x9B: return GB(t->radius[3], 8, 8); + case 0x9C: return t->radius[4]; + case 0x9D: return GB(t->radius[4], 8, 8); + case 0x9E: return t->ratings[0]; + case 0x9F: return t->ratings[1]; + case 0xA0: return t->ratings[2]; + case 0xA1: return t->ratings[3]; + case 0xA2: return t->ratings[4]; + case 0xA3: return t->ratings[5]; + case 0xA4: return t->ratings[6]; + case 0xA5: return t->ratings[7]; + case 0xA6: return t->ratings[8]; + case 0xAE: return t->have_ratings; + case 0xB2: return t->statues; + case 0xB6: return t->num_houses; + case 0xB9: return t->growth_rate; + case 0xBA: return t->new_max_pass; + case 0xBB: return GB(t->new_max_pass, 8, 8); + case 0xBC: return t->new_max_mail; + case 0xBD: return GB(t->new_max_mail, 8, 8); + case 0xBE: return t->new_act_pass; + case 0xBF: return GB(t->new_act_pass, 8, 8); + case 0xC0: return t->new_act_mail; + case 0xC1: return GB(t->new_act_mail, 8, 8); + case 0xC2: return t->max_pass; + case 0xC3: return GB(t->max_pass, 8, 8); + case 0xC4: return t->max_mail; + case 0xC5: return GB(t->max_mail, 8, 8); + case 0xC6: return t->act_pass; + case 0xC7: return GB(t->act_pass, 8, 8); + case 0xC8: return t->act_mail; + case 0xC9: return GB(t->act_mail, 8, 8); + case 0xCA: return t->pct_pass_transported; + case 0xCB: return t->pct_mail_transported; + case 0xCC: return t->new_act_food; + case 0xCD: return GB(t->new_act_food, 8, 8); + case 0xCE: return t->new_act_water; + case 0xCF: return GB(t->new_act_water, 8, 8); + case 0xD0: return t->act_food; + case 0xD1: return GB(t->act_food, 8, 8); + case 0xD2: return t->act_water; + case 0xD3: return GB(t->act_water, 8, 8); + case 0xD4: return t->road_build_months; + case 0xD5: return t->fund_buildings_months; + } + + DEBUG(grf, 1, "Unhandled town property 0x%X", variable); + + *available = false; + return (uint32)-1; +} diff --git a/src/newgrf_town.h b/src/newgrf_town.h new file mode 100644 index 000000000..ec53fcc9a --- /dev/null +++ b/src/newgrf_town.h @@ -0,0 +1,13 @@ +/* $Id$ */ + +/** @file newgrf_town.h */ + +#ifndef NEWGRF_TOWN_H +#define NEWGRF_TOWN_H + +/* Currently there is no direct town resolver; we only need to get town + * variable results from inside stations, house tiles and industry tiles. */ + +uint32 TownGetVariable(byte variable, byte parameter, bool *available, const Town *t); + +#endif /* NEWGRF_TOWN_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index 57e99a71a..88a460429 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -56,6 +56,7 @@ #include "clear_map.h" #include "fontcache.h" #include "newgrf_config.h" +#include "newgrf_house.h" #include "player_face.h" #include "bridge_map.h" @@ -677,6 +678,7 @@ static void MakeNewGame(bool from_heightmap) _game_mode = GM_NORMAL; ResetGRFConfig(true); + ResetHouseIDMapping(); GenerateWorldSetCallback(&MakeNewGameDone); GenerateWorld(from_heightmap ? GW_HEIGHTMAP : GW_NEWGAME, 1 << _patches.map_x, 1 << _patches.map_y); @@ -1756,6 +1758,47 @@ bool AfterLoadGame() /* do the same as when elrails were enabled/disabled manually just now */ SettingsDisableElrail(_patches.disable_elrails); + /* From version 52, the map array was changed for house tiles to allow + * space for newhouses grf features. A new byte, m7, was also added. */ + if (CheckSavegameVersion(52)) { + for (TileIndex t = 0; t < map_size; t++) { + _me[t].m7 = 0; + + if (IsTileType(t, MP_HOUSE)) { + if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { + /* Move the construction stage from m3[7..6] to m5[5..4]. + * The construction counter does not have to move. */ + SB(_m[t].m5, 3, 2, GB(_m[t].m3, 6, 2)); + SB(_m[t].m3, 6, 2, 0); + + /* The "house is completed" bit is now in m6[2]. */ + SetHouseCompleted(t, false); + } else { + /* The "lift has destination" bit has been moved from + * m5[7] to m7[0]. */ + SB(_me[t].m7, 0, 1, HASBIT(_m[t].m5, 7)); + CLRBIT(_m[t].m5, 7); + + /* The "lift is moving" bit has been removed, as it does + * the same job as the "lift has destination" bit. */ + CLRBIT(_m[t].m1, 7); + + /* The position of the lift goes from m1[7..0] to m6[7..2], + * making m1 totally free, now. The lift position does not + * have to be a full byte since the maximum value is 36. */ + SetLiftPosition(t, GB(_m[t].m1, 0, 6 )); + + _m[t].m1 = 0; + _m[t].m3 = 0; + SetHouseCompleted(t, true); + } + } + } + } + + /* Count the buildings after updating the map array. */ + AfterLoadCountBuildings(); + if (CheckSavegameVersion(43)) { for (TileIndex t = 0; t < map_size; t++) { if (IsTileType(t, MP_INDUSTRY)) { @@ -1886,6 +1929,8 @@ void ReloadNewGRFData() /* update station and waypoint graphics */ AfterLoadWaypoints(); AfterLoadStations(); + /* check that house ids are still valid */ + CheckHouseIDs(); /* redraw the whole screen */ MarkWholeScreenDirty(); } diff --git a/src/table/sprites.h b/src/table/sprites.h index e9b9c9b69..0301d9f20 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -35,7 +35,6 @@ * @todo Split the "Sprites" enum into smaller chunks and document them */ - enum Sprites { SPR_SELECT_TILE = 752, SPR_DOT = 774, // corner marker for lower/raise land @@ -1324,6 +1323,7 @@ enum SpriteSetup { */ enum Modifiers { SPRITE_MODIFIER_USE_OFFSET = OFFSET_BIT, + SPRITE_MODIFIER_OPAQUE = OFFSET_BIT, ///when a sprite is to be displayed transparently, this bit needs to be set. PALETTE_MODIFIER_TRANSPARENT = TRANSPARENT_BIT, ///this bit is set when a recoloring process is in action @@ -1418,6 +1418,5 @@ enum PaletteSprites { PALETTE_59E = 0x59E, PALETTE_59F = 0x59F, }; -#undef PALETTE_RECOLOR_SPRITE #endif /* SPRITES_H */ diff --git a/src/table/town_land.h b/src/table/town_land.h index 091b8926a..313c2b2b0 100644 --- a/src/table/town_land.h +++ b/src/table/town_land.h @@ -2,17 +2,6 @@ /** @file town_land.h */ -enum { - HOUSE_TEMP_CHURCH = 0x03, - HOUSE_STADIUM = 0x14, - HOUSE_MODERN_STADIUM = 0x20, - HOUSE_ARCT_CHURCH = 0x3c, - HOUSE_SNOW_CHURCH = 0x3d, - HOUSE_TROP_CHURCH = 0x53, - HOUSE_TOY_CHURCH = 0x5b, - - HOUSE_MAX = 110 -}; /** Writes the data into the Town Tile Drawing Struct * @param s1 The first sprite of the building, mostly the ground sprite @@ -1790,437 +1779,372 @@ static const DrawBuildingsTileStruct _town_draw_tile_data[] = { M(0x1244, PAL_NONE, 0x125a, PAL_NONE, 0, 0, 16, 16, 50, 0), }; #undef M -// 4 variants * 4 build stages -assert_compile(lengthof(_town_draw_tile_data) == HOUSE_MAX * 4 * 4); - - -static const StringID _town_tile_names[] = { - STR_200F_TALL_OFFICE_BLOCK, - STR_2010_OFFICE_BLOCK, - STR_2011_SMALL_BLOCK_OF_FLATS, - STR_2012_CHURCH, - STR_2013_LARGE_OFFICE_BLOCK, - STR_2013_LARGE_OFFICE_BLOCK, - STR_2014_TOWN_HOUSES, - STR_2015_HOTEL, - STR_2015_HOTEL, - STR_2016_STATUE, - STR_2017_FOUNTAIN, - STR_2018_PARK, - STR_2018_PARK, - STR_2019_OFFICE_BLOCK, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_201B_MODERN_OFFICE_BUILDING, - STR_201C_WAREHOUSE, - STR_201D_OFFICE_BLOCK, - STR_201E_STADIUM, - STR_201E_STADIUM, - STR_201E_STADIUM, - STR_201E_STADIUM, - STR_201F_OLD_HOUSES, - STR_2036_COTTAGES, - STR_2037_HOUSES, - STR_2038_FLATS, - STR_2039_TALL_OFFICE_BLOCK, - STR_203A_SHOPS_AND_OFFICES, - STR_203B_SHOPS_AND_OFFICES, - STR_203C_THEATER, - STR_203D_STADIUM, - STR_203D_STADIUM, - STR_203D_STADIUM, - STR_203D_STADIUM, - STR_203E_OFFICES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_2040_CINEMA, - STR_2041_SHOPPING_MALL, - STR_2041_SHOPPING_MALL, - STR_2041_SHOPPING_MALL, - STR_2041_SHOPPING_MALL, - STR_2038_FLATS, - STR_2038_FLATS, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_2012_CHURCH, - STR_2012_CHURCH, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_2015_HOTEL, - STR_2015_HOTEL, - STR_2015_HOTEL, - STR_2015_HOTEL, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_2038_FLATS, - STR_2012_CHURCH, - STR_203F_HOUSES, - STR_2038_FLATS, - STR_2038_FLATS, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_2038_FLATS, - STR_200F_TALL_OFFICE_BLOCK, - STR_2012_CHURCH, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_200F_TALL_OFFICE_BLOCK, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_200F_TALL_OFFICE_BLOCK, - STR_2059_IGLOO, - STR_205A_TEPEES, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_200F_TALL_OFFICE_BLOCK, - STR_2016_STATUE, - STR_205B_TEAPOT_HOUSE, - STR_205C_PIGGY_BANK, -}; -assert_compile(lengthof(_town_tile_names) == HOUSE_MAX); - -static const uint16 _housetype_flags[] = { - 0x1010, 0x1018, 0x100E, 0x100F, 0x7010, 0x0810, 0x100F, 0x1014, - 0x0000, 0x7018, 0x7010, 0x1004, 0x1008, 0x1018, 0x101C, 0x101C, - 0x101C, 0x7010, 0x1010, 0x1010, 0x100F, 0x0000, 0x0000, 0x0000, - 0x1003, 0x1001, 0x100F, 0x101C, 0x101C, 0x101C, 0x5014, 0x1018, - 0x700F, 0x0000, 0x0000, 0x0000, 0x7018, 0x2003, 0x0803, 0x101C, - 0x101E, 0x0000, 0x0000, 0x0000, 0x201C, 0x081C, 0x200F, 0x080F, - 0x2007, 0x0807, 0x6018, 0x0818, 0x2018, 0x0818, 0x6018, 0x0818, - 0x2001, 0x0801, 0x201E, 0x081E, 0x200F, 0x080F, 0x2007, 0x0807, - 0x201C, 0x081C, 0x201C, 0x0000, 0x081C, 0x0000, 0x601C, 0x081C, - 0x2018, 0x0818, 0x201C, 0x0000, 0x081C, 0x0000, 0x401E, 0x401E, - 0x401E, 0x4001, 0x401C, 0x400E, 0x401E, 0x401C, 0x401C, 0x4018, - 0x4000, 0x401C, 0x4018, 0x801F, 0x801F, 0x8003, 0x800F, 0x800F, - 0x800F, 0x800F, 0x801C, 0x801F, 0x0000, 0x801C, 0x8001, 0x8001, - 0x801C, 0x801C, 0x801C, 0x801C, 0x801F, 0x801F, -}; -assert_compile(lengthof(_housetype_flags) == HOUSE_MAX); - -static const byte _housetype_extra_flags[] = { - 0, 0, 0, 0, 32, 32, 0, 8, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 16, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 16, 0, 0, 0, 0, 0, 0, 0, - 16, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 8, 0, 8, 0, 0, 0, - 0, 0, 4, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, -}; -assert_compile(lengthof(_housetype_extra_flags) == HOUSE_MAX); - -static const byte _housetype_population[] = { - 187, 85, 40, 5, 220, 220, 30, 140, - 0, 0, 0, 0, 0, 150, 95, 95, - 95, 130, 6, 110, 65, 0, 0, 0, - 15, 12, 13, 100, 170, 100, 180, 35, - 65, 0, 0, 0, 140, 15, 15, 35, - 180, 0, 0, 0, 80, 80, 16, 16, - 14, 14, 135, 135, 170, 170, 210, 210, - 10, 10, 25, 25, 6, 6, 17, 17, - 90, 90, 140, 0, 140, 0, 105, 105, - 190, 190, 250, 0, 250, 0, 16, 16, - 16, 7, 45, 8, 18, 90, 120, 250, - 0, 80, 180, 8, 18, 7, 15, 17, - 19, 21, 75, 35, 0, 85, 11, 10, - 67, 86, 95, 30, 25, 18, -}; -assert_compile(lengthof(_housetype_population) == HOUSE_MAX); - -static const byte _housetype_mailamount[] = { - 70, 55, 20, 2, 85, 85, 12, 22, - 22, 0, 0, 0, 0, 65, 48, 48, - 48, 50, 10, 55, 5, 5, 5, 5, - 6, 7, 8, 35, 50, 40, 64, 23, - 5, 5, 5, 5, 65, 6, 6, 23, - 5, 5, 5, 5, 20, 20, 6, 6, - 6, 6, 60, 60, 70, 70, 80, 80, - 5, 5, 20, 20, 2, 2, 7, 7, - 45, 45, 25, 25, 25, 25, 50, 50, - 75, 75, 60, 60, 60, 60, 6, 6, - 5, 4, 15, 3, 7, 24, 25, 80, - 80, 23, 90, 3, 5, 3, 6, 6, - 6, 6, 20, 9, 0, 18, 3, 3, - 22, 23, 28, 10, 8, 7, -}; -assert_compile(lengthof(_housetype_mailamount) == HOUSE_MAX); - -static const byte _housetype_remove_cost[] = { - 150, 140, 100, 90, 160, 160, 80, 180, - 180, 65, 65, 60, 60, 130, 110, 105, - 107, 200, 145, 155, 250, 250, 250, 250, - 70, 75, 71, 135, 145, 132, 155, 220, - 250, 250, 250, 250, 170, 70, 70, 210, - 250, 250, 250, 250, 100, 100, 70, 70, - 80, 80, 150, 150, 170, 170, 200, 200, - 60, 60, 100, 100, 85, 85, 80, 80, - 140, 140, 160, 160, 160, 160, 130, 130, - 190, 190, 140, 140, 140, 140, 80, 80, - 80, 30, 130, 90, 80, 110, 120, 190, - 190, 110, 180, 90, 90, 70, 80, 80, - 80, 80, 160, 90, 90, 150, 60, 60, - 140, 145, 165, 90, 75, 85, -}; -assert_compile(lengthof(_housetype_remove_cost) == HOUSE_MAX); - -static const uint16 _housetype_remove_ratingmod[] = { - 140, 130, 90, 230, 160, 160, 80, 150, - 150, 40, 40, 75, 75, 110, 100, 100, - 100, 150, 110, 110, 300, 300, 300, 300, - 75, 75, 75, 100, 170, 135, 180, 230, - 300, 300, 300, 300, 250, 75, 75, 230, - 300, 300, 300, 300, 90, 90, 70, 70, - 70, 70, 120, 120, 130, 130, 140, 140, - 60, 60, 80, 80, 230, 230, 80, 80, - 110, 110, 160, 160, 160, 160, 105, 105, - 135, 135, 200, 200, 200, 200, 80, 80, - 80, 30, 95, 200, 80, 95, 95, 140, - 140, 95, 150, 200, 90, 50, 75, 75, - 75, 75, 130, 80, 80, 130, 45, 45, - 130, 130, 130, 70, 65, 95, -}; -assert_compile(lengthof(_housetype_remove_ratingmod) == HOUSE_MAX); - - -struct HousetypeYear { - Year min, max; -}; - -static const HousetypeYear _housetype_years[] = { - { 1963, MAX_YEAR }, - { 1957, MAX_YEAR }, - { 1968, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1975, MAX_YEAR }, - { 1975, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1959, MAX_YEAR }, - { 1959, MAX_YEAR }, - { 1945, MAX_YEAR }, - { 1945, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1935, MAX_YEAR }, - { 1951, MAX_YEAR }, - { 1930, 1960 }, - { 1930, 1960 }, - { 1930, 1960 }, - { 1977, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 1985, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, 1951 }, - { 0, 1952 }, - { 1931, MAX_YEAR }, - { 1935, MAX_YEAR }, - { 1963, MAX_YEAR }, - { 0, 1955 }, - { 1973, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1958, MAX_YEAR }, - { 1958, MAX_YEAR }, - { 1958, MAX_YEAR }, - { 1958, MAX_YEAR }, - { 2000, MAX_YEAR }, - { 0, 1960 }, - { 0, 1960 }, - { 1945, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, 1963 }, - { 0, 1963 }, - { 1966, MAX_YEAR }, - { 1966, MAX_YEAR }, - { 1970, MAX_YEAR }, - { 1970, MAX_YEAR }, - { 1974, MAX_YEAR }, - { 1974, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, 1960 }, - { 0, 1960 }, - { 1972, MAX_YEAR }, - { 1972, MAX_YEAR }, - { 1972, MAX_YEAR }, - { 1972, MAX_YEAR }, - { 1963, MAX_YEAR }, - { 1963, MAX_YEAR }, - { 1978, MAX_YEAR }, - { 1978, MAX_YEAR }, - { 1967, MAX_YEAR }, - { 1967, MAX_YEAR }, - { 1967, MAX_YEAR }, - { 1967, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1973, MAX_YEAR }, - { 1962, MAX_YEAR }, - { 1984, MAX_YEAR }, - { 1984, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1993, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, -}; -assert_compile(lengthof(_housetype_years) == HOUSE_MAX); - -static const byte _housetype_cargo_passengers[] = { - 8, 8, 8, 2, 10, 10, 4, 6, - 6, 2, 2, 2, 2, 8, 6, 6, - 6, 10, 6, 6, 4, 4, 4, 4, - 3, 3, 3, 7, 8, 6, 8, 8, - 4, 4, 4, 4, 8, 3, 3, 8, - 8, 8, 8, 8, 5, 5, 3, 3, - 3, 3, 8, 8, 9, 9, 10, 10, - 2, 2, 3, 3, 2, 2, 3, 3, - 6, 6, 6, 6, 6, 6, 7, 7, - 9, 9, 7, 7, 7, 7, 3, 3, - 3, 3, 6, 2, 3, 6, 6, 8, - 8, 6, 8, 2, 6, 3, 3, 3, - 3, 3, 8, 4, 4, 8, 3, 3, - 8, 8, 8, 4, 3, 3, -}; -assert_compile(lengthof(_housetype_cargo_passengers) == HOUSE_MAX); +/* 4 variants * 4 build stages */ +assert_compile(lengthof(_town_draw_tile_data) == (NEW_HOUSE_OFFSET) * 4 * 4); -static const byte _housetype_cargo_mail[] = { - 3, 3, 3, 0, 4, 4, 1, 1, - 1, 0, 0, 0, 0, 2, 2, 2, - 2, 3, 3, 2, 0, 0, 0, 0, - 1, 1, 1, 2, 3, 2, 3, 2, - 0, 0, 0, 0, 3, 1, 1, 2, - 2, 2, 2, 2, 2, 2, 1, 1, - 1, 1, 3, 3, 3, 3, 3, 3, - 1, 1, 1, 1, 0, 0, 1, 1, - 2, 2, 1, 1, 1, 1, 2, 2, - 3, 3, 2, 2, 2, 2, 1, 1, - 1, 1, 2, 0, 1, 2, 2, 3, - 3, 2, 3, 0, 2, 1, 1, 1, - 1, 1, 4, 1, 1, 4, 1, 1, - 4, 4, 4, 1, 1, 2, -}; -assert_compile(lengthof(_housetype_cargo_mail) == HOUSE_MAX); - -static const byte _housetype_cargo_goods[] = { - 4, 4, 1, 0, 6, 6, 0, 2, - 2, 0, 0, 0, 0, 4, 3, 3, - 3, 6, 8, 6, 0, 0, 0, 0, - 0, 0, 0, 2, 3, 3, 3, 2, - 0, 0, 0, 0, 2, 0, 0, 2, - 3, 3, 3, 3, 0, 0, 0, 0, - 0, 0, 4, 4, 4, 4, 5, 5, - 0, 0, 0, 0, 0, 0, 0, 0, - 3, 3, 0, 2, 0, 2, 3, 3, - 4, 4, 2, 2, 2, 2, 0, 0, - 0, 0, 1, 0, 0, 1, 1, 4, - 4, 1, 4, 0, 2, 1, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 1, - 0, 0, 2, 2, 2, 0 -}; -assert_compile(lengthof(_housetype_cargo_goods) == HOUSE_MAX); - -static const byte _housetype_cargo_food[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 1, 1, - 0, 0, 3, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, - 2, 1, 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 4, 4, 0, 0, 0, 4 -}; -assert_compile(lengthof(_housetype_cargo_food) == HOUSE_MAX); - -static const byte _house_more_flags[] = { - 8, 8, 8, 8, 8, 8, 8, 12, - 0, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 15, 0, 0, 0, - 8, 8, 8, 8, 8, 8, 8, 8, - 15, 0, 0, 0, 8, 8, 8, 8, - 15, 0, 0, 0, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 12, 0, 12, 0, 8, 8, - 8, 8, 10, 0, 10, 0, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 10, - 0, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 12, 0, 8, 8, 8, - 8, 8, 8, 8, 8, 8, +/** Describes the data that defines each house in the game + * @param mnd introduction year of the house + * @param mxd last year it can be built + * @param p population + * @param rc cost multiplier for removing it + * @param bn building name + * @param rr rating decrease if removed + * @param mg mail generation multiplier + * @param pa passenger acceptance + * @param ma mail acceptance + * @param ga goods acceptance + * @param fa food acceptance + * @param bf building flags (size, stadium etc...) + * @param ba building availability (zone, climate...) + * @see HouseSpec + */ +#define M(mnd, mxd, p, rc, bn, rr, mg, pa, ma, ga, fa, bf, ba) \ + {mnd, mxd, p, rc, bn, rr, mg, pa, ma, ga, fa, bf, ba, true, \ + 0, NULL, 0, 0, {0, 0, 0, 0}, 16, NO_EXTRA_FLAG, HOUSE_NO_CLASS, 0, 2, 0, 0, NULL} +static const HouseSpec _original_house_specs[] = { + /** + * remove_rating_decrease + * | mail_generation + * min_date | | passenger_acceptance + * | max_date | | | mail_acceptance + * | | population | | | | goods_acceptance + * | | | removal_cost | | | | | food_acceptance + * | | | | building_name | | | | | | + * | | | | | | | | | | | + * | | | | | | | | | | | + * +-building_flags | | | | | | | | | + * +-building_availability | | | | | | | | + * | | | | | | | | | | | |*/ + M( 1963, MAX_YEAR, 187, 150, STR_200F_TALL_OFFICE_BLOCK, 140, 70, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5), + M( 1957, MAX_YEAR, 85, 140, STR_2010_OFFICE_BLOCK, 130, 55, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4), + M( 1968, MAX_YEAR, 40, 100, STR_2011_SMALL_BLOCK_OF_FLATS, 90, 20, 8, 3, 1, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 5, 90, STR_2012_CHURCH, 230, 2, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1975, MAX_YEAR, 220, 160, STR_2013_LARGE_OFFICE_BLOCK, 160, 85, 10, 4, 6, 0, + BUILDING_IS_ANIMATED | TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5), + M( 1975, MAX_YEAR, 220, 160, STR_2013_LARGE_OFFICE_BLOCK, 160, 85, 10, 4, 6, 0, + BUILDING_IS_ANIMATED | TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5), + M( 0, MAX_YEAR, 30, 80, STR_2014_TOWN_HOUSES, 80, 12, 4, 1, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1959, MAX_YEAR, 140, 180, STR_2015_HOTEL, 150, 22, 6, 1, 2, 0, + TILE_SIZE_1x2, + HZ_TEMP | HZ_ZON5 | HZ_ZON3), + M( 1959, MAX_YEAR, 0, 180, STR_2015_HOTEL, 150, 22, 6, 1, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1945, MAX_YEAR, 0, 65, STR_2016_STATUE, 40, 0, 2, 0, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 1945, MAX_YEAR, 0, 65, STR_2017_FOUNTAIN, 40, 0, 2, 0, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5), + M( 0, MAX_YEAR, 0, 60, STR_2018_PARK, 75, 0, 2, 0, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON3), + M( 1935, MAX_YEAR, 0, 60, STR_2018_PARK, 75, 0, 2, 0, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4), + M( 1951, MAX_YEAR, 150, 130, STR_2019_OFFICE_BLOCK, 110, 65, 8, 2, 4, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4), + M( 1930, 1960, 95, 110, STR_201A_SHOPS_AND_OFFICES, 100, 48, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1930, 1960, 95, 105, STR_201A_SHOPS_AND_OFFICES, 100, 48, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1930, 1960, 95, 107, STR_201A_SHOPS_AND_OFFICES, 100, 48, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1977, MAX_YEAR, 130, 200, STR_201B_MODERN_OFFICE_BUILDING, 150, 50, 10, 3, 6, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5), + M( 1983, MAX_YEAR, 6, 145, STR_201C_WAREHOUSE, 110, 10, 6, 3, 8, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5), + M( 1985, MAX_YEAR, 110, 155, STR_201D_OFFICE_BLOCK, 110, 55, 6, 2, 6, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5), + M( 0, MAX_YEAR, 65, 250, STR_201E_STADIUM, 300, 5, 4, 0, 0, 0, + BUILDING_IS_STADIUM | TILE_SIZE_2x2, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 0, 250, STR_201E_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 0, 250, STR_201E_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 0, 250, STR_201E_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, 1951, 15, 70, STR_201F_OLD_HOUSES, 75, 6, 3, 1, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON2 | HZ_ZON1), + M( 0, 1952, 12, 75, STR_2036_COTTAGES, 75, 7, 3, 1, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON1), + M( 1931, MAX_YEAR, 13, 71, STR_2037_HOUSES, 75, 8, 3, 1, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1935, MAX_YEAR, 100, 135, STR_2038_FLATS, 100, 35, 7, 2, 2, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1963, MAX_YEAR, 170, 145, STR_2039_TALL_OFFICE_BLOCK, 170, 50, 8, 3, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, 1955, 100, 132, STR_203A_SHOPS_AND_OFFICES, 135, 40, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1973, MAX_YEAR, 180, 155, STR_203B_SHOPS_AND_OFFICES, 180, 64, 8, 3, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON3), + M( 0, MAX_YEAR, 35, 220, STR_203C_THEATER, 230, 23, 8, 2, 2, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4), + M( 1958, MAX_YEAR, 65, 250, STR_203D_STADIUM, 300, 5, 4, 0, 0, 0, + BUILDING_IS_STADIUM | TILE_SIZE_2x2, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1958, MAX_YEAR, 0, 250, STR_203D_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1958, MAX_YEAR, 0, 250, STR_203D_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1958, MAX_YEAR, 0, 250, STR_203D_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 2000, MAX_YEAR, 140, 170, STR_203E_OFFICES, 250, 65, 8, 3, 2, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 0, 1960, 15, 70, STR_203F_HOUSES, 75, 6, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON2 | HZ_ZON1), + M( 0, 1960, 15, 70, STR_203F_HOUSES, 75, 6, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON2 | HZ_ZON1), + M( 1945, MAX_YEAR, 35, 210, STR_2040_CINEMA, 230, 23, 8, 2, 2, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1983, MAX_YEAR, 180, 250, STR_2041_SHOPPING_MALL, 300, 5, 8, 2, 3, 0, + TILE_SIZE_2x2, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 |HZ_ZON2), + M( 1983, MAX_YEAR, 0, 250, STR_2041_SHOPPING_MALL, 300, 5, 8, 2, 3, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1983, MAX_YEAR, 0, 250, STR_2041_SHOPPING_MALL, 300, 5, 8, 2, 3, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1983, MAX_YEAR, 0, 250, STR_2041_SHOPPING_MALL, 300, 5, 8, 2, 3, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 80, 100, STR_2038_FLATS, 90, 20, 5, 2, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 80, 100, STR_2038_FLATS, 90, 20, 5, 2, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 16, 70, STR_203F_HOUSES, 70, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 16, 70, STR_203F_HOUSES, 70, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, 1963, 14, 80, STR_203F_HOUSES, 70, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, 1963, 14, 80, STR_203F_HOUSES, 70, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1966, MAX_YEAR, 135, 150, STR_200F_TALL_OFFICE_BLOCK, 120, 60, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 1966, MAX_YEAR, 135, 150, STR_200F_TALL_OFFICE_BLOCK, 120, 60, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4), + M( 1970, MAX_YEAR, 170, 170, STR_200F_TALL_OFFICE_BLOCK, 130, 70, 9, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON5 | HZ_ZON4), + M( 1970, MAX_YEAR, 170, 170, STR_200F_TALL_OFFICE_BLOCK, 130, 70, 9, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4), + M( 1974, MAX_YEAR, 210, 200, STR_200F_TALL_OFFICE_BLOCK, 140, 80, 10, 3, 5, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 1974, MAX_YEAR, 210, 200, STR_200F_TALL_OFFICE_BLOCK, 140, 80, 10, 3, 5, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4), + M( 0, MAX_YEAR, 10, 60, STR_203F_HOUSES, 60, 5, 2, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON1), + M( 0, MAX_YEAR, 10, 60, STR_203F_HOUSES, 60, 5, 2, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON1), + M( 0, MAX_YEAR, 25, 100, STR_201A_SHOPS_AND_OFFICES, 80, 20, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 25, 100, STR_201A_SHOPS_AND_OFFICES, 80, 20, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 6, 85, STR_2012_CHURCH, 230, 2, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 6, 85, STR_2012_CHURCH, 230, 2, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 17, 80, STR_203F_HOUSES, 80, 7, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 17, 80, STR_203F_HOUSES, 80, 7, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, 1960, 90, 140, STR_201A_SHOPS_AND_OFFICES, 110, 45, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, 1960, 90, 140, STR_201A_SHOPS_AND_OFFICES, 110, 45, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1972, MAX_YEAR, 140, 160, STR_2015_HOTEL, 160, 25, 6, 1, 0, 3, + TILE_SIZE_1x2, + HZ_SUBARTC_BELOW| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1972, MAX_YEAR, 0, 160, STR_2015_HOTEL, 160, 25, 6, 1, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1972, MAX_YEAR, 140, 160, STR_2015_HOTEL, 160, 25, 6, 1, 0, 3, + TILE_SIZE_1x2, + HZ_SUBARTC_ABOVE| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1972, MAX_YEAR, 0, 160, STR_2015_HOTEL, 160, 25, 6, 1, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1963, MAX_YEAR, 105, 130, STR_201A_SHOPS_AND_OFFICES, 105, 50, 7, 2, 3, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1963, MAX_YEAR, 105, 130, STR_201A_SHOPS_AND_OFFICES, 105, 50, 7, 2, 3, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1978, MAX_YEAR, 190, 190, STR_200F_TALL_OFFICE_BLOCK, 135, 75, 9, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON5 | HZ_ZON4), + M( 1978, MAX_YEAR, 190, 190, STR_200F_TALL_OFFICE_BLOCK, 135, 75, 9, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4), + M( 1967, MAX_YEAR, 250, 140, STR_200F_TALL_OFFICE_BLOCK, 200, 60, 7, 2, 2, 0, + TILE_SIZE_2x1, + HZ_SUBARTC_BELOW| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1967, MAX_YEAR, 0, 140, STR_200F_TALL_OFFICE_BLOCK, 200, 60, 7, 2, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1967, MAX_YEAR, 250, 140, STR_200F_TALL_OFFICE_BLOCK, 200, 60, 7, 2, 2, 0, + TILE_SIZE_2x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1967, MAX_YEAR, 0, 140, STR_200F_TALL_OFFICE_BLOCK, 200, 60, 7, 2, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 16, 80, STR_203F_HOUSES, 80, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 16, 80, STR_203F_HOUSES, 80, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 16, 80, STR_203F_HOUSES, 80, 5, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 7, 30, STR_203F_HOUSES, 30, 4, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON1), + M( 0, MAX_YEAR, 45, 130, STR_2038_FLATS, 95, 15, 6, 2, 1, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 8, 90, STR_2012_CHURCH, 200, 3, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 18, 80, STR_203F_HOUSES, 80, 7, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 1973, MAX_YEAR, 90, 110, STR_2038_FLATS, 95, 24, 6, 2, 1, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1962, MAX_YEAR, 120, 120, STR_2038_FLATS, 95, 25, 6, 2, 1, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1984, MAX_YEAR, 250, 190, STR_200F_TALL_OFFICE_BLOCK, 140, 80, 8, 3, 4, 0, + TILE_SIZE_2x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 1984, MAX_YEAR, 0, 190, STR_200F_TALL_OFFICE_BLOCK, 140, 80, 8, 3, 4, 0, + TILE_NO_FLAG, + HZ_SUBTROPIC), + M( 0, MAX_YEAR, 80, 110, STR_2038_FLATS, 95, 23, 6, 2, 1, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1993, MAX_YEAR, 180, 180, STR_200F_TALL_OFFICE_BLOCK, 150, 90, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 8, 90, STR_2012_CHURCH, 200, 3, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 18, 90, STR_203F_HOUSES, 90, 5, 6, 2, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 7, 70, STR_203F_HOUSES, 50, 3, 3, 1, 1, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 15, 80, STR_203F_HOUSES, 75, 6, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 17, 80, STR_203F_HOUSES, 75, 6, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 19, 80, STR_203F_HOUSES, 75, 6, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 21, 80, STR_203F_HOUSES, 75, 6, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 75, 160, STR_200F_TALL_OFFICE_BLOCK, 130, 20, 8, 4, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 35, 90, STR_203F_HOUSES, 80, 9, 4, 1, 2, 0, + TILE_SIZE_1x2, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 0, 90, STR_203F_HOUSES, 80, 0, 4, 1, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 85, 150, STR_200F_TALL_OFFICE_BLOCK, 130, 18, 8, 4, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 11, 60, STR_2059_IGLOO, 45, 3, 3, 1, 1, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON1), + M( 0, MAX_YEAR, 10, 60, STR_205A_TEPEES, 45, 3, 3, 1, 1, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON1), + M( 0, MAX_YEAR, 67, 140, STR_201A_SHOPS_AND_OFFICES, 130, 22, 8, 4, 0, 4, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 86, 145, STR_201A_SHOPS_AND_OFFICES, 130, 23, 8, 4, 0, 4, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 95, 165, STR_200F_TALL_OFFICE_BLOCK, 130, 28, 8, 4, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 30, 90, STR_2016_STATUE, 70, 10, 4, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 25, 75, STR_205B_TEAPOT_HOUSE, 65, 8, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 18, 85, STR_205C_PIGGY_BANK, 95, 7, 3, 2, 0, 4, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), }; -assert_compile(lengthof(_house_more_flags) == HOUSE_MAX); +#undef M +assert_compile(lengthof(_original_house_specs) == NEW_HOUSE_OFFSET); diff --git a/src/town.h b/src/town.h index ad3b60ee5..3bc81f2b6 100644 --- a/src/town.h +++ b/src/town.h @@ -5,9 +5,72 @@ #include "oldpool.h" #include "player.h" +#include "functions.h" +#include "helpers.hpp" enum { - INVALID_TOWN = 0xFFFF, + HOUSE_NO_CLASS = 0, + NEW_HOUSE_OFFSET = 110, + HOUSE_MAX = 512, + INVALID_TOWN = 0xFFFF, + INVALID_HOUSE_ID = 0xFFFF, + + /* There can only be as many classes as there are new houses, plus one for + * NO_CLASS, as the original houses don't have classes. */ + HOUSE_CLASS_MAX = HOUSE_MAX - NEW_HOUSE_OFFSET + 1, +}; + +enum BuildingFlags { + TILE_NO_FLAG = 0, + TILE_SIZE_1x1 = 1U << 0, + TILE_NOT_SLOPED = 1U << 1, + TILE_SIZE_2x1 = 1U << 2, + TILE_SIZE_1x2 = 1U << 3, + TILE_SIZE_2x2 = 1U << 4, + BUILDING_IS_ANIMATED = 1U << 5, + BUILDING_IS_CHURCH = 1U << 6, + BUILDING_IS_STADIUM = 1U << 7, + BUILDING_HAS_1_TILE = TILE_SIZE_1x1 | TILE_SIZE_2x1 | TILE_SIZE_1x2 | TILE_SIZE_2x2, + BUILDING_2_TILES_X = TILE_SIZE_2x1 | TILE_SIZE_2x2, + BUILDING_2_TILES_Y = TILE_SIZE_1x2 | TILE_SIZE_2x2, + BUILDING_HAS_4_TILES = TILE_SIZE_2x2, +}; + +DECLARE_ENUM_AS_BIT_SET(BuildingFlags) + +enum HouseZones { ///< Bit Value Meaning + HZ_NOZNS = 0x0000, ///< 0 This is just to get rid of zeros, meaning none + HZ_ZON1 = 0x0001, ///< 0..4 1,2,4,8,10 which town zones the building can be built in, Zone1 been the further suburb + HZ_ZON2 = 0x0002, + HZ_ZON3 = 0x0004, + HZ_ZON4 = 0x0008, + HZ_ZON5 = 0x0010, ///< center of town + HZ_ZONALL = 0x001F, ///< 1F This is just to englobe all above types at once + HZ_SUBARTC_ABOVE = 0x0800, ///< 11 800 can appear in sub-arctic climate above the snow line + HZ_TEMP = 0x1000, ///< 12 1000 can appear in temperate climate + HZ_SUBARTC_BELOW = 0x2000, ///< 13 2000 can appear in sub-arctic climate below the snow line + HZ_SUBTROPIC = 0x4000, ///< 14 4000 can appear in subtropical climate + HZ_TOYLND = 0x8000 ///< 15 8000 can appear in toyland climate +}; + +DECLARE_ENUM_AS_BIT_SET(HouseZones) + +enum HouseExtraFlags { + NO_EXTRA_FLAG = 0, + BUILDING_IS_HISTORICAL = 1U << 0, ///< this house will only appear during town generation in random games, thus the historical + BUILDING_IS_PROTECTED = 1U << 1, ///< towns and AI will not remove this house, while human players will be able tp + SYNCHRONISED_CALLBACK_1B = 1U << 2, ///< synchronized callback 1B will be performed, on multi tile houses + CALLBACK_1A_RANDOM_BITS = 1U << 3, ///< callback 1A needs random bits +}; + +DECLARE_ENUM_AS_BIT_SET(HouseExtraFlags) + +typedef uint16 HouseID; +typedef uint16 HouseClassID; + +struct BuildingCounts { + uint8 id_count[HOUSE_MAX]; + uint8 class_count[HOUSE_CLASS_MAX]; }; struct Town { @@ -78,8 +141,48 @@ struct Town { // NOSAVE: UpdateTownRadius updates this given the house count. uint16 radius[5]; + + // NOSAVE: The number of each type of building in the town. + BuildingCounts building_counts; }; +struct HouseSpec { + /* Standard properties */ + Year min_date; ///< introduction year of the house + Year max_date; ///< last year it can be built + byte population; ///< population (Zero on other tiles in multi tile house.) + byte removal_cost; ///< cost multiplier for removing it + StringID building_name; ///< building name + uint16 remove_rating_decrease; ///< rating decrease if removed + byte mail_generation; ///< mail generation multiplier (tile based, as the acceptances below) + byte passenger_acceptance; ///< passenger acceptance, given in 1/8th unit, max is 8, as the 3 next properies + byte mail_acceptance; ///< mail acceptance + byte goods_acceptance; ///< good acceptance + byte food_acceptance; ///< food (or fizzy drink) acceptance + BuildingFlags building_flags; ///< some flags that describe the house (size, stadium etc...) + HouseZones building_availability; ///< where can it be built (climates, zones) + bool enabled; ///< the house is still avaible (by default, true.newgrf can disable it, though) + + /* NewHouses properties */ + HouseID substitute_id; ///< which house this one is based on + struct SpriteGroup *spritegroup; ///< pointer to the different sprites of the house + HouseID override; ///< which house this one replaces + uint16 callback_mask; ///< House callback flags + byte random_colour[4]; ///< 4 "random" colours + byte probability; ///< Relative probability of appearing (16 is the standard value) + HouseExtraFlags extra_flags; ///< some more flags + HouseClassID class_id; ///< defines the class this house has (grf file based) @See HouseGetVariable, prop 0x44 + byte animation_frames; ///< number of animation frames + byte animation_speed; ///< amount of time between each of those frames + byte processing_time; ///< Periodic refresh multiplier + + /* grf file related properties*/ + uint8 local_id; ///< id defined by the grf file for this house + const struct GRFFile *grffile; ///< grf file that introduced this house +}; + +VARDEF HouseSpec _house_specs[HOUSE_MAX]; + uint32 GetWorldPopulation(); void UpdateTownVirtCoord(Town *t); @@ -158,6 +261,12 @@ VARDEF const Town** _town_sort; DECLARE_OLD_POOL(Town, Town, 3, 8000) +static inline HouseSpec *GetHouseSpecs(HouseID house_id) +{ + assert(house_id < HOUSE_MAX); + return &_house_specs[house_id]; +} + /** * Check if a Town really exists. */ @@ -229,4 +338,9 @@ VARDEF byte _town_sort_order; VARDEF Town *_cleared_town; VARDEF int _cleared_town_rating; +uint OriginalTileRandomiser(uint x, uint y); +void ResetHouses(); + +void ClearTownHouse(Town *t, TileIndex tile); + #endif /* TOWN_H */ diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 14079e98b..bdc4c6d12 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -5,6 +5,7 @@ #include "stdafx.h" #include "openttd.h" #include "functions.h" +#include "debug.h" #include "strings.h" #include "road_map.h" #include "table/strings.h" @@ -19,6 +20,7 @@ #include "gfx.h" #include "industry.h" #include "station.h" +#include "vehicle.h" #include "player.h" #include "news.h" #include "saveload.h" @@ -32,6 +34,9 @@ #include "date.h" #include "table/town_land.h" #include "genworld.h" +#include "newgrf.h" +#include "newgrf_callbacks.h" +#include "newgrf_house.h" /** * Called if a new block is added to the town-pool @@ -91,7 +96,6 @@ void DestroyTown(Town *t) static int _grow_town_result; static bool BuildTownHouse(Town *t, TileIndex tile); -static void ClearTownHouse(Town *t, TileIndex tile); static void DoBuildTownHouse(Town *t, TileIndex tile); static void TownDrawHouseLift(const TileInfo *ti) @@ -104,25 +108,32 @@ static TownDrawTileProc * const _town_draw_tile_procs[1] = { TownDrawHouseLift }; +uint OriginalTileRandomiser(uint x, uint y) +{ + uint variant; + variant = x >> 4; + variant ^= x >> 6; + variant ^= y >> 4; + variant -= y >> 6; + variant &= 3; + return variant; +} static void DrawTile_Town(TileInfo *ti) { const DrawBuildingsTileStruct *dcts; SpriteID image; SpriteID pal; + HouseID house_id = GetHouseType(ti->tile); - /* Retrieve pointer to the draw town tile struct */ - { - /* this "randomizes" on the (up to) 4 variants of a building */ - uint variant; - variant = ti->x >> 4; - variant ^= ti->x >> 6; - variant ^= ti->y >> 4; - variant -= ti->y >> 6; - variant &= 3; - dcts = &_town_draw_tile_data[GetHouseType(ti->tile) << 4 | variant << 2 | GetHouseBuildingStage(ti->tile)]; + if (house_id >= NEW_HOUSE_OFFSET) { + DrawNewHouseTile(ti, house_id); + return; } + /* Retrieve pointer to the draw town tile struct */ + dcts = &_town_draw_tile_data[house_id << 4 | OriginalTileRandomiser(ti->x, ti->y) << 2 | GetHouseBuildingStage(ti->tile)]; + if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh); image = dcts->ground.sprite; @@ -172,18 +183,23 @@ static void AnimateTile_Town(TileIndex tile) { int pos, dest; + if (GetHouseType(tile) >= NEW_HOUSE_OFFSET) { + AnimateNewHouseTile(tile); + return; + } + if (_tick_counter & 3) return; // If the house is not one with a lift anymore, then stop this animating. // Not exactly sure when this happens, but probably when a house changes. // Before this was just a return...so it'd leak animated tiles.. // That bug seems to have been here since day 1?? - if (!(_housetype_extra_flags[GetHouseType(tile)] & 0x20)) { + if (!(GetHouseSpecs(GetHouseType(tile))->building_flags & BUILDING_IS_ANIMATED)) { DeleteAnimatedTile(tile); return; } - if (!IsLiftMoving(tile)) { + if (!LiftHasDestination(tile)) { int i; /** Building has 6 floors, number 0 .. 6, where 1 is illegal. @@ -270,44 +286,53 @@ static void MakeSingleHouseBigger(TileIndex tile) IncHouseConstructionTick(tile); if (GetHouseConstructionTick(tile) != 0) return; - IncHouseBuildingStage(tile); /*increase construction stage of one more step*/ + if (HASBIT(GetHouseSpecs(GetHouseType(tile))->callback_mask, CBM_CONSTRUCTION_STATE_CHANGE)) { + uint16 callback_res = GetHouseCallback(CBID_CONSTRUCTION_STATE_CHANGE, 0, GetHouseType(tile), GetTownByTile(tile), tile); + if (callback_res != CALLBACK_FAILED) ChangeHouseAnimationFrame(tile, callback_res); + } - if (GetHouseBuildingStage(tile) == TOWN_HOUSE_COMPLETED){ - /*Now, construction is completed. Can add population of building to the town*/ - ChangePopulation(GetTownByTile(tile), _housetype_population[GetHouseType(tile)]); + if (IsHouseCompleted(tile)) { + /* Now that construction is complete, we can add the population of the + * building to the town. */ + ChangePopulation(GetTownByTile(tile), GetHouseSpecs(GetHouseType(tile))->population); } MarkTileDirtyByTile(tile); } static void MakeTownHouseBigger(TileIndex tile) { - uint flags = _house_more_flags[GetHouseType(tile)]; - if (flags & 8) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 0)); - if (flags & 4) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 1)); - if (flags & 2) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 0)); - if (flags & 1) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 1)); + uint flags = GetHouseSpecs(GetHouseType(tile))->building_flags; + if (flags & BUILDING_HAS_1_TILE) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 0)); + if (flags & BUILDING_2_TILES_Y) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 1)); + if (flags & BUILDING_2_TILES_X) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 0)); + if (flags & BUILDING_HAS_4_TILES) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 1)); } static void TileLoop_Town(TileIndex tile) { - int house; Town *t; uint32 r; + HouseID house_id = GetHouseType(tile); + HouseSpec *hs = GetHouseSpecs(house_id); + + /* NewHouseTileLoop returns false if Callback 21 succeeded, i.e. the house + * doesn't exist any more, so don't continue here. */ + if (house_id >= NEW_HOUSE_OFFSET && !NewHouseTileLoop(tile)) return; - if (GetHouseBuildingStage(tile) != TOWN_HOUSE_COMPLETED) { + if (!IsHouseCompleted(tile)) { /*Construction is not completed. See if we can go further in construction*/ MakeTownHouseBigger(tile); return; } - house = GetHouseType(tile); - if ((_housetype_extra_flags[house] & 0x20) && !LiftHasDestination(tile) && CHANCE16(1, 2) && AddAnimatedTile(tile)) BeginLiftMovement(tile); + /* If the lift has a destination, it is already an animated tile. */ + if ((hs->building_flags & BUILDING_IS_ANIMATED) && house_id < NEW_HOUSE_OFFSET && !LiftHasDestination(tile) && CHANCE16(1, 2)) AddAnimatedTile(tile); t = GetTownByTile(tile); r = Random(); - if (GB(r, 0, 8) < _housetype_population[house]) { + if (GB(r, 0, 8) < hs->population) { uint amt = GB(r, 0, 8) / 8 + 1; uint moved; @@ -317,7 +342,7 @@ static void TileLoop_Town(TileIndex tile) t->new_act_pass += moved; } - if (GB(r, 8, 8) < _housetype_mailamount[house] ) { + if (GB(r, 8, 8) < hs->mail_generation) { uint amt = GB(r, 8, 8) / 8 + 1; uint moved; @@ -327,18 +352,18 @@ static void TileLoop_Town(TileIndex tile) t->new_act_mail += moved; } - if (_house_more_flags[house] & 8 && HASBIT(t->flags12, TOWN_IS_FUNDED) && --t->time_until_rebuild == 0) { - t->time_until_rebuild = GB(r, 16, 6) + 130; + _current_player = OWNER_TOWN; - _current_player = OWNER_TOWN; + if (hs->building_flags & BUILDING_HAS_1_TILE && HASBIT(t->flags12, TOWN_IS_FUNDED) && CanDeleteHouse(tile) && --t->time_until_rebuild == 0) { + t->time_until_rebuild = GB(r, 16, 6) + 130; ClearTownHouse(t, tile); // rebuild with another house? if (GB(r, 24, 8) >= 12) DoBuildTownHouse(t, tile); - - _current_player = OWNER_NONE; } + + _current_player = OWNER_NONE; } static void ClickTile_Town(TileIndex tile) @@ -348,16 +373,17 @@ static void ClickTile_Town(TileIndex tile) static int32 ClearTile_Town(TileIndex tile, byte flags) { - int house, rating; + int rating; int32 cost; Town *t; + HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); if (flags&DC_AUTO && !(flags&DC_AI_BUILDING)) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED); + if (!CanDeleteHouse(tile)) return CMD_ERROR; - house = GetHouseType(tile); - cost = _price.remove_house * _housetype_remove_cost[house] >> 8; + cost = _price.remove_house * hs->removal_cost >> 8; - rating = _housetype_remove_ratingmod[house]; + rating = hs->remove_rating_decrease; _cleared_town_rating += rating; _cleared_town = t = GetTownByTile(tile); @@ -378,18 +404,18 @@ static int32 ClearTile_Town(TileIndex tile, byte flags) static void GetAcceptedCargo_Town(TileIndex tile, AcceptedCargo ac) { - byte type = GetHouseType(tile); + HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); - ac[CT_PASSENGERS] = _housetype_cargo_passengers[type]; - ac[CT_MAIL] = _housetype_cargo_mail[type]; - ac[CT_GOODS] = _housetype_cargo_goods[type]; - ac[CT_FOOD] = _housetype_cargo_food[type]; + ac[CT_PASSENGERS] = hs->passenger_acceptance; + ac[CT_MAIL] = hs->mail_acceptance; + ac[CT_GOODS] = hs->goods_acceptance; + ac[CT_FOOD] = hs->food_acceptance; } static void GetTileDesc_Town(TileIndex tile, TileDesc *td) { - td->str = _town_tile_names[GetHouseType(tile)]; - if (GetHouseBuildingStage(tile) != TOWN_HOUSE_COMPLETED) { + td->str = GetHouseSpecs(GetHouseType(tile))->building_name; + if (!IsHouseCompleted(tile)) { SetDParamX(td->dparam, 0, td->str); td->str = STR_2058_UNDER_CONSTRUCTION; } @@ -1186,10 +1212,11 @@ static void DoBuildTownHouse(Town *t, TileIndex tile) { int i; uint bitmask; - int house; + HouseID house; Slope slope; uint z; uint oneof = 0; + HouseSpec *hs; // Above snow? slope = GetTileSlope(tile, &z); @@ -1208,45 +1235,63 @@ static void DoBuildTownHouse(Town *t, TileIndex tile) // bits 11-15 are used // bits 5-10 are not used. { - byte houses[lengthof(_housetype_flags)]; + HouseID houses[HOUSE_MAX]; int num = 0; + uint cumulative_probs[HOUSE_MAX]; + uint probability_max = 0; // Generate a list of all possible houses that can be built. - for (i=0; i!=lengthof(_housetype_flags); i++) { - if ((~_housetype_flags[i] & bitmask) == 0) - houses[num++] = (byte)i; + for (i = 0; i < HOUSE_MAX; i++) { + hs = GetHouseSpecs(i); + if ((~hs->building_availability & bitmask) == 0 && hs->enabled) { + if (_have_newhouses) { + probability_max += hs->probability; + cumulative_probs[num] = probability_max; + } + houses[num++] = (HouseID)i; + } } for (;;) { - house = houses[RandomRange(num)]; + if (_have_newhouses) { + uint r = RandomRange(probability_max); + for (i = 0; i < num; i++) if (cumulative_probs[i] >= r) break; + + house = houses[i]; + } else { + house = houses[RandomRange(num)]; + } + + hs = GetHouseSpecs(house); + + if (_have_newhouses) { + if (hs->override != 0) hs = GetHouseSpecs(hs->override); - if (_cur_year < _housetype_years[house].min || _cur_year > _housetype_years[house].max) - continue; + if ((hs->extra_flags & BUILDING_IS_HISTORICAL) && !_generating_world) continue; + + if (HASBIT(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, house, t, tile); + if (callback_res != CALLBACK_FAILED && callback_res == 0) continue; + } + } + + if (_cur_year < hs->min_date || _cur_year > hs->max_date) continue; // Special houses that there can be only one of. - switch (house) { - case HOUSE_TEMP_CHURCH: - case HOUSE_ARCT_CHURCH: - case HOUSE_SNOW_CHURCH: - case HOUSE_TROP_CHURCH: - case HOUSE_TOY_CHURCH: - SETBIT(oneof, TOWN_HAS_CHURCH); - break; - case HOUSE_STADIUM: - case HOUSE_MODERN_STADIUM: - SETBIT(oneof, TOWN_HAS_STADIUM); - break; - default: - oneof = 0; - break; + if (hs->building_flags & BUILDING_IS_CHURCH) { + SETBIT(oneof, TOWN_HAS_CHURCH); + } else if (hs->building_flags & BUILDING_IS_STADIUM) { + SETBIT(oneof, TOWN_HAS_STADIUM); + } else { + oneof = 0; } if (HASBITS(t->flags12 , oneof)) continue; // Make sure there is no slope? - if (_housetype_extra_flags[house] & 0x12 && slope != SLOPE_FLAT) continue; + if (hs->building_flags & TILE_NOT_SLOPED && slope != SLOPE_FLAT) continue; - if (_housetype_extra_flags[house] & 0x10) { + if (hs->building_flags & TILE_SIZE_2x2) { if (CheckFree2x2Area(tile) || CheckFree2x2Area(tile += TileDiffXY(-1, 0)) || CheckFree2x2Area(tile += TileDiffXY( 0, -1)) || @@ -1254,14 +1299,14 @@ static void DoBuildTownHouse(Town *t, TileIndex tile) break; } tile += TileDiffXY(0, 1); - } else if (_housetype_extra_flags[house] & 4) { + } else if (hs->building_flags & TILE_SIZE_2x1) { if (CheckBuildHouseMode(tile + TileDiffXY(1, 0), slope, 0)) break; if (CheckBuildHouseMode(tile + TileDiffXY(-1, 0), slope, 1)) { tile += TileDiffXY(-1, 0); break; } - } else if (_housetype_extra_flags[house] & 8) { + } else if (hs->building_flags & TILE_SIZE_1x2) { if (CheckBuildHouseMode(tile + TileDiffXY(0, 1), slope, 2)) break; if (CheckBuildHouseMode(tile + TileDiffXY(0, -1), slope, 3)) { @@ -1275,12 +1320,13 @@ static void DoBuildTownHouse(Town *t, TileIndex tile) } t->num_houses++; + IncreaseBuildingCount(t, house); // Special houses that there can be only one of. t->flags12 |= oneof; { - byte construction_counter = 0, construction_stage = 0, size_flags; + byte construction_counter = 0, construction_stage = 0; if (_generating_world) { uint32 r = Random(); @@ -1289,13 +1335,12 @@ static void DoBuildTownHouse(Town *t, TileIndex tile) if (CHANCE16(1, 7)) construction_stage = GB(r, 0, 2); if (construction_stage == TOWN_HOUSE_COMPLETED) { - ChangePopulation(t, _housetype_population[house]); + ChangePopulation(t, hs->population); } else { construction_counter = GB(r, 2, 2); } } - size_flags = GB(_housetype_extra_flags[house], 2, 3); - MakeTownHouse(tile, t->index, construction_counter, construction_stage, size_flags, house); + MakeTownHouse(tile, t->index, construction_counter, construction_stage, house, VehicleRandomBits()); } } @@ -1321,60 +1366,54 @@ static void DoClearTownHouseHelper(TileIndex tile) DeleteAnimatedTile(tile); } -static void ClearTownHouse(Town *t, TileIndex tile) +void ClearTownHouse(Town *t, TileIndex tile) { - uint house = GetHouseType(tile); + HouseID house = GetHouseType(tile); uint eflags; + HouseSpec *hs; assert(IsTileType(tile, MP_HOUSE)); // need to align the tile to point to the upper left corner of the house if (house >= 3) { // house id 0,1,2 MUST be single tile houses, or this code breaks. - if (_housetype_extra_flags[house-1] & 0x04) { + if (GetHouseSpecs(house-1)->building_flags & TILE_SIZE_2x1) { house--; tile += TileDiffXY(-1, 0); - } else if (_housetype_extra_flags[house-1] & 0x18) { + } else if (GetHouseSpecs(house-1)->building_flags & BUILDING_2_TILES_Y) { house--; tile += TileDiffXY(0, -1); - } else if (_housetype_extra_flags[house-2] & 0x10) { + } else if (GetHouseSpecs(house-2)->building_flags & BUILDING_HAS_4_TILES) { house-=2; tile += TileDiffXY(-1, 0); - } else if (_housetype_extra_flags[house-3] & 0x10) { + } else if (GetHouseSpecs(house-3)->building_flags & BUILDING_HAS_4_TILES) { house-=3; tile += TileDiffXY(-1, -1); } } + hs = GetHouseSpecs(house); + // Remove population from the town if the house is finished. - if (GetHouseBuildingStage(tile) == TOWN_HOUSE_COMPLETED) { - ChangePopulation(t, -_housetype_population[house]); + if (IsHouseCompleted(tile)) { + ChangePopulation(t, -hs->population); } t->num_houses--; + DecreaseBuildingCount(t, house); // Clear flags for houses that only may exist once/town. - switch (house) { - case HOUSE_TEMP_CHURCH: - case HOUSE_ARCT_CHURCH: - case HOUSE_SNOW_CHURCH: - case HOUSE_TROP_CHURCH: - case HOUSE_TOY_CHURCH: - CLRBIT(t->flags12, TOWN_HAS_CHURCH); - break; - case HOUSE_STADIUM: - case HOUSE_MODERN_STADIUM: - CLRBIT(t->flags12, TOWN_HAS_STADIUM); - break; - default: - break; + if (hs->building_flags & BUILDING_IS_CHURCH) { + CLRBIT(t->flags12, TOWN_HAS_CHURCH); + } else if (hs->building_flags & BUILDING_IS_STADIUM) { + CLRBIT(t->flags12, TOWN_HAS_STADIUM); } // Do the actual clearing of tiles - eflags = _housetype_extra_flags[house]; + eflags = hs->building_flags; DoClearTownHouseHelper(tile); - if (eflags & 0x14) DoClearTownHouseHelper(tile + TileDiffXY(1, 0)); - if (eflags & 0x18) DoClearTownHouseHelper(tile + TileDiffXY(0, 1)); - if (eflags & 0x10) DoClearTownHouseHelper(tile + TileDiffXY(1, 1)); + if (eflags & BUILDING_2_TILES_X) DoClearTownHouseHelper(tile + TileDiffXY(1, 0)); + if (eflags & BUILDING_2_TILES_Y) DoClearTownHouseHelper(tile + TileDiffXY(0, 1)); + if (eflags & BUILDING_HAS_4_TILES) DoClearTownHouseHelper(tile + TileDiffXY(1, 1)); } /** Rename a town (server-only). @@ -1922,6 +1961,37 @@ static const SaveLoad _town_desc[] = { SLE_END() }; +/* Save and load the mapping between the house id on the map, and the grf file + * it came from. */ +static const SaveLoad _house_id_mapping_desc[] = { + SLE_VAR(HouseIDMapping, grfid, SLE_UINT32), + SLE_VAR(HouseIDMapping, house_id, SLE_UINT8), + SLE_VAR(HouseIDMapping, substitute_id, SLE_UINT8), + SLE_END() +}; + +static void Save_HOUSEIDS() +{ + uint i; + + for (i = 0; i != lengthof(_house_id_mapping); i++) { + SlSetArrayIndex(i); + SlObject(&_house_id_mapping[i], _house_id_mapping_desc); + } +} + +static void Load_HOUSEIDS() +{ + int index; + + ResetHouseIDMapping(); + + while ((index = SlIterateArray()) != -1) { + if ((uint)index >= lengthof(_house_id_mapping)) break; + SlObject(&_house_id_mapping[index], _house_id_mapping_desc); + } +} + static void Save_TOWN() { Town *t; @@ -1966,7 +2036,13 @@ void AfterLoadTown() _town_sort_dirty = true; } - extern const ChunkHandler _town_chunk_handlers[] = { - { 'CITY', Save_TOWN, Load_TOWN, CH_ARRAY | CH_LAST}, + { 'HIDS', Save_HOUSEIDS, Load_HOUSEIDS, CH_ARRAY }, + { 'CITY', Save_TOWN, Load_TOWN, CH_ARRAY | CH_LAST}, }; + +void ResetHouses() +{ + memset(&_house_specs, 0, sizeof(_house_specs)); + memcpy(&_house_specs, &_original_house_specs, sizeof(_original_house_specs)); +} diff --git a/src/town_map.h b/src/town_map.h index fe4bee33e..cf20e9c9a 100644 --- a/src/town_map.h +++ b/src/town_map.h @@ -6,7 +6,14 @@ #define TOWN_MAP_H #include "town.h" +#include "date.h" +/** + * Get the index of which town this house/street is attached to. + * @param t the tile + * @pre IsTileType(t, MP_HOUSE) or IsTileType(t, MP_STREET) + * @return TownID + */ static inline TownID GetTownIndex(TileIndex t) { assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_STREET)); // XXX incomplete @@ -15,13 +22,14 @@ static inline TownID GetTownIndex(TileIndex t) /** * Set the town index for a road or house tile. - * @param tile the tile + * @param t the tile + * @pre IsTileType(t, MP_HOUSE) or IsTileType(t, MP_STREET) * @param index the index of the town * @pre IsTileType(t, MP_STREET) || IsTileType(t, MP_HOUSE) */ static inline void SetTownIndex(TileIndex t, TownID index) { - assert(IsTileType(t, MP_STREET) || IsTileType(t, MP_HOUSE)); + assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_STREET)); _m[t].m2 = index; } @@ -35,84 +43,189 @@ static inline Town* GetTownByTile(TileIndex t) return GetTown(GetTownIndex(t)); } +/** + * Get the type of this house, which is an index into the house spec array + * Since m4 is only a byte and we want to support 512 houses, we use the bit 6 + * of m3 as an additional bit to house type. + * @param t the tile + * @pre IsTileType(t, MP_HOUSE) + * @return house type + */ +static inline HouseID GetHouseType(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + return _m[t].m4 | (GB(_m[t].m3, 6, 1) << 8); +} -static inline int GetHouseType(TileIndex t) +/** + * Set the house type. + * @param t the tile + * @param house_id the new house type + * @pre IsTileType(t, MP_HOUSE) + */ +static inline void SetHouseType(TileIndex t, HouseID house_id) { assert(IsTileType(t, MP_HOUSE)); - return _m[t].m4; + _m[t].m4 = GB(house_id, 0, 8); + SB(_m[t].m3, 6, 1, GB(house_id, 8, 1)); } +/** + * Check if the lift of this animated house has a destination + * @param t the tile + * @return has destination + */ static inline bool LiftHasDestination(TileIndex t) { - return HASBIT(_m[t].m5, 7); + return HASBIT(_me[t].m7, 0); } +/** + * Set the new destination of the lift for this animated house, and activate + * the LiftHasDestination bit. + * @param t the tile + * @param dest new destination + */ static inline void SetLiftDestination(TileIndex t, byte dest) { - SB(_m[t].m5, 0, 6, dest); - SETBIT(_m[t].m1, 7); /* Start moving */ + SETBIT(_me[t].m7, 0); + SB(_me[t].m7, 1, 3, dest); } +/** + * Get the current destination for this lift + * @param t the tile + * @return destination + */ static inline byte GetLiftDestination(TileIndex t) { - return GB(_m[t].m5, 0, 6); + return GB(_me[t].m7, 1, 3); } -static inline bool IsLiftMoving(TileIndex t) +/** + * Stop the lift of this animated house from moving. + * Clears the first 4 bits of m7 at once, clearing the LiftHasDestination bit + * and the destination. + * @param t the tile + */ +static inline void HaltLift(TileIndex t) { - return HASBIT(_m[t].m1, 7); + SB(_me[t].m7, 0, 4, 0); + DeleteAnimatedTile(t); } -static inline void BeginLiftMovement(TileIndex t) +/** + * Get the position of the lift on this animated house + * @param t the tile + * @return position, from 0 to 36 + */ +static inline byte GetLiftPosition(TileIndex t) { - SETBIT(_m[t].m5, 7); + return GB(_m[t].m6, 2, 6); } -static inline void HaltLift(TileIndex t) +/** + * Set the position of the lift on this animated house + * @param t the tile + * @param pos, from 0 to 36 + */ +static inline void SetLiftPosition(TileIndex t, byte pos) { - CLRBIT(_m[t].m1, 7); - CLRBIT(_m[t].m5, 7); - SB(_m[t].m5, 0, 6, 0); + SB(_m[t].m6, 2, 6, pos); +} - DeleteAnimatedTile(t); +/** + * Get the current animation frame for this house + * @param t the tile + * @pre IsTileType(t, MP_HOUSE) + * @return frame number + */ +static inline byte GetHouseAnimationFrame(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + return GB(_m[t].m6, 3, 5); } -static inline byte GetLiftPosition(TileIndex t) +/** + * Set a new animation frame for this house + * @param t the tile + * @param frame the new frame number + * @pre IsTileType(t, MP_HOUSE) + */ +static inline void SetHouseAnimationFrame(TileIndex t, byte frame) { - return GB(_m[t].m1, 0, 7); + assert(IsTileType(t, MP_HOUSE)); + SB(_m[t].m6, 3, 5, frame); } -static inline void SetLiftPosition(TileIndex t, byte pos) +/** + * Get the completion of this house + * @param t the tile + * @return true if it is, false if it is not + */ +static inline bool IsHouseCompleted(TileIndex t) { - SB(_m[t].m1, 0, 7, pos); + assert(IsTileType(t, MP_HOUSE)); + return HASBIT(_m[t].m3, 7); } -static inline void MakeHouseTile(TileIndex t, TownID tid, byte counter, byte stage, byte type) +/** + * Mark this house as been completed + * @param t the tile + * @param status + */ +static inline void SetHouseCompleted(TileIndex t, bool status) +{ + assert(IsTileType(t, MP_HOUSE)); + SB(_m[t].m3, 7, 1, !!status); +} + +/** + * Make the tile a house. + * @param t tile index + * @param tid Town index + * @param counter of construction step + * @param stage of construction (used for drawing) + * @param type of house. Index into house specs array + * @param random_bits required for newgrf houses + * @pre IsTileType(t, MP_CLEAR) + */ +static inline void MakeHouseTile(TileIndex t, TownID tid, byte counter, byte stage, HouseID type, byte random_bits) { assert(IsTileType(t, MP_CLEAR)); SetTileType(t, MP_HOUSE); - _m[t].m1 = 0; + _m[t].m1 = random_bits; _m[t].m2 = tid; - SB(_m[t].m3, 6, 2, stage); - _m[t].m4 = type; - SB(_m[t].m5, 0, 2, counter); + _m[t].m3 = 0; + SetHouseType(t, type); + SetHouseCompleted(t, stage == TOWN_HOUSE_COMPLETED); + _m[t].m5 = IsHouseCompleted(t) ? 0 : (stage << 3 | counter); + SetHouseAnimationFrame(t, 0); + _me[t].m7 = GetHouseSpecs(type)->processing_time; + if (GetHouseSpecs(type)->building_flags & BUILDING_IS_ANIMATED) AddAnimatedTile(t); MarkTileDirtyByTile(t); } -enum { - TWO_BY_TWO_BIT = 2, ///< House is two tiles in X and Y directions - ONE_BY_TWO_BIT = 1, ///< House is two tiles in Y direction - TWO_BY_ONE_BIT = 0, ///< House is two tiles in X direction -}; - -static inline void MakeTownHouse(TileIndex t, TownID tid, byte counter, byte stage, byte size, byte type) +/** + * Helper function for MakeHouseTile. + * It is called for each tile of a multi-tile house. + * Parametes are the same. + * @param t tile index + * @param tid Town index + * @param counter of construction step + * @param stage of construction (used for drawing) + * @param type of house. Index into house specs array + * @param random_bits required for newgrf houses + */ +static inline void MakeTownHouse(TileIndex t, TownID tid, byte counter, byte stage, HouseID type, byte random_bits) { - MakeHouseTile(t, tid, counter, stage, type); - if (HASBIT(size, TWO_BY_TWO_BIT) || HASBIT(size, ONE_BY_TWO_BIT)) MakeHouseTile(t + TileDiffXY(0, 1), tid, counter, stage, ++type); - if (HASBIT(size, TWO_BY_TWO_BIT) || HASBIT(size, TWO_BY_ONE_BIT)) MakeHouseTile(t + TileDiffXY(1, 0), tid, counter, stage, ++type); - if (HASBIT(size, TWO_BY_TWO_BIT)) MakeHouseTile(t + TileDiffXY(1, 1), tid, counter, stage, ++type); + BuildingFlags size = GetHouseSpecs(type)->building_flags; + MakeHouseTile(t, tid, counter, stage, type, random_bits); + if (size & BUILDING_2_TILES_Y) MakeHouseTile(t + TileDiffXY(0, 1), tid, counter, stage, ++type, random_bits); + if (size & BUILDING_2_TILES_X) MakeHouseTile(t + TileDiffXY(1, 0), tid, counter, stage, ++type, random_bits); + if (size & BUILDING_HAS_4_TILES) MakeHouseTile(t + TileDiffXY(1, 1), tid, counter, stage, ++type, random_bits); } /** @@ -120,81 +233,143 @@ static inline void MakeTownHouse(TileIndex t, TownID tid, byte counter, byte sta * Construction counter, for buildings under construction. Incremented on every * periodic tile processing. * On wraparound, the stage of building in is increased. - * (Get|Set|Inc)HouseBuildingStage are taking care of the real stages, + * GetHouseBuildingStage is taking care of the real stages, * (as the sprite for the next phase of house building) - * (Get|Set|Inc)HouseConstructionTick is simply a tick counter between the + * (Get|Inc)HouseConstructionTick is simply a tick counter between the * different stages */ /** * Gets the building stage of a house - * @param tile the tile of the house to get the building stage of + * Since the stage is used for determining what sprite to use, + * if the house is complete (and that stage no longuer is available), + * fool the system by returning the TOWN_HOUSE_COMPLETE (3), + * thus showing a beautiful complete house. + * @param t the tile of the house to get the building stage of * @pre IsTileType(t, MP_HOUSE) * @return the building stage of the house */ static inline byte GetHouseBuildingStage(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return GB(_m[t].m3, 6, 2); + return IsHouseCompleted(t) ? (byte)TOWN_HOUSE_COMPLETED : GB(_m[t].m5, 3, 2); } /** - * Sets the building stage of a house - * @param tile the tile of the house to set the building stage of - * @param stage the new stage + * Gets the construction stage of a house + * @param t the tile of the house to get the construction stage of * @pre IsTileType(t, MP_HOUSE) + * @return the construction stage of the house */ -static inline void SetHouseBuildingStage(TileIndex t, byte stage) +static inline byte GetHouseConstructionTick(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - SB(_m[t].m3, 6, 2, stage); + return IsHouseCompleted(t) ? 0 : GB(_m[t].m5, 0, 3); } /** - * Increments the building stage of a house - * @param tile the tile of the house to increment the building stage of + * Sets the increment stage of a house + * It is working with the whole counter + stage 5 bits, making it + * easier to work: the wraparound is automatic. + * @param t the tile of the house to increment the construction stage of * @pre IsTileType(t, MP_HOUSE) */ -static inline void IncHouseBuildingStage( TileIndex t ) +static inline void IncHouseConstructionTick(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - AB(_m[t].m3, 6, 2, 1); + AB(_m[t].m5, 0, 5, 1); + + if (GB(_m[t].m5, 3, 2) == TOWN_HOUSE_COMPLETED) { + /* House is now completed. + * Store the year of construction as well, for newgrf house purpose */ + SetHouseCompleted(t, true); + _m[t].m5 = clamp(_cur_year - ORIGINAL_BASE_YEAR, 0, 0xFF); + } } /** - * Gets the construction stage of a house - * @param tile the tile of the house to get the construction stage of + * Get the year that this house was constructed (between 1920 and 2175). + * @param t the tile of this house * @pre IsTileType(t, MP_HOUSE) - * @return the construction stage of the house + * @return year */ -static inline byte GetHouseConstructionTick(TileIndex t) +static inline Year GetHouseConstructionYear(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return GB(_m[t].m5, 0, 3); + return IsHouseCompleted(t) ? _m[t].m5 + ORIGINAL_BASE_YEAR : 0; } /** - * Sets the construction stage of a house - * @param tile the tile of the house to set the construction stage of - * @param stage the new stage + * Get the random bits for this house. + * This is required for newgrf house + * @param t the tile of this house * @pre IsTileType(t, MP_HOUSE) + * @return random bits */ -static inline void SetHouseConstructionTick(TileIndex t, byte stage) +static inline byte GetHouseRandomBits(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - SB(_m[t].m5, 0, 3, stage); + return _m[t].m1; } /** - * Sets the increment stage of a house - * @param tile the tile of the house to increment the construction stage of + * Set the activated triggers bits for this house. + * This is required for newgrf house + * @param t the tile of this house * @pre IsTileType(t, MP_HOUSE) */ -static inline void IncHouseConstructionTick(TileIndex t) +static inline void SetHouseTriggers(TileIndex t, byte triggers) +{ + assert(IsTileType(t, MP_HOUSE)); + SB(_m[t].m3, 0, 5, triggers); +} + +/** + * Get the already activated triggers bits for this house. + * This is required for newgrf house + * @param t the tile of this house + * @pre IsTileType(t, MP_HOUSE) + * @return triggers + */ +static inline byte GetHouseTriggers(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + return GB(_m[t].m3, 0, 5); +} + +/** + * Get the amount of time remaining before the tile loop processes this tile. + * @param t the house tile + * @pre IsTileType(t, MP_HOUSE) + * @return time remaining + */ +static inline byte GetHouseProcessingTime(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - AB(_m[t].m5, 0, 3, 1); + return _me[t].m7; } +/** + * Set the amount of time remaining before the tile loop processes this tile. + * @param t the house tile + * @param time the time to be set + * @pre IsTileType(t, MP_HOUSE) + */ +static inline void SetHouseProcessingTime(TileIndex t, byte time) +{ + assert(IsTileType(t, MP_HOUSE)); + _me[t].m7 = time; +} + +/** + * Decrease the amount of time remaining before the tile loop processes this tile. + * @param t the house tile + * @pre IsTileType(t, MP_HOUSE) + */ +static inline void DecHouseProcessingTime(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + _me[t].m7--; +} #endif /* TOWN_MAP_H */ diff --git a/src/void_map.h b/src/void_map.h index 21658746e..06c2570f9 100644 --- a/src/void_map.h +++ b/src/void_map.h @@ -13,6 +13,7 @@ static inline void MakeVoid(TileIndex t) _m[t].m4 = 0; _m[t].m5 = 0; _m[t].m6 = 0; + _me[t].m7 = 0; } #endif /* VOID_MAP_H */ |