diff --git a/rogueviz/ru/classes.cpp b/rogueviz/ru/classes.cpp index a4c19b22..b42cd602 100644 --- a/rogueviz/ru/classes.cpp +++ b/rogueviz/ru/classes.cpp @@ -166,6 +166,8 @@ struct entity { xy zero_vel; /* relative to the platform */ virtual struct moving_platform* as_platform() { return nullptr; } + virtual struct shopitem* as_shopitem() { return nullptr; } + virtual struct trader* as_trader() { return nullptr; } int hp; int invinc_end; @@ -313,19 +315,33 @@ struct pendulum_platform : public moving_platform { string get_help() override { return "These pendulum platforms go back and forth between two locations, taking the shortest path possible."; } }; -struct npc : public entity { - string sglyph, name; - color_t col; +struct npc_or_trader : public entity { string text; + string name; int talk_on; xy siz() override { return {12, 12}; } + void act() override; + string get_name() override { return name; } + }; + +struct npc : public npc_or_trader { + string sglyph; + color_t col; + xy siz() override { return {12, 12}; } string glyph() override { return sglyph; } color_t color() override { return col; } - void act() override; - string get_name() override { return name; } string get_help() override { return "Stay awhile and listen."; } }; +struct trader : public npc_or_trader { + xy siz() override { return {18, 18}; } + string glyph() override { return "@"; } + color_t color() override { return 0x2020D0FF; } + void act() override; + string get_help() override { return "Stay awhile and listen. Or use gold to pay."; } + virtual struct trader* as_trader() { return this; } + }; + struct enemy : public entity { xy respawn; int num_kills; @@ -452,6 +468,26 @@ struct item : public entity { string get_help() override { return powers[id].get_desc(); } }; +struct shopitem : public item { + int qty1; + int price; + bool bought; + string glyph() override; + color_t color() override; + bool last_intersect; + void act() override { + kino(); + bool next_intersect = intersect(get_pixel_bbox(), m.get_pixel_bbox()); + if(next_intersect && !last_intersect) { + addMessage("This costs " + its(price) + " gold."); + } + last_intersect = next_intersect; + } + string get_name() override { if(bought) return its(price) + " gold"; return item::get_name(); } + string get_help() override { if(bought) return "You have bought something from this shop. The trader has stored the gold here."; return item::get_help() + "\n\nPrice: " + its(price); } + shopitem* as_shopitem() override { return this; } + }; + struct missile : public entity { missile() { destroyed = false; } xy siz() override { return {4, 4}; } diff --git a/rogueviz/ru/entity.cpp b/rogueviz/ru/entity.cpp index e1795414..3d813740 100644 --- a/rogueviz/ru/entity.cpp +++ b/rogueviz/ru/entity.cpp @@ -247,14 +247,14 @@ void missile::act() { if(where.x > screen_x || where.x < 0 || where.y < 0 || where.y > screen_y) destroyed = true; } -void npc::act() { +void npc_or_trader::act() { kino(); if(gframeid > m.last_action + 300 && intersect(extend_all(get_pixel_bbox(), get_scale()*12), m.get_pixel_bbox()) && talk_on != m.last_action) { talk_on = m.last_action = gframeid; cmode = mode::menu; pushScreen([&] { cmode = mode::playing; popScreen(); }); pushScreen([&] { - dialog::init(name, col >> 8); + dialog::init(name, color() >> 8); dialog::addHelp(text); dialog::addBreak(100); dialog::addBack(); @@ -263,6 +263,27 @@ void npc::act() { } } +extern int gold_id; + +string shopitem::glyph() { if(bought) return powers[gold_id].get_glyph(); else return item::glyph(); } +color_t shopitem::color() { if(bought) return powers[gold_id].get_color(); else return item::color(); } + +void trader::act() { + bool any_purchases = false; + for(auto& e: current_room->entities) if(auto si = e->as_shopitem()) if(!si->existing) any_purchases = true; + if(any_purchases) { + walls[wShopDoor].glyph = '+'; + walls[wShopDoor].flags = W_BLOCK | W_BLOCKBIRD; + } + else { + walls[wShopDoor].glyph = '\''; + walls[wShopDoor].flags = W_TRANS; + } + + if(any_purchases) talk_on = m.last_action; + npc_or_trader::act(); + } + void boar::act() { stay_on_screen(); kino(); diff --git a/rogueviz/ru/map.ru b/rogueviz/ru/map.ru index 701871c2..ebdbd1fb 100644 --- a/rogueviz/ru/map.ru +++ b/rogueviz/ru/map.ru @@ -1182,6 +1182,15 @@ Why is there always gold lying in these mazes... ITEM 308 261 toughness You find a beautiful ring. +SHOPITEM 421 238 50 1 0 +toughness +You find a beautiful ring in this store. +SHOPITEM 420 200 50 1 0 +wisdom +You find another beautiful ring in this store. +TRADER 458 247 +John the trader +Hello here! Please buy something from my shop! OK MOVE 2 Mazes of Menace diff --git a/rogueviz/ru/powers.cpp b/rogueviz/ru/powers.cpp index 119ca91f..3d210d39 100644 --- a/rogueviz/ru/powers.cpp +++ b/rogueviz/ru/powers.cpp @@ -146,6 +146,12 @@ power& gen_power(int key, string name, string desc, string glyph, color_t color, power *extra_life; int gold_id; +void power_death_revert(power& p) { + int q0 = p.qty_filled; + int q1 = p.qty_owned; + add_revert(death_revert, [&p, q0, q1] { p.qty_filled = q0; p.qty_owned = q1; }); + } + void gen_powers() { powers.reserve(100); @@ -405,13 +411,69 @@ void gen_powers() { gold_id = isize(powers); - gen_power('g', "gold", + gen_power('t', "gold", "For some weird reason, people love gold, and they will give you anything if you give them enough gold.\n\n" "This can be used to buy things in shops. " "Just stand on the item, press the hotkey, go to the shopkeeper, and press the hotkey again.\n\n" "If you decide not to buy, press the hotkey without going to the shopkeeper.", "$", 0xFFD500FF, - [] (data& d) {} + [] (data& d) { + if(d.keystate == 1) { + trader *tr; + for(auto& e: current_room->entities) if(auto t = e->as_trader()) tr = t; + bool on_trader = intersect(tr->get_pixel_bbox(), m.get_pixel_bbox()); + bool done_something = false; + for(int it: {0, 1}) + for(auto& e: current_room->entities) if(auto si = e->as_shopitem()) { + bool on = intersect(si->get_pixel_bbox(), m.get_pixel_bbox()); + if(it == 0 && on && si->existing && !si->bought) { + done_something = true; + addMessage(si->pickup_message); + power_death_revert(powers[si->id]); + powers[si->id].qty_owned += si->qty; powers[si->id].qty_filled += si->qty1; + add_revert(death_revert, [si] { si->existing = true; }); + si->existing = false; + } + else if(it == 0 && on && si->existing && si->bought) { + done_something = true; + addMessage("You get some gold."); + power_death_revert(powers[gold_id]); + powers[gold_id].qty_owned += si->price; powers[gold_id].qty_filled += si->price; + add_revert(death_revert, [si] { si->existing = true; }); + si->existing = false; + } + else if((it ? !done_something : on) && !si->existing && !si->bought) { + done_something = true; + addMessage("You rethink your purchase."); + power_death_revert(powers[si->id]); + powers[si->id].qty_owned -= si->qty; powers[si->id].qty_filled -= si->qty1; + add_revert(death_revert, [si] { si->existing = false; }); + si->existing = true; + } + else if((it ? !done_something : on) && !si->existing && si->bought) { + done_something = true; + addMessage("You rethink your actions."); + power_death_revert(powers[gold_id]); + powers[gold_id].qty_owned -= si->price; powers[gold_id].qty_filled -= si->price; + add_revert(death_revert, [si] { si->existing = false; }); + si->existing = true; + } + else if(it == 0 && on_trader && !si->existing && d.p->qty_owned >= si->price) { + done_something = true; + addMessage("You buy the " + powers[si->id].get_name() + "."); + power_death_revert(powers[gold_id]); + powers[gold_id].qty_owned -= si->price; powers[gold_id].qty_filled -= si->price; + si->existing = true; si->bought = true; + add_revert(death_revert, [si] { si->existing = false; si->bought = false; }); + } + else if(it == 0 && on_trader && !si->existing && !si->bought) { + done_something = true; + addMessage("You have not enough gold to buy the " + powers[si->id].get_name() + "."); + } + } + if(!done_something) addMessage("You count your gold. You have " + its(d.p->qty_owned) + " gold."); + } + } ).be_resource("pieces of gold"); }; diff --git a/rogueviz/ru/save.cpp b/rogueviz/ru/save.cpp index c1b78290..d47952c3 100644 --- a/rogueviz/ru/save.cpp +++ b/rogueviz/ru/save.cpp @@ -120,6 +120,17 @@ void load_room(fhstream& f, cell *c) { b->pickup_message = scanline_noblank(f); r.entities.emplace_back(std::move(b)); } + else if(cap == "SHOPITEM") { + auto b = std::make_unique(); + b->qty = 1; b->qty1 = 0; + sscanf(param.c_str(), "%lf%lf%d%d%d", &b->where.x, &b->where.y, &b->price, &b->qty, &b->qty1); + s = scanline_noblank(f); + b->id = -1; + for(int i=0; iid = i; + if(b->id == -1) println(hlog, "error: unknown item name ", s), b->id = 0; + b->pickup_message = scanline_noblank(f); + r.entities.emplace_back(std::move(b)); + } else if(cap == "NPC") { auto b = std::make_unique(); sscanf(param.c_str(), "%lf%lf%08x", &b->where.x, &b->where.y, &b->col); @@ -129,6 +140,13 @@ void load_room(fhstream& f, cell *c) { b->text = scanline_noblank(f); r.entities.emplace_back(std::move(b)); } + else if(cap == "TRADER") { + auto b = std::make_unique(); + sscanf(param.c_str(), "%lf%lf", &b->where.x, &b->where.y); + b->name = scanline_noblank(f); + b->text = scanline_noblank(f); + r.entities.emplace_back(std::move(b)); + } else if(cap == "BOAR") { auto b = std::make_unique(); sscanf(param.c_str(), "%lf%lf", &b->where.x, &b->where.y);