diff options
author | maedhros <maedhros@openttd.org> | 2007-03-19 11:27:30 +0000 |
---|---|---|
committer | maedhros <maedhros@openttd.org> | 2007-03-19 11:27:30 +0000 |
commit | 73ff939ddbb4772178de68f821d9c32ded846a06 (patch) | |
tree | 8ccaccab74c6399452d6ff3f09d5c94335516a62 | |
parent | ae48a7447be849bdad48ca5996a246d29165f39e (diff) | |
download | openttd-73ff939ddbb4772178de68f821d9c32ded846a06.tar.xz |
(svn r9315) -Merge: The newhouses branch. With this merge comes almost complete support for
the newhouses grf specs, so all newhouses grfs will be playable in the game.
Many thanks to everyone who contributed code and ideas, and all the testers
who found things we missed.
-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 */ |