-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add HealthComponent #1
Changes from all commits
274360a
f8f242a
93a9aec
f341472
211799b
84ade1c
9b6f581
a4d033f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/* | ||
* Copyright 2019 MovingBlocks | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.terasology.logic.health; | ||
|
||
import gnu.trove.iterator.TFloatIterator; | ||
import gnu.trove.iterator.TIntIterator; | ||
import gnu.trove.list.TFloatList; | ||
import gnu.trove.list.TIntList; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.terasology.entitySystem.entity.EntityManager; | ||
import org.terasology.entitySystem.entity.EntityRef; | ||
import org.terasology.entitySystem.event.ReceiveEvent; | ||
import org.terasology.entitySystem.systems.BaseComponentSystem; | ||
import org.terasology.entitySystem.systems.RegisterMode; | ||
import org.terasology.entitySystem.systems.RegisterSystem; | ||
import org.terasology.entitySystem.systems.UpdateSubscriberSystem; | ||
import org.terasology.logic.health.event.BeforeHealEvent; | ||
import org.terasology.logic.health.event.DoHealEvent; | ||
import org.terasology.logic.health.event.FullHealthEvent; | ||
import org.terasology.logic.health.event.OnHealedEvent; | ||
import org.terasology.logic.players.event.OnPlayerRespawnedEvent; | ||
import org.terasology.math.TeraMath; | ||
import org.terasology.registry.In; | ||
|
||
/** | ||
* This system takes care of healing of entities with HealthComponent. | ||
* To increase the health of an entity, send DoHealEvent | ||
* | ||
* Logic flow for healing: | ||
* - DoHealEvent | ||
* - BeforeHealEvent | ||
* - (HealthComponent saved) | ||
* - OnHealedEvent | ||
* - FullHealthEvent (if at full health) | ||
*/ | ||
@RegisterSystem(RegisterMode.AUTHORITY) | ||
public class HealthAuthoritySystem extends BaseComponentSystem implements UpdateSubscriberSystem { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(HealthAuthoritySystem.class); | ||
|
||
@In | ||
private EntityManager entityManager; | ||
|
||
@In | ||
private org.terasology.engine.Time time; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason for having this fully qualified? |
||
|
||
@Override | ||
public void update(float delta) { | ||
for (EntityRef entity : entityManager.getEntitiesWith(HealthComponent.class)) { | ||
HealthComponent health = entity.getComponent(HealthComponent.class); | ||
if (health.currentHealth <= 0) { | ||
continue; | ||
} | ||
|
||
if (health.currentHealth == health.maxHealth || health.regenRate == 0) { | ||
continue; | ||
} | ||
|
||
int healAmount = 0; | ||
healAmount = regenerateHealth(health, healAmount); | ||
|
||
checkHealed(entity, health, healAmount); | ||
} | ||
} | ||
|
||
private int regenerateHealth(HealthComponent health, int healAmount) { | ||
int newHeal = healAmount; | ||
while (time.getGameTimeInMs() >= health.nextRegenTick) { | ||
newHeal++; | ||
health.nextRegenTick = health.nextRegenTick + (long) (1000 / health.regenRate); | ||
} | ||
return newHeal; | ||
} | ||
|
||
private void checkHealed(EntityRef entity, HealthComponent health, int healAmount) { | ||
if (healAmount > 0) { | ||
checkHeal(entity, healAmount, entity); | ||
entity.saveComponent(health); | ||
} | ||
} | ||
|
||
private void checkHeal(EntityRef entity, int healAmount, EntityRef instigator) { | ||
BeforeHealEvent beforeHeal = entity.send(new BeforeHealEvent(healAmount, instigator)); | ||
if (!beforeHeal.isConsumed()) { | ||
int modifiedAmount = calculateTotal(beforeHeal.getBaseHeal(), beforeHeal.getMultipliers(), beforeHeal.getModifiers()); | ||
if (modifiedAmount > 0) { | ||
doHeal(entity, modifiedAmount, instigator); | ||
} else if (modifiedAmount < 0) { | ||
// TODO: Add damage events | ||
// doDamage(entity, -modifiedAmount, EngineDamageTypes.HEALING.get(), instigator, EntityRef.NULL); | ||
} | ||
} | ||
} | ||
|
||
private void doHeal(EntityRef entity, int healAmount, EntityRef instigator) { | ||
HealthComponent health = entity.getComponent(HealthComponent.class); | ||
if (health != null) { | ||
int healedAmount = Math.min(health.currentHealth + healAmount, health.maxHealth) - health.currentHealth; | ||
health.currentHealth += healedAmount; | ||
entity.saveComponent(health); | ||
entity.send(new OnHealedEvent(healAmount, healedAmount, instigator)); | ||
if (health.currentHealth == health.maxHealth) { | ||
entity.send(new FullHealthEvent(instigator)); | ||
} | ||
} | ||
} | ||
|
||
private int calculateTotal(int base, TFloatList multipliers, TIntList modifiers) { | ||
// For now, add all modifiers and multiply by all multipliers. Negative modifiers cap to zero, but negative | ||
// multipliers remain (so damage can be flipped to healing) | ||
|
||
float total = base; | ||
TIntIterator modifierIter = modifiers.iterator(); | ||
while (modifierIter.hasNext()) { | ||
total += modifierIter.next(); | ||
} | ||
total = Math.max(0, total); | ||
if (total == 0) { | ||
return 0; | ||
} | ||
TFloatIterator multiplierIter = multipliers.iterator(); | ||
while (multiplierIter.hasNext()) { | ||
total *= multiplierIter.next(); | ||
} | ||
return TeraMath.floorToInt(total); | ||
|
||
} | ||
|
||
/** Handles DoHeal event to increase health of entity */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One-liner javadoc is good for simple fields, but methods should always use the expanded version including doc for parameters etc :-) |
||
@ReceiveEvent(components = {HealthComponent.class}) | ||
public void onHeal(DoHealEvent event, EntityRef entity) { | ||
checkHeal(entity, event.getAmount(), event.getInstigator()); | ||
} | ||
|
||
/** Resets the health of player to maxHealth on re-spawn. */ | ||
@ReceiveEvent | ||
public void onRespawn(OnPlayerRespawnedEvent event, EntityRef entity, HealthComponent healthComponent) { | ||
healthComponent.currentHealth = healthComponent.maxHealth; | ||
entity.saveComponent(healthComponent); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
* Copyright 2019 MovingBlocks | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.terasology.logic.health; | ||
|
||
import org.terasology.entitySystem.Component; | ||
import org.terasology.network.Replicate; | ||
import org.terasology.rendering.nui.properties.TextField; | ||
|
||
/** | ||
* Provides Health to entity attached with HealthComponent. Contains the parameters | ||
* required for all health related events. | ||
*/ | ||
public class HealthComponent implements Component { | ||
|
||
/** Maximum allowed health, capped to this if exceeding this value. */ | ||
@Replicate | ||
public int maxHealth = 20; | ||
|
||
/** Amount of health restored in each regeneration tick. */ | ||
@Replicate | ||
public float regenRate; | ||
|
||
/** Time delay before regeneration starts. */ | ||
@Replicate | ||
public float waitBeforeRegen; | ||
|
||
/** Falling speed threshold above which damage is inflicted to entity. */ | ||
@Replicate | ||
public float fallingDamageSpeedThreshold = 20; | ||
|
||
/** Horizontal speed threshold above which damage is inflicted to entity. */ | ||
@Replicate | ||
public float horizontalDamageSpeedThreshold = 20; | ||
|
||
/** The multiplier used to calculate damage when horizontal or vertical threshold is crossed. */ | ||
@Replicate | ||
public float excessSpeedDamageMultiplier = 10f; | ||
|
||
|
||
/** The current value of health. */ | ||
@Replicate | ||
@TextField | ||
public int currentHealth = 20; | ||
|
||
/** Next tick time that will trigger regeneration. */ | ||
public long nextRegenTick; | ||
|
||
/** Used to send Destroy event when health breaches zero. */ | ||
public boolean destroyEntityOnNoHealth; | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright 2019 MovingBlocks | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.terasology.logic.health.event; | ||
|
||
import gnu.trove.list.TFloatList; | ||
import gnu.trove.list.TIntList; | ||
import gnu.trove.list.array.TFloatArrayList; | ||
import gnu.trove.list.array.TIntArrayList; | ||
import org.terasology.entitySystem.entity.EntityRef; | ||
import org.terasology.entitySystem.event.ConsumableEvent; | ||
|
||
/** | ||
* This event is sent to an entity to allow modification and cancellation of healing. | ||
* | ||
*/ | ||
public class BeforeHealEvent implements ConsumableEvent { | ||
private int baseHeal; | ||
private EntityRef instigator; | ||
|
||
private boolean consumed; | ||
private TFloatList multipliers = new TFloatArrayList(); | ||
private TIntList modifiers = new TIntArrayList(); | ||
|
||
public BeforeHealEvent(int amount, EntityRef instigator) { | ||
this.baseHeal = amount; | ||
this.instigator = instigator; | ||
} | ||
|
||
public int getBaseHeal() { | ||
return baseHeal; | ||
} | ||
|
||
public EntityRef getInstigator() { | ||
return instigator; | ||
} | ||
|
||
public TFloatList getMultipliers() { | ||
return multipliers; | ||
} | ||
|
||
public TIntList getModifiers() { | ||
return modifiers; | ||
} | ||
|
||
public void multiply(float amount) { | ||
multipliers.add(amount); | ||
} | ||
|
||
public void add(int amount) { | ||
modifiers.add(amount); | ||
} | ||
|
||
public void subtract(int amount) { | ||
modifiers.add(-amount); | ||
} | ||
|
||
@Override | ||
public boolean isConsumed() { | ||
return consumed; | ||
} | ||
|
||
@Override | ||
public void consume() { | ||
consumed = true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* Copyright 2019 MovingBlocks | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.terasology.logic.health.event; | ||
|
||
import org.terasology.entitySystem.entity.EntityRef; | ||
import org.terasology.entitySystem.event.Event; | ||
|
||
/** | ||
* Send this event to an entity to heal it. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "to heal it ..." - fully? Or could a negative number be hurtful instead? Maybe that's covered in the System There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A negative value is supposed to cause damage, but not working at present. Will update the Javadoc when the issue is solved. |
||
* | ||
*/ | ||
public class DoHealEvent implements Event { | ||
private int amount; | ||
private EntityRef instigator; | ||
|
||
public DoHealEvent(int amount) { | ||
this(amount, EntityRef.NULL); | ||
} | ||
|
||
public DoHealEvent(int amount, EntityRef instigator) { | ||
this.amount = amount; | ||
this.instigator = instigator; | ||
} | ||
|
||
public int getAmount() { | ||
return amount; | ||
} | ||
|
||
public EntityRef getInstigator() { | ||
return instigator; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* Copyright 2019 MovingBlocks | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.terasology.logic.health.event; | ||
|
||
import org.terasology.entitySystem.entity.EntityRef; | ||
import org.terasology.entitySystem.event.Event; | ||
|
||
/** | ||
* Event sent upon an entity reaching full health if previously on less than full health. | ||
* | ||
*/ | ||
public class FullHealthEvent implements Event { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might this be more descriptive as Would also put it more in line with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renaming it as |
||
private EntityRef instigator; | ||
|
||
public FullHealthEvent(EntityRef instigator) { | ||
this.instigator = instigator; | ||
} | ||
|
||
public EntityRef getInstigator() { | ||
return instigator; | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to toss an author name in there :-)