Split scr/actions.cpp into actions.cpp/animation.cpp

This commit is contained in:
joris 2012-03-20 09:28:31 +01:00
parent 4856df457b
commit a1130dfbeb
3 changed files with 828 additions and 775 deletions

View file

@ -56,6 +56,7 @@ set(action_SRCS
src/action/action_unload.cpp
src/action/action_upgradeto.cpp
src/action/actions.cpp
src/action/animation.cpp
src/action/command.cpp
)
source_group(action FILES ${action_SRCS})

View file

@ -42,40 +42,13 @@
#include "actions.h"
#include "animation.h"
#include "commands.h"
#include "map.h"
#include "missile.h"
#include "pathfinder.h"
#include "player.h"
#include "sound.h"
#include "spells.h"
#include "unit.h"
#include "unittype.h"
#include "video.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
@ -179,754 +152,6 @@ void COrder::UpdatePathFinderData_NotCalled(PathFinderInput& input)
// Fixme : Auto select position to attack ?
}
/*----------------------------------------------------------------------------
-- Animation
----------------------------------------------------------------------------*/
/**
** Rotate a unit
**
** @param unit Unit to rotate
** @param rotate Number of frames to rotate (>0 clockwise, <0 counterclockwise)
*/
static void UnitRotate(CUnit &unit, int rotate)
{
unit.Direction += rotate * 256 / unit.Type->NumDirections;
UnitUpdateHeading(unit);
}
/**
** Show unit animation.
**
** @param unit Unit of the animation.
** @param anim Animation script to handle.
**
** @return The flags of the current script step.
*/
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]) {
Players[player].SetResource(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].GetUnitCount();
} 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
char* next = strchr(cur, '.');
if (next == NULL) {
return SyncRand(atoi(cur));
} else {
*next = '\0';
return atoi(cur) + SyncRand(atoi(next + 1));
}
}
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.
**
** @param unit Unit of the animation.
** @param anim Animation script to handle.
** @param scale Scaling factor of the wait times in animation (8 means no scaling).
**
** @return The flags of the current script step.
*/
int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scale)
{
// Changing animations
if (anim && unit.Anim.CurrAnim != anim) {
// Assert fails when transforming unit (upgrade-to).
Assert(!unit.Anim.Unbreakable);
unit.Anim.Anim = unit.Anim.CurrAnim = anim;
unit.Anim.Wait = 0;
}
// Currently waiting
if (unit.Anim.Wait) {
--unit.Anim.Wait;
if (!unit.Anim.Wait) {
// Advance to next frame
unit.Anim.Anim = unit.Anim.Anim->Next;
if (!unit.Anim.Anim) {
unit.Anim.Anim = unit.Anim.CurrAnim;
}
}
return 0;
}
int move = 0;
while (!unit.Anim.Wait) {
switch (unit.Anim.Anim->Type) {
case AnimationFrame:
unit.Frame = ParseAnimInt(&unit, unit.Anim.Anim->D.Frame.Frame);
UnitUpdateHeading(unit);
break;
case AnimationExactFrame:
unit.Frame = ParseAnimInt(&unit, unit.Anim.Anim->D.Frame.Frame);
break;
case AnimationWait:
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;
}
if (unit.Variable[HASTE_INDEX].Value && unit.Anim.Wait > 1) { // unit is accelerated
unit.Anim.Wait >>= 1;
}
if (unit.Anim.Wait <= 0)
unit.Anim.Wait = 1;
break;
case AnimationRandomWait:
{
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);
}
break;
case AnimationRandomSound:
if (unit.IsVisible(*ThisPlayer) || ReplayRevealMap) {
const int sound = SyncRand() % unit.Anim.Anim->D.RandomSound.NumSounds;
PlayUnitSound(unit, unit.Anim.Anim->D.RandomSound.Sound[sound]);
}
break;
case AnimationAttack:
{
unit.CurrentOrder()->OnAnimationAttack(unit);
break;
}
case AnimationSpawnMissile:
{
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;
PixelPos start;
PixelPos dest;
if ((flags & ANIM_SM_RELTARGET)) {
goal = unit.CurrentOrder()->GetGoal();
} else {
goal = &unit;
}
if (!goal || goal->Destroyed || goal->Removed) {
break;
}
if ((flags & ANIM_SM_PIXEL)) {
start.x = goal->tilePos.x * PixelTileSize.x + goal->IX + startx;
start.y = goal->tilePos.y * PixelTileSize.y + goal->IY + starty;
} else {
start.x = (goal->tilePos.x + startx) * PixelTileSize.x + PixelTileSize.x / 2;
start.y = (goal->tilePos.y + starty) * PixelTileSize.y + PixelTileSize.y / 2;
}
if ((flags & ANIM_SM_TOTARGET)) {
CUnit *target = goal->CurrentOrder()->GetGoal();
Assert(goal->CurrentAction() == UnitActionAttack);
if (!target || target->Destroyed || target->Removed) {
break;
}
if (flags & ANIM_SM_PIXEL) {
dest.x = target->tilePos.x * PixelTileSize.x + target->IX + destx;
dest.y = target->tilePos.y * PixelTileSize.y + target->IY + desty;
} else {
dest.x = (target->tilePos.x + destx) * PixelTileSize.x + target->Type->TileWidth * PixelTileSize.x / 2;
dest.y = (target->tilePos.y + desty) * PixelTileSize.y + target->Type->TileHeight * PixelTileSize.y / 2;
}
} else {
if ((flags & ANIM_SM_PIXEL)) {
dest.x = goal->tilePos.x * PixelTileSize.x + goal->IX + destx;
dest.y = goal->tilePos.y * PixelTileSize.y + goal->IY + desty;
} else {
dest.x = (goal->tilePos.x + destx) * PixelTileSize.x + goal->Type->TileWidth * PixelTileSize.x / 2;
dest.y = (goal->tilePos.y + desty) * PixelTileSize.y + goal->Type->TileHeight * PixelTileSize.y / 2;
}
}
const int dist = goal->MapDistanceTo(dest.x, dest.y);
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), start, dest);
if (flags & ANIM_SM_DAMAGE) {
missile->SourceUnit = &unit;
unit.RefsIncrease();
}
if (!missile->Type->Range) {
if (flags & ANIM_SM_TOTARGET) {
missile->TargetUnit = goal->CurrentOrder()->GetGoal();
goal->CurrentOrder()->GetGoal()->RefsIncrease();
} else {
missile->TargetUnit = goal;
goal->RefsIncrease();
}
}
}
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_ type->Name.c_str());
FindNearestDrop(*type, pos, resPos, LookingW);
if (MapDistance(pos, resPos) <= range) {
CUnit *target = MakeUnit(*type, &player);
if (target != NoUnitP) {
target->tilePos = resPos;
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.Unbreakable) {
fprintf(stderr, "Can't call \"die\" action in unbreakable section\n");
Exit(1);
return 0;
}
if (unit.Anim.Anim->D.Die.DeathType[0] != '\0') {
unit.DamagedType = ExtraDeathIndex(unit.Anim.Anim->D.Die.DeathType); }
unit.CurrentOrder()->NeedToDie = true;
return 0;
case AnimationRotate:
if (!strcmp(unit.Anim.Anim->D.Rotate.Rotate, "target") && unit.CurrentOrder()->HasGoal()) {
COrder &order = *unit.CurrentOrder();
const CUnit &target = *order.GetGoal();
if (target.Destroyed) {
order.ClearGoal();
break;
}
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, -ParseAnimPlayer(unit, unit.Anim.Anim->D.Rotate.Rotate));
} else {
UnitRotate(unit, ParseAnimPlayer(unit, unit.Anim.Anim->D.Rotate.Rotate));
}
break;
case AnimationMove:
Assert(!move);
move = ParseAnimPlayer(unit, unit.Anim.Anim->D.Move.Move);
break;
case AnimationUnbreakable:
Assert(unit.Anim.Unbreakable ^ unit.Anim.Anim->D.Unbreakable.Begin);
/*DebugPrint("UnitShowAnimationScaled: switch Unbreakable from %s to %s\n"
_C_ unit.Anim.Unbreakable ? "TRUE" : "FALSE"
_C_ unit.Anim.Anim->D.Unbreakable.Begin ? "TRUE" : "FALSE" );*/
unit.Anim.Unbreakable = unit.Anim.Anim->D.Unbreakable.Begin;
break;
case AnimationNone:
case AnimationLabel:
break;
case AnimationGoto:
unit.Anim.Anim = unit.Anim.Anim->D.Goto.Goto;
break;
case AnimationRandomGoto:
if (SyncRand() % 100 < ParseAnimPlayer(unit, unit.Anim.Anim->D.RandomGoto.Random)) {
unit.Anim.Anim = unit.Anim.Anim->D.RandomGoto.Goto;
}
break;
}
if (!unit.Anim.Wait) {
// Advance to next frame
unit.Anim.Anim = unit.Anim.Anim->Next;
if (!unit.Anim.Anim) {
unit.Anim.Anim = unit.Anim.CurrAnim;
}
}
}
--unit.Anim.Wait;
if (!unit.Anim.Wait) {
// Advance to next frame
unit.Anim.Anim = unit.Anim.Anim->Next;
if (!unit.Anim.Anim) {
unit.Anim.Anim = unit.Anim.CurrAnim;
}
}
return move;
}
/*----------------------------------------------------------------------------
-- Actions
----------------------------------------------------------------------------*/

827
src/action/animation.cpp Normal file
View file

@ -0,0 +1,827 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name actions.cpp - The actions. */
//
// (c) Copyright 1998-2005 by Lutz Sammer, Russell Smith, and Jimmy Salmon
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; only version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//@{
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "stratagus.h"
#include "actions.h"
#include "animation.h"
#include "commands.h"
#include "map.h"
#include "missile.h"
#include "pathfinder.h"
#include "player.h"
#include "sound.h"
#include "spells.h"
#include "unit.h"
#include "unittype.h"
#include "video.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
/*----------------------------------------------------------------------------
-- Animation
----------------------------------------------------------------------------*/
/**
** Rotate a unit
**
** @param unit Unit to rotate
** @param rotate Number of frames to rotate (>0 clockwise, <0 counterclockwise)
*/
static void UnitRotate(CUnit &unit, int rotate)
{
unit.Direction += rotate * 256 / unit.Type->NumDirections;
UnitUpdateHeading(unit);
}
/**
** Show unit animation.
**
** @param unit Unit of the animation.
** @param anim Animation script to handle.
**
** @return The flags of the current script step.
*/
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]) {
Players[player].SetResource(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].GetUnitCount();
} 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
char* next = strchr(cur, '.');
if (next == NULL) {
return SyncRand(atoi(cur));
} else {
*next = '\0';
return atoi(cur) + SyncRand(atoi(next + 1));
}
}
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.
**
** @param unit Unit of the animation.
** @param anim Animation script to handle.
** @param scale Scaling factor of the wait times in animation (8 means no scaling).
**
** @return The flags of the current script step.
*/
int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scale)
{
// Changing animations
if (anim && unit.Anim.CurrAnim != anim) {
// Assert fails when transforming unit (upgrade-to).
Assert(!unit.Anim.Unbreakable);
unit.Anim.Anim = unit.Anim.CurrAnim = anim;
unit.Anim.Wait = 0;
}
// Currently waiting
if (unit.Anim.Wait) {
--unit.Anim.Wait;
if (!unit.Anim.Wait) {
// Advance to next frame
unit.Anim.Anim = unit.Anim.Anim->Next;
if (!unit.Anim.Anim) {
unit.Anim.Anim = unit.Anim.CurrAnim;
}
}
return 0;
}
int move = 0;
while (!unit.Anim.Wait) {
switch (unit.Anim.Anim->Type) {
case AnimationFrame:
unit.Frame = ParseAnimInt(&unit, unit.Anim.Anim->D.Frame.Frame);
UnitUpdateHeading(unit);
break;
case AnimationExactFrame:
unit.Frame = ParseAnimInt(&unit, unit.Anim.Anim->D.Frame.Frame);
break;
case AnimationWait:
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;
}
if (unit.Variable[HASTE_INDEX].Value && unit.Anim.Wait > 1) { // unit is accelerated
unit.Anim.Wait >>= 1;
}
if (unit.Anim.Wait <= 0)
unit.Anim.Wait = 1;
break;
case AnimationRandomWait:
{
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);
}
break;
case AnimationRandomSound:
if (unit.IsVisible(*ThisPlayer) || ReplayRevealMap) {
const int sound = SyncRand() % unit.Anim.Anim->D.RandomSound.NumSounds;
PlayUnitSound(unit, unit.Anim.Anim->D.RandomSound.Sound[sound]);
}
break;
case AnimationAttack:
{
unit.CurrentOrder()->OnAnimationAttack(unit);
break;
}
case AnimationSpawnMissile:
{
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;
PixelPos start;
PixelPos dest;
if ((flags & ANIM_SM_RELTARGET)) {
goal = unit.CurrentOrder()->GetGoal();
} else {
goal = &unit;
}
if (!goal || goal->Destroyed || goal->Removed) {
break;
}
if ((flags & ANIM_SM_PIXEL)) {
start.x = goal->tilePos.x * PixelTileSize.x + goal->IX + startx;
start.y = goal->tilePos.y * PixelTileSize.y + goal->IY + starty;
} else {
start.x = (goal->tilePos.x + startx) * PixelTileSize.x + PixelTileSize.x / 2;
start.y = (goal->tilePos.y + starty) * PixelTileSize.y + PixelTileSize.y / 2;
}
if ((flags & ANIM_SM_TOTARGET)) {
CUnit *target = goal->CurrentOrder()->GetGoal();
Assert(goal->CurrentAction() == UnitActionAttack);
if (!target || target->Destroyed || target->Removed) {
break;
}
if (flags & ANIM_SM_PIXEL) {
dest.x = target->tilePos.x * PixelTileSize.x + target->IX + destx;
dest.y = target->tilePos.y * PixelTileSize.y + target->IY + desty;
} else {
dest.x = (target->tilePos.x + destx) * PixelTileSize.x + target->Type->TileWidth * PixelTileSize.x / 2;
dest.y = (target->tilePos.y + desty) * PixelTileSize.y + target->Type->TileHeight * PixelTileSize.y / 2;
}
} else {
if ((flags & ANIM_SM_PIXEL)) {
dest.x = goal->tilePos.x * PixelTileSize.x + goal->IX + destx;
dest.y = goal->tilePos.y * PixelTileSize.y + goal->IY + desty;
} else {
dest.x = (goal->tilePos.x + destx) * PixelTileSize.x + goal->Type->TileWidth * PixelTileSize.x / 2;
dest.y = (goal->tilePos.y + desty) * PixelTileSize.y + goal->Type->TileHeight * PixelTileSize.y / 2;
}
}
const int dist = goal->MapDistanceTo(dest.x, dest.y);
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), start, dest);
if (flags & ANIM_SM_DAMAGE) {
missile->SourceUnit = &unit;
unit.RefsIncrease();
}
if (!missile->Type->Range) {
if (flags & ANIM_SM_TOTARGET) {
missile->TargetUnit = goal->CurrentOrder()->GetGoal();
goal->CurrentOrder()->GetGoal()->RefsIncrease();
} else {
missile->TargetUnit = goal;
goal->RefsIncrease();
}
}
}
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_ type->Name.c_str());
FindNearestDrop(*type, pos, resPos, LookingW);
if (MapDistance(pos, resPos) <= range) {
CUnit *target = MakeUnit(*type, &player);
if (target != NoUnitP) {
target->tilePos = resPos;
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.Unbreakable) {
fprintf(stderr, "Can't call \"die\" action in unbreakable section\n");
Exit(1);
return 0;
}
if (unit.Anim.Anim->D.Die.DeathType[0] != '\0') {
unit.DamagedType = ExtraDeathIndex(unit.Anim.Anim->D.Die.DeathType); }
unit.CurrentOrder()->NeedToDie = true;
return 0;
case AnimationRotate:
if (!strcmp(unit.Anim.Anim->D.Rotate.Rotate, "target") && unit.CurrentOrder()->HasGoal()) {
COrder &order = *unit.CurrentOrder();
const CUnit &target = *order.GetGoal();
if (target.Destroyed) {
order.ClearGoal();
break;
}
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, -ParseAnimPlayer(unit, unit.Anim.Anim->D.Rotate.Rotate));
} else {
UnitRotate(unit, ParseAnimPlayer(unit, unit.Anim.Anim->D.Rotate.Rotate));
}
break;
case AnimationMove:
Assert(!move);
move = ParseAnimPlayer(unit, unit.Anim.Anim->D.Move.Move);
break;
case AnimationUnbreakable:
Assert(unit.Anim.Unbreakable ^ unit.Anim.Anim->D.Unbreakable.Begin);
/*DebugPrint("UnitShowAnimationScaled: switch Unbreakable from %s to %s\n"
_C_ unit.Anim.Unbreakable ? "TRUE" : "FALSE"
_C_ unit.Anim.Anim->D.Unbreakable.Begin ? "TRUE" : "FALSE" );*/
unit.Anim.Unbreakable = unit.Anim.Anim->D.Unbreakable.Begin;
break;
case AnimationNone:
case AnimationLabel:
break;
case AnimationGoto:
unit.Anim.Anim = unit.Anim.Anim->D.Goto.Goto;
break;
case AnimationRandomGoto:
if (SyncRand() % 100 < ParseAnimPlayer(unit, unit.Anim.Anim->D.RandomGoto.Random)) {
unit.Anim.Anim = unit.Anim.Anim->D.RandomGoto.Goto;
}
break;
}
if (!unit.Anim.Wait) {
// Advance to next frame
unit.Anim.Anim = unit.Anim.Anim->Next;
if (!unit.Anim.Anim) {
unit.Anim.Anim = unit.Anim.CurrAnim;
}
}
}
--unit.Anim.Wait;
if (!unit.Anim.Wait) {
// Advance to next frame
unit.Anim.Anim = unit.Anim.Anim->Next;
if (!unit.Anim.Anim) {
unit.Anim.Anim = unit.Anim.CurrAnim;
}
}
return move;
}
//@}