diff --git a/scripts/ai/include/basic.lua b/scripts/ai/include/basic.lua index b448bf1..f779913 100644 --- a/scripts/ai/include/basic.lua +++ b/scripts/ai/include/basic.lua @@ -17,24 +17,33 @@ function attack() return end + ai.settarget(target) + -- Get stats about enemy. - dir = ai.face(target) -- Face target. - dist = ai.dist(ai.pos(target)) -- Get distance. - second = ai.secondary() -- Get best secondary weapon. + dist = ai.dist(ai.pos(target)) -- Get distance. - -- Shoot missiles if in range. - if ai.secondary() == "Launcher" and - dist < ai.getweaprange(1) and dir < 30 then -- More lenient with aiming. - ai.settarget(target) - ai.shoot(2) - end - - -- Attack if in range. range = ai.getweaprange() - if dir < 10 and dist > range then - ai.accel() - elseif(dir < 10 or ai.hasturrets()) and dist < range then - ai.shoot() + + -- We first bias towards range. + if dist > range then + dir = ai.face(target) -- Normal face the target. + + -- Shoot missiles if in range. + if ai.secondary() == "Launcher" and + dist < ai.getweaprange(1) and dir < 30 then -- More leniant with aiming. + ai.shoot(2) + end + + if dir < 10 then + ai.accel() + end + + -- Close enough to melee. + else + dir = ai.aim(target) -- we aim instead of face. + if (dir < 10 or ai.hasturret()) then + ai.shoot() + end end end diff --git a/src/ai.c b/src/ai.c index 0ba1e66..6d882ce 100644 --- a/src/ai.c +++ b/src/ai.c @@ -118,6 +118,7 @@ static int ai_haslockon(lua_State* L); /* boolean haslockon() */ static int ai_accel(lua_State* L); /* accel(number); nuimber <= 1. */ static int ai_turn(lua_State* L); /* turn(number); abs(number) <= 1. */ static int ai_face(lua_State* L); /* face(number/pointer) */ +static int ai_aim(lua_State* L); /* aim(number). */ static int ai_brake(lua_State* L); /* Brake() */ static int ai_getnearestplanet(lua_State* L); /* pointer getnearestplanet() */ static int ai_getrndplanet(lua_State* L); /* pointer getrndplanet() */ @@ -180,6 +181,7 @@ static const luaL_Reg ai_methods[] = { { "stop", ai_stop }, { "hyperspace", ai_hyperspace }, /* Combat. */ + { "aim", ai_aim }, { "combat", ai_combat }, { "settarget", ai_settarget }, { "secondary", ai_secondary }, @@ -243,7 +245,7 @@ int ai_init(void) { strlen(AI_INCLUDE)) != 0) && (strncmp(files[i] + strlen(files[i]) - strlen(AI_SUFFIX), /* Suffixed. */ AI_SUFFIX, strlen(AI_SUFFIX))==0)) - if(ai_loadProfile(files[i])) + if(ai_loadProfile(files[i])) /* Load the profiles. */ WARN("Error loading AI profile '%s'", files[i]); /* Free the char allocated by pack. */ @@ -358,7 +360,7 @@ void ai_think(Pilot* pilot) { cur_pilot->solid->dir_vel = 0.; if(pilot_turn) /* Set the turning velocity. */ - cur_pilot->solid->dir_vel -= cur_pilot->turn * pilot_turn; + cur_pilot->solid->dir_vel += cur_pilot->turn * pilot_turn; vect_pset(&cur_pilot->solid->force, cur_pilot->thrust * pilot_acc, cur_pilot->solid->dir); @@ -723,7 +725,7 @@ static int ai_face(lua_State* L) { } else if(lua_islightuserdata(L,1)) v = (Vec2*)lua_topointer(L,1); - mod = -10; + mod = 10; if(lua_gettop(L) > 1 && lua_isnumber(L,2)) invert = (int)lua_tonumber(L,2); if(invert) mod *= -1; vect_cset(&sv, VX(cur_pilot->solid->pos) + FACE_WVEL*VX(cur_pilot->solid->vel), @@ -856,6 +858,54 @@ static int ai_stop(lua_State* L) { return 0; } +/* Aim at the pilot, trying to hit it. */ +static int ai_aim(lua_State* L) { + int id; + double x, y; + double t; + Pilot* p; + Vec2 tv; + double dist, diff; + double mod; + LLUA_MIN_ARGS(1); + + /* Only acceptable parameter is pilot id. */ + if(lua_isnumber(L,1)) + id = lua_tonumber(L,1); + else + LLUA_INVALID_PARAMETER(); + + /* Get the pilot. */ + p = pilot_get(id); + if(p == NULL) { + WARN("Pilot is invalid"); + return 0; + } + + /* Get the distance. */ + dist = vect_dist(&cur_pilot->solid->pos, &p->solid->pos); + + /* Time for shots to reach distance. */ + t = dist / cur_pilot->weap_speed; + + /* Position is calculated on where it should be. */ + x = p->solid->pos.x + p->solid->vel.x*t + - (cur_pilot->solid->pos.x + cur_pilot->solid->vel.x*t); + + y = p->solid->pos.y + p->solid->vel.y*t + - (cur_pilot->solid->pos.y + cur_pilot->solid->vel.y*t); + vect_cset(&tv, x, y); + + /* Calculate what we need to turn. */ + mod = 10.; + diff = angle_diff(cur_pilot->solid->dir, VANGLE(tv)); + pilot_turn = mod * diff; + + /* Return distance to target (in grad). */ + lua_pushnumber(L, ABS(diff*180./M_PI)); + return 1; +} + /* Toggle combat flag. Default is on. */ static int ai_combat(lua_State* L) { int i; @@ -952,9 +1002,7 @@ static int ai_hostile(lua_State* L) { /* Return the maximum range of weapons if parameter is 1, then do secondary. */ static int ai_getweaprange(lua_State* L) { - int i; - double range, max; - Outfit* o; + double range; /* If 1 is passed as parameter, secondary weapon is checked. */ if(lua_isnumber(L, 1) && ((int)lua_tonumber(L, 1) == 1)) @@ -972,23 +1020,7 @@ static int ai_getweaprange(lua_State* L) { return 1; } - max = -1.; - for(i = 0; i < cur_pilot->noutfits; i++) { - o = cur_pilot->outfits[i].outfit; - - /* Not interested in secondary weapons nor ammunition. */ - if(outfit_isProp(o, OUTFIT_PROP_WEAP_SECONDARY) || outfit_isAmmo(o)) - continue; - - /* Compare vs current outfit's range. */ - range = outfit_range(o); - if(range > max) - max = range; - } - - if(max < 0.) return 0; /* No ranged weapons. */ - - lua_pushnumber(L, max); + lua_pushnumber(L, cur_pilot->weap_range); return 1; } diff --git a/src/outfit.c b/src/outfit.c index 0fe06e6..eecb1fd 100644 --- a/src/outfit.c +++ b/src/outfit.c @@ -243,6 +243,13 @@ double outfit_range(const Outfit* o) { return -1.; } +double outfit_speed(const Outfit* o) { + if(outfit_isWeapon(o)) return o->u.blt.speed; + else if(outfit_isAmmo(o)) return o->u.amm.speed; + else if(outfit_isTurret(o)) return o->u.blt.speed; + return -1.; +} + int outfit_isSeeker(const Outfit* o) { if((o->type == OUTFIT_TYPE_MISSILE_SEEK_AMMO) || (o->type == OUTFIT_TYPE_MISSILE_SEEK_SMART_AMMO) || diff --git a/src/outfit.h b/src/outfit.h index c972158..f7a6207 100644 --- a/src/outfit.h +++ b/src/outfit.h @@ -155,6 +155,7 @@ DamageType outfit_damageType(const Outfit* o); int outfit_delay(const Outfit* o); double outfit_energy(const Outfit* o); double outfit_range(const Outfit* o); +double outfit_speed(const Outfit* o); int outfit_isSeeker(const Outfit* o); /* Load/free outfit stack. */ diff --git a/src/pilot.c b/src/pilot.c index f66de93..683cf12 100644 --- a/src/pilot.c +++ b/src/pilot.c @@ -697,6 +697,8 @@ char* pilot_getOutfits(Pilot* pilot) { void pilot_calcStats(Pilot* pilot) { int i; double q; + double wrange, wspeed; + int nweaps; Outfit* o; double ac, sc, ec, fc; /* Temp health coeficients to set. */ @@ -725,6 +727,8 @@ void pilot_calcStats(Pilot* pilot) { pilot_calcCargo(pilot); /* Now add outfit changes. */ + nweaps = 0; + wrange = wspeed = 0.; for(i = 0; i < pilot->noutfits; i++) { o = pilot->outfits[i].outfit; q = (double) pilot->outfits[i].quantity; @@ -756,8 +760,18 @@ void pilot_calcStats(Pilot* pilot) { } pilot->energy_regen -= o->u.jam.energy; } + else if((outfit_isWeapon(o) || outfit_isTurret(o)) && /* Primary weapon. */ + !outfit_isProp(o, OUTFIT_PROP_WEAP_SECONDARY)) { + nweaps++; + wrange = MAX(wrange, outfit_range(o)); + wspeed += outfit_speed(o); + } } + /* Set weapon range and speed. */ + pilot->weap_range = wrange; /* Range is max. */ + pilot->weap_speed = wspeed / (double)nweaps; + /* Give the pilot her health proportion back. */ pilot->armour = ac * pilot->armour_max; pilot->shield = sc * pilot->shield_max; diff --git a/src/pilot.h b/src/pilot.h index 2d0ead1..406e92c 100644 --- a/src/pilot.h +++ b/src/pilot.h @@ -106,6 +106,10 @@ typedef struct Pilot_ { int ncommodities; int cargo_free; + /* Weapon Properties. */ + double weap_range; /* Average range of primary weapons */ + double weap_speed; /* Average speed of primary weapons. */ + /* Misc. */ uint32_t flags; /* Used for AI etc. */ unsigned int ptimer; /* Generic timer for internal pilot use. */ diff --git a/src/weapon.c b/src/weapon.c index 384272d..89df1e8 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -389,6 +389,7 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* Vec2 v; double mass, rdir; Pilot* pilot_target; + double x, y, t, dist; Weapon* w; /* Create basic features. */ @@ -408,7 +409,23 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* /* Only difference is the direction of fire. */ if((outfit->type == OUTFIT_TYPE_TURRET_BOLT) && (w->parent != w->target) && (w->target != 0)) { /* Must have a valid target. */ - rdir = vect_angle(pos, &pilot_get(w->target)->solid->pos); + pilot_target = pilot_get(w->target); + + /* Get the distance. */ + dist = vect_dist(pos, &pilot_target->solid->pos); + + /* Time for shots to reach that distance. */ + t = dist / w->outfit->u.blt.speed; + + /* Position is calculated on where it should be. */ + x = (pilot_target->solid->pos.x + pilot_target->solid->vel.x*t) + - (pos->x + vel->x*t); + + y = (pilot_target->solid->pos.y + pilot_target->solid->vel.y*t) + - (pos->y + vel->y*t); + vect_cset(&v, x, y); + + rdir = VANGLE(v); rdir += RNG(-outfit->u.blt.accuracy/2., outfit->u.blt.accuracy/2.)/180.*M_PI; } else /* Fire straight. */ @@ -424,7 +441,8 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* w->solid = solid_create(mass, rdir, pos, &v); w->voice = sound_addVoice(VOICE_PRIORITY_BOLT, w->solid->pos.x, w->solid->pos.y, - w->solid->vel.x, w->solid->vel.y, w->outfit->u.blt.sound, 0); + w->solid->vel.x, w->solid->vel.y, + w->outfit->u.blt.sound, 0); break; /* Treat seekers togther. */