diff --git a/src/g_hexen/a_healingradius.cpp b/src/g_hexen/a_healingradius.cpp deleted file mode 100644 index bf18a7e0e..000000000 --- a/src/g_hexen/a_healingradius.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* -#include "info.h" -#include "a_pickups.h" -#include "a_artifacts.h" -#include "gstrings.h" -#include "p_local.h" -#include "s_sound.h" -#include "m_random.h" -#include "a_action.h" -#include "a_hexenglobal.h" -#include "gi.h" -#include "doomstat.h" -*/ - -#define HEAL_RADIUS_DIST 255. - -static FRandom pr_healradius ("HealRadius"); - -// Healing Radius Artifact -------------------------------------------------- - -class AArtiHealingRadius : public AInventory -{ - DECLARE_CLASS (AArtiHealingRadius, AInventory) -public: - bool Use (bool pickup); -}; - -IMPLEMENT_CLASS(AArtiHealingRadius, false, false) - -bool AArtiHealingRadius::Use (bool pickup) -{ - bool effective = false; - FName mode; - - if (Owner->IsKindOf(RUNTIME_CLASS(APlayerPawn))) - { - mode = static_cast(Owner->GetClass())->HealingRadiusType; - } - - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i] && - players[i].mo != NULL && - players[i].mo->health > 0 && - players[i].mo->Distance2D (Owner) <= HEAL_RADIUS_DIST) - { - // Q: Is it worth it to make this selectable as a player property? - // A: Probably not - but it sure doesn't hurt. - bool gotsome=false; - switch (mode) - { - case NAME_Armor: - for (int j = 0; j < 4; ++j) - { - AHexenArmor *armor = Spawn (); - armor->health = j; - armor->Amount = 1; - if (!armor->CallTryPickup (players[i].mo)) - { - armor->Destroy (); - } - else - { - gotsome = true; - } - } - break; - - case NAME_Mana: - { - int amount = 50 + (pr_healradius() % 50); - - if (players[i].mo->GiveAmmo (dyn_cast(PClass::FindClass(NAME_Mana1)), amount) || - players[i].mo->GiveAmmo (dyn_cast(PClass::FindClass(NAME_Mana2)), amount)) - { - gotsome = true; - } - break; - } - - default: - //case NAME_Health: - gotsome = P_GiveBody (players[i].mo, 50 + (pr_healradius()%50)); - break; - } - if (gotsome) - { - S_Sound (players[i].mo, CHAN_AUTO, "MysticIncant", 1, ATTN_NORM); - effective=true; - } - } - } - return effective; - -} diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index e00b72583..4fc0d4590 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -26,7 +26,6 @@ // Include all the Hexen stuff here to reduce compile time #include "a_flechette.cpp" #include "a_flies.cpp" -#include "a_healingradius.cpp" #include "a_heresiarch.cpp" #include "a_hexenspecialdecs.cpp" #include "a_iceguy.cpp" diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 7e9b8a96b..5abc02e96 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1093,7 +1093,6 @@ DEFINE_ACTION_FUNCTION(AActor, GiveInventoryType) ACTION_RETURN_OBJECT(self->GiveInventoryType(type)); } - //============================================================================ // // AActor :: GiveAmmo @@ -1122,6 +1121,14 @@ bool AActor::GiveAmmo (PClassAmmo *type, int amount) return false; } +DEFINE_ACTION_FUNCTION(AActor, GiveAmmo) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_CLASS(type, AAmmo); + PARAM_INT(amount); + ACTION_RETURN_BOOL(self->GiveAmmo(type, amount)); +} + //============================================================================ // // AActor :: ClearInventory diff --git a/src/p_user.cpp b/src/p_user.cpp index eadbda10e..ce78d04a8 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3175,6 +3175,19 @@ DEFINE_FIELD(APlayerPawn, FlechetteType) DEFINE_FIELD(APlayerPawn, DamageFade) DEFINE_FIELD(APlayerPawn, ViewBob) +DEFINE_FIELD(PClassPlayerPawn, HealingRadiusType) +DEFINE_FIELD(PClassPlayerPawn, DisplayName) +DEFINE_FIELD(PClassPlayerPawn, SoundClass) +DEFINE_FIELD(PClassPlayerPawn, Face) +DEFINE_FIELD(PClassPlayerPawn, Portrait) +DEFINE_FIELD(PClassPlayerPawn, Slot) +DEFINE_FIELD(PClassPlayerPawn, InvulMode) +DEFINE_FIELD(PClassPlayerPawn, HexenArmor) +DEFINE_FIELD(PClassPlayerPawn, ColorRangeStart) +DEFINE_FIELD(PClassPlayerPawn, ColorRangeEnd) +DEFINE_FIELD(PClassPlayerPawn, ColorSets) +DEFINE_FIELD(PClassPlayerPawn, PainFlashes) + DEFINE_FIELD_X(PlayerInfo, player_t, mo) DEFINE_FIELD_X(PlayerInfo, player_t, playerstate) DEFINE_FIELD_X(PlayerInfo, player_t, original_oldbuttons) diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 4562f3ef7..0fb6b172f 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -8338,7 +8338,7 @@ bool FxCompoundStatement::CheckLocalVariable(FName name) FxSwitchStatement::FxSwitchStatement(FxExpression *cond, FArgumentList &content, const FScriptPosition &pos) : FxExpression(EFX_SwitchStatement, pos) { - Condition = new FxIntCast(cond, false); + Condition = cond; Content = std::move(content); } @@ -8352,6 +8352,12 @@ FxExpression *FxSwitchStatement::Resolve(FCompileContext &ctx) CHECKRESOLVED(); SAFE_RESOLVE(Condition, ctx); + if (Condition->ValueType != TypeName) + { + Condition = new FxIntCast(Condition, false); + SAFE_RESOLVE(Condition, ctx); + } + if (Content.Size() == 0) { ScriptPosition.Message(MSG_WARNING, "Empty switch statement"); @@ -8370,15 +8376,15 @@ FxExpression *FxSwitchStatement::Resolve(FCompileContext &ctx) } } + auto outerctrl = ctx.ControlStmt; + ctx.ControlStmt = this; + for (auto &line : Content) { - // Do not resolve breaks, they need special treatment inside switch blocks. - if (line->ExprType != EFX_JumpStatement || static_cast(line)->Token != TK_Break) - { - SAFE_RESOLVE(line, ctx); - line->NeedResult = false; - } + SAFE_RESOLVE(line, ctx); + line->NeedResult = false; } + ctx.ControlStmt = outerctrl; if (Condition->isConstant()) { @@ -8398,6 +8404,12 @@ FxExpression *FxSwitchStatement::Resolve(FCompileContext &ctx) auto casestmt = static_cast(content[i]); if (casestmt->Condition == nullptr) defaultindex = i; else if (casestmt->CaseValue == static_cast(Condition)->GetValue().GetInt()) caseindex = i; + if (casestmt->Condition && casestmt->Condition->ValueType != Condition->ValueType) + { + casestmt->Condition->ScriptPosition.Message(MSG_ERROR, "Type mismatch in case statement"); + delete this; + return nullptr; + } } if (content[i]->ExprType == EFX_JumpStatement && static_cast(content[i])->Token == TK_Break) { @@ -8478,7 +8490,6 @@ ExpEmit FxSwitchStatement::Emit(VMFunctionBuilder *build) ca.jumpaddress = build->Emit(OP_JMP, 0); } size_t DefaultAddress = build->Emit(OP_JMP, 0); - TArray BreakAddresses; bool defaultset = false; for (auto line : Content) @@ -8504,22 +8515,14 @@ ExpEmit FxSwitchStatement::Emit(VMFunctionBuilder *build) } break; - case EFX_JumpStatement: - if (static_cast(line)->Token == TK_Break) - { - BreakAddresses.Push(build->Emit(OP_JMP, 0)); - break; - } - // fall through for continue. - default: line->Emit(build); break; } } - for (auto addr : BreakAddresses) + for (auto addr : Breaks) { - build->BackpatchToHere(addr); + build->BackpatchToHere(addr->Address); } if (!defaultset) build->BackpatchToHere(DefaultAddress); Content.DeleteAndClear(); @@ -8555,7 +8558,7 @@ bool FxSwitchStatement::CheckReturn() FxCaseStatement::FxCaseStatement(FxExpression *cond, const FScriptPosition &pos) : FxExpression(EFX_CaseStatement, pos) { - Condition = cond? new FxIntCast(cond, false) : nullptr; + Condition = cond; } FxCaseStatement::~FxCaseStatement() @@ -8576,7 +8579,17 @@ FxExpression *FxCaseStatement::Resolve(FCompileContext &ctx) delete this; return nullptr; } - CaseValue = static_cast(Condition)->GetValue().GetInt(); + // Case labels can be ints or names. + if (Condition->ValueType != TypeName) + { + Condition = new FxIntCast(Condition, false); + SAFE_RESOLVE(Condition, ctx); + CaseValue = static_cast(Condition)->GetValue().GetInt(); + } + else + { + CaseValue = static_cast(Condition)->GetValue().GetName(); + } } return this; } @@ -8747,10 +8760,13 @@ bool FxIfStatement::CheckReturn() FxExpression *FxLoopStatement::Resolve(FCompileContext &ctx) { + auto outerctrl = ctx.ControlStmt; auto outer = ctx.Loop; + ctx.ControlStmt = this; ctx.Loop = this; auto x = DoResolve(ctx); ctx.Loop = outer; + ctx.ControlStmt = outerctrl; return x; } @@ -9074,9 +9090,17 @@ FxExpression *FxJumpStatement::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); - if (ctx.Loop != nullptr) + if (ctx.ControlStmt != nullptr) { - ctx.Loop->Jumps.Push(this); + if (ctx.ControlStmt == ctx.Loop || Token == TK_Continue) + { + ctx.Loop->Jumps.Push(this); + } + else + { + // break in switch. + static_cast(ctx.ControlStmt)->Breaks.Push(this); + } return this; } else diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index 1110cd6b6..ae0581e05 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -72,6 +72,7 @@ typedef TDeletingArray FArgumentList; struct FCompileContext { + FxExpression *ControlStmt = nullptr; FxLoopStatement *Loop = nullptr; FxCompoundStatement *Block = nullptr; PPrototype *ReturnProto; @@ -1656,6 +1657,8 @@ class FxSwitchStatement : public FxExpression TArray CaseAddresses; public: + TArray Breaks; + FxSwitchStatement(FxExpression *cond, FArgumentList &content, const FScriptPosition &pos); ~FxSwitchStatement(); FxExpression *Resolve(FCompileContext&); diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 0adc0f1eb..cefcd6511 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -357,6 +357,8 @@ class Actor : Thinker native native bool CheckClass(class checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); native Inventory FindInventory(class itemtype, bool subclass = false); native Inventory GiveInventoryType(class itemtype); + native bool GiveAmmo (Class type, int amount); + native int CountInv(class itemtype, int ptr_select = AAPTR_DEFAULT); native double GetDistance(bool checkz, int ptr = AAPTR_TARGET); native double GetAngle(int flags, int ptr = AAPTR_DEFAULT); diff --git a/wadsrc/static/zscript/hexen/healingradius.txt b/wadsrc/static/zscript/hexen/healingradius.txt index 94d8e2dd5..52b87c15b 100644 --- a/wadsrc/static/zscript/hexen/healingradius.txt +++ b/wadsrc/static/zscript/hexen/healingradius.txt @@ -1,8 +1,10 @@ // Healing Radius Artifact -------------------------------------------------- -class ArtiHealingRadius : Inventory native +class ArtiHealingRadius : Inventory { + const HEAL_RADIUS_DIST = 255.; + Default { +COUNTITEM @@ -21,6 +23,70 @@ class ArtiHealingRadius : Inventory native Spawn: HRAD ABCDEFGHIJKLMNOP 4 Bright; Loop; + } + + override bool Use (bool pickup) + { + bool effective = false; + Name mode = 'Health'; + + PlayerPawn pp = PlayerPawn(Owner); + if (pp) mode = pp.HealingRadiusType; + + for (int i = 0; i < MAXPLAYERS; ++i) + { + PlayerPawn mo = players[i].mo; + if (playeringame[i] && mo != null && mo.health > 0 && mo.Distance2D (Owner) <= HEAL_RADIUS_DIST) + { + // Q: Is it worth it to make this selectable as a player property? + // A: Probably not - but it sure doesn't hurt. + bool gotsome=false; + switch (mode) + { + case 'Armor': + for (int j = 0; j < 4; ++j) + { + HexenArmor armor = HexenArmor(Spawn("HexenArmor")); + armor.health = j; + armor.Amount = 1; + if (!armor.CallTryPickup (mo)) + { + armor.Destroy (); + } + else + { + gotsome = true; + } + } + break; + + case 'Mana': + { + int amount = 50 + (random[HealRadius]() % 50); + + if (mo.GiveAmmo ("Mana1", amount) || + mo.GiveAmmo ("Mana2", amount)) + { + gotsome = true; + } + break; + } + + default: + //case NAME_Health: + gotsome = mo.GiveBody (50 + (random[HealRadius]() % 50)); + break; + } + if (gotsome) + { + mo.A_PlaySound ("MysticIncant", CHAN_AUTO); + effective=true; + } + } + } + return effective; + } + } diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 48d62688b..2d5be6278 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -8,6 +8,18 @@ class PlayerPawn : Actor native native int PlayerFlags; native Inventory InvFirst; // first inventory item displayed on inventory bar native Inventory InvSel; // selected inventory item + native meta String DisplayName; // Display name (used in menus, etc.) + native meta String SoundClass; // Sound class + native meta String Face; // Doom status bar face (when used) + native meta String Portrait; + native meta String Slot[10]; + native meta Name InvulMode; + native meta Name HealingRadiusType; + native meta double HexenArmor[5]; + native meta uint8 ColorRangeStart; // Skin color range + native meta uint8 ColorRangeEnd; + //FPlayerColorSetMap ColorSets; + //PainFlashList PainFlashes; // [GRB] Player class properties native double JumpZ;