Permalink
Browse files

training, portals and fixes

- craft weapons with limited ammunition that doesn't require ammo items
(like disruptor weaponry) now properly recharges and doesn't break the
transaction screen
- agent training (and relevant ui elements)
- proper alien dimension portals
- entering alien dimension for the first time, view is centerd on the
portals
- alien dimension has proper strategy map icons
- aliens moving between buildings
- proper relationship change on scenery hit
- multi-projectiles fire only if projectile itself expires
- transaction screen now shows item when hovering over scrollbar too
  • Loading branch information...
Istrebitel committed Oct 26, 2017
1 parent 9b87ab9 commit 707edb63b6455f11bd151c7dff69136f3138895c
View
@@ -73,15 +73,16 @@ Cityscape Keyboard:
- [N] Manual control (if using vanilla scheme then [M] is the manual control key and there is no way to open message log, as per vanilla)
Cityscape Debug:
- Debug hotkeys are always active
- [F1] Debug hotkeys are toggled on/off
- [Ctrl] + [Alt] + [Shift] + [Left Click] destroys scenery
- [Ctrl] + [Alt] + [Shift] + [Right Click] collapses building
- [A] gives every vehicle weapon and ammo to current base
- [W] warp to alien dimension and back
- [R] repairs all buildings
- [R] repairs all scenery
- [B] spawn UFO on base assault mission
- [U] spawns three crashed UFOs
- [X] crashes every vehcile on map
- [PgUp] / [PgDown] displays only one layer of map, with other layers being transparent
- [F2] show road pathfinding map
- [F3] highlight walkmode, collapsing tiles, basement tiles
- [F4] show aliens in buildings on strategy map
@@ -532,7 +532,9 @@
</armor>
<damage_modifier>DAMAGEMODIFIER_HUMAN</damage_modifier>
<inventory>true</inventory>
<can_improve>true</can_improve>
<improvementPercentagePhysical>100</improvementPercentagePhysical>
<improvementPercentagePsi>100</improvementPercentagePsi>
<canTrain>true</canTrain>
<playable>true</playable>
<allowsDirectControl>true</allowsDirectControl>
<aiType>Group</aiType>
@@ -773,7 +775,9 @@
</armor>
<damage_modifier>DAMAGEMODIFIER_MUTANT</damage_modifier>
<inventory>true</inventory>
<can_improve>true</can_improve>
<improvementPercentagePhysical>100</improvementPercentagePhysical>
<improvementPercentagePsi>100</improvementPercentagePsi>
<canTrain>true</canTrain>
<playable>true</playable>
<allowsDirectControl>true</allowsDirectControl>
<aiType>Group</aiType>
@@ -966,7 +970,9 @@
</armor>
<damage_modifier>DAMAGEMODIFIER_ANDROID</damage_modifier>
<inventory>true</inventory>
<can_improve>true</can_improve>
<improvementPercentagePhysical>10</improvementPercentagePhysical>
<improvementPercentagePsi>0</improvementPercentagePsi>
<canTrain>false</canTrain>
<playable>true</playable>
<allowsDirectControl>true</allowsDirectControl>
<aiType>Group</aiType>
View
@@ -58,18 +58,18 @@
<image/>
<imagedepressed>PCK:xcom3/ufodata/icon_m.pck:xcom3/ufodata/icon_m.tab:9:xcom3/ufodata/pal_01.dat</imagedepressed>
</graphicbutton>
<graphicbutton id="BUTTON_AGENT_PHYSICAL">
<checkbox id="BUTTON_AGENT_PHYSICAL">
<size width="27" height="46"/>
<position x="482" y="75"/>
<image/>
<imagedepressed>PCK:xcom3/ufodata/icon_m.pck:xcom3/ufodata/icon_m.tab:48:xcom3/ufodata/pal_01.dat</imagedepressed>
</graphicbutton>
<graphicbutton id="BUTTON_AGENT_PSI">
<imagechecked>PCK:xcom3/ufodata/icon_m.pck:xcom3/ufodata/icon_m.tab:48:xcom3/ufodata/pal_01.dat</imagechecked>
</checkbox>
<checkbox id="BUTTON_AGENT_PSI">
<size width="27" height="46"/>
<position x="517" y="75"/>
<image/>
<imagedepressed>PCK:xcom3/ufodata/icon_m.pck:xcom3/ufodata/icon_m.tab:49:xcom3/ufodata/pal_01.dat</imagedepressed>
</graphicbutton>
<imagechecked>PCK:xcom3/ufodata/icon_m.pck:xcom3/ufodata/icon_m.tab:49:xcom3/ufodata/pal_01.dat</imagechecked>
</checkbox>
</style>
</form>
</openapoc>
@@ -2164,7 +2164,7 @@ void Battle::spawnReinforcements(GameState &state)
}
void Battle::handleProjectileHit(GameState &state, sp<Projectile> projectile, bool displayDoodad,
bool playSound)
bool playSound, bool expired)
{
if (projectile->damageType->explosive)
{
@@ -2177,29 +2177,33 @@ void Battle::handleProjectileHit(GameState &state, sp<Projectile> projectile, bo
{
this->placeDoodad(projectile->doodadType, projectile->position);
}
if (playSound && projectile->impactSfx && projectile->splitIntoTypesBattle.empty())
if (playSound && projectile->impactSfx &&
(!expired || projectile->splitIntoTypesBattle.empty()))
{
fw().soundBackend->playSample(projectile->impactSfx, projectile->position);
}
std::set<sp<Sample>> fireSounds;
for (auto &p : projectile->splitIntoTypesBattle)
{
auto direction = (float)randBoundsInclusive(state.rng, 0, 628) / 100.0f;
auto velocity = glm::normalize(
VehicleType::directionToVector(VehicleType::getDirectionLarge(direction)));
velocity *= p->speed * PROJECTILE_VELOCITY_MULTIPLIER;
auto newProj = mksp<Projectile>(
p->guided ? Projectile::Type::Missile : Projectile::Type::Beam, projectile->firerUnit,
projectile->trackedUnit, projectile->targetPosition, projectile->position, velocity,
p->turn_rate, p->ttl, p->damage, 0, 0, p->tail_size, p->projectile_sprites,
p->impact_sfx, p->explosion_graphic, p->damage_type);
map->addObjectToMap(newProj);
projectiles.insert(newProj);
fireSounds.insert(p->fire_sfx);
}
for (auto &s : fireSounds)
{
fw().soundBackend->playSample(s, projectile->position);
if (expired)
{
std::set<sp<Sample>> fireSounds;
for (auto &p : projectile->splitIntoTypesBattle)
{
auto direction = (float)randBoundsInclusive(state.rng, 0, 628) / 100.0f;
auto velocity = glm::normalize(
VehicleType::directionToVector(VehicleType::getDirectionLarge(direction)));
velocity *= p->speed * PROJECTILE_VELOCITY_MULTIPLIER;
auto newProj = mksp<Projectile>(
p->guided ? Projectile::Type::Missile : Projectile::Type::Beam,
projectile->firerUnit, projectile->trackedUnit, projectile->targetPosition,
projectile->position, velocity, p->turn_rate, p->ttl, p->damage, 0, 0, p->tail_size,
p->projectile_sprites, p->impact_sfx, p->explosion_graphic, p->damage_type);
map->addObjectToMap(newProj);
projectiles.insert(newProj);
fireSounds.insert(p->fire_sfx);
}
for (auto &s : fireSounds)
{
fw().soundBackend->playSample(s, projectile->position);
}
}
projectiles.erase(projectile);
}
@@ -245,7 +245,7 @@ class Battle : public std::enable_shared_from_this<Battle>
void spawnReinforcements(GameState &state);
void handleProjectileHit(GameState &state, sp<Projectile> projectile, bool displayDoodad,
bool playSound);
bool playSound, bool expired);
void update(GameState &state, unsigned int ticks);
void updateTB(GameState &state);
@@ -4024,66 +4024,81 @@ int BattleUnit::rollForPrimaryStat(GameState &state, int experience)
// except psi which assumes it's same 3x limit that is applied when using psi gym
void BattleUnit::processExperience(GameState &state)
{
if (!agent->type->can_improve)
{
return;
}
int secondaryXP = experiencePoints.accuracy + experiencePoints.bravery +
experiencePoints.psi_attack + experiencePoints.psi_energy +
experiencePoints.reactions;
if (agent->current_stats.accuracy < 100)
{
agent->current_stats.accuracy += rollForPrimaryStat(state, experiencePoints.accuracy);
agent->current_stats.accuracy += rollForPrimaryStat(
state, experiencePoints.accuracy * agent->type->improvementPercentagePhysical / 100);
}
if (agent->current_stats.psi_attack < 100 &&
agent->current_stats.psi_attack < agent->initial_stats.psi_attack * 3)
{
agent->current_stats.psi_attack += rollForPrimaryStat(state, experiencePoints.psi_attack);
agent->current_stats.psi_attack += rollForPrimaryStat(
state, experiencePoints.psi_attack * agent->type->improvementPercentagePsi / 100);
}
if (agent->current_stats.psi_energy < 100 &&
agent->current_stats.psi_energy < agent->initial_stats.psi_energy * 3)
{
agent->current_stats.psi_energy += rollForPrimaryStat(state, experiencePoints.psi_energy);
agent->current_stats.psi_energy += rollForPrimaryStat(
state, experiencePoints.psi_energy * agent->type->improvementPercentagePsi / 100);
}
if (agent->current_stats.reactions < 100)
{
if (state.current_battle->mode == Battle::Mode::TurnBased)
{
agent->current_stats.reactions += rollForPrimaryStat(state, experiencePoints.reactions);
agent->current_stats.reactions += rollForPrimaryStat(
state,
experiencePoints.reactions * agent->type->improvementPercentagePhysical / 100);
}
else
{
agent->current_stats.reactions += rollForPrimaryStat(state, std::min(3, secondaryXP));
agent->current_stats.reactions += rollForPrimaryStat(
state, std::min(3, secondaryXP * agent->type->improvementPercentagePhysical / 100));
}
}
if (agent->current_stats.bravery < 100)
{
agent->current_stats.bravery +=
10 * randBoundsExclusive(state.rng, 0, 99) < experiencePoints.bravery * 9;
}
if (secondaryXP > 0)
10 * randBoundsExclusive(state.rng, 0, 99) <
experiencePoints.bravery * 9 * agent->type->improvementPercentagePhysical / 100;
}
// Units with slower improvement rates need to gain more xp to have a chance to iprove
// >= 100% improvement rate need just 1 xp
// 50% improvement rate needs 2 xp
// 10% improvement rate needs 10 xp
// ---
// Percentile increase also affected (units with 100% improvement gain 10% of their missing
// stat, units with 50% gain 5% etc)
if (agent->type->improvementPercentagePhysical > 0 &&
secondaryXP > 100 / agent->type->improvementPercentagePhysical)
{
if (agent->current_stats.health < 100)
{
int healthBoost =
randBoundsInclusive(state.rng, 0, 2) + (100 - agent->current_stats.health) / 10;
int healthBoost = randBoundsInclusive(state.rng, 0, 2) +
(100 - agent->current_stats.health) / 10 *
agent->type->improvementPercentagePhysical / 100;
agent->current_stats.health += healthBoost;
agent->modified_stats.health += healthBoost;
}
if (agent->current_stats.speed < 100)
{
agent->current_stats.speed +=
randBoundsInclusive(state.rng, 0, 2) + (100 - agent->current_stats.speed) / 10;
agent->current_stats.speed += randBoundsInclusive(state.rng, 0, 2) +
(100 - agent->current_stats.speed) / 10 *
agent->type->improvementPercentagePhysical / 100;
}
if (agent->current_stats.stamina < 2000)
{
agent->current_stats.stamina += randBoundsInclusive(state.rng, 0, 2) * 20 +
(2000 - agent->current_stats.stamina) / 10;
(2000 - agent->current_stats.stamina) / 10 *
agent->type->improvementPercentagePhysical / 100;
}
if (agent->current_stats.strength < 100)
{
agent->current_stats.strength +=
randBoundsInclusive(state.rng, 0, 2) + (100 - agent->current_stats.strength) / 10;
agent->current_stats.strength += randBoundsInclusive(state.rng, 0, 2) +
(100 - agent->current_stats.strength) / 10 *
agent->type->improvementPercentagePhysical / 100;
}
}
agent->updateModifiedStats();
View
@@ -394,11 +394,12 @@ int Base::getCapacityUsed(GameState &state, FacilityType::Capacity type) const
switch (type)
{
case FacilityType::Capacity::Repair:
for (auto &v : building->currentVehicles)
for (auto &v : state.vehicles)
{
if (v->homeBuilding == v->currentBuilding && v->getMaxHealth() > v->getHealth())
if (v.second->homeBuilding == building &&
v.second->getMaxHealth() > v.second->getHealth())
{
total += v->getMaxHealth() - v->getHealth();
total += v.second->getMaxHealth() - v.second->getHealth();
}
}
// Show percentage of repair bay used if it can repair in one hour, or 100% if can't
@@ -411,7 +412,7 @@ int Base::getCapacityUsed(GameState &state, FacilityType::Capacity type) const
case FacilityType::Capacity::Medical:
for (auto &a : state.agents)
{
if (a.second->homeBuilding == building && a.second->currentBuilding == building)
if (a.second->homeBuilding == building)
{
if (a.second->modified_stats.health < a.second->current_stats.health)
{
@@ -420,6 +421,26 @@ int Base::getCapacityUsed(GameState &state, FacilityType::Capacity type) const
}
}
break;
case FacilityType::Capacity::Training:
for (auto &a : state.agents)
{
if (a.second->homeBuilding == building &&
a.second->trainingAssignment == TrainingAssignment::Physical)
{
total++;
}
}
break;
case FacilityType::Capacity::Psi:
for (auto &a : state.agents)
{
if (a.second->homeBuilding == building &&
a.second->trainingAssignment == TrainingAssignment::Psi)
{
total++;
}
}
break;
case FacilityType::Capacity::Chemistry:
case FacilityType::Capacity::Physics:
case FacilityType::Capacity::Workshop:
@@ -842,16 +842,80 @@ void Building::alienGrowth(GameState &state)
void Building::alienMovement(GameState &state)
{
if (!hasAliens())
{
return;
}
// Run once when crew landed and once every hour after grow
// Pick 15 intact buildings within range of 15 tiles (counting from center to center)
std::list<StateRef<Building>> neighbours;
for (auto &b : city->buildings)
{
auto distVec = bounds.p0 + bounds.p1 - b.second->bounds.p0 - b.second->bounds.p1;
distVec /= 2;
int distance = std::abs(distVec.x) + std::abs(distVec.y);
if (distance > 0 && distance <= 15)
{
neighbours.emplace_back(&state, b.first);
}
if (neighbours.size() >= 15)
{
break;
}
}
if (neighbours.empty())
{
return;
}
// Pick one random of them
auto bld = listRandomiser(state.rng, neighbours);
// For every alien calculate move percent as:
// alien's move chance + random 0..30
// Calculate amount of moving aliens
std::map<StateRef<AgentType>, int> moveAmounts;
int totalMoveAmount = 0;
for (auto &e : current_crew)
{
if (e.second == 0)
{
continue;
}
int movePercent =
std::min(100, e.first->movementPercent + randBoundsInclusive(state.rng, 0, 30));
int moveAmount = e.second * movePercent / 100;
if (moveAmount > 0)
{
moveAmounts[e.first] = moveAmount;
totalMoveAmount += moveAmount;
}
}
// Chance to move is:
// 15 + 3 * amount + 20 (if owner is friendly+ to aliens)
// If success then everybody moves according to percentage
LogWarning("Implement alien movement");
int friendlyBonus = 0;
switch (bld->owner->isRelatedTo(state.getAliens()))
{
case Organisation::Relation::Friendly:
case Organisation::Relation::Allied:
friendlyBonus = 20;
break;
default:
friendlyBonus = 0;
break;
}
bool moving = randBoundsInclusive(state.rng, 0, 100) < 15 + 3 * totalMoveAmount + friendlyBonus;
if (!moving)
{
return;
}
for (auto &e : moveAmounts)
{
current_crew[e.first] -= e.second;
bld->current_crew[e.first] += e.second;
}
if (bld->base)
{
fw().pushEvent(new GameDefenseEvent(GameEventType::DefendTheBase, base, state.getAliens()));
}
}
void Building::underAttack(GameState &state, StateRef<Organisation> attacker)
Oops, something went wrong.

0 comments on commit 707edb6

Please sign in to comment.