15
15
*/
16
16
package org .terasology .world .block .items ;
17
17
18
+ import com .google .common .base .Preconditions ;
19
+ import com .google .common .primitives .SignedBytes ;
18
20
import org .terasology .entitySystem .Component ;
21
+ import org .terasology .entitySystem .ComponentContainer ;
19
22
import org .terasology .entitySystem .entity .EntityBuilder ;
20
23
import org .terasology .entitySystem .entity .EntityManager ;
21
24
import org .terasology .entitySystem .entity .EntityRef ;
22
- import org .terasology .entitySystem .prefab .Prefab ;
23
25
import org .terasology .logic .common .DisplayNameComponent ;
26
+ import org .terasology .logic .common .RetainComponentsComponent ;
24
27
import org .terasology .logic .inventory .ItemComponent ;
25
28
import org .terasology .rendering .logic .LightComponent ;
26
29
import org .terasology .world .block .family .BlockFamily ;
27
30
31
+ import java .util .Collections ;
28
32
import java .util .Optional ;
33
+ import java .util .Set ;
29
34
30
35
/**
36
+ * A factory to create new <em>block items</em> for a {@link BlockFamily}.
37
+ * <p>
38
+ * A <strong>block item</strong> is an entity guaranteed to have the following components.
39
+ * <ul>
40
+ * <li>{@link BlockItemComponent}. the back-reference to the block family</li>
41
+ * <li>{@link ItemComponent}. the item component with automatic stack id and quantity if block is stackable</li>
42
+ * <li>{@link DisplayNameComponent}. the block family archetype display name</li>
43
+ * </ul>
44
+ * Components on the block prefab (or the reference block entity) will only be retained if the component class is
45
+ * annotated with {@link AddToBlockBasedItem} or the component class is listed in {@link RetainComponentsComponent}.
46
+ * <p>
47
+ * The created entity (builder) is based on the block item base prefab ({@code engine:blockItemBase}).
48
+ *
49
+ * @see AddToBlockBasedItem
50
+ * @see RetainComponentsComponent
31
51
*/
32
52
public class BlockItemFactory {
33
- private EntityManager entityManager ;
53
+ private final EntityManager entityManager ;
34
54
55
+ /**
56
+ * Instantiate new block item factory with the given entity manager.
57
+ *
58
+ * @param entityManager entity manager to create new {@link EntityBuilder}s and copy components from
59
+ * reference entities or prefabs to the new block items
60
+ */
35
61
public BlockItemFactory (EntityManager entityManager ) {
36
62
this .entityManager = entityManager ;
37
63
}
38
64
65
+ /**
66
+ * Create a new block item for the given {@link BlockFamily}.
67
+ * <p>
68
+ * Attempts to resolve the corresponding block prefab to retrieve a list of potential components to add. The item
69
+ * quantity defaults to 1.
70
+ * <p>
71
+ * Use {@link #newBuilder(BlockFamily, int)} if you want to modify the block item entity's properties before it gets
72
+ * created.
73
+ *
74
+ * @param blockFamily block family to create the block item builder for
75
+ * @return the block item entity
76
+ */
39
77
public EntityRef newInstance (BlockFamily blockFamily ) {
40
78
return newInstance (blockFamily , 1 );
41
79
}
42
80
43
81
/**
44
- * Use this method instead of {@link #newInstance(BlockFamily)} to modify entity properties like the persistence
45
- * flag before it gets created.
82
+ * Create a new block item for the given {@link BlockFamily} and item quantity.
83
+ * <p>
84
+ * Attempts to resolve the corresponding block prefab to retrieve a list of potential components to add.
85
+ * <p>
86
+ * Use {@link #newBuilder(BlockFamily, int)} if you want to modify the block item entity's properties before it gets
87
+ * created.
46
88
*
47
- * @param blockFamily must not be null
89
+ * @param blockFamily block family to create the block item builder for
90
+ * @param quantity item quantity (see {@link ItemComponent#stackCount}); constrained to [0...128)
91
+ * @return the block item entity
48
92
*/
49
- public EntityBuilder newBuilder (BlockFamily blockFamily , int quantity ) {
50
- EntityBuilder builder = entityManager .newBuilder ("engine:blockItemBase" );
51
- if (blockFamily .getArchetypeBlock ().getLuminance () > 0 ) {
52
- builder .addComponent (new LightComponent ());
53
- }
54
-
55
- // Copy the components from block prefab into the block item
56
- Optional <Prefab > prefab = blockFamily .getArchetypeBlock ().getPrefab ();
57
- if (prefab .isPresent ()) {
58
- for (Component component : prefab .get ().iterateComponents ()) {
59
- if (component .getClass ().getAnnotation (AddToBlockBasedItem .class ) != null ) {
60
- builder .addComponent (entityManager .getComponentLibrary ().copy (component ));
61
- }
62
- }
63
- }
64
-
65
- DisplayNameComponent displayNameComponent = builder .getComponent (DisplayNameComponent .class );
66
- displayNameComponent .name = blockFamily .getDisplayName ();
67
-
68
- ItemComponent item = builder .getComponent (ItemComponent .class );
69
- if (blockFamily .getArchetypeBlock ().isStackable ()) {
70
- item .stackId = "block:" + blockFamily .getURI ().toString ();
71
- item .stackCount = (byte ) quantity ;
72
- }
73
-
74
- BlockItemComponent blockItem = builder .getComponent (BlockItemComponent .class );
75
- blockItem .blockFamily = blockFamily ;
76
-
77
- return builder ;
78
- }
79
-
80
93
public EntityRef newInstance (BlockFamily blockFamily , int quantity ) {
81
94
if (blockFamily == null ) {
82
95
return EntityRef .NULL ;
@@ -85,37 +98,145 @@ public EntityRef newInstance(BlockFamily blockFamily, int quantity) {
85
98
return builder .build ();
86
99
}
87
100
101
+ /**
102
+ * Create a new block item for the given {@link BlockFamily}, with {@code blockEntity} as reference entity to retain
103
+ * components from.
104
+ * <p>
105
+ * The item quantity defaults to 1.
106
+ * <p>
107
+ * Use {@link #newBuilder(BlockFamily, EntityRef, int)} if you want to modify the block item entity's properties
108
+ * before it gets created.
109
+ *
110
+ * @param blockFamily block family to create the block item builder for
111
+ * @param blockEntity reference block entity to retain components from
112
+ * @return the block item entity
113
+ */
88
114
public EntityRef newInstance (BlockFamily blockFamily , EntityRef blockEntity ) {
89
115
if (blockFamily == null ) {
90
116
return EntityRef .NULL ;
91
117
}
92
118
119
+ return createBuilder (blockFamily , blockEntity , (byte ) 1 ).build ();
120
+ }
121
+
122
+ /**
123
+ * Create a new block item builder for the given {@link BlockFamily} and item quantity.
124
+ * <p>
125
+ * Attempts to resolve the corresponding block prefab to retrieve a list of potential components to add.
126
+ * <p>
127
+ * Use this method if you want to modify the block item entity's properties before it gets created.
128
+ *
129
+ * @param blockFamily block family to create the block item builder for
130
+ * @param quantity item quantity (see {@link ItemComponent#stackCount}); constrained to [0...128)
131
+ * @return a pre-populated entity builder for a block item entity
132
+ */
133
+ public EntityBuilder newBuilder (BlockFamily blockFamily , int quantity ) {
134
+ final ComponentContainer components =
135
+ blockFamily .getArchetypeBlock ().getPrefab ()
136
+ .map (p -> ((ComponentContainer ) p ))
137
+ .orElse (EntityRef .NULL );
138
+
139
+ return createBuilder (blockFamily , components , SignedBytes .saturatedCast (quantity ));
140
+ }
141
+
142
+ /**
143
+ * Create a new block item builder for the given {@link BlockFamily} and item quantity, with {@code blockEntity} as
144
+ * reference entity to retain components from.
145
+ * <p>
146
+ * Use this method if you want to modify the block item entity's properties before it gets created.
147
+ *
148
+ * @param blockFamily block family to create the block item builder for
149
+ * @param blockEntity reference block entity to retain components from
150
+ * @param quantity item quantity (see {@link ItemComponent#stackCount}); constrained to [0...128)
151
+ * @return a pre-populated entity builder for a block item entity
152
+ */
153
+ public EntityBuilder newBuilder (BlockFamily blockFamily , EntityRef blockEntity , int quantity ) {
154
+ return createBuilder (blockFamily , blockEntity , SignedBytes .saturatedCast (quantity ));
155
+ }
156
+
157
+ /**
158
+ * Create a new block item builder for the given {@link BlockFamily}.
159
+ *
160
+ * @param blockFamily block family to create the block item builder for
161
+ * @param components potential components to add to the block item entity
162
+ * @param quantity item quantity (see {@link ItemComponent#stackCount})
163
+ * @return a pre-populated entity builder for a block item entity
164
+ */
165
+ private EntityBuilder createBuilder (BlockFamily blockFamily , ComponentContainer components , byte quantity ) {
166
+ Preconditions .checkNotNull (blockFamily , "Block family must not be null when creating block item" );
167
+
93
168
EntityBuilder builder = entityManager .newBuilder ("engine:blockItemBase" );
94
- if (blockFamily .getArchetypeBlock ().getLuminance () > 0 ) {
95
- builder .addComponent (new LightComponent ());
96
- }
169
+ addComponents (builder , components );
97
170
98
- // Copy the components from block prefab into the block item
99
- for (Component component : blockEntity .iterateComponents ()) {
100
- if (component .getClass ().getAnnotation (AddToBlockBasedItem .class ) != null ) {
171
+ adjustLightComponent (builder , blockFamily );
172
+ adjustDisplayNameComponent (builder , blockFamily );
173
+ adjustItemComponent (builder , blockFamily , quantity );
174
+ adjustBlockItemComponent (builder , blockFamily );
175
+
176
+ return builder ;
177
+ }
178
+
179
+ /**
180
+ * Mutate the builder to add components from the given component container.
181
+ * <p>
182
+ * A component is only added to the builder if at least one of the following conditions holds:
183
+ * <ul>
184
+ * <li>the component class is annotated with {@link AddToBlockBasedItem}</li>
185
+ * <li>the container contains a {@link RetainComponentsComponent} and the component class is listed as
186
+ * retained</li>
187
+ * </ul>
188
+ * <p>
189
+ * Components that should be added to the block item entity are <emph>copied</emph>.
190
+ *
191
+ * @param builder the builder to add the components to
192
+ * @param components the container with potential components to add
193
+ */
194
+ private void addComponents (EntityBuilder builder , ComponentContainer components ) {
195
+ final Set <Class <? extends Component >> retainComponents =
196
+ Optional .ofNullable (components .getComponent (RetainComponentsComponent .class ))
197
+ .map (retain -> retain .components )
198
+ .orElse (Collections .emptySet ());
199
+
200
+ for (Component component : components .iterateComponents ()) {
201
+ if (keepByAnnotation (component ) || retainComponents .contains (component .getClass ())) {
101
202
builder .addComponent (entityManager .getComponentLibrary ().copy (component ));
102
203
}
103
204
}
205
+ }
104
206
105
- DisplayNameComponent displayNameComponent = builder .getComponent (DisplayNameComponent .class );
106
- if (displayNameComponent != null ) {
107
- displayNameComponent .name = blockFamily .getDisplayName ();
108
- }
207
+ private boolean keepByAnnotation (Component component ) {
208
+ return component .getClass ().getAnnotation (AddToBlockBasedItem .class ) != null ;
209
+ }
109
210
110
- ItemComponent item = builder . getComponent ( ItemComponent . class );
111
- if ( blockFamily . getArchetypeBlock (). isStackable ()) {
112
- item . stackId = "block:" + blockFamily . getURI (). toString ();
113
- item . stackCount = ( byte ) 1 ;
211
+ private void adjustLightComponent ( EntityBuilder builder , BlockFamily blockFamily ) {
212
+ //TODO: set properties of the LightComponent based on the archetype block?
213
+ if ( blockFamily . getArchetypeBlock (). getLuminance () > 0 && ! builder . hasComponent ( LightComponent . class )) {
214
+ builder . addComponent ( new LightComponent ()) ;
114
215
}
216
+ }
115
217
116
- BlockItemComponent blockItem = builder .getComponent (BlockItemComponent .class );
117
- blockItem .blockFamily = blockFamily ;
218
+ private void adjustDisplayNameComponent (EntityBuilder builder , BlockFamily blockFamily ) {
219
+ builder .updateComponent (DisplayNameComponent .class , displayName -> {
220
+ displayName .name = blockFamily .getDisplayName ();
221
+ return displayName ;
222
+ });
223
+ }
118
224
119
- return builder .build ();
225
+ private void adjustItemComponent (EntityBuilder builder , BlockFamily blockFamily , byte quantity ) {
226
+ builder .updateComponent (ItemComponent .class , item -> {
227
+ if (blockFamily .getArchetypeBlock ().isStackable ()) {
228
+ item .stackId = "block:" + blockFamily .getURI ().toString ();
229
+ //TODO: quantity may be greater than item.maxStackSize
230
+ item .stackCount = quantity ;
231
+ }
232
+ return item ;
233
+ });
234
+ }
235
+
236
+ private void adjustBlockItemComponent (EntityBuilder builder , BlockFamily blockFamily ) {
237
+ builder .updateComponent (BlockItemComponent .class , blockItem -> {
238
+ blockItem .blockFamily = blockFamily ;
239
+ return blockItem ;
240
+ });
120
241
}
121
242
}
0 commit comments