diff --git a/src/ai/ai_magic.cpp b/src/ai/ai_magic.cpp index d7ec204cd..d7a266ed3 100644 --- a/src/ai/ai_magic.cpp +++ b/src/ai/ai_magic.cpp @@ -74,7 +74,8 @@ global void AiCheckMagic(void) if(unit->Type->CanCastSpell) { for (j = 0; j < SpellTypeCount; j++) { // Check if we can cast this spell. SpellIsAvailable checks for upgrades. - if (unit->Type->CanCastSpell[j] && SpellIsAvailable(player, j)) { + if (unit->Type->CanCastSpell[j] && SpellIsAvailable(player, j) && + SpellTypeById(j)->AutoCast) { #ifdef DEBUG success = // Follow on next line (AutoCastSpell). #endif diff --git a/src/include/spells.h b/src/include/spells.h index 52984c157..44281d0db 100644 --- a/src/include/spells.h +++ b/src/include/spells.h @@ -89,82 +89,81 @@ typedef enum { #endif } TargetType; +/* +** Pointer on function that cast the spell. +*/ +typedef int SpellFunc(Unit* caster, const struct _spell_type_* spell, Unit* target,int x, int y); - - -typedef union +typedef struct { -// FIXME rename structure more properly. -// TTL's below are in ticks: approx: 500=13sec, 1000=25sec, 2000=50sec -// FIXME use TTL, as in TICKS to live - struct { - int Fields; /// The size of the affected square - int Shards; /// Number of shards thrown. - int Damage; /// Damage for every shard. - /// The offset of the missile start point to the hit location. - int StartOffsetX; - int StartOffsetY; - } AreaBombardment; - - struct { - UnitType *PortalType; /// The unit type spawned - } SpawnPortal; - - struct { - int TTL; /// time to live (ticks) - int Damage; /// Damage. - } Fireball; - - struct { - int TTL; /// time to live (ticks) - } FlameShield; + SpellFunc* CastFunction; + union { +// FIXME time information doesn't work as it should. + struct { + int Fields; /// The size of the affected square + int Shards; /// Number of shards thrown. + int Damage; /// Damage for every shard. + /// The offset of the missile start point to the hit location. + int StartOffsetX; + int StartOffsetY; + } AreaBombardment; + + struct { + UnitType *PortalType; /// The unit type spawned + } SpawnPortal; + + struct { + int TTL; /// time to live (ticks) + int Damage; /// Damage. + } Fireball; + + struct { + int TTL; /// time to live (ticks) + } FlameShield; - struct { - int HasteTicks; /// Number of ticks to set Haste to. - int SlowTicks; /// Number of ticks to set Slow to. - int BloodlustTicks; /// Number of ticks to set Bloodlust to. - int InvisibilityTicks; /// Number of ticks to set Invisibility to. - int InvincibilityTicks; /// Number of ticks to set UnholyArmor to. + struct { + int HasteTicks; /// Number of ticks to set Haste to. + int SlowTicks; /// Number of ticks to set Slow to. + int BloodlustTicks; /// Number of ticks to set Bloodlust to. + int InvisibilityTicks; /// Number of ticks to set Invisibility to. + int InvincibilityTicks; /// Number of ticks to set UnholyArmor to. #define BUFF_NOT_AFFECTED 0xC0FF33 /// Don't like the value? The value doesn't like you! - } AdjustBuffs; - - struct { - int HP; /// Target HP gain.(can be negative) - int Mana; /// Target Mana gain.(can be negative) - /// This spell is designed to be used wit very small amounts. The spell - /// can scale up to MaxMultiCast times. Use 0 for infinite. - int MaxMultiCast; - } AdjustVitals; - - struct { - UnitType *revealer; /// Type of unit to be summoned: (unit-revealer). - } HolyVision; - - struct { - UnitType *NewForm; /// The new form - // TODO: temporary polymorphs would be awesome, but hard to implement - } Polymorph; - - struct { - UnitType *UnitType; /// Type of unit to be summoned. - int TTL; /// Time to live for summoned unit. 0 means infinite - } Summon; - - struct { - UnitType *UnitRaised; /// The unit to spawn from corpses - int TTL; /// Time to live for summon. 0 means infinite. - } RaiseDead; - // What about a resurection spell? + } AdjustBuffs; + + struct { + int HP; /// Target HP gain.(can be negative) + int Mana; /// Target Mana gain.(can be negative) + /// This spell is designed to be used wit very small amounts. The spell + /// can scale up to MaxMultiCast times. Use 0 for infinite. + int MaxMultiCast; + } AdjustVitals; + + struct { + UnitType *NewForm; /// The new form + // TODO: temporary polymorphs would be awesome, but hard to implement + } Polymorph; + + struct { + UnitType *UnitType; /// Type of unit to be summoned. + int TTL; /// Time to live for summoned unit. 0 means infinite + } Summon; + + struct { + UnitType *UnitRaised; /// The unit to spawn from corpses + int TTL; /// Time to live for summon. 0 means infinite. + } RaiseDead; + // What about a resurection spell? - struct { - int TTL; /// time to live (ticks) - int Damage; /// Damage. - } Runes; - - struct { - int TTL; /// time to live (ticks) - // FIXME: more configurations - } Whirlwind; + struct { + int TTL; /// time to live (ticks) + int Damage; /// Damage. + } Runes; + + struct { + int TTL; /// time to live (ticks) + // FIXME: more configurations + } Whirlwind; + } Data; } SpellActionType; /* @@ -242,11 +241,6 @@ typedef struct { struct _spell_type_; -/* -** Pointer on function that cast the spell. -*/ -typedef int SpellFunc(Unit* caster, const struct _spell_type_* spell, Unit* target,int x, int y); - /** ** Base structure of a spell type. */ @@ -258,8 +252,7 @@ typedef struct _spell_type_ { // Spell Specifications TargetType Target; /// Targetting information. See TargetType. - SpellFunc *CastFunction; /// function to cast the spell. - SpellActionType *SpellAction; /// More arguments for spell (damage, delay, additional sounds...). + SpellActionType *Action; /// More arguments for spell (damage, delay, additional sounds...). #define INFINITE_RANGE 0xFFFFFFF int Range; /// Max range of the target. int ManaCost; /// required mana for each cast @@ -336,7 +329,6 @@ extern unsigned CclGetSpellByIdent(SCM value); ** Spelltype to cast. */ -SpellFunc CastHolyVision; SpellFunc CastAdjustVitals; SpellFunc CastAdjustBuffs; SpellFunc CastFireball; @@ -350,7 +342,6 @@ SpellFunc CastRaiseDead; SpellFunc CastWhirlwind; SpellFunc CastSpawnPortal; - //@} #endif // !__SPELLS_H__ diff --git a/src/stratagus/script_spell.cpp b/src/stratagus/script_spell.cpp index c2ec3e1cb..1f8aee882 100644 --- a/src/stratagus/script_spell.cpp +++ b/src/stratagus/script_spell.cpp @@ -109,36 +109,36 @@ local void CclParseSpellAction(SCM list, SpellType* spell, SpellActionType *spel memset(spellaction, 0, sizeof(*spellaction)); if (gh_eq_p(value,gh_symbol2scm("area-bombardment"))) { - spell->CastFunction=CastAreaBombardment; + spellaction->CastFunction=CastAreaBombardment; while (!gh_null_p(list)) { value = gh_car(list); list=gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("fields"))) { - spellaction->AreaBombardment.Fields = gh_scm2int(gh_car(list)); + spellaction->Data.AreaBombardment.Fields = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("shards"))) { - spellaction->AreaBombardment.Shards = gh_scm2int(gh_car(list)); + spellaction->Data.AreaBombardment.Shards = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("damage"))) { - spellaction->AreaBombardment.Damage = gh_scm2int(gh_car(list)); + spellaction->Data.AreaBombardment.Damage = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("start-offset-x"))) { - spellaction->AreaBombardment.StartOffsetX = gh_scm2int(gh_car(list)); + spellaction->Data.AreaBombardment.StartOffsetX = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("start-offset-y"))) { - spellaction->AreaBombardment.StartOffsetY = gh_scm2int(gh_car(list)); + spellaction->Data.AreaBombardment.StartOffsetY = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else { errl("Unsupported area-bombardment tag",value); } } } else if (gh_eq_p(value,gh_symbol2scm("flame-shield"))) { - spell->CastFunction=CastFlameShield; + spellaction->CastFunction=CastFlameShield; while (!gh_null_p(list)) { value = gh_car(list); list=gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("duration"))) { - spellaction->FlameShield.TTL = gh_scm2int(gh_car(list)); + spellaction->Data.FlameShield.TTL = gh_scm2int(gh_car(list)); list = gh_cdr(list); /// FIXME:damage, missiles, rotation speed? } else { @@ -146,107 +146,107 @@ local void CclParseSpellAction(SCM list, SpellType* spell, SpellActionType *spel } } } else if (gh_eq_p(value,gh_symbol2scm("fireball"))) { - spell->CastFunction=CastFireball; + spellaction->CastFunction=CastFireball; while (!gh_null_p(list)) { value = gh_car(list); list=gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("damage"))) { - spellaction->Fireball.Damage = gh_scm2int(gh_car(list)); + spellaction->Data.Fireball.Damage = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("ttl"))) { - spellaction->Fireball.TTL = gh_scm2int(gh_car(list)); + spellaction->Data.Fireball.TTL = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else { errl("Unsupported fireball tag",value); } } } else if (gh_eq_p(value,gh_symbol2scm("runes"))) { - spell->CastFunction=CastRunes; + spellaction->CastFunction=CastRunes; while (!gh_null_p(list)) { value = gh_car(list); list=gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("damage"))) { - spellaction->Fireball.Damage = gh_scm2int(gh_car(list)); + spellaction->Data.Fireball.Damage = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("ttl"))) { - spellaction->Fireball.TTL = gh_scm2int(gh_car(list)); + spellaction->Data.Fireball.TTL = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else { errl("Unsupported runes tag",value); } } } else if (gh_eq_p(value,gh_symbol2scm("whirlwind"))) { - spell->CastFunction=CastWhirlwind; + spellaction->CastFunction=CastWhirlwind; while (!gh_null_p(list)) { value = gh_car(list); list=gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("duration"))) { - spellaction->Whirlwind.TTL = gh_scm2int(gh_car(list)); + spellaction->Data.Whirlwind.TTL = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else { errl("Unsupported runes tag",value); } } } else if (gh_eq_p(value,gh_symbol2scm("adjust-buffs"))) { - spell->CastFunction=CastAdjustBuffs; - spellaction->AdjustBuffs.HasteTicks=BUFF_NOT_AFFECTED; - spellaction->AdjustBuffs.SlowTicks=BUFF_NOT_AFFECTED; - spellaction->AdjustBuffs.BloodlustTicks=BUFF_NOT_AFFECTED; - spellaction->AdjustBuffs.InvisibilityTicks=BUFF_NOT_AFFECTED; - spellaction->AdjustBuffs.InvincibilityTicks=BUFF_NOT_AFFECTED; + spellaction->CastFunction=CastAdjustBuffs; + spellaction->Data.AdjustBuffs.HasteTicks=BUFF_NOT_AFFECTED; + spellaction->Data.AdjustBuffs.SlowTicks=BUFF_NOT_AFFECTED; + spellaction->Data.AdjustBuffs.BloodlustTicks=BUFF_NOT_AFFECTED; + spellaction->Data.AdjustBuffs.InvisibilityTicks=BUFF_NOT_AFFECTED; + spellaction->Data.AdjustBuffs.InvincibilityTicks=BUFF_NOT_AFFECTED; while (!gh_null_p(list)) { value = gh_car(list); list=gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("haste-ticks"))) { - spellaction->AdjustBuffs.HasteTicks = gh_scm2int(gh_car(list)); + spellaction->Data.AdjustBuffs.HasteTicks = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("slow-ticks"))) { - spellaction->AdjustBuffs.SlowTicks = gh_scm2int(gh_car(list)); + spellaction->Data.AdjustBuffs.SlowTicks = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("bloodlust-ticks"))) { - spellaction->AdjustBuffs.BloodlustTicks = gh_scm2int(gh_car(list)); + spellaction->Data.AdjustBuffs.BloodlustTicks = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("invisibility-ticks"))) { - spellaction->AdjustBuffs.InvisibilityTicks = gh_scm2int(gh_car(list)); + spellaction->Data.AdjustBuffs.InvisibilityTicks = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("invincibility-ticks"))) { - spellaction->AdjustBuffs.InvincibilityTicks = gh_scm2int(gh_car(list)); + spellaction->Data.AdjustBuffs.InvincibilityTicks = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else { errl("Unsupported adjust-buffs tag",value); } } } else if (gh_eq_p(value,gh_symbol2scm("summon"))) { - spell->CastFunction=CastSummon; + spellaction->CastFunction=CastSummon; while (!gh_null_p(list)) { value = gh_car(list); list = gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("unit-type"))) { str = gh_scm2newstr(gh_car(list),0); - spellaction->Summon.UnitType = UnitTypeByIdent(str); - if (!spellaction->Summon.UnitType) { - spellaction->Summon.UnitType = 0; + spellaction->Data.Summon.UnitType = UnitTypeByIdent(str); + if (!spellaction->Data.Summon.UnitType) { + spellaction->Data.Summon.UnitType = 0; DebugLevel0("unit type \"%s\" not found for summon spell.\n" _C_ str); } free(str); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("time-to-live"))) { - spellaction->Summon.TTL = gh_scm2int(gh_car(list)); + spellaction->Data.Summon.TTL = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else { errl("Unsupported summon tag",value); } } } else if (gh_eq_p(value,gh_symbol2scm("spawn-portal"))) { - spell->CastFunction=CastSpawnPortal; + spellaction->CastFunction=CastSpawnPortal; while (!gh_null_p(list)) { value = gh_car(list); list = gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("portal-type"))) { str = gh_scm2newstr(gh_car(list),0); - spellaction->SpawnPortal.PortalType = UnitTypeByIdent(str); - if (!spellaction->SpawnPortal.PortalType) { - spellaction->SpawnPortal.PortalType = 0; + spellaction->Data.SpawnPortal.PortalType = UnitTypeByIdent(str); + if (!spellaction->Data.SpawnPortal.PortalType) { + spellaction->Data.SpawnPortal.PortalType = 0; DebugLevel0("unit type \"%s\" not found for spawn-portal.\n" _C_ str); } free(str); @@ -256,36 +256,36 @@ local void CclParseSpellAction(SCM list, SpellType* spell, SpellActionType *spel } } } else if (gh_eq_p(value,gh_symbol2scm("raise-dead"))) { - spell->CastFunction=CastRaiseDead; + spellaction->CastFunction=CastRaiseDead; while (!gh_null_p(list)) { value = gh_car(list); list = gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("unit-raised"))) { str = gh_scm2newstr(gh_car(list),0); - spellaction->RaiseDead.UnitRaised = UnitTypeByIdent(str); - if (!spellaction->RaiseDead.UnitRaised) { - spellaction->RaiseDead.UnitRaised = 0; + spellaction->Data.RaiseDead.UnitRaised = UnitTypeByIdent(str); + if (!spellaction->Data.RaiseDead.UnitRaised) { + spellaction->Data.RaiseDead.UnitRaised = 0; DebugLevel0("unit type \"%s\" not found for summon spell.\n" _C_ str); } free(str); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("time-to-live"))) { - spellaction->RaiseDead.TTL = gh_scm2int(gh_car(list)); + spellaction->Data.RaiseDead.TTL = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else { errl("Unsupported raise-dead tag",value); } } } else if (gh_eq_p(value,gh_symbol2scm("polymorph"))) { - spell->CastFunction=CastPolymorph; + spellaction->CastFunction=CastPolymorph; while (!gh_null_p(list)) { value = gh_car(list); list = gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("new-form"))) { str = gh_scm2newstr(gh_car(list),0); - spellaction->Summon.UnitType = UnitTypeByIdent(str); - if (!spellaction->Summon.UnitType) { - spellaction->Summon.UnitType = 0; + spellaction->Data.Summon.UnitType = UnitTypeByIdent(str); + if (!spellaction->Data.Summon.UnitType) { + spellaction->Data.Summon.UnitType = 0; DebugLevel0("unit type \"%s\" not found for summon spell.\n" _C_ str); } free(str); @@ -296,18 +296,18 @@ local void CclParseSpellAction(SCM list, SpellType* spell, SpellActionType *spel } } } else if (gh_eq_p(value,gh_symbol2scm("adjust-vitals"))) { - spell->CastFunction=CastAdjustVitals; + spellaction->CastFunction=CastAdjustVitals; while (!gh_null_p(list)) { value = gh_car(list); list=gh_cdr(list); if (gh_eq_p(value, gh_symbol2scm("hit-points"))) { - spellaction->AdjustVitals.HP = gh_scm2int(gh_car(list)); + spellaction->Data.AdjustVitals.HP = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("mana-points"))) { - spellaction->AdjustVitals.Mana = gh_scm2int(gh_car(list)); + spellaction->Data.AdjustVitals.Mana = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else if (gh_eq_p(value, gh_symbol2scm("max-multi-cast"))) { - spellaction->AdjustVitals.MaxMultiCast = gh_scm2int(gh_car(list)); + spellaction->Data.AdjustVitals.MaxMultiCast = gh_scm2int(gh_car(list)); list = gh_cdr(list); } else { errl("Unsupported adjust-vitals tag",value); @@ -396,7 +396,7 @@ local void CclSpellParseCondition(SCM list, ConditionInfo* condition) condition->TargetSelf=Scm2Condition(gh_car(list)); list=gh_cdr(list); } else if (gh_eq_p(value,gh_symbol2scm("min-hp-percent"))) { - condition->MaxHpPercent=gh_scm2int(gh_car(list)); + condition->MinHpPercent=gh_scm2int(gh_car(list)); list=gh_cdr(list); } else if (gh_eq_p(value,gh_symbol2scm("max-hp-percent"))) { condition->MaxHpPercent=gh_scm2int(gh_car(list)); @@ -525,8 +525,8 @@ local SCM CclDefineSpell(SCM list) } list=gh_cdr(list); } else if (gh_eq_p(value,gh_symbol2scm("action"))) { - spell->SpellAction=(SpellActionType*)malloc(sizeof(SpellActionType)); - CclParseSpellAction(gh_car(list),spell,spell->SpellAction); + spell->Action=(SpellActionType*)malloc(sizeof(SpellActionType)); + CclParseSpellAction(gh_car(list),spell,spell->Action); list=gh_cdr(list); } else if (gh_eq_p(value,gh_symbol2scm("condition"))) { spell->Conditions=(ConditionInfo*)malloc(sizeof(ConditionInfo)); @@ -574,6 +574,136 @@ global void SpellCclRegister(void) gh_new_procedureN("define-spell", CclDefineSpell); } +/* +** Save a spell action to a file. +** +** @param file File pointer to save to +** @param action Pointer to action to save. +*/ +local void SaveSpellCondition(CLFile *file,ConditionInfo* condition) +{ + char condstrings [3][10] = { + "true", /// CONDITION_TRUE + "false", /// CONDITION_FALSE + "only" /// CONDITION_ONLY + }; + + DebugCheck(!file); + DebugCheck(!condition); + + CLprintf(file,"'( "); + // + // First save data related to flags. + // NOTE: (int) is there to keep compilers happy. + // + CLprintf(file,"undead %s ",condstrings[(int)condition->Undead]); + CLprintf(file,"organic %s ",condstrings[(int)condition->Organic]); + CLprintf(file,"hero %s ",condstrings[(int)condition->Hero]); + CLprintf(file,"coward %s ",condstrings[(int)condition->Coward]); + CLprintf(file,"alliance %s ",condstrings[(int)condition->Alliance]); + CLprintf(file,"building %s ",condstrings[(int)condition->Building]); + CLprintf(file,"self %s ",condstrings[(int)condition->TargetSelf]); + // + // Min/Max vital percents + // + CLprintf(file,"min-hp-percent %d ",condition->MinHpPercent); + CLprintf(file,"max-hp-percent %d ",condition->MaxHpPercent); + CLprintf(file,"min-mana-percent %d ",condition->MinManaPercent); + CLprintf(file,"max-mana-percent %d ",condition->MaxManaPercent); + // + // Max buff ticks stuff + // + CLprintf(file,"max-slow-ticks %d ",condition->MaxSlowTicks); + CLprintf(file,"max-haste-ticks %d ",condition->MaxHasteTicks); + CLprintf(file,"max-bloodlust-ticks %d ",condition->MaxBloodlustTicks); + CLprintf(file,"max-invisibility-ticks %d ",condition->MaxInvisibilityTicks); + CLprintf(file,"max-invincibility-ticks %d ",condition->MaxInvincibilityTicks); + // + // The end. + // + CLprintf(file,")\n"); +} + +/* +** Save a spell action to a file. +** +** @param file File pointer to save to +** @param action Pointer to action to save. +*/ +local void SaveSpellAction(CLFile *file,SpellActionType* action) +{ + if (action->CastFunction==CastAreaBombardment) { + CLprintf(file," '(area-bombardment fields %d shards %d damage %d start-offset-x %d start-offset-y %d)", + action->Data.AreaBombardment.Fields, + action->Data.AreaBombardment.Shards, + action->Data.AreaBombardment.Damage, + action->Data.AreaBombardment.StartOffsetX, + action->Data.AreaBombardment.StartOffsetY); + } else if (action->CastFunction==CastFireball) { + CLprintf(file," '(fireball ttl %d damage %d)", + action->Data.Fireball.TTL, + action->Data.Fireball.Damage); + } else if (action->CastFunction==CastAdjustVitals) { + CLprintf(file," '(adjust-vitals"); + if (action->Data.AdjustVitals.HP) { + CLprintf(file," hit-points %d",action->Data.AdjustVitals.HP); + } + if (action->Data.AdjustVitals.Mana) { + CLprintf(file," mana-points %d",action->Data.AdjustVitals.Mana); + } + if (action->Data.AdjustVitals.MaxMultiCast) { + CLprintf(file," max-multi-cast %d",action->Data.AdjustVitals.MaxMultiCast); + } + CLprintf(file,")\n"); + } else if (action->CastFunction==CastSummon) { + CLprintf(file," '(summon unit-type %s time-to-live %d)", + action->Data.Summon.UnitType->Ident, + action->Data.Summon.TTL); + } else if (action->CastFunction==CastAdjustBuffs) { + CLprintf(file," '(adjust-buffs"); + if (action->Data.AdjustBuffs.HasteTicks!=BUFF_NOT_AFFECTED) { + CLprintf(file," haste-ticks %d",action->Data.AdjustBuffs.HasteTicks); + } + if (action->Data.AdjustBuffs.SlowTicks!=BUFF_NOT_AFFECTED) { + CLprintf(file," slow-ticks %d",action->Data.AdjustBuffs.SlowTicks); + } + if (action->Data.AdjustBuffs.BloodlustTicks!=BUFF_NOT_AFFECTED) { + CLprintf(file," bloodlust-ticks %d",action->Data.AdjustBuffs.BloodlustTicks); + } + if (action->Data.AdjustBuffs.InvisibilityTicks!=BUFF_NOT_AFFECTED) { + CLprintf(file," invisibility-ticks %d",action->Data.AdjustBuffs.InvisibilityTicks); + } + if (action->Data.AdjustBuffs.InvincibilityTicks!=BUFF_NOT_AFFECTED) { + CLprintf(file," invincibility-ticks %d",action->Data.AdjustBuffs.InvincibilityTicks); + } + CLprintf(file,")"); + } else if (action->CastFunction==CastPolymorph) { + CLprintf(file," '(polymorph new-form %s)", + action->Data.Polymorph.NewForm->Ident); + } else if (action->CastFunction==CastRaiseDead) { + CLprintf(file," '(raise-dead unit-raised %s time-to-live %d)", + action->Data.RaiseDead.UnitRaised->Ident, + action->Data.RaiseDead.TTL); + } else if (action->CastFunction==CastFlameShield) { + CLprintf(file," '(flame-shield duration %d)", + action->Data.FlameShield.TTL); + } else if (action->CastFunction==CastRunes) { + CLprintf(file," '(runes ttl %d damage %d)", + action->Data.Runes.TTL, + action->Data.Runes.Damage); + } else if (action->CastFunction==CastSpawnPortal) { + CLprintf(file," '(spawn-portal portal-type %s)", + action->Data.SpawnPortal.PortalType->Ident); + } else if (action->CastFunction==CastDeathCoil) { + CLprintf(file," '(death-coil)"); + // FIXME: more? + } else if (action->CastFunction==CastWhirlwind) { + CLprintf(file," '(whirlwind duration %d)", + action->Data.Whirlwind.TTL); + // FIXME: more? + } +} + /** ** Save spells to a CCL file. ** @@ -624,81 +754,17 @@ global void SaveSpells(CLFile *file) // // Save the action(effect of the spell) // - CLprintf(file," 'action"); - if (spell->CastFunction==CastAreaBombardment) { - CLprintf(file," '(area-bombardment fields %d shards %d damage %d start-offset-x %d start-offset-y %d)\n", - spell->SpellAction->AreaBombardment.Fields, - spell->SpellAction->AreaBombardment.Shards, - spell->SpellAction->AreaBombardment.Damage, - spell->SpellAction->AreaBombardment.StartOffsetX, - spell->SpellAction->AreaBombardment.StartOffsetY); - } else if (spell->CastFunction==CastFireball) { - CLprintf(file," '(fireball ttl %d damage %d)\n", - spell->SpellAction->Fireball.TTL, - spell->SpellAction->Fireball.Damage); - } else if (spell->CastFunction==CastAdjustVitals) { - CLprintf(file," '(adjust-vitals"); - if (spell->SpellAction->AdjustVitals.HP) { - CLprintf(file," hit-points %d",spell->SpellAction->AdjustVitals.HP); - } - if (spell->SpellAction->AdjustVitals.Mana) { - CLprintf(file," mana-points %d",spell->SpellAction->AdjustVitals.Mana); - } - if (spell->SpellAction->AdjustVitals.MaxMultiCast) { - CLprintf(file," max-multi-cast %d",spell->SpellAction->AdjustVitals.MaxMultiCast); - } - CLprintf(file,")\n"); - } else if (spell->CastFunction==CastSummon) { - CLprintf(file," '(summon unit-type %s time-to-live %d)\n", - spell->SpellAction->Summon.UnitType->Ident, - spell->SpellAction->Summon.TTL); - } else if (spell->CastFunction==CastAdjustBuffs) { - CLprintf(file," '(adjust-buffs"); - if (spell->SpellAction->AdjustBuffs.HasteTicks!=BUFF_NOT_AFFECTED) { - CLprintf(file," haste-ticks %d",spell->SpellAction->AdjustBuffs.HasteTicks); - } - if (spell->SpellAction->AdjustBuffs.SlowTicks!=BUFF_NOT_AFFECTED) { - CLprintf(file," slow-ticks %d",spell->SpellAction->AdjustBuffs.SlowTicks); - } - if (spell->SpellAction->AdjustBuffs.BloodlustTicks!=BUFF_NOT_AFFECTED) { - CLprintf(file," bloodlust-ticks %d",spell->SpellAction->AdjustBuffs.BloodlustTicks); - } - if (spell->SpellAction->AdjustBuffs.InvisibilityTicks!=BUFF_NOT_AFFECTED) { - CLprintf(file," invisibility-ticks %d",spell->SpellAction->AdjustBuffs.InvisibilityTicks); - } - if (spell->SpellAction->AdjustBuffs.InvincibilityTicks!=BUFF_NOT_AFFECTED) { - CLprintf(file," invincibility-ticks %d",spell->SpellAction->AdjustBuffs.InvincibilityTicks); - } - CLprintf(file,")\n"); - } else if (spell->CastFunction==CastPolymorph) { - CLprintf(file," '(polymorph new-form %s)\n", - spell->SpellAction->Polymorph.NewForm->Ident); - } else if (spell->CastFunction==CastRaiseDead) { - CLprintf(file," '(raise-dead unit-raised %s time-to-live %d)\n", - spell->SpellAction->RaiseDead.UnitRaised->Ident, - spell->SpellAction->RaiseDead.TTL); - } else if (spell->CastFunction==CastFlameShield) { - CLprintf(file," '(flame-shield duration %d)\n", - spell->SpellAction->FlameShield.TTL); - } else if (spell->CastFunction==CastRunes) { - CLprintf(file," '(runes ttl %d damage %d)\n", - spell->SpellAction->Runes.TTL, - spell->SpellAction->Runes.Damage); - } else if (spell->CastFunction==CastSpawnPortal) { - CLprintf(file," '(spawn-portal portal-type %s)\n", - spell->SpellAction->SpawnPortal.PortalType->Ident); - } else if (spell->CastFunction==CastDeathCoil) { - CLprintf(file," '(death-coil)\n"); - // FIXME: more? - } else if (spell->CastFunction==CastWhirlwind) { - CLprintf(file," '(whirlwind duration %d)\n", - spell->SpellAction->Whirlwind.TTL); - // FIXME: more? - } + CLprintf(file," 'action "); + SaveSpellAction(file,spell->Action); + CLprintf(file,"\n"); // // FIXME: Save conditions // - + if (spell->Conditions) { + CLprintf(file," 'condition "); + SaveSpellCondition(file,spell->Conditions); + CLprintf(file,"\n"); + } // // FIXME: Save autocast and AI info // diff --git a/src/stratagus/spells.cpp b/src/stratagus/spells.cpp index 1690a6934..7f2d8796a 100644 --- a/src/stratagus/spells.cpp +++ b/src/stratagus/spells.cpp @@ -451,10 +451,10 @@ global int CastSpawnPortal(Unit *caster, const SpellType *spell __attribute__((u DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); - DebugCheck(!spell->SpellAction->SpawnPortal.PortalType); + DebugCheck(!spell->Action); + DebugCheck(!spell->Action->Data.SpawnPortal.PortalType); - ptype = spell->SpellAction->SpawnPortal.PortalType; + ptype = spell->Action->Data.SpawnPortal.PortalType; portal = caster->Goal; if (portal) { @@ -505,16 +505,16 @@ global int CastAreaBombardment(Unit *caster, const SpellType *spell, DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); //assert(x in range, y in range); mis = NULL; - fields = spell->SpellAction->AreaBombardment.Fields; - shards = spell->SpellAction->AreaBombardment.Shards; - damage = spell->SpellAction->AreaBombardment.Damage; - offsetx = spell->SpellAction->AreaBombardment.StartOffsetX; - offsety = spell->SpellAction->AreaBombardment.StartOffsetY; + fields = spell->Action->Data.AreaBombardment.Fields; + shards = spell->Action->Data.AreaBombardment.Shards; + damage = spell->Action->Data.AreaBombardment.Damage; + offsetx = spell->Action->Data.AreaBombardment.StartOffsetX; + offsety = spell->Action->Data.AreaBombardment.StartOffsetY; while (fields--) { // FIXME : radius configurable... do { @@ -568,7 +568,7 @@ global int CastDeathCoil(Unit *caster, const SpellType *spell, Unit *target, DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); // assert(target); // assert(x in range, y in range); @@ -615,7 +615,7 @@ global int CastFireball(Unit *caster, const SpellType *spell, DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); DebugCheck(!spell->Missile); missile = NULL; @@ -634,8 +634,8 @@ global int CastFireball(Unit *caster, const SpellType *spell, caster->Mana -= spell->ManaCost; PlayGameSound(spell->SoundWhenCast.Sound, MaxSampleVolume); missile = MakeMissile(spell->Missile, sx, sy, x, y); - missile->State = spell->SpellAction->Fireball.TTL - (dist - 1) * 2; - missile->TTL = spell->SpellAction->Fireball.TTL; + missile->State = spell->Action->Data.Fireball.TTL - (dist - 1) * 2; + missile->TTL = spell->Action->Data.Fireball.TTL; missile->Controller = SpellFireballController; missile->SourceUnit = caster; RefsDebugCheck(!caster->Refs || caster->Destroyed); @@ -662,7 +662,7 @@ global int CastFlameShield(Unit* caster, const SpellType *spell, Unit *target, DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); DebugCheck(!target); // assert(x in range, y in range); DebugCheck(!spell->Missile); @@ -671,11 +671,11 @@ global int CastFlameShield(Unit* caster, const SpellType *spell, Unit *target, // get mana cost caster->Mana -= spell->ManaCost; - target->FlameShield = spell->SpellAction->FlameShield.TTL; + target->FlameShield = spell->Action->Data.FlameShield.TTL; PlayGameSound(spell->SoundWhenCast.Sound, MaxSampleVolume); for (i = 0; i < 5; i++) { mis = MakeMissile(spell->Missile, 0, 0, 0, 0); - mis->TTL = spell->SpellAction->FlameShield.TTL + i * 7; + mis->TTL = spell->Action->Data.FlameShield.TTL + i * 7; mis->TargetUnit = target; mis->Controller = SpellFlameShieldController; RefsDebugCheck(!target->Refs || target->Destroyed); @@ -700,26 +700,26 @@ global int CastAdjustBuffs(Unit *caster, const SpellType *spell, Unit *target, { DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); DebugCheck(!target); // get mana cost caster->Mana -= spell->ManaCost; - if (spell->SpellAction->AdjustBuffs.SlowTicks!=BUFF_NOT_AFFECTED) { - target->Haste=spell->SpellAction->AdjustBuffs.HasteTicks; + if (spell->Action->Data.AdjustBuffs.SlowTicks!=BUFF_NOT_AFFECTED) { + target->Haste=spell->Action->Data.AdjustBuffs.HasteTicks; } - if (spell->SpellAction->AdjustBuffs.SlowTicks!=BUFF_NOT_AFFECTED) { - target->Slow=spell->SpellAction->AdjustBuffs.SlowTicks; + if (spell->Action->Data.AdjustBuffs.SlowTicks!=BUFF_NOT_AFFECTED) { + target->Slow=spell->Action->Data.AdjustBuffs.SlowTicks; } - if (spell->SpellAction->AdjustBuffs.BloodlustTicks!=BUFF_NOT_AFFECTED) { - target->Bloodlust=spell->SpellAction->AdjustBuffs.BloodlustTicks; + if (spell->Action->Data.AdjustBuffs.BloodlustTicks!=BUFF_NOT_AFFECTED) { + target->Bloodlust=spell->Action->Data.AdjustBuffs.BloodlustTicks; } - if (spell->SpellAction->AdjustBuffs.InvisibilityTicks!=BUFF_NOT_AFFECTED) { - target->Invisible=spell->SpellAction->AdjustBuffs.InvisibilityTicks; + if (spell->Action->Data.AdjustBuffs.InvisibilityTicks!=BUFF_NOT_AFFECTED) { + target->Invisible=spell->Action->Data.AdjustBuffs.InvisibilityTicks; } - if (spell->SpellAction->AdjustBuffs.InvincibilityTicks!=BUFF_NOT_AFFECTED) { - target->UnholyArmor=spell->SpellAction->AdjustBuffs.InvincibilityTicks; + if (spell->Action->Data.AdjustBuffs.InvincibilityTicks!=BUFF_NOT_AFFECTED) { + target->UnholyArmor=spell->Action->Data.AdjustBuffs.InvincibilityTicks; } CheckUnitToBeDrawn(target); PlayGameSound(spell->SoundWhenCast.Sound,MaxSampleVolume); @@ -752,11 +752,11 @@ global int CastAdjustVitals(Unit *caster, const SpellType *spell, Unit *target, DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); DebugCheck(!target); - hp = spell->SpellAction->AdjustVitals.HP; - mana = spell->SpellAction->AdjustVitals.Mana; + hp = spell->Action->Data.AdjustVitals.HP; + mana = spell->Action->Data.AdjustVitals.Mana; manacost = spell->ManaCost; // Healing and harming @@ -784,8 +784,8 @@ global int CastAdjustVitals(Unit *caster, const SpellType *spell, Unit *target, if (manacost) { castcount=min(castcount,caster->Mana/manacost); } - if (spell->SpellAction->AdjustVitals.MaxMultiCast) { - castcount=min(castcount,spell->SpellAction->AdjustVitals.MaxMultiCast); + if (spell->Action->Data.AdjustVitals.MaxMultiCast) { + castcount=min(castcount,spell->Action->Data.AdjustVitals.MaxMultiCast); } DebugCheck(castcount<0); @@ -817,43 +817,6 @@ global int CastAdjustVitals(Unit *caster, const SpellType *spell, Unit *target, return 0; } -/** -** Cast holy vision. -** -** @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 CastHolyVision(Unit *caster, const SpellType *spell, Unit *target, - int x, int y) -{ - DebugCheck(!caster); - DebugCheck(!spell); - DebugCheck(!spell->SpellAction); -// assert(x in range, y in range); - - caster->Mana -= spell->ManaCost; // get mana cost - // FIXME: compact with summon. - target = MakeUnit(spell->SpellAction->HolyVision.revealer, caster->Player); - target->Orders[0].Action = UnitActionStill; - target->HP = 0; - target->X = x; - target->Y = y; -// target->TTL = GameCycle + CYCLES_PER_SECOND + CYCLES_PER_SECOND / 2; - target->CurrentSightRange = target->Stats->SightRange; - target->Removed = 1; - target->CurrentSightRange = target->Stats->SightRange; - MapMarkUnitSight(target); - target->TTL = GameCycle + target->Type->DecayRate * 6 * CYCLES_PER_SECOND; - CheckUnitToBeDrawn(target); - PlayGameSound(spell->SoundWhenCast.Sound, MaxSampleVolume); - return 0; -} - /** ** Cast polymorph. ** @@ -872,10 +835,10 @@ global int CastPolymorph(Unit *caster, const SpellType *spell, Unit *target, DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); DebugCheck(!target); - type = spell->SpellAction->Polymorph.NewForm; + type = spell->Action->Data.Polymorph.NewForm; DebugCheck(!type); caster->Player->Score += target->Type->Points; @@ -926,10 +889,10 @@ global int CastRaiseDead(Unit *caster, const SpellType *spell, Unit *target, DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); // assert(x in range, y in range); - skeleton = spell->SpellAction->RaiseDead.UnitRaised; + skeleton = spell->Action->Data.RaiseDead.UnitRaised; DebugCheck(!skeleton); corpses = &CorpseList; @@ -992,7 +955,7 @@ global int CastRunes(Unit *caster, const SpellType *spell, DebugCheck(!caster); DebugCheck(spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); // assert(x in range, y in range); mis = NULL; @@ -1010,7 +973,7 @@ global int CastRunes(Unit *caster, const SpellType *spell, y * TileSizeY + TileSizeY / 2, x * TileSizeX + TileSizeX / 2, y * TileSizeY + TileSizeY / 2); - mis->TTL = spell->SpellAction->Runes.TTL; + mis->TTL = spell->Action->Data.Runes.TTL; mis->Controller = SpellRunesController; caster->Mana -= spell->ManaCost / 5; } @@ -1036,14 +999,14 @@ global int CastSummon(Unit *caster, const SpellType *spell, Unit *target, DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); - DebugCheck(!spell->SpellAction->Summon.UnitType); + DebugCheck(!spell->Action); + DebugCheck(!spell->Action->Data.Summon.UnitType); DebugLevel0("Summoning\n"); - ttl=spell->SpellAction->Summon.TTL; + ttl=spell->Action->Data.Summon.TTL; caster->Mana -= spell->ManaCost; // FIXME: johns: the unit is placed on the wrong position - target = MakeUnit(spell->SpellAction->Summon.UnitType, caster->Player); + target = MakeUnit(spell->Action->Data.Summon.UnitType, caster->Player); target->X = x; target->Y = y; // set life span @@ -1090,7 +1053,7 @@ global int CastWhirlwind(Unit *caster, const SpellType *spell, DebugCheck(!caster); DebugCheck(!spell); - DebugCheck(!spell->SpellAction); + DebugCheck(!spell->Action); // assert(x in range, y in range); mis = NULL; @@ -1100,7 +1063,7 @@ global int CastWhirlwind(Unit *caster, const SpellType *spell, mis = MakeMissile(spell->Missile, x * TileSizeX + TileSizeX / 2, y * TileSizeY + TileSizeY / 2, x * TileSizeX + TileSizeX / 2, y * TileSizeY + TileSizeY / 2); - mis->TTL = spell->SpellAction->Whirlwind.TTL; + mis->TTL = spell->Action->Data.Whirlwind.TTL; mis->Controller = SpellWhirlwindController; return 0; } @@ -1208,7 +1171,6 @@ local int PassCondition(const Unit* caster,const SpellType* spell,const Unit* ta } if (condition->Building!=CONDITION_TRUE) { if ((condition->Building==CONDITION_ONLY)^(target->Type->Building)) { - DebugLevel0("QQQ\n"); return 0; } } @@ -1541,7 +1503,7 @@ global int SpellCast(Unit *caster, const SpellType *spell, Unit *target, int x, int y) { DebugCheck(!spell); - DebugCheck(!spell->CastFunction); + DebugCheck(!spell->Action->CastFunction); DebugCheck(!caster); DebugCheck(!SpellIsAvailable(caster->Player, spell->Ident)); @@ -1555,7 +1517,7 @@ global int SpellCast(Unit *caster, const SpellType *spell, Unit *target, } DebugLevel3Fn("Spell cast: (%s), %s -> %s (%d,%d)\n" _C_ spell->IdentName _C_ unit->Type->Name _C_ target ? target->Type->Name : "none" _C_ x _C_ y); - return CanCastSpell(caster, spell, target, x, y) && spell->CastFunction(caster, spell, target, x, y); + return CanCastSpell(caster, spell, target, x, y) && spell->Action->CastFunction(caster, spell, target, x, y); }