aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/tectech/mechanics/tesla/ITeslaConnectable.java
blob: 27fa829bad0fa5ebececd21c1eb4e8065d9ac8b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package tectech.mechanics.tesla;

import static java.lang.Math.sqrt;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import com.google.common.collect.Multimap;

import tectech.mechanics.spark.ThaumSpark;

public interface ITeslaConnectable extends ITeslaConnectableSimple {

    // Map with all Teslas in the same dimension and the distance to them //TODO Range
    Multimap<Integer, ITeslaConnectableSimple> getTeslaNodeMap();

    // ThaumCraft lighting coordinate pairs, so we can send them in bursts and save on lag
    HashSet<ThaumSpark> getSparkList();

    // -128 to -1 disables capability
    // 0 means any source or target
    // 1 to 127 must match on source and target or source/target must be 0
    byte getTeslaTransmissionCapability();

    // Transmission Range is typically 16+ in blocks
    int getTeslaTransmissionRange();

    boolean isOverdriveEnabled();

    int getTeslaEnergyLossPerBlock();

    float getTeslaOverdriveLossCoefficient();

    long getTeslaOutputVoltage();

    long getTeslaOutputCurrent();

    boolean teslaDrainEnergy(long teslaVoltageDrained);

    class TeslaUtil {

        private static final HashSet<ITeslaConnectableSimple> teslaSimpleNodeSet = new HashSet<>(); // Targets for power
                                                                                                    // transmission
        private static final HashSet<ITeslaConnectable> teslaNodeSet = new HashSet<>(); // Sources of power transmission
        private static final List<ITeslaConnectableSimple> scheduledRemove = new ArrayList<>();

        public static void teslaSimpleNodeSetAdd(ITeslaConnectableSimple target) {
            if (!teslaSimpleNodeSet.contains(target)) {
                teslaSimpleNodeSet.add(target);
                teslaNodeSet.forEach(origin -> addTargetToTeslaOrigin(target, origin));
            }
        }

        public static void teslaSimpleNodeSetRemove(ITeslaConnectableSimple target) {
            teslaSimpleNodeSet.remove(target);
            if (target instanceof ITeslaConnectable) teslaNodeSet.remove(target);
            teslaNodeSet.forEach(origin -> removeTargetFromTeslaOrigin(target, origin));
        }

        public static void teslaSimpleNodeSetRemoveScheduled(ITeslaConnectableSimple target) {
            scheduledRemove.add(target);
        }

        public static void housekeep() {
            for (ITeslaConnectableSimple e : scheduledRemove) {
                teslaSimpleNodeSet.remove(e);
            }
            scheduledRemove.clear();
        }

        private static void addTargetToTeslaOrigin(ITeslaConnectableSimple target, ITeslaConnectable origin) {
            if (origin.equals(target) || !origin.getTeslaDimension()
                .equals(target.getTeslaDimension())) {
                // Skip if looking at myself and skip if not in the same dimension
                // TODO, INTERDIM?
                return;
            } else if (origin.getTeslaTransmissionCapability() != 0 && origin.getTeslaReceptionCapability() != 0
                && origin.getTeslaTransmissionCapability() != origin.getTeslaReceptionCapability()) {
                    // Skip if incompatible
                    return;
                }
            // Range calc
            int distance = (int) sqrt(
                origin.getTeslaPosition()
                    .distanceSq(target.getTeslaPosition()));
            if (distance > origin.getTeslaTransmissionRange() * target.getTeslaReceptionCoefficient()) {
                // Skip if the range is too vast
                return;
            }
            origin.getTeslaNodeMap()
                .put(distance, target);
        }

        private static void removeTargetFromTeslaOrigin(ITeslaConnectableSimple target, ITeslaConnectable origin) {
            // Range calc TODO Remove duplicate?
            int distance = (int) sqrt(
                origin.getTeslaPosition()
                    .distanceSq(target.getTeslaPosition()));
            origin.getTeslaNodeMap()
                .remove(distance, target);
        }

        public static void generateTeslaNodeMap(ITeslaConnectable origin) {
            origin.getTeslaNodeMap()
                .clear();
            for (ITeslaConnectableSimple target : teslaSimpleNodeSet) {
                // Sanity checks
                if (target == null) {
                    // The Tesla Covers do not remove themselves from the list and this is the code that does
                    teslaSimpleNodeSet.remove(null);
                    continue;
                }
                addTargetToTeslaOrigin(target, origin);
            }
            teslaNodeSet.add(origin);
        }

        public static long powerTeslaNodeMap(ITeslaConnectable origin) {
            long remainingAmperes = origin.getTeslaOutputCurrent();
            boolean canSendPower = !origin.isTeslaReadyToReceive() && remainingAmperes > 0;

            if (canSendPower) {
                for (Map.Entry<Integer, ITeslaConnectableSimple> Rx : origin.getTeslaNodeMap()
                    .entries()) {
                    // Do we still have power left to send kind of check
                    if (origin.getTeslaStoredEnergy()
                        < (origin.isOverdriveEnabled() ? origin.getTeslaOutputVoltage() * 2
                            : origin.getTeslaOutputVoltage()))
                        break;
                    // Explicit words for the important fields
                    ITeslaConnectableSimple target = Rx.getValue();
                    int distance = Rx.getKey();
                    // Can our target receive energy?
                    if (!target.isTeslaReadyToReceive()) continue;

                    // Calculate the voltage output
                    long outputVoltageInjectable;
                    long outputVoltageConsumption;
                    if (origin.isOverdriveEnabled()) {
                        outputVoltageInjectable = origin.getTeslaOutputVoltage();
                        outputVoltageConsumption = origin.getTeslaOutputVoltage()
                            + ((long) distance * origin.getTeslaEnergyLossPerBlock())
                            + (long) Math
                                .round(origin.getTeslaOutputVoltage() * origin.getTeslaOverdriveLossCoefficient());
                    } else {
                        outputVoltageInjectable = origin.getTeslaOutputVoltage()
                            - ((long) distance * origin.getTeslaEnergyLossPerBlock());
                        outputVoltageConsumption = origin.getTeslaOutputVoltage();
                    }

                    // Break out of the loop if the cost is too high
                    // Since the next target will have an even higher cost, just quit now.
                    if (origin.getTeslaStoredEnergy() < outputVoltageConsumption) break;

                    // Now shove in as many packets as will fit~
                    while (canSendPower) {
                        if (target.teslaInjectEnergy(outputVoltageInjectable)) {
                            origin.teslaDrainEnergy(outputVoltageConsumption);
                            origin.getSparkList()
                                .add(
                                    new ThaumSpark(
                                        origin.getTeslaPosition(),
                                        target.getTeslaPosition(),
                                        origin.getTeslaDimension()));
                            remainingAmperes--;
                            // Update the can send power flag each time we send power
                            canSendPower = (origin.getTeslaStoredEnergy() < outputVoltageConsumption
                                || remainingAmperes > 0);
                        } else {
                            // Breaks out when I can't send anymore power
                            break;
                        }
                    }

                    // Break out if we can't send power anymore
                    if (!canSendPower) break;
                }
            }
            return origin.getTeslaOutputCurrent() - remainingAmperes;
        }
    }
}