1 #include "stdafx.h"
2 #include "furniture_factory.h"
3 #include "furniture.h"
4 #include "furniture_type.h"
5 #include "view_id.h"
6 #include "view_layer.h"
7 #include "view_object.h"
8 #include "tribe.h"
9 #include "item_type.h"
10 #include "furniture_usage.h"
11 #include "furniture_click.h"
12 #include "furniture_tick.h"
13 #include "furniture_entry.h"
14 #include "furniture_dropped_items.h"
15 #include "level.h"
16 #include "creature.h"
17 #include "creature_factory.h"
18 #include "movement_set.h"
19 #include "lasting_effect.h"
20 
get(FurnitureType type,TribeId tribe)21 static Furniture get(FurnitureType type, TribeId tribe) {
22   switch (type) {
23     case FurnitureType::TRAINING_WOOD:
24       return Furniture("wooden training dummy", ViewObject(ViewId::TRAINING_WOOD, ViewLayer::FLOOR), type, tribe)
25           .setBlocking()
26           .setUsageType(FurnitureUsageType::TRAIN)
27           .setFireInfo(Fire(500, 0.5))
28           .setCanHide()
29           .setDestroyable(80);
30     case FurnitureType::TRAINING_IRON:
31       return Furniture("iron training dummy", ViewObject(ViewId::TRAINING_IRON, ViewLayer::FLOOR), type, tribe)
32           .setBlocking()
33           .setUsageType(FurnitureUsageType::TRAIN)
34           .setCanHide()
35           .setDestroyable(80);
36     case FurnitureType::TRAINING_STEEL:
37       return Furniture("steel training dummy", ViewObject(ViewId::TRAINING_STEEL, ViewLayer::FLOOR), type, tribe)
38           .setBlocking()
39           .setUsageType(FurnitureUsageType::TRAIN)
40           .setCanHide()
41           .setDestroyable(80);
42     case FurnitureType::ARCHERY_RANGE:
43       return Furniture("archery target", ViewObject(ViewId::ARCHERY_RANGE, ViewLayer::FLOOR), type, tribe)
44           .setBlocking()
45           .setStopProjectiles()
46           .setUsageType(FurnitureUsageType::ARCHERY_RANGE)
47           .setCanHide()
48           .setDestroyable(80);
49     case FurnitureType::WORKSHOP:
50       return Furniture("workshop", ViewObject(ViewId::WORKSHOP, ViewLayer::FLOOR), type, tribe)
51           .setBlocking()
52           .setUsageTime(5)
53           .setCanHide()
54           .setFireInfo(Fire(500, 0.5))
55           .setDestroyable(80);
56     case FurnitureType::FORGE:
57       return Furniture("forge", ViewObject(ViewId::FORGE, ViewLayer::FLOOR), type, tribe)
58           .setBlocking()
59           .setUsageTime(5)
60           .setCanHide()
61           .setDestroyable(80);
62     case FurnitureType::LABORATORY:
63       return Furniture("laboratory", ViewObject(ViewId::LABORATORY, ViewLayer::FLOOR), type, tribe)
64           .setBlocking()
65           .setUsageTime(5)
66           .setCanHide()
67           .setDestroyable(80);
68     case FurnitureType::JEWELER:
69       return Furniture("jeweler", ViewObject(ViewId::JEWELER, ViewLayer::FLOOR), type, tribe)
70           .setBlocking()
71           .setUsageTime(5)
72           .setCanHide()
73           .setFireInfo(Fire(500, 0.5))
74           .setDestroyable(40);
75     case FurnitureType::STEEL_FURNACE:
76       return Furniture("steel furnace", ViewObject(ViewId::STEEL_FURNACE, ViewLayer::FLOOR), type, tribe)
77           .setBlocking()
78           .setUsageTime(5)
79           .setCanHide()
80           .setDestroyable(100);
81     case FurnitureType::BOOKCASE_WOOD:
82       return Furniture("wooden bookcase", ViewObject(ViewId::BOOKCASE_WOOD, ViewLayer::FLOOR), type, tribe)
83           .setBlocking()
84           .setUsageType(FurnitureUsageType::STUDY)
85           .setUsageTime(5)
86           .setCanHide()
87           .setFireInfo(Fire(700, 1.0))
88           .setDestroyable(50);
89     case FurnitureType::BOOKCASE_IRON:
90       return Furniture("iron bookcase", ViewObject(ViewId::BOOKCASE_IRON, ViewLayer::FLOOR), type, tribe)
91           .setBlocking()
92           .setUsageType(FurnitureUsageType::STUDY)
93           .setUsageTime(5)
94           .setCanHide()
95           .setFireInfo(Fire(700, 0.5))
96           .setDestroyable(50);
97     case FurnitureType::BOOKCASE_GOLD:
98       return Furniture("golden bookcase", ViewObject(ViewId::BOOKCASE_GOLD, ViewLayer::FLOOR), type, tribe)
99           .setBlocking()
100           .setUsageType(FurnitureUsageType::STUDY)
101           .setUsageTime(5)
102           .setCanHide()
103           .setFireInfo(Fire(700, 0.5))
104           .setDestroyable(50);
105     case FurnitureType::THRONE:
106       return Furniture("throne", ViewObject(ViewId::THRONE, ViewLayer::FLOOR), type, tribe)
107           .setCanHide()
108           .setDestroyable(80);
109     case FurnitureType::IMPALED_HEAD:
110       return Furniture("impaled head", ViewObject(ViewId::IMPALED_HEAD, ViewLayer::FLOOR), type, tribe)
111           .setDestroyable(10);
112     case FurnitureType::BEAST_CAGE:
113       return Furniture("beast cage", ViewObject(ViewId::BEAST_CAGE, ViewLayer::FLOOR), type, tribe)
114           .setUsageType(FurnitureUsageType::SLEEP)
115           .setTickType(FurnitureTickType::BED)
116           .setCanHide()
117           .setFireInfo(Fire(500, 0.5))
118           .setDestroyable(40);
119     case FurnitureType::BED:
120       return Furniture("bed", ViewObject(ViewId::BED, ViewLayer::FLOOR), type, tribe)
121           .setUsageType(FurnitureUsageType::SLEEP)
122           .setTickType(FurnitureTickType::BED)
123           .setCanHide()
124           .setFireInfo(Fire(500, 0.7))
125           .setDestroyable(40);
126     case FurnitureType::GRAVE:
127       return Furniture("grave", ViewObject(ViewId::GRAVE, ViewLayer::FLOOR), type, tribe)
128           .setUsageType(FurnitureUsageType::SLEEP)
129           .setCanHide()
130           .setTickType(FurnitureTickType::BED)
131           .setDestroyable(40);
132     case FurnitureType::DEMON_SHRINE:
133       return Furniture("demon shrine", ViewObject(ViewId::DEMON_SHRINE, ViewLayer::FLOOR), type, tribe)
134           .setBlocking()
135           .setCanHide()
136           .setUsageTime(5)
137           .setDestroyable(80);
138     case FurnitureType::PRISON:
139         return Furniture("prison", ViewObject(ViewId::PRISON, ViewLayer::FLOOR_BACKGROUND), type, tribe);
140     case FurnitureType::TREASURE_CHEST:
141       return Furniture("treasure chest", ViewObject(ViewId::TREASURE_CHEST, ViewLayer::FLOOR), type, tribe)
142           .setCanHide()
143           .setFireInfo(Fire(500, 0.5))
144           .setDestroyable(40);
145     case FurnitureType::EYEBALL:
146       return Furniture("eyeball", ViewObject(ViewId::EYEBALL, ViewLayer::FLOOR), type, tribe)
147           .setBlocking()
148           .setCanHide()
149           .setLightEmission(8.2)
150           .setDestroyable(30);
151     case FurnitureType::WHIPPING_POST:
152       return Furniture("whipping post", ViewObject(ViewId::WHIPPING_POST, ViewLayer::FLOOR), type, tribe)
153           .setUsageType(FurnitureUsageType::TIE_UP)
154           .setFireInfo(Fire(300, 0.5))
155           .setDestroyable(30);
156     case FurnitureType::GALLOWS:
157       return Furniture("gallows", ViewObject(ViewId::GALLOWS, ViewLayer::FLOOR), type, tribe)
158           .setUsageType(FurnitureUsageType::TIE_UP)
159           .setFireInfo(Fire(300, 0.5))
160           .setDestroyable(30);
161     case FurnitureType::MINION_STATUE:
162       return Furniture("statue", ViewObject(ViewId::MINION_STATUE, ViewLayer::FLOOR), type, tribe)
163           .setBlocking()
164           .setCanHide()
165           .setDestroyable(50);
166     case FurnitureType::BARRICADE:
167       return Furniture("barricade", ViewObject(ViewId::BARRICADE, ViewLayer::FLOOR), type, tribe)
168           .setBlocking()
169           .setFireInfo(Fire(500, 0.3))
170           .setDestroyable(80);
171     case FurnitureType::CANIF_TREE:
172       return Furniture("tree", ViewObject(ViewId::CANIF_TREE, ViewLayer::FLOOR), type, tribe)
173           .setCanHide()
174           .setBlockVision()
175           .setBlockVision(VisionId::ELF, false)
176           .setDestroyedRemains(FurnitureType::TREE_TRUNK)
177           .setBurntRemains(FurnitureType::BURNT_TREE)
178           .setDestroyable(100, DestroyAction::Type::BOULDER)
179           .setDestroyable(70, DestroyAction::Type::CUT)
180           .setFireInfo(Fire(1000, 0.7))
181           .setItemDrop(ItemFactory::singleType(ItemType::WoodPlank{}, Range(8, 14)))
182           .setSummonedElement(CreatureId::ENT);
183     case FurnitureType::DECID_TREE:
184       return Furniture("tree", ViewObject(ViewId::DECID_TREE, ViewLayer::FLOOR), type, tribe)
185           .setCanHide()
186           .setBlockVision()
187           .setBlockVision(VisionId::ELF, false)
188           .setDestroyedRemains(FurnitureType::TREE_TRUNK)
189           .setBurntRemains(FurnitureType::BURNT_TREE)
190           .setFireInfo(Fire(1000, 0.7))
191           .setDestroyable(100, DestroyAction::Type::BOULDER)
192           .setDestroyable(70, DestroyAction::Type::CUT)
193           .setItemDrop(ItemFactory::singleType(ItemType::WoodPlank{}, Range(8, 14)))
194           .setSummonedElement(CreatureId::ENT);
195     case FurnitureType::TREE_TRUNK:
196       return Furniture("tree trunk", ViewObject(ViewId::TREE_TRUNK, ViewLayer::FLOOR), type, tribe);
197     case FurnitureType::BURNT_TREE:
198       return Furniture("burnt tree", ViewObject(ViewId::BURNT_TREE, ViewLayer::FLOOR), type, tribe)
199           .setCanHide()
200           .setDestroyable(30);
201     case FurnitureType::BUSH:
202       return Furniture("bush", ViewObject(ViewId::BUSH, ViewLayer::FLOOR), type, tribe)
203           .setDestroyable(20, DestroyAction::Type::BOULDER)
204           .setDestroyable(10, DestroyAction::Type::CUT)
205           .setCanHide()
206           .setFireInfo(Fire(100, 0.8))
207           .setItemDrop(ItemFactory::singleType(ItemType::WoodPlank{}, Range(2, 4)));
208     case FurnitureType::CROPS:
209       return Furniture("wheat", ViewObject(Random.choose(ViewId::CROPS, ViewId::CROPS2), ViewLayer::FLOOR), type, tribe)
210           .setUsageType(FurnitureUsageType::CROPS)
211           .setUsageTime(3);
212     case FurnitureType::PIGSTY:
213       return Furniture("pigsty", ViewObject(ViewId::MUD, ViewLayer::FLOOR),
214                        type, tribe)
215           .setTickType(FurnitureTickType::PIGSTY);
216     case FurnitureType::GROUND_TORCH:
217       return Furniture("torch", ViewObject(ViewId::TORCH, ViewLayer::FLOOR), type, tribe)
218           .setLightEmission(8.2);
219     case FurnitureType::TORCH_N:
220       return Furniture("torch", ViewObject(ViewId::TORCH, ViewLayer::TORCH1).setAttachmentDir(Dir::N), type, tribe)
221           .setLightEmission(8.2)
222           .setLayer(FurnitureLayer::CEILING);
223     case FurnitureType::TORCH_E:
224       return Furniture("torch", ViewObject(ViewId::TORCH, ViewLayer::TORCH2).setAttachmentDir(Dir::E), type, tribe)
225           .setLightEmission(8.2)
226           .setLayer(FurnitureLayer::CEILING);
227     case FurnitureType::TORCH_S:
228       return Furniture("torch", ViewObject(ViewId::TORCH, ViewLayer::TORCH2).setAttachmentDir(Dir::S), type, tribe)
229           .setLightEmission(8.2)
230           .setLayer(FurnitureLayer::CEILING);
231     case FurnitureType::TORCH_W:
232       return Furniture("torch", ViewObject(ViewId::TORCH, ViewLayer::TORCH2).setAttachmentDir(Dir::W), type, tribe)
233           .setLightEmission(8.2)
234           .setLayer(FurnitureLayer::CEILING);
235     case FurnitureType::TORTURE_TABLE:
236       return Furniture("torture table", ViewObject(ViewId::TORTURE_TABLE, ViewLayer::FLOOR), type,
237           tribe)
238           .setCanHide()
239           .setUsageType(FurnitureUsageType::TIE_UP)
240           .setDestroyable(80);
241     case FurnitureType::FOUNTAIN:
242       return Furniture("fountain", ViewObject(ViewId::FOUNTAIN, ViewLayer::FLOOR), type, tribe)
243           .setCanHide()
244           .setUsageType(FurnitureUsageType::FOUNTAIN)
245           .setSummonedElement(CreatureId::WATER_ELEMENTAL)
246           .setDestroyable(80);
247     case FurnitureType::CHEST:
248       return Furniture("chest", ViewObject(ViewId::CHEST, ViewLayer::FLOOR), type, tribe)
249           .setCanHide()
250           .setUsageType(FurnitureUsageType::CHEST)
251           .setFireInfo(Fire(500, 0.5))
252           .setDestroyable(30);
253     case FurnitureType::OPENED_CHEST:
254       return Furniture("opened chest", ViewObject(ViewId::OPENED_CHEST, ViewLayer::FLOOR), type, tribe)
255           .setCanHide()
256           .setFireInfo(Fire(500, 0.5))
257           .setDestroyable(30);
258     case FurnitureType::COFFIN:
259       return Furniture("coffin", ViewObject(ViewId::COFFIN, ViewLayer::FLOOR), type, tribe)
260           .setCanHide()
261           .setUsageType(FurnitureUsageType::COFFIN)
262           .setFireInfo(Fire(500, 0.5))
263           .setDestroyable(40);
264     case FurnitureType::VAMPIRE_COFFIN:
265       return Furniture("coffin", ViewObject(ViewId::COFFIN, ViewLayer::FLOOR), type, tribe)
266           .setCanHide()
267           .setUsageType(FurnitureUsageType::VAMPIRE_COFFIN)
268           .setFireInfo(Fire(500, 0.5))
269           .setDestroyable(40);
270     case FurnitureType::OPENED_COFFIN:
271       return Furniture("opened coffin", ViewObject(ViewId::OPENED_COFFIN, ViewLayer::FLOOR), type, tribe)
272           .setCanHide()
273           .setFireInfo(Fire(500, 0.5))
274           .setDestroyable(40);
275     case FurnitureType::DOOR:
276       return Furniture("door", ViewObject(ViewId::DOOR, ViewLayer::FLOOR), type, tribe)
277           .setBlockingEnemies()
278           .setCanHide()
279           .setBlockVision()
280           .setFireInfo(Fire(500, 0.5))
281           .setDestroyable(100)
282           .setClickType(FurnitureClickType::LOCK);
283     case FurnitureType::LOCKED_DOOR:
284       return Furniture("locked door", ViewObject(ViewId::LOCKED_DOOR, ViewLayer::FLOOR), type, tribe)
285           .setBlocking()
286           .setBlockVision()
287           .setFireInfo(Fire(500, 0.5))
288           .setDestroyable(100)
289           .setClickType(FurnitureClickType::UNLOCK);
290     case FurnitureType::WELL:
291       return Furniture("well", ViewObject(ViewId::WELL, ViewLayer::FLOOR), type, tribe)
292           .setCanHide()
293           .setFireInfo(Fire(500, 0.5))
294           .setSummonedElement(CreatureId::WATER_ELEMENTAL)
295           .setDestroyable(80);
296     case FurnitureType::KEEPER_BOARD:
297       return Furniture("message board", ViewObject(ViewId::NOTICE_BOARD, ViewLayer::FLOOR), type, tribe)
298           .setCanHide()
299           .setUsageType(FurnitureUsageType::KEEPER_BOARD)
300           .setFireInfo(Fire(500, 0.5))
301           .setDestroyable(50);
302     case FurnitureType::UP_STAIRS:
303       return Furniture("stairs", ViewObject(ViewId::UP_STAIRCASE, ViewLayer::FLOOR), type, tribe)
304           .setCanHide()
305           .setUsageType(FurnitureUsageType::STAIRS);
306     case FurnitureType::DOWN_STAIRS:
307       return Furniture("stairs", ViewObject(ViewId::DOWN_STAIRCASE, ViewLayer::FLOOR), type, tribe)
308           .setCanHide()
309           .setUsageType(FurnitureUsageType::STAIRS);
310     case FurnitureType::SOKOBAN_HOLE:
311       return Furniture("hole", ViewObject(ViewId::SOKOBAN_HOLE, ViewLayer::FLOOR), type, tribe)
312           .setEntryType(FurnitureEntry(FurnitureEntry::Sokoban{}));
313     case FurnitureType::BRIDGE:
314       return Furniture("bridge", ViewObject(ViewId::BRIDGE, ViewLayer::FLOOR), type, tribe)
315           .setOverrideMovement();
316     case FurnitureType::ROAD:
317       return Furniture("road", ViewObject(ViewId::ROAD, ViewLayer::FLOOR), type, tribe);
318     case FurnitureType::MOUNTAIN:
319       return Furniture("mountain", ViewObject(ViewId::MOUNTAIN, ViewLayer::FLOOR), type, tribe)
320           .setBlocking()
321           .setBlockVision()
322           .setConstructMessage(Furniture::FILL_UP)
323           .setIsWall()
324           .setDestroyable(200, DestroyAction::Type::BOULDER)
325           .setDestroyable(50, DestroyAction::Type::DIG)
326           .setDestroyable(200, DestroyAction::Type::HOSTILE_DIG)
327           .setDestroyable(2000, DestroyAction::Type::HOSTILE_DIG_NO_SKILL)
328           .setSummonedElement(CreatureId::EARTH_ELEMENTAL);
329     case FurnitureType::IRON_ORE:
330       return Furniture("iron ore", ViewObject(ViewId::IRON_ORE, ViewLayer::FLOOR), type, tribe)
331           .setBlocking()
332           .setBlockVision()
333           .setIsWall()
334           .setItemDrop(ItemFactory::singleType(ItemType::IronOre{}, Range(8, 14)))
335           .setDestroyable(200, DestroyAction::Type::BOULDER)
336           .setDestroyable(220, DestroyAction::Type::DIG)
337           .setDestroyable(200, DestroyAction::Type::HOSTILE_DIG)
338           .setDestroyable(2000, DestroyAction::Type::HOSTILE_DIG_NO_SKILL);
339     case FurnitureType::STONE:
340       return Furniture("granite", ViewObject(ViewId::STONE, ViewLayer::FLOOR), type, tribe)
341           .setBlocking()
342           .setBlockVision()
343           .setIsWall()
344           .setItemDrop(ItemFactory::singleType(ItemType::Rock{}, Range(8, 14)))
345           .setDestroyable(200, DestroyAction::Type::BOULDER)
346           .setDestroyable(250, DestroyAction::Type::DIG)
347           .setDestroyable(200, DestroyAction::Type::HOSTILE_DIG)
348           .setDestroyable(2000, DestroyAction::Type::HOSTILE_DIG_NO_SKILL);
349     case FurnitureType::GOLD_ORE:
350       return Furniture("gold ore", ViewObject(ViewId::GOLD_ORE, ViewLayer::FLOOR), type, tribe)
351           .setBlocking()
352           .setBlockVision()
353           .setIsWall()
354           .setItemDrop(ItemFactory::singleType(ItemType::GoldPiece{}, Range(8, 14)))
355           .setDestroyable(200, DestroyAction::Type::BOULDER)
356           .setDestroyable(220, DestroyAction::Type::DIG)
357           .setDestroyable(200, DestroyAction::Type::HOSTILE_DIG)
358           .setDestroyable(2000, DestroyAction::Type::HOSTILE_DIG_NO_SKILL);
359     case FurnitureType::DUNGEON_WALL:
360       return Furniture("wall", ViewObject(ViewId::DUNGEON_WALL, ViewLayer::FLOOR), type, tribe)
361           .setBlocking()
362           .setBlockVision()
363           .setIsWall()
364           .setConstructMessage(Furniture::REINFORCE)
365           .setDestroyable(300, DestroyAction::Type::BOULDER)
366           .setDestroyable(100, DestroyAction::Type::DIG)
367           .setDestroyable(1900, DestroyAction::Type::HOSTILE_DIG)
368           .setDestroyable(2000, DestroyAction::Type::HOSTILE_DIG_NO_SKILL);
369     case FurnitureType::CASTLE_WALL:
370       return Furniture("wall", ViewObject(ViewId::CASTLE_WALL, ViewLayer::FLOOR), type, tribe)
371           .setBlocking()
372           .setBlockVision()
373           .setIsWall()
374           .setDestroyable(300, DestroyAction::Type::BOULDER);
375     case FurnitureType::WOOD_WALL:
376       return Furniture("wall", ViewObject(ViewId::WOOD_WALL, ViewLayer::FLOOR), type, tribe)
377           .setBlocking()
378           .setBlockVision()
379           .setIsWall()
380           .setDestroyable(100, DestroyAction::Type::BOULDER)
381           .setSummonedElement(CreatureId::ENT)
382           .setFireInfo(Fire(1000, 0.7));
383     case FurnitureType::MUD_WALL:
384       return Furniture("wall", ViewObject(ViewId::MUD_WALL, ViewLayer::FLOOR), type, tribe)
385           .setBlocking()
386           .setBlockVision()
387           .setIsWall()
388           .setDestroyable(100, DestroyAction::Type::BOULDER);
389     case FurnitureType::FLOOR_WOOD1:
390       return Furniture("floor", ViewObject(ViewId::WOOD_FLOOR2, ViewLayer::FLOOR_BACKGROUND), type, tribe)
391           .setFireInfo(Fire(500, 0.5))
392           .setLayer(FurnitureLayer::FLOOR);
393     case FurnitureType::FLOOR_WOOD2:
394       return Furniture("floor", ViewObject(ViewId::WOOD_FLOOR4, ViewLayer::FLOOR_BACKGROUND), type, tribe)
395           .setFireInfo(Fire(500, 0.5))
396           .setLayer(FurnitureLayer::FLOOR);
397     case FurnitureType::FLOOR_STONE1:
398       return Furniture("floor", ViewObject(ViewId::STONE_FLOOR1, ViewLayer::FLOOR_BACKGROUND), type, tribe)
399           .setLayer(FurnitureLayer::FLOOR);
400     case FurnitureType::FLOOR_STONE2:
401       return Furniture("floor", ViewObject(ViewId::STONE_FLOOR5, ViewLayer::FLOOR_BACKGROUND), type, tribe)
402           .setLayer(FurnitureLayer::FLOOR);
403     case FurnitureType::FLOOR_CARPET1:
404       return Furniture("floor", ViewObject(ViewId::CARPET_FLOOR1, ViewLayer::FLOOR_BACKGROUND), type, tribe)
405           .setLayer(FurnitureLayer::FLOOR);
406     case FurnitureType::FLOOR_CARPET2:
407       return Furniture("floor", ViewObject(ViewId::CARPET_FLOOR4, ViewLayer::FLOOR_BACKGROUND), type, tribe)
408           .setLayer(FurnitureLayer::FLOOR);
409     case FurnitureType::ALARM_TRAP:
410       return Furniture("alarm trap", ViewObject(ViewId::ALARM_TRAP, ViewLayer::FLOOR), type, tribe)
411           .setEntryType(FurnitureEntry(FurnitureEntry::Trap(Effect::Alarm{})))
412           .setEmitsWarning()
413           .setConstructMessage(Furniture::SET_UP);
414     case FurnitureType::POISON_GAS_TRAP:
415       return Furniture("poison gas trap", ViewObject(ViewId::GAS_TRAP, ViewLayer::FLOOR), type, tribe)
416           .setEntryType(FurnitureEntry(FurnitureEntry::Trap(Effect::EmitPoisonGas{})))
417           .setEmitsWarning()
418           .setConstructMessage(Furniture::SET_UP);
419     case FurnitureType::WEB_TRAP:
420       return Furniture("web trap", ViewObject(ViewId::WEB_TRAP, ViewLayer::FLOOR), type, tribe)
421           .setEntryType(FurnitureEntry(FurnitureEntry::Trap(Effect::Lasting{LastingEffect::ENTANGLED})))
422           .setEmitsWarning()
423           .setConstructMessage(Furniture::SET_UP);
424     case FurnitureType::SPIDER_WEB:
425       return Furniture("spider web", ViewObject(ViewId::WEB_TRAP, ViewLayer::FLOOR), type, tribe)
426           .setEntryType(FurnitureEntry(FurnitureEntry::Trap(Effect::Lasting{LastingEffect::ENTANGLED},
427               true)));
428     case FurnitureType::SURPRISE_TRAP:
429       return Furniture("surprise trap", ViewObject(ViewId::SURPRISE_TRAP, ViewLayer::FLOOR), type, tribe)
430           .setEntryType(FurnitureEntry(FurnitureEntry::Trap(Effect::TeleEnemies{})))
431           .setEmitsWarning()
432           .setConstructMessage(Furniture::SET_UP);
433     case FurnitureType::TERROR_TRAP:
434       return Furniture("panic trap", ViewObject(ViewId::TERROR_TRAP, ViewLayer::FLOOR), type, tribe)
435           .setEntryType(FurnitureEntry(FurnitureEntry::Trap(Effect::Lasting{LastingEffect::PANIC})))
436           .setEmitsWarning()
437           .setConstructMessage(Furniture::SET_UP);
438     case FurnitureType::BOULDER_TRAP:
439       return Furniture("boulder trap", ViewObject(ViewId::BOULDER, ViewLayer::CREATURE), type, tribe)
440           .setBlocking()
441           .setCanHide()
442           .setTickType(FurnitureTickType::BOULDER_TRAP)
443           .setDestroyable(40)
444           .setEmitsWarning()
445           .setConstructMessage(Furniture::SET_UP);
446     case FurnitureType::PORTAL:
447       return Furniture("portal", ViewObject(ViewId::PORTAL, ViewLayer::FLOOR), type, tribe)
448           .setDestroyable(40)
449           .setUsageType(FurnitureUsageType::PORTAL)
450           .setTickType(FurnitureTickType::PORTAL);
451     case FurnitureType::METEOR_SHOWER:
452       return Furniture("meteor shower", none, type, tribe)
453           .setLayer(FurnitureLayer::CEILING)
454           .setConstructMessage(none)
455           .setTickType(FurnitureTickType::METEOR_SHOWER);
456     case FurnitureType::WATER: {
457       auto ret = Furniture("water", ViewObject(ViewId::WATER, ViewLayer::FLOOR_BACKGROUND)
458               .setAttribute(ViewObject::Attribute::WATER_DEPTH, 100.0), type, tribe)
459           .setLayer(FurnitureLayer::GROUND)
460           .setEntryType(FurnitureEntry::Water{})
461           .setDroppedItems(FurnitureDroppedItems::Water{"sinks", "sink", "You hear a splash."_s})
462           .setCanBuildBridgeOver()
463           .setSummonedElement(CreatureId::WATER_ELEMENTAL);
464       ret.modMovementSet()
465           .clearTraits()
466           .addTrait(MovementTrait::FLY)
467           .addTrait(MovementTrait::SWIM)
468           .addForcibleTrait(MovementTrait::WALK);
469       return ret;
470     }
471     case FurnitureType::SHALLOW_WATER1: {
472       auto ret = Furniture("shallow water", ViewObject(ViewId::WATER, ViewLayer::FLOOR_BACKGROUND)
473               .setAttribute(ViewObject::Attribute::WATER_DEPTH, 1.5), type, tribe)
474           .setLayer(FurnitureLayer::GROUND)
475           .setEntryType(FurnitureEntry::Water{})
476           .setCanBuildBridgeOver()
477           .setSummonedElement(CreatureId::WATER_ELEMENTAL);
478       ret.modMovementSet()
479           .clearTraits()
480           .addForcibleTrait(MovementTrait::WALK)
481           .addTrait(MovementTrait::FLY)
482           .addTrait(MovementTrait::SWIM)
483           .addTrait(MovementTrait::WADE);
484       return ret;
485     }
486     case FurnitureType::SHALLOW_WATER2: {
487       auto ret = Furniture("shallow water", ViewObject(ViewId::WATER, ViewLayer::FLOOR_BACKGROUND)
488               .setAttribute(ViewObject::Attribute::WATER_DEPTH, 0.5), type, tribe)
489           .setLayer(FurnitureLayer::GROUND)
490           .setEntryType(FurnitureEntry::Water{})
491           .setCanBuildBridgeOver()
492           .setSummonedElement(CreatureId::WATER_ELEMENTAL);
493       ret.modMovementSet()
494           .clearTraits()
495           .addForcibleTrait(MovementTrait::WALK)
496           .addTrait(MovementTrait::FLY)
497           .addTrait(MovementTrait::SWIM)
498           .addTrait(MovementTrait::WADE);
499       return ret;
500     }
501     case FurnitureType::MAGMA: {
502       auto ret = Furniture("magma", ViewObject(ViewId::MAGMA, ViewLayer::FLOOR_BACKGROUND), type, tribe)
503           .setLayer(FurnitureLayer::GROUND)
504           .setEntryType(FurnitureEntry::Magma{})
505           .setDroppedItems(FurnitureDroppedItems::Water{"burns", "burn", none})
506           .setLightEmission(8.2)
507           .setCanBuildBridgeOver()
508           .setSummonedElement(CreatureId::FIRE_ELEMENTAL);
509       ret.modMovementSet()
510           .clearTraits()
511           .addTrait(MovementTrait::FLY)
512           .addForcibleTrait(MovementTrait::WALK);
513       return ret;
514     }
515     case FurnitureType::SAND:
516       return Furniture("sand", ViewObject(ViewId::SAND, ViewLayer::FLOOR_BACKGROUND), type, tribe)
517           .setLayer(FurnitureLayer::GROUND);
518     case FurnitureType::GRASS:
519       return Furniture("grass", ViewObject(ViewId::GRASS, ViewLayer::FLOOR_BACKGROUND), type, tribe)
520           .setLayer(FurnitureLayer::GROUND);
521     case FurnitureType::MUD:
522       return Furniture("mud", ViewObject(ViewId::MUD, ViewLayer::FLOOR_BACKGROUND), type, tribe)
523           .setLayer(FurnitureLayer::GROUND);
524     case FurnitureType::HILL:
525       return Furniture("hill", ViewObject(ViewId::HILL, ViewLayer::FLOOR_BACKGROUND), type, tribe)
526           .setLayer(FurnitureLayer::GROUND);
527     case FurnitureType::FLOOR:
528       return Furniture("floor", ViewObject(ViewId::FLOOR, ViewLayer::FLOOR_BACKGROUND), type, tribe)
529           .setLayer(FurnitureLayer::GROUND);
530     case FurnitureType::BLACK_FLOOR:
531       return Furniture("floor", ViewObject(ViewId::EMPTY, ViewLayer::FLOOR_BACKGROUND), type, tribe)
532           .setLayer(FurnitureLayer::GROUND);
533     case FurnitureType::TUTORIAL_ENTRANCE:
534       return Furniture("tutorial entrance", ViewObject(ViewId::TUTORIAL_ENTRANCE, ViewLayer::TORCH2), type, tribe)
535           .setLayer(FurnitureLayer::CEILING);
536   }
537 }
538 
operator ==(const FurnitureParams & p) const539 bool FurnitureParams::operator == (const FurnitureParams& p) const {
540 
541   return type == p.type && tribe == p.tribe;
542 }
543 
hasSupport(FurnitureType type,Position pos)544 bool FurnitureFactory::hasSupport(FurnitureType type, Position pos) {
545   switch (type) {
546     case FurnitureType::TORCH_N:
547       return pos.minus(Vec2(0, 1)).isWall();
548     case FurnitureType::TORCH_E:
549       return pos.plus(Vec2(1, 0)).isWall();
550     case FurnitureType::TORCH_S:
551       return pos.plus(Vec2(0, 1)).isWall();
552     case FurnitureType::TORCH_W:
553       return pos.minus(Vec2(1, 0)).isWall();
554     case FurnitureType::DOOR:
555       return (pos.minus(Vec2(0, 1)).isWall() && pos.minus(Vec2(0, -1)).isWall()) ||
556              (pos.minus(Vec2(1, 0)).isWall() && pos.minus(Vec2(-1, 0)).isWall());
557     default:
558       return true;
559   }
560 }
561 
canBuild(FurnitureType type,Position pos)562 bool FurnitureFactory::canBuild(FurnitureType type, Position pos) {
563   switch (type) {
564     case FurnitureType::BRIDGE:
565       return pos.getFurniture(FurnitureLayer::GROUND)->canBuildBridgeOver();
566     case FurnitureType::DUNGEON_WALL:
567       if (auto furniture = pos.getFurniture(FurnitureLayer::MIDDLE))
568         return furniture->getType() == FurnitureType::MOUNTAIN;
569       else
570         return false;
571     default:
572       return pos.getFurniture(FurnitureLayer::GROUND)->getMovementSet().canEnter({MovementTrait::WALK}) &&
573           !pos.getFurniture(Furniture::getLayer(type)) &&
574           !pos.isWall();
575   }
576 }
577 
isUpgrade(FurnitureType base,FurnitureType upgraded)578 bool FurnitureFactory::isUpgrade(FurnitureType base, FurnitureType upgraded) {
579   switch (base) {
580     case FurnitureType::TRAINING_WOOD:
581       return upgraded == FurnitureType::TRAINING_IRON || upgraded == FurnitureType::TRAINING_STEEL;
582     case FurnitureType::TRAINING_IRON:
583       return upgraded == FurnitureType::TRAINING_STEEL;
584     case FurnitureType::BOOKCASE_WOOD:
585       return upgraded == FurnitureType::BOOKCASE_IRON || upgraded == FurnitureType::BOOKCASE_GOLD;
586     case FurnitureType::BOOKCASE_IRON:
587       return upgraded == FurnitureType::BOOKCASE_GOLD;
588     default:
589       return false;
590   }
591 }
592 
getUpgrades(FurnitureType base)593 const vector<FurnitureType>& FurnitureFactory::getUpgrades(FurnitureType base) {
594   static EnumMap<FurnitureType, vector<FurnitureType>> upgradeMap(
595       [](const FurnitureType& base) {
596     vector<FurnitureType> ret;
597     for (auto type2 : ENUM_ALL(FurnitureType))
598       if (FurnitureFactory::isUpgrade(base, type2))
599         ret.push_back(type2);
600     return ret;
601   });
602   return upgradeMap[base];
603 }
604 
FurnitureFactory(TribeId t,const EnumMap<FurnitureType,double> & d,const vector<FurnitureType> & u)605 FurnitureFactory::FurnitureFactory(TribeId t, const EnumMap<FurnitureType, double>& d,
606     const vector<FurnitureType>& u) : tribe(t), distribution(d), unique(u) {
607 }
608 
FurnitureFactory(TribeId t,FurnitureType f)609 FurnitureFactory::FurnitureFactory(TribeId t, FurnitureType f) : tribe(t), distribution({{f, 1}}) {
610 }
611 
get(FurnitureType type,TribeId tribe)612 PFurniture FurnitureFactory::get(FurnitureType type, TribeId tribe) {
613   return makeOwner<Furniture>(::get(type, tribe));
614 }
615 
roomFurniture(TribeId tribe)616 FurnitureFactory FurnitureFactory::roomFurniture(TribeId tribe) {
617   return FurnitureFactory(tribe, {
618       {FurnitureType::BED, 2},
619       {FurnitureType::GROUND_TORCH, 1},
620       {FurnitureType::CHEST, 2}
621   });
622 }
623 
castleFurniture(TribeId tribe)624 FurnitureFactory FurnitureFactory::castleFurniture(TribeId tribe) {
625   return FurnitureFactory(tribe, {
626       {FurnitureType::BED, 2},
627       {FurnitureType::GROUND_TORCH, 1},
628       {FurnitureType::FOUNTAIN, 1},
629       {FurnitureType::CHEST, 2}
630   });
631 }
632 
dungeonOutside(TribeId tribe)633 FurnitureFactory FurnitureFactory::dungeonOutside(TribeId tribe) {
634   return FurnitureFactory(tribe, {
635       {FurnitureType::GROUND_TORCH, 1},
636   });
637 }
638 
castleOutside(TribeId tribe)639 FurnitureFactory FurnitureFactory::castleOutside(TribeId tribe) {
640   return FurnitureFactory(tribe, {
641       {FurnitureType::GROUND_TORCH, 1},
642       {FurnitureType::WELL, 1},
643   });
644 }
645 
villageOutside(TribeId tribe)646 FurnitureFactory FurnitureFactory::villageOutside(TribeId tribe) {
647   return FurnitureFactory(tribe, {
648       {FurnitureType::GROUND_TORCH, 1},
649       {FurnitureType::WELL, 1},
650   });
651 }
652 
cryptCoffins(TribeId tribe)653 FurnitureFactory FurnitureFactory::cryptCoffins(TribeId tribe) {
654   return FurnitureFactory(tribe, {
655       {FurnitureType::COFFIN, 1},
656   }, {
657       FurnitureType::VAMPIRE_COFFIN
658   });
659 }
660 
getWaterType(double depth)661 FurnitureType FurnitureFactory::getWaterType(double depth) {
662   if (depth >= 2.0)
663     return FurnitureType::WATER;
664   else if (depth >= 1.0)
665     return FurnitureType::SHALLOW_WATER1;
666   else
667     return FurnitureType::SHALLOW_WATER2;
668 }
669 
getRandom(RandomGen & random)670 FurnitureParams FurnitureFactory::getRandom(RandomGen& random) {
671   if (!unique.empty()) {
672     FurnitureType f = unique.back();
673     unique.pop_back();
674     return {f, *tribe};
675   } else
676     return {random.choose(distribution), *tribe};
677 }
678 
679