mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-17 18:54:48 +00:00
first version of crossbow
This commit is contained in:
parent
f8fb7d5950
commit
e6a4d987bb
@ -1729,6 +1729,8 @@ MONSTER('d', 0x901020, "Angry Die", moAngryDie, ZERO, RESERVED, moAnimatedDie,
|
||||
"You have made a die unhappy. Taste the revenge! This one won't forgive you, no matter what you do."
|
||||
)
|
||||
|
||||
ITEM('}', 0xFFFF80, "Crossbow", itCrossbow, IC_NAI, ZERO, RESERVED, osNone, "Your crossbow.")
|
||||
|
||||
//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.")
|
||||
|
194
crossbow.cpp
Normal file
194
crossbow.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
// Hyperbolic Rogue
|
||||
// Copyright (C) 2011-2019 Zeno Rogue
|
||||
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
/** \file crossbow.cpp
|
||||
* \brief implementation of the crossbow mode
|
||||
*/
|
||||
|
||||
#include "hyper.h"
|
||||
|
||||
namespace hr {
|
||||
|
||||
EX namespace bow {
|
||||
|
||||
#if HDR
|
||||
enum eWeapon { wBlade, wCrossbow };
|
||||
enum eCrossbowStyle { cbBull, cbGeodesic };
|
||||
#endif
|
||||
|
||||
EX eWeapon weapon;
|
||||
EX eCrossbowStyle style;
|
||||
EX bool bump_to_shoot = true;
|
||||
|
||||
EX bool crossbow_mode() { return weapon == wCrossbow; }
|
||||
|
||||
#if HDR
|
||||
struct bowpoint {
|
||||
int total;
|
||||
movei last, next, lastun;
|
||||
cell *con;
|
||||
bowpoint() : last(nullptr), next(nullptr), lastun(nullptr) { total = 0; }
|
||||
};
|
||||
#endif
|
||||
|
||||
EX vector<bowpoint> bowpath;
|
||||
|
||||
EX vector<bowpoint> last_bowpath;
|
||||
|
||||
EX map<int, cell*> target_at;
|
||||
|
||||
EX int loading_time() {
|
||||
return style == cbBull ? 3 : 4;
|
||||
}
|
||||
|
||||
EX bool blocks(cell *c) {
|
||||
if(isWall(c) && c->wall != waMirrorWall) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX int create_path() {
|
||||
map<cell*, bowpoint> scores;
|
||||
scores[cwt.at] = bowpoint();
|
||||
scores[cwt.at].last = movei(cwt.at, STAY);
|
||||
|
||||
int best_score = -1; cell* best_score_at = cwt.at;
|
||||
|
||||
for(cell *c: dcal) {
|
||||
cell *c1 = target_at[c->cpdist];
|
||||
if(c1 && c != c1) continue;
|
||||
if(c == c1) { best_score = -1; }
|
||||
bowpoint best;
|
||||
best.total = -1;
|
||||
forCellIdEx(c1, i, c) if(c1->cpdist < c->cpdist && scores.count(c1)) {
|
||||
auto last = scores[c1];
|
||||
int ntotal = last.total;
|
||||
auto cw2 = cellwalker(c, i);
|
||||
if(inmirror(c)) cw2 = mirror::reflect(cw2);
|
||||
if(blocks(cw2.peek())) continue;
|
||||
if(cw2.at->monst) { ntotal += 10000; ntotal += 1280 >> c->cpdist; }
|
||||
ntotal += 2;
|
||||
if(c->cpdist > 1) {
|
||||
int d = abs(szgmod(last.lastun.d - c->c.spin(i), c1->type));
|
||||
if(d != c1->type / 2) {
|
||||
if(style == cbGeodesic) ntotal--;
|
||||
if(style == cbBull) continue;
|
||||
}
|
||||
}
|
||||
if(ntotal > best.total) { best.total = ntotal; best.lastun = movei(c, i); best.last = movei(cw2.at, cw2.spin); best.con = c1; best.next = movei(cw2.at, STAY); }
|
||||
best.total = max(best.total, ntotal);
|
||||
}
|
||||
if(best.total > best_score) { best_score = best.total; best_score_at = c; }
|
||||
if(best.total > -1) scores[c] = best;
|
||||
}
|
||||
|
||||
bowpath.clear();
|
||||
if(best_score == -1) return best_score;
|
||||
while(best_score_at != cwt.at) {
|
||||
bowpath.push_back(scores[best_score_at]); best_score_at = bowpath.back().con; scores[best_score_at].next = bowpath.back().last.rev();
|
||||
}
|
||||
bowpath.push_back(scores[best_score_at]);
|
||||
|
||||
reverse(bowpath.begin(), bowpath.end());
|
||||
return best_score;
|
||||
}
|
||||
|
||||
EX bool auto_path() {
|
||||
target_at = {};
|
||||
target_at[1] = cwt.cpeek();
|
||||
return create_path() >= 10000;
|
||||
}
|
||||
|
||||
EX bool fire_mode;
|
||||
|
||||
EX void switch_fire_mode() {
|
||||
if(!crossbow_mode()) { addMessage(XLAT("You fire an angry glance at your enemies.")); return; }
|
||||
if(items[itCrossbow]) { addMessage(XLAT("You need more time to reload your crossbow!")); return; }
|
||||
if(!fire_mode) {
|
||||
addMessage(XLAT("Double-click tile to fire."));
|
||||
fire_mode = true;
|
||||
last_bowpath = {};
|
||||
targets = {};
|
||||
}
|
||||
else if(fire_mode) {
|
||||
addMessage(XLAT("Firing cancelled."));
|
||||
fire_mode = false;
|
||||
last_bowpath = {};
|
||||
}
|
||||
}
|
||||
|
||||
EX void add_fire(cell *c) {
|
||||
bool emp = target_at.empty();
|
||||
auto& t = target_at[c->cpdist];
|
||||
if(t == c) {
|
||||
println(hlog, "done");
|
||||
bow::last_bowpath.clear();
|
||||
checked_move_issue = miVALID;
|
||||
pcmove pcm;
|
||||
pcm.checkonly = false;
|
||||
changes.init(false);
|
||||
bool b = pcm.try_shooting(false);
|
||||
println(hlog, "b = ", b);
|
||||
if(!b) changes.rollback();
|
||||
fire_mode = false;
|
||||
}
|
||||
else {
|
||||
t = c;
|
||||
int res = create_path();
|
||||
if(res == -1) {
|
||||
if(!emp) {
|
||||
target_at = {};
|
||||
add_fire(c);
|
||||
}
|
||||
else addMessage(XLAT("No way to hit this place."));
|
||||
}
|
||||
bow::last_bowpath = bow::bowpath;
|
||||
}
|
||||
}
|
||||
|
||||
EX void shoot() {
|
||||
flagtype attackflags = AF_NORMAL;
|
||||
if(items[itOrbSpeed]&1) attackflags |= AF_FAST;
|
||||
if(items[itOrbSlaying]) attackflags |= AF_CRUSH;
|
||||
if(items[itCurseWeakness]) attackflags |= AF_WEAK;
|
||||
|
||||
for(auto& m: bowpath) {
|
||||
cell *c = m.next.s;
|
||||
if(!c) continue;
|
||||
changes.ccell(c);
|
||||
if(c->monst) attackMonster(c, attackflags | AF_MSG, moPlayer);
|
||||
}
|
||||
last_bowpath = bowpath;
|
||||
}
|
||||
|
||||
EX void showMenu() {
|
||||
cmode = sm::SIDE | sm::MAYDARK;
|
||||
gamescreen();
|
||||
dialog::init(XLAT("weapon selection"));
|
||||
add_edit(weapon);
|
||||
if(crossbow_mode()) {
|
||||
add_edit(style);
|
||||
add_edit(bump_to_shoot);
|
||||
}
|
||||
else dialog::addBreak(200);
|
||||
dialog::addBack();
|
||||
dialog::display();
|
||||
}
|
||||
|
||||
EX }
|
||||
|
||||
}
|
||||
|
@ -126,6 +126,7 @@
|
||||
#include "inforder.cpp"
|
||||
#include "vr.cpp"
|
||||
#include "intra.cpp"
|
||||
#include "crossbow.cpp"
|
||||
|
||||
#if CAP_ROGUEVIZ
|
||||
#include "rogueviz/rogueviz-all.cpp"
|
||||
|
1
orbs.cpp
1
orbs.cpp
@ -216,6 +216,7 @@ EX void reduceOrbPowers() {
|
||||
whirlwind::calcdirs(cwt.at);
|
||||
items[itStrongWind] = !items[itOrbAether] && whirlwind::qdirs == 1;
|
||||
items[itWarning] = 0;
|
||||
if(items[itCrossbow]) items[itCrossbow]--;
|
||||
}
|
||||
|
||||
eWall orig_wall;
|
||||
|
54
pcmove.cpp
54
pcmove.cpp
@ -219,6 +219,7 @@ struct pcmove {
|
||||
void tell_why_cannot_attack();
|
||||
void tell_why_impassable();
|
||||
void handle_friendly_ivy();
|
||||
bool try_shooting(bool auto_target);
|
||||
|
||||
movei mi, mip;
|
||||
pcmove() : mi(nullptr, nullptr, 0), mip(nullptr, nullptr, 0) {}
|
||||
@ -241,6 +242,46 @@ EX bool movepcto(int d, int subdir IS(1), bool checkonly IS(false)) {
|
||||
return b;
|
||||
}
|
||||
|
||||
bool pcmove::try_shooting(bool auto_target) {
|
||||
if(auto_target) {
|
||||
auto b = bow::auto_path();
|
||||
if(!b) {
|
||||
if(!isWall(cwt.peek())) {
|
||||
changes.rollback();
|
||||
if(!checkonly) addMessage(XLAT("Cannot hit anything by shooting this direction!"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
items[itCrossbow] = bow::loading_time();
|
||||
bow::shoot();
|
||||
|
||||
if(items[itOrbGravity]) {
|
||||
gravity_state = get_static_gravity(cwt.at);
|
||||
if(gravity_state) markOrb(itOrbGravity);
|
||||
}
|
||||
lastmovetype = lmAttack; lastmove = NULL;
|
||||
if(checkNeedMove(checkonly, false))
|
||||
return false;
|
||||
swordAttackStatic();
|
||||
nextmovetype = lmAttack;
|
||||
|
||||
mi = movei(cwt.at, STAY);
|
||||
if(last_gravity_state && !gravity_state)
|
||||
playerMoveEffects(mi);
|
||||
|
||||
if(monstersnear_add_pmi(mi)) {
|
||||
if(vmsg(miTHREAT)) wouldkill("%The1 would catch you!");
|
||||
return false;
|
||||
}
|
||||
if(checkonly) return true;
|
||||
if(changes.on) changes.commit();
|
||||
if(cellUnstable(cwt.at) && !markOrb(itOrbAether))
|
||||
doesFallSound(cwt.at);
|
||||
|
||||
return after_move();
|
||||
}
|
||||
|
||||
bool pcmove::movepcto() {
|
||||
reset_spill();
|
||||
if(dual::state == 1) return dual::movepc(d, subdir, checkonly);
|
||||
@ -281,10 +322,19 @@ bool pcmove::movepcto() {
|
||||
fmsActivate = forcedmovetype == fmSkip || forcedmovetype == fmActivate;
|
||||
|
||||
changes.init(checkonly);
|
||||
changes.value_keep(bow::last_bowpath);
|
||||
bow::last_bowpath.clear();
|
||||
bool b = (d >= 0) ? actual_move() : stay();
|
||||
if(checkonly || !b) {
|
||||
changes.rollback();
|
||||
if(!checkonly) flipplayer = false;
|
||||
|
||||
if(!b && items[itCrossbow] == 0 && bow::crossbow_mode() && !bow::fire_mode) {
|
||||
changes.init(checkonly);
|
||||
changes.value_keep(bow::last_bowpath);
|
||||
b = try_shooting(true);
|
||||
if(checkonly || !b) changes.rollback();
|
||||
}
|
||||
}
|
||||
else if(changes.on) {
|
||||
println(hlog, "error: not commited!");
|
||||
@ -803,6 +853,8 @@ void pcmove::tell_why_cannot_attack() {
|
||||
addMessage(XLAT("You cannot attack your own mount!"));
|
||||
else if(checkOrb(c2->monst, itOrbShield))
|
||||
addMessage(XLAT("A magical shield protects %the1!", c2->monst));
|
||||
else if(bow::crossbow_mode() && items[itCrossbow])
|
||||
addMessage(XLAT("Your crossbow is still reloading!"));
|
||||
else
|
||||
addMessage(XLAT("For some reason... cannot attack!"));
|
||||
}
|
||||
@ -999,7 +1051,7 @@ bool pcmove::attack() {
|
||||
if(items[itOrbSlaying]) attackflags |= AF_CRUSH;
|
||||
if(items[itCurseWeakness]) attackflags |= AF_WEAK;
|
||||
|
||||
bool ca =canAttack(cwt.at, moPlayer, c2, c2->monst, attackflags);
|
||||
bool ca = bow::crossbow_mode() ? false : canAttack(cwt.at, moPlayer, c2, c2->monst, attackflags);
|
||||
|
||||
if(!ca) {
|
||||
if(forcedmovetype == fmAttack) {
|
||||
|
Loading…
Reference in New Issue
Block a user