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 }