- 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:
Christoph Oelckers 2016-11-27 00:18:07 +01:00
parent 796c262285
commit 69d4d36429
9 changed files with 151 additions and 120 deletions

View file

@ -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;
}

View file

@ -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"

View file

@ -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

View file

@ -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)

View file

@ -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;
}
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,7 +8579,17 @@ FxExpression *FxCaseStatement::Resolve(FCompileContext &ctx)
delete this;
return nullptr;
}
CaseValue = static_cast<FxConstant *>(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<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)
{
ctx.Loop->Jumps.Push(this);
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

View file

@ -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&);

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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;