mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-27 21:49:10 +02:00
Refactoring of projectile controller
- projectiles now separated based on type - each type has its own rendering method - refactoring of CShootingAnimation
This commit is contained in:
parent
abb553d975
commit
4f91b062db
@ -723,7 +723,8 @@ CRangedAttackAnimation::CRangedAttackAnimation(CBattleInterface * owner_, const
|
|||||||
|
|
||||||
CShootingAnimation::CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool _catapult, int _catapultDmg)
|
CShootingAnimation::CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool _catapult, int _catapultDmg)
|
||||||
: CRangedAttackAnimation(_owner, attacker, _dest, _attacked),
|
: CRangedAttackAnimation(_owner, attacker, _dest, _attacked),
|
||||||
catapultDamage(_catapultDmg)
|
catapultDamage(_catapultDmg),
|
||||||
|
projectileEmitted(false)
|
||||||
{
|
{
|
||||||
logAnim->debug("Created shooting anim for %s", stack->getName());
|
logAnim->debug("Created shooting anim for %s", stack->getName());
|
||||||
}
|
}
|
||||||
@ -733,10 +734,9 @@ bool CShootingAnimation::init()
|
|||||||
if( !CAttackAnimation::checkInitialConditions() )
|
if( !CAttackAnimation::checkInitialConditions() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const CStack * shooter = attackingStack;
|
if(!attackingStack || myAnim->isDead())
|
||||||
|
|
||||||
if(!shooter || myAnim->isDead())
|
|
||||||
{
|
{
|
||||||
|
//FIXME: how is this possible?
|
||||||
endAnim();
|
endAnim();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -753,80 +753,81 @@ bool CShootingAnimation::init()
|
|||||||
//if (attackingStack && attackedStack && owner->creDir[attackingStack->ID] == owner->creDir[attackedStack->ID])
|
//if (attackingStack && attackedStack && owner->creDir[attackingStack->ID] == owner->creDir[attackedStack->ID])
|
||||||
// return false;
|
// return false;
|
||||||
|
|
||||||
|
setAnimationGroup();
|
||||||
|
shooting = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShootingAnimation::setAnimationGroup()
|
||||||
|
{
|
||||||
|
Point shooterPos = stackAnimation(attackingStack)->pos.topLeft();
|
||||||
|
Point shotTarget = CClickableHex::getXYUnitAnim(dest, attackedStack, owner) + Point(225, 225);
|
||||||
|
|
||||||
//maximal angle in radians between straight horizontal line and shooting line for which shot is considered to be straight (absoulte value)
|
//maximal angle in radians between straight horizontal line and shooting line for which shot is considered to be straight (absoulte value)
|
||||||
static const double straightAngle = 0.2;
|
static const double straightAngle = 0.2;
|
||||||
|
|
||||||
// Get further info about the shooter e.g. relative pos of projectile to unit.
|
double projectileAngle = atan2(shotTarget.y - shooterPos.y, std::abs(shotTarget.x - shooterPos.x));
|
||||||
// If the creature id is 149 then it's a arrow tower which has no additional info so get the
|
|
||||||
// actual arrow tower shooter instead.
|
// Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
|
||||||
const CCreature *shooterInfo = shooter->getCreature();
|
if (projectileAngle > straightAngle)
|
||||||
|
group = CCreatureAnim::SHOOT_UP;
|
||||||
|
else if (projectileAngle < -straightAngle)
|
||||||
|
group = CCreatureAnim::SHOOT_DOWN;
|
||||||
|
else
|
||||||
|
group = CCreatureAnim::SHOOT_FRONT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CShootingAnimation::initializeProjectile()
|
||||||
|
{
|
||||||
|
const CCreature *shooterInfo = attackingStack->getCreature();
|
||||||
|
|
||||||
if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
|
if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
|
||||||
shooterInfo = owner->siegeController->getTurretCreature();
|
shooterInfo = owner->siegeController->getTurretCreature();
|
||||||
|
|
||||||
Point shooterPos;
|
Point shotTarget = CClickableHex::getXYUnitAnim(dest, attackedStack, owner) + Point(225, 225);
|
||||||
Point shotPos;
|
Point shotOrigin = stackAnimation(attackingStack)->pos.topLeft() + Point(222, 265);
|
||||||
Point destPos;
|
int multiplier = stackFacingRight(attackingStack) ? 1 : -1;
|
||||||
|
|
||||||
// NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
|
if (group == CCreatureAnim::SHOOT_UP)
|
||||||
shooterPos = stackAnimation(shooter)->pos.topLeft();
|
|
||||||
//xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner);
|
|
||||||
|
|
||||||
destPos = CClickableHex::getXYUnitAnim(dest, attackedStack, owner) + Point(225, 225);
|
|
||||||
|
|
||||||
// to properly translate coordinates when shooter is rotated
|
|
||||||
int multiplier = 0;
|
|
||||||
if (shooter)
|
|
||||||
multiplier = stackFacingRight(shooter) ? 1 : -1;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
assert(false); // unreachable?
|
shotOrigin.x += ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier;
|
||||||
multiplier = shooter->getCreature()->idNumber == CreatureID::ARROW_TOWERS ? -1 : 1;
|
shotOrigin.y += shooterInfo->animation.upperRightMissleOffsetY;
|
||||||
}
|
}
|
||||||
|
else if (group == CCreatureAnim::SHOOT_DOWN)
|
||||||
double projectileAngle = atan2(fabs((double)destPos.y - shooterPos.y), fabs((double)destPos.x - shooterPos.x));
|
|
||||||
if(shooter->getPosition() < dest)
|
|
||||||
projectileAngle = -projectileAngle;
|
|
||||||
|
|
||||||
// Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
|
|
||||||
if (projectileAngle > straightAngle)
|
|
||||||
{
|
{
|
||||||
//upper shot
|
shotOrigin.x += ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier;
|
||||||
shotPos.x = shooterPos.x + 222 + ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier;
|
shotOrigin.y += shooterInfo->animation.lowerRightMissleOffsetY;
|
||||||
shotPos.y = shooterPos.y + 265 + shooterInfo->animation.upperRightMissleOffsetY;
|
|
||||||
}
|
}
|
||||||
else if (projectileAngle < -straightAngle)
|
else if (group == CCreatureAnim::SHOOT_FRONT)
|
||||||
{
|
{
|
||||||
//lower shot
|
shotOrigin.x += ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier;
|
||||||
shotPos.x = shooterPos.x + 222 + ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier;
|
shotOrigin.y += shooterInfo->animation.rightMissleOffsetY;
|
||||||
shotPos.y = shooterPos.y + 265 + shooterInfo->animation.lowerRightMissleOffsetY;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//straight shot
|
assert(0);
|
||||||
shotPos.x = shooterPos.x + 222 + ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier;
|
|
||||||
shotPos.y = shooterPos.y + 265 + shooterInfo->animation.rightMissleOffsetY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
owner->projectilesController->createProjectile(attackingStack, attackedStack, shotPos, destPos);
|
owner->projectilesController->createProjectile(attackingStack, attackedStack, shotOrigin, shotTarget);
|
||||||
|
}
|
||||||
|
|
||||||
//attack animation
|
void CShootingAnimation::emitProjectile()
|
||||||
|
{
|
||||||
shooting = true;
|
//owner->projectilesController->fireStackProjectile(attackingStack);
|
||||||
|
projectileEmitted = true;
|
||||||
if(projectileAngle > straightAngle)
|
|
||||||
group = CCreatureAnim::SHOOT_UP;
|
|
||||||
else if(projectileAngle < -straightAngle)
|
|
||||||
group = CCreatureAnim::SHOOT_DOWN;
|
|
||||||
else //straight shot
|
|
||||||
group = CCreatureAnim::SHOOT_FRONT;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CShootingAnimation::nextFrame()
|
void CShootingAnimation::nextFrame()
|
||||||
{
|
{
|
||||||
if (owner->projectilesController->hasActiveProjectile(attackingStack))
|
for(auto & it : pendingAnimations())
|
||||||
|
{
|
||||||
|
CMovementStartAnimation * anim = dynamic_cast<CMovementStartAnimation *>(it.first);
|
||||||
|
CReverseAnimation * anim2 = dynamic_cast<CReverseAnimation *>(it.first);
|
||||||
|
if( (anim && anim->stack->ID == stack->ID) || (anim2 && anim2->stack->ID == stack->ID && anim2->priority ) )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!projectileEmitted)
|
||||||
{
|
{
|
||||||
const CCreature *shooterInfo = attackingStack->getCreature();
|
const CCreature *shooterInfo = attackingStack->getCreature();
|
||||||
|
|
||||||
@ -836,18 +837,15 @@ void CShootingAnimation::nextFrame()
|
|||||||
// animation should be paused if there is an active projectile
|
// animation should be paused if there is an active projectile
|
||||||
if ( stackAnimation(attackingStack)->getCurrentFrame() >= shooterInfo->animation.attackClimaxFrame )
|
if ( stackAnimation(attackingStack)->getCurrentFrame() >= shooterInfo->animation.attackClimaxFrame )
|
||||||
{
|
{
|
||||||
owner->projectilesController->fireStackProjectile(attackingStack);//FIXME: should only be called once
|
initializeProjectile();
|
||||||
|
emitProjectile();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto & it : pendingAnimations())
|
if (projectileEmitted && owner->projectilesController->hasActiveProjectile(attackingStack))
|
||||||
{
|
return; // projectile in air, pause animation
|
||||||
CMovementStartAnimation * anim = dynamic_cast<CMovementStartAnimation *>(it.first);
|
|
||||||
CReverseAnimation * anim2 = dynamic_cast<CReverseAnimation *>(it.first);
|
|
||||||
if( (anim && anim->stack->ID == stack->ID) || (anim2 && anim2->stack->ID == stack->ID && anim2->priority ) )
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CAttackAnimation::nextFrame();
|
CAttackAnimation::nextFrame();
|
||||||
}
|
}
|
||||||
@ -855,7 +853,11 @@ void CShootingAnimation::nextFrame()
|
|||||||
void CShootingAnimation::endAnim()
|
void CShootingAnimation::endAnim()
|
||||||
{
|
{
|
||||||
// FIXME: is this possible? Animation is over but we're yet to fire projectile?
|
// FIXME: is this possible? Animation is over but we're yet to fire projectile?
|
||||||
owner->projectilesController->fireStackProjectile(attackingStack);
|
if (!projectileEmitted)
|
||||||
|
{
|
||||||
|
initializeProjectile();
|
||||||
|
emitProjectile();
|
||||||
|
}
|
||||||
|
|
||||||
// play wall hit/miss sound for catapult attack
|
// play wall hit/miss sound for catapult attack
|
||||||
if(!attackedStack)
|
if(!attackedStack)
|
||||||
|
@ -207,7 +207,12 @@ protected:
|
|||||||
class CShootingAnimation : public CRangedAttackAnimation
|
class CShootingAnimation : public CRangedAttackAnimation
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
bool projectileEmitted;
|
||||||
int catapultDamage;
|
int catapultDamage;
|
||||||
|
|
||||||
|
void setAnimationGroup();
|
||||||
|
void initializeProjectile();
|
||||||
|
void emitProjectile();
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
bool init() override;
|
||||||
void nextFrame() override;
|
void nextFrame() override;
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "../CVideoHandler.h"
|
#include "../CVideoHandler.h"
|
||||||
#include "../Graphics.h"
|
#include "../Graphics.h"
|
||||||
#include "../gui/CAnimation.h"
|
#include "../gui/CAnimation.h"
|
||||||
|
#include "../gui/CCanvas.h"
|
||||||
#include "../gui/CCursorHandler.h"
|
#include "../gui/CCursorHandler.h"
|
||||||
#include "../gui/CGuiHandler.h"
|
#include "../gui/CGuiHandler.h"
|
||||||
#include "../gui/SDL_Extensions.h"
|
#include "../gui/SDL_Extensions.h"
|
||||||
@ -919,6 +920,8 @@ void CBattleInterface::showAll(SDL_Surface *to)
|
|||||||
|
|
||||||
void CBattleInterface::show(SDL_Surface *to)
|
void CBattleInterface::show(SDL_Surface *to)
|
||||||
{
|
{
|
||||||
|
auto canvas = std::make_shared<CCanvas>(to);
|
||||||
|
|
||||||
assert(to);
|
assert(to);
|
||||||
|
|
||||||
SDL_Rect buf;
|
SDL_Rect buf;
|
||||||
@ -942,7 +945,7 @@ void CBattleInterface::show(SDL_Surface *to)
|
|||||||
fieldController->showHighlightedHexes(to);
|
fieldController->showHighlightedHexes(to);
|
||||||
|
|
||||||
showBattlefieldObjects(to);
|
showBattlefieldObjects(to);
|
||||||
projectilesController->showProjectiles(to);
|
projectilesController->showProjectiles(canvas);
|
||||||
|
|
||||||
if(battleActionsStarted)
|
if(battleActionsStarted)
|
||||||
stacksController->updateBattleAnimations();
|
stacksController->updateBattleAnimations();
|
||||||
|
@ -14,14 +14,15 @@
|
|||||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||||
#include "../CGameInfo.h"
|
#include "../CGameInfo.h"
|
||||||
#include "../gui/CAnimation.h"
|
#include "../gui/CAnimation.h"
|
||||||
|
#include "../gui/CCanvas.h"
|
||||||
#include "CBattleInterface.h"
|
#include "CBattleInterface.h"
|
||||||
#include "CBattleSiegeController.h"
|
#include "CBattleSiegeController.h"
|
||||||
#include "CBattleStacksController.h"
|
#include "CBattleStacksController.h"
|
||||||
#include "CCreatureAnimation.h"
|
#include "CCreatureAnimation.h"
|
||||||
|
|
||||||
CatapultProjectileInfo::CatapultProjectileInfo(const Point &from, const Point &dest)
|
static double calculateCatapultParabolaY(const Point & from, const Point & dest, int x)
|
||||||
{
|
{
|
||||||
facA = 0.005; // seems to be constant
|
double facA = 0.005; // seems to be constant
|
||||||
|
|
||||||
// system of 2 linear equations, solutions of which are missing coefficients
|
// system of 2 linear equations, solutions of which are missing coefficients
|
||||||
// for quadratic equation a*x*x + b*x + c
|
// for quadratic equation a*x*x + b*x + c
|
||||||
@ -35,163 +36,205 @@ CatapultProjectileInfo::CatapultProjectileInfo(const Point &from, const Point &d
|
|||||||
double detB = eq[0][2] *eq[1][1] - eq[1][2] *eq[0][1];
|
double detB = eq[0][2] *eq[1][1] - eq[1][2] *eq[0][1];
|
||||||
double detC = eq[0][0] *eq[1][2] - eq[1][0] *eq[0][2];
|
double detC = eq[0][0] *eq[1][2] - eq[1][0] *eq[0][2];
|
||||||
|
|
||||||
facB = detB / det;
|
double facB = detB / det;
|
||||||
facC = detC / det;
|
double facC = detC / det;
|
||||||
|
|
||||||
// make sure that parabola is correct e.g. passes through from and dest
|
return facA *pow(x, 2.0) + facB *x + facC;
|
||||||
assert(fabs(calculateY(from.x) - from.y) < 1.0);
|
|
||||||
assert(fabs(calculateY(dest.x) - dest.y) < 1.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double CatapultProjectileInfo::calculateY(double x)
|
void ProjectileMissile::show(std::shared_ptr<CCanvas> canvas)
|
||||||
{
|
{
|
||||||
return facA *pow(x, 2.0) + facB *x + facC;
|
size_t group = reverse ? 1 : 0;
|
||||||
|
auto image = animation->getImage(frameNum, group, true);
|
||||||
|
|
||||||
|
if(image)
|
||||||
|
{
|
||||||
|
float progress = float(step) / steps;
|
||||||
|
|
||||||
|
Point pos {
|
||||||
|
CSDL_Ext::lerp(from.x, dest.x, progress) - image->width() / 2,
|
||||||
|
CSDL_Ext::lerp(from.y, dest.y, progress) - image->height() / 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
canvas->draw(image, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
++step;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectileCatapult::show(std::shared_ptr<CCanvas> canvas)
|
||||||
|
{
|
||||||
|
size_t group = reverse ? 1 : 0;
|
||||||
|
auto image = animation->getImage(frameNum, group, true);
|
||||||
|
|
||||||
|
if(image)
|
||||||
|
{
|
||||||
|
float progress = float(step) / steps;
|
||||||
|
|
||||||
|
int posX = CSDL_Ext::lerp(from.x, dest.x, progress);
|
||||||
|
int posY = calculateCatapultParabolaY(from, dest, posX);
|
||||||
|
Point pos(posX, posY);
|
||||||
|
|
||||||
|
canvas->draw(image, pos);
|
||||||
|
|
||||||
|
frameNum = (frameNum + 1) % animation->size(0);
|
||||||
|
|
||||||
|
if (step == steps)
|
||||||
|
{
|
||||||
|
//TODO: re-enable. Move to ShootingAnimation? What about spells?
|
||||||
|
// last step - explosion effect
|
||||||
|
//Point explosion_pos = pos + image->dimensions() / 2 - Point(126, 105);
|
||||||
|
|
||||||
|
//owner->addNewAnim( new CEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", animPos.x, animPos.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++step;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectileRay::show(std::shared_ptr<CCanvas> canvas)
|
||||||
|
{
|
||||||
|
float progress = float(step) / steps;
|
||||||
|
|
||||||
|
Point curr {
|
||||||
|
CSDL_Ext::lerp(from.x, dest.x, progress),
|
||||||
|
CSDL_Ext::lerp(from.y, dest.y, progress),
|
||||||
|
};
|
||||||
|
|
||||||
|
Point length = curr - from;
|
||||||
|
|
||||||
|
//select axis to draw ray on, we want angle to be less than 45 degrees so individual sub-rays won't overlap each other
|
||||||
|
|
||||||
|
if (std::abs(length.x) > std::abs(length.y)) // draw in horizontal axis
|
||||||
|
{
|
||||||
|
int y1 = from.y - rayConfig.size() / 2;
|
||||||
|
int y2 = curr.y - rayConfig.size() / 2;
|
||||||
|
|
||||||
|
int x1 = from.x;
|
||||||
|
int x2 = curr.x;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < rayConfig.size(); ++i)
|
||||||
|
{
|
||||||
|
auto ray = rayConfig[i];
|
||||||
|
SDL_Color beginColor{ ray.r1, ray.g1, ray.b1, ray.a1};
|
||||||
|
SDL_Color endColor { ray.r2, ray.g2, ray.b2, ray.a2};
|
||||||
|
|
||||||
|
canvas->drawLine(Point(x1, y1 + i), Point(x2, y2+i), beginColor, endColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // draw in vertical axis
|
||||||
|
{
|
||||||
|
int x1 = from.x - rayConfig.size() / 2;
|
||||||
|
int x2 = curr.x - rayConfig.size() / 2;
|
||||||
|
|
||||||
|
int y1 = from.y;
|
||||||
|
int y2 = curr.y;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < rayConfig.size(); ++i)
|
||||||
|
{
|
||||||
|
auto ray = rayConfig[i];
|
||||||
|
SDL_Color beginColor{ ray.r1, ray.g1, ray.b1, ray.a1};
|
||||||
|
SDL_Color endColor { ray.r2, ray.g2, ray.b2, ray.a2};
|
||||||
|
|
||||||
|
canvas->drawLine(Point(x1 + i, y1), Point(x2 + i, y2), beginColor, endColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CBattleProjectileController::CBattleProjectileController(CBattleInterface * owner):
|
CBattleProjectileController::CBattleProjectileController(CBattleInterface * owner):
|
||||||
owner(owner)
|
owner(owner)
|
||||||
{
|
{}
|
||||||
|
|
||||||
|
const CCreature * CBattleProjectileController::getShooter(const CStack * stack)
|
||||||
|
{
|
||||||
|
const CCreature * creature = stack->getCreature();
|
||||||
|
|
||||||
|
if(creature->idNumber == CreatureID::ARROW_TOWERS)
|
||||||
|
creature = owner->siegeController->getTurretCreature();
|
||||||
|
|
||||||
|
if(creature->animation.missleFrameAngles.empty())
|
||||||
|
{
|
||||||
|
logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead...", creature->nameSing);
|
||||||
|
creature = CGI->creh->objects[CreatureID::ARCHER];
|
||||||
|
}
|
||||||
|
|
||||||
|
return creature;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBattleProjectileController::stackUsesRayProjectile(const CStack * stack)
|
||||||
|
{
|
||||||
|
return !getShooter(stack)->animation.projectileRay.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBattleProjectileController::stackUsesMissileProjectile(const CStack * stack)
|
||||||
|
{
|
||||||
|
return !getShooter(stack)->animation.projectileImageName.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBattleProjectileController::initStackProjectile(const CStack * stack)
|
void CBattleProjectileController::initStackProjectile(const CStack * stack)
|
||||||
{
|
{
|
||||||
const CCreature * creature;//creature whose shots should be loaded
|
if (!stackUsesMissileProjectile(stack))
|
||||||
if(stack->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
|
return;
|
||||||
creature = owner->siegeController->getTurretCreature();
|
|
||||||
|
const CCreature * creature = getShooter(stack);
|
||||||
|
|
||||||
|
std::shared_ptr<CAnimation> projectile = std::make_shared<CAnimation>(creature->animation.projectileImageName);
|
||||||
|
projectile->preload();
|
||||||
|
|
||||||
|
if(projectile->size(1) != 0)
|
||||||
|
logAnim->error("Expected empty group 1 in stack projectile");
|
||||||
else
|
else
|
||||||
creature = stack->getCreature();
|
projectile->createFlippedGroup(0, 1);
|
||||||
|
|
||||||
if (creature->animation.projectileRay.empty())
|
projectilesCache[creature->animation.projectileImageName] = projectile;
|
||||||
{
|
|
||||||
std::shared_ptr<CAnimation> projectile = std::make_shared<CAnimation>(creature->animation.projectileImageName);
|
|
||||||
projectile->preload();
|
|
||||||
|
|
||||||
if(projectile->size(1) != 0)
|
|
||||||
logAnim->error("Expected empty group 1 in stack projectile");
|
|
||||||
else
|
|
||||||
projectile->createFlippedGroup(0, 1);
|
|
||||||
|
|
||||||
idToProjectile[stack->getCreature()->idNumber] = projectile;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
idToRay[stack->getCreature()->idNumber] = creature->animation.projectileRay;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBattleProjectileController::fireStackProjectile(const CStack * stack)
|
std::shared_ptr<CAnimation> CBattleProjectileController::getProjectileImage(const CStack * stack)
|
||||||
{
|
{
|
||||||
for (auto it = projectiles.begin(); it!=projectiles.end(); ++it)
|
const CCreature * creature = getShooter(stack);
|
||||||
{
|
std::string imageName = creature->animation.projectileImageName;
|
||||||
if ( !it->shotDone && it->stackID == stack->ID)
|
|
||||||
{
|
if (!projectilesCache.count(imageName))
|
||||||
it->shotDone = true;
|
initStackProjectile(stack);
|
||||||
return;
|
|
||||||
}
|
return projectilesCache[imageName];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBattleProjectileController::showProjectiles(SDL_Surface *to)
|
//void CBattleProjectileController::fireStackProjectile(const CStack * stack)
|
||||||
{
|
//{
|
||||||
assert(to);
|
// for (auto it = projectiles.begin(); it!=projectiles.end(); ++it)
|
||||||
|
// {
|
||||||
|
// if ( !it->shotDone && it->stackID == stack->ID)
|
||||||
|
// {
|
||||||
|
// it->shotDone = true;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
std::list< std::list<ProjectileInfo>::iterator > toBeDeleted;
|
|
||||||
for (auto it = projectiles.begin(); it!=projectiles.end(); ++it)
|
void CBattleProjectileController::showProjectiles(std::shared_ptr<CCanvas> canvas)
|
||||||
|
{
|
||||||
|
for (auto projectile : projectiles)
|
||||||
{
|
{
|
||||||
// Check if projectile is already visible (shooter animation did the shot)
|
// Check if projectile is already visible (shooter animation did the shot)
|
||||||
if (!it->shotDone)
|
//if (!it->shotDone)
|
||||||
continue;
|
// continue;
|
||||||
|
|
||||||
if (idToProjectile.count(it->creID))
|
projectile->show(canvas);
|
||||||
{
|
|
||||||
size_t group = it->reverse ? 1 : 0;
|
|
||||||
auto image = idToProjectile[it->creID]->getImage(it->frameNum, group, true);
|
|
||||||
|
|
||||||
if(image)
|
// finished flying
|
||||||
{
|
if ( projectile->step > projectile->steps)
|
||||||
SDL_Rect dst;
|
projectile.reset();
|
||||||
dst.h = image->height();
|
|
||||||
dst.w = image->width();
|
|
||||||
dst.x = static_cast<int>(it->x - dst.w / 2);
|
|
||||||
dst.y = static_cast<int>(it->y - dst.h / 2);
|
|
||||||
|
|
||||||
image->draw(to, &dst, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (idToRay.count(it->creID))
|
|
||||||
{
|
|
||||||
auto const & ray = idToRay[it->creID];
|
|
||||||
|
|
||||||
if (std::abs(it->dx) > std::abs(it->dy)) // draw in horizontal axis
|
|
||||||
{
|
|
||||||
int y1 = it->y0 - ray.size() / 2;
|
|
||||||
int y2 = it->y - ray.size() / 2;
|
|
||||||
|
|
||||||
int x1 = it->x0;
|
|
||||||
int x2 = it->x;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ray.size(); ++i)
|
|
||||||
{
|
|
||||||
SDL_Color beginColor{ ray[i].r1, ray[i].g1, ray[i].b1, ray[i].a1};
|
|
||||||
SDL_Color endColor { ray[i].r2, ray[i].g2, ray[i].b2, ray[i].a2};
|
|
||||||
|
|
||||||
CSDL_Ext::drawLine(to, x1, y1 + i, x2, y2 + i, beginColor, endColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // draw in vertical axis
|
|
||||||
{
|
|
||||||
int x1 = it->x0 - ray.size() / 2;
|
|
||||||
int x2 = it->x - ray.size() / 2;
|
|
||||||
|
|
||||||
int y1 = it->y0;
|
|
||||||
int y2 = it->y;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ray.size(); ++i)
|
|
||||||
{
|
|
||||||
SDL_Color beginColor{ ray[i].r1, ray[i].g1, ray[i].b1, ray[i].a1};
|
|
||||||
SDL_Color endColor { ray[i].r2, ray[i].g2, ray[i].b2, ray[i].a2};
|
|
||||||
|
|
||||||
CSDL_Ext::drawLine(to, x1 + i, y1, x2 + i, y2, beginColor, endColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update projectile
|
|
||||||
++it->step;
|
|
||||||
if (it->step > it->lastStep)
|
|
||||||
{
|
|
||||||
toBeDeleted.insert(toBeDeleted.end(), it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (it->catapultInfo)
|
|
||||||
{
|
|
||||||
// Parabolic shot of the trajectory, as follows: f(x) = ax^2 + bx + c
|
|
||||||
it->x += it->dx;
|
|
||||||
it->y = it->catapultInfo->calculateY(it->x);
|
|
||||||
|
|
||||||
++(it->frameNum);
|
|
||||||
it->frameNum %= idToProjectile[it->creID]->size(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Normal projectile, just add the calculated "deltas" to the x and y positions.
|
|
||||||
it->x += it->dx;
|
|
||||||
it->y += it->dy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & elem : toBeDeleted)
|
boost::range::remove( projectiles, std::shared_ptr<ProjectileBase>());
|
||||||
projectiles.erase(elem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CBattleProjectileController::hasActiveProjectile(const CStack * stack)
|
bool CBattleProjectileController::hasActiveProjectile(const CStack * stack)
|
||||||
{
|
{
|
||||||
for(auto const & instance : projectiles)
|
for(auto const & instance : projectiles)
|
||||||
{
|
{
|
||||||
if(instance.creID == stack->getCreature()->idNumber)
|
if(instance->shooterID == stack->getCreature()->idNumber)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -199,117 +242,87 @@ bool CBattleProjectileController::hasActiveProjectile(const CStack * stack)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CBattleProjectileController::createProjectile(const CStack * shooter, const CStack * target, Point from, Point dest)
|
void CBattleProjectileController::createProjectile(const CStack * shooter, const CStack * target, Point from, Point dest)
|
||||||
{
|
{
|
||||||
// Get further info about the shooter e.g. relative pos of projectile to unit.
|
|
||||||
// If the creature id is 149 then it's a arrow tower which has no additional info so get the
|
|
||||||
// actual arrow tower shooter instead.
|
|
||||||
const CCreature *shooterInfo = shooter->getCreature();
|
const CCreature *shooterInfo = shooter->getCreature();
|
||||||
|
|
||||||
if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
|
std::shared_ptr<ProjectileBase> projectile;
|
||||||
shooterInfo = owner->siegeController->getTurretCreature();
|
|
||||||
|
|
||||||
if(!shooterInfo->animation.missleFrameAngles.size())
|
if (!target)
|
||||||
logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead..."
|
|
||||||
, shooterInfo->nameSing);
|
|
||||||
|
|
||||||
auto & angles = shooterInfo->animation.missleFrameAngles.size()
|
|
||||||
? shooterInfo->animation.missleFrameAngles
|
|
||||||
: CGI->creh->operator[](CreatureID::ARCHER)->animation.missleFrameAngles;
|
|
||||||
|
|
||||||
// recalculate angle taking in account offsets
|
|
||||||
//projectileAngle = atan2(fabs(destPos.y - spi.y), fabs(destPos.x - spi.x));
|
|
||||||
//if(shooter->position < dest)
|
|
||||||
// projectileAngle = -projectileAngle;
|
|
||||||
|
|
||||||
ProjectileInfo spi;
|
|
||||||
spi.shotDone = false;
|
|
||||||
spi.creID = shooter->getCreature()->idNumber;
|
|
||||||
spi.stackID = shooter->ID;
|
|
||||||
// reverse if creature is facing right OR this is non-existing stack that is not tower (war machines)
|
|
||||||
spi.reverse = shooter ? !owner->stacksController->facingRight(shooter) : shooter->getCreature()->idNumber != CreatureID::ARROW_TOWERS;
|
|
||||||
|
|
||||||
spi.step = 0;
|
|
||||||
spi.frameNum = 0;
|
|
||||||
|
|
||||||
spi.x0 = from.x;
|
|
||||||
spi.y0 = from.y;
|
|
||||||
|
|
||||||
spi.x = from.x;
|
|
||||||
spi.y = from.y;
|
|
||||||
|
|
||||||
if (target)
|
|
||||||
{
|
{
|
||||||
double animSpeed = AnimationControls::getProjectileSpeed(); // flight speed of projectile
|
auto catapultProjectile= new ProjectileCatapult();
|
||||||
double distanceSquared = (dest.x - spi.x) * (dest.x - spi.x) + (dest.y - spi.y) * (dest.y - spi.y);
|
projectile.reset(catapultProjectile);
|
||||||
double distance = sqrt(distanceSquared);
|
|
||||||
spi.lastStep = std::round(distance / animSpeed);
|
catapultProjectile->animation = getProjectileImage(shooter);
|
||||||
if(spi.lastStep == 0)
|
catapultProjectile->wallDamageAmount = 0; //FIXME - receive from caller
|
||||||
spi.lastStep = 1;
|
catapultProjectile->frameNum = 0;
|
||||||
spi.dx = (dest.x - spi.x) / spi.lastStep;
|
catapultProjectile->reverse = false;
|
||||||
spi.dy = (dest.y - spi.y) / spi.lastStep;
|
catapultProjectile->step = 0;
|
||||||
}
|
catapultProjectile->steps = 0;
|
||||||
else
|
|
||||||
{
|
|
||||||
// Catapult attack
|
|
||||||
spi.catapultInfo.reset(new CatapultProjectileInfo(Point((int)spi.x, (int)spi.y), dest));
|
|
||||||
|
|
||||||
double animSpeed = AnimationControls::getProjectileSpeed() / 10;
|
double animSpeed = AnimationControls::getProjectileSpeed() / 10;
|
||||||
spi.lastStep = static_cast<int>(std::abs((dest.x - spi.x) / animSpeed));
|
catapultProjectile->steps = std::round(std::abs((dest.x - from.x) / animSpeed));
|
||||||
spi.dx = animSpeed;
|
|
||||||
spi.dy = 0;
|
|
||||||
|
|
||||||
auto img = idToProjectile[spi.creID]->getImage(0);
|
|
||||||
|
|
||||||
// Add explosion anim
|
|
||||||
Point animPos(dest.x - 126 + img->width() / 2,
|
|
||||||
dest.y - 105 + img->height() / 2);
|
|
||||||
|
|
||||||
//owner->addNewAnim( new CEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", animPos.x, animPos.y));
|
|
||||||
}
|
|
||||||
double pi = std::atan(1)*4;
|
|
||||||
|
|
||||||
//in some cases (known one: hero grants shooter bonus to unit) the shooter stack's projectile may not be properly initialized
|
|
||||||
if (!idToProjectile.count(spi.creID) && !idToRay.count(spi.creID))
|
|
||||||
initStackProjectile(shooter);
|
|
||||||
|
|
||||||
if (idToProjectile.count(spi.creID))
|
|
||||||
{
|
|
||||||
// only frames below maxFrame are usable: anything higher is either no present or we don't know when it should be used
|
|
||||||
size_t maxFrame = std::min<size_t>(angles.size(), idToProjectile.at(spi.creID)->size(0));
|
|
||||||
|
|
||||||
assert(maxFrame > 0);
|
|
||||||
double projectileAngle = atan2(fabs((double)dest.y - from.y), fabs((double)dest.x - from.x));
|
|
||||||
//if(shooter->getPosition() < dest)
|
|
||||||
// projectileAngle = -projectileAngle;
|
|
||||||
|
|
||||||
// values in angles array indicate position from which this frame was rendered, in degrees.
|
|
||||||
// find frame that has closest angle to one that we need for this shot
|
|
||||||
size_t bestID = 0;
|
|
||||||
double bestDiff = fabs( angles[0] / 180 * pi - projectileAngle );
|
|
||||||
|
|
||||||
for (size_t i=1; i<maxFrame; i++)
|
|
||||||
{
|
|
||||||
double currentDiff = fabs( angles[i] / 180 * pi - projectileAngle );
|
|
||||||
if (currentDiff < bestDiff)
|
|
||||||
{
|
|
||||||
bestID = i;
|
|
||||||
bestDiff = currentDiff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spi.frameNum = static_cast<int>(bestID);
|
|
||||||
}
|
|
||||||
else if (idToRay.count(spi.creID))
|
|
||||||
{
|
|
||||||
// no-op
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logGlobal->error("Unable to find valid projectile for shooter %d", spi.creID);
|
if (stackUsesRayProjectile(shooter) && stackUsesMissileProjectile(shooter))
|
||||||
|
{
|
||||||
|
logAnim->error("Mod error: Creature '%s' has both missile and ray projectiles configured. Mod should be fixed. Using ray projectile configuration...", shooterInfo->nameSing);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stackUsesRayProjectile(shooter))
|
||||||
|
{
|
||||||
|
auto rayProjectile = new ProjectileRay();
|
||||||
|
projectile.reset(rayProjectile);
|
||||||
|
|
||||||
|
rayProjectile->rayConfig = shooterInfo->animation.projectileRay;
|
||||||
|
}
|
||||||
|
else if (stackUsesMissileProjectile(shooter))
|
||||||
|
{
|
||||||
|
auto missileProjectile = new ProjectileMissile();
|
||||||
|
projectile.reset(missileProjectile);
|
||||||
|
|
||||||
|
auto & angles = shooterInfo->animation.missleFrameAngles;
|
||||||
|
|
||||||
|
missileProjectile->animation = getProjectileImage(shooter);
|
||||||
|
missileProjectile->reverse = owner->stacksController->facingRight(shooter);
|
||||||
|
|
||||||
|
|
||||||
|
// only frames below maxFrame are usable: anything higher is either no present or we don't know when it should be used
|
||||||
|
size_t maxFrame = std::min<size_t>(angles.size(), missileProjectile->animation->size(0));
|
||||||
|
|
||||||
|
assert(maxFrame > 0);
|
||||||
|
double projectileAngle = atan2(dest.y - from.y, std::abs(dest.x - from.x));
|
||||||
|
|
||||||
|
// values in angles array indicate position from which this frame was rendered, in degrees.
|
||||||
|
// find frame that has closest angle to one that we need for this shot
|
||||||
|
int bestID = 0;
|
||||||
|
double bestDiff = fabs( angles[0] / 180 * M_PI - projectileAngle );
|
||||||
|
|
||||||
|
for (int i=1; i<maxFrame; i++)
|
||||||
|
{
|
||||||
|
double currentDiff = fabs( angles[i] / 180 * M_PI - projectileAngle );
|
||||||
|
if (currentDiff < bestDiff)
|
||||||
|
{
|
||||||
|
bestID = i;
|
||||||
|
bestDiff = currentDiff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
missileProjectile->frameNum = bestID;
|
||||||
|
}
|
||||||
|
|
||||||
|
double animSpeed = AnimationControls::getProjectileSpeed(); // flight speed of projectile
|
||||||
|
double distanceSquared = (dest.x - from.x) * (dest.x - from.x) + (dest.y - from.y) * (dest.y - from.y);
|
||||||
|
double distance = sqrt(distanceSquared);
|
||||||
|
projectile->steps = std::round(distance / animSpeed);
|
||||||
|
if(projectile->steps == 0)
|
||||||
|
projectile->steps = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set projectile animation start delay which is specified in frames
|
projectile->from = from;
|
||||||
projectiles.push_back(spi);
|
projectile->dest = dest;
|
||||||
|
projectile->shooterID = shooter->ID;
|
||||||
|
projectile->step = 0;
|
||||||
|
|
||||||
|
projectiles.push_back(projectile);
|
||||||
}
|
}
|
||||||
|
@ -10,56 +10,80 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../../lib/CCreatureHandler.h"
|
#include "../../lib/CCreatureHandler.h"
|
||||||
|
#include "../gui/Geometries.h"
|
||||||
|
|
||||||
struct Point;
|
struct Point;
|
||||||
struct SDL_Surface;
|
struct SDL_Surface;
|
||||||
class CAnimation;
|
class CAnimation;
|
||||||
|
class CCanvas;
|
||||||
class CStack;
|
class CStack;
|
||||||
class CBattleInterface;
|
class CBattleInterface;
|
||||||
|
|
||||||
/// Small struct which is needed for drawing the parabolic trajectory of the catapult cannon
|
/// Small struct which contains information about the position and the velocity of a projectile
|
||||||
struct CatapultProjectileInfo
|
struct ProjectileBase
|
||||||
{
|
{
|
||||||
CatapultProjectileInfo(const Point &from, const Point &dest);
|
virtual ~ProjectileBase() = default;
|
||||||
|
virtual void show(std::shared_ptr<CCanvas> canvas) = 0;
|
||||||
|
|
||||||
double facA, facB, facC;
|
Point from; // initial position on the screen
|
||||||
|
Point dest; // target position on the screen
|
||||||
|
|
||||||
double calculateY(double x);
|
int step; // current step counter
|
||||||
|
int steps; // total number of steps/frames to show
|
||||||
|
int shooterID; // ID of shooter stack
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Small struct which contains information about the position and the velocity of a projectile
|
struct ProjectileMissile : ProjectileBase
|
||||||
struct ProjectileInfo
|
|
||||||
{
|
{
|
||||||
double x0, y0; //initial position on the screen
|
void show(std::shared_ptr<CCanvas> canvas) override;
|
||||||
double x, y; //position on the screen
|
|
||||||
double dx, dy; //change in position in one step
|
std::shared_ptr<CAnimation> animation;
|
||||||
int step, lastStep; //to know when finish showing this projectile
|
int frameNum; // frame to display from projectile animation
|
||||||
int creID; //ID of creature that shot this projectile
|
bool reverse; // if true, projectile will be flipped by vertical axis
|
||||||
int stackID; //ID of stack
|
};
|
||||||
int frameNum; //frame to display form projectile animation
|
|
||||||
//bool spin; //if true, frameNum will be increased
|
struct ProjectileCatapult : ProjectileMissile
|
||||||
bool shotDone; // actual shot already done, projectile is flying
|
{
|
||||||
bool reverse; //if true, projectile will be flipped by vertical asix
|
void show(std::shared_ptr<CCanvas> canvas) override;
|
||||||
std::shared_ptr<CatapultProjectileInfo> catapultInfo; // holds info about the parabolic trajectory of the cannon
|
|
||||||
|
int wallDamageAmount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProjectileRay : ProjectileBase
|
||||||
|
{
|
||||||
|
void show(std::shared_ptr<CCanvas> canvas) override;
|
||||||
|
|
||||||
|
std::vector<CCreature::CreatureAnimation::RayColor> rayConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CBattleProjectileController
|
class CBattleProjectileController
|
||||||
{
|
{
|
||||||
CBattleInterface * owner;
|
CBattleInterface * owner;
|
||||||
|
|
||||||
std::map<int, std::shared_ptr<CAnimation>> idToProjectile;
|
/// all projectiles loaded during current battle
|
||||||
std::map<int, std::vector<CCreature::CreatureAnimation::RayColor>> idToRay;
|
std::map<std::string, std::shared_ptr<CAnimation>> projectilesCache;
|
||||||
|
|
||||||
std::list<ProjectileInfo> projectiles; //projectiles flying on battlefield
|
// std::map<int, std::shared_ptr<CAnimation>> idToProjectile;
|
||||||
|
// std::map<int, std::vector<CCreature::CreatureAnimation::RayColor>> idToRay;
|
||||||
|
|
||||||
|
/// projectiles currently flying on battlefield
|
||||||
|
std::vector<std::shared_ptr<ProjectileBase>> projectiles;
|
||||||
|
|
||||||
|
std::shared_ptr<CAnimation> getProjectileImage(const CStack * stack);
|
||||||
|
void initStackProjectile(const CStack * stack);
|
||||||
|
|
||||||
|
bool stackUsesRayProjectile(const CStack * stack);
|
||||||
|
bool stackUsesMissileProjectile(const CStack * stack);
|
||||||
|
|
||||||
|
void showProjectile(std::shared_ptr<CCanvas> canvas, std::shared_ptr<ProjectileBase> projectile);
|
||||||
|
|
||||||
|
const CCreature * getShooter(const CStack * stack);
|
||||||
public:
|
public:
|
||||||
CBattleProjectileController(CBattleInterface * owner);
|
CBattleProjectileController(CBattleInterface * owner);
|
||||||
|
|
||||||
void showProjectiles(SDL_Surface *to);
|
void showProjectiles(std::shared_ptr<CCanvas> canvas);
|
||||||
void initStackProjectile(const CStack * stack);
|
//void fireStackProjectile(const CStack * stack);
|
||||||
void fireStackProjectile(const CStack * stack);
|
|
||||||
|
|
||||||
bool hasActiveProjectile(const CStack * stack);
|
bool hasActiveProjectile(const CStack * stack);
|
||||||
|
|
||||||
void createProjectile(const CStack * shooter, const CStack * target, Point from, Point dest);
|
void createProjectile(const CStack * shooter, const CStack * target, Point from, Point dest);
|
||||||
};
|
};
|
||||||
|
@ -199,12 +199,6 @@ void CBattleStacksController::stackAdded(const CStack * stack)
|
|||||||
creAnims[stack->ID]->pos.y = coords.y;
|
creAnims[stack->ID]->pos.y = coords.y;
|
||||||
creAnims[stack->ID]->pos.w = creAnims[stack->ID]->getWidth();
|
creAnims[stack->ID]->pos.w = creAnims[stack->ID]->getWidth();
|
||||||
creAnims[stack->ID]->setType(CCreatureAnim::HOLDING);
|
creAnims[stack->ID]->setType(CCreatureAnim::HOLDING);
|
||||||
|
|
||||||
//loading projectiles for units
|
|
||||||
if(stack->isShooter())
|
|
||||||
{
|
|
||||||
owner->projectilesController->initStackProjectile(stack);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBattleStacksController::setActiveStack(const CStack *stack)
|
void CBattleStacksController::setActiveStack(const CStack *stack)
|
||||||
|
@ -40,6 +40,11 @@ void CCanvas::draw(std::shared_ptr<CCanvas> image, const Point & pos)
|
|||||||
image->copyTo(surface, pos);
|
image->copyTo(surface, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CCanvas::drawLine(const Point & from, const Point & dest, const SDL_Color & colorFrom, const SDL_Color & colorDest)
|
||||||
|
{
|
||||||
|
CSDL_Ext::drawLine(surface, from.x, from.y, dest.x, dest.y, colorFrom, colorDest);
|
||||||
|
}
|
||||||
|
|
||||||
void CCanvas::copyTo(SDL_Surface * to, const Point & pos)
|
void CCanvas::copyTo(SDL_Surface * to, const Point & pos)
|
||||||
{
|
{
|
||||||
blitAt(to, pos.x, pos.y, surface);
|
blitAt(to, pos.x, pos.y, surface);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
struct SDL_Color;
|
||||||
struct SDL_Surface;
|
struct SDL_Surface;
|
||||||
struct Point;
|
struct Point;
|
||||||
class IImage;
|
class IImage;
|
||||||
@ -30,6 +31,9 @@ public:
|
|||||||
// renders another canvas onto this canvas
|
// renders another canvas onto this canvas
|
||||||
void draw(std::shared_ptr<CCanvas> image, const Point & pos);
|
void draw(std::shared_ptr<CCanvas> image, const Point & pos);
|
||||||
|
|
||||||
|
// renders continuous, 1-pixel wide line with color gradient
|
||||||
|
void drawLine(const Point & from, const Point & dest, const SDL_Color & colorFrom, const SDL_Color & colorDest);
|
||||||
|
|
||||||
// for compatibility, copies content of this canvas onto SDL_Surface
|
// for compatibility, copies content of this canvas onto SDL_Surface
|
||||||
void copyTo(SDL_Surface * to, const Point & pos);
|
void copyTo(SDL_Surface * to, const Point & pos);
|
||||||
};
|
};
|
||||||
|
@ -362,23 +362,17 @@ void CSDL_Ext::update(SDL_Surface * what)
|
|||||||
logGlobal->error("%s SDL_UpdateTexture %s", __FUNCTION__, SDL_GetError());
|
logGlobal->error("%s SDL_UpdateTexture %s", __FUNCTION__, SDL_GetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Int>
|
|
||||||
Int lerp(Int a, Int b, float f)
|
|
||||||
{
|
|
||||||
return a + std::round((b - a) * f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2)
|
static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2)
|
||||||
{
|
{
|
||||||
for(int x = x1; x <= x2; x++)
|
for(int x = x1; x <= x2; x++)
|
||||||
{
|
{
|
||||||
float f = float(x - x1) / float(x2 - x1);
|
float f = float(x - x1) / float(x2 - x1);
|
||||||
int y = lerp(y1, y2, f);
|
int y = CSDL_Ext::lerp(y1, y2, f);
|
||||||
|
|
||||||
uint8_t r = lerp(color1.r, color2.r, f);
|
uint8_t r = CSDL_Ext::lerp(color1.r, color2.r, f);
|
||||||
uint8_t g = lerp(color1.g, color2.g, f);
|
uint8_t g = CSDL_Ext::lerp(color1.g, color2.g, f);
|
||||||
uint8_t b = lerp(color1.b, color2.b, f);
|
uint8_t b = CSDL_Ext::lerp(color1.b, color2.b, f);
|
||||||
uint8_t a = lerp(color1.a, color2.a, f);
|
uint8_t a = CSDL_Ext::lerp(color1.a, color2.a, f);
|
||||||
|
|
||||||
Uint8 *p = CSDL_Ext::getPxPtr(sur, x, y);
|
Uint8 *p = CSDL_Ext::getPxPtr(sur, x, y);
|
||||||
ColorPutter<4, 0>::PutColor(p, r,g,b,a);
|
ColorPutter<4, 0>::PutColor(p, r,g,b,a);
|
||||||
@ -390,12 +384,12 @@ static void drawLineY(SDL_Surface * sur, int x1, int y1, int x2, int y2, const S
|
|||||||
for(int y = y1; y <= y2; y++)
|
for(int y = y1; y <= y2; y++)
|
||||||
{
|
{
|
||||||
float f = float(y - y1) / float(y2 - y1);
|
float f = float(y - y1) / float(y2 - y1);
|
||||||
int x = lerp(x1, x2, f);
|
int x = CSDL_Ext::lerp(x1, x2, f);
|
||||||
|
|
||||||
uint8_t r = lerp(color1.r, color2.r, f);
|
uint8_t r = CSDL_Ext::lerp(color1.r, color2.r, f);
|
||||||
uint8_t g = lerp(color1.g, color2.g, f);
|
uint8_t g = CSDL_Ext::lerp(color1.g, color2.g, f);
|
||||||
uint8_t b = lerp(color1.b, color2.b, f);
|
uint8_t b = CSDL_Ext::lerp(color1.b, color2.b, f);
|
||||||
uint8_t a = lerp(color1.a, color2.a, f);
|
uint8_t a = CSDL_Ext::lerp(color1.a, color2.a, f);
|
||||||
|
|
||||||
Uint8 *p = CSDL_Ext::getPxPtr(sur, x, y);
|
Uint8 *p = CSDL_Ext::getPxPtr(sur, x, y);
|
||||||
ColorPutter<4, 0>::PutColor(p, r,g,b,a);
|
ColorPutter<4, 0>::PutColor(p, r,g,b,a);
|
||||||
|
@ -49,6 +49,12 @@ inline bool isShiftKeyDown()
|
|||||||
}
|
}
|
||||||
namespace CSDL_Ext
|
namespace CSDL_Ext
|
||||||
{
|
{
|
||||||
|
template<typename Int>
|
||||||
|
Int lerp(Int a, Int b, float f)
|
||||||
|
{
|
||||||
|
return a + std::round((b - a) * f);
|
||||||
|
}
|
||||||
|
|
||||||
//todo: should this better be assignment operator?
|
//todo: should this better be assignment operator?
|
||||||
STRONG_INLINE void colorAssign(SDL_Color & dest, const SDL_Color & source)
|
STRONG_INLINE void colorAssign(SDL_Color & dest, const SDL_Color & source)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user