[+] Ability to get info for unit's bool-flags and currently used spell in animations.

[+] Ability to change some target's variable when missile hits it. Use (in DefineMissile) ChangeVariable for variable name, ChangeAmount for changing amount (may be negative) and bool ChangeMax to increase the Max field if Value will be greater than Max.
[*] CclDefineUnitStats now supports more user defined variables (NOTE: saves compatibility will be lost)
[*] AI units who uses repeat-cast magic won't stop on each cast.
[-] Units no more attack revealers.
[-] Some fixes to stored resources.
[-] Don't clone attack action for dead units.
This commit is contained in:
iddqd-mail 2012-06-15 11:24:08 +06:00
parent 75f911dbc9
commit 973631c335
15 changed files with 126 additions and 48 deletions

View file

@ -477,7 +477,7 @@ void COrder_Attack::AttackTarget(CUnit &unit)
return;
}
// Save current command to come back.
COrder *savedOrder = this->Clone();
COrder *savedOrder = COrder::NewActionAttack(unit, this->goalPos);
if (unit.StoreOrder(savedOrder) == false) {
delete savedOrder;

View file

@ -116,7 +116,7 @@ static int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype)
for (int i = 0; i < MaxCosts; ++i) {
if (player.MaxResources[i] != -1) {
player.MaxResources[i] += newtype.Stats[player.Index].Storing[i] - oldtype.Stats[player.Index].Storing[i];
player.SetResource(i, player.StoredResources[i], true);
player.SetResource(i, player.StoredResources[i], STORE_BUILDING);
}
}

View file

@ -37,6 +37,7 @@
#include "unittype.h"
#include "unit.h"
#include "spells.h"
#include "actions.h"
#include "ai_local.h"
/*----------------------------------------------------------------------------
@ -55,8 +56,8 @@ void AiCheckMagic()
for (int i = 0; i < n; ++i) {
CUnit &unit = player.GetUnit(i);
// Check only magic units
if (unit.Type->CanCastSpell) {
// Check only idle magic units
if (unit.Type->CanCastSpell && unit.CurrentAction() != UnitActionSpellCast) {
for (unsigned int j = 0; j < SpellTypeTable.size(); ++j) {
// Check if we can cast this spell. SpellIsAvailable checks for upgrades.
if (unit.Type->CanCastSpell[j] && SpellIsAvailable(player, j)

View file

@ -39,6 +39,8 @@
#include "stratagus.h"
#include "action/action_spellcast.h"
#include "animation.h"
#include "animation/animation_attack.h"
@ -66,6 +68,7 @@
#include "iolib.h"
#include "player.h"
#include "script.h"
#include "spells.h"
#include "unit.h"
#include "unittype.h"
@ -178,6 +181,22 @@ int ParseAnimInt(const CUnit *unit, const char *parseint)
return goal->Variable[index].Value * 100 / goal->Variable[index].Max;
}
return 0;
} else if ((s[0] == 'b') && unit != NULL) { //unit bool flag detected
const int index = UnitTypeVar.BoolFlagNameLookup[cur];// User bool flags
if (index == -1) {
fprintf(stderr, "Bad bool-flag name '%s'\n", cur);
Exit(1);
return 0;
}
return goal->Type->BoolFlag[index].value;
} else if ((s[0] == 's') && unit != NULL) { //spell type detected
Assert(goal->CurrentAction() == UnitActionSpellCast);
const COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(goal->CurrentOrder());
const SpellType &spell = order.GetSpell();
if (!strcmp(spell.Ident.c_str(), cur)) {
return 1;
}
return 0;
} else if (s[0] == 'p' && unit != NULL) { //player variable detected
char *next = strchr(cur, '.');
if (next == NULL) {

View file

@ -138,14 +138,14 @@ static void SetPlayerData(int player, const char *prop, const char *arg, int val
fprintf(stderr, "Invalid resource \"%s\"", arg);
Exit(1);
}
Players[player].SetResource(resId, value);
Players[player].SetResource(resId, value, STORE_BOTH);
} else if (!strcmp(prop, "StoredResources")) {
const int resId = GetResourceIdByName(arg);
if (resId == -1) {
fprintf(stderr, "Invalid resource \"%s\"", arg);
Exit(1);
}
Players[player].SetResource(resId, value, true);
Players[player].SetResource(resId, value, STORE_BUILDING);
} else if (!strcmp(prop, "UnitLimit")) {
Players[player].UnitLimit = value;
} else if (!strcmp(prop, "BuildingLimit")) {

View file

@ -360,6 +360,9 @@ public:
int DrawLevel; /// Level to draw missile at
int SpriteFrames; /// number of sprite frames in graphic
int NumDirections; /// number of directions missile can face
int ChangeVariable; /// variable to change
int ChangeAmount; /// how many to change
bool ChangeMax; /// modify the max, if value will exceed it
/// @todo FiredSound defined but not used!
SoundConfig FiredSound; /// fired sound

View file

@ -45,6 +45,14 @@
#endif
#include "vec2i.h"
/*----------------------------------------------------------------------------
-- Definitons
----------------------------------------------------------------------------*/
#define STORE_OVERALL 0
#define STORE_BUILDING 1
#define STORE_BOTH 2
/*----------------------------------------------------------------------------
-- Declarations
----------------------------------------------------------------------------*/
@ -135,7 +143,7 @@ public:
/// Adds/subtracts some resources to/from the player store
void ChangeResource(int resource, int value, bool store = false);
/// Set a resource of the player
void SetResource(int resource, int value, bool store = false);
void SetResource(int resource, int value, int type = STORE_OVERALL);
/// Check, if there enough resources for action.
bool CheckResource(int resource, int value);

View file

@ -339,6 +339,10 @@
#include "player.h"
#endif
#ifndef __MISSILE_H__
#include "missile.h"
#endif
#include "vec2i.h"
/*----------------------------------------------------------------------------
@ -676,6 +680,10 @@ unsigned ByPlayer : PlayerMax; /// Track unit seen by player
if (IsInvisibile(player)) {
return false;
}
// Don't attack revealers
if (this->Type->Revealer) {
return false;
}
if ((player.Type == PlayerComputer && !this->Type->PermanentCloak)
|| IsVisible(player) || IsVisibleOnRadar(player)) {
return IsAliveOnMap();
@ -960,7 +968,7 @@ extern void DestroyAllInside(CUnit &source);
/// Calculate some value to measure the unit's priority for AI
extern int ThreatCalculate(const CUnit &unit, const CUnit &dest);
/// Hit unit with damage, if destroyed give attacker the points
extern void HitUnit(CUnit *attacker, CUnit &target, int damage);
extern void HitUnit(CUnit *attacker, CUnit &target, int damage, const Missile *missile = NULL);
/// Calculate the distance from current view point to coordinate
extern int ViewPointDistance(const Vec2i &pos);

View file

@ -682,7 +682,7 @@ static void MissileHitsGoal(const Missile &missile, CUnit &goal, int splash)
} else {
Assert(missile.SourceUnit != NULL);
HitUnit(missile.SourceUnit, goal,
CalculateDamage(*missile.SourceUnit, goal) / splash);
CalculateDamage(*missile.SourceUnit, goal) / splash, &missile);
}
}
}
@ -1091,8 +1091,8 @@ void InitMissileTypes()
** Constructor.
*/
MissileType::MissileType(const std::string &ident) :
Ident(ident), Transparency(0),
DrawLevel(0), SpriteFrames(0), NumDirections(0),
Ident(ident), Transparency(0), DrawLevel(0),
SpriteFrames(0), NumDirections(0), ChangeVariable(-1), ChangeAmount(0), ChangeMax(false),
CorrectSphashDamage(false), Flip(false), CanHitOwner(false), FriendlyFire(false),
AlwaysFire(false), Class(), NumBounces(0), StartDelay(0), Sleep(0), Speed(0),
Range(0), SplashFactor(0), ImpactParticle(NULL), G(NULL)

View file

@ -116,6 +116,18 @@ void MissileType::Load(lua_State *l)
this->FiredSound.Name = LuaToString(l, -1);
} else if (!strcmp(value, "ImpactSound")) {
this->ImpactSound.Name = LuaToString(l, -1);
} else if (!strcmp(value, "ChangeVariable")) {
const int index = UnitTypeVar.VariableNameLookup[LuaToString(l, -1)];// User variables
if (index == -1) {
fprintf(stderr, "Bad variable name '%s'\n", LuaToString(l, -1));
Exit(1);
return;
}
this->ChangeVariable = index;
} else if (!strcmp(value, "ChangeAmount")) {
this->ChangeAmount = LuaToNumber(l, -1);
} else if (!strcmp(value, "ChangeMax")) {
this->ChangeMax = LuaToBoolean(l, -1);
} else if (!strcmp(value, "Class")) {
const char *className = LuaToString(l, -1);
unsigned int i = 0;

View file

@ -812,18 +812,18 @@ int CPlayer::GetUnitCount() const
** Gets the player resource.
**
** @param resource Resource to get.
** @param store Resource type to get
** @param type Storing type
**
** @note Resource types: 0 - overall store, 1 - store buildings, 2 - both
** @note Storing types: 0 - overall store, 1 - store buildings, 2 - both
*/
int CPlayer::GetResource(int resource, int type)
{
switch (type) {
case 0:
case STORE_OVERALL:
return this->Resources[resource];
case 1:
case STORE_BUILDING:
return this->StoredResources[resource];
case 2:
case STORE_BOTH:
return this->Resources[resource] + this->StoredResources[resource];
default:
DebugPrint("Wrong resource type\n");
@ -844,6 +844,9 @@ void CPlayer::ChangeResource(int resource, int value, bool store)
int fromStore = std::min(this->StoredResources[resource], abs(value));
this->StoredResources[resource] -= fromStore;
this->Resources[resource] -= abs(value) - fromStore;
if (this->Resources[resource] < 0) {
this->Resources[resource] = 0;
}
} else {
if (store && this->MaxResources[resource] != -1) {
this->StoredResources[resource] += std::min(value, this->MaxResources[resource] - this->StoredResources[resource]);
@ -858,13 +861,21 @@ void CPlayer::ChangeResource(int resource, int value, bool store)
**
** @param resource Resource to change.
** @param value How many of this resource.
** @param store If true, sets the building store resources, else the overall resources.
** @param type Resource types: 0 - overall store, 1 - store buildings, 2 - both
*/
void CPlayer::SetResource(int resource, int value, bool store)
void CPlayer::SetResource(int resource, int value, int type)
{
if (store && this->MaxResources[resource] != -1) {
if (type == STORE_BOTH) {
if (this->MaxResources[resource] != -1) {
const int toStore = std::min(0, value - this->Resources[resource]);
this->StoredResources[resource] = std::min(toStore, this->MaxResources[resource]);
this->Resources[resource] = std::max(0, value - toStore);
} else {
this->Resources[resource] = value;
}
} else if (type == STORE_BUILDING && this->MaxResources[resource] != -1) {
this->StoredResources[resource] = std::min(value, this->MaxResources[resource]);
} else {
} else if (type == STORE_OVERALL) {
this->Resources[resource] = value;
}
}

View file

@ -840,7 +840,7 @@ static int CclSetPlayerData(lua_State *l)
const std::string res = LuaToString(l, 3);
const int resId = GetResourceIdByName(l, res.c_str());
p->SetResource(resId, LuaToNumber(l, 4), true);
p->SetResource(resId, LuaToNumber(l, 4), STORE_BUILDING);
// } else if (!strcmp(data, "UnitTypesCount")) {
// } else if (!strcmp(data, "AiEnabled")) {
// } else if (!strcmp(data, "TotalNumUnits")) {

View file

@ -1179,16 +1179,11 @@ static int CclDefineUnitType(lua_State *l)
*/
static int CclDefineUnitStats(lua_State *l)
{
const int args = lua_gettop(l);
int j = 0;
CUnitType *type = UnitTypeByIdent(LuaToString(l, j + 1));
CUnitType *type = UnitTypeByIdent(LuaToString(l, 1));
const int playerId = LuaToNumber(l, 2);
Assert(type);
++j;
int playerId = LuaToNumber(l, j + 1);
Assert(playerId < PlayerMax);
++j;
CUnitStats *stats = &type->Stats[playerId];
if (!stats->Variables) {
@ -1196,51 +1191,61 @@ static int CclDefineUnitStats(lua_State *l)
}
// Parse the list: (still everything could be changed!)
for (; j < args; ++j) {
const char *value = LuaToString(l, j + 1);
const int args = lua_rawlen(l, 3);
for (int j = 0; j < args; ++j) {
lua_rawgeti(l, 3, j + 1);
const char *value = LuaToString(l, -1);
lua_pop(l, 1);
++j;
if (!strcmp(value, "costs")) {
if (!lua_istable(l, j + 1)) {
lua_rawgeti(l, 3, j + 1);
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, j + 1);
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, j + 1, k + 1);
lua_rawgeti(l, 3, j + 1);
lua_rawgeti(l, -1, k + 1);
value = LuaToString(l, -1);
lua_pop(l, 1);
++k;
const int resId = GetResourceIdByName(l, value);
lua_rawgeti(l, j + 1, k + 1);
lua_rawgeti(l, -1, k + 1);
stats->Costs[resId] = LuaToNumber(l, -1);
lua_pop(l, 1);
lua_pop(l, 1);
}
} else if (!strcmp(value, "storing")) {
if (!lua_istable(l, j + 1)) {
lua_rawgeti(l, 3, j + 1);
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, j + 1);
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, j + 1, k + 1);
lua_rawgeti(l, 3, j + 1);
lua_rawgeti(l, -1, k + 1);
value = LuaToString(l, -1);
lua_pop(l, 1);
++k;
const int resId = GetResourceIdByName(l, value);
lua_rawgeti(l, j + 1, k + 1);
lua_rawgeti(l, -1, k + 1);
stats->Storing[resId] = LuaToNumber(l, -1);
lua_pop(l, 1);
lua_pop(l, 1);
}
} else {
int i = UnitTypeVar.VariableNameLookup[value];// User variables
if (i != -1) { // valid index
if (lua_istable(l, j + 1)) {
DefineVariableField(l, stats->Variables + i, j + 1);
lua_rawgeti(l, 3, j + 1);
if (lua_istable(l, -1)) {
DefineVariableField(l, stats->Variables + i, -1);
} else if (lua_isnumber(l, -1)) {
stats->Variables[i].Enable = 1;
stats->Variables[i].Value = LuaToNumber(l, j + 1);
stats->Variables[i].Max = LuaToNumber(l, j + 1);
stats->Variables[i].Value = LuaToNumber(l, -1);
stats->Variables[i].Max = LuaToNumber(l, -1);
} else { // Error
LuaError(l, "incorrect argument for the variable in unittype");
}

View file

@ -959,7 +959,7 @@ void UnitLost(CUnit &unit)
const int newMaxValue = player.MaxResources[i] - type.Stats[player.Index].Storing[i];
player.MaxResources[i] = std::max(0, newMaxValue);
player.SetResource(i, player.StoredResources[i], true);
player.SetResource(i, player.StoredResources[i], STORE_BUILDING);
}
}
// Handle income improvements, look if a player loses a building
@ -2695,8 +2695,9 @@ int ThreatCalculate(const CUnit &unit, const CUnit &dest)
** @param attacker Unit that attacks.
** @param target Unit that is hit.
** @param damage How many damage to take.
** @param missile Which missile took the damage.
*/
void HitUnit(CUnit *attacker, CUnit &target, int damage)
void HitUnit(CUnit *attacker, CUnit &target, int damage, const Missile *missile)
{
// Can now happen by splash damage
// Multiple places send x/y as damage, which may be zero
@ -2836,6 +2837,16 @@ void HitUnit(CUnit *attacker, CUnit &target, int damage)
type->OnHit->run();
}
// Increase variables
if (missile && missile->Type->ChangeVariable != -1) {
const int var = missile->Type->ChangeVariable;
target.Variable[var].Enable = 1;
target.Variable[var].Value += missile->Type->ChangeAmount;
if (target.Variable[var].Value > target.Variable[var].Max && missile->Type->ChangeMax) {
target.Variable[var].Max = target.Variable[var].Value;
}
}
// Show impact missiles
if (target.Variable[SHIELD_INDEX].Value > 0
&& !target.Type->Impact[ANIMATIONS_DEATHTYPES + 1].Name.empty()) { // shield impact

View file

@ -368,7 +368,7 @@ static bool SaveUnitStats(const CUnitStats &stats, const CUnitType &type, int pl
if (stats == type.DefaultStat) {
return false;
}
file.printf("DefineUnitStats(\"%s\", %d,\n ", type.Ident.c_str(), plynr);
file.printf("DefineUnitStats(\"%s\", %d, {\n ", type.Ident.c_str(), plynr);
for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) {
file.printf("\"%s\", {Value = %d, Max = %d, Increase = %d%s},\n ",
UnitTypeVar.VariableNameLookup[i], stats.Variables[i].Value,
@ -382,14 +382,14 @@ static bool SaveUnitStats(const CUnitStats &stats, const CUnitType &type, int pl
}
file.printf("\"%s\", %d,", DefaultResourceNames[i].c_str(), stats.Costs[i]);
}
file.printf("\"storing\", {");
file.printf("},\n\"storing\", {");
for (unsigned int i = 0; i < MaxCosts; ++i) {
if (i) {
file.printf(" ");
}
file.printf("\"%s\", %d,", DefaultResourceNames[i].c_str(), stats.Storing[i]);
}
file.printf("})\n");
file.printf("}})\n");
return true;
}