[+]Integer constants in animations could be given as unit's or player's variables.
Definition of animation variable: v_<unit_variable.tag>, where unit_variable - any variable(you can also use user defined variables) of the current unit, tag - variable's tag(Value,Max,Increase,Enable,Percent). Example: v_HitPoints.Value Percent returns the proportion between Value and Max in range 0..100 t_<unit_variable.tag> - same as previous, but refers to unit's current goal unit(target in attack animation, mine in resource etc.). If goal doesn't exist, returns 0. p_<playernum.property[.argument]> - where playernum - player's number(you can use "this" to refer the unit's player), property - player's property(see the GetPlayerData function for more information), optional <argument> - specifies the property's argument(need for resources and unit-types) Examples: p_1.TotalUnitLimit p_this.Resources.gold r_<value> - returns the random value between 0 and <value> Though if vaiable couldn't be reconized, game will try to convert it into integer constant(0 will be returned if convertation fails). [+]New actions in animations: (modified) "spawn-missile <missile-type> <startx> <starty> <destx> <desty> <flags>" - spawns <missile-type> relative to the current unit adding <startx> and <starty> to position. <destx> and <desty> specify the missile's destination point. Could be used as more configurable replacement to the standard "attack" action. <flags> used to configure the action, you should specify it separated by points: "damage" - missile will generate damage, values are taken from the action unit. "totarget" - missile will ignore <destx> and <desty> and will fly to unit's current goal. "pixel" - missile will use pixel coordinates instead of tile coordinates, useful for more accurate missile placing. "reltarget" - the source of missile will be the unit's current target, so all parameters will be calculated depending of target's parameters, not source's(even the "totarget" will shot the missile back to source unit). Through the damage parameters are always taken from the source unit. "ranged" - missile will check the current max and min attack ranges from the unit("reltarget" inverses logic) "spawn-unit <unit-type> <offx> <offy> <range> <player>" - spawn <unit-type> relative to the current unit adding <offx> and <offy> to position. <range> specifies the maximum distatnce from the source unit. Example: "spawn-unit unit-something 2 1 v_AttackRange.Value this" "if-var <lvalue> <rvalue> <condition> <state>" - jump animation frame to label <state>, if <lvalue> is <condition> to <rvalue> Possible conditions are: 1 - greater or equal 2 - greater 3 - less or equal 4 - less 5 - equal 6 - not equal Example: this code will show different animation frames depending of unit's current health: Still = {"if-var v_HitPoints.Percent 50 3 damaged50", "frame 0", "wait 1", "frame 0", "wait 1","goto end", "label damaged50", "frame 1", "wait 1", "frame 1", "wait 1", "label end"} "set-var <var> <mod> <value>" - sets the current unit's <var> variable. <mod> specifies, how should be <value> applied to <var>: 1 - <value> will be added to <var>'s current value. 2 - <value> will be subtracted from <var>'s current value. 3 - <var>'s current value will be multiplied to <value>. 4 - <var>'s current value will be divided to <value>. 5 - <var>'s current value will be divided to <value> and remainder will be taken as value. Warning: if <value> = 0 in 4 and 5, the game will bomb out with a error. "set-player-var <player> <var> <value> <mod>[ <argument>]" - sets the <player>'s <var>. The <value> and <mod> are used same as in set-var. Optional <argument> - specifies the <var>'s argument(need for resources and unit-types) "die[ <deathtype>]" - causes unit to die. If <deathtype> is set, unit will play his extra death animation depending on <deathtype>, if he has one. [-]Health states in animations are removed, should use "if-var" animation to do this. Patch frpm Cybermind
This commit is contained in:
parent
d2e61fda55
commit
f8aff0f96d
18 changed files with 967 additions and 180 deletions
|
@ -77,12 +77,12 @@ void AnimateActionAttack(CUnit &unit)
|
|||
// No animation.
|
||||
// So direct fire missile.
|
||||
// FIXME : wait a little.
|
||||
if (!unit.Type->Animations || !unit.Type->Animations->Attack[GetAnimationDamagedState(unit,3)]) {
|
||||
if (!unit.Type->Animations || !unit.Type->Animations->Attack) {
|
||||
FireMissile(unit);
|
||||
UnHideUnit(unit);// unit is invisible until attacks
|
||||
return;
|
||||
}
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Attack[GetAnimationDamagedState(unit,3)]);
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Attack);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -183,7 +183,7 @@ void HandleActionBoard(COrder& order, CUnit &unit)
|
|||
if (WaitForTransporter(unit)) {
|
||||
unit.SubAction = 202;
|
||||
} else {
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still[GetAnimationDamagedState(unit,1)]);
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still);
|
||||
}
|
||||
break;
|
||||
// Enter transporter
|
||||
|
|
|
@ -313,7 +313,7 @@ static void StartBuilding(CUnit &unit, CUnit &ontop)
|
|||
// HACK: allows the unit to be removed
|
||||
build->CurrentSightRange = 1;
|
||||
//HACK: reset anim
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still[GetAnimationDamagedState(unit,1)]);
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still);
|
||||
unit.Remove(build);
|
||||
build->CurrentSightRange = 0;
|
||||
unit.tilePos = pos;
|
||||
|
|
|
@ -77,7 +77,7 @@ int DoActionMove(CUnit &unit)
|
|||
|
||||
Assert(unit.CanMove());
|
||||
if (!unit.Moving &&
|
||||
(unit.Type->Animations->Move[GetAnimationDamagedState(unit,2)] != unit.Anim.CurrAnim || !unit.Anim.Wait)) {
|
||||
(unit.Type->Animations->Move != unit.Anim.CurrAnim || !unit.Anim.Wait)) {
|
||||
Assert(!unit.Anim.Unbreakable);
|
||||
|
||||
// FIXME: So units flying up and down are not affected.
|
||||
|
@ -147,7 +147,7 @@ int DoActionMove(CUnit &unit)
|
|||
}
|
||||
|
||||
unit.CurrentOrder()->Data.Move.Cycles++;//reset have to be manualy controled by caller.
|
||||
move = UnitShowAnimationScaled(unit, unit.Type->Animations->Move[GetAnimationDamagedState(unit,2)],
|
||||
move = UnitShowAnimationScaled(unit, unit.Type->Animations->Move,
|
||||
Map.Field(unit.Offset)->Cost);
|
||||
|
||||
unit.IX += posd.x * move;
|
||||
|
|
|
@ -83,7 +83,7 @@ void HandleActionResearch(COrder& order, CUnit &unit)
|
|||
|
||||
unit.Type->Animations->Research ?
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Research) :
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still[GetAnimationDamagedState(unit,1)]);
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still);
|
||||
if (unit.Wait) {
|
||||
unit.Wait--;
|
||||
return;
|
||||
|
|
|
@ -60,30 +60,32 @@
|
|||
-- Functions
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
#if 0
|
||||
|
||||
/**
|
||||
** Animate unit spell cast (it is attack really)!
|
||||
** Animate unit spell cast
|
||||
**
|
||||
** @param unit Unit, for that spell cast/attack animation is played.
|
||||
*/
|
||||
void AnimateActionSpellCast(CUnit &unit)
|
||||
{
|
||||
int flags;
|
||||
|
||||
if (unit.Type->Animations) {
|
||||
Assert(unit.Type->Animations->Attack);
|
||||
|
||||
flags = UnitShowAnimation(unit, unit.Type->Animations->Attack);
|
||||
|
||||
if (flags & AnimationMissile) { // FIXME: should cast spell ?
|
||||
FireMissile(unit); // we should not get here ??
|
||||
//if don't have animations just cast spell
|
||||
if (!unit.Type->Animations || (!unit.Type->Animations->Attack && !unit.Type->Animations->SpellCast)) {
|
||||
CUnit *goal = unit.CurrentOrder()->GetGoal();
|
||||
if (goal && !goal->IsVisibleAsGoal(*unit.Player)) {
|
||||
unit.ReCast = 0;
|
||||
} else {
|
||||
COrderPtr order = unit.CurrentOrder();
|
||||
unit.ReCast = SpellCast(unit, order->Arg1.Spell,
|
||||
goal, order->goalPos.x, order->goalPos.y);
|
||||
}
|
||||
UnHideUnit(unit);// unit is invisible until casts spell
|
||||
return;
|
||||
}
|
||||
if (unit.Type->Animations->SpellCast)
|
||||
UnitShowAnimation(unit, unit.Type->Animations->SpellCast);
|
||||
else
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Attack);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
** Handle moving to the target.
|
||||
**
|
||||
|
@ -180,10 +182,9 @@ void HandleActionSpellCast(COrder& order, CUnit &unit)
|
|||
}
|
||||
// FALL THROUGH
|
||||
case 2: // Cast spell on the target.
|
||||
// FIXME: should use AnimateActionSpellCast here
|
||||
if (unit.Type->Animations->Attack[GetAnimationDamagedState(unit,3)] && !spell->IsCasterOnly()) {
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Attack[GetAnimationDamagedState(unit,3)]);
|
||||
if (unit.Anim.Unbreakable) { // end of animation
|
||||
if (!spell->IsCasterOnly()) {
|
||||
AnimateActionSpellCast(unit);
|
||||
if (unit.Anim.Unbreakable) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -367,7 +367,7 @@ void ActionStillGeneric(CUnit &unit, bool stand_ground)
|
|||
unit.SubAction = SUB_STILL_STANDBY;
|
||||
// no break : follow
|
||||
case SUB_STILL_STANDBY:
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still[GetAnimationDamagedState(unit, 1)]);
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still);
|
||||
break;
|
||||
case SUB_STILL_ATTACK: // attacking unit in attack range.
|
||||
AnimateActionAttack(unit);
|
||||
|
|
|
@ -104,7 +104,7 @@ void HandleActionTrain(COrder& order, CUnit &unit)
|
|||
|
||||
unit.Type->Animations->Train ?
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Train) :
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still[GetAnimationDamagedState(unit,1)]);
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still);
|
||||
|
||||
if (unit.Wait) {
|
||||
unit.Wait--;
|
||||
|
|
|
@ -161,7 +161,7 @@ void HandleActionUpgradeTo(COrder& order, CUnit &unit)
|
|||
}
|
||||
unit.Type->Animations->Upgrade ?
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Upgrade) :
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still[GetAnimationDamagedState(unit,1)]);
|
||||
UnitShowAnimation(unit, unit.Type->Animations->Still);
|
||||
if (unit.Wait) {
|
||||
unit.Wait--;
|
||||
return;
|
||||
|
|
|
@ -51,6 +51,30 @@
|
|||
#include "map.h"
|
||||
#include "sound.h"
|
||||
#include "spells.h"
|
||||
#include "commands.h"
|
||||
|
||||
//SpawnMissile flags
|
||||
#define ANIM_SM_DAMAGE 1
|
||||
#define ANIM_SM_TOTARGET 2
|
||||
#define ANIM_SM_PIXEL 4
|
||||
#define ANIM_SM_RELTARGET 8
|
||||
#define ANIM_SM_RANGED 16
|
||||
|
||||
//IfVar compare types
|
||||
#define IF_GREATER_EQUAL 1
|
||||
#define IF_GREATER 2
|
||||
#define IF_LESS_EQUAL 3
|
||||
#define IF_LESS 4
|
||||
#define IF_EQUAL 5
|
||||
#define IF_NOT_EQUAL 6
|
||||
|
||||
//Modify types
|
||||
#define MOD_ADD 1
|
||||
#define MOD_SUB 2
|
||||
#define MOD_MUL 3
|
||||
#define MOD_DIV 4
|
||||
#define MOD_MOD 5
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Variables
|
||||
|
@ -628,7 +652,6 @@ void COrder::OnAnimationAttack(CUnit &unit)
|
|||
UnHideUnit(unit); // unit is invisible until attacks
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Animation
|
||||
----------------------------------------------------------------------------*/
|
||||
|
@ -658,6 +681,324 @@ int UnitShowAnimation(CUnit &unit, const CAnimation *anim)
|
|||
return UnitShowAnimationScaled(unit, anim, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
** Sets the player data.
|
||||
*/
|
||||
static void SetPlayerData(int player, const char *prop, const char *arg, int value)
|
||||
{
|
||||
if (!strcmp(prop, "RaceName")) {
|
||||
Players[player].Race = value;
|
||||
} else if (!strcmp(prop, "Resources")) {
|
||||
const std::string res(arg);
|
||||
|
||||
for (int i = 0; i < MaxCosts; ++i) {
|
||||
if (res == DefaultResourceNames[i]) {
|
||||
SendCommandSetResource(Players[player].Index, i, value);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Invalid resource \"%s\"" _C_ res.c_str());
|
||||
Exit(1);
|
||||
} else if (!strcmp(prop, "UnitLimit")) {
|
||||
Players[player].UnitLimit = value;
|
||||
} else if (!strcmp(prop, "BuildingLimit")) {
|
||||
Players[player].BuildingLimit = value;
|
||||
} else if (!strcmp(prop, "TotalUnitLimit")) {
|
||||
Players[player].TotalUnitLimit = value;
|
||||
} else if (!strcmp(prop, "Score")) {
|
||||
Players[player].Score = value;
|
||||
} else if (!strcmp(prop, "TotalUnits")) {
|
||||
Players[player].TotalUnits = value;
|
||||
} else if (!strcmp(prop, "TotalBuildings")) {
|
||||
Players[player].TotalBuildings = value;
|
||||
} else if (!strcmp(prop, "TotalResources")) {
|
||||
const std::string res(arg);
|
||||
|
||||
for (int i = 0; i < MaxCosts; ++i) {
|
||||
if (res == DefaultResourceNames[i]) {
|
||||
Players[player].TotalResources[i] = value;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Invalid resource \"%s\"" _C_ res.c_str());
|
||||
Exit(1);
|
||||
} else if (!strcmp(prop, "TotalRazings")) {
|
||||
Players[player].TotalRazings = value;
|
||||
} else if (!strcmp(prop, "TotalKills")) {
|
||||
Players[player].TotalKills = value;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid field: %s" _C_ prop);
|
||||
Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Gets the player data.
|
||||
**
|
||||
** @param player Player number.
|
||||
** @param prop Player's property.
|
||||
** @param arg Additional argument (for resource and unit).
|
||||
**
|
||||
** @return Returning value (only integer).
|
||||
*/
|
||||
static int GetPlayerData(int player, const char *prop, const char *arg)
|
||||
{
|
||||
if (!strcmp(prop, "RaceName")) {
|
||||
return Players[player].Race;
|
||||
} else if (!strcmp(prop, "Resources")) {
|
||||
const std::string resource(arg);
|
||||
|
||||
for (int i = 0; i < MaxCosts; ++i) {
|
||||
if (resource == DefaultResourceNames[i]) {
|
||||
return Players[player].Resources[i];
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Invalid resource \"%s\"", resource.c_str());
|
||||
Exit(1);
|
||||
} else if (!strcmp(prop, "MaxResources")) {
|
||||
const std::string resource(arg);
|
||||
|
||||
for (int i = 0; i < MaxCosts; ++i) {
|
||||
if (resource == DefaultResourceNames[i]) {
|
||||
return Players[player].MaxResources[i];
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Invalid resource \"%s\"", resource.c_str());
|
||||
Exit(1);
|
||||
} else if (!strcmp(prop, "UnitTypesCount")) {
|
||||
const std::string unit(arg);
|
||||
CUnitType *type = UnitTypeByIdent(unit);
|
||||
return Players[player].UnitTypesCount[type->Slot];
|
||||
} else if (!strcmp(prop, "AiEnabled")) {
|
||||
return Players[player].AiEnabled;
|
||||
} else if (!strcmp(prop, "TotalNumUnits")) {
|
||||
return Players[player].TotalNumUnits;
|
||||
} else if (!strcmp(prop, "NumBuildings")) {
|
||||
return Players[player].NumBuildings;
|
||||
} else if (!strcmp(prop, "Supply")) {
|
||||
return Players[player].Supply;
|
||||
} else if (!strcmp(prop, "Demand")) {
|
||||
return Players[player].Demand;
|
||||
} else if (!strcmp(prop, "UnitLimit")) {
|
||||
return Players[player].UnitLimit;
|
||||
} else if (!strcmp(prop, "BuildingLimit")) {
|
||||
return Players[player].BuildingLimit;
|
||||
} else if (!strcmp(prop, "TotalUnitLimit")) {
|
||||
return Players[player].TotalUnitLimit;
|
||||
} else if (!strcmp(prop, "Score")) {
|
||||
return Players[player].Score;
|
||||
} else if (!strcmp(prop, "TotalUnits")) {
|
||||
return Players[player].TotalUnits;
|
||||
} else if (!strcmp(prop, "TotalBuildings")) {
|
||||
return Players[player].TotalBuildings;
|
||||
} else if (!strcmp(prop, "TotalResources")) {
|
||||
const std::string resource(arg);
|
||||
|
||||
for (int i = 0; i < MaxCosts; ++i) {
|
||||
if (resource == DefaultResourceNames[i]) {
|
||||
return Players[player].TotalResources[i];
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Invalid resource \"%s\"", resource.c_str());
|
||||
Exit(1);
|
||||
} else if (!strcmp(prop, "TotalRazings")) {
|
||||
return Players[player].TotalRazings;
|
||||
} else if (!strcmp(prop, "TotalKills")) {
|
||||
return Players[player].TotalKills;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid field: %s" _C_ prop);
|
||||
Exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
** Parse player number in animation frame
|
||||
**
|
||||
** @param unit Unit of the animation.
|
||||
** @param parseint Integer to parse.
|
||||
**
|
||||
** @return The parsed value.
|
||||
*/
|
||||
|
||||
static int ParseAnimPlayer(CUnit &unit, const char *parseint){
|
||||
if (!strcmp(parseint, "this")) {
|
||||
return unit.Player->Index;
|
||||
}
|
||||
return atoi(parseint);
|
||||
}
|
||||
|
||||
/**
|
||||
** Parse flags list in animation frame.
|
||||
**
|
||||
** @param unit Unit of the animation.
|
||||
** @param parseflag Flag list to parse.
|
||||
**
|
||||
** @return The parsed value.
|
||||
*/
|
||||
int ParseAnimFlags(CUnit &unit, const char *parseflag)
|
||||
{
|
||||
char s[100];
|
||||
int flags = 0;
|
||||
|
||||
strcpy(s, parseflag);
|
||||
char* cur = s;
|
||||
char* next = s;
|
||||
while (next){
|
||||
next = strchr(cur, '.');
|
||||
if (next){
|
||||
*next = '\0';
|
||||
++next;
|
||||
}
|
||||
if (unit.Anim.Anim->Type == AnimationSpawnMissile) {
|
||||
if (!strcmp(cur, "damage")) {
|
||||
flags |= ANIM_SM_DAMAGE;
|
||||
} else if (!strcmp(cur, "totarget")) {
|
||||
flags |= ANIM_SM_TOTARGET;
|
||||
} else if (!strcmp(cur, "pixel")) {
|
||||
flags |= ANIM_SM_PIXEL;
|
||||
} else if (!strcmp(cur, "reltarget")) {
|
||||
flags |= ANIM_SM_RELTARGET;
|
||||
} else if (!strcmp(cur, "ranged")) {
|
||||
flags |= ANIM_SM_RANGED;
|
||||
}
|
||||
}
|
||||
cur = next;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
** Parse integer in animation frame.
|
||||
**
|
||||
** @param unit Unit of the animation.
|
||||
** @param parseint Integer to parse.
|
||||
**
|
||||
** @return The parsed value.
|
||||
*/
|
||||
|
||||
int ParseAnimInt(CUnit *unit, const char *parseint)
|
||||
{
|
||||
char s[100];
|
||||
const CUnit* goal = unit;
|
||||
|
||||
strncpy(s, parseint, strlen(parseint) + 1);
|
||||
char* cur = &s[2];
|
||||
if ((s[0] == 'v' || s[0] == 't') && unit != NULL) { //unit variable detected
|
||||
if (s[0] == 't') {
|
||||
if (unit->CurrentOrder()->HasGoal()) {
|
||||
goal = unit->CurrentOrder()->GetGoal();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
char* next = strchr(cur, '.');
|
||||
if (next == NULL) {
|
||||
fprintf(stderr, "Need also specify the variable '%s' tag \n", cur);
|
||||
Exit(1);
|
||||
} else {
|
||||
*next = '\0';
|
||||
}
|
||||
const int index = UnitTypeVar.VariableNameLookup[cur];// User variables
|
||||
if (index == -1) {
|
||||
if (!strcmp(cur, "ResourcesHeld")) {
|
||||
return goal->ResourcesHeld;
|
||||
}
|
||||
fprintf(stderr, "Bad variable name '%s'\n", cur);
|
||||
Exit(1);
|
||||
}
|
||||
if (!strcmp(next + 1,"Value")) {
|
||||
return goal->Variable[index].Value;
|
||||
} else if (!strcmp(next + 1, "Max")) {
|
||||
return goal->Variable[index].Max;
|
||||
} else if (!strcmp(next + 1, "Increase")) {
|
||||
return goal->Variable[index].Increase;
|
||||
} else if (!strcmp(next + 1, "Enable")) {
|
||||
return goal->Variable[index].Enable;
|
||||
} else if (!strcmp(next + 1, "Percent")) {
|
||||
return goal->Variable[index].Value * 100 / goal->Variable[index].Max;
|
||||
}
|
||||
return 0;
|
||||
} else if (s[0] == 'p' && unit != NULL) { //player variable detected
|
||||
char* next = strchr(cur, '.');
|
||||
if (next == NULL) {
|
||||
fprintf(stderr, "Need also specify the %s player's property\n", cur);
|
||||
Exit(1);
|
||||
} else {
|
||||
*next='\0';
|
||||
}
|
||||
char *arg = strchr(next + 1, '.');
|
||||
if (arg != NULL) {
|
||||
*arg = '\0';
|
||||
}
|
||||
return GetPlayerData(ParseAnimPlayer(*unit, cur), next + 1, arg + 1);
|
||||
} else if (s[0] == 'r') { //random value
|
||||
return SyncRand(atoi(cur));
|
||||
}
|
||||
return atoi(parseint);
|
||||
}
|
||||
|
||||
/**
|
||||
** Find the nearest position at which unit can be placed.
|
||||
**
|
||||
** @param type Type of the dropped unit.
|
||||
** @param goalPos Goal map tile position.
|
||||
** @param resPos Holds the nearest point.
|
||||
** @param heading preferense side to drop out of.
|
||||
*/
|
||||
static void FindNearestDrop(const CUnitType &type, const Vec2i &goalPos, Vec2i &resPos, int heading)
|
||||
{
|
||||
int addx = 0;
|
||||
int addy = 0;
|
||||
Vec2i pos = goalPos;
|
||||
|
||||
if (heading < LookingNE || heading > LookingNW) {
|
||||
goto starts;
|
||||
} else if (heading < LookingSE) {
|
||||
goto startw;
|
||||
} else if (heading < LookingSW) {
|
||||
goto startn;
|
||||
} else {
|
||||
goto starte;
|
||||
}
|
||||
|
||||
// FIXME: don't search outside of the map
|
||||
for (;;) {
|
||||
startw:
|
||||
for (int i = addy; i--; ++pos.y) {
|
||||
if (UnitTypeCanBeAt(type, pos)) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
++addx;
|
||||
starts:
|
||||
for (int i = addx; i--; ++pos.x) {
|
||||
if (UnitTypeCanBeAt(type, pos)) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
++addy;
|
||||
starte:
|
||||
for (int i = addy; i--; --pos.y) {
|
||||
if (UnitTypeCanBeAt(type, pos)) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
++addx;
|
||||
startn:
|
||||
for (int i = addx; i--; --pos.x) {
|
||||
if (UnitTypeCanBeAt(type, pos)) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
++addy;
|
||||
}
|
||||
|
||||
found:
|
||||
resPos = pos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
** Show unit animation.
|
||||
**
|
||||
|
@ -669,9 +1010,6 @@ int UnitShowAnimation(CUnit &unit, const CAnimation *anim)
|
|||
*/
|
||||
int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scale)
|
||||
{
|
||||
int move;
|
||||
int x,y;
|
||||
|
||||
// Changing animations
|
||||
if (anim && unit.Anim.CurrAnim != anim) {
|
||||
// Assert fails when transforming unit (upgrade-to).
|
||||
|
@ -692,20 +1030,20 @@ int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scale)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
move = 0;
|
||||
int move = 0;
|
||||
while (!unit.Anim.Wait) {
|
||||
switch (unit.Anim.Anim->Type) {
|
||||
case AnimationFrame:
|
||||
unit.Frame = unit.Anim.Anim->D.Frame.Frame;
|
||||
unit.Frame = ParseAnimInt(&unit, unit.Anim.Anim->D.Frame.Frame);
|
||||
UnitUpdateHeading(unit);
|
||||
break;
|
||||
|
||||
case AnimationExactFrame:
|
||||
unit.Frame = unit.Anim.Anim->D.Frame.Frame;
|
||||
unit.Frame = ParseAnimInt(&unit, unit.Anim.Anim->D.Frame.Frame);
|
||||
break;
|
||||
|
||||
case AnimationWait:
|
||||
unit.Anim.Wait = unit.Anim.Anim->D.Wait.Wait << scale >> 8;
|
||||
unit.Anim.Wait = ParseAnimInt(&unit, unit.Anim.Anim->D.Wait.Wait) << scale >> 8;
|
||||
if (unit.Variable[SLOW_INDEX].Value) { // unit is slowed down
|
||||
unit.Anim.Wait <<= 1;
|
||||
}
|
||||
|
@ -716,10 +1054,13 @@ int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scale)
|
|||
unit.Anim.Wait = 1;
|
||||
break;
|
||||
case AnimationRandomWait:
|
||||
unit.Anim.Wait = unit.Anim.Anim->D.RandomWait.MinWait +
|
||||
SyncRand() % (unit.Anim.Anim->D.RandomWait.MaxWait - unit.Anim.Anim->D.RandomWait.MinWait + 1);
|
||||
break;
|
||||
{
|
||||
const int arg1 = ParseAnimInt(&unit, unit.Anim.Anim->D.RandomWait.MinWait);
|
||||
const int arg2 = ParseAnimInt(&unit, unit.Anim.Anim->D.RandomWait.MaxWait);
|
||||
|
||||
unit.Anim.Wait = arg1 + SyncRand() % (arg2 - arg1 + 1);
|
||||
break;
|
||||
}
|
||||
case AnimationSound:
|
||||
if (unit.IsVisible(*ThisPlayer) || ReplayRevealMap) {
|
||||
PlayUnitSound(unit, unit.Anim.Anim->D.Sound.Sound);
|
||||
|
@ -727,8 +1068,7 @@ int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scale)
|
|||
break;
|
||||
case AnimationRandomSound:
|
||||
if (unit.IsVisible(*ThisPlayer) || ReplayRevealMap) {
|
||||
int sound;
|
||||
sound = SyncRand() % unit.Anim.Anim->D.RandomSound.NumSounds;
|
||||
const int sound = SyncRand() % unit.Anim.Anim->D.RandomSound.NumSounds;
|
||||
PlayUnitSound(unit, unit.Anim.Anim->D.RandomSound.Sound[sound]);
|
||||
}
|
||||
break;
|
||||
|
@ -739,25 +1079,256 @@ int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scale)
|
|||
break;
|
||||
}
|
||||
case AnimationSpawnMissile:
|
||||
x = unit.tilePos.x * PixelTileSize.x + PixelTileSize.x / 2;
|
||||
y = unit.tilePos.y * PixelTileSize.y + PixelTileSize.y / 2;
|
||||
MakeMissile(MissileTypeByIdent(unit.Anim.Anim->D.SpawnMissile.Missile),x,y,x,y);
|
||||
{
|
||||
const int startx = ParseAnimInt(&unit, unit.Anim.Anim->D.SpawnMissile.StartX);
|
||||
const int starty = ParseAnimInt(&unit, unit.Anim.Anim->D.SpawnMissile.StartY);
|
||||
const int destx = ParseAnimInt(&unit, unit.Anim.Anim->D.SpawnMissile.DestX);
|
||||
const int desty = ParseAnimInt(&unit, unit.Anim.Anim->D.SpawnMissile.DestY);
|
||||
const int flags = ParseAnimFlags(unit, unit.Anim.Anim->D.SpawnMissile.Flags);
|
||||
CUnit *goal;
|
||||
int x, y, dx, dy;
|
||||
|
||||
if ((flags & ANIM_SM_RELTARGET) && unit.CurrentOrder()->HasGoal()) {
|
||||
goal = unit.CurrentOrder()->GetGoal();
|
||||
} else {
|
||||
goal = &unit;
|
||||
}
|
||||
if ((flags & ANIM_SM_PIXEL)) {
|
||||
x = goal->tilePos.x * PixelTileSize.x + goal->IX + startx;
|
||||
y = goal->tilePos.y * PixelTileSize.y + goal->IY + starty;
|
||||
} else {
|
||||
x = (goal->tilePos.x + startx) * PixelTileSize.x + PixelTileSize.x / 2;
|
||||
y = (goal->tilePos.y + starty) * PixelTileSize.y + PixelTileSize.y / 2;
|
||||
}
|
||||
if ((flags & ANIM_SM_TOTARGET) && goal->CurrentOrder()->HasGoal()) {
|
||||
CUnit &target = *goal->CurrentOrder()->GetGoal();
|
||||
|
||||
if (flags & ANIM_SM_PIXEL) {
|
||||
dx = target.tilePos.x * PixelTileSize.x + target.IX;
|
||||
dy = target.tilePos.y * PixelTileSize.y + target.IY;
|
||||
} else {
|
||||
dx = target.tilePos.x * PixelTileSize.x + PixelTileSize.x / 2;
|
||||
dy = target.tilePos.y * PixelTileSize.y + PixelTileSize.y / 2;
|
||||
}
|
||||
} else {
|
||||
if ((flags & ANIM_SM_PIXEL)) {
|
||||
dx = goal->tilePos.x * PixelTileSize.x + goal->IX + destx;
|
||||
dy = goal->tilePos.y * PixelTileSize.y + goal->IY + desty;
|
||||
} else {
|
||||
dx = (unit.tilePos.x + destx) * PixelTileSize.x + PixelTileSize.x / 2;
|
||||
dy = (unit.tilePos.y + desty) * PixelTileSize.y + PixelTileSize.y / 2;
|
||||
}
|
||||
}
|
||||
const int dist = goal->MapDistanceTo(dx, dy);
|
||||
if ((flags & ANIM_SM_RANGED) && !(flags & ANIM_SM_PIXEL)
|
||||
&& dist > goal->Stats->Variables[ATTACKRANGE_INDEX].Max
|
||||
&& dist < goal->Type->MinAttackRange) {
|
||||
} else {
|
||||
Missile *missile = MakeMissile(MissileTypeByIdent(unit.Anim.Anim->D.SpawnMissile.Missile), x, y, dx, dy);
|
||||
if (flags & ANIM_SM_DAMAGE) {
|
||||
missile->SourceUnit = &unit;
|
||||
}
|
||||
if ((flags & ANIM_SM_TOTARGET) && unit.CurrentOrder()->HasGoal()) {
|
||||
missile->TargetUnit = goal->CurrentOrder()->GetGoal();
|
||||
} else {
|
||||
missile->TargetUnit = goal;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AnimationSpawnUnit:
|
||||
{
|
||||
const int offX = ParseAnimInt(&unit, unit.Anim.Anim->D.SpawnUnit.OffX);
|
||||
const int offY = ParseAnimInt(&unit, unit.Anim.Anim->D.SpawnUnit.OffY);
|
||||
const int range = ParseAnimInt(&unit, unit.Anim.Anim->D.SpawnUnit.Range);
|
||||
const int playerId = ParseAnimPlayer(unit, unit.Anim.Anim->D.SpawnUnit.Player);
|
||||
CPlayer &player = Players[playerId];
|
||||
const Vec2i pos = { unit.tilePos.x + offX, unit.tilePos.y + offY};
|
||||
CUnitType *type = UnitTypeByIdent(unit.Anim.Anim->D.SpawnUnit.Unit);
|
||||
Vec2i resPos;
|
||||
//DebugPrint("Creating a %s\n" _C_ unittype.Name.c_str());
|
||||
FindNearestDrop(*type, pos, resPos, LookingW);
|
||||
if (MapDistance(pos, resPos) <= range) {
|
||||
CUnit *target = MakeUnit(*type, &player);
|
||||
if (target != NoUnitP) {
|
||||
target->tilePos = pos;
|
||||
target->Place(resPos);
|
||||
//DropOutOnSide(*target, LookingW, NULL);
|
||||
} else {
|
||||
DebugPrint("Unable to allocate Unit");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AnimationIfVar:
|
||||
{
|
||||
const int lop = ParseAnimInt(&unit, unit.Anim.Anim->D.IfVar.LeftVar);
|
||||
const int rop = ParseAnimInt(&unit, unit.Anim.Anim->D.IfVar.RightVar);
|
||||
bool go = false;
|
||||
|
||||
switch (unit.Anim.Anim->D.IfVar.Type) {
|
||||
case IF_GREATER_EQUAL:
|
||||
go = (lop >= rop);
|
||||
break;
|
||||
case IF_GREATER:
|
||||
go = (lop > rop);
|
||||
break;
|
||||
case IF_LESS_EQUAL:
|
||||
go = (lop <= rop);
|
||||
break;
|
||||
case IF_LESS:
|
||||
go = (lop < rop);
|
||||
break;
|
||||
case IF_EQUAL:
|
||||
go = (lop == rop);
|
||||
break;
|
||||
case IF_NOT_EQUAL:
|
||||
go = (lop != rop);
|
||||
break;
|
||||
}
|
||||
if (go) {
|
||||
unit.Anim.Anim = unit.Anim.Anim->D.IfVar.Goto;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AnimationSetVar:
|
||||
{
|
||||
char arg1[128];
|
||||
|
||||
strcpy(arg1, unit.Anim.Anim->D.SetVar.Var);
|
||||
const int rop = ParseAnimInt(&unit, unit.Anim.Anim->D.SetVar.Value);
|
||||
|
||||
char *next = strchr(arg1, '.');
|
||||
if (next == NULL) {
|
||||
fprintf(stderr, "Need also specify the variable '%s' tag \n" _C_ arg1);
|
||||
Exit(1);
|
||||
} else {
|
||||
*next ='\0';
|
||||
}
|
||||
const int index = UnitTypeVar.VariableNameLookup[arg1];// User variables
|
||||
if (index == -1) {
|
||||
fprintf(stderr, "Bad variable name '%s'\n" _C_ arg1);
|
||||
Exit(1);
|
||||
}
|
||||
int value = 0;
|
||||
if (!strcmp(next + 1, "Value")) {
|
||||
value = unit.Variable[index].Value;
|
||||
} else if (!strcmp(next + 1, "Max")) {
|
||||
value = unit.Variable[index].Max;
|
||||
} else if (!strcmp(next + 1,"Increase")) {
|
||||
value = unit.Variable[index].Increase;
|
||||
} else if (!strcmp(next + 1, "Enable")) {
|
||||
value = unit.Variable[index].Enable;
|
||||
}
|
||||
switch (unit.Anim.Anim->D.SetVar.Mod) {
|
||||
case MOD_ADD:
|
||||
value += rop;
|
||||
break;
|
||||
case MOD_SUB:
|
||||
value -= rop;
|
||||
break;
|
||||
case MOD_MUL:
|
||||
value *= rop;
|
||||
break;
|
||||
case MOD_DIV:
|
||||
if (!rop) {
|
||||
fprintf(stderr, "Division by zero in AnimationSetVar\n");
|
||||
Exit(1);
|
||||
return 0;
|
||||
}
|
||||
value /= rop;
|
||||
break;
|
||||
case MOD_MOD:
|
||||
if (!rop) {
|
||||
fprintf(stderr, "Division by zero in AnimationSetVar\n");
|
||||
Exit(1);
|
||||
return 0;
|
||||
}
|
||||
value %= rop;
|
||||
break;
|
||||
default:
|
||||
value = rop;
|
||||
}
|
||||
if (!strcmp(next + 1, "Value")) {
|
||||
unit.Variable[index].Value = value;
|
||||
} else if (!strcmp(next + 1, "Max")) {
|
||||
unit.Variable[index].Max = value;
|
||||
} else if (!strcmp(next + 1, "Increase")) {
|
||||
unit.Variable[index].Increase = value;
|
||||
} else if (!strcmp(next + 1, "Enable")) {
|
||||
unit.Variable[index].Enable = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AnimationSetPlayerVar:
|
||||
{
|
||||
const char *var = unit.Anim.Anim->D.SetPlayerVar.Var;
|
||||
const char *arg = unit.Anim.Anim->D.SetPlayerVar.Arg;
|
||||
int playerId = ParseAnimPlayer(unit, unit.Anim.Anim->D.SetPlayerVar.Player);
|
||||
int rop = ParseAnimInt(&unit, unit.Anim.Anim->D.SetPlayerVar.Value);
|
||||
int data = GetPlayerData(playerId, var, arg);
|
||||
|
||||
switch (unit.Anim.Anim->D.SetPlayerVar.Mod) {
|
||||
case MOD_ADD:
|
||||
data += rop;
|
||||
break;
|
||||
case MOD_SUB:
|
||||
data -= rop;
|
||||
break;
|
||||
case MOD_MUL:
|
||||
data *= rop;
|
||||
break;
|
||||
case MOD_DIV:
|
||||
if (!rop) {
|
||||
fprintf(stderr, "Division by zero in AnimationSetPlayerVar\n");
|
||||
Exit(1);
|
||||
return 0;
|
||||
}
|
||||
data /= rop;
|
||||
break;
|
||||
case MOD_MOD:
|
||||
if (!rop) {
|
||||
fprintf(stderr, "Division by zero in AnimationSetPlayerVar\n");
|
||||
Exit(1);
|
||||
return 0;
|
||||
}
|
||||
data %= rop;
|
||||
break;
|
||||
default:
|
||||
data = rop;
|
||||
}
|
||||
rop = data;
|
||||
SetPlayerData(playerId, var, arg, rop);
|
||||
break;
|
||||
}
|
||||
case AnimationDie:
|
||||
if (unit.Anim.Anim->D.Die.DeathType[0] != '\0') {
|
||||
unit.DamagedType = ExtraDeathIndex(unit.Anim.Anim->D.Die.DeathType);
|
||||
}
|
||||
LetUnitDie(unit);
|
||||
return 0;
|
||||
|
||||
case AnimationRotate:
|
||||
UnitRotate(unit, unit.Anim.Anim->D.Rotate.Rotate);
|
||||
if (!strcmp(unit.Anim.Anim->D.Rotate.Rotate, "target") && unit.CurrentOrder()->HasGoal()) {
|
||||
const CUnit &target = *unit.CurrentOrder()->GetGoal();
|
||||
const Vec2i pos = target.tilePos + target.Type->GetHalfTileSize() - unit.tilePos;
|
||||
UnitHeadingFromDeltaXY(unit, pos);
|
||||
} else {
|
||||
UnitRotate(unit, ParseAnimPlayer(unit, unit.Anim.Anim->D.Rotate.Rotate));
|
||||
}
|
||||
break;
|
||||
|
||||
case AnimationRandomRotate:
|
||||
if ((SyncRand() >> 8) & 1) {
|
||||
UnitRotate(unit, -unit.Anim.Anim->D.Rotate.Rotate);
|
||||
UnitRotate(unit, -ParseAnimPlayer(unit, unit.Anim.Anim->D.Rotate.Rotate));
|
||||
} else {
|
||||
UnitRotate(unit, unit.Anim.Anim->D.Rotate.Rotate);
|
||||
UnitRotate(unit, ParseAnimPlayer(unit, unit.Anim.Anim->D.Rotate.Rotate));
|
||||
}
|
||||
break;
|
||||
|
||||
case AnimationMove:
|
||||
Assert(!move);
|
||||
move = unit.Anim.Anim->D.Move.Move;
|
||||
move = ParseAnimPlayer(unit, unit.Anim.Anim->D.Move.Move);
|
||||
break;
|
||||
|
||||
case AnimationUnbreakable:
|
||||
|
@ -776,7 +1347,7 @@ int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scale)
|
|||
unit.Anim.Anim = unit.Anim.Anim->D.Goto.Goto;
|
||||
break;
|
||||
case AnimationRandomGoto:
|
||||
if (SyncRand() % 100 < unit.Anim.Anim->D.RandomGoto.Random) {
|
||||
if (SyncRand() % 100 < ParseAnimPlayer(unit, unit.Anim.Anim->D.RandomGoto.Random)) {
|
||||
unit.Anim.Anim = unit.Anim.Anim->D.RandomGoto.Goto;
|
||||
}
|
||||
break;
|
||||
|
@ -997,7 +1568,6 @@ static void HandleBuffs(CUnit &unit, int amount)
|
|||
unit.Variable[INVISIBLE_INDEX].Increase = -amount;
|
||||
unit.Variable[UNHOLYARMOR_INDEX].Increase = -amount;
|
||||
|
||||
|
||||
unit.Variable[SHIELD_INDEX].Increase = 1;
|
||||
|
||||
// User defined variables
|
||||
|
|
|
@ -347,6 +347,8 @@ extern HandleActionFunc HandleActionRepair;
|
|||
extern HandleActionFunc HandleActionPatrol;
|
||||
/// Show attack animation
|
||||
extern void AnimateActionAttack(CUnit &unit);
|
||||
/// Show spell cast animation
|
||||
extern void AnimateActionSpellCast(CUnit &unit);
|
||||
/// Handle command attack
|
||||
extern HandleActionFunc HandleActionAttack;
|
||||
/// Handle command board
|
||||
|
|
|
@ -63,7 +63,12 @@ enum AnimationType {
|
|||
AnimationLabel,
|
||||
AnimationGoto,
|
||||
AnimationRandomGoto,
|
||||
AnimationSpawnMissile
|
||||
AnimationSpawnMissile,
|
||||
AnimationSpawnUnit,
|
||||
AnimationIfVar,
|
||||
AnimationSetVar,
|
||||
AnimationSetPlayerVar,
|
||||
AnimationDie
|
||||
};
|
||||
|
||||
class CAnimation {
|
||||
|
@ -73,10 +78,58 @@ public:
|
|||
}
|
||||
|
||||
~CAnimation() {
|
||||
if (Type == AnimationSound)
|
||||
if (Type == AnimationFrame || Type == AnimationExactFrame)
|
||||
delete[] D.Frame.Frame;
|
||||
else if (Type == AnimationWait)
|
||||
delete[] D.Wait.Wait;
|
||||
else if (Type == AnimationRandomWait) {
|
||||
delete[] D.RandomWait.MinWait;
|
||||
delete[] D.RandomWait.MaxWait;
|
||||
}
|
||||
else if (Type == AnimationRotate || Type == AnimationRandomRotate)
|
||||
delete[] D.Rotate.Rotate;
|
||||
else if (Type == AnimationMove)
|
||||
delete[] D.Move.Move;
|
||||
else if (Type == AnimationSound)
|
||||
delete[] D.Sound.Name;
|
||||
else if (Type == AnimationSpawnMissile)
|
||||
{
|
||||
delete[] D.SpawnMissile.Missile;
|
||||
delete[] D.SpawnMissile.StartX;
|
||||
delete[] D.SpawnMissile.StartY;
|
||||
delete[] D.SpawnMissile.DestX;
|
||||
delete[] D.SpawnMissile.DestY;
|
||||
delete[] D.SpawnMissile.Flags;
|
||||
}
|
||||
else if (Type == AnimationSpawnUnit)
|
||||
{
|
||||
delete[] D.SpawnUnit.Unit;
|
||||
delete[] D.SpawnUnit.OffX;
|
||||
delete[] D.SpawnUnit.OffY;
|
||||
delete[] D.SpawnUnit.Range;
|
||||
delete[] D.SpawnUnit.Player;
|
||||
}
|
||||
else if (Type == AnimationIfVar)
|
||||
{
|
||||
delete[] D.IfVar.LeftVar;
|
||||
delete[] D.IfVar.RightVar;
|
||||
}
|
||||
else if (Type == AnimationSetVar)
|
||||
{
|
||||
delete[] D.SetVar.Var;
|
||||
delete[] D.SetVar.Value;
|
||||
}
|
||||
else if (Type == AnimationSetPlayerVar)
|
||||
{
|
||||
delete[] D.SetPlayerVar.Player;
|
||||
delete[] D.SetPlayerVar.Var;
|
||||
delete[] D.SetPlayerVar.Arg;
|
||||
delete[] D.SetPlayerVar.Value;
|
||||
}
|
||||
else if (Type == AnimationDie)
|
||||
{
|
||||
delete[] D.Die.DeathType;
|
||||
}
|
||||
else if (Type == AnimationRandomSound) {
|
||||
for (unsigned int i = 0; i < D.RandomSound.NumSounds; ++i) {
|
||||
delete[] D.RandomSound.Name[i];
|
||||
|
@ -89,14 +142,14 @@ public:
|
|||
AnimationType Type;
|
||||
union {
|
||||
struct {
|
||||
int Frame;
|
||||
const char *Frame;
|
||||
} Frame;
|
||||
struct {
|
||||
int Wait;
|
||||
const char *Wait;
|
||||
} Wait;
|
||||
struct {
|
||||
int MinWait;
|
||||
int MaxWait;
|
||||
const char *MinWait;
|
||||
const char *MaxWait;
|
||||
} RandomWait;
|
||||
struct {
|
||||
const char *Name;
|
||||
|
@ -108,10 +161,10 @@ public:
|
|||
unsigned int NumSounds;
|
||||
} RandomSound;
|
||||
struct {
|
||||
int Rotate;
|
||||
const char *Rotate;
|
||||
} Rotate;
|
||||
struct {
|
||||
int Move;
|
||||
const char *Move;
|
||||
} Move;
|
||||
struct {
|
||||
int Begin;
|
||||
|
@ -120,61 +173,90 @@ public:
|
|||
CAnimation *Goto;
|
||||
} Goto;
|
||||
struct {
|
||||
int Random;
|
||||
const char *Random;
|
||||
CAnimation *Goto;
|
||||
} RandomGoto;
|
||||
struct {
|
||||
const char *Missile;
|
||||
const char *StartX;
|
||||
const char *StartY;
|
||||
const char *DestX;
|
||||
const char *DestY;
|
||||
const char *Flags;
|
||||
} SpawnMissile;
|
||||
struct {
|
||||
const char *Unit;
|
||||
const char *OffX;
|
||||
const char *OffY;
|
||||
const char *Range;
|
||||
const char *Player;
|
||||
} SpawnUnit;
|
||||
struct {
|
||||
const char *LeftVar;
|
||||
const char *RightVar;
|
||||
int Type;
|
||||
CAnimation *Goto;
|
||||
} IfVar;
|
||||
struct {
|
||||
int Mod;
|
||||
const char *Var;
|
||||
const char *Value;
|
||||
} SetVar;
|
||||
struct {
|
||||
int Mod;
|
||||
const char *Player;
|
||||
const char *Var;
|
||||
const char *Arg;
|
||||
const char *Value;
|
||||
} SetPlayerVar;
|
||||
struct {
|
||||
const char *DeathType;
|
||||
} Die;
|
||||
} D;
|
||||
CAnimation *Next;
|
||||
};
|
||||
|
||||
class CAnimations {
|
||||
public:
|
||||
CAnimations() : Start(NULL),
|
||||
Repair(NULL), Train(NULL), Research(NULL),
|
||||
Upgrade(NULL), Build(NULL)
|
||||
CAnimations() : Attack(NULL), Build(NULL), Move(NULL), Repair(NULL),
|
||||
Research(NULL), SpellCast(NULL), Start(NULL), Still(NULL),
|
||||
Train(NULL), Upgrade(NULL)
|
||||
{
|
||||
memset(Still, 0, sizeof(Still));
|
||||
memset(Move, 0, sizeof(Move));
|
||||
memset(Attack, 0, sizeof(Attack));
|
||||
memset(Harvest, 0, sizeof(Harvest));
|
||||
memset(Death, 0, sizeof(Death));
|
||||
memset(Death, 0, sizeof (Death));
|
||||
memset(Harvest, 0, sizeof (Harvest));
|
||||
}
|
||||
|
||||
~CAnimations() {
|
||||
delete[] Start;
|
||||
delete[] Repair;
|
||||
delete[] Train;
|
||||
delete[] Research;
|
||||
delete[] Upgrade;
|
||||
delete[] Attack;
|
||||
delete[] Build;
|
||||
for ( int i = 0; i< MaxCosts; ++i) {
|
||||
delete[] Harvest[i];
|
||||
}
|
||||
for ( int i = 0; i< ANIMATIONS_DEATHTYPES+1; ++i) {
|
||||
for ( int i = 0; i < ANIMATIONS_DEATHTYPES + 1; ++i) {
|
||||
delete[] Death[i];
|
||||
}
|
||||
for ( int i = 0; i< 100; ++i) {
|
||||
delete[] Still[i];
|
||||
delete[] Move[i];
|
||||
delete[] Attack[i];
|
||||
for (int i = 0; i < MaxCosts; ++i) {
|
||||
delete[] Harvest[i];
|
||||
}
|
||||
|
||||
delete[] Move;
|
||||
delete[] Repair;
|
||||
delete[] Research;
|
||||
delete[] SpellCast;
|
||||
delete[] Start;
|
||||
delete[] Still;
|
||||
delete[] Train;
|
||||
delete[] Upgrade;
|
||||
}
|
||||
|
||||
CAnimation *Start;
|
||||
CAnimation *Still[100];
|
||||
CAnimation *Death[ANIMATIONS_DEATHTYPES+1];
|
||||
CAnimation *Attack[100];
|
||||
CAnimation *Move[100];
|
||||
CAnimation *Repair;
|
||||
CAnimation *Train;
|
||||
CAnimation *Research;
|
||||
CAnimation *Upgrade;
|
||||
CAnimation *Attack;
|
||||
CAnimation *Build;
|
||||
CAnimation *Death[ANIMATIONS_DEATHTYPES + 1];
|
||||
CAnimation *Harvest[MaxCosts];
|
||||
CAnimation *Move;
|
||||
CAnimation *Repair;
|
||||
CAnimation *Research;
|
||||
CAnimation *SpellCast;
|
||||
CAnimation *Start;
|
||||
CAnimation *Still;
|
||||
CAnimation *Train;
|
||||
CAnimation *Upgrade;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1073,8 +1073,8 @@ extern CUnit *CanBuildHere(const CUnit *unit, const CUnitType &type, const Vec2i
|
|||
extern bool CanBuildOn(const Vec2i &pos, int mask);
|
||||
/// FIXME: more docu
|
||||
extern CUnit *CanBuildUnitType(const CUnit *unit, const CUnitType &type, const Vec2i &pos, int real);
|
||||
/// Get the suitable animation frame when unit is damaged.
|
||||
extern int GetAnimationDamagedState(CUnit &unit, int anim);
|
||||
/// Get the suitable animation frame depends of unit's damaged type.
|
||||
extern int ExtraDeathIndex(const char *death);
|
||||
|
||||
/// Find resource
|
||||
extern CUnit *UnitFindResource(const CUnit &unit, const Vec2i &startPos, int range,
|
||||
|
|
|
@ -1053,7 +1053,7 @@ public:
|
|||
}
|
||||
bool CanMove() const
|
||||
{
|
||||
return Animations && Animations->Move[99];
|
||||
return Animations && Animations->Move;
|
||||
}
|
||||
|
||||
bool CanSelect(GroupSelectionMode mode = SELECTABLE_BY_RECTANGLE_ONLY) const
|
||||
|
|
|
@ -98,11 +98,9 @@ static void MapAnimSounds(CUnitType *type)
|
|||
}
|
||||
|
||||
MapAnimSounds2(type->Animations->Start);
|
||||
for (i = 0; i < 100; ++i) {
|
||||
MapAnimSounds2(type->Animations->Still[i]);
|
||||
MapAnimSounds2(type->Animations->Move[i]);
|
||||
MapAnimSounds2(type->Animations->Attack[i]);
|
||||
}
|
||||
MapAnimSounds2(type->Animations->Still);
|
||||
MapAnimSounds2(type->Animations->Move);
|
||||
MapAnimSounds2(type->Animations->Attack);
|
||||
for (i = 0; i <= ANIMATIONS_DEATHTYPES; ++i) {
|
||||
MapAnimSounds2(type->Animations->Death[i]);
|
||||
}
|
||||
|
|
|
@ -1413,6 +1413,7 @@ static void ParseAnimationFrame(lua_State *l, const char *str,
|
|||
std::string all2;
|
||||
char* op2;
|
||||
int index;
|
||||
char *next;
|
||||
|
||||
index = op1.find(' ');
|
||||
|
||||
|
@ -1428,27 +1429,32 @@ static void ParseAnimationFrame(lua_State *l, const char *str,
|
|||
}
|
||||
if (op1 == "frame") {
|
||||
anim->Type = AnimationFrame;
|
||||
anim->D.Frame.Frame = atoi(op2);
|
||||
anim->D.Frame.Frame = new_strdup(op2);
|
||||
} else if (op1 == "exact-frame") {
|
||||
anim->Type = AnimationExactFrame;
|
||||
anim->D.Frame.Frame = atoi(op2);
|
||||
anim->D.Frame.Frame = new_strdup(op2);
|
||||
} else if (op1 == "wait") {
|
||||
anim->Type = AnimationWait;
|
||||
anim->D.Wait.Wait = atoi(op2);
|
||||
anim->D.Wait.Wait = new_strdup(op2);
|
||||
} else if (op1 == "random-wait") {
|
||||
anim->Type = AnimationRandomWait;
|
||||
anim->D.RandomWait.MinWait = atoi(op2);
|
||||
op2 = strchr(op2, ' ');
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.RandomWait.MinWait = new_strdup(op2);
|
||||
op2 = next;
|
||||
while (*op2 == ' ') {
|
||||
++op2;
|
||||
}
|
||||
anim->D.RandomWait.MaxWait = atoi(op2);
|
||||
anim->D.RandomWait.MaxWait = new_strdup(op2);
|
||||
} else if (op1 == "sound") {
|
||||
anim->Type = AnimationSound;
|
||||
anim->D.Sound.Name = new_strdup(op2);
|
||||
} else if (op1 == "random-sound") {
|
||||
int count;
|
||||
char *next;
|
||||
|
||||
anim->Type = AnimationRandomSound;
|
||||
count = 0;
|
||||
|
@ -1471,16 +1477,195 @@ static void ParseAnimationFrame(lua_State *l, const char *str,
|
|||
anim->Type = AnimationAttack;
|
||||
} else if (op1 == "spawn-missile") {
|
||||
anim->Type = AnimationSpawnMissile;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SpawnMissile.Missile = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SpawnMissile.StartX = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
anim->D.SpawnMissile.StartY = new_strdup(op2);
|
||||
}
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
anim->D.SpawnMissile.DestX = new_strdup(op2);
|
||||
}
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
anim->D.SpawnMissile.DestY = new_strdup(op2);
|
||||
}
|
||||
op2 = next;
|
||||
if (next) {
|
||||
while (*op2 == ' ') {
|
||||
++op2;
|
||||
}
|
||||
anim->D.SpawnMissile.Flags = new_strdup(op2);
|
||||
}
|
||||
} else if (op1 == "spawn-unit") {
|
||||
anim->Type = AnimationSpawnUnit;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SpawnUnit.Unit = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SpawnUnit.OffX = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SpawnUnit.OffY = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SpawnUnit.Range = new_strdup(op2);
|
||||
op2 = next;
|
||||
while (*op2 == ' ') {
|
||||
++op2;
|
||||
}
|
||||
anim->D.SpawnUnit.Player = new_strdup(op2);
|
||||
} else if (op1 == "if-var") {
|
||||
anim->Type = AnimationIfVar;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.IfVar.LeftVar = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.IfVar.RightVar = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.IfVar.Type = atoi(op2);
|
||||
op2 = next;
|
||||
while (*op2 == ' ') {
|
||||
++op2;
|
||||
}
|
||||
FindLabelLater(l, &anim->D.IfVar.Goto, op2);
|
||||
} else if (op1 == "set-var") {
|
||||
anim->Type = AnimationSetVar;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SetVar.Var = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SetVar.Mod = atoi(op2);
|
||||
op2 = next;
|
||||
while (*op2 == ' ') {
|
||||
++op2;
|
||||
}
|
||||
anim->D.SetVar.Value = new_strdup(op2);
|
||||
} else if (op1 == "set-player-var") {
|
||||
anim->Type = AnimationSetPlayerVar;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SetPlayerVar.Player = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SetPlayerVar.Var = new_strdup(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SetPlayerVar.Mod = atoi(op2);
|
||||
op2 = next;
|
||||
next = strchr(op2, ' ');
|
||||
if (next) {
|
||||
while (*next == ' ') {
|
||||
*next++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.SetPlayerVar.Value = new_strdup(op2);
|
||||
op2 = next;
|
||||
while (*op2 == ' ') {
|
||||
++op2;
|
||||
}
|
||||
anim->D.SetPlayerVar.Arg = new_strdup(op2);
|
||||
} else if (op1 == "die") {
|
||||
anim->Type = AnimationDie;
|
||||
if (op2!='\0')
|
||||
anim->D.Die.DeathType = new_strdup(op2);
|
||||
else
|
||||
anim->D.Die.DeathType = "\0";
|
||||
} else if (op1 == "rotate") {
|
||||
anim->Type = AnimationRotate;
|
||||
anim->D.Rotate.Rotate = atoi(op2);
|
||||
anim->D.Rotate.Rotate = new_strdup(op2);
|
||||
} else if (op1 == "random-rotate") {
|
||||
anim->Type = AnimationRandomRotate;
|
||||
anim->D.Rotate.Rotate = atoi(op2);
|
||||
anim->D.Rotate.Rotate = new_strdup(op2);
|
||||
} else if (op1 == "move") {
|
||||
anim->Type = AnimationMove;
|
||||
anim->D.Move.Move = atoi(op2);
|
||||
anim->D.Move.Move = new_strdup(op2);
|
||||
} else if (op1 == "unbreakable") {
|
||||
anim->Type = AnimationUnbreakable;
|
||||
if (!strcmp(op2, "begin")) {
|
||||
|
@ -1508,7 +1693,7 @@ static void ParseAnimationFrame(lua_State *l, const char *str,
|
|||
*label++ = '\0';
|
||||
}
|
||||
}
|
||||
anim->D.RandomGoto.Random = atoi(op2);
|
||||
anim->D.RandomGoto.Random = new_strdup(op2);
|
||||
FindLabelLater(l, &anim->D.RandomGoto.Goto, label);
|
||||
} else {
|
||||
LuaError(l, "Unknown animation: %s" _C_ op1.c_str());
|
||||
|
@ -1574,11 +1759,9 @@ static int CclDefineAnimations(lua_State *l)
|
|||
{
|
||||
const char *name;
|
||||
const char *value;
|
||||
char *end;
|
||||
CAnimations *anims;
|
||||
int res = -1;
|
||||
int death = ANIMATIONS_DEATHTYPES;
|
||||
long still = 99, move = 99, attack = 99;
|
||||
|
||||
LuaCheckArgs(l, 2);
|
||||
if (!lua_istable(l, 2)) {
|
||||
|
@ -1599,15 +1782,7 @@ static int CclDefineAnimations(lua_State *l)
|
|||
if (!strcmp(value, "Start")) {
|
||||
anims->Start = ParseAnimation(l, -1);
|
||||
} else if (!strncmp(value, "Still", 5)) {
|
||||
if (strlen(value)>5)
|
||||
{
|
||||
still = strtol(value + 6,&end, 10);
|
||||
if (still>99 || still < 1)
|
||||
LuaError(l,"Damaged percent must be between 1 and 99 : %s" _C_ value + 6);
|
||||
anims->Still[still-1] = ParseAnimation(l, -1);
|
||||
}
|
||||
else
|
||||
anims->Still[99] = ParseAnimation(l, -1);
|
||||
anims->Still = ParseAnimation(l, -1);
|
||||
} else if (!strncmp(value, "Death", 5)) {
|
||||
if (strlen(value)>5)
|
||||
{
|
||||
|
@ -1619,27 +1794,12 @@ static int CclDefineAnimations(lua_State *l)
|
|||
}
|
||||
else
|
||||
anims->Death[ANIMATIONS_DEATHTYPES] = ParseAnimation(l, -1);
|
||||
} else if (!strncmp(value, "Attack", 6)) {
|
||||
if (strlen(value)>6)
|
||||
{
|
||||
attack = strtol(value + 7,&end, 10);
|
||||
if (attack>99 || attack < 1)
|
||||
LuaError(l,"Damaged percent must be between 1 and 99 : %s" _C_ value + 7);
|
||||
anims->Attack[attack-1] = ParseAnimation(l, -1);
|
||||
}
|
||||
else
|
||||
anims->Attack[99] = ParseAnimation(l, -1);
|
||||
|
||||
} else if (!strncmp(value, "Move", 4)) {
|
||||
if (strlen(value)>4)
|
||||
{
|
||||
move = strtol(value + 5,&end, 10);
|
||||
if (move>99 || move < 1)
|
||||
LuaError(l,"Damaged percent must be between 1 and 99 : %s" _C_ value + 5);
|
||||
anims->Move[move-1] = ParseAnimation(l, -1);
|
||||
}
|
||||
else
|
||||
anims->Move[99] = ParseAnimation(l, -1);
|
||||
} else if (!strcmp(value, "Attack")) {
|
||||
anims->Attack = ParseAnimation(l, -1);
|
||||
} else if (!strcmp(value, "SpellCast")) {
|
||||
anims->SpellCast = ParseAnimation(l, -1);
|
||||
} else if (!strcmp(value, "Move")) {
|
||||
anims->Move = ParseAnimation(l, -1);
|
||||
} else if (!strcmp(value, "Repair")) {
|
||||
anims->Repair = ParseAnimation(l, -1);
|
||||
} else if (!strcmp(value, "Train")) {
|
||||
|
@ -1660,10 +1820,11 @@ static int CclDefineAnimations(lua_State *l)
|
|||
}
|
||||
// Must add to array in a fixed order for save games
|
||||
AddAnimationToArray(anims->Start);
|
||||
AddAnimationToArray(anims->Still[still]);
|
||||
AddAnimationToArray(anims->Still);
|
||||
AddAnimationToArray(anims->Death[death]);
|
||||
AddAnimationToArray(anims->Attack[attack]);
|
||||
AddAnimationToArray(anims->Move[move]);
|
||||
AddAnimationToArray(anims->Attack);
|
||||
AddAnimationToArray(anims->SpellCast);
|
||||
AddAnimationToArray(anims->Move);
|
||||
AddAnimationToArray(anims->Repair);
|
||||
AddAnimationToArray(anims->Train);
|
||||
if(res != -1)
|
||||
|
|
|
@ -860,8 +860,9 @@ void CUnit::Place(const Vec2i &pos)
|
|||
UnitCountSeen(*this);
|
||||
// Vision
|
||||
MapMarkUnitSight(*this);
|
||||
|
||||
// Correct directions for wall units
|
||||
if (this->Type->Wall && this->CurrentAction() != UnitActionBuilt){
|
||||
if (this->Type->Wall && this->CurrentAction() != UnitActionBuilt) {
|
||||
CorrectWallDirections(*this);
|
||||
UnitUpdateHeading(*this);
|
||||
CorrectWallNeighBours(*this);
|
||||
|
@ -2156,7 +2157,6 @@ void DropOutAll(const CUnit &source)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Finding units
|
||||
----------------------------------------------------------------------------*/
|
||||
|
@ -3303,35 +3303,6 @@ int CanTransport(const CUnit &transporter, const CUnit &unit)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
** Get the suitable animation frame when unit is damaged.
|
||||
*/
|
||||
int GetAnimationDamagedState(CUnit &unit, int anim)
|
||||
{
|
||||
if (unit.Variable[HP_INDEX].Max==0)
|
||||
return 99;
|
||||
int health = unit.Variable[HP_INDEX].Value * 100 / unit.Variable[HP_INDEX].Max;
|
||||
if (health==0)
|
||||
return 99;
|
||||
for (int i = health - 1; i<=99; ++i)
|
||||
switch (anim)
|
||||
{
|
||||
case 1:
|
||||
if (unit.Type->Animations->Still[i])
|
||||
return i;
|
||||
break;
|
||||
case 2:
|
||||
if (unit.Type->Animations->Move[i])
|
||||
return i;
|
||||
break;
|
||||
case 3:
|
||||
if (unit.Type->Animations->Attack[i])
|
||||
return i;
|
||||
break;
|
||||
}
|
||||
return 99;
|
||||
}
|
||||
|
||||
/**
|
||||
** Check if the player is an enemy
|
||||
**
|
||||
|
|
|
@ -108,6 +108,9 @@ std::string ExtraDeathTypes[ANIMATIONS_DEATHTYPES];
|
|||
-- Functions
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/// Parse integer in animation frame.
|
||||
extern int ParseAnimInt(CUnit *unit, const char *parseint);
|
||||
|
||||
CUnitType::CUnitType() :
|
||||
Slot(0), Width(0), Height(0), OffsetX(0), OffsetY(0), DrawLevel(0),
|
||||
ShadowWidth(0), ShadowHeight(0), ShadowOffsetX(0), ShadowOffsetY(0),
|
||||
|
@ -455,15 +458,14 @@ void DrawUnitType(const CUnitType &type, CPlayerColorGraphic *sprite, int player
|
|||
*/
|
||||
static int GetStillFrame(CUnitType *type)
|
||||
{
|
||||
CAnimation *anim;
|
||||
CAnimation *anim = type->Animations->Still;
|
||||
|
||||
anim = type->Animations->Still[99];
|
||||
while (anim) {
|
||||
if (anim->Type == AnimationFrame) {
|
||||
// Use the frame facing down
|
||||
return anim->D.Frame.Frame + type->NumDirections / 2;
|
||||
return ParseAnimInt(NoUnitP, anim->D.Frame.Frame) + type->NumDirections / 2;
|
||||
} else if (anim->Type == AnimationExactFrame) {
|
||||
return anim->D.Frame.Frame;
|
||||
return ParseAnimInt(NoUnitP, anim->D.Frame.Frame);
|
||||
}
|
||||
anim = anim->Next;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue