Skip to content
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

Feature/creature attacks #473

Merged
merged 8 commits into from
Oct 19, 2024
Merged
49 changes: 49 additions & 0 deletions src/toniarts/openkeeper/game/component/CreatureSpell.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2014-2024 OpenKeeper
*
* OpenKeeper is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenKeeper is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenKeeper. If not, see <http://www.gnu.org/licenses/>.
*/
package toniarts.openkeeper.game.component;

import com.simsilica.es.EntityId;

/**
* Creature spell. Note that since a creature can have multiple, these are
* supposed to be given to individual entities
*
* @author Toni Helenius <[email protected]>
*/
public class CreatureSpell extends Attack {

public short creatureSpellId;
public EntityId creatureId;

public CreatureSpell() {
// For serialization
}

public CreatureSpell(short creatureSpellId, EntityId creatureId, float rechargeTime, float range) {
super(rechargeTime, range);
this.creatureSpellId = creatureSpellId;
this.creatureId = creatureId;
}

public CreatureSpell(CreatureSpell attack, double attackStartTime) {
super(attack.rechargeTime, attack.range);
this.creatureSpellId = attack.creatureSpellId;
this.creatureId = attack.creatureId;
this.attactStartTime = attackStartTime;
Copy link
Preview

Copilot AI Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name 'attactStartTime' is misspelled. It should be 'attackStartTime'.

Suggested change
this.attactStartTime = attackStartTime;
this.attackStartTime = attackStartTime;

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
}

}
42 changes: 42 additions & 0 deletions src/toniarts/openkeeper/game/component/CreatureSpells.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2014-2024 OpenKeeper
*
* OpenKeeper is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenKeeper is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenKeeper. If not, see <http://www.gnu.org/licenses/>.
*/
package toniarts.openkeeper.game.component;

import com.simsilica.es.EntityComponent;
import com.simsilica.es.EntityId;
import java.util.List;

/**
* Creature spells. Minor infraction of ECS design having a list here. The idea
* is to link the actual spell entities to a creature. Give this component to a
* creature and you have yourself a search key to the actual spells.
*
* @author Toni Helenius <[email protected]>
*/
public class CreatureSpells implements EntityComponent {

public List<EntityId> creatureSpells;

public CreatureSpells() {
// For serialization
}

public CreatureSpells(List<EntityId> creatureSpells) {
this.creatureSpells = creatureSpells;
}

}
50 changes: 49 additions & 1 deletion src/toniarts/openkeeper/game/controller/CreaturesController.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
import java.lang.System.Logger.Level;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.stream.Collectors;
import toniarts.openkeeper.game.component.CreatureAi;
import toniarts.openkeeper.game.component.CreatureComponent;
import toniarts.openkeeper.game.component.CreatureEfficiency;
Expand All @@ -42,6 +44,8 @@
import toniarts.openkeeper.game.component.CreatureMeleeAttack;
import toniarts.openkeeper.game.component.CreatureMood;
import toniarts.openkeeper.game.component.CreatureSleep;
import toniarts.openkeeper.game.component.CreatureSpell;
import toniarts.openkeeper.game.component.CreatureSpells;
import toniarts.openkeeper.game.component.CreatureTortured;
import toniarts.openkeeper.game.component.CreatureViewState;
import toniarts.openkeeper.game.component.Death;
Expand Down Expand Up @@ -303,6 +307,7 @@ private EntityId loadCreature(EntityId entity, Creature creature, String name, S

// Set every attribute by the level of the created creature
setAttributesByLevel(creatureComponent, creatureExperience, healthComponent, goldComponent, sensesComponent, threatComponent, creatureMeleeAttack, regeneration);
setSpells(entity, creature, level);

entityData.setComponent(entity, creatureComponent);
entityData.setComponent(entity, creatureExperience);
Expand Down Expand Up @@ -445,6 +450,7 @@ public void levelUpCreature(EntityId entityId, int level, int experience) {

// Update stats
setAttributesByLevel(creatureComponent, creatureExperience, health, gold, senses, threat, creatureMeleeAttack, regeneration);
setSpells(entityId, kwdFile.getCreature(creatureComponent.creatureId), level);

// Set the new components to the entity
entityData.setComponents(entityId, creatureComponent, creatureExperience, health, gold, senses, threat, creatureMeleeAttack);
Expand Down Expand Up @@ -558,7 +564,7 @@ public ICreatureController createController(EntityId entityId) {
}

private ICreatureController createCreatureController(EntityId id, CreatureComponent creatureComponent) {
return new CreatureController(id, entityData, kwdFile.getCreature(creatureComponent.creatureId), gameController.getNavigationService(), gameController.getTaskManager(), gameTimer, gameSettings, this, gameController.getEntityLookupService(), mapController, levelInfo, gameController.getGameWorldController().getObjectsController());
return new CreatureController(id, entityData, kwdFile.getCreature(creatureComponent.creatureId), gameController.getNavigationService(), gameController.getTaskManager(), gameTimer, gameSettings, this, gameController.getEntityLookupService(), mapController, levelInfo, gameController.getGameWorldController().getObjectsController(), gameController.getGameWorldController().getShotsController());
}

@Override
Expand Down Expand Up @@ -587,4 +593,46 @@ public void turnCreatureIntoAnother(EntityId entityId, short playerId, short cre
loadCreature(newEntityId, kwdFile.getCreature(creatureId), creatureComponent.name, creatureComponent.bloodType, 100, 0, 1, SpawnType.PLACE, position.position.x, position.position.z, playerId, position.rotation, null, (short) 0, 0, trigger != null ? trigger.triggerId : null);
}

private void setSpells(EntityId entityId, Creature creature, int level) {

// Get the spells the creature should have
Map<Short, toniarts.openkeeper.tools.convert.map.CreatureSpell> availableSpells = creature.getSpells()
.stream()
.filter((spell) -> spell.getLevelAvailable() >= level)
.collect(Collectors.toMap((spell) -> spell.getCreatureSpellId(), (spell) -> kwdFile.getCreatureSpellById(spell.getCreatureSpellId())));
List<EntityId> creatureSpellIds = new ArrayList<>(availableSpells.size());
boolean spellsChanged = false;

// ... and compare it to the list we have
CreatureSpells creatureSpells = entityData.getComponent(entityId, CreatureSpells.class);
List<EntityId> ownedSpells = creatureSpells != null ? creatureSpells.creatureSpells : Collections.emptyList();

// Remove all spells we should not have
for (EntityId spellEntityId : ownedSpells) {
CreatureSpell spell = entityData.getComponent(spellEntityId, CreatureSpell.class);
if (availableSpells.containsKey(spell.creatureSpellId)) {
availableSpells.remove(spell.creatureSpellId);
creatureSpellIds.add(spellEntityId);
} else {
entityData.removeEntity(spellEntityId);
spellsChanged = true;
}
}

// All that is left is to add the remaining spells to the creature
for (toniarts.openkeeper.tools.convert.map.CreatureSpell spell : availableSpells.values()) {
EntityId spellEntity = entityData.createEntity();
entityData.setComponent(spellEntity, new CreatureSpell(spell.getCreatureSpellId(), entityId, spell.getRechargeTime(), spell.getRange()));
creatureSpellIds.add(spellEntity);
spellsChanged = true;
}

// Update the creature spell catalog
if (creatureSpellIds.isEmpty()) {
entityData.removeComponent(entityId, CreatureSpells.class);
} else if (spellsChanged) {
entityData.setComponent(entityId, new CreatureSpells(creatureSpellIds));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ public void castKeeperSpell(short keeperSpellId, EntityId target, Point tile, Ve
boolean spellUpgraded = researchableEntity.isUpgraded();
int shotData1 = spellUpgraded ? keeperSpell.getBonusShotData1() : keeperSpell.getShotData1();
int shotData2 = spellUpgraded ? keeperSpell.getBonusShotData2() : keeperSpell.getShotData2();
shotsController.createShot(keeperSpell.getShotTypeId(), shotData1, shotData2, playerId, position, target);
shotsController.createShot(keeperSpell.getShotTypeId(), shotData1, shotData2, playerId, WorldUtils.vector2fToVector3(position), target);
}

@Override
Expand Down Expand Up @@ -995,6 +995,11 @@ public ITrapsController getTrapsController() {
return trapsController;
}

@Override
public IShotsController getShotsController() {
return shotsController;
}

public void setEntityPositionLookup(IEntityPositionLookup entityPositionLookup) {
this.entityPositionLookup = entityPositionLookup;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ public interface IGameWorldController {
*/
public IObjectsController getObjectsController();

/**
* Get the shots controller
*
* @return shots controller
*/
public IShotsController getShotsController();

/**
* Cast a keeper spell on target / location
*
Expand Down
8 changes: 4 additions & 4 deletions src/toniarts/openkeeper/game/controller/IShotsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
package toniarts.openkeeper.game.controller;

import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.simsilica.es.EntityId;

/**
Expand All @@ -33,10 +33,10 @@ public interface IShotsController {
* @param shotTypeId shot type to create
* @param shotData1 arbitrary value, interpreted per shot
* @param shotData2 arbitrary value, interpreted per shot
* @param playerId owher of the shot
* @param position 2D coordinate of the shot origin
* @param playerId owner of the shot
* @param position coordinate of the shot origin
* @param target shot target, can be null
*/
public void createShot(short shotTypeId, int shotData1, int shotData2, short playerId, Vector2f position, EntityId target);
public void createShot(short shotTypeId, int shotData1, int shotData2, short playerId, Vector3f position, EntityId target);

}
7 changes: 4 additions & 3 deletions src/toniarts/openkeeper/game/controller/ShotsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
package toniarts.openkeeper.game.controller;

import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.simsilica.es.EntityData;
import com.simsilica.es.EntityId;
import java.lang.System.Logger;
Expand All @@ -29,6 +29,7 @@
import static toniarts.openkeeper.tools.convert.map.Shot.ProcessType.MODIFY_HEALTH;
import static toniarts.openkeeper.tools.convert.map.Shot.ProcessType.POSSESS_CREATURE;
import toniarts.openkeeper.tools.convert.map.Variable;
import toniarts.openkeeper.utils.WorldUtils;

/**
*
Expand Down Expand Up @@ -62,11 +63,11 @@ public ShotsController(KwdFile kwdFile, EntityData entityData, Map<Variable.Misc
}

@Override
public void createShot(short shotTypeId, int shotData1, int shotData2, short playerId, Vector2f position, EntityId target) {
public void createShot(short shotTypeId, int shotData1, int shotData2, short playerId, Vector3f position, EntityId target) {
Shot shot = kwdFile.getShotById(shotTypeId);
switch (shot.getProcessType()) {
case CREATE_CREATURE -> {
creaturesController.spawnCreature((short) shotData1, playerId, shotData2, position, ICreaturesController.SpawnType.CONJURE);
creaturesController.spawnCreature((short) shotData1, playerId, shotData2, WorldUtils.vector3fToVector2f(position), ICreaturesController.SpawnType.CONJURE);
}
case CREATE_OBJECT -> {
objectsController.loadObject((short) shotData1, playerId, position, 0);
Expand Down
Loading
Loading