Applied jarod's flag patch.

This commit is contained in:
n0body 2003-10-25 17:58:44 +00:00
parent 8ca456c750
commit 7c28b39c7a
16 changed files with 325 additions and 174 deletions

View file

@ -192,7 +192,6 @@ IFLAGS= -I$(TOPDIR)/src/include $(XIFLAGS) -I$(TOPDIR)/src/movie/vp31/include
## There are some still not well tested code parts or branches.
## UNIT_ON_MAP Fast lookup of units
## UNITS_ON_MAP Faster lookup of units
## NEW_AI New better improved AI code
## USE_TILECACHE Faster tile drawing, costs memory.
## USE_SMART_TILECACHE Faster tile drawing, slow with hardware video memory.
## USE_HP_FOR_XP Use hit-points for XP calculations.
@ -210,7 +209,7 @@ IFLAGS= -I$(TOPDIR)/src/include $(XIFLAGS) -I$(TOPDIR)/src/movie/vp31/include
## HIERARCHIC_PATHFINDER Use hierarchic pathfinder
## USE_LUA Lua scripting support
DFLAGS= $(THREAD) $(CCL) $(VERSION) $(VIDEO) $(ZDEFS) $(DSOUND) $(DEBUG) \
-DUNIT_ON_MAP -DNEW_AI -DUSE_FLAC -DUSE_OGG -DUSE_MAD \
-DUNIT_ON_MAP -DUSE_FLAC -DUSE_OGG -DUSE_MAD \
-DUSE_HP_FOR_XP # -DNEW_MAPDRAW=1 -DNEW_SHIPS
## choose optimise level

View file

@ -36,7 +36,8 @@
<li>Future 2.00 Release<p>
<ul>
<li>++
<li>Applied patch #2116 (correct missile directions) (from Ludovic Jarod Dauphin).
<li>Applied patch #2130 (missile flags and area-heal) (from Jarod Dauphin).
<li>Applied patch #2116 (correct missile directions) (from Jarod Dauphin).
<li>Added Neutral Minimap Color. (from Russell Smith).
<li>Removed the demolish action, we now use the demolish spell. (from Crestez Leonard).
<li>Applied patch #1969 (AI enhancement) (from Ludovic Pollet).

View file

@ -48,6 +48,7 @@
<a href="ccl-index.html">Index</a>
<hr>
<a href="#define-animations">define-animations</a>
<a href="#define-bool-flags">define-bool-flags</a>
<a href="#define-unit-stats">define-unit-stats</a>
<a href="#define-unit-type">define-unit-type</a>
<a href="pud.html#define-unittype-wc-names">define-unittype-wc-names</a>
@ -119,6 +120,20 @@ Define animations.
#( 0 0 3 45) #( 0 0 3 50) #( 0 0 100 55) #( 3 0 1 55)))
</pre>
<a name="define-bool-flags"></a>
<h3>(define-bool-flags 'flag-1 'flag-2 ...)</h3>
Define boolean unit flags. Examples are organic, mechanical, undead, etc.
Spells use these to determine who to hit, and units can be restricted too.
Try to avoid using names with other meanings (nothing from unit definitions
or spell condition definition.)
<h4>Example</h4>
<pre>
(define-bool-flags 'organic 'hero 'mechanical 'undead 'demonic 'angelic)
</pre>
<a name="define-unit-stats"></a>
<h3>(define-unit-stats ident player 'tag1 value1 'tag2 value2 ...)</h3>
@ -415,12 +430,6 @@ be built on.
<dd>This should be true for suicide bombers, a bit of a hack. Available as a
target check for spells, making those invisible would ruin the game.
</dd>
<dt>organic</dt>
<dd>This is a flag for spell target check.
</dd>
<dt>hero</dt>
<dd>This is a flag for spell target check. Prevent instant kills for instance.
</dd>
<dt>coward</dt>
<dd>Unit will not attack on sight, and will run away instead of retaliating.
Use this for units that can't attack or are next to useless in combat (like
@ -480,9 +489,6 @@ suicide. Doesn't work with resource workers/resources. It can even be used
in combination with demolish-damage and demolish-range, though it wouldn't
be very fair.
</dd>
<dt>sniper</dt>
<dd>Unit can only target organic units.
</dd>
<dt>computer-reaction-range</dt>
<dd>This is supossed to be the reaction range for AI units, but it is not used.
</dd>
@ -540,6 +546,17 @@ and can't be interacted with. Please see the documentation on spells.</dd>
<dd>Most units should have this flag. When false the unit will only get selected
alone, use this for buildings. Enemy units are never selectable by rectangle.
</dd>
<dt>flags<dt>
<dd>You can add a bunch of flags, defined with <a href="#define-bool-flags">define-bool-flags</a>
You can add how many flags you would like, but keep in mind that you have to call
define-bool-flags before.
</dd>
<dt>can-target-flag</dt>
<dd>This allows check for targetting similar to spell conditions. It has
the following form: can-target-flag '(flag-1 true/false/only flag-2 true/false/only ... )
By default everything is set to true, so you can target everything. Only means that
you can only cast units with that flag, and false only units without that flag.
</dd>
<!--IDEA:<dt>force-minimap-color<dt>
<dd>An unit with this flag will ignore any friend/foe/owning player considerations
for the minimap color, and will force this. It takes a number from the palette here.

3
setup
View file

@ -28,7 +28,7 @@
##-----------------------------------------------------------------------------
## HERE ARE SOME USER-CONFIGURABLE VARIABLES
EXTRA_CFLAGS="$CFLAGS -DUNIT_ON_MAP -DNEW_AI -DUSE_LIBMODPLUG -DUSE_HP_FOR_XP"
EXTRA_CFLAGS="$CFLAGS -DUNIT_ON_MAP -DUSE_LIBMODPLUG -DUSE_HP_FOR_XP"
EXTRA_LDFLAGS="$LDFLAGS"
## These could be used for experimental versions
@ -41,7 +41,6 @@ EXTRA_LDFLAGS="$LDFLAGS"
## There are some still not well tested code parts or branches.
## UNIT_ON_MAP Fast lookup of units
## UNITS_ON_MAP Faster lookup of units
## NEW_AI New better improved AI code
## USE_TILECACHE Faster tile drawing, costs memory.
## USE_SMART_TILECACHE Faster tile drawing, slow with hardware video memory.
## USE_HP_FOR_XP Use hit-points for XP calculations.

View file

@ -261,41 +261,6 @@ local void (*HandleActionTable[256])(Unit*) = {
HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten,
};
/**
** Do the runestone work each second.
**
** @param unit unit that heals an area
*/
local void IncrementAreaHealthMana(Unit* unit)
{
Unit* units[UnitMax];
int nunits;
int j;
// Get all the units around the unit
nunits = SelectUnits(unit->X - unit->Stats->SightRange,
unit->Y - unit->Stats->SightRange,
unit->X + unit->Stats->SightRange+unit->Type->Width,
unit->Y + unit->Stats->SightRange+unit->Type->Height,
units);
// Mana and HP on units, 2 every time
for (j = 0; j < nunits; ++j) {
if (units[j] == unit) {
continue;
}
// Restore HP in everything but buildings (even in other player's units)
if (units[j]->Type->Organic && units[j]->HP < units[j]->Stats->HitPoints) {
units[j]->HP++;
}
// Restore mana in all magical units
if(units[j]->Type->CanCastSpell && units[j]->Mana < units[j]->Type->_MaxMana) {
units[j]->Mana++;
}
}
}
/**
** Increment a unit's health
**
@ -593,10 +558,6 @@ global void UnitActions(void)
}
}
//FIXME: Need to configure this to work
if (0) { //Condition for Area Heal
IncrementAreaHealthMana(unit);
}
if (healthiscycle) {
IncrementUnitHealth(unit);
}

View file

@ -103,7 +103,7 @@ local char** ShownUnitTypes; /// Shown editor unit-type table
local int MaxShownUnits; /// Max unit icon draw index
local char ShowUnitsToSelect; /// Show units in unit list
local char ShowBuildingsToSelect; /// Show buildings in unit list
local char ShowHeroesToSelect; /// Show heroes in unit list
//local char ShowHeroesToSelect; /// Show heroes in unit list
local char ShowAirToSelect; /// Show air units in unit list
local char ShowLandToSelect; /// Show land units in unit list
local char ShowWaterToSelect; /// Show water units in unit list
@ -376,9 +376,9 @@ local void RecalculateShownUnits(void)
if (!type->Building && !ShowUnitsToSelect) {
continue;
}
if (type->Hero && !ShowHeroesToSelect) {
continue;
}
// if (type->Hero && !ShowHeroesToSelect) {
// continue;
// }
if (type->UnitType == UnitTypeLand && !ShowLandToSelect) {
continue;
}
@ -567,9 +567,9 @@ local void DrawUnitIcons(void)
VideoDraw(MenuButtonGfx.Sprite,
MBUTTON_GEM_SQUARE + (ShowBuildingsToSelect ? 2 : 0), x + 28 * 1,
y + 16);
VideoDrawText(x + 28 * 2, y, GameFont, "He");
VideoDraw(MenuButtonGfx.Sprite,
MBUTTON_GEM_SQUARE + (ShowHeroesToSelect ? 2 : 0), x + 28 * 2, y + 16);
// VideoDrawText(x + 28 * 2, y, GameFont, "He");
// VideoDraw(MenuButtonGfx.Sprite,
// MBUTTON_GEM_SQUARE + (ShowHeroesToSelect ? 2 : 0), x + 28 * 2, y + 16);
VideoDrawText(x + 28 * 3, y, GameFont, "La");
VideoDraw(MenuButtonGfx.Sprite,
MBUTTON_GEM_SQUARE + (ShowLandToSelect ? 2 : 0), x + 28 * 3, y + 16);
@ -1162,14 +1162,14 @@ local void EditorCallbackButtonDown(unsigned button __attribute__ ((unused)))
RecalculateShownUnits();
return;
}
if (TheUI.InfoPanelX + 10 + 28 * 2 < CursorX &&
CursorX < TheUI.InfoPanelX + 10 + 28 * 3 &&
TheUI.InfoPanelY + 140 < CursorY &&
CursorY < TheUI.InfoPanelY + 140 + 28) {
ShowHeroesToSelect ^= 1;
RecalculateShownUnits();
return;
}
// if (TheUI.InfoPanelX + 10 + 28 * 2 < CursorX &&
// CursorX < TheUI.InfoPanelX + 10 + 28 * 3 &&
// TheUI.InfoPanelY + 140 < CursorY &&
// CursorY < TheUI.InfoPanelY + 140 + 28) {
// ShowHeroesToSelect ^= 1;
// RecalculateShownUnits();
// return;
// }
if (TheUI.InfoPanelX + 10 + 28 * 3 < CursorX &&
CursorX < TheUI.InfoPanelX + 10 + 28 * 4 &&
TheUI.InfoPanelY + 140 < CursorY &&
@ -1924,7 +1924,7 @@ local void CreateEditor(void)
ShowUnitsToSelect = 1; // Show all units as default
ShowBuildingsToSelect = 1;
ShowHeroesToSelect = 1;
// ShowHeroesToSelect = 1;
ShowAirToSelect = 1;
ShowLandToSelect = 1;
ShowWaterToSelect = 1;

View file

@ -137,6 +137,7 @@ global void SaveGame(const char* filename)
SaveConstructions(file);
SaveDecorations(file);
SaveMissileTypes(file);
SaveFlags(file);
SaveUnitTypeDefs(file);
SaveSpells(file);
SaveUnitTypes(file);

View file

@ -95,6 +95,11 @@ struct _spell_action_type_ {
// FIXME" some time information doesn't work as it should.
union {
struct {
int HP; /// Target HP gain.(can be negative)
int Mana; /// Target Mana gain.(can be negative)
} AreaAdjustVitals;
struct {
int Damage; /// Missile damage.
int TTL; /// Missile TTL.
@ -184,14 +189,12 @@ typedef struct ConditionInfo {
#define CONDITION_FALSE 1
#define CONDITION_TRUE 0
#define CONDITION_ONLY 2
char Undead; /// Target is undead.
char Volatile; /// Target is volatile (suicide bomber).
char Organic; /// Target is organic.
char Hero; /// Target is hero. Set this to false for instant-kill spells.
char Coward; /// Target is coward. Don't bloodlust them.
char Alliance; /// Target is allied.
char Building; /// Target is a building.
char TargetSelf; /// Target is the same as the caster.
char *BoolFlag; /// User defined boolean flag.
/// FIXME: NOT IMPLEMENTED:
char UnitBuffed; /// Target is buffed(haste/slow/bloodlust). Dispel magic?
//
@ -218,7 +221,6 @@ typedef struct ConditionInfo {
/**
** Informations about the autocasting mode.
**
*/
typedef struct {
/// FIXME: this below is SQUARE!!!
@ -322,10 +324,14 @@ extern SpellType* SpellTypeById(int Id);
extern unsigned CclGetSpellByIdent(SCM value);
// return 0, 1, 2 for true, only, false.
extern char Scm2Condition(SCM value);
/*
** Spelltype to cast.
*/
SpellFunc CastAreaAdjustVitals;
SpellFunc CastAdjustVitals;
SpellFunc CastAdjustBuffs;
SpellFunc CastPolymorph;

View file

@ -293,16 +293,6 @@
** If this is non-zero, then after that many clicks the unit will
** commit suicide. Doesn't work with resource workers/resources.
**
** UnitType::Sniper
**
** This unit can only hit organic units.
** TODO: have a table of armor types and damage types with damage
** modifiers.
**
** UnitType::Wall
**
** This Unit is a wall, and should exihibit joining properties
**
** UnitType::Building
**
** Unit is a Building
@ -375,10 +365,6 @@
**
** Can do command ground attack
**
** UnitType::IsUndead
**
** Unit is already dead
**
** UnitType::ShoreBuilding
**
** Building must be build on coast
@ -413,20 +399,10 @@
** Only valid for buildings without the BuilderOutside flag.
** The worker is lost when the building is completed.
**
** UnitType::Hero
**
** FIXME: I don't think w*rcr*ft 2 exp heroes have this flag.
** This is should be used for spells, to make heroes imune to
** instant kill spells (like polymorph)
**
** UnitType::Volatile
**
** Unit is a suicide bomber
**
** UnitType::Organic
**
** Organic can be healed
**
** UnitType::SelectableByRectangle
**
** Selectable with mouse rectangle
@ -740,8 +716,6 @@ struct _unit_type_ {
unsigned AirUnit : 1; /// Air animated
unsigned SeaUnit : 1; /// Sea animated
unsigned ExplodeWhenKilled : 1; /// Death explosion animated
unsigned Sniper : 1; /// The unit can only hit organic units.
unsigned Wall : 1; /// Wall
unsigned Building : 1; /// Building
unsigned PermanentCloak : 1; /// Is only visible by CloakDetectors.
unsigned DetectCloak : 1; /// Can see Cloaked units.
@ -749,16 +723,15 @@ struct _unit_type_ {
unsigned Transporter : 1; /// Can transport units
unsigned Vanishes : 1; /// Corpes & destroyed places.
unsigned GroundAttack : 1; /// Can do command ground attack.
unsigned IsUndead : 1; /// Unit is already dead.
unsigned ShoreBuilding : 1; /// Building must be build on coast.
unsigned CanAttack : 1; /// Unit can attack.
unsigned BuilderOutside : 1; /// The builder stays outside during the build.
unsigned BuilderLost : 1; /// The builder is lost after the build.
unsigned Hero : 1; /// Is hero only used for triggers .
unsigned Volatile : 1; /// Unit is a suicide bomber.
unsigned Organic : 1; /// Organic can be healed.
unsigned CanHarvest : 1; /// Resource can be harvested.
unsigned Harvester : 1; /// unit is a resource harvester.
unsigned char *BoolFlag; /// User defined flag. Used for (dis)allow target.
unsigned char *CanTargetFlag; /// Flag needed to target with missile.
unsigned SelectableByRectangle : 1; /// Selectable with mouse rectangle.
unsigned Selectable : 1; /// Unit Is Selectable at all.
@ -818,6 +791,9 @@ extern UnitType*UnitTypeOrcWall; /// Orc wall
extern char** UnitTypeWcNames; /// Mapping wc-number 2 symbol
extern char **BoolFlagName; /// Array of name of user defined bool flag.
extern int NumberBoolFlag; /// Number of user defined bool flag.
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
@ -832,6 +808,7 @@ extern UnitType* UnitTypeByWcNum(unsigned); /// Get unit-type by wc number
/// Get the animations structure by ident
extern Animations* AnimationsByIdent(const char* ident);
extern void SaveFlags(CLFile* file); /// Save declaration of user defined flas.
extern void SaveUnitTypeDefs(CLFile* file); /// Declare the unit-type table first.
extern void SaveUnitTypes(CLFile* file); /// Save the unit-type table
extern UnitType* NewUnitTypeSlot(char*); /// Allocate an empty unit-type slot

View file

@ -1893,7 +1893,6 @@ global void MissileActionWhirlwind(Missile *missile)
// Animate, move.
//
if (!missile->AnimWait--) {
DebugLevel0Fn("?\n");
if (NextMissileFrame(missile, 1, 0)) {
missile->SpriteFrame = 0;
PointToPointMissile(missile);
@ -2018,12 +2017,12 @@ global void MissileActionDeathCoil(Missile *missile)
// calculate organic enemy count
for (i = 0; i < n; ++i) {
ec += (IsEnemy(source->Player, table[i])
&& table[i]->Type->Organic != 0);
/*&& table[i]->Type->Organic != 0*/);
}
if (ec > 0) {
// yes organic enemies found
for (i = 0; i < n; ++i) {
if (IsEnemy(source->Player, table[i]) && table[i]->Type->Organic != 0) {
if (IsEnemy(source->Player, table[i])/* && table[i]->Type->Organic != 0*/) {
// disperse damage between them
//NOTE: 1 is the minimal damage
HitUnit(source,table[i], missile->Damage / ec);

View file

@ -141,7 +141,21 @@ local void CclSpellAction(SCM list, SpellActionType* spellaction)
errl("Unsupported area-bombardment tag", value);
}
}
} else if (gh_eq_p(value, gh_symbol2scm("area-adjust-vitals"))) {
spellaction->CastFunction = CastAreaAdjustVitals;
while (!gh_null_p(list)) {
value = gh_car(list);
list = gh_cdr(list);
if (gh_eq_p(value, gh_symbol2scm("hit-points"))) {
spellaction->Data.AreaAdjustVitals.HP = gh_scm2int(gh_car(list));
list = gh_cdr(list);
} else if (gh_eq_p(value, gh_symbol2scm("mana-points"))) {
spellaction->Data.AreaAdjustVitals.Mana = gh_scm2int(gh_car(list));
list = gh_cdr(list);
} else {
errl("Unsupported area-adjust-vitals tag", value);
}
}
} else if (gh_eq_p(value, gh_symbol2scm("area-bombardment"))) {
spellaction->CastFunction = CastAreaBombardment;
while (!gh_null_p(list)) {
@ -302,7 +316,7 @@ local void CclSpellAction(SCM list, SpellActionType* spellaction)
** @note This is a helper function to make CclSpellCondition shorter
** and easier to understand.
*/
local char Scm2Condition(SCM value)
global char Scm2Condition(SCM value)
{
if (gh_eq_p(value, gh_symbol2scm("true"))) {
return CONDITION_TRUE;
@ -327,7 +341,7 @@ local char Scm2Condition(SCM value)
local void CclSpellCondition(SCM list, ConditionInfo* condition)
{
SCM value;
int i;
//
// Initializations:
//
@ -335,7 +349,8 @@ local void CclSpellCondition(SCM list, ConditionInfo* condition)
// Set everything to 0:
memset(condition, 0, sizeof(ConditionInfo));
// Flags are defaulted to 0(CONDITION_TRUE)
condition->BoolFlag = malloc(NumberBoolFlag * sizeof (*condition->BoolFlag));
memset(condition->BoolFlag, 0, NumberBoolFlag * sizeof (*condition->BoolFlag));
// Initialize min/max stuff to values with no effect.
condition->MinHpPercent = -10;
condition->MaxHpPercent = 1000;
@ -351,18 +366,9 @@ local void CclSpellCondition(SCM list, ConditionInfo* condition)
while (!gh_null_p(list)) {
value = gh_car(list);
list = gh_cdr(list);
if (gh_eq_p(value, gh_symbol2scm("undead"))) {
condition->Undead = Scm2Condition(gh_car(list));
list = gh_cdr(list);
} else if (gh_eq_p(value, gh_symbol2scm("organic"))) {
condition->Organic = Scm2Condition(gh_car(list));
list = gh_cdr(list);
} else if (gh_eq_p(value, gh_symbol2scm("volatile"))) {
if (gh_eq_p(value, gh_symbol2scm("volatile"))) {
condition->Volatile = Scm2Condition(gh_car(list));
list = gh_cdr(list);
} else if (gh_eq_p(value, gh_symbol2scm("hero"))) {
condition->Hero = Scm2Condition(gh_car(list));
list = gh_cdr(list);
} else if (gh_eq_p(value, gh_symbol2scm("coward"))) {
condition->Coward = Scm2Condition(gh_car(list));
list = gh_cdr(list);
@ -403,6 +409,16 @@ local void CclSpellCondition(SCM list, ConditionInfo* condition)
condition->MaxInvincibilityTicks = gh_scm2int(gh_car(list));
list = gh_cdr(list);
} else {
for (i = 0; i < NumberBoolFlag; i++) { // User defined flags
if (gh_eq_p(value, gh_symbol2scm(BoolFlagName[i]))) {
condition->BoolFlag[i] = Scm2Condition(gh_car(list));
list = gh_cdr(list);
break;
}
}
if (i != NumberBoolFlag) {
continue;
}
errl("Unsuported condition tag", value);
}
}
@ -597,6 +613,15 @@ local void SaveSpellAction(CLFile *file,SpellActionType* action)
action->Data.AreaBombardment.Damage,
action->Data.AreaBombardment.StartOffsetX,
action->Data.AreaBombardment.StartOffsetY);
} else if (action->CastFunction == CastAreaAdjustVitals) {
CLprintf(file, "(area-adjust-vitals");
if (action->Data.AreaAdjustVitals.HP) {
CLprintf(file, " hit-points %d", action->Data.AdjustVitals.HP);
}
if (action->Data.AreaAdjustVitals.Mana) {
CLprintf(file, " mana-points %d", action->Data.AdjustVitals.Mana);
}
CLprintf(file, ")\n");
} else if (action->CastFunction == CastSpawnMissile) {
CLprintf(file, "(spawn-missile delay %d ttl %d damage %d ",
action->Data.SpawnMissile.Delay,
@ -691,7 +716,7 @@ local void SaveSpellCondition(CLFile *file, ConditionInfo* condition)
"false", /// CONDITION_FALSE
"only" /// CONDITION_ONLY
};
int i;
DebugCheck(!file);
DebugCheck(!condition);
@ -700,18 +725,9 @@ local void SaveSpellCondition(CLFile *file, ConditionInfo* condition)
// First save data related to flags.
// NOTE: (int) is there to keep compilers happy.
//
if (condition->Undead != CONDITION_TRUE) {
CLprintf(file, "undead %s ", condstrings[(int)condition->Undead]);
}
if (condition->Organic != CONDITION_TRUE) {
CLprintf(file, "organic %s ", condstrings[(int)condition->Organic]);
}
if (condition->Volatile != CONDITION_TRUE) {
CLprintf(file, "volatile %s ", condstrings[(int)condition->Volatile]);
}
if (condition->Hero != CONDITION_TRUE) {
CLprintf(file, "hero %s ", condstrings[(int)condition->Hero]);
}
if (condition->Coward != CONDITION_TRUE) {
CLprintf(file, "coward %s ", condstrings[(int)condition->Coward]);
}
@ -724,6 +740,12 @@ local void SaveSpellCondition(CLFile *file, ConditionInfo* condition)
if (condition->TargetSelf != CONDITION_TRUE) {
CLprintf(file, "self %s ", condstrings[(int)condition->TargetSelf]);
}
for (i = 0; i < NumberBoolFlag; i++) { // User defined flags
if (condition->BoolFlag[i] != CONDITION_TRUE) {
CLprintf(file, "%s %s ",
BoolFlagName[i], condstrings[(int)condition->BoolFlag[i]]);
}
}
//
// Min/Max vital percents
//

View file

@ -209,6 +209,64 @@ global int CastSpawnPortal(Unit* caster, const SpellType* spell __attribute__((u
return 0;
}
/**
** Cast Area Adjust Vitals on all valid units in range.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param x X coord of target spot when/if target does not exist
** @param y Y coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
global int CastAreaAdjustVitals(Unit* caster, const SpellType* spell,
const SpellActionType* action, Unit* target __attribute__((unused)), int x, int y)
{
Unit* units[UnitMax];
int nunits;
int j;
int hp;
int mana;
DebugCheck(!caster);
DebugCheck(!spell);
DebugCheck(!action);
// Get all the units around the unit
nunits = SelectUnits(x - spell->Range,
y - spell->Range,
x + spell->Range + caster->Type->Width,
y + spell->Range + caster->Type->Height,
units);
hp = action->Data.AreaAdjustVitals.HP;
mana = action->Data.AreaAdjustVitals.Mana;
caster->Mana -= spell->ManaCost;
for (j = 0; j < nunits; ++j) {
target = units[j];
// if (!PassCondition(caster, spell, target, x, y) {
if (!CanCastSpell(caster, spell, target, x, y)) {
continue;
}
if (hp < 0) {
HitUnit(caster, target, hp);
} else {
target->HP += hp;
if (target->HP > target->Stats->HitPoints) {
target->HP = target->Stats->HitPoints;
}
}
target->Mana += mana;
if (target->Mana < 0) {
target->Mana = 0;
}
if (target->Mana > target->Type->_MaxMana) {
target->Mana = target->Type->_MaxMana;
}
}
return 0;
}
// AreaBombardment
// NOTE: vladi: blizzard differs than original in this way:
// original: launches 50 shards at 5 random spots x 10 for 25 mana.
@ -697,6 +755,7 @@ local Target* NewTargetPosition(int x, int y)
local int PassCondition(const Unit* caster, const SpellType* spell, const Unit* target,
int x, int y, const ConditionInfo* condition)
{
int i;
//
// Check caster mana. FIXME: move somewhere else?
//
@ -717,16 +776,16 @@ local int PassCondition(const Unit* caster, const SpellType* spell, const Unit*
// Now check conditions regarding the target unit.
//
if (target) {
if (condition->Undead != CONDITION_TRUE) {
if ((condition->Undead == CONDITION_ONLY) ^ (target->Type->IsUndead)) {
return 0;
}
}
if (condition->Organic != CONDITION_TRUE) {
if ((condition->Organic == CONDITION_ONLY) ^ (target->Type->Organic)) {
return 0;
}
}
// if (condition->Undead != CONDITION_TRUE) {
// if ((condition->Undead == CONDITION_ONLY) ^ (target->Type->IsUndead)) {
// return 0;
// }
// }
// if (condition->Organic != CONDITION_TRUE) {
// if ((condition->Organic == CONDITION_ONLY) ^ (target->Type->Organic)) {
// return 0;
// }
// }
if (condition->Volatile != CONDITION_TRUE) {
if ((condition->Volatile == CONDITION_ONLY) ^ (target->Type->Volatile)) {
return 0;
@ -737,16 +796,23 @@ local int PassCondition(const Unit* caster, const SpellType* spell, const Unit*
return 0;
}
}
if (condition->Hero != CONDITION_TRUE) {
if ((condition->Hero == CONDITION_ONLY) ^ (target->Type->Hero)) {
return 0;
}
}
// if (condition->Hero != CONDITION_TRUE) {
// if ((condition->Hero == CONDITION_ONLY) ^ (target->Type->Hero)) {
// return 0;
// }
// }
if (condition->Coward != CONDITION_TRUE) {
if ((condition->Coward == CONDITION_ONLY) ^ (target->Type->Coward)) {
return 0;
}
}
for (i = 0; i < NumberBoolFlag; i++) { // User defined flags
if (condition->BoolFlag[i] != CONDITION_TRUE) {
if ((condition->BoolFlag[i] == CONDITION_ONLY) ^ (target->Type->BoolFlag[i])) {
return 0;
}
}
}
if (condition->Alliance != CONDITION_TRUE) {
if ((condition->Alliance == CONDITION_ONLY) ^
(IsAllied(caster->Player,target) || target->Player == caster->Player)) {
@ -1184,6 +1250,7 @@ void CleanSpells(void)
free(spell->Name);
free(spell->Action);
if (spell->Condition) {
free(spell->Condition->BoolFlag);
free(spell->Condition);
}
//
@ -1191,12 +1258,14 @@ void CleanSpells(void)
//
if (spell->AutoCast) {
if (spell->AutoCast->Condition) {
free(spell->AutoCast->Condition->BoolFlag);
free(spell->AutoCast->Condition);
}
free(spell->AutoCast);
}
if (spell->AICast) {
if (spell->AICast->Condition) {
free(spell->AICast->Condition->BoolFlag);
free(spell->AICast->Condition);
}
free(spell->AICast);

View file

@ -273,7 +273,11 @@ global void DoRightButton(int sx, int sy)
DebugCheck(spellnum == SpellTypeCount);
SendCommandSpellCast(unit, x, y, dest, spellnum, flush);
} else {
SendCommandAttack(unit, x, y, dest, flush);
if (CanTarget(unit->Type, dest->Type)) {
SendCommandAttack(unit, x, y, dest, flush);
} else { // No valid target
SendCommandAttack(unit, x, y, NoUnitP, flush);
}
}
continue;
}

View file

@ -62,6 +62,9 @@ global _AnimationsHash AnimationsHash; /// Animations hash table
local ccl_smob_type_t SiodUnitTypeTag; /// siod unit-type object
global char **BoolFlagName = NULL; /// Name of user defined flag
global int NumberBoolFlag = 0; /// Number of defined flags.
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
@ -128,6 +131,11 @@ local SCM CclDefineUnitType(SCM list)
type->_RegenerationRate = 0;
type->Selectable = 1;
}
type->BoolFlag = realloc(type->BoolFlag, NumberBoolFlag * sizeof (*type->BoolFlag));
memset(type->BoolFlag, 0, NumberBoolFlag * sizeof (*type->BoolFlag));
type->CanTargetFlag = realloc(type->CanTargetFlag, NumberBoolFlag * sizeof (*type->CanTargetFlag));
memset(type->CanTargetFlag, 0, NumberBoolFlag * sizeof (*type->CanTargetFlag));
type->NumDirections = 8;
//
@ -406,8 +414,6 @@ local SCM CclDefineUnitType(SCM list)
} else if (gh_eq_p(value, gh_symbol2scm("building"))) {
type->Building = 1;
} else if (gh_eq_p(value, gh_symbol2scm("wall"))) {
type->Wall = 1;
} else if (gh_eq_p(value, gh_symbol2scm("builder-outside"))) {
type->BuilderOutside = 1;
} else if (gh_eq_p(value, gh_symbol2scm("builder-lost"))) {
@ -429,8 +435,6 @@ local SCM CclDefineUnitType(SCM list)
} else if (gh_eq_p(value, gh_symbol2scm("clicks-to-explode"))) {
type->ClicksToExplode = gh_scm2int(gh_car(list));
list = gh_cdr(list);
} else if (gh_eq_p(value, gh_symbol2scm("sniper"))) {
type->Sniper = 1;
} else if (gh_eq_p(value, gh_symbol2scm("permanent-cloak"))) {
type->PermanentCloak = 1;
} else if (gh_eq_p(value, gh_symbol2scm("detect-cloak"))) {
@ -506,12 +510,8 @@ local SCM CclDefineUnitType(SCM list)
}
} else if (gh_eq_p(value, gh_symbol2scm("vanishes"))) {
type->Vanishes = 1;
} else if (gh_eq_p(value, gh_symbol2scm("hero"))) {
type->Hero = 1;
} else if (gh_eq_p(value, gh_symbol2scm("volatile"))) {
type->Volatile = 1;
} else if (gh_eq_p(value, gh_symbol2scm("isundead"))) {
type->IsUndead = 1;
} else if (gh_eq_p(value, gh_symbol2scm("can-cast-spell"))) {
//
// Warning: can-cast-spell should only be used AFTER all spells
@ -535,8 +535,29 @@ local SCM CclDefineUnitType(SCM list)
sublist = gh_cdr(sublist);
type->Magic = 1;
}
} else if (gh_eq_p(value, gh_symbol2scm("organic"))) {
type->Organic = 1;
} else if (gh_eq_p(value, gh_symbol2scm("can-target-flag"))) {
//
// Warning: can-target-flag should only be used AFTER all bool flags
// have been defined.
//
sublist = gh_car(list);
list = gh_cdr(list);
while (!gh_null_p(sublist)) {
value = gh_car(sublist);
sublist = gh_cdr(sublist);
for (i = 0; i < NumberBoolFlag; i++) {
if (gh_eq_p(value, gh_symbol2scm(BoolFlagName[i]))) {
type->CanTargetFlag[i] = Scm2Condition(gh_car(sublist));
sublist = gh_cdr(sublist);
break;
}
}
if (i != NumberBoolFlag) {
continue;
}
printf("\n%s\n", type->Name);
errl("Unsupported flag tag for can-target-flag", value);
}
} else if (gh_eq_p(value, gh_symbol2scm("selectable-by-rectangle"))) {
type->SelectableByRectangle = 1;
} else if (gh_eq_p(value, gh_symbol2scm("teleporter"))) {
@ -596,6 +617,15 @@ local SCM CclDefineUnitType(SCM list)
}
}
} else {
for (i = 0; i < NumberBoolFlag; i++) { // User defined bool flags
if (gh_eq_p(value, gh_symbol2scm(BoolFlagName[i]))) {
type->BoolFlag[i] = 1;
break;
}
}
if (i != NumberBoolFlag) {
continue;
}
// FIXME: this leaves a half initialized unit-type
printf("\n%s\n",type->Name);
errl("Unsupported tag", value);
@ -978,7 +1008,7 @@ local SCM CclDefineAnimations(SCM list)
if (anims->Attack) {
free(anims->Attack);
}
anims->Attack=anim;
anims->Attack = anim;
} else if (gh_eq_p(id, gh_symbol2scm("die"))) {
if (anims->Die) {
free(anims->Die);
@ -995,6 +1025,37 @@ local SCM CclDefineAnimations(SCM list)
return SCM_UNSPECIFIED;
}
/*
** Define boolean flag.
**
** @param list : list of flags' name.
*/
local SCM CclDefineBoolFlags(SCM list)
{
char* str;
int i;
if (NumberBoolFlag != 0) {
DebugLevel0("Warning, Redefine Bool flags\n");
}
while (!gh_null_p(list)) {
str = gh_scm2newstr(gh_car(list), NULL);
list = gh_cdr(list);
for (i = 0; i < NumberBoolFlag; i++) {
if (!strcmp(str, BoolFlagName[i])) {
DebugLevel0("Warning, Bool flags already defined\n");
break;
}
}
if (i != NumberBoolFlag) {
break;
}
BoolFlagName = realloc(BoolFlagName, (NumberBoolFlag + 1) * sizeof (*BoolFlagName));
BoolFlagName[NumberBoolFlag++] = str;
}
return SCM_UNSPECIFIED;
}
// ----------------------------------------------------------------------------
/**
@ -1004,6 +1065,7 @@ global void UnitTypeCclRegister(void)
{
gh_new_procedureN("define-unit-type", CclDefineUnitType);
gh_new_procedureN("define-unit-stats", CclDefineUnitStats);
gh_new_procedureN("define-bool-flags", CclDefineBoolFlags);
SiodUnitTypeTag = CclMakeSmobType("UnitType");

View file

@ -3311,9 +3311,14 @@ global int IsSharedVision(const Player* player, const Unit* dest)
*/
global int CanTarget(const UnitType* source, const UnitType* dest)
{
// Hack for snipers, can only target organic units.
if (!dest->Organic && source->Sniper) {
return 0;
int i;
for (i = 0; i < NumberBoolFlag; i++) {
if (source->CanTargetFlag[i] != CONDITION_TRUE) {
if ((source->CanTargetFlag[i] == CONDITION_ONLY) ^ (dest->BoolFlag[i])) {
return 0;
}
}
}
if (dest->UnitType == UnitTypeLand) {
if (dest->ShoreBuilding) {

View file

@ -481,17 +481,17 @@ global void ParsePudUDTA(const char* udta, int length __attribute__((unused)))
unittype->CanStore[GoldCost] = BIT(12, v);
unittype->Vanishes = BIT(13, v);
unittype->GroundAttack = BIT(14, v);
unittype->IsUndead = BIT(15, v);
// unittype->IsUndead = BIT(15, v);
unittype->ShoreBuilding = BIT(16, v);
// unittype->CanCastSpell = BIT(17,v);
// unittype->CanCastSpell = (char*)malloc(/*nb_spell*/);
unittype->CanCastSpell = NULL;//
unittype->CanStore[WoodCost] = BIT(18, v);
unittype->CanAttack = BIT(19, v);
unittype->Hero = BIT(23, v);
// unittype->Hero = BIT(23, v);
unittype->CanStore[OilCost] = BIT(24, v);
unittype->Volatile = BIT(25, v);
unittype->Organic = BIT(27, v);
// unittype->Organic = BIT(27, v);
if (BIT(11, v) || BIT(21, v)) {
unittype->GivesResource = OilCost;
@ -890,6 +890,14 @@ local void SaveUnitType(CLFile* file, const UnitType* type, int all)
}
CLprintf(file, "\n");
}
CLprintf(file, "'can-target-flag '(");
for (i = 0; i < NumberBoolFlag; i++) { // User defined flags
if (type->CanTargetFlag[i] != CONDITION_TRUE) {
CLprintf(file, "%s %s ", BoolFlagName[i],
type->CanTargetFlag[i] == CONDITION_ONLY ? "only" : "false");
}
}
CLprintf(file, ") ");
if (type->Building) {
CLprintf(file, " 'building");
@ -923,9 +931,6 @@ local void SaveUnitType(CLFile* file, const UnitType* type, int all)
if (type->ClicksToExplode) {
CLprintf(file, " 'clicks-to-explode %d\n", type->ClicksToExplode);
}
if (type->Sniper) {
CLprintf(file, " 'sniper \n");
}
if (type->Revealer) {
CLprintf(file, " 'revealer\n");
}
@ -1034,24 +1039,20 @@ local void SaveUnitType(CLFile* file, const UnitType* type, int all)
if (type->Vanishes) {
CLprintf(file, " 'vanishes\n");
}
if (type->Hero) {
CLprintf(file, " 'hero\n");
}
if (type->Volatile) {
CLprintf(file, " 'volatile\n");
}
if (type->IsUndead) {
CLprintf(file, " 'isundead\n");
}
if (type->Organic) {
CLprintf(file, " 'organic\n");
}
if (type->SelectableByRectangle) {
CLprintf(file, " 'selectable-by-rectangle\n");
}
if (type->Teleporter) {
CLprintf(file, " 'teleporter\n");
}
for (i = 0; i < NumberBoolFlag; i++) { // User defined flags
if (type->BoolFlag[i]) {
CLprintf(file, " '%s\n", BoolFlagName[i]);
}
}
CLprintf(file, " 'sounds '(");
@ -1119,6 +1120,24 @@ local void SaveUnitStats(const UnitStats* stats,const char* ident,int plynr,
CLprintf(file, ") )\n");
}
/**
** Save declaration of user defined flags.
**
** @param file Output file.
*/
global void SaveFlags(CLFile* file)
{
int i;
if (NumberBoolFlag != 0) {
CLprintf(file, "(define-bool-flags");
for (i = 0; i < NumberBoolFlag; i++) {
CLprintf(file, " '%s", BoolFlagName[i]);
}
CLprintf(file, ")\n");
}
}
/**
** Save the names of all unit types, before actually defining anything about them.
**
@ -1479,6 +1498,9 @@ global void CleanUnitTypes(void)
DebugCheck(!type->Name);
free(type->Name);
free(type->BoolFlag);
free(type->CanTargetFlag);
if (type->SameSprite) {
free(type->SameSprite);
}
@ -1563,6 +1585,13 @@ global void CleanUnitTypes(void)
}
NumUnitTypes = 0;
for (i = 0; i < NumberBoolFlag; i++) { // User defined flags
free(BoolFlagName[i]);
}
free(BoolFlagName);
BoolFlagName = NULL;
NumberBoolFlag = 0;
//
// Clean hardcoded unit types.
//