1
0
mirror of https://github.com/Anuken/Mindustry.git synced 2024-09-20 12:58:38 +03:00

Added basic ground AI

This commit is contained in:
Anuken 2020-03-21 10:49:38 -04:00
parent d91ff744f2
commit d673167477
23 changed files with 211 additions and 71 deletions

View File

@ -0,0 +1,7 @@
package mindustry.ai.types;
import mindustry.entities.units.*;
public class FlyingAI extends AIController{
}

View File

@ -0,0 +1,100 @@
package mindustry.ai.types;
import arc.util.*;
import mindustry.ai.Pathfinder.*;
import mindustry.entities.*;
import mindustry.entities.units.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.world.*;
import static mindustry.Vars.pathfinder;
public class GroundAI extends AIController{
@Override
public void behavior(){
//attack
Tilec core = unit.closestEnemyCore();
if(core == null) return;
float dst = unit.dst(core);
if(dst < unit.range() / 1.1f){
target = core;
}
if(dst > unit.range() * 0.5f){
moveToCore(PathTarget.enemyCores);
}
boolean rotate = false, shoot = false;
if(!Units.invalidateTarget(target, unit, unit.range())){
rotate = true;
shoot = unit.within(target, unit.range());
if(unit.type().hasWeapons()){
unit.aimLook(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed));
}
}
unit.controlWeapons(rotate, shoot);
}
@Override
public void targeting(){
if(Units.invalidateTarget(target, unit.team(), unit.x(), unit.y(), Float.MAX_VALUE)){
target = null;
//TODO this is hacky, cleanup
if(unit instanceof Legsc){
unit.lookAt(((Legsc)unit).baseRotation());
}
}
if(retarget()){
targetClosest();
}
}
protected void moveToCore(PathTarget path){
Tile tile = unit.tileOn();
if(tile == null) return;
Tile targetTile = pathfinder.getTargetTile(tile, unit.team(), path);
if(tile == targetTile) return;
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed * Time.delta()));
}
protected void moveAwayFromCore(){
Team enemy = null;
for(Team team : unit.team().enemies()){
if(team.active()){
enemy = team;
break;
}
}
if(enemy == null){
for(Team team : unit.team().enemies()){
enemy = team;
break;
}
}
if(enemy == null) return;
Tile tile = unit.tileOn();
if(tile == null) return;
Tile targetTile = pathfinder.getTargetTile(tile, enemy, PathTarget.enemyCores);
Tilec core = unit.closestCore();
if(tile == targetTile || core == null || unit.within(core, 120f)) return;
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed * Time.delta()));
}
}

View File

@ -2,7 +2,6 @@ package mindustry.content;
import mindustry.ctype.*;
import mindustry.game.Objectives.*;
import mindustry.game.*;
import mindustry.maps.generators.*;
import mindustry.type.*;
@ -12,7 +11,7 @@ import static mindustry.content.Planets.starter;
import static mindustry.type.ItemStack.list;
public class Zones implements ContentList{
public static Zone
public static SectorPreset
groundZero, desertWastes,
craters, frozenForest, ruinousShores, stainedMountains, tarFields, fungalPass,
saltFlats, overgrowth, impact0078, crags,
@ -21,7 +20,7 @@ public class Zones implements ContentList{
@Override
public void load(){
groundZero = new Zone("groundZero", starter, new FileMapGenerator("groundZero")){{
groundZero = new SectorPreset("groundZero", starter, new FileMapGenerator("groundZero")){{
baseLaunchCost = list(copper, -60);
startingItems = list(copper, 60);
alwaysUnlocked = true;
@ -31,6 +30,7 @@ public class Zones implements ContentList{
}};
//TODO remove
/*
desertWastes = new Zone("desertWastes", starter, new FileMapGenerator("groundZero")){{
startingItems = list(copper, 120);
conditionWave = 20;
@ -80,9 +80,9 @@ public class Zones implements ContentList{
new ZoneWave(groundZero, 20),
new Unlock(Blocks.combustionGenerator)
);
}};
}};*/
saltFlats = new Zone("saltFlats", starter, new FileMapGenerator("saltFlats")){{
saltFlats = new SectorPreset("saltFlats", starter, new FileMapGenerator("saltFlats")){{
startingItems = list(copper, 200, Items.silicon, 200, lead, 200);
loadout = Loadouts.basicFoundation;
conditionWave = 10;
@ -98,7 +98,7 @@ public class Zones implements ContentList{
);
}};
frozenForest = new Zone("frozenForest", starter, new FileMapGenerator("frozenForest")){{
frozenForest = new SectorPreset("frozenForest", starter, new FileMapGenerator("frozenForest")){{
loadout = Loadouts.basicFoundation;
startingItems = list(copper, 250);
conditionWave = 10;
@ -110,7 +110,7 @@ public class Zones implements ContentList{
);
}};
craters = new Zone("craters", starter, new FileMapGenerator("craters")){{
craters = new SectorPreset("craters", starter, new FileMapGenerator("craters")){{
startingItems = list(copper, 100);
conditionWave = 10;
resources = with(copper, lead, coal, sand, scrap);
@ -121,7 +121,7 @@ public class Zones implements ContentList{
);
}};
ruinousShores = new Zone("ruinousShores", starter, new FileMapGenerator("ruinousShores")){{
ruinousShores = new SectorPreset("ruinousShores", starter, new FileMapGenerator("ruinousShores")){{
loadout = Loadouts.basicFoundation;
startingItems = list(copper, 140, lead, 50);
conditionWave = 20;
@ -137,7 +137,7 @@ public class Zones implements ContentList{
);
}};
stainedMountains = new Zone("stainedMountains", starter, new FileMapGenerator("stainedMountains")){{
stainedMountains = new SectorPreset("stainedMountains", starter, new FileMapGenerator("stainedMountains")){{
loadout = Loadouts.basicFoundation;
startingItems = list(copper, 200, lead, 50);
conditionWave = 10;
@ -151,7 +151,7 @@ public class Zones implements ContentList{
);
}};
fungalPass = new Zone("fungalPass", starter, new FileMapGenerator("fungalPass")){{
fungalPass = new SectorPreset("fungalPass", starter, new FileMapGenerator("fungalPass")){{
startingItems = list(copper, 250, lead, 250, Items.metaglass, 100, Items.graphite, 100);
resources = with(copper, lead, coal, titanium, sand);
configureObjective = new Launched(this);
@ -164,7 +164,7 @@ public class Zones implements ContentList{
);
}};
overgrowth = new Zone("overgrowth", starter, new FileMapGenerator("overgrowth")){{
overgrowth = new SectorPreset("overgrowth", starter, new FileMapGenerator("overgrowth")){{
startingItems = list(copper, 1500, lead, 1000, Items.silicon, 500, Items.metaglass, 250);
conditionWave = 12;
launchPeriod = 4;
@ -181,7 +181,7 @@ public class Zones implements ContentList{
);
}};
tarFields = new Zone("tarFields", starter, new FileMapGenerator("tarFields")){{
tarFields = new SectorPreset("tarFields", starter, new FileMapGenerator("tarFields")){{
loadout = Loadouts.basicFoundation;
startingItems = list(copper, 250, lead, 100);
conditionWave = 15;
@ -195,7 +195,7 @@ public class Zones implements ContentList{
);
}};
desolateRift = new Zone("desolateRift", starter, new FileMapGenerator("desolateRift")){{
desolateRift = new SectorPreset("desolateRift", starter, new FileMapGenerator("desolateRift")){{
loadout = Loadouts.basicNucleus;
startingItems = list(copper, 1000, lead, 1000, Items.graphite, 250, titanium, 250, Items.silicon, 250);
conditionWave = 3;
@ -220,7 +220,7 @@ public class Zones implements ContentList{
resources = Array.with(Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand};
}};*/
nuclearComplex = new Zone("nuclearComplex", starter, new FileMapGenerator("nuclearProductionComplex")){{
nuclearComplex = new SectorPreset("nuclearComplex", starter, new FileMapGenerator("nuclearProductionComplex")){{
loadout = Loadouts.basicNucleus;
startingItems = list(copper, 1250, lead, 1500, Items.silicon, 400, Items.metaglass, 250);
conditionWave = 30;

View File

@ -265,7 +265,7 @@ public class ContentLoader{
return (BulletType)getByID(ContentType.bullet, id);
}
public Array<Zone> zones(){
public Array<SectorPreset> zones(){
return getBy(ContentType.zone);
}

View File

@ -36,10 +36,6 @@ abstract class FlyingComp implements Posc, Velc, Healthc, Hitboxc{
y += Mathf.cos(Time.time() + id() * 99, 25f, 0.05f) * Time.delta() * elevation;
}
void moveAt(Vec2 vector){
moveAt(vector, 1f);
}
void moveAt(Vec2 vector, float acceleration){
Vec2 t = Tmp.v3.set(vector).scl(floorSpeedMultiplier()); //target vector
Tmp.v1.set(t).sub(vel).limit(acceleration * vector.len()); //delta vector

View File

@ -13,8 +13,11 @@ abstract class TeamComp implements Posc{
Team team = Team.derelict;
public @Nullable
Tilec closestCore(){
public @Nullable Tilec closestCore(){
return state.teams.closestCore(x, y, team);
}
public @Nullable Tilec closestEnemyCore(){
return state.teams.closestEnemyCore(x, y, team);
}
}

View File

@ -25,6 +25,15 @@ abstract class UnitComp implements Healthc, Velc, Statusc, Teamc, Itemsc, Hitbox
private UnitController controller;
private UnitType type;
public void moveAt(Vec2 vector){
moveAt(vector, type.accel);
}
public void aimLook(Position pos){
aim(pos);
lookAt(pos);
}
@Override
public float clipSize(){
return type.region.getWidth() * 2f;

View File

@ -21,11 +21,14 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc{
/** weapon mount array, never null */
@ReadOnly WeaponMount[] mounts = {};
@ReadOnly float range;
void setupWeapons(UnitType def){
mounts = new WeaponMount[def.weapons.size];
range = 0f;
for(int i = 0; i < mounts.length; i++){
mounts[i] = new WeaponMount(def.weapons.get(i));
range = Math.max(range, def.weapons.get(i).bullet.range());
}
}

View File

@ -1,12 +1,43 @@
package mindustry.entities.units;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.entities.*;
import mindustry.gen.*;
public class AIController implements UnitController{
protected Unitc unit;
protected static final Vec2 vec = new Vec2();
protected static final int timerTarget = 0;
float rot = Mathf.random(360f);
protected Unitc unit;
protected Teamc target;
protected Interval timer = new Interval(4);
@Override
public void update(){
targeting();
behavior();
}
protected boolean retarget(){
return timer.get(timerTarget, 20);
}
protected void targetClosest(){
Teamc newTarget = Units.closestTarget(unit.team(), unit.x(), unit.y(), Math.max(unit.range(), unit.type().range), u -> unit.type().targetAir || !u.isFlying());
if(newTarget != null){
target = newTarget;
}
}
public void targeting(){
}
public void behavior(){
}
@Override
public void unit(Unitc unit){
@ -17,15 +48,4 @@ public class AIController implements UnitController{
public Unitc unit(){
return unit;
}
@Override
public void update(){
//TODO implement
//rot += Mathf.range(3f) * Time.delta();
//unit.moveAt(Tmp.v1.trns(rot, unit.type().speed));
//if(!unit.vel().isZero()){
// unit.lookAt(unit.vel().angle());
//}
}
}

View File

@ -72,10 +72,10 @@ public class EventType{
/** Called when a zone's requirements are met. */
public static class ZoneRequireCompleteEvent{
public final Zone zoneMet, zoneForMet;
public final SectorPreset zoneMet, zoneForMet;
public final Objective objective;
public ZoneRequireCompleteEvent(Zone zoneMet, Zone zoneForMet, Objective objective){
public ZoneRequireCompleteEvent(SectorPreset zoneMet, SectorPreset zoneForMet, Objective objective){
this.zoneMet = zoneMet;
this.zoneForMet = zoneForMet;
this.objective = objective;
@ -84,9 +84,9 @@ public class EventType{
/** Called when a zone's requirements are met. */
public static class ZoneConfigureCompleteEvent{
public final Zone zone;
public final SectorPreset zone;
public ZoneConfigureCompleteEvent(Zone zone){
public ZoneConfigureCompleteEvent(SectorPreset zone){
this.zone = zone;
}
}

View File

@ -21,7 +21,7 @@ public interface Objective{
}
default Zone zone(){
default SectorPreset zone(){
return this instanceof ZoneObjective ? ((ZoneObjective)this).zone : null;
}
}

View File

@ -53,7 +53,7 @@ public class Objectives{
public static class ZoneWave extends ZoneObjective{
public int wave;
public ZoneWave(Zone zone, int wave){
public ZoneWave(SectorPreset zone, int wave){
this.zone = zone;
this.wave = wave;
}
@ -73,7 +73,7 @@ public class Objectives{
public static class Launched extends ZoneObjective{
public Launched(Zone zone){
public Launched(SectorPreset zone){
this.zone = zone;
}
@ -91,6 +91,6 @@ public class Objectives{
}
public abstract static class ZoneObjective implements Objective{
public @NonNull Zone zone;
public @NonNull SectorPreset zone;
}
}

View File

@ -13,7 +13,6 @@ import arc.scene.ui.layout.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.*;
import mindustry.core.GameState.*;
import mindustry.entities.*;
import mindustry.entities.units.*;
import mindustry.game.EventType.*;
@ -517,9 +516,9 @@ public class DesktopInput extends InputHandler{
}
if(omni){
unit.moveAt(movement, unit.type().accel);
unit.moveAt(movement);
}else{
unit.moveAt(Tmp.v2.trns(unit.rotation(), movement.len()), unit.type().accel);
unit.moveAt(Tmp.v2.trns(unit.rotation(), movement.len()));
if(!movement.isZero()){
unit.vel().rotateTo(movement.angle(), unit.type().rotateSpeed * Time.delta());
}

View File

@ -81,14 +81,14 @@ public class JsonIO{
}
});
json.setSerializer(Zone.class, new Serializer<Zone>(){
json.setSerializer(SectorPreset.class, new Serializer<SectorPreset>(){
@Override
public void write(Json json, Zone object, Class knownType){
public void write(Json json, SectorPreset object, Class knownType){
json.writeValue(object.name);
}
@Override
public Zone read(Json json, JsonValue jsonData, Class type){
public SectorPreset read(Json json, JsonValue jsonData, Class type){
return Vars.content.getByName(ContentType.zone, jsonData.asString());
}
});

View File

@ -303,7 +303,7 @@ public class ContentParser{
},
ContentType.item, parser(ContentType.item, Item::new),
ContentType.liquid, parser(ContentType.liquid, Liquid::new),
ContentType.zone, parser(ContentType.zone, Zone::new)
ContentType.zone, parser(ContentType.zone, SectorPreset::new)
);
private String getString(JsonValue value, String key){

View File

@ -1,5 +1,7 @@
package mindustry.type;
import mindustry.content.*;
public class LiquidStack{
public Liquid liquid;
public float amount;
@ -11,7 +13,7 @@ public class LiquidStack{
/** serialization only*/
protected LiquidStack(){
liquid = Liquids.water;
}
@Override

View File

@ -23,6 +23,9 @@ public class Sector{
public @Nullable SaveSlot save;
public boolean unlocked;
/** */
public float hostility;
//TODO implement a dynamic (?) launch period
public int launchPeriod = 10;

View File

@ -15,7 +15,7 @@ import mindustry.maps.generators.*;
import static mindustry.Vars.*;
//TODO ? remove ?
public class Zone extends UnlockableContent{
public class SectorPreset extends UnlockableContent{
public @NonNull WorldGenerator generator;
public @NonNull Objective configureObjective = new ZoneWave(this, 15);
public @NonNull Planet planet;
@ -35,13 +35,13 @@ public class Zone extends UnlockableContent{
private Array<ItemStack> defaultStartingItems = new Array<>();
public Zone(String name, Planet planet, WorldGenerator generator){
public SectorPreset(String name, Planet planet, WorldGenerator generator){
super(name);
this.generator = generator;
this.planet = planet;
}
public Zone(String name){
public SectorPreset(String name){
this(name, Planets.starter, new FileMapGenerator(name));
}
@ -103,7 +103,7 @@ public class Zone extends UnlockableContent{
public void updateObjectives(Runnable closure){
Array<ZoneObjective> incomplete = content.zones()
.map(z -> z.requirements).<Objective>flatten()
.flatMap(z -> z.requirements)
.select(o -> o.zone() == this && !o.complete())
.as(ZoneObjective.class);

View File

@ -10,6 +10,7 @@ import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.ai.types.*;
import mindustry.annotations.Annotations.*;
import mindustry.ctype.*;
import mindustry.entities.units.*;
@ -24,9 +25,9 @@ import static mindustry.Vars.*;
public class UnitType extends UnlockableContent{
static final float shadowTX = -12, shadowTY = -13, shadowColor = Color.toFloatBits(0, 0, 0, 0.22f);
public @NonNull Prov<? extends UnitController> defaultController = AIController::new;
public @NonNull Prov<? extends Unitc> constructor;
public boolean flying;
public @NonNull Prov<? extends Unitc> constructor;
public @NonNull Prov<? extends UnitController> defaultController = () -> !flying ? new GroundAI() : new FlyingAI();
public float speed = 1.1f, boostSpeed = 0.75f, rotateSpeed = 6f, baseRotateSpeed = 10f;
public float drag = 0.3f, mass = 1f, accel = 0.9f;
public float health = 200f, range = -1;

View File

@ -25,12 +25,12 @@ public class ZoneInfoDialog extends FloatingDialog{
addCloseButton();
}
public void show(Zone zone){
public void show(SectorPreset zone){
setup(zone);
show();
}
private void setup(Zone zone){
private void setup(SectorPreset zone){
cont.clear();
Table iteminfo = new Table();

View File

@ -1,19 +1,16 @@
package mindustry.world.meta;
import mindustry.ctype.Content;
import arc.struct.*;
import mindustry.gen.*;
public class Producers{
private Content output;
private Array<Produce> producers = new Array<>();
public void set(Content content){
this.output = content;
public void add(Produce prod){
producers.add(prod);
}
public Content get(){
return output;
}
public boolean is(Content content){
return content == output;
interface Produce{
void add(Tilec entity);
}
}

View File

@ -146,7 +146,7 @@ public class SStats implements SteamUserStatsCallback{
if(e.content == Items.thorium) obtainThorium.complete();
if(e.content == Items.titanium) obtainTitanium.complete();
if(!content.zones().contains(Zone::locked)){
if(!content.zones().contains(SectorPreset::locked)){
unlockAllZones.complete();
}
});

View File

@ -36,7 +36,7 @@ public class ZoneTests{
if(true) return new DynamicTest[0];
//fail("Zone validity tests need to be refactored!");
for(Zone zone : content.zones()){
for(SectorPreset zone : content.zones()){
out.add(dynamicTest(zone.name, () -> {
logic.reset();
try{