1 /*
2 * Copyright (c) 2017-2019 sel-project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 */
23 /**
24 * Copyright: Copyright (c) 2017-2019 sel-project
25 * License: MIT
26 * Authors: Kripth
27 * Source: $(HTTP github.com/sel-project/selery/source/selery/item/consumeable.d, selery/item/consumeable.d)
28 */
29 module selery.item.consumeable;
30
31 import std.random : uniform, uniform01;
32
33 import selery.about : block_t, item_t;
34 import selery.block.block : compareBlock, blockInto;
35 import selery.block.blocks : Blocks;
36 import selery.effect;
37 import selery.item.item : Item, SimpleItem;
38 import selery.item.items : Items;
39 import selery.math.vector;
40 import selery.player.player : Player;
41 import selery.util.tuple : Tuple;
42 import selery.world.world : World;
43
44 import roman : fromRoman;
45
46 static import sul.effects;
47 static import sul.items;
48
49 enum Residue {
50
51 substract,
52 bowl,
53 bottle
54
55 }
56
57 alias EffectInfo = Tuple!(sul.effects.Effect, "effect", uint, "duration", ubyte, "level", float, "probability");
58
59 public EffectInfo effectInfo(sul.effects.Effect effect, uint duration, string level, float prob=1) {
60 return EffectInfo(effect, duration, (level.fromRoman - 1) & 255, prob);
61 }
62
63 enum Potions : EffectInfo {
64
65 // extended = duration extended
66 // plus = level 1 (default is 0)
67
68 nightVision = effectInfo(Effects.nightVision, 180, "I"),
69 nightVisionExtended = effectInfo(Effects.nightVision, 480, "I"),
70 invisibility = effectInfo(Effects.invisibility, 180, "I"),
71 invisibilityExtended = effectInfo(Effects.invisibility, 480, "I"),
72 leaping = effectInfo(Effects.jumpBoost, 180, "I"),
73 leapingExtended = effectInfo(Effects.jumpBoost, 480, "I"),
74 leapingPlus = effectInfo(Effects.jumpBoost, 90, "II"),
75 fireResistance = effectInfo(Effects.fireResistance, 180, "I"),
76 fireResistanceExtended = effectInfo(Effects.fireResistance, 480, "I"),
77 swiftness = effectInfo(Effects.speed, 180, "I"),
78 swiftnessExtended = effectInfo(Effects.speed, 480, "I"),
79 swiftnessPlus = effectInfo(Effects.speed, 90, "II"),
80 slowness = effectInfo(Effects.slowness, 60, "I"),
81 slownessExtended = effectInfo(Effects.slowness, 240, "I"),
82 waterBreathing = effectInfo(Effects.waterBreathing, 180, "I"),
83 waterBreathingExtended = effectInfo(Effects.waterBreathing, 480, "I"),
84 healing = effectInfo(Effects.instantHealth, 0, "I"),
85 healingPlus = effectInfo(Effects.instantHealth, 0, "II"),
86 harming = effectInfo(Effects.instantDamage, 0, "I"),
87 harmingPlus = effectInfo(Effects.instantDamage, 0, "II"),
88 poison = effectInfo(Effects.poison, 45, "I"),
89 poisonExtended = effectInfo(Effects.poison, 120, "I"),
90 poisonPlus = effectInfo(Effects.poison, 22, "II"),
91 regeneration = effectInfo(Effects.regeneration, 45, "I"),
92 regenerationExtended = effectInfo(Effects.regeneration, 120, "I"),
93 regenerationPlus = effectInfo(Effects.regeneration, 22, "II"),
94 strength = effectInfo(Effects.strength, 180, "I"),
95 strengthExtended = effectInfo(Effects.strength, 480, "I"),
96 strengthPlus = effectInfo(Effects.strength, 90, "II"),
97 weakness = effectInfo(Effects.weakness, 90, "I"),
98 weaknessExtended = effectInfo(Effects.weakness, 240, "I"),
99 decay = effectInfo(Effects.wither, 40, "II"),
100
101 }
102
103 class ConsumeableItem(sul.items.Item si, EffectInfo[] effects, Residue residue=Residue.substract) : SimpleItem!(si) {
104
105 alias sul = si;
106
107 public @safe this(E...)(E args) {
108 super(args);
109 }
110
111 public final override pure nothrow @property @safe @nogc bool consumeable() {
112 return true;
113 }
114
115 public override Item onConsumed(Player player) {
116 static if(effects.length) {
117 foreach(EffectInfo effect ; effects) {
118 if(effect.probability >= 1 || uniform01!float(player.world.random) >= effect.probability) {
119 player.addEffect(effect.effect, effect.level, effect.duration);
120 }
121 }
122 }
123 static if(residue == Residue.substract) return null;
124 else static if(residue == Residue.bottle) return player.world.items.get(Items.glassBottle);
125 else return player.world.items.get(Items.bowl);
126 }
127
128 alias slot this;
129
130 }
131
132 class FoodItem(sul.items.Item si, uint ghunger, float gsaturation, EffectInfo[] effects=[], Residue residue=Residue.substract) : ConsumeableItem!(si, effects, residue) {
133
134 alias sul = si;
135
136 public @safe this(E...)(E args) {
137 super(args);
138 }
139
140 public static pure nothrow @property @safe @nogc uint hunger() { return ghunger; }
141
142 public static pure nothrow @property @safe @nogc float saturation() { return gsaturation; }
143
144 public override Item onConsumed(Player player) {
145 player.hunger = player.hunger + ghunger;
146 player.saturate(gsaturation);
147 return super.onConsumed(player);
148 }
149
150 alias slot this;
151
152 }
153
154 alias SoupItem(sul.items.Item si, uint ghunger, float gsaturation) = FoodItem!(si, ghunger, gsaturation, [], Residue.bowl);
155
156 class CropFoodItem(sul.items.Item si, uint ghunger, float gsaturation, block_t block) : FoodItem!(si, ghunger, gsaturation) {
157
158 alias sul = si;
159
160 public @safe this(E...)(E args) {
161 super(args);
162 }
163
164 public override pure nothrow @property @safe @nogc bool placeable() {
165 return true;
166 }
167
168 public override block_t place(World world, BlockPosition position, uint face) {
169 if(compareBlock!(Blocks.farmland)(world[position - [0, 1, 0]])) return block;
170 else return Blocks.air;
171 }
172
173 alias slot this;
174
175 }
176
177 class TeleportationItem(sul.items.Item si, uint ghunger, float gsaturation) : FoodItem!(si, ghunger, gsaturation) {
178
179 alias sul = si;
180
181 public @safe this(E...)(E args) {
182 super(args);
183 }
184
185 public override Item onConsumed(Player player) {
186 @property int rand() {
187 return uniform!"[]"(-8, 8, player.world.random);
188 }
189 auto center = BlockPosition(player.position.x.blockInto, player.position.y.blockInto, player.position.z.blockInto);
190 foreach(i ; 0..16) {
191 auto position = center + [rand, rand, rand];
192 if(!player.world[position].hasBoundingBox && !player.world[position + [0, 1, 0]].hasBoundingBox) {
193 player.teleport(cast(EntityPosition)position + [.5, 0, .5]);
194 break;
195 }
196 }
197 return super.onConsumed(player);
198 }
199
200 alias slot this;
201
202 }
203
204 alias PotionItem(sul.items.Item si) = ConsumeableItem!(si, [], Residue.bottle);
205
206 alias PotionItem(sul.items.Item si, EffectInfo effect) = ConsumeableItem!(si, [effect], Residue.bottle);
207
208 class ClearEffectsItem(sul.items.Item si, item_t residue) : SimpleItem!(si) {
209
210 alias sul = si;
211
212 public @safe this(E...)(E args) {
213 super(args);
214 }
215
216 public final override pure nothrow @property @safe @nogc bool consumeable() {
217 return true;
218 }
219
220 public override Item onConsumed(Player player) {
221 player.clearEffects();
222 return player.world.items.get(residue);
223 }
224
225 alias slot this;
226
227 }