(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.replace/41b28d7194a279bdc17475d4fbe2ea6ec885a466
parent
ae48a7447b
commit
73ff939ddb
@ -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>
|
@ -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;
|
||||
}
|
@ -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 */
|
@ -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;
|
||||
}
|
@ -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 */
|
Loading…
Reference in New Issue