/* ** g_skill.cpp ** Skill level handling ** **--------------------------------------------------------------------------- ** Copyright 2008-2009 Christoph Oelckers ** Copyright 2008-2009 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include #include "doomstat.h" #include "d_player.h" #include "g_level.h" #include "g_game.h" #include "gi.h" #include "templates.h" #include "v_font.h" #include "m_fixed.h" #include "gstrings.h" #include "g_levellocals.h" #include "vm.h" TArray AllSkills; int DefaultSkill = -1; //========================================================================== // // ParseSkill // //========================================================================== void FMapInfoParser::ParseSkill () { FSkillInfo skill; bool thisisdefault = false; bool acsreturnisset = false; skill.NoMenu = false; skill.AmmoFactor = 1.; skill.DoubleAmmoFactor = 2.; skill.DropAmmoFactor = -1.; skill.DamageFactor = 1.; skill.ArmorFactor = 1.; skill.HealthFactor = 1.; skill.KickbackFactor = 1.; skill.FastMonsters = false; skill.SlowMonsters = false; skill.DisableCheats = false; skill.EasyBossBrain = false; skill.EasyKey = false; skill.AutoUseHealth = false; skill.RespawnCounter = 0; skill.RespawnLimit = 0; skill.Aggressiveness = 1.; skill.SpawnFilter = 0; skill.ACSReturn = 0; skill.MustConfirm = false; skill.Shortcut = 0; skill.TextColor = ""; skill.Replace.Clear(); skill.Replaced.Clear(); skill.MonsterHealth = 1.; skill.FriendlyHealth = 1.; skill.NoPain = false; skill.Infighting = 0; sc.MustGetString(); skill.Name = sc.String; ParseOpenBrace(); while (sc.GetString ()) { if (sc.Compare ("ammofactor")) { ParseAssign(); sc.MustGetFloat (); skill.AmmoFactor = sc.Float; } else if (sc.Compare ("doubleammofactor")) { ParseAssign(); sc.MustGetFloat (); skill.DoubleAmmoFactor = sc.Float; } else if (sc.Compare ("dropammofactor")) { ParseAssign(); sc.MustGetFloat (); skill.DropAmmoFactor = sc.Float; } else if (sc.Compare ("damagefactor")) { ParseAssign(); sc.MustGetFloat (); skill.DamageFactor = sc.Float; } else if (sc.Compare("kickbackfactor")) { ParseAssign(); sc.MustGetFloat(); skill.KickbackFactor = sc.Float; } else if (sc.Compare ("fastmonsters")) { skill.FastMonsters = true; } else if (sc.Compare ("slowmonsters")) { skill.SlowMonsters = true; } else if (sc.Compare ("disablecheats")) { skill.DisableCheats = true; } else if (sc.Compare ("easybossbrain")) { skill.EasyBossBrain = true; } else if (sc.Compare ("easykey")) { skill.EasyKey = true; } else if (sc.Compare("autousehealth")) { skill.AutoUseHealth = true; } else if (sc.Compare("nomenu")) { skill.NoMenu = true; } else if (sc.Compare("respawntime")) { ParseAssign(); sc.MustGetFloat (); skill.RespawnCounter = int(sc.Float*TICRATE); } else if (sc.Compare("respawnlimit")) { ParseAssign(); sc.MustGetNumber (); skill.RespawnLimit = sc.Number; } else if (sc.Compare("Aggressiveness")) { ParseAssign(); sc.MustGetFloat (); skill.Aggressiveness = 1. - clamp(sc.Float, 0.,1.); } else if (sc.Compare("SpawnFilter")) { ParseAssign(); if (sc.CheckNumber()) { if (sc.Number > 0) skill.SpawnFilter |= (1<<(sc.Number-1)); } else { sc.MustGetString (); if (sc.Compare("baby")) skill.SpawnFilter |= 1; else if (sc.Compare("easy")) skill.SpawnFilter |= 2; else if (sc.Compare("normal")) skill.SpawnFilter |= 4; else if (sc.Compare("hard")) skill.SpawnFilter |= 8; else if (sc.Compare("nightmare")) skill.SpawnFilter |= 16; } } else if (sc.Compare("ACSReturn")) { ParseAssign(); sc.MustGetNumber (); skill.ACSReturn = sc.Number; acsreturnisset = true; } else if (sc.Compare("ReplaceActor")) { ParseAssign(); sc.MustGetString(); FName replaced = sc.String; ParseComma(); sc.MustGetString(); FName replacer = sc.String; skill.SetReplacement(replaced, replacer); skill.SetReplacedBy(replacer, replaced); } else if (sc.Compare("Name")) { ParseAssign(); sc.MustGetString (); skill.MenuName = sc.String; } else if (sc.Compare("PlayerClassName")) { ParseAssign(); sc.MustGetString (); FName pc = sc.String; ParseComma(); sc.MustGetString (); skill.MenuNamesForPlayerClass[pc]=sc.String; } else if (sc.Compare("PicName")) { ParseAssign(); sc.MustGetString (); skill.PicName = sc.String; } else if (sc.Compare("MustConfirm")) { skill.MustConfirm = true; if (format_type == FMT_New) { if (CheckAssign()) { sc.MustGetString(); skill.MustConfirmText = sc.String; } } else { if (sc.CheckToken(TK_StringConst)) { skill.MustConfirmText = sc.String; } } } else if (sc.Compare("Key")) { ParseAssign(); sc.MustGetString(); skill.Shortcut = tolower(sc.String[0]); } else if (sc.Compare("TextColor")) { ParseAssign(); sc.MustGetString(); skill.TextColor.Format("[%s]", sc.String); } else if (sc.Compare("MonsterHealth")) { ParseAssign(); sc.MustGetFloat(); skill.MonsterHealth = sc.Float; } else if (sc.Compare("FriendlyHealth")) { ParseAssign(); sc.MustGetFloat(); skill.FriendlyHealth = sc.Float; } else if (sc.Compare("NoPain")) { skill.NoPain = true; } else if (sc.Compare("ArmorFactor")) { ParseAssign(); sc.MustGetFloat(); skill.ArmorFactor = sc.Float; } else if (sc.Compare("HealthFactor")) { ParseAssign(); sc.MustGetFloat(); skill.HealthFactor = sc.Float; } else if (sc.Compare("NoInfighting")) { skill.Infighting = LEVEL2_NOINFIGHTING; } else if (sc.Compare("TotalInfighting")) { skill.Infighting = LEVEL2_TOTALINFIGHTING; } else if (sc.Compare("DefaultSkill")) { if (DefaultSkill >= 0) { sc.ScriptError("%s is already the default skill\n", AllSkills[DefaultSkill].Name.GetChars()); } thisisdefault = true; } else if (!ParseCloseBrace()) { // Unknown sc.ScriptMessage("Unknown property '%s' found in skill definition\n", sc.String); SkipToNext(); } else { break; } } CheckEndOfFile("skill"); for(unsigned int i = 0; i < AllSkills.Size(); i++) { if (AllSkills[i].Name == skill.Name) { if (!acsreturnisset) { // Use the ACS return for the skill we are overwriting. skill.ACSReturn = AllSkills[i].ACSReturn; } AllSkills[i] = skill; if (thisisdefault) { DefaultSkill = i; } return; } } if (!acsreturnisset) { skill.ACSReturn = AllSkills.Size(); } if (thisisdefault) { DefaultSkill = AllSkills.Size(); } AllSkills.Push(skill); } //========================================================================== // // // //========================================================================== int G_SkillProperty(ESkillProperty prop) { if (AllSkills.Size() > 0) { switch(prop) { case SKILLP_FastMonsters: return AllSkills[gameskill].FastMonsters || (dmflags & DF_FAST_MONSTERS); case SKILLP_SlowMonsters: return AllSkills[gameskill].SlowMonsters; case SKILLP_Respawn: if (dmflags & DF_MONSTERS_RESPAWN && AllSkills[gameskill].RespawnCounter==0) return TICRATE * gameinfo.defaultrespawntime; return AllSkills[gameskill].RespawnCounter; case SKILLP_RespawnLimit: return AllSkills[gameskill].RespawnLimit; case SKILLP_DisableCheats: return AllSkills[gameskill].DisableCheats; case SKILLP_AutoUseHealth: return AllSkills[gameskill].AutoUseHealth; case SKILLP_EasyBossBrain: return AllSkills[gameskill].EasyBossBrain; case SKILLP_EasyKey: return AllSkills[gameskill].EasyKey; case SKILLP_SpawnFilter: return AllSkills[gameskill].SpawnFilter; case SKILLP_ACSReturn: return AllSkills[gameskill].ACSReturn; case SKILLP_NoPain: return AllSkills[gameskill].NoPain; case SKILLP_Infight: // This property also needs to consider the level flags for the same info. if (level.flags2 & LEVEL2_TOTALINFIGHTING) return 1; if (level.flags2 & LEVEL2_NOINFIGHTING) return -1; if (AllSkills[gameskill].Infighting == LEVEL2_TOTALINFIGHTING) return 1; if (AllSkills[gameskill].Infighting == LEVEL2_NOINFIGHTING) return -1; return infighting; } } return 0; } DEFINE_ACTION_FUNCTION(DObject, G_SkillPropertyInt) { PARAM_PROLOGUE; PARAM_INT(which); ACTION_RETURN_INT(G_SkillProperty((ESkillProperty)which)); } //========================================================================== // // // //========================================================================== double G_SkillProperty(EFSkillProperty prop) { if (AllSkills.Size() > 0) { switch (prop) { case SKILLP_AmmoFactor: if (dmflags2 & DF2_YES_DOUBLEAMMO) { return AllSkills[gameskill].DoubleAmmoFactor; } return AllSkills[gameskill].AmmoFactor; case SKILLP_DropAmmoFactor: return AllSkills[gameskill].DropAmmoFactor; case SKILLP_ArmorFactor: return AllSkills[gameskill].ArmorFactor; case SKILLP_HealthFactor: return AllSkills[gameskill].HealthFactor; case SKILLP_DamageFactor: return AllSkills[gameskill].DamageFactor; case SKILLP_Aggressiveness: return AllSkills[gameskill].Aggressiveness; case SKILLP_MonsterHealth: return AllSkills[gameskill].MonsterHealth; case SKILLP_FriendlyHealth: return AllSkills[gameskill].FriendlyHealth; case SKILLP_KickbackFactor: return AllSkills[gameskill].KickbackFactor; } } return 0; } DEFINE_ACTION_FUNCTION(DObject, G_SkillPropertyFloat) { PARAM_PROLOGUE; PARAM_INT(which); ACTION_RETURN_FLOAT(G_SkillProperty((EFSkillProperty)which)); } //========================================================================== // // // //========================================================================== const char * G_SkillName() { const char *name = AllSkills[gameskill].MenuName; player_t *player = &players[consoleplayer]; const char *playerclass = player->mo->GetInfo()->DisplayName; if (playerclass != NULL) { FString * pmnm = AllSkills[gameskill].MenuNamesForPlayerClass.CheckKey(playerclass); if (pmnm != NULL) name = *pmnm; } if (*name == '$') name = GStrings(name+1); return name; } DEFINE_ACTION_FUNCTION(DObject, G_SkillName) { PARAM_PROLOGUE; ACTION_RETURN_STRING(G_SkillName()); } //========================================================================== // // // //========================================================================== void G_VerifySkill() { if (gameskill >= (int)AllSkills.Size()) gameskill = AllSkills.Size()-1; else if (gameskill < 0) gameskill = 0; } //========================================================================== // // // //========================================================================== FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other) { Name = other.Name; AmmoFactor = other.AmmoFactor; NoMenu = other.NoMenu; DoubleAmmoFactor = other.DoubleAmmoFactor; DropAmmoFactor = other.DropAmmoFactor; DamageFactor = other.DamageFactor; KickbackFactor = other.KickbackFactor; FastMonsters = other.FastMonsters; SlowMonsters = other.SlowMonsters; DisableCheats = other.DisableCheats; AutoUseHealth = other.AutoUseHealth; EasyBossBrain = other.EasyBossBrain; EasyKey = other.EasyKey; RespawnCounter= other.RespawnCounter; RespawnLimit= other.RespawnLimit; Aggressiveness= other.Aggressiveness; SpawnFilter = other.SpawnFilter; ACSReturn = other.ACSReturn; MenuName = other.MenuName; PicName = other.PicName; MenuNamesForPlayerClass = other.MenuNamesForPlayerClass; MustConfirm = other.MustConfirm; MustConfirmText = other.MustConfirmText; Shortcut = other.Shortcut; TextColor = other.TextColor; Replace = other.Replace; Replaced = other.Replaced; MonsterHealth = other.MonsterHealth; FriendlyHealth = other.FriendlyHealth; NoPain = other.NoPain; Infighting = other.Infighting; ArmorFactor = other.ArmorFactor; HealthFactor = other.HealthFactor; return *this; } //========================================================================== // // // //========================================================================== int FSkillInfo::GetTextColor() const { if (TextColor.IsEmpty()) { return CR_UNTRANSLATED; } const uint8_t *cp = (const uint8_t *)TextColor.GetChars(); int color = V_ParseFontColor(cp, 0, 0); if (color == CR_UNDEFINED) { Printf("Undefined color '%s' in definition of skill %s\n", TextColor.GetChars(), Name.GetChars()); color = CR_UNTRANSLATED; } return color; } //========================================================================== // // FSkillInfo::SetReplacement // //========================================================================== void FSkillInfo::SetReplacement(FName a, FName b) { Replace[a] = b; } //========================================================================== // // FSkillInfo::GetReplacement // //========================================================================== FName FSkillInfo::GetReplacement(FName a) { if (Replace.CheckKey(a)) return Replace[a]; else return NAME_None; } //========================================================================== // // FSkillInfo::SetReplaced // //========================================================================== void FSkillInfo::SetReplacedBy(FName b, FName a) { Replaced[b] = a; } //========================================================================== // // FSkillInfo::GetReplaced // //========================================================================== FName FSkillInfo::GetReplacedBy(FName b) { if (Replaced.CheckKey(b)) return Replaced[b]; else return NAME_None; }