New unit-type flag "Wall", which will treat unit as wall.

This will use the unit's Direction field to show different wall connections.
The unit should be a building and have NumDirections = 16 and Flip = false.
The wall unit didn't count in GetNumOpponents. When it attacked, AI didn't fight back and no warnings are shown.

NOTE: need to modify triggers, so they won't count the wall units when calculating player's remaining units. Not needed for those games which don't use this feature. 

Wall mapping:
 Frame 0 - wall without connections.
 Frame 1 - wall with north connection.
 Frame 2 - wall with west connection.
 Frame 3 - wall with north and west connections.
 Frame 4 - wall with south connection.
 Frame 5 - wall with south and north connections.
 Frame 6 - wall with south and west connections.
 Frame 7 - wall with south, north and west connections.
 Frame 8 - wall with east connection.
 Frame 9 - wall with east and north connections.
 Frame 10 - wall with east and west connections.
 Frame 11 - wall with east, north and west connections.
 Frame 12 - wall with east and south connections.
 Frame 13 - wall with east, south and north connections.
 Frame 14 - wall with east, south and west connections.
 Frame 15 - wall with all connections.

patch from Cybermind.
This commit is contained in:
joris 2012-02-18 09:59:57 +01:00
parent d1ad94a052
commit 55d2e864ee
7 changed files with 147 additions and 22 deletions

View file

@ -593,7 +593,12 @@ void HandleActionBuilt(COrder& order, CUnit &unit)
// Set the direction of the building if it supports them
if (unit.Type->NumDirections > 1) {
unit.Direction = (MyRand() >> 8) & 0xFF; // random heading
if (unit.Type->Wall) { // Special logic for walls
CorrectWallDirections(unit);
CorrectWallNeighBours(unit);
} else {
unit.Direction = (MyRand() >> 8) & 0xFF; // random heading
}
UnitUpdateHeading(unit);
}

View file

@ -443,13 +443,19 @@ int GetNumOpponents(int player)
// Check the player opponents
for (int i = 0; i < PlayerMax; ++i) {
const int unitCount = Players[i].TotalNumUnits;
// This player is our enemy and has units left.
if (((Players[player].Enemy & (1 << i)) || (Players[i].Enemy & (1 << player))) &&
Players[i].TotalNumUnits) {
++n;
if ((Players[player].Enemy & (1 << i)) || (Players[i].Enemy & (1 << player))) {
// Don't count walls
for (int j = 0; j < unitCount; ++j) {
if (Players[i].Units[j]->Type->Wall == false) {
++n;
break;
}
}
}
}
return n;
}

View file

@ -1046,6 +1046,11 @@ extern int DirectionToHeading(const Vec2i &dir);
/// Convert direction (dx,dy) to heading (0-255)
extern int DirectionToHeading(const PixelDiff &dir);
///Correct directions for placed wall.
extern void CorrectWallDirections(CUnit &unit);
/// Correct the surrounding walls.
extern void CorrectWallNeighBours(CUnit &unit);
/// Update frame from heading
extern void UnitUpdateHeading(CUnit &unit);
/// Heading and frame from delta direction

View file

@ -645,6 +645,7 @@ enum {
TELEPORTER_INDEX,
SHIELDPIERCE_INDEX,
SAVECARGO_INDEX,
WALL_INDEX,
NBARALREADYDEFINED
};
@ -1007,8 +1008,9 @@ public:
unsigned Decoration : 1; /// Unit is a decoration (act as tile).
unsigned Indestructible : 1; /// Unit is indestructible (take no damage).
unsigned Teleporter : 1; /// Can teleport other units.
unsigned ShieldPiercing : 1; /// Can directly damage shield-protected units, without shield damaging.
unsigned SaveCargo : 1; /// Unit unloads his passengers after death.
unsigned ShieldPiercing : 1; /// Can directly damage shield-protected units, without shield damaging.
unsigned SaveCargo : 1; /// Unit unloads his passengers after death.
unsigned Wall : 1; /// Use special logic for Direction field.
CVariable *Variable; /// Array of user defined variables.
struct BoolFlags {

View file

@ -106,6 +106,7 @@ static const char INDESTRUCTIBLE_KEY[] = "Indestructible";
static const char TELEPORTER_KEY[] = "Teleporter";
static const char SHIELDPIERCE_KEY[] = "ShieldPiercing";
static const char SAVECARGO_KEY[] = "LoseCargo";
static const char WALL_KEY[] = "Wall";
// names of the variable.
static const char HITPOINTS_KEY[] = "HitPoints";
static const char BUILD_KEY[] = "Build";
@ -153,7 +154,7 @@ CUnitTypeVar::CBoolKeys::CBoolKeys() {
SHOREBUILDING_KEY, CANATTACK_KEY,BUILDEROUTSIDE_KEY,
BUILDERLOST_KEY,CANHARVEST_KEY,HARVESTER_KEY,SELECTABLEBYRECTANGLE_KEY,
ISNOTSELECTABLE_KEY,DECORATION_KEY,INDESTRUCTIBLE_KEY,TELEPORTER_KEY,SHIELDPIERCE_KEY,
SAVECARGO_KEY};
SAVECARGO_KEY, WALL_KEY};
for (int i = 0; i < NBARALREADYDEFINED; ++i) {
buildin[i].offset = i;
@ -1005,6 +1006,8 @@ static int CclDefineUnitType(lua_State *l)
type->ShieldPiercing = LuaToBoolean(l, -1);
} else if (!strcmp(value, "SaveCargo")) {
type->SaveCargo = LuaToBoolean(l, -1);
} else if (!strcmp(value, "Wall")) {
type->Wall = LuaToBoolean(l, -1);
} else if (!strcmp(value, "Sounds")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
@ -2095,9 +2098,9 @@ void UpdateUnitVariables(const CUnit &unit)
type->BoolFlag[DECORATION_INDEX].value = type->Decoration;
type->BoolFlag[INDESTRUCTIBLE_INDEX].value = type->Indestructible;
type->BoolFlag[TELEPORTER_INDEX].value = type->Teleporter;
type->BoolFlag[SHIELDPIERCE_INDEX].value = type->ShieldPiercing;
type->BoolFlag[SAVECARGO_INDEX].value = type->SaveCargo;
type->BoolFlag[SHIELDPIERCE_INDEX].value = type->ShieldPiercing;
type->BoolFlag[SAVECARGO_INDEX].value = type->SaveCargo;
type->BoolFlag[WALL_INDEX].value = type->Wall;
}

View file

@ -428,7 +428,7 @@ void CUnit::AssignToPlayer(CPlayer *player)
// don't count again
if (type->Building) {
// FIXME: support more races
if (type != UnitTypeOrcWall && type != UnitTypeHumanWall) {
if (!type->Wall && type != UnitTypeOrcWall && type != UnitTypeHumanWall) {
player->TotalBuildings++;
}
} else {
@ -445,7 +445,7 @@ void CUnit::AssignToPlayer(CPlayer *player)
// Don't Add the building if it's dieing, used to load a save game
if (type->Building && CurrentAction() != UnitActionDie) {
// FIXME: support more races
if (type != UnitTypeOrcWall && type != UnitTypeHumanWall) {
if (!type->Wall && type != UnitTypeOrcWall && type != UnitTypeHumanWall) {
player->NumBuildings++;
}
}
@ -860,6 +860,12 @@ void CUnit::Place(const Vec2i &pos)
UnitCountSeen(*this);
// Vision
MapMarkUnitSight(*this);
// Correct directions for wall units
if (this->Type->Wall && this->CurrentAction() != UnitActionBuilt){
CorrectWallDirections(*this);
UnitUpdateHeading(*this);
CorrectWallNeighBours(*this);
}
}
/**
@ -912,6 +918,11 @@ void CUnit::Remove(CUnit *host)
Removed = 1;
// Correct surrounding walls directions
if (this->Type->Wall){
CorrectWallNeighBours(*this);
}
// Remove unit from the current selection
if (Selected) {
if (NumSelected == 1) { // Remove building cursor
@ -974,7 +985,7 @@ void UnitLost(CUnit &unit)
if (unit.Type->Building) {
// FIXME: support more races
if (type != UnitTypeOrcWall && type != UnitTypeHumanWall) {
if (!type->Wall && type != UnitTypeOrcWall && type != UnitTypeHumanWall) {
player->NumBuildings--;
}
}
@ -1152,6 +1163,97 @@ static void UnitFillSeenValues(CUnit &unit)
unit.CurrentOrder()->FillSeenValues(unit);
}
class SamePlayerAndTypeAs
{
public:
explicit SamePlayerAndTypeAs(const CUnit &unit) :
player(unit.Player), type(unit.Type)
{}
bool operator() (const CUnit *unit) const
{
return (unit->Player == player && unit->Type == type);
}
private:
const CPlayer *player;
const CUnitType *type;
};
// Wall unit positions
enum {
W_NORTH = 0x10,
W_WEST = 0x20,
W_SOUTH = 0x40,
W_EAST = 0x80
};
/**
** Correct direction for placed wall.
**
** @param unit The wall unit.
*/
void CorrectWallDirections(CUnit &unit)
{
Assert(unit.Type->Wall);
Assert(unit.Type->NumDirections == 16);
Assert(!unit.Type->Flip);
if (!Map.Info.IsPointOnMap(unit.tilePos)) {
return ;
}
const struct {
Vec2i offset;
const int dirFlag;
} configs[] = {{{0, -1}, W_NORTH}, {{1, 0}, W_EAST},
{{0, 1}, W_SOUTH}, {{-1, 0}, W_WEST}};
int flags = 0;
for (int i = 0; i != sizeof (configs) / sizeof (*configs); ++i) {
const Vec2i pos = unit.tilePos + configs[i].offset;
const int dirFlag = configs[i].dirFlag;
if (Map.Info.IsPointOnMap(pos) == false) {
flags |= dirFlag;
} else {
const CUnitCache &unitCache = Map.Field(pos)->UnitCache;
const CUnit *neighboor = unitCache.find(SamePlayerAndTypeAs(unit));
if (neighboor != NULL) {
flags |= dirFlag;
}
}
}
unit.Direction = flags;
}
/**
** Correct the surrounding walls.
**
** @param unit The wall unit.
*/
void CorrectWallNeighBours(CUnit &unit)
{
Assert(unit.Type->Wall);
const Vec2i offset[] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
for (unsigned int i = 0; i < sizeof (offset) / sizeof (*offset); ++i) {
const Vec2i pos = unit.tilePos + offset[i];
if (Map.Info.IsPointOnMap(pos) == false) {
continue;
}
CUnitCache &unitCache = Map.Field(pos)->UnitCache;
CUnit *neighboor = unitCache.find(SamePlayerAndTypeAs(unit));
if (neighboor != NULL) {
CorrectWallDirections(*neighboor);
UnitUpdateHeading(*neighboor);
}
}
}
/**
** This function should get called when a unit goes under fog of war.
**
@ -1554,9 +1656,10 @@ void CUnit::ChangeOwner(CPlayer &newplayer)
PlayerSlot = newplayer.Units + newplayer.TotalNumUnits++;
if (Type->Building) {
newplayer.TotalBuildings++;
}
else {
if (!Type->Wall) {
newplayer.TotalBuildings++;
}
} else {
newplayer.TotalUnits++;
}
*PlayerSlot = this;
@ -1581,7 +1684,7 @@ void CUnit::ChangeOwner(CPlayer &newplayer)
newplayer.MaxResources[i] += Type->_Storing[i];
}
}
if (Type->Building) {
if (Type->Building && !Type->Wall) {
newplayer.NumBuildings++;
}
newplayer.UnitTypesCount[Type->Slot]++;
@ -2842,7 +2945,7 @@ void HitUnit(CUnit *attacker, CUnit &target, int damage)
if (attacker) {
target.DamagedType = ExtraDeathIndex(attacker->Type->DamageType.c_str());
}
if (!lastattack || lastattack + 2 * CYCLES_PER_SECOND < GameCycle) {
if (!target.Type->Wall && (!lastattack || lastattack + 2 * CYCLES_PER_SECOND < GameCycle)) {
// NOTE: perhaps this should also be moved into the notify?
if (target.Player == ThisPlayer) {
// FIXME: Problem with load+save.
@ -2875,7 +2978,7 @@ void HitUnit(CUnit *attacker, CUnit &target, int damage)
}
}
if (attacker && target.Type->Building && target.Player->AiEnabled) {
if (attacker && !target.Type->Wall && target.Type->Building && target.Player->AiEnabled) {
AiHelpMe(attacker, target);
}

View file

@ -113,7 +113,7 @@ CUnitType::CUnitType() :
ShadowWidth(0), ShadowHeight(0), ShadowOffsetX(0), ShadowOffsetY(0),
Animations(NULL), StillFrame(0),
DeathExplosion(NULL), CorpseType(NULL),
Construction(NULL), RepairHP(0), TileWidth(0), TileHeight(0),
Construction(NULL), RepairHP(0), TileWidth(0), TileHeight(0),
BoxWidth(0), BoxHeight(0), NumDirections(0), MinAttackRange(0),
ReactRangeComputer(0), ReactRangePerson(0), Priority(0),
BurnPercent(0), BurnDamageRate(0), RepairRange(0),
@ -129,7 +129,8 @@ CUnitType::CUnitType() :
Vanishes(0), GroundAttack(0), ShoreBuilding(0), CanAttack(0),
BuilderOutside(0), BuilderLost(0), CanHarvest(0), Harvester(0),
Neutral(0), SelectableByRectangle(0), IsNotSelectable(0), Decoration(0),
Indestructible(0), Teleporter(0), ShieldPiercing(0), SaveCargo(0), Variable(NULL),
Indestructible(0), Teleporter(0), ShieldPiercing(0), SaveCargo(0),
Wall(0), Variable(NULL),
GivesResource(0), Supply(0), Demand(0), FieldFlags(0), MovementMask(0),
Sprite(NULL), ShadowSprite(NULL)
{