mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-02-21 20:18:06 +07:00
Commander unit component
This commit is contained in:
parent
dce8b8faa1
commit
cb6920b8be
@ -0,0 +1,51 @@
|
||||
package mindustry.ai.formations;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
|
||||
public class DistanceAssignmentStrategy implements SlotAssignmentStrategy{
|
||||
private final Vec3 vec = new Vec3();
|
||||
private final FormationPattern form;
|
||||
|
||||
public DistanceAssignmentStrategy(FormationPattern form){
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSlotAssignments(Array<SlotAssignment> assignments){
|
||||
IntArray slots = IntArray.range(0, assignments.size);
|
||||
|
||||
for(SlotAssignment slot : assignments){
|
||||
int mindex = 0;
|
||||
float mcost = Float.MAX_VALUE;
|
||||
|
||||
for(int i = 0; i < slots.size; i++){
|
||||
float cost = cost(slot.member, slots.get(i));
|
||||
if(cost < mcost){
|
||||
mcost = cost;
|
||||
mindex = i;
|
||||
}
|
||||
}
|
||||
|
||||
slot.slotNumber = slots.get(mindex);
|
||||
slots.removeIndex(mindex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculateNumberOfSlots(Array<SlotAssignment> assignments){
|
||||
return assignments.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSlotAssignment(Array<SlotAssignment> assignments, int index){
|
||||
assignments.remove(index);
|
||||
}
|
||||
|
||||
float cost(FormationMember member, int slot){
|
||||
form.calculateSlotLocation(vec, slot);
|
||||
return Mathf.dst2(member.formationPos().x, member.formationPos().y, vec.x, vec.y);
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ import arc.struct.*;
|
||||
public class Formation{
|
||||
|
||||
/** A list of slots assignments. */
|
||||
Array<SlotAssignment> slotAssignments;
|
||||
public Array<SlotAssignment> slotAssignments;
|
||||
|
||||
/** The anchor point of this formation. */
|
||||
public Vec3 anchor;
|
||||
@ -81,6 +81,8 @@ public class Formation{
|
||||
|
||||
/** Updates the assignment of members to slots */
|
||||
public void updateSlotAssignments(){
|
||||
pattern.slots = slotAssignments.size;
|
||||
|
||||
// Apply the strategy to update slot assignments
|
||||
slotAssignmentStrategy.updateSlotAssignments(slotAssignments);
|
||||
|
||||
@ -114,6 +116,21 @@ public class Formation{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Much more efficient than adding a single member.
|
||||
* @return number of members added. */
|
||||
public int addMembers(Iterable<? extends FormationMember> members){
|
||||
int added = 0;
|
||||
for(FormationMember member : members){
|
||||
if(pattern.supportsSlots(slotAssignments.size + 1)){
|
||||
slotAssignments.add(new SlotAssignment(member, slotAssignments.size));
|
||||
added ++;
|
||||
}
|
||||
}
|
||||
|
||||
updateSlotAssignments();
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new member to the first available slot and updates slot assignments if the number of member is supported by the
|
||||
* current pattern.
|
||||
@ -121,13 +138,11 @@ public class Formation{
|
||||
* @return {@code false} if no more slots are available; {@code true} otherwise.
|
||||
*/
|
||||
public boolean addMember(FormationMember member){
|
||||
// Find out how many slots we have occupied
|
||||
int occupiedSlots = slotAssignments.size;
|
||||
|
||||
// Check if the pattern supports one more slot
|
||||
if(pattern.supportsSlots(occupiedSlots + 1)){
|
||||
if(pattern.supportsSlots(slotAssignments.size + 1)){
|
||||
// Add a new slot assignment
|
||||
slotAssignments.add(new SlotAssignment(member, occupiedSlots));
|
||||
slotAssignments.add(new SlotAssignment(member, slotAssignments.size));
|
||||
|
||||
// Update the slot assignments and return success
|
||||
updateSlotAssignments();
|
||||
|
@ -1,68 +1,46 @@
|
||||
package mindustry.ai.types;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.ai.formations.*;
|
||||
import mindustry.ai.formations.patterns.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class FormationAI extends AIController implements FormationMember{
|
||||
public @Nullable Unitc leader;
|
||||
public Unitc leader;
|
||||
|
||||
private transient Vec3 target = new Vec3();
|
||||
private Vec3 target = new Vec3();
|
||||
private Formation formation;
|
||||
|
||||
public FormationAI(@Nullable Unitc leader){
|
||||
public FormationAI(Unitc leader, Formation formation){
|
||||
this.leader = leader;
|
||||
this.formation = formation;
|
||||
}
|
||||
|
||||
static Formation formation;
|
||||
static Vec2 vec = new Vec2();
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
if(formation == null){
|
||||
Vec3 vec = new Vec3();
|
||||
|
||||
formation = new Formation(vec, new SquareFormation());
|
||||
Core.app.addListener(new ApplicationListener(){
|
||||
@Override
|
||||
public void update(){
|
||||
formation.updateSlots();
|
||||
vec.set(leader.x(), leader.y(), leader.rotation());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
formation.addMember(this);
|
||||
target.set(unit.x(), unit.y(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(leader != null){
|
||||
unit.controlWeapons(leader.isRotate(), leader.isShooting());
|
||||
// unit.moveAt(Tmp.v1.set(deltaX, deltaY).limit(unit.type().speed));
|
||||
if(leader.isShooting()){
|
||||
unit.aimLook(leader.aimX(), leader.aimY());
|
||||
}else{
|
||||
|
||||
unit.controlWeapons(leader.isRotate(), leader.isShooting());
|
||||
// unit.moveAt(Tmp.v1.set(deltaX, deltaY).limit(unit.type().speed));
|
||||
if(leader.isShooting()){
|
||||
unit.aimLook(leader.aimX(), leader.aimY());
|
||||
}else{
|
||||
|
||||
unit.lookAt(leader.rotation());
|
||||
if(!unit.vel().isZero(0.001f)){
|
||||
// unit.lookAt(unit.vel().angle());
|
||||
}
|
||||
unit.lookAt(leader.rotation());
|
||||
if(!unit.vel().isZero(0.001f)){
|
||||
// unit.lookAt(unit.vel().angle());
|
||||
}
|
||||
|
||||
|
||||
|
||||
unit.moveAt(vec.set(target).sub(unit).limit2(unit.type().speed));
|
||||
}
|
||||
|
||||
unit.moveAt(vec.set(target).sub(unit).limit2(unit.type().speed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFollowing(Playerc player){
|
||||
return leader == player.unit();
|
||||
public boolean isBeingControlled(Unitc player){
|
||||
return leader == player;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,7 +19,7 @@ public class GroundAI extends AIController{
|
||||
target = null;
|
||||
|
||||
//TODO this is hacky, cleanup
|
||||
if(unit instanceof Legsc){
|
||||
if(unit instanceof Legsc && unit.moving()){
|
||||
unit.lookAt(((Legsc)unit).baseRotation());
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public class UnitTypes implements ContentList{
|
||||
public static @EntityDef({Unitc.class, Builderc.class}) UnitType phantom, spirit;
|
||||
|
||||
//water
|
||||
public static @EntityDef({Unitc.class, WaterMovec.class}) UnitType vanguard;
|
||||
public static @EntityDef({Unitc.class, WaterMovec.class, Commanderc.class}) UnitType vanguard;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
|
83
core/src/mindustry/entities/def/CommanderComp.java
Normal file
83
core/src/mindustry/entities/def/CommanderComp.java
Normal file
@ -0,0 +1,83 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ai.formations.*;
|
||||
import mindustry.ai.types.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
/** A unit that can command other units. */
|
||||
@Component
|
||||
abstract class CommanderComp implements Unitc{
|
||||
private static final Array<FormationMember> members = new Array<>();
|
||||
|
||||
@Import float x, y, rotation;
|
||||
|
||||
transient @Nullable Formation formation;
|
||||
transient Array<Unitc> controlling = new Array<>();
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(formation != null){
|
||||
formation.anchor.set(x, y, rotation);
|
||||
formation.updateSlots();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
clearCommand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void killed(){
|
||||
clearCommand();
|
||||
}
|
||||
|
||||
//make sure to reset command state when the controller is switched
|
||||
@Override
|
||||
public void controller(UnitController unitController){
|
||||
clearCommand();
|
||||
}
|
||||
|
||||
void command(Formation formation, Array<Unitc> units){
|
||||
clearCommand();
|
||||
|
||||
controlling.addAll(units);
|
||||
for(Unitc unit : units){
|
||||
unit.controller(new FormationAI(this, formation));
|
||||
}
|
||||
this.formation = formation;
|
||||
|
||||
members.clear();
|
||||
for(Unitc u : units){
|
||||
members.add((FormationAI)u.controller());
|
||||
}
|
||||
|
||||
Log.info(members);
|
||||
Log.info(members.size);
|
||||
|
||||
|
||||
//TODO doesn't handle units that don't fit a formation
|
||||
formation.addMembers(members);
|
||||
}
|
||||
|
||||
boolean isCommanding(){
|
||||
return formation != null;
|
||||
}
|
||||
|
||||
void clearCommand(){
|
||||
//reset controlled units
|
||||
for(Unitc unit : controlling){
|
||||
if(unit.controller().isBeingControlled(this)){
|
||||
unit.controller(unit.type().createController());
|
||||
}
|
||||
}
|
||||
|
||||
controlling.clear();
|
||||
formation = null;
|
||||
}
|
||||
}
|
@ -59,8 +59,8 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controller(UnitController controller){
|
||||
this.controller = controller;
|
||||
public void controller(UnitController next){
|
||||
this.controller = next;
|
||||
if(controller.unit() != this) controller.unit(this);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,10 @@ abstract class VelComp implements Posc{
|
||||
vel.scl(1f - drag * Time.delta());
|
||||
}
|
||||
|
||||
boolean moving(){
|
||||
return !vel.isZero(0.001f);
|
||||
}
|
||||
|
||||
void move(float cx, float cy){
|
||||
x += cx;
|
||||
y += cy;
|
||||
|
@ -15,7 +15,7 @@ public interface UnitController{
|
||||
|
||||
}
|
||||
|
||||
default boolean isFollowing(Playerc player){
|
||||
default boolean isBeingControlled(Unitc player){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ import arc.scene.ui.layout.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ai.types.*;
|
||||
import mindustry.ai.formations.*;
|
||||
import mindustry.ai.formations.patterns.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
@ -182,13 +183,28 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
|
||||
//TODO this is for debugging, remove later
|
||||
if(Core.input.keyTap(KeyCode.q) && !player.dead()){
|
||||
Fx.commandSend.at(player);
|
||||
Units.nearby(player.team(), player.x(), player.y(), 200f, u -> {
|
||||
if(u.isAI()){
|
||||
u.controller(new FormationAI(player.unit()));
|
||||
}
|
||||
});
|
||||
if(Core.input.keyTap(KeyCode.g) && !player.dead() && player.unit() instanceof Commanderc){
|
||||
Commanderc commander = (Commanderc)player.unit();
|
||||
|
||||
if(commander.isCommanding()){
|
||||
commander.clearCommand();
|
||||
}else{
|
||||
|
||||
FormationPattern pattern = new SquareFormation();
|
||||
Formation formation = new Formation(new Vec3(player.x(), player.y(), player.unit().rotation()), pattern);
|
||||
formation.slotAssignmentStrategy = new DistanceAssignmentStrategy(pattern);
|
||||
|
||||
units.clear();
|
||||
|
||||
Fx.commandSend.at(player);
|
||||
Units.nearby(player.team(), player.x(), player.y(), 200f, u -> {
|
||||
if(u.isAI()){
|
||||
units.add(u);
|
||||
}
|
||||
});
|
||||
|
||||
commander.command(formation, units);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
final static int maxLength = 100;
|
||||
final static Vec2 stackTrns = new Vec2();
|
||||
final static Rect r1 = new Rect(), r2 = new Rect();
|
||||
final static Array<Unitc> units = new Array<>();
|
||||
/** Distance on the back from where items originate. */
|
||||
final static float backTrns = 3f;
|
||||
|
||||
|
@ -115,7 +115,7 @@ public class UnitType extends UnlockableContent{
|
||||
//region drawing
|
||||
|
||||
public void draw(Unitc unit){
|
||||
if(unit.controller().isFollowing(player)){
|
||||
if(unit.controller().isBeingControlled(player.unit())){
|
||||
drawControl(unit);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user