mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-23 21:07:17 +00:00
implemented curses
This commit is contained in:
parent
46683b06fe
commit
61891c4eb8
@ -90,6 +90,8 @@ EX bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags)
|
||||
|
||||
if(m2 == moPlayer && peace::on) return false;
|
||||
|
||||
if((flags & AF_WEAK) && isIvy(c2)) return false;
|
||||
|
||||
if((flags & AF_MUSTKILL) && attackJustStuns(c2, flags, m1))
|
||||
return false;
|
||||
|
||||
@ -353,6 +355,7 @@ EX void stunMonster(cell *c2, eMonster killer, flagtype flags) {
|
||||
c2->monst == moFlailer ? 1 :
|
||||
c2->monst == moSalamander ? 6 :
|
||||
c2->monst == moBrownBug ? 3 :
|
||||
((flags & AF_WEAK) && !attackJustStuns(c2, flags &~ AF_WEAK, killer)) ? min(5+c2->stuntime, 15) :
|
||||
3);
|
||||
if(killer == moArrowTrap) newtime = min(newtime + 3, 7);
|
||||
if(!isMetalBeast(c2->monst) && !among(c2->monst, moSkeleton, moReptile, moSalamander, moTortoise, moWorldTurtle, moBrownBug)) {
|
||||
@ -366,6 +369,8 @@ EX void stunMonster(cell *c2, eMonster killer, flagtype flags) {
|
||||
}
|
||||
|
||||
EX bool attackJustStuns(cell *c2, flagtype f, eMonster attacker) {
|
||||
if(f & AF_WEAK)
|
||||
return true;
|
||||
if(f & AF_HORNS)
|
||||
return hornStuns(c2);
|
||||
else if(attacker == moArrowTrap && arrow_stuns(c2->monst))
|
||||
@ -1040,6 +1045,8 @@ EX void killFriendlyIvy() {
|
||||
}
|
||||
|
||||
EX bool monsterPushable(cell *c2) {
|
||||
if(markOrb(itCurseWeakness) && attackJustStuns(c2, 0, moPlayer))
|
||||
return false;
|
||||
return (c2->monst != moFatGuard && !(isMetalBeast(c2->monst) && c2->stuntime < 2) && c2->monst != moTortoise && c2->monst != moTerraWarrior && c2->monst != moVizier && c2->monst != moWorldTurtle);
|
||||
}
|
||||
|
||||
|
@ -545,6 +545,7 @@ static const flagtype IF_EMPATHY = Flag(3);
|
||||
static const flagtype IF_RANGED = Flag(4);
|
||||
static const flagtype IF_SHMUPLIFE = Flag(5);
|
||||
static const flagtype IF_REVIVAL = Flag(6);
|
||||
static const flagtype IF_CURSE = Flag(7);
|
||||
|
||||
// 0 = basic treasure, 1 = other item, 2 = power orb, 3 = not an item
|
||||
#define IC_TREASURE 0
|
||||
|
37
content.cpp
37
content.cpp
@ -1645,6 +1645,43 @@ WALL( '$', 0x40FD40, "Crate on Target", waCrateOnTarget, WF_WALL | WF_PUSHABLE,
|
||||
"A crate already on a target."
|
||||
)
|
||||
|
||||
ITEM( 'o', 0xF0F0FF, "Orb of Purity", itOrbPurity, IC_ORB, ZERO, RESERVED, osProtective,
|
||||
"Reverses all the curses."
|
||||
)
|
||||
|
||||
ITEM('c', 0x202020, "Curse of Weakness", itCurseWeakness, IC_ORB, IF_CURSE, RESERVED, osOffensive,
|
||||
"Makes you weak."
|
||||
)
|
||||
|
||||
ITEM('c', 0x6060FF, "Curse of Draining", itCurseDraining, IC_ORB, IF_CURSE, RESERVED, osPowerUtility,
|
||||
"Drains your power."
|
||||
)
|
||||
|
||||
ITEM('c', 0x000060, "Curse of Water", itCurseWater, IC_ORB, IF_CURSE, RESERVED, osTerraform,
|
||||
"Makes you fear water."
|
||||
)
|
||||
|
||||
ITEM('c', 0xFF6060, "Curse of Fatigue", itCurseFatigue, IC_ORB, IF_CURSE, RESERVED, osMovement,
|
||||
"Cannot move too quickly."
|
||||
)
|
||||
|
||||
ITEM('c', 0xFFFF80, "Curse of Repulsion", itCurseRepulsion, IC_ORB, IF_CURSE, RESERVED, osUtility,
|
||||
"All items are repelled."
|
||||
)
|
||||
|
||||
ITEM('c', 0xD08080, "Curse of Gluttony", itCurseGluttony, IC_ORB, IF_CURSE, RESERVED, osNone,
|
||||
"The first item you pick up is consumed."
|
||||
)
|
||||
|
||||
ITEM('>', 0xFF6060, "fatigue", itFatigue, IC_NAI, ZERO, RESERVED, osNone,
|
||||
"In the Windy Plains, you can let the wind carry you, "
|
||||
"causing you to move two cells with the wind in a single turn. "
|
||||
"This cannot be done if you are standing at distance at most 2 "
|
||||
"from the Air Elemental, or if any of the three cells on the way "
|
||||
"has two wind directions.\n\n"
|
||||
"Press 't' or click the destination to activate."
|
||||
)
|
||||
|
||||
//shmupspecials
|
||||
MONSTER( '@', 0xC0C0C0, "Rogue", moPlayer, CF_FACE_UP | CF_PLAYER, RESERVED, moNone, "In the Shoot'em Up mode, you are armed with thrown Knives.")
|
||||
MONSTER( '*', 0xC0C0C0, "Knife", moBullet, ZERO | CF_BULLET, RESERVED, moNone, "A simple, but effective, missile, used by rogues.")
|
||||
|
1
game.cpp
1
game.cpp
@ -296,6 +296,7 @@ EX void placeGolem(cell *on, cell *moveto, eMonster m) {
|
||||
EX bool multiRevival(cell *on, cell *moveto) {
|
||||
int fl = 0;
|
||||
if(items[itOrbAether]) fl |= P_AETHER;
|
||||
if(items[itCurseWater]) fl |= P_WATERCURSE;
|
||||
if(items[itOrbFish]) fl |= P_FISH;
|
||||
if(items[itOrbWinter]) fl |= P_WINTER;
|
||||
if(passable(on, moveto, fl)) {
|
||||
|
@ -819,6 +819,10 @@ EX bool drawItemType(eItem it, cell *c, const shiftmatrix& V, color_t icol, int
|
||||
queuepoly(Vit * spinptick(750, 0), cgi.shFan, darkena(icol, 0, 255));
|
||||
}
|
||||
|
||||
else if(it == itFatigue) {
|
||||
queuepoly(Vit * spinptick(750, 0), cgi.shFan, darkena(icol, 0, 255));
|
||||
}
|
||||
|
||||
else if(it == itWarning) {
|
||||
queuepoly(Vit * spinptick(750, 0), cgi.shTriangle, darkena(icol, 0, 255));
|
||||
}
|
||||
@ -958,7 +962,7 @@ EX bool drawItemType(eItem it, cell *c, const shiftmatrix& V, color_t icol, int
|
||||
}
|
||||
}
|
||||
|
||||
else if(xch == 'o' || it == itInventory) {
|
||||
else if(xch == 'o' || xch == 'c' || it == itInventory) {
|
||||
if(it == itOrbFire) icol = firecolor(100);
|
||||
PPR prio = PPR::ITEM;
|
||||
bool inice = c && c->wall == waIcewall;
|
||||
@ -971,6 +975,9 @@ EX bool drawItemType(eItem it, cell *c, const shiftmatrix& V, color_t icol, int
|
||||
if(it == itOrbFish)
|
||||
queuepolyat(Vit * spinptick(1500, 0), cgi.shFishTail, col, PPR::ITEM_BELOW);
|
||||
|
||||
if(xch == 'c')
|
||||
queuepolyat(Vit * spinptick(500, 0), cgi.shMoonDisk, darkena(0x801080, 0, hidden ? 0x20 : 0xC0), prio);
|
||||
else
|
||||
queuepolyat(Vit, cgi.shDisk, darkena(icol1, 0, inice ? 0x80 : hidden ? 0x20 : 0xC0), prio);
|
||||
|
||||
queuepolyat(Vit * spinptick(1500, 0), orbshape(iinf[it].orbshape), col, prio);
|
||||
|
2
hud.cpp
2
hud.cpp
@ -141,7 +141,7 @@ int glyphflags(int gid) {
|
||||
int f = 0;
|
||||
if(gid < ittypes) {
|
||||
eItem i = eItem(gid);
|
||||
if(itemclass(i) == IC_NAI) f |= GLYPH_NONUMBER;
|
||||
if(itemclass(i) == IC_NAI && i != itFatigue) f |= GLYPH_NONUMBER;
|
||||
if(isElementalShard(i)) {
|
||||
f |= GLYPH_LOCAL | GLYPH_INSQUARE;
|
||||
if(i == localshardof(cwt.at->land)) f |= GLYPH_LOCAL2;
|
||||
|
1
hyper.h
1
hyper.h
@ -615,6 +615,7 @@ typedef function<int(struct cell*)> cellfunction;
|
||||
#define AF_CRUSH Flag(31) // Crusher's delayed attack
|
||||
#define AF_PLAGUE Flag(32) // Orb of Plague (do not check adjacency)
|
||||
#define AF_PSI Flag(33) // Orb of the Mind
|
||||
#define AF_WEAK Flag(34) // Curse of Weakness
|
||||
|
||||
#if CAP_SDL
|
||||
|
||||
|
23
items.cpp
23
items.cpp
@ -29,6 +29,21 @@ EX bool canPickupItemWithMagnetism(cell *c, cell *from) {
|
||||
}
|
||||
|
||||
EX bool doPickupItemsWithMagnetism(cell *c) {
|
||||
/* repulsion works first -- Magnetism will collect the item if not repulsed */
|
||||
if(items[itCurseRepulsion])
|
||||
forCellIdEx(c3, i, c) if(c3->item) {
|
||||
cellwalker cw(c, i);
|
||||
cw += wstep;
|
||||
for(int j=1; j<c3->type; j++) {
|
||||
cell *c4 = (cw+j).peek();
|
||||
if(!isNeighbor(c, c4) && !c4->item && passable(c4, c3, ZERO)) {
|
||||
changes.ccell(c3);
|
||||
changes.ccell(c4);
|
||||
c4->item = c3->item;
|
||||
c3->item = itNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
cell *csaf = NULL;
|
||||
if(items[itOrbMagnetism])
|
||||
forCellEx(c3, c) if(canPickupItemWithMagnetism(c3, c)) {
|
||||
@ -73,6 +88,14 @@ EX bool collectItem(cell *c2, bool telekinesis IS(false)) {
|
||||
if(cannotPickupItem(c2, telekinesis))
|
||||
return false;
|
||||
|
||||
if(items[itCurseGluttony] && c2->item) {
|
||||
addMessage(XLAT("%The1 is consumed!", c2->item));
|
||||
playSound(c2, "apple");
|
||||
items[itCurseGluttony] = 0;
|
||||
c2->item = itNone;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* if(c2->item == itHolyGrail && telekinesis)
|
||||
return false; */
|
||||
|
||||
|
@ -420,6 +420,50 @@ EX bool makeflame(cell *c, int timeout, bool checkonly) {
|
||||
return true;
|
||||
}
|
||||
|
||||
EX bool makeshallow(cell *c, int timeout, bool checkonly) {
|
||||
changes.ccell(c);
|
||||
if(!checkonly) destroyTrapsOn(c);
|
||||
if(c->land == laBrownian) {
|
||||
if(checkonly) return true;
|
||||
brownian::dissolve(c, 1);
|
||||
}
|
||||
if(c->wall == waChasm || c->wall == waOpenGate || c->wall == waRed2 || c->wall == waRed3 ||
|
||||
c->wall == waTower)
|
||||
return false;
|
||||
else if(c->wall == waNone && c->land == laCocytus) {
|
||||
if(checkonly) return true;
|
||||
c->wall = waLake;
|
||||
}
|
||||
else if(c->wall == waFireTrap) {
|
||||
if(checkonly) return true;
|
||||
c->wall = waShallow;
|
||||
}
|
||||
else if(c->wall == waFrozenLake) {
|
||||
if(checkonly) return true;
|
||||
drawParticles(c, MELTCOLOR, 8, 8);
|
||||
c->wall = waLake;
|
||||
}
|
||||
else if(isFire(c)) {
|
||||
if(checkonly) return true;
|
||||
c->wall = waNone;
|
||||
}
|
||||
else if(c->wall == waMineMine) {
|
||||
if(checkonly) return true;
|
||||
c->wall = waShallow;
|
||||
}
|
||||
else if(among(c->wall, waNone, waRubble, waDeadfloor2, waCavefloor, waDeadfloor, waFloorA, waFloorB) && !cellUnstable(c) && !isGravityLand(c)) {
|
||||
if(checkonly) return true;
|
||||
c->wall = waShallow;
|
||||
}
|
||||
else if(c->wall == waDock) {
|
||||
if(checkonly) return true;
|
||||
c->wall = waSea;
|
||||
c->wparam = 3;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
EX void explosion(cell *c, int power, int central) {
|
||||
changes.ccell(c);
|
||||
playSound(c, "explosion");
|
||||
|
26
orbs.cpp
26
orbs.cpp
@ -59,6 +59,11 @@ EX void empathyMove(const movei& mi) {
|
||||
if(makeflame(mi.s, 10, false)) markEmpathy(itOrbFire);
|
||||
}
|
||||
|
||||
if(items[itCurseWater]) {
|
||||
invismove = false;
|
||||
if(makeshallow(mi.s, 10, false)) markEmpathy(itCurseWater);
|
||||
}
|
||||
|
||||
if(items[itOrbDigging]) {
|
||||
if(mi.proper() && earthMove(mi))
|
||||
markEmpathy(itOrbDigging), invismove = false;
|
||||
@ -76,6 +81,12 @@ EX int intensify(int val) {
|
||||
}
|
||||
|
||||
EX bool reduceOrbPower(eItem it, int cap) {
|
||||
if(items[it] && markOrb(itCurseDraining)) {
|
||||
items[it] -= (markOrb(itOrbEnergy) ? 1 : 2) * multi::activePlayers();
|
||||
if(items[it] < 0) items[it] = 0;
|
||||
if(items[it] == 0 && it == itOrbLove)
|
||||
princess::bringBack();
|
||||
}
|
||||
if(items[it] && (lastorbused[it] || (it == itOrbShield && items[it]>3) || !markOrb(itOrbTime))) {
|
||||
items[it] -= multi::activePlayers();
|
||||
if(isHaunted(cwt.at->land))
|
||||
@ -101,7 +112,22 @@ EX void reduceOrbPowerAlways(eItem it) {
|
||||
}
|
||||
}
|
||||
|
||||
EX void reverse_curse(eItem curse, eItem orb) {
|
||||
if(items[curse] && markOrb(itOrbPurity)) {
|
||||
items[orb] += items[curse];
|
||||
items[curse] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
EX void reduceOrbPowers() {
|
||||
|
||||
reverse_curse(itCurseWeakness, itOrbSlaying);
|
||||
reverse_curse(itCurseFatigue, itOrbSpeed); // OK
|
||||
reverse_curse(itCurseRepulsion, itOrbMagnetism); // OK
|
||||
reverse_curse(itCurseWater, itOrbFire); // OK
|
||||
reverse_curse(itCurseDraining, itOrbTime); // OK
|
||||
reverse_curse(itCurseGluttony, itOrbChoice); // OK
|
||||
|
||||
if(haveMount()) markOrb(itOrbDomination);
|
||||
for(int i=0; i<ittypes; i++)
|
||||
lastorbused[i] = orbused[i], orbused[i] = false;
|
||||
|
@ -74,6 +74,7 @@ EX bool checkflags(flagtype flags, flagtype x) {
|
||||
if((x & P_FISH) && markOrb(itOrbFish)) return true;
|
||||
if((x & P_MARKWATER) && markOrb(itOrbWater)) return true;
|
||||
if((x & P_AETHER) && markOrb2(itOrbAether) && !(flags&P_NOAETHER)) return true;
|
||||
if((x & P_WATERCURSE)&& markOrb2(itCurseWater)) return true;
|
||||
}
|
||||
if(flags & P_ISFRIEND) if(items[itOrbEmpathy])
|
||||
if(checkflags(flags ^ P_ISPLAYER ^ P_ISFRIEND, x) && markOrb(itOrbEmpathy))
|
||||
@ -129,6 +130,7 @@ EX bool anti_alchemy(cell *w, cell *from) {
|
||||
#define P_VOID Flag(32) // void beast
|
||||
#define P_PHASE Flag(33) // phasing movement
|
||||
#define P_PULLMAGNET Flag(34) // pull the other part of the magnet
|
||||
#define P_WATERCURSE Flag(35) // Curse of Water
|
||||
#endif
|
||||
|
||||
EX bool passable(cell *w, cell *from, flagtype flags) {
|
||||
@ -137,6 +139,9 @@ EX bool passable(cell *w, cell *from, flagtype flags) {
|
||||
|
||||
if(from && from != w && nonAdjacent(from, w) && !F(P_IGNORE37 | P_BULLET)) return false;
|
||||
|
||||
if((isWateryOrBoat(w) || w->wall == waShallow) && F(P_WATERCURSE))
|
||||
return false;
|
||||
|
||||
for(cell *pp: player_positions()) {
|
||||
if(w == pp && F(P_ONPLAYER)) return true;
|
||||
if(from == pp && F(P_ONPLAYER) && F(P_REVDIR)) return true;
|
||||
|
27
pcmove.cpp
27
pcmove.cpp
@ -778,7 +778,7 @@ bool pcmove::after_escape() {
|
||||
|
||||
bool dont_attack = items[itOrbFlash] || items[itOrbLightning];
|
||||
|
||||
if(attackable && fmsAttack && !dont_attack) {
|
||||
if(attackable && fmsAttack && !dont_attack && !items[itCurseWeakness]) {
|
||||
if(checkNeedMove(checkonly, true)) return false;
|
||||
nextmovetype = nm ? lmAttack : lmSkip;
|
||||
if(c2->wall == waSmallTree) {
|
||||
@ -831,6 +831,11 @@ bool pcmove::after_escape() {
|
||||
if(vmsg()) tell_why_impassable();
|
||||
return false;
|
||||
}
|
||||
else if(items[itFatigue] + fatigue_cost(mi) > 10) {
|
||||
if(vmsg())
|
||||
addMessage(XLAT("You are too fatigued!"));
|
||||
return false;
|
||||
}
|
||||
else if(fmsMove)
|
||||
return move_if_okay();
|
||||
|
||||
@ -898,6 +903,7 @@ bool pcmove::attack() {
|
||||
attackflags = AF_NORMAL;
|
||||
if(items[itOrbSpeed]&1) attackflags |= AF_FAST;
|
||||
if(items[itOrbSlaying]) attackflags |= AF_CRUSH;
|
||||
if(items[itCurseWeakness]) attackflags |= AF_WEAK;
|
||||
|
||||
bool ca =canAttack(cwt.at, moPlayer, c2, c2->monst, attackflags);
|
||||
|
||||
@ -999,6 +1005,13 @@ EX bool chaos_forbidden(cell *c) {
|
||||
return do_not_touch_this_wall(c) || isMultitile(c->monst);
|
||||
}
|
||||
|
||||
EX int fatigue_cost(const movei& mi) {
|
||||
return
|
||||
gravityLevelDiff(mi.t, mi.s) +
|
||||
(snakelevel(mi.t) - snakelevel(mi.s)) +
|
||||
(againstWind(mi.s, mi.t) ? 0 : 1);
|
||||
}
|
||||
|
||||
bool pcmove::perform_actual_move() {
|
||||
cell*& c2 = mi.t;
|
||||
changes.at_commit([&] {
|
||||
@ -1026,6 +1039,14 @@ bool pcmove::perform_actual_move() {
|
||||
if(makeflame(cwt.at, 10, false)) markOrb(itOrbFire);
|
||||
}
|
||||
|
||||
if(items[itCurseWater]) {
|
||||
invismove = false;
|
||||
if(makeshallow(mi.s, 10, false)) markOrb(itCurseWater);
|
||||
}
|
||||
|
||||
if(markOrb(itCurseFatigue) && !markOrb(itOrbAether))
|
||||
items[itFatigue] += fatigue_cost(mi);
|
||||
|
||||
handle_friendly_ivy();
|
||||
|
||||
if(items[itOrbDigging]) {
|
||||
@ -1145,6 +1166,10 @@ bool pcmove::stay() {
|
||||
if(d == -2)
|
||||
dropGreenStone(cwt.at);
|
||||
|
||||
items[itFatigue] -= 5;
|
||||
if(items[itFatigue] < 0)
|
||||
items[itFatigue] = 0;
|
||||
|
||||
if(monstersnear_add_pmi(mi)) {
|
||||
if(vmsg()) wouldkill("%The1 would get you!");
|
||||
return false;
|
||||
|
10
polygons.cpp
10
polygons.cpp
@ -503,6 +503,16 @@ void geometry_information::procedural_shapes() {
|
||||
for(int i=0; i<=S84; i+=SD3)
|
||||
hpcpush(ddi(i, orbsize * .2) * C0);
|
||||
|
||||
bshape(shMoonDisk, PPR::ITEM);
|
||||
for(int i=0; i<=S84; i+=SD3)
|
||||
if(i <= S84 * 2 / 3)
|
||||
hpcpush(ddi(i, orbsize * .2) * C0);
|
||||
else {
|
||||
hyperpoint h1 = ddi(i, orbsize * .2) * C0;
|
||||
hyperpoint h2 = ddi(S84-i*2, orbsize * .2) * C0;
|
||||
hpcpush(mid(mid(h1,h2), h2));
|
||||
}
|
||||
|
||||
bshape(shHugeDisk, PPR::ITEM);
|
||||
for(int i=0; i<=S84; i+=SD3)
|
||||
hpcpush(ddi(i, orbsize * .4) * C0);
|
||||
|
Loading…
Reference in New Issue
Block a user