1 /*
2  * Copyright (c) 2017-2018 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: 2017-2018 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.util.util : roman;
43 import selery.world.world : World;
44 
45 static import sul.effects;
46 static import sul.items;
47 
48 enum Residue {
49 
50 	substract,
51 	bowl,
52 	bottle
53 
54 }
55 
56 alias EffectInfo = Tuple!(sul.effects.Effect, "effect", uint, "duration", ubyte, "level", float, "probability");
57 
58 public EffectInfo effectInfo(sul.effects.Effect effect, uint duration, string level, float prob=1) {
59 	return EffectInfo(effect, duration, (level.roman - 1) & 255, prob);
60 }
61 
62 enum Potions : EffectInfo {
63 	
64 	// extended = duration extended
65 	// plus = level 1 (default is 0)
66 	
67 	nightVision = effectInfo(Effects.nightVision, 180, "I"),
68 	nightVisionExtended = effectInfo(Effects.nightVision, 480, "I"),
69 	invisibility = effectInfo(Effects.invisibility, 180, "I"),
70 	invisibilityExtended = effectInfo(Effects.invisibility, 480, "I"),
71 	leaping = effectInfo(Effects.jumpBoost, 180, "I"),
72 	leapingExtended = effectInfo(Effects.jumpBoost, 480, "I"),
73 	leapingPlus = effectInfo(Effects.jumpBoost, 90, "II"),
74 	fireResistance = effectInfo(Effects.fireResistance, 180, "I"),
75 	fireResistanceExtended = effectInfo(Effects.fireResistance, 480, "I"),
76 	swiftness = effectInfo(Effects.speed, 180, "I"),
77 	swiftnessExtended = effectInfo(Effects.speed, 480, "I"),
78 	swiftnessPlus = effectInfo(Effects.speed, 90, "II"),
79 	slowness = effectInfo(Effects.slowness, 60, "I"),
80 	slownessExtended = effectInfo(Effects.slowness, 240, "I"),
81 	waterBreathing = effectInfo(Effects.waterBreathing, 180, "I"),
82 	waterBreathingExtended = effectInfo(Effects.waterBreathing, 480, "I"),
83 	healing = effectInfo(Effects.instantHealth, 0, "I"),
84 	healingPlus = effectInfo(Effects.instantHealth, 0, "II"),
85 	harming = effectInfo(Effects.instantDamage, 0, "I"),
86 	harmingPlus = effectInfo(Effects.instantDamage, 0, "II"),
87 	poison = effectInfo(Effects.poison, 45, "I"),
88 	poisonExtended = effectInfo(Effects.poison, 120, "I"),
89 	poisonPlus = effectInfo(Effects.poison, 22, "II"),
90 	regeneration = effectInfo(Effects.regeneration, 45, "I"),
91 	regenerationExtended = effectInfo(Effects.regeneration, 120, "I"),
92 	regenerationPlus = effectInfo(Effects.regeneration, 22, "II"),
93 	strength = effectInfo(Effects.strength, 180, "I"),
94 	strengthExtended = effectInfo(Effects.strength, 480, "I"),
95 	strengthPlus = effectInfo(Effects.strength, 90, "II"),
96 	weakness = effectInfo(Effects.weakness, 90, "I"),
97 	weaknessExtended = effectInfo(Effects.weakness, 240, "I"),
98 	decay = effectInfo(Effects.wither, 40, "II"),
99 	
100 }
101 
102 class ConsumeableItem(sul.items.Item si, EffectInfo[] effects, Residue residue=Residue.substract) : SimpleItem!(si) {
103 	
104 	alias sul = si;
105 	
106 	public @safe this(E...)(E args) {
107 		super(args);
108 	}
109 	
110 	public final override pure nothrow @property @safe @nogc bool consumeable() {
111 		return true;
112 	}
113 	
114 	public override Item onConsumed(Player player) {
115 		static if(effects.length) {
116 			foreach(EffectInfo effect ; effects) {
117 				if(effect.probability >= 1 || uniform01!float(player.world.random) >= effect.probability) {
118 					player.addEffect(effect.effect, effect.level, effect.duration);
119 				}
120 			}
121 		}
122 		static if(residue == Residue.substract) return null;
123 		else static if(residue == Residue.bottle) return player.world.items.get(Items.glassBottle);
124 		else return player.world.items.get(Items.bowl);
125 	}
126 	
127 	alias slot this;
128 	
129 }
130 
131 class FoodItem(sul.items.Item si, uint ghunger, float gsaturation, EffectInfo[] effects=[], Residue residue=Residue.substract) : ConsumeableItem!(si, effects, residue) {
132 	
133 	alias sul = si;
134 	
135 	public @safe this(E...)(E args) {
136 		super(args);
137 	}
138 	
139 	public static pure nothrow @property @safe @nogc uint hunger() { return ghunger; }
140 	
141 	public static pure nothrow @property @safe @nogc float saturation() { return gsaturation; }
142 	
143 	public override Item onConsumed(Player player) {
144 		player.hunger = player.hunger + ghunger;
145 		player.saturate(gsaturation);
146 		return super.onConsumed(player);
147 	}
148 	
149 	alias slot this;
150 	
151 }
152 
153 alias SoupItem(sul.items.Item si, uint ghunger, float gsaturation) = FoodItem!(si, ghunger, gsaturation, [], Residue.bowl);
154 
155 class CropFoodItem(sul.items.Item si, uint ghunger, float gsaturation, block_t block) : FoodItem!(si, ghunger, gsaturation) {
156 
157 	alias sul = si;
158 	
159 	public @safe this(E...)(E args) {
160 		super(args);
161 	}
162 	
163 	public override pure nothrow @property @safe @nogc bool placeable() {
164 		return true;
165 	}
166 	
167 	public override block_t place(World world, BlockPosition position, uint face) {
168 		if(compareBlock!(Blocks.farmland)(world[position - [0, 1, 0]])) return block;
169 		else return Blocks.air;
170 	}
171 	
172 	alias slot this;
173 
174 }
175 
176 class TeleportationItem(sul.items.Item si, uint ghunger, float gsaturation) : FoodItem!(si, ghunger, gsaturation) {
177 
178 	alias sul = si;
179 	
180 	public @safe this(E...)(E args) {
181 		super(args);
182 	}
183 	
184 	public override Item onConsumed(Player player) {
185 		@property int rand() {
186 			return uniform!"[]"(-8, 8, player.world.random);
187 		}
188 		auto center = BlockPosition(player.position.x.blockInto, player.position.y.blockInto, player.position.z.blockInto);
189 		foreach(i ; 0..16) {
190 			auto position = center + [rand, rand, rand];
191 			if(!player.world[position].hasBoundingBox && !player.world[position + [0, 1, 0]].hasBoundingBox) {
192 				player.teleport(cast(EntityPosition)position + [.5, 0, .5]);
193 				break;
194 			}
195 		}
196 		return super.onConsumed(player);
197 	}
198 	
199 	alias slot this;
200 
201 }
202 
203 alias PotionItem(sul.items.Item si) = ConsumeableItem!(si, [], Residue.bottle);
204 
205 alias PotionItem(sul.items.Item si, EffectInfo effect) = ConsumeableItem!(si, [effect], Residue.bottle);
206 
207 class ClearEffectsItem(sul.items.Item si, item_t residue) : SimpleItem!(si) {
208 	
209 	alias sul = si;
210 	
211 	public @safe this(E...)(E args) {
212 		super(args);
213 	}
214 	
215 	public final override pure nothrow @property @safe @nogc bool consumeable() {
216 		return true;
217 	}
218 
219 	public override Item onConsumed(Player player) {
220 		player.clearEffects();
221 		return player.world.items.get(residue);
222 	}
223 	
224 	alias slot this;
225 	
226 }