mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-28 06:53:58 +00:00
- scriptified ArtiHealingRadius.
- allow switch/case with names. - fixed break jump target handling for switch/case. This only worked when the break was in the outermost compound statement, those in inner ones were missed.
This commit is contained in:
parent
796c262285
commit
69d4d36429
9 changed files with 151 additions and 120 deletions
|
@ -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<PClassPlayerPawn *>(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<AHexenArmor> ();
|
||||
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<PClassAmmo>(PClass::FindClass(NAME_Mana1)), amount) ||
|
||||
players[i].mo->GiveAmmo (dyn_cast<PClassAmmo>(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;
|
||||
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<FxJumpStatement *>(line)->Token != TK_Break)
|
||||
{
|
||||
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<FxCaseStatement *>(content[i]);
|
||||
if (casestmt->Condition == nullptr) defaultindex = i;
|
||||
else if (casestmt->CaseValue == static_cast<FxConstant *>(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<FxJumpStatement *>(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<size_t> BreakAddresses;
|
||||
bool defaultset = false;
|
||||
|
||||
for (auto line : Content)
|
||||
|
@ -8504,22 +8515,14 @@ ExpEmit FxSwitchStatement::Emit(VMFunctionBuilder *build)
|
|||
}
|
||||
break;
|
||||
|
||||
case EFX_JumpStatement:
|
||||
if (static_cast<FxJumpStatement *>(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,8 +8579,18 @@ FxExpression *FxCaseStatement::Resolve(FCompileContext &ctx)
|
|||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
// Case labels can be ints or names.
|
||||
if (Condition->ValueType != TypeName)
|
||||
{
|
||||
Condition = new FxIntCast(Condition, false);
|
||||
SAFE_RESOLVE(Condition, ctx);
|
||||
CaseValue = static_cast<FxConstant *>(Condition)->GetValue().GetInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
CaseValue = static_cast<FxConstant *>(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)
|
||||
{
|
||||
if (ctx.ControlStmt == ctx.Loop || Token == TK_Continue)
|
||||
{
|
||||
ctx.Loop->Jumps.Push(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// break in switch.
|
||||
static_cast<FxSwitchStatement*>(ctx.ControlStmt)->Breaks.Push(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -72,6 +72,7 @@ typedef TDeletingArray<FxExpression*> FArgumentList;
|
|||
|
||||
struct FCompileContext
|
||||
{
|
||||
FxExpression *ControlStmt = nullptr;
|
||||
FxLoopStatement *Loop = nullptr;
|
||||
FxCompoundStatement *Block = nullptr;
|
||||
PPrototype *ReturnProto;
|
||||
|
@ -1656,6 +1657,8 @@ class FxSwitchStatement : public FxExpression
|
|||
TArray<CaseAddr> CaseAddresses;
|
||||
|
||||
public:
|
||||
TArray<FxJumpStatement *> Breaks;
|
||||
|
||||
FxSwitchStatement(FxExpression *cond, FArgumentList &content, const FScriptPosition &pos);
|
||||
~FxSwitchStatement();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
|
|
|
@ -357,6 +357,8 @@ class Actor : Thinker native
|
|||
native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false);
|
||||
native Inventory FindInventory(class<Inventory> itemtype, bool subclass = false);
|
||||
native Inventory GiveInventoryType(class<Inventory> itemtype);
|
||||
native bool GiveAmmo (Class<Ammo> type, int amount);
|
||||
|
||||
native int CountInv(class<Inventory> itemtype, int ptr_select = AAPTR_DEFAULT);
|
||||
native double GetDistance(bool checkz, int ptr = AAPTR_TARGET);
|
||||
native double GetAngle(int flags, int ptr = AAPTR_DEFAULT);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
|
||||
// Healing Radius Artifact --------------------------------------------------
|
||||
|
||||
class ArtiHealingRadius : Inventory native
|
||||
class ArtiHealingRadius : Inventory
|
||||
{
|
||||
const HEAL_RADIUS_DIST = 255.;
|
||||
|
||||
Default
|
||||
{
|
||||
+COUNTITEM
|
||||
|
@ -22,5 +24,69 @@ class ArtiHealingRadius : Inventory native
|
|||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue