diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index e23c9f29f1d..85f55463279 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -2029,7 +2029,7 @@ "aNa" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 5},/turf/simulated/floor/plasteel,/area/crew_quarters/fitness) "aNb" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/plasteel,/area/crew_quarters/fitness) "aNc" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 10},/turf/simulated/floor/plasteel,/area/crew_quarters/fitness) -"aNd" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 10; initialize_directions = 10; level = 1},/obj/machinery/camera{c_tag = "Fitness Rooms North"; dir = 2; network = list("SS13")},/turf/simulated/floor/plasteel{icon_state = "neutral"; dir = 4},/area/crew_quarters/fitness) +"aNd" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 10; initialize_directions = 10; level = 1},/obj/machinery/camera{c_tag = "Fitness Rooms North"; dir = 2; network = list("SS13")},/obj/machinery/computer/mob_battle_terminal/red,/turf/simulated/floor/plasteel{icon_state = "neutral"; dir = 4},/area/crew_quarters/fitness) "aNe" = (/obj/machinery/access_button{command = "cycle_interior"; frequency = 1450; master_tag = "bar_maint"; name = "interior access button"; pixel_x = -25; pixel_y = 25; req_access_txt = "13"},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aNf" = (/obj/structure/sign/securearea{desc = "A warning sign which reads 'EXTERNAL AIRLOCK'"; icon_state = "space"; layer = 4; name = "EXTERNAL AIRLOCK"; pixel_x = 0; pixel_y = 32},/obj/machinery/atmospherics/unary/portables_connector{dir = 1},/obj/machinery/portable_atmospherics/canister/air{filled = 0.1},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aNg" = (/obj/structure/rack,/obj/effect/spawner/lootdrop/maintenance{lootcount = 2; name = "2maintenance loot spawner"},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) @@ -2342,7 +2342,7 @@ "aTb" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/disposalpipe/segment{dir = 4},/obj/structure/cable{d1 = 2; d2 = 8; icon_state = "2-8"; tag = ""},/turf/simulated/floor/plasteel,/area/crew_quarters/fitness) "aTc" = (/obj/structure/disposalpipe/segment{dir = 2; icon_state = "pipe-c"},/turf/simulated/floor/plasteel,/area/crew_quarters/fitness) "aTd" = (/turf/simulated/floor/plasteel{icon_state = "freezerfloor"},/area/crew_quarters/fitness) -"aTe" = (/turf/simulated/floor/plasteel{icon_state = "neutral"; dir = 4},/area/crew_quarters/fitness) +"aTe" = (/obj/machinery/computer/mob_battle_terminal/blue,/turf/simulated/floor/plasteel{icon_state = "neutral"; dir = 4},/area/crew_quarters/fitness) "aTf" = (/obj/effect/spawner/window/reinforced,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/turf/simulated/floor/plating,/area/crew_quarters/fitness) "aTg" = (/obj/structure/grille{density = 0; icon_state = "brokengrille"},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aTh" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/structure/table,/obj/effect/spawner/lootdrop/maintenance,/turf/simulated/floor/plating,/area/maintenance/fsmaint2) @@ -4368,7 +4368,7 @@ "bFZ" = (/obj/machinery/light{dir = 1},/obj/structure/stool,/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTH)"; icon_state = "whiteblue"; dir = 1},/area/medical/reception) "bGa" = (/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/reception) "bGb" = (/obj/machinery/camera{c_tag = "Medbay Lobby East"; network = list("SS13")},/obj/structure/stool,/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTH)"; icon_state = "whiteblue"; dir = 1},/area/medical/reception) -"bGc" = (/obj/structure/stool,/obj/structure/closet/walllocker/emerglocker/north,/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTH)"; icon_state = "whiteblue"; dir = 1},/area/medical/reception) +"bGc" = (/obj/structure/closet/walllocker/emerglocker/north,/obj/structure/table,/obj/machinery/computer/mob_healer_terminal,/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTH)"; icon_state = "whiteblue"; dir = 1},/area/medical/reception) "bGd" = (/obj/machinery/newscaster{pixel_x = 30},/obj/machinery/alarm{pixel_y = 26},/obj/item/weapon/twohanded/required/kirbyplants,/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTHEAST)"; icon_state = "whiteblue"; dir = 5},/area/medical/reception) "bGe" = (/obj/machinery/status_display{layer = 4; pixel_x = 0; pixel_y = 32},/obj/machinery/light{dir = 1},/obj/machinery/bodyscanner,/turf/simulated/floor/plasteel{dir = 2; icon_state = "cafeteria"; tag = "icon-cafeteria (NORTHEAST)"},/area/medical/exam_room) "bGf" = (/obj/structure/disposalpipe/segment{dir = 4; icon_state = "pipe-c"},/obj/item/device/radio/intercom{broadcasting = 0; name = "station intercom (General)"; pixel_y = 25},/obj/machinery/body_scanconsole,/turf/simulated/floor/plasteel{dir = 2; icon_state = "cafeteria"; tag = "icon-cafeteria (NORTHEAST)"},/area/medical/exam_room) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 9e38a1b46e1..600dac2f273 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -33,4 +33,4 @@ var/global/list/shuttle_caller_list = list() //list of all communication cons var/global/list/tracked_implants = list() //list of all current implants that are tracked to work out what sort of trek everyone is on. Sadly not on lavaworld not implemented... var/global/list/pinpointer_list = list() //list of all pinpointers. Used to change stuff they are pointing to all at once. var/global/list/abductor_equipment = list() //list of all abductor equipment -var/global/list/global_intercoms = list() //list of all intercomms, across all z-levels +var/global/list/global_intercoms = list() //list of all intercomms, across all z-levels \ No newline at end of file diff --git a/code/controllers/Processes/nano_mob_hunter.dm b/code/controllers/Processes/nano_mob_hunter.dm new file mode 100644 index 00000000000..469ce22651d --- /dev/null +++ b/code/controllers/Processes/nano_mob_hunter.dm @@ -0,0 +1,157 @@ +var/global/datum/controller/process/mob_hunt/mob_hunt_server + +/datum/controller/process/mob_hunt + var/max_normal_spawns = 15 //change this to adjust the number of normal spawns that can exist at one time. trapped spawns (from traitors) don't count towards this + var/list/normal_spawns = list() + var/max_trap_spawns = 15 //change this to adjust the number of trap spawns that can exist at one time. traps spawned beyond this point clear the oldest traps + var/list/trap_spawns = list() + var/list/connected_clients = list() + var/server_status = 1 //1 is online, 0 is offline + var/reset_cooldown = 0 //number of controller cycles before the manual_reboot proc can be used again (ignored if server is offline so you can always boot back up) + var/obj/machinery/computer/mob_battle_terminal/red_terminal + var/obj/machinery/computer/mob_battle_terminal/blue_terminal + var/battle_turn = null + +/datum/controller/process/mob_hunt/setup() + name = "Nano-Mob Hunter GO Server" + start_delay = 20 + mob_hunt_server = src + +/datum/controller/process/mob_hunt/doWork() + if(reset_cooldown) //if reset_cooldown is set (we are on cooldown, duh), reduce the remaining cooldown every cycle + reset_cooldown-- + if(!server_status) + return + client_mob_update() + if(normal_spawns.len < max_normal_spawns) + spawn_mob() + +//leaving this here in case admins want to use it for a random mini-event or something +/datum/controller/process/mob_hunt/proc/server_crash(recover_time = 3000) + server_status = 0 + for(var/datum/data/pda/app/mob_hunter_game/client in connected_clients) + client.disconnect("Server Crash") + for(var/obj/effect/nanomob/N in trap_spawns) + N.despawn() + for(var/obj/effect/nanomob/N in normal_spawns) + N.despawn() + //just in case + normal_spawns.Cut() + trap_spawns.Cut() + connected_clients.Cut() + if(!isnum(recover_time)) + recover_time = 3000 + if(recover_time > 0) //when provided with a negative or zero valued recover_time argument, the server won't auto-restart but can be manually rebooted still + //set a timer to automatically recover after recover_time has passed (can be manually restarted if you get impatient too) + addtimer(src, "auto_recover", recover_time, TRUE) + +/datum/controller/process/mob_hunt/proc/client_mob_update() + var/list/ex_players = list() + for(var/datum/data/pda/app/mob_hunter_game/client in connected_clients) + var/mob/living/carbon/human/H = client.get_player() + if(connected_clients[client]) + if(!H || H != connected_clients[client]) + ex_players |= connected_clients[client] + connected_clients[client] = H + if(ex_players.len) //to make sure we don't do this if we didn't lose any player since the last update + for(var/obj/effect/nanomob/N in (normal_spawns + trap_spawns)) + N.conceal(ex_players) + +/datum/controller/process/mob_hunt/proc/auto_recover() + if(server_status != 0) + return + server_status = 1 + while(normal_spawns.len < max_normal_spawns) //repopulate the server's spawns completely if we auto-recover from crash + spawn_mob() + +/datum/controller/process/mob_hunt/proc/manual_reboot() + if(server_status && reset_cooldown) + return 0 + for(var/obj/effect/nanomob/N in trap_spawns) + N.despawn() + for(var/obj/effect/nanomob/N in normal_spawns) + N.despawn() + server_status = 1 + reset_cooldown = 25 //25 controller cycle cooldown for manual restarts + return 1 + +/datum/controller/process/mob_hunt/proc/spawn_mob() + var/list/nanomob_types = subtypesof(/datum/mob_hunt) + var/datum/mob_hunt/mob_info = pick(nanomob_types) + new mob_info() + +/datum/controller/process/mob_hunt/proc/register_spawn(datum/mob_hunt/mob_info) + if(!mob_info) + return 0 + var/obj/effect/nanomob/new_mob = new /obj/effect/nanomob(mob_info.spawn_point, mob_info) + normal_spawns += new_mob + new_mob.reveal() + return 1 + +/datum/controller/process/mob_hunt/proc/register_trap(datum/mob_hunt/mob_info) + if(!mob_info) + return 0 + if(!mob_info.is_trap) + return register_spawn(mob_info) + var/obj/effect/nanomob/new_mob = new /obj/effect/nanomob(mob_info.spawn_point, mob_info) + trap_spawns += new_mob + new_mob.reveal() + if(trap_spawns.len > max_trap_spawns) + var/obj/effect/nanomob/old_trap = trap_spawns[1] + old_trap.despawn() + return 1 + +/datum/controller/process/mob_hunt/proc/start_check() + if(battle_turn) //somehow we got called mid-battle, so lets just stop now + return + if(red_terminal && red_terminal.ready && blue_terminal && blue_terminal.ready) + battle_turn = pick("Red", "Blue") + red_terminal.audible_message("Battle starting!", null, 5) + blue_terminal.audible_message("Battle starting!", null, 5) + if(battle_turn == "Red") + red_terminal.audible_message("Red Player's Turn!", null, 5) + else if(battle_turn == "Blue") + blue_terminal.audible_message("Blue Player's Turn!", null, 5) + +/datum/controller/process/mob_hunt/proc/launch_attack(team, raw_damage, datum/mob_type/attack_type) + if(!team || !raw_damage) + return + var/obj/machinery/computer/mob_battle_terminal/target = null + if(team == "Red") + target = blue_terminal + else if(team == "Blue") + target = red_terminal + else + return + target.receive_attack(raw_damage, attack_type) + +/datum/controller/process/mob_hunt/proc/end_battle(loser, surrender = 0) + var/obj/machinery/computer/mob_battle_terminal/winner_terminal = null + var/obj/machinery/computer/mob_battle_terminal/loser_terminal = null + if(loser == "Red") + loser_terminal = red_terminal + winner_terminal = blue_terminal + else if (loser == "Blue") + loser_terminal = blue_terminal + winner_terminal = red_terminal + battle_turn = null + winner_terminal.ready = 0 + loser_terminal.ready = 0 + if(surrender) //surrender doesn't give exp, to avoid people just farming exp without actually doing a battle + winner_terminal.audible_message("Your rival surrendered!", null, 2) + else + var/progress_message = winner_terminal.mob_info.gain_exp() + winner_terminal.audible_message("[winner_terminal.team] Player wins!", null, 5) + winner_terminal.audible_message(progress_message, null, 2) + +/datum/controller/process/mob_hunt/proc/end_turn() + red_terminal.updateUsrDialog() + blue_terminal.updateUsrDialog() + if(!battle_turn) + return + if(battle_turn == "Red") + battle_turn = "Blue" + blue_terminal.audible_message("Blue's turn.", null, 5) + else if(battle_turn == "Blue") + battle_turn = "Red" + blue_terminal.audible_message("Red's turn.", null, 5) diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 3aa638aa40f..a7678e1d261 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -18,7 +18,7 @@ return -/client/proc/debug_controller(controller in list("Master","failsafe","Ticker","Air","Lighting","Jobs","Sun","Radio","Configuration","pAI", "Cameras","Garbage", "Transfer Controller","Event","Alarm","Scheduler","Nano","Vote")) +/client/proc/debug_controller(controller in list("Master","failsafe","Ticker","Air","Lighting","Jobs","Sun","Radio","Configuration","pAI", "Cameras","Garbage", "Transfer Controller","Event","Alarm","Scheduler","Nano","Vote","Mob Hunt Server")) set category = "Debug" set name = "Debug Controller" set desc = "Debug the various periodic loop controllers for the game (be careful!)" @@ -76,6 +76,9 @@ if("Vote") debug_variables(vote) feedback_add_details("admin_verb","DVote") + if("Mob Hunt Server") + debug_variables(mob_hunt_server) + feedback_add_details("admin_verb","DMobHuntServer") message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") return diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 7420aaaa24a..9813597f9cd 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -276,6 +276,18 @@ src.emagged = 0 setMenuState(usr,COMM_SCREEN_MAIN) + if("RestartNanoMob") + if(mob_hunt_server) + if(mob_hunt_server.manual_reboot()) + var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", + "Capturing all of them", "Fixing minor text issues", "Being the very best", + "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") + to_chat(usr, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") + else + to_chat(usr, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") + else + to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") + if("AcceptDocking") to_chat(usr, "Docking request accepted!") trade_dock_timelimit = world.time + 1200 diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index ed6a23998c4..37666b2eba1 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -834,11 +834,11 @@ product_slogans = "Carts to go!" icon_state = "cart" icon_deny = "cart-deny" - products = list(/obj/item/device/pda =10,/obj/item/weapon/cartridge/medical = 10,/obj/item/weapon/cartridge/chemistry = 10, + products = list(/obj/item/device/pda =10,/obj/item/weapon/cartridge/mob_hunt_game = 25,/obj/item/weapon/cartridge/medical = 10,/obj/item/weapon/cartridge/chemistry = 10, /obj/item/weapon/cartridge/engineering = 10,/obj/item/weapon/cartridge/atmos = 10,/obj/item/weapon/cartridge/janitor = 10, /obj/item/weapon/cartridge/signal/toxins = 10,/obj/item/weapon/cartridge/signal = 10) contraband = list(/obj/item/weapon/cartridge/clown = 1,/obj/item/weapon/cartridge/mime = 1) - prices = list(/obj/item/device/pda =300,/obj/item/weapon/cartridge/medical = 200,/obj/item/weapon/cartridge/chemistry = 150,/obj/item/weapon/cartridge/engineering = 100, + prices = list(/obj/item/device/pda =300,/obj/item/weapon/cartridge/mob_hunt_game = 50,/obj/item/weapon/cartridge/medical = 200,/obj/item/weapon/cartridge/chemistry = 150,/obj/item/weapon/cartridge/engineering = 100, /obj/item/weapon/cartridge/atmos = 75,/obj/item/weapon/cartridge/janitor = 100,/obj/item/weapon/cartridge/signal/toxins = 150, /obj/item/weapon/cartridge/signal = 75) diff --git a/code/modules/arcade/mob_hunt/battle_computer.dm b/code/modules/arcade/mob_hunt/battle_computer.dm new file mode 100644 index 00000000000..97055664cdb --- /dev/null +++ b/code/modules/arcade/mob_hunt/battle_computer.dm @@ -0,0 +1,278 @@ + +/obj/machinery/computer/mob_battle_terminal + name = "Nano-Mob Hunter GO! Battle Terminal" + desc = "Insert a mob card to partake in life-like Nano-Mob Battle Action!" + icon_state = "mob_battle_empty" + icon_screen = null + icon_keyboard = null + density = 0 + anchored = 1 + var/obj/item/weapon/nanomob_card/card + var/datum/mob_hunt/mob_info + var/obj/effect/nanomob/battle/avatar + var/ready = 0 + var/team = "Grey" + var/avatar_x_offset = 4 + var/avatar_y_offset = 0 + +/obj/machinery/computer/mob_battle_terminal/red + pixel_y = 24 + dir = SOUTH + team = "Red" + avatar_y_offset = -1 + +/obj/machinery/computer/mob_battle_terminal/blue + pixel_y = -24 + dir = NORTH + team = "Blue" + avatar_y_offset = 1 + +/obj/machinery/computer/mob_battle_terminal/red/initialize() + ..() + check_connection() + +/obj/machinery/computer/mob_battle_terminal/blue/initialize() + ..() + check_connection() + +/obj/machinery/computer/mob_battle_terminal/update_icon() + if(card) + icon_state = "mob_battle_loaded" + else + icon_state = "mob_battle_empty" + ..() + +/obj/machinery/computer/mob_battle_terminal/Destroy() + eject_card(1) + if(mob_hunt_server) + if(mob_hunt_server.battle_turn) + mob_hunt_server.battle_turn = null + if(mob_hunt_server.red_terminal == src) + mob_hunt_server.red_terminal = null + if(mob_hunt_server.blue_terminal == src) + mob_hunt_server.blue_terminal = null + if(avatar) + qdel(avatar) + return ..() + +/obj/machinery/computer/mob_battle_terminal/attackby(obj/item/O, mob/user) + if(istype(O, /obj/item/weapon/nanomob_card)) + insert_card(O, user) + +/obj/machinery/computer/mob_battle_terminal/proc/insert_card(obj/item/weapon/nanomob_card/new_card, mob/user) + if(!new_card) + return + if(card) + to_chat(user, "The card slot is currently filled.") + return + if(!new_card.mob_data) + to_chat(user, "This is a blank mob card.") + return + if(new_card.mob_data && !new_card.mob_data.cur_health) + to_chat(user, "This mob is incapacitated! Heal it before attempting to use it in battle!") + return + user.unEquip(new_card) + new_card.forceMove(src) + card = new_card + mob_info = card.mob_data + update_icon() + update_avatar() + updateUsrDialog() + +/obj/machinery/computer/mob_battle_terminal/proc/eject_card(override = 0) + if(!override) + if(ready && mob_hunt_server.battle_turn != team) + audible_message("You can't recall on your rival's turn!", null, 2) + return + card.mob_data = mob_info + mob_info = null + card.forceMove(get_turf(src)) + card = null + update_avatar() + update_icon() + updateUsrDialog() + +/obj/machinery/computer/mob_battle_terminal/proc/update_avatar() + //if we don't have avatars yet, spawn them + if(!avatar) + avatar = new(locate((x + avatar_x_offset), (y + avatar_y_offset), z)) + //update avatar info from card + if(mob_info) + avatar.mob_info = mob_info + else + avatar.mob_info = null + //tell the avatar to update themself with the new info + avatar.update_self() + +/obj/machinery/computer/mob_battle_terminal/attack_hand(mob/user) + add_fingerprint(user) + interact(user) + +/obj/machinery/computer/mob_battle_terminal/attack_ai(mob/user) + to_chat(user, "You cannot interface with this portion of the simulation.") + return + +/obj/machinery/computer/mob_battle_terminal/interact(mob/user) + check_connection() + var/dat = "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + if(ready && mob_hunt_server.battle_turn) //offer the surrender option if they are in a battle (ready), but don't have a card loaded + dat += "" + dat += "" + dat += "" + else + dat += "
" + dat += "[team] PLAYER" + dat += "
" + if(!card) + dat += "

No Nano-Mob card loaded.

" + dat += "
Surrender!
" + dat += "" + dat += "" + if(mob_info.nickname) + dat += "" + dat += "" + if(mob_info.nickname) + dat += "" + dat += "" + dat += "" + dat += "
" + dat += "

[mob_info.mob_name]

" + dat += "
" + else + dat += "" + var/img_src = "[mob_info.icon_state_normal].png" + if(mob_info.is_shiny) + dat += "[mob_info.icon_state_shiny].png" + dat += "" + dat += "
" + dat += "

[mob_info.nickname]

" + dat += "
" + dat += "
" + dat += "Health: [mob_info.cur_health] / [mob_info.max_health]
" + dat += "" + dat += "" + if(mob_info.cur_health) + dat += "" + else + dat += "" + dat += "" + dat += "" + dat += "
Attack!Incapacitated!Recall!
" + dat += "" + dat += "" + if(!ready) + dat += "" + dat += "Battle!" + dat += "" + if(ready && !mob_hunt_server.battle_turn) + dat += "" + dat += "Cancel Battle!" + dat += "" + dat += "" + + var/datum/browser/popup = new(user, "mob_battle_terminal", "Nano-Mob Hunter GO! Battle Terminal", 575, 400) + popup.set_content(dat) + popup.open() + +/obj/machinery/computer/mob_battle_terminal/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["attack"]) + do_attack() + + if(href_list["eject"]) + eject_card() + + if(href_list["surrender"]) + surrender() + + if(href_list["ready"]) + var/option = text2num(href_list["ready"]) + if(option == 1) + start_battle() + else if(option == 2) + ready = 0 + audible_message("[team] Player cancels their battle challenge.", null, 5) + + updateUsrDialog() + +/obj/machinery/computer/mob_battle_terminal/proc/check_connection() + if(team == "Red") + if(mob_hunt_server && !mob_hunt_server.red_terminal) + mob_hunt_server.red_terminal = src + else if(team == "Blue") + if(mob_hunt_server && !mob_hunt_server.blue_terminal) + mob_hunt_server.blue_terminal = src + +/obj/machinery/computer/mob_battle_terminal/proc/do_attack() + if(!ready) //no attacking if you arent ready to fight (duh) + return + if(!mob_hunt_server || team != mob_hunt_server.battle_turn) //don't attack unless it is actually our turn + return + else + var/message = "[mob_info.mob_name] attacks!" + if(mob_info.nickname) + message = "[mob_info.nickname] attacks!" + audible_message(message, null, 5) + mob_hunt_server.launch_attack(team, mob_info.get_raw_damage(), mob_info.get_attack_type()) + +/obj/machinery/computer/mob_battle_terminal/proc/start_battle() + if(ready) //don't do anything if we are still ready + return + if(!card) //don't do anything if there isn't a card inserted + return + ready = 1 + audible_message("[team] Player is ready for battle! Waiting for rival...", null, 5) + mob_hunt_server.start_check() + +/obj/machinery/computer/mob_battle_terminal/proc/receive_attack(raw_damage, datum/mob_type/attack_type) + var/message = mob_info.take_damage(raw_damage, attack_type) + avatar.audible_message(message, null, 5) + if(!mob_info.cur_health) + mob_hunt_server.end_battle(team) + eject_card(1) //force the card out, they were defeated + else + mob_hunt_server.end_turn() + +/obj/machinery/computer/mob_battle_terminal/proc/surrender() + audible_message("[team] Player surrenders the battle!", null, 5) + mob_hunt_server.end_battle(team, 1) + +////////////////////////////// +// Mob Healing Terminal // +// (Pokemon Center) // +////////////////////////////// + +/obj/machinery/computer/mob_healer_terminal + name = "Nano-Mob Hunter GO! Restoration Terminal" + desc = "Swipe a mob card to instantly restore it to full health!" + icon_state = "mob_battle_loaded" + icon_screen = null + icon_keyboard = null + density = 0 + anchored = 1 + dir = EAST + +/obj/machinery/computer/mob_healer_terminal/attackby(obj/item/O, mob/user) + if(istype(O, /obj/item/weapon/nanomob_card)) + heal_card(O, user) + +/obj/machinery/computer/mob_healer_terminal/proc/heal_card(obj/item/weapon/nanomob_card/patient, mob/user) + if(!patient) + return + if(!patient.mob_data) + to_chat(user, "This is a blank mob card.") + return + if(patient.mob_data && patient.mob_data.cur_health == patient.mob_data.max_health) + to_chat(user, "This mob is already at maximum health!") + return + patient.mob_data.cur_health = patient.mob_data.max_health + to_chat(user, "[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!") \ No newline at end of file diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm new file mode 100644 index 00000000000..8717d5eb2a5 --- /dev/null +++ b/code/modules/arcade/mob_hunt/mob_avatar.dm @@ -0,0 +1,162 @@ + +/obj/effect/nanomob + name = "Nano-Mob Avatar" //will be overridden by the mob datum name value when created + desc = "A wild Nano-Mob appeared! Hit it with your PDA with the game open to attempt to capture it!" + invisibility = 101 + alpha = 128 + anchored = 1 //just in case + density = 0 + icon = 'icons/effects/mob_hunt.dmi' + var/state_name + var/datum/mob_hunt/mob_info = null + var/list/clients_encountered = list() //tracks who has already interacted with us, so they can't attempt a second capture + var/image/avatar + +/obj/effect/nanomob/New(loc, datum/mob_hunt/new_info) + ..() + if(!new_info) + qdel(src) + return + mob_info = new_info + update_self() + forceMove(mob_info.spawn_point) + if(!mob_info.is_trap) + addtimer(src, "despawn", mob_info.lifetime) + +/obj/effect/nanomob/proc/update_self() + if(!mob_info) + return + name = mob_info.mob_name + desc = "A wild [name] (level [mob_info.level]) appeared! Hit it with your PDA with the game open to attempt to capture it!" + if(mob_info.is_shiny) + state_name = mob_info.icon_state_shiny + else + state_name = mob_info.icon_state_normal + avatar = image(icon, src, state_name) + avatar.override = 1 + add_alt_appearance("nanomob_avatar", avatar) + +/obj/effect/nanomob/attackby(obj/item/O, mob/user) + if(istype(O, /obj/item/device/pda)) + var/obj/item/device/pda/P = O + attempt_capture(P, -20) //attempting a melee capture reduces the mob's effective run_chance by 20% to balance the risk of triggering a trap mob + return 1 + +/obj/effect/nanomob/hitby(obj/item/O) + if(istype(O, /obj/item/device/pda)) + var/obj/item/device/pda/P = O + attempt_capture(P) //attempting a ranged capture does not affect the mob's effective run_chance but does prevent you from being shocked by a trap mob + return 1 + +/obj/effect/nanomob/proc/attempt_capture(obj/item/device/pda/P, catch_mod = 0) //negative catch_mods lower effective run chance, + if(!P || !P.current_app || !istype(P.current_app, /datum/data/pda/app/mob_hunter_game) || !P.cartridge) + return + + var/datum/data/pda/app/mob_hunter_game/client = P.current_app + var/total_catch_mod = client.catch_mod + catch_mod //negative values decrease the chance of the mob running, positive values makes it more likely to flee + if(!client.connected) //must be connected to attempt captures + P.audible_message("[bicon(P)] No server connection. Capture aborted.", null, 4) + return + + if(mob_info.is_trap) //traps work even if you ran into them before, which is why this is before the clients_encountered check + if(client.hacked) //hacked copies of the game (copies capable of setting traps) are protected from traps + return + if(iscarbon(P.loc)) + var/mob/living/carbon/C = P.loc + //Strike them down with a lightning bolt to complete the illusion (copied from the surge reagent overdose, probably could make this a general-use proc in the future) + playsound(get_turf(C), 'sound/effects/eleczap.ogg', 75, 1) + var/icon/I=new('icons/obj/zap.dmi',"lightningend") + I.Turn(-135) + var/obj/effect/overlay/beam/B = new(get_turf(C)) + B.pixel_x = rand(-20, 0) + B.pixel_y = rand(-20, 0) + B.icon = I + //then actually do the damage/stun + C.electrocute_act(20, P, 1) //same damage as a revenant's overload ability, except subject to gloves/species shock resistance (for human mobs at least) + return + + if(client in clients_encountered) //we've already dealt with you, go away! + return + else //deal with the new hunter by either running away or getting caught + clients_encountered += client + var/message = "[bicon(P)] " + var/effective_run_chance = mob_info.run_chance + total_catch_mod + if((effective_run_chance > 0) && prob(effective_run_chance)) + message += "Capture failed! [name] escaped [P.owner ? "from [P.owner]" : "from this hunter"]!" + conceal(client) + else + if(client.register_capture(mob_info, 1)) + message += "Capture success! [P.owner ? P.owner : "This hunter"] captured [name]!" + conceal(client) + else + message += "Capture error! Try again." + clients_encountered -= client //if the capture registration failed somehow, let them have another chance with this mob + P.audible_message(message, null, 4) + +/obj/effect/nanomob/proc/despawn() + if(mob_hunt_server) + if(mob_info.is_trap) + mob_hunt_server.trap_spawns -= src + else + mob_hunt_server.normal_spawns -= src + qdel(src) + +/obj/effect/nanomob/proc/reveal() + if(!mob_hunt_server) + return + var/list/show_to = list() + for(var/A in mob_hunt_server.connected_clients) + if((A in clients_encountered) || !mob_hunt_server.connected_clients[A]) + continue + show_to |= mob_hunt_server.connected_clients[A] + display_alt_appearance("nanomob_avatar", show_to) + +/obj/effect/nanomob/proc/conceal(list/hide_from) + if(!mob_hunt_server) + return + var/list/hiding_from = list() + if(hide_from) + hiding_from = hide_from + else + for(var/A in mob_hunt_server.connected_clients) + if((A in clients_encountered) && mob_hunt_server.connected_clients[A]) + hiding_from |= mob_hunt_server.connected_clients[A] + hide_alt_appearance("nanomob_avatar", hiding_from) + +// BATTLE MOB AVATARS + +/obj/effect/nanomob/battle + name = "Nano-Mob Battle Avatar" + desc = "A new challenger approaches!" + invisibility = 0 + icon_state = "placeholder" + var/obj/machinery/computer/mob_battle_terminal/my_terminal + +/obj/effect/nanomob/battle/New(loc, datum/mob_hunt/new_info) + if(new_info) + mob_info = new_info + update_self() + +/obj/effect/nanomob/battle/update_self() + if(!mob_info) + name = "Nano-Mob Battle Avatar" + desc = "A new challenger approaches" + icon_state = "placeholder" + else + name = mob_info.mob_name + desc = "A tamed [name] (level [mob_info.level]) ready for battle!" + if(mob_info.is_shiny) + icon_state = mob_info.icon_state_shiny + else + icon_state = mob_info.icon_state_normal + +/obj/effect/nanomob/battle/attempt_capture(obj/item/device/pda/P, catch_mod = 0) + //you can't capture battle avatars, since they belong to someone already + return + +//battle avatars are always visible, so we can ignore reveal and conceal calls for them +/obj/effect/nanomob/battle/reveal() + return + +/obj/effect/nanomob/battle/conceal() + return diff --git a/code/modules/arcade/mob_hunt/mob_cards.dm b/code/modules/arcade/mob_hunt/mob_cards.dm new file mode 100644 index 00000000000..16aea7ba55f --- /dev/null +++ b/code/modules/arcade/mob_hunt/mob_cards.dm @@ -0,0 +1,43 @@ + +//Standard Cards +/obj/item/weapon/nanomob_card + name = "Nano-Mob Hunter Trading Card" + desc = "A blank Nano-Mob Hunter Trading Card. Worthless!" + icon = 'icons/obj/card.dmi' + icon_state = "trade_card" + force = 0 + throwforce = 1 + w_class = 1 + var/datum/mob_hunt/mob_data + +/obj/item/weapon/nanomob_card/proc/update_info() + if(!mob_data) + return + if(mob_data.is_shiny) + name = "Holographic [mob_data.mob_name] Nano-Mob Hunter Card" + desc = "WOW! A holographic trading card containing a level [mob_data.level] [mob_data.mob_name]!" + icon_state = "trade_card_holo" + else + name = "[mob_data.mob_name] Nano-Mob Hunter Card" + desc = "A trading card containing a level [mob_data.level] [mob_data.mob_name]!" + +//Booster Pack Cards (random mob data) +/obj/item/weapon/nanomob_card/booster + name = "Nano-Mob Hunter Booster Pack Card" + desc = "A random Nano-Mob Trading Card from a Booster Pack. Wonder what it is?" + +/obj/item/weapon/nanomob_card/booster/New() + var/datum/mob_hunt/mob_info = pick(subtypesof(/datum/mob_hunt)) + mob_data = new mob_info(0,null,1) + update_info() + +//Booster Packs (Box of booster pack cards) +/obj/item/weapon/storage/box/nanomob_booster_pack + name = "Nano-Mob Hunter Trading Card Booster Pack" + desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!" + can_hold = list("/obj/item/weapon/nanomob_card") + +/obj/item/weapon/storage/box/nanomob_booster_pack/New() + ..() + for(var/i in 1 to 6) + new /obj/item/weapon/nanomob_card/booster(src) \ No newline at end of file diff --git a/code/modules/arcade/mob_hunt/mob_datums.dm b/code/modules/arcade/mob_hunt/mob_datums.dm new file mode 100644 index 00000000000..f6bc9dff826 --- /dev/null +++ b/code/modules/arcade/mob_hunt/mob_datums.dm @@ -0,0 +1,501 @@ + +#define TYPE_FIRE /datum/mob_type/fire +#define TYPE_WATER /datum/mob_type/water +#define TYPE_GRASS /datum/mob_type/grass +#define TYPE_ELECTRIC /datum/mob_type/electric +#define TYPE_GROUND /datum/mob_type/ground +#define TYPE_ROCK /datum/mob_type/rock +#define TYPE_BUG /datum/mob_type/bug +#define TYPE_POISON /datum/mob_type/poison +#define TYPE_NORMAL /datum/mob_type/normal +#define TYPE_FIGHTING /datum/mob_type/fighting +#define TYPE_PSYCHIC /datum/mob_type/psychic +#define TYPE_GHOST /datum/mob_type/ghost +#define TYPE_ICE /datum/mob_type/ice +#define TYPE_FLYING /datum/mob_type/flying +#define TYPE_BLUESPACE /datum/mob_type/bluespace +#define TYPE_DARK /datum/mob_type/dark +#define TYPE_STEEL /datum/mob_type/steel + +/datum/mob_hunt + //GENERAL STATS AND VARIABLES + var/mob_name = "Generic Mob" //the mob's original name (its species/type/whatever) + var/nickname = "" //the mob's nickname (if given by the owner) + var/run_chance = 0 //percent chance the mob will escape capture attempts (higher is obviously more likely to get away) + + //COMBAT STATS AND VARIABLES + var/level = 0 //actual level of this mob (don't set this, it gets overwritten in New()) + var/min_level = 1 //minimum level of this mob (used for randomizing the actual level) + var/max_level = 1 //maximum level of this mob (used for randomizing the actual level) + var/exp = 0 //number of battles the mob has won towards the next level (resets to 0 on level-up) + var/exp_to_level = 3 //number of battles the mob must win to level up (in case we want to make some mobs harder or easier to level) + //the types of the mob will be used for battles to determine damage resistance or weakness (mob_type_datums.dm) + var/datum/mob_type/primary_type //Make sure you set this or the mob will be unable to deal damage and will take absurd damage in battles + var/datum/mob_type/secondary_type //Don't set if not a dual-type mob so the mob will only calculate damage based on primary type + var/base_attack = 5 //base damage dealt by the mob's attacks for battling (effectively damage dealt at level 0) + var/base_health = 5 //base health of the mob for battling (effectively max health at level 0) + var/attack_multiplier = 1 //how much additional damage per level the mob deals (level * attack_multiplier) + var/health_multiplier = 1 //how much additional health per level the mob gets (level * health_multiplier) for calculating max health + var/cur_health = 0 + var/max_health = 0 + + //SPAWN PREFERENCES AND VARIABLES + //A note on mob spawn preferences: The mob types also have preferences, which are handled prior to per-mob preferences, so ultimately you use a combined set of preferences + var/list/area_blacklist = list() //list of areas this mob can NOT spawn in (such as the bridge) + var/list/turf_blacklist = list() //list of turfs this mob can NOT spawn on (such as wood floors) + var/list/area_whitelist = list() //list of areas this mob is more likely to spawn in (can be used to reinclude subtypes of blacklisted areas) + var/list/turf_whitelist = list() //list of turfs this mob is more likely to spawn on (can be used to reinclude subtypes of blacklisted turfs) + var/turf/spawn_point //gets set and sent to the game server to spawn its avatar (generated in select_spawn or assigned via set_trap) + var/lifetime = 6000 //number of deciseconds the mob will remain before despawning (REMEMBER: DECISECONDS! So 6000 is 600 seconds which is 10 minutes) + var/is_shiny = 0 //if this gets set at spawn (super rare), the mob is considered "shiny" and will use the shiny icon_state and holographic cards + + //the icon file for mob_hunt stuff is 'icons/effects/mob_hunt.dmi' so reference that for the following vars + var/icon_state_normal = "placeholder" //the icon_state for this mob's normal version + var/icon_state_shiny = "placeholder" //the icon_state for this mob's rare shiney version + + var/is_trap = 0 //if this gets set, the mob is a booby-trap and will electrocute any players that dare attempt to catch it + +/datum/mob_hunt/New(set_trap = 0, turf/trap_turf = null, no_register = 0) + if(set_trap) + level = max_level + is_trap = 1 + spawn_point = trap_turf + else + level = rand(min_level, max_level) + if(prob(1) && prob(1)) + is_shiny = 1 + max_health = base_health + (level * health_multiplier) + cur_health = max_health + if(primary_type) + primary_type = new primary_type() + if(secondary_type) + secondary_type = new secondary_type() + if(no_register) //for booster pack cards + return + if(mob_hunt_server) + if(set_trap) + if(mob_hunt_server.register_trap(src)) + return + else if(select_spawn()) + if(mob_hunt_server.register_spawn(src)) + return + qdel(src) //if you reach this, the datum is just pure clutter, so delete it + +/datum/mob_hunt/proc/select_spawn() + var/list/possible_areas = get_possible_areas() + if(!possible_areas.len) + log_admin("No possible areas to spawn [type] found. Possible code/mapping error?") + return 0 + while(possible_areas.len) + //randomly select an area from our possible_areas list to try spawning in, then remove it from possible_areas so it won't get picked over and over forever. + var/area/spawn_area = locate(pickweight(possible_areas)) + possible_areas -= spawn_area + if(!spawn_area) + break + //clear and generate a fresh list of turfs in the selected area, weighted based on white/black lists + var/list/possible_turfs = get_possible_turfs(spawn_area) + if(!possible_turfs.len) //If we don't have any possible turfs, this attempt was a failure. Try again. + continue + //if we got this far, we're spawning on this attempt, hooray! + spawn_point = pickweight(possible_turfs) + break + if(!spawn_point) + //if we get to this, we failed every attempt to find a suitable turf for EVERY area in our list of possible areas. DAMN. + log_admin("No acceptable turfs to spawn [type] on could be located. Possible code/mapping error, or someone replaced/destroyed all the acceptable turf types?") + return 0 + return 1 + +/datum/mob_hunt/proc/get_possible_areas() + var/list/possible_areas = list() + //setup, sets all station areas (and subtypes) to weight 1 + for(var/A in the_station_areas) + if(A == /area/holodeck) //don't allow holodeck areas as possible spawns since it will allow it to spawn in the holodeck rooms on z2 as well + continue + if(A in possible_areas) + continue + for(var/areapath in typesof(A)) + possible_areas[areapath] = 1 + //primary type preferences + if(primary_type) + for(var/A in primary_type.area_whitelist) + for(var/areapath in typesof(A)) + possible_areas[areapath] += 4 + for(var/A in primary_type.area_blacklist) + for(var/areapath in typesof(A)) + possible_areas[areapath] -= 2 + //secondary type preferences + if(secondary_type) + for(var/A in secondary_type.area_whitelist) + for(var/areapath in typesof(A)) + possible_areas[areapath] += 4 + for(var/A in secondary_type.area_blacklist) + for(var/areapath in typesof(A)) + possible_areas[areapath] -= 2 + //mob preferences + for(var/A in area_whitelist) + for(var/areapath in typesof(A)) + possible_areas[areapath] += 4 + for(var/A in area_blacklist) + for(var/areapath in typesof(A)) + possible_areas[areapath] -= 2 + //removes "bad areas" which shouldn't be on-station but are subtypes of station areas. probably should the unused ones and consider repathing the rest + var/list/bad_areas = list(subtypesof(/area/construction), /area/solar/derelict_starboard, /area/solar/derelict_aft, /area/solar/constructionsite) + for(var/A in bad_areas) + possible_areas -= A + //weight check, remove negative or zero weight areas from the list, then return the list. + for(var/areapath in possible_areas) + //remove any areas that shouldn't be on the station-level + if(possible_areas[areapath] < 1) + possible_areas -= areapath + continue + return possible_areas + +/datum/mob_hunt/proc/get_possible_turfs(area/spawn_area) + if(!spawn_area) + return list() + var/list/possible_turfs = list() + //setup, sets all turfs in spawn_area to weight 1 + for(var/turf/T in spawn_area) + if(!is_station_level(T.z)) //mobs will only consider station-level turfs for spawning. Largely here so we won't have to worry about mapping errors or mobs on the derelict solars + continue + possible_turfs[T] = 1 + //primary type preferences + if(primary_type) + if(is_type_in_list(T, primary_type.turf_whitelist)) + possible_turfs[T] += 4 + if(is_type_in_list(T, primary_type.turf_blacklist)) + possible_turfs[T] -= 2 + //secondary type preferences + if(secondary_type) + if(is_type_in_list(T, secondary_type.turf_whitelist)) + possible_turfs[T] += 4 + if(is_type_in_list(T, secondary_type.turf_blacklist)) + possible_turfs[T] -= 2 + //mob preferences + if(is_type_in_list(T, turf_whitelist)) + possible_turfs[T] += 4 + if(is_type_in_list(T, turf_blacklist)) + possible_turfs[T] -= 2 + //weight check, remove negative or zero weight turfs from the list, then return the list + if(possible_turfs[T] < 1) + possible_turfs -= T + return possible_turfs + +/datum/mob_hunt/proc/calc_def_multiplier(datum/mob_type/attack_type) + if(!primary_type) + return 99 //typeless mobs are weak to everything since they shouldn't exist + if(!attack_type) //typeless attacks will return a multiplier of 1 in case we want to use this for calculating unmodified damage for some reason (UI maybe?) + return 1 + + var/multiplier = 1 + if(attack_type in primary_type.immunity) + return 0 //a single immunity negates all damage + else if(attack_type in primary_type.resistance) + multiplier *= 0.5 + else if(attack_type in primary_type.weakness) + multiplier *= 2 + else + multiplier *= 1 + + if(!secondary_type) //if we don't have a second type, we're done here + return multiplier + if(attack_type in secondary_type.immunity) + return 0 //a single immunity negates all damage + else if(attack_type in secondary_type.resistance) + multiplier *= 0.5 + else if(attack_type in secondary_type.weakness) + multiplier *= 2 + else + multiplier *= 1 + return multiplier + +/datum/mob_hunt/proc/take_damage(raw_damage, datum/mob_type/attack_type) + var/message = "" + var/multiplier = calc_def_multiplier(attack_type) + var/total_damage = raw_damage * multiplier + if(!cur_health) //it's already downed, quit hitting it + return null + if(!total_damage) + message += "The attack is completely ineffective! " + else + cur_health = max(cur_health - total_damage, 0) + switch(multiplier) + if(0) + message += "The attack is completely ineffective! " + if(0.25) + message += "It's barely effective... " + if(0.5) + message += "It's not very effective... " + if(2) + message += "It's super effective! " + if(4) + message += "It's ultra effective! " + if(99) + message += pick("REKT! ", "DUNKED! ", "DEFENSE BREAK! ", "WOMBO-COMBO'D!") + if(!cur_health) + message += "[nickname ? nickname : mob_name] is downed!" + return message + +/datum/mob_hunt/proc/get_raw_damage() + return (level * attack_multiplier) + +/datum/mob_hunt/proc/get_attack_type() + var/datum/mob_type/attack_type = primary_type + if(secondary_type && prob(40)) + attack_type = secondary_type + return attack_type + +/datum/mob_hunt/proc/gain_exp() + exp++ + var/message = "[nickname ? nickname : mob_name] gained EXP! ([exp] / [exp_to_level] EXP)" + if(exp >= exp_to_level) + message = levelup() + return message + +/datum/mob_hunt/proc/levelup() + var/message = "" + level++ + exp = 0 + if(level > max_level) //This is where we would trigger an evolution, when those are added (need to add evolved forms first) + level = max_level //for now though, we'll just cap them back at their max_level + message += "[nickname ? nickname : mob_name] can't get any stronger right now!" + else + max_health = base_health + (level * health_multiplier) + cur_health = max_health //full heal on level-up + message += "[nickname ? nickname : mob_name] has reached level [level]!" + return message + +/datum/mob_hunt/proc/get_type1() + if(!primary_type) + return "Typeless" + else + return primary_type.name + +/datum/mob_hunt/proc/get_type2() + if(!secondary_type) + return null + else + return secondary_type.name + + +/datum/mob_hunt/nemabug + mob_name = "Nemabug" + run_chance = 50 + min_level = 1 + max_level = 10 + primary_type = TYPE_BUG + icon_state_normal = "nemabug" + icon_state_shiny = "nemabug_shiny" + lifetime = 6000 + +/datum/mob_hunt/stoutquill + mob_name = "Stoutquill" + run_chance = 50 + min_level = 5 + max_level = 15 + primary_type = TYPE_ICE + icon_state_normal = "stoutquill" + icon_state_shiny = "stoutquill_shiny" + lifetime = 4500 + +/datum/mob_hunt/spectra + mob_name = "Spectra" + run_chance = 35 + min_level = 1 + max_level = 10 + primary_type = TYPE_POISON + icon_state_normal = "spectra" + icon_state_shiny = "spectra_shiny" + lifetime = 6000 + +/datum/mob_hunt/dunny + mob_name = "Dunny" + run_chance = 35 + min_level = 1 + max_level = 10 + primary_type = TYPE_FIRE + icon_state_normal = "dunny" + icon_state_shiny = "dunny_shiny" + lifetime = 6000 + +/datum/mob_hunt/buffsel + mob_name = "Buffsel" + run_chance = 35 + min_level = 1 + max_level = 10 + primary_type = TYPE_ROCK + icon_state_normal = "buffsel" + icon_state_shiny = "buffsel_shiny" + lifetime = 6000 + +/datum/mob_hunt/quarrel + mob_name = "Quarrel" + run_chance = 35 + min_level = 1 + max_level = 10 + primary_type = TYPE_NORMAL + icon_state_normal = "quarrel" + icon_state_shiny = "quarrel_shiny" + lifetime = 6000 + +/datum/mob_hunt/vulerrt + mob_name = "Vulerrt" + run_chance = 50 + min_level = 5 + max_level = 15 + primary_type = TYPE_DARK + icon_state_normal = "vulerrt" + icon_state_shiny = "vulerrt_shiny" + turf_whitelist = list() + lifetime = 4500 + +/datum/mob_hunt/strudel + mob_name = "Strudel" + run_chance = 35 + min_level = 1 + max_level = 10 + primary_type = TYPE_ELECTRIC + icon_state_normal = "strudel" + icon_state_shiny = "strudel_shiny" + lifetime = 4500 + +/datum/mob_hunt/folstick + mob_name = "Folstick" + run_chance = 35 + min_level = 1 + max_level = 10 + primary_type = TYPE_WATER + icon_state_normal = "folstick" + icon_state_shiny = "folstick_shiny" + lifetime = 6000 + +/datum/mob_hunt/glimmerflare + mob_name = "Glimmerflare" + run_chance = 50 + min_level = 5 + max_level = 15 + primary_type = TYPE_PSYCHIC + icon_state_normal = "glimmerflare" + icon_state_shiny = "glimmerflare_shiny" + lifetime = 4500 + +/datum/mob_hunt/leecoon + mob_name = "Leecoon" + run_chance = 35 + min_level = 1 + max_level = 10 + primary_type = TYPE_GRASS + icon_state_normal = "leecoon" + icon_state_shiny = "leecoon_shiny" + lifetime = 6000 + +/datum/mob_hunt/halk + mob_name = "Halk" + run_chance = 35 + min_level = 1 + max_level = 10 + primary_type = TYPE_FLYING + icon_state_normal = "halk" + icon_state_shiny = "halk_shiny" + lifetime = 6000 + +/datum/mob_hunt/gooby + mob_name = "Gooby" + run_chance = 65 + min_level = 5 + max_level = 20 + primary_type = TYPE_ELECTRIC + secondary_type = TYPE_BUG + icon_state_normal = "gooby" + icon_state_shiny = "gooby_shiny" + lifetime = 3000 + +/datum/mob_hunt/pandoom + mob_name = "Pandoom" + run_chance = 50 + min_level = 5 + max_level = 15 + primary_type = TYPE_GHOST + icon_state_normal = "pandoom" + icon_state_shiny = "pandoom_shiny" + lifetime = 4500 + +/datum/mob_hunt/relish + mob_name = "Relish" + run_chance = 65 + min_level = 5 + max_level = 20 + primary_type = TYPE_FIRE + secondary_type = TYPE_GROUND + icon_state_normal = "relish" + icon_state_shiny = "relish_shiny" + lifetime = 3000 + +/datum/mob_hunt/xofine + mob_name = "Xofine" + run_chance = 50 + min_level = 5 + max_level = 10 + primary_type = TYPE_FIRE + secondary_type = TYPE_NORMAL + icon_state_normal = "xofine" + icon_state_shiny = "xofine_shiny" + lifetime = 3000 + +/datum/mob_hunt/gitten + mob_name = "Gitten" + run_chance = 65 + min_level = 5 + max_level = 20 + primary_type = TYPE_WATER + secondary_type = TYPE_POISON + icon_state_normal = "gitten" + icon_state_shiny = "gitten_shiny" + lifetime = 3000 + +/datum/mob_hunt/nai + mob_name = "Nai" + run_chance = 65 + min_level = 5 + max_level = 20 + primary_type = TYPE_GRASS + secondary_type = TYPE_NORMAL + icon_state_normal = "nai" + icon_state_shiny = "nai_shiny" + lifetime = 3000 + +/datum/mob_hunt/pyroghast + mob_name = "Pyroghast" + run_chance = 65 + min_level = 5 + max_level = 20 + primary_type = TYPE_FIRE + secondary_type = TYPE_GHOST + icon_state_normal = "pyroghast" + icon_state_shiny = "pyroghast" + lifetime = 4500 + +/datum/mob_hunt/starslam + mob_name = "Starslam" + run_chance = 65 + min_level = 5 + max_level = 20 + primary_type = TYPE_FIGHTING + secondary_type = TYPE_ICE + icon_state_normal = "starslam" + icon_state_shiny = "starslam_shiny" + lifetime = 2500 + +/datum/mob_hunt/pheron + mob_name = "Pheron" + run_chance = 85 + min_level = 10 + max_level = 20 + primary_type = TYPE_BLUESPACE + icon_state_normal = "pheron" + icon_state_shiny = "pheron_shiny" + area_blacklist = list() + turf_blacklist = list() + area_whitelist = list() + turf_whitelist = list() + lifetime = 2000 \ No newline at end of file diff --git a/code/modules/arcade/mob_hunt/mob_type_datums.dm b/code/modules/arcade/mob_hunt/mob_type_datums.dm new file mode 100644 index 00000000000..9d27a844ea8 --- /dev/null +++ b/code/modules/arcade/mob_hunt/mob_type_datums.dm @@ -0,0 +1,256 @@ + +/datum/mob_type + var/name = "Typeless" + var/list/weakness = list() + var/list/resistance = list() + var/list/immunity = list() + + //Type-based spawn preferences (to eliminate copy-pasting the same area and turf lists on each mob type) + var/list/area_blacklist = list() //areas to be avoided + var/list/area_whitelist = list() //areas to be more preferred + var/list/turf_blacklist = list() //turf types to be avoided + var/list/turf_whitelist = list() //turf types to be more preferred + +//Type defines, to avoid spelling mistakes +/datum/mob_type/fire + name = "Fire" + weakness = list(TYPE_WATER, + TYPE_ROCK, + TYPE_GROUND) + resistance = list(TYPE_BUG, + TYPE_FIRE, + TYPE_GRASS, + TYPE_ICE, + TYPE_STEEL) + area_blacklist = list(/area/crew_quarters/toilet, + /area/crew_quarters/sleep_male/toilet_male, + /area/crew_quarters/sleep_female/toilet_female, + /area/crew_quarters/locker/locker_toilet, + /area/toxins/server_coldroom) + area_whitelist = list(/area/maintenance/turbine, + /area/maintenance/incinerator, + /area/crew_quarters/kitchen) + turf_blacklist = list(/turf/simulated/floor/beach/water) + +/datum/mob_type/water + name = "Water" + weakness = list(TYPE_ELECTRIC, + TYPE_GRASS) + resistance = list(TYPE_FIRE, + TYPE_ICE, + TYPE_STEEL, + TYPE_WATER) + area_blacklist = list(/area/maintenance/turbine, + /area/maintenance/incinerator, + /area/crew_quarters/kitchen) + area_whitelist = list(/area/crew_quarters/toilet, + /area/crew_quarters/sleep_male/toilet_male, + /area/crew_quarters/sleep_female/toilet_female, + /area/crew_quarters/locker/locker_toilet) + turf_whitelist = list(/turf/simulated/floor/beach/water) + +/datum/mob_type/grass + name = "Grass" + weakness = list(TYPE_FIRE, + TYPE_BUG, + TYPE_POISON, + TYPE_ICE, + TYPE_FLYING) + resistance = list(TYPE_WATER, + TYPE_GRASS, + TYPE_ELECTRIC, + TYPE_GROUND) + area_blacklist = list(/area/toxins) + area_whitelist = list(/area/hydroponics, + /area/hallway/secondary/construction) + turf_whitelist = list(/turf/simulated/floor/grass) + +/datum/mob_type/electric + name = "Electric" + weakness = list(TYPE_GROUND) + resistance = list(TYPE_ELECTRIC, + TYPE_FLYING, + TYPE_STEEL) + area_whitelist = list(/area/engine, + /area/toxins/server, + /area/maintenance, + /area/turret_protected/ai, + /area/turret_protected/ai_upload, + /area/turret_protected/aisat_interior, + /area/aisat, + /area/assembly) + turf_whitelist = list(/turf/simulated/floor/bluegrid) + +/datum/mob_type/ground + name = "Ground" + weakness = list(TYPE_WATER, + TYPE_GRASS, + TYPE_ICE) + resistance = list(TYPE_ROCK, + TYPE_POISON) + immunity = list(TYPE_ELECTRIC) + +/datum/mob_type/rock + name = "Rock" + weakness = list(TYPE_WATER, + TYPE_GRASS, + TYPE_GROUND, + TYPE_FIGHTING, + TYPE_STEEL) + resistance = list(TYPE_FIRE, + TYPE_FLYING, + TYPE_POISON, + TYPE_NORMAL) + area_whitelist = list(/area/quartermaster, + /area/maintenance/disposal) + turf_whitelist = list(/turf/simulated/wall, + /turf/simulated/floor/mineral) + +/datum/mob_type/bug + name = "Bug" + weakness = list(TYPE_FIRE, + TYPE_ROCK, + TYPE_FLYING) + resistance = list(TYPE_GRASS, + TYPE_GROUND, + TYPE_FIGHTING) + area_blacklist = list(/area/toxins) + area_whitelist = list(/area/hydroponics, + /area/hallway/secondary/construction) + turf_whitelist = list(/turf/simulated/floor/grass) + +/datum/mob_type/poison + name = "Poison" + weakness = list(TYPE_GROUND, + TYPE_PSYCHIC) + resistance = list(TYPE_GRASS, + TYPE_BUG, + TYPE_POISON, + TYPE_FIGHTING) + area_blacklist = list(/area/medical, + /area/security/medbay, + /area/janitor) + area_whitelist = list(/area/medical/virology, + /area/toxins, + /area/medical/research, + /area/medical/research_shuttle_dock, + /area/crew_quarters/hor, + /area/maintenance/asmaint2) + +/datum/mob_type/normal + name = "Normal" + weakness = list(TYPE_FIGHTING) + immunity = list(TYPE_GHOST) + +/datum/mob_type/fighting + name = "Fighting" + weakness = list(TYPE_PSYCHIC, + TYPE_FLYING) + resistance = list(TYPE_ROCK, + TYPE_BUG, + TYPE_DARK) + area_blacklist = list(/area/medical) + area_whitelist = list(/area/crew_quarters/bar, + /area/crew_quarters/fitness, + /area/security) + turf_whitelist = list(/turf/simulated/floor/wood) + +/datum/mob_type/psychic + name = "Psychic" + weakness = list(TYPE_BUG, + TYPE_GHOST, + TYPE_DARK) + resistance = list(TYPE_FIGHTING, + TYPE_PSYCHIC) + area_blacklist = list(/area/toxins, + /area/medical/research, + /area/medical/research_shuttle_dock, + /area/crew_quarters/hor, + /area/maintenance/asmaint2, + /area/teleporter, + /area/gateway) + area_whitelist = list(/area/library, + /area/chapel, + /area/medical/psych) + +/datum/mob_type/ghost + name = "Ghost" + weakness = list(TYPE_GHOST, + TYPE_DARK) + resistance = list(TYPE_BUG, + TYPE_POISON) + immunity = list(TYPE_NORMAL, + TYPE_FIGHTING) + area_whitelist = list(/area/medical/morgue, + /area/chapel, + /area/medical/genetics_cloning) + +/datum/mob_type/ice + name = "Ice" + weakness = list(TYPE_FIRE, + TYPE_ROCK, + TYPE_FIGHTING, + TYPE_STEEL) + resistance = list(TYPE_ICE) + area_blacklist = list(/area/maintenance/turbine, + /area/maintenance/incinerator, + /area/crew_quarters/kitchen) + area_whitelist = list(/area/toxins/server_coldroom) + +/datum/mob_type/flying + name = "Flying" + weakness = list(TYPE_ELECTRIC, + TYPE_ROCK, + TYPE_ICE) + resistance = list(TYPE_GRASS, + TYPE_BUG, + TYPE_FIGHTING) + immunity = list(TYPE_GROUND) + area_blacklist = list(/area/maintenance) + area_whitelist = list(/area/hallway, + /area/escapepodbay, + /area/engine/mechanic_workshop, + /area/security/podbay) + +/datum/mob_type/bluespace + name = "Bluespace" + weakness = list(TYPE_ICE, + TYPE_BLUESPACE) + resistance = list(TYPE_FIRE, + TYPE_WATER, + TYPE_GRASS, + TYPE_ELECTRIC) + +/datum/mob_type/dark + name = "Dark" + weakness = list(TYPE_BUG, + TYPE_FIGHTING) + resistance = list(TYPE_GHOST, + TYPE_DARK) + immunity = list(TYPE_PSYCHIC) + area_blacklist = list(/area/solar, + /area/maintenance/auxsolarport, + /area/maintenance/starboardsolar, + /area/maintenance/portsolar, + /area/maintenance/auxsolarstarboard, + /area/clownoffice) + area_whitelist = list(/area/maintenance, + /area/assembly/assembly_line, + /area/mimeoffice) + turf_blacklist = list(/turf/simulated/floor/light) + +/datum/mob_type/steel + name = "Steel" + weakness = list(TYPE_FIRE, + TYPE_GROUND, + TYPE_FIGHTING) + resistance = list(TYPE_GRASS, + TYPE_ROCK, + TYPE_BUG, + TYPE_NORMAL, + TYPE_PSYCHIC, + TYPE_ICE, + TYPE_FLYING, + TYPE_BLUESPACE, + TYPE_STEEL) + immunity = list(TYPE_POISON) \ No newline at end of file diff --git a/code/modules/arcade/prize_datums.dm b/code/modules/arcade/prize_datums.dm index da78a7806ef..9334502dd10 100644 --- a/code/modules/arcade/prize_datums.dm +++ b/code/modules/arcade/prize_datums.dm @@ -190,6 +190,12 @@ var/global/datum/prizes/global_prizes = new typepath = /obj/item/weapon/spellbook/oneuse/fake_gib cost = 100 +/datum/prize_item/nanomob_booster + name = "Nano-Mob Hunter Trading Card Booster Pack" + desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!" + typepath = /obj/item/weapon/storage/box/nanomob_booster_pack + cost = 100 + /datum/prize_item/capgun name = "Capgun Revolver" desc = "Do you feel lucky... punk?" diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 082fc0d930f..e02a1a8d810 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -261,6 +261,8 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) send_asset_list(client, uncommon) send_asset_list(client, common) + +//Pill sprites for UIs /datum/asset/chem_master var/assets = list() var/verify = FALSE @@ -274,4 +276,21 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) register_asset(asset_name, assets[asset_name]) /datum/asset/chem_master/send(client) - send_asset_list(client,assets,verify) \ No newline at end of file + send_asset_list(client, assets, verify) + + +//Mob Hunt sprites for UIs +/datum/asset/mob_hunt + var/assets = list() + var/verify = FALSE + +/datum/asset/mob_hunt/register() + for(var/state in icon_states('icons/effects/mob_hunt.dmi')) + if(state == "Placeholder") + continue + assets["[state].png"] = icon('icons/effects/mob_hunt.dmi', state) + for(var/asset_name in assets) + register_asset(asset_name, assets[asset_name]) + +/datum/asset/mob_hunt/send(client) + send_asset_list(client, assets, verify) \ No newline at end of file diff --git a/code/modules/client/preference/loadout/loadout_general.dm b/code/modules/client/preference/loadout/loadout_general.dm index 347447f892f..919a77a44e8 100644 --- a/code/modules/client/preference/loadout/loadout_general.dm +++ b/code/modules/client/preference/loadout/loadout_general.dm @@ -74,3 +74,8 @@ /datum/gear/skullbandana display_name = "bandana, skull" path = /obj/item/clothing/mask/bandana/skull + +/datum/gear/mob_hunt_game + display_name = "Nano-Mob Hunter GO! Cartridge" + path = /obj/item/weapon/cartridge/mob_hunt_game + cost = 2 diff --git a/code/modules/pda/PDA.dm b/code/modules/pda/PDA.dm index ed7c380c426..5cbaf08fd34 100755 --- a/code/modules/pda/PDA.dm +++ b/code/modules/pda/PDA.dm @@ -427,6 +427,9 @@ var/global/list/obj/item/device/pda/PDAs = list() user.drop_item() C.forceMove(src) to_chat(user, "You slide \the [C] into \the [src].") + else if(istype(C, /obj/item/weapon/nanomob_card)) + if(cartridge && istype(cartridge, /obj/item/weapon/cartridge/mob_hunt_game)) + cartridge.attackby(C, user, params) /obj/item/device/pda/attack(mob/living/C as mob, mob/living/user as mob) if(istype(C, /mob/living/carbon) && scanmode) @@ -498,3 +501,12 @@ var/global/list/obj/item/device/pda/PDAs = list() else close(usr) return 0 + +/obj/item/device/pda/process() + if(current_app) + current_app.program_process() + +/obj/item/device/pda/hit_check(speed) + if(current_app) + current_app.program_hit_check() + ..() \ No newline at end of file diff --git a/code/modules/pda/app.dm b/code/modules/pda/app.dm index e7c433c65a6..4ad6c40ae24 100644 --- a/code/modules/pda/app.dm +++ b/code/modules/pda/app.dm @@ -1,6 +1,6 @@ // Base class for anything that can show up on home screen /datum/data/pda - var/icon = "tasks" + var/icon = "tasks" //options comes from http://fontawesome.io/icons/ var/notify_icon = "exclamation-circle" var/notify_silent = 0 var/hidden = 0 // program not displayed in main menu @@ -12,6 +12,16 @@ return ..() /datum/data/pda/proc/start() + return + +/datum/data/pda/proc/stop() + return + +/datum/data/pda/proc/program_process() + return + +/datum/data/pda/proc/program_hit_check() + return /datum/data/pda/proc/notify(message, blink = 1) if(message) @@ -56,6 +66,8 @@ title = name /datum/data/pda/app/start() + if(pda.current_app) + pda.current_app.stop() pda.current_app = src return 1 diff --git a/code/modules/pda/cart.dm b/code/modules/pda/cart.dm index c96a8cfb11c..30d26607113 100644 --- a/code/modules/pda/cart.dm +++ b/code/modules/pda/cart.dm @@ -22,7 +22,7 @@ if(radio) radio.initialize() ..() - + /obj/item/weapon/cartridge/Destroy() if(radio) qdel(radio) @@ -53,8 +53,7 @@ /obj/item/weapon/cartridge/atmos name = "BreatheDeep Cartridge" icon_state = "cart-a" - programs = list( - new/datum/data/pda/utility/scanmode/gas) + programs = list(new/datum/data/pda/utility/scanmode/gas) /obj/item/weapon/cartridge/medical name = "Med-U Cartridge" @@ -66,8 +65,7 @@ /obj/item/weapon/cartridge/chemistry name = "ChemWhiz Cartridge" icon_state = "cart-chem" - programs = list( - new/datum/data/pda/utility/scanmode/reagent) + programs = list(new/datum/data/pda/utility/scanmode/reagent) /obj/item/weapon/cartridge/security name = "R.O.B.U.S.T. Cartridge" @@ -94,30 +92,25 @@ name = "CustodiPRO Cartridge" desc = "The ultimate in clean-room design." icon_state = "cart-j" - programs = list( - new/datum/data/pda/app/janitor) + programs = list(new/datum/data/pda/app/janitor) /obj/item/weapon/cartridge/lawyer name = "P.R.O.V.E. Cartridge" icon_state = "cart-s" - programs = list( - new/datum/data/pda/app/crew_records/security) + programs = list(new/datum/data/pda/app/crew_records/security) /obj/item/weapon/cartridge/clown name = "Honkworks 5.0" icon_state = "cart-clown" charges = 5 - programs = list( - new/datum/data/pda/utility/honk) - messenger_plugins = list( - new/datum/data/pda/messenger_plugin/virus/clown) + programs = list(new/datum/data/pda/utility/honk) + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/clown) /obj/item/weapon/cartridge/mime name = "Gestur-O 1000" icon_state = "cart-mi" charges = 5 - messenger_plugins = list( - new/datum/data/pda/messenger_plugin/virus/mime) + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/mime) /* /obj/item/weapon/cartridge/botanist @@ -129,8 +122,7 @@ /obj/item/weapon/cartridge/signal name = "generic signaler cartridge" desc = "A data cartridge with an integrated radio signaler module." - programs = list( - new/datum/data/pda/app/signaller) + programs = list(new/datum/data/pda/app/signaller) /obj/item/weapon/cartridge/signal/initialize() radio = new /obj/item/radio/integrated/signal(src) @@ -162,8 +154,7 @@ /obj/item/weapon/cartridge/head name = "Easy-Record DELUXE" icon_state = "cart-h" - programs = list( - new/datum/data/pda/app/status_display) + programs = list(new/datum/data/pda/app/status_display) /obj/item/weapon/cartridge/hop name = "HumanResources9001" @@ -303,12 +294,38 @@ icon_state = "cart" var/initial_remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing. charges = 4 - programs = list( - new/datum/data/pda/utility/toggle_door) - messenger_plugins = list( - new/datum/data/pda/messenger_plugin/virus/detonate) + programs = list(new/datum/data/pda/utility/toggle_door) + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/detonate) /obj/item/weapon/cartridge/syndicate/New() var/datum/data/pda/utility/toggle_door/D = programs[1] if(istype(D)) - D.remote_door_id = initial_remote_door_id \ No newline at end of file + D.remote_door_id = initial_remote_door_id + +/obj/item/weapon/cartridge/mob_hunt_game + name = "Nano-Mob Hunter GO! Cartridge" + desc = "The hit new PDA game that lets you track down and capture your favorite Nano-Mobs living in your world!" + icon_state = "cart-eye" + programs = list(new/datum/data/pda/app/mob_hunter_game) + var/emagged = 0 + +/obj/item/weapon/cartridge/mob_hunt_game/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/weapon/nanomob_card)) + var/obj/item/weapon/nanomob_card/card = O + var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] + if(my_game.register_capture(card.mob_data)) + to_chat(user, "Transfer successful!") + qdel(card) + else + to_chat(user, "Transfer failed. Could not read mob data from card.") + else + ..() + +/obj/item/weapon/cartridge/mob_hunt_game/emag_act(mob/user) + if(!emagged) + emagged = 1 + var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] + my_game.hacked = 1 + to_chat(user, "TR4P_M45T3R.mod successfully initialized. ToS violated. User Agreement nullified. Gotta pwn them all.") + to_chat(user, "You can now create trapped versions of any mob in your collection that will damage hunters who attempt to capture it.") + description_antag = "This copy of Nano-Mob Hunter GO! has been hacked to allow the creation of trap mobs which will cause any PDA that attempts to capture it to shock anyone holding it. Hacked copies of the game will not trigger the trap." \ No newline at end of file diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm new file mode 100644 index 00000000000..9b8e3225976 --- /dev/null +++ b/code/modules/pda/mob_hunt_game_app.dm @@ -0,0 +1,182 @@ + +/* + This stuff isn't included with in core_apps.dm because it is so distinct and would end up making the file huge (more than it already is). + I put it in it's own file in order to help it stay organized and easier to locate the procs and such. + For reference, the other Nano-mob Hunter GO files are in /code/game/modules/arcade/mob_hunt +*/ + +/datum/data/pda/app/mob_hunter_game + name = "Nano-Mob Hunter GO" + icon = "gamepad" + template = "pda_mob_hunt" + category = "Games" + + var/list/my_collection = list() + var/current_index = 0 + var/connected = 0 + var/hacked = 0 //if set, this cartridge is able to spawn trap mobs from its collection (set via emag_act on the cartridge) + var/catch_mod = 0 //used to adjust the likelihood of a mob running from this client, a negative value means it is less likely to run (support for temporary bonuses) + var/wild_captures = 0 //used to track the total number of mobs captured from the wild (does not count card mobs) by this client + var/scan_range = 3 //maximum distance (in tiles) from which the client can reveal nearby mobs + +/datum/data/pda/app/mob_hunter_game/start() + ..() + processing_objects.Add(pda) + +/datum/data/pda/app/mob_hunter_game/stop() + ..() + disconnect("Program Terminated") + processing_objects.Remove(pda) + +/datum/data/pda/app/mob_hunter_game/proc/scan_nearby() + if(!mob_hunt_server || !connected) + return + for(var/turf/T in range(scan_range, get_turf(pda))) + for(var/obj/effect/nanomob/N in T.contents) + if(src in N.clients_encountered) + //hide the mob + N.conceal() + else + //reveal the mob + N.reveal() + +/datum/data/pda/app/mob_hunter_game/proc/reconnect() + if(!mob_hunt_server || !mob_hunt_server.server_status || connected) + //show a message about the server being unavailable (because it doesn't exist / didn't get set to the global var / is offline) + return 0 + mob_hunt_server.connected_clients += src + connected = 1 + if(pda) + pda.audible_message("[bicon(pda)] Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!", null, 2) + return 1 + +/datum/data/pda/app/mob_hunter_game/proc/get_player() + if(!pda) + return null + if(ishuman(pda.loc)) + var/mob/living/carbon/human/H = pda.loc + return H + return null + +/datum/data/pda/app/mob_hunter_game/proc/disconnect(reason = null) + if(!mob_hunt_server || !connected) + return + mob_hunt_server.connected_clients -= src + for(var/obj/effect/nanomob/N in (mob_hunt_server.normal_spawns + mob_hunt_server.trap_spawns)) + N.conceal(list(get_player())) + connected = 0 + //show a disconnect message if we were disconnected involuntarily (reason argument provided) + if(pda && reason) + pda.audible_message("[bicon(pda)] Disconnected from server. Reason: [reason].", null, 2) + +/datum/data/pda/app/mob_hunter_game/program_process() + if(!mob_hunt_server || !connected) + return + scan_nearby() + +/datum/data/pda/app/mob_hunter_game/program_hit_check() + if(!pda) + return + for(var/obj/effect/nanomob/hit_mob in get_turf(pda)) + hit_mob.hitby(pda) + +/datum/data/pda/app/mob_hunter_game/proc/register_capture(datum/mob_hunt/captured, wild = 0) + if(!captured) + return 0 + my_collection.Add(captured) + if(wild) + wild_captures++ + return 1 + +/datum/data/pda/app/mob_hunter_game/update_ui(mob/user, list/data) + if(!mob_hunt_server || !(src in mob_hunt_server.connected_clients)) + data["connected"] = 0 + else + data["connected"] = 1 + data["wild_captures"] = wild_captures + data["no_collection"] = 0 + if(!my_collection.len) + data["no_collection"] = 1 + return + var/datum/mob_hunt/mob_info + if(!current_index) + current_index = 1 + if(current_index > my_collection.len) + current_index = 1 + if(current_index < 1) + current_index = my_collection.len + mob_info = my_collection[current_index] + var/list/entry = list( + "nickname" = mob_info.nickname, + "real_name" = mob_info.mob_name, + "level" = mob_info.level, + "type1" = mob_info.get_type1(), + "type2" = mob_info.get_type2(), + "sprite" = "[mob_info.icon_state_normal].png", + "is_hacked" = hacked + ) + if(mob_info.is_shiny) + entry["sprite"] = "[mob_info.icon_state_shiny].png" + data["entry"] = entry + +/datum/data/pda/app/mob_hunter_game/proc/assign_nickname() + if(!my_collection.len) + return + var/datum/mob_hunt/mob_info = my_collection[current_index] + var/old_name = mob_info.mob_name + if(mob_info.nickname) + old_name = mob_info.nickname + mob_info.nickname = input("Give a nickname to [old_name]?", "Nickname", old_name) + +/datum/data/pda/app/mob_hunter_game/proc/release() + if(!my_collection.len) + return + if(alert("Are you sure you want to release this mob back into the wild?", "Confirm Release", "Yes", "No") == "Yes") + remove_mob() + +/datum/data/pda/app/mob_hunter_game/proc/print_card() + if(!pda || !my_collection.len) + return + var/obj/item/weapon/nanomob_card/card = new/obj/item/weapon/nanomob_card(null) + var/datum/mob_hunt/mob_info = my_collection[current_index] + card.mob_data = mob_info + card.update_info() + card.forceMove(get_turf(pda)) + remove_mob() + +/datum/data/pda/app/mob_hunter_game/proc/remove_mob() + if(!my_collection.len) + return + my_collection.Remove(my_collection[current_index]) + if(current_index > my_collection.len) + current_index = my_collection.len + +/datum/data/pda/app/mob_hunter_game/proc/set_trap() + if(!my_collection.len || !pda || !hacked) + return + var/datum/mob_hunt/bait = my_collection[current_index] + bait = bait.type + new bait(1, get_turf(pda)) + +/datum/data/pda/app/mob_hunter_game/Topic(href, list/href_list) + switch(href_list["choice"]) + if("Rename") + assign_nickname() + if("Release") + release() + if("Next") + current_index++ + if(current_index > my_collection.len) + current_index = 1 + if("Prev") + current_index-- + if(current_index < 1) + current_index = my_collection.len + if("Reconnect") + reconnect() + if("Disconnect") + disconnect() + if("Transfer") + print_card() + if("Set_Trap") + set_trap() \ No newline at end of file diff --git a/code/modules/store/items.dm b/code/modules/store/items.dm index c67ed43fd92..4fc51d7fb8d 100644 --- a/code/modules/store/items.dm +++ b/code/modules/store/items.dm @@ -57,6 +57,12 @@ typepath = /obj/item/weapon/storage/box/dice cost = 200 +/datum/storeitem/nanomob_booster + name = "Nano-Mob Hunter Trading Card Booster Pack" + desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!" + typepath = /obj/item/weapon/storage/box/nanomob_booster_pack + cost = 250 + /datum/storeitem/crayons name = "Crayons" desc = "Let security know how they're doing by scrawling lovenotes all over their hallways." diff --git a/icons/effects/mob_hunt.dmi b/icons/effects/mob_hunt.dmi new file mode 100644 index 00000000000..1eea83a9afa Binary files /dev/null and b/icons/effects/mob_hunt.dmi differ diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi index 00588161a65..4799f8736d2 100644 Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi index 09380373db8..9ad68e22072 100644 Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ diff --git a/nano/templates/comm_console.tmpl b/nano/templates/comm_console.tmpl index 6d566aa85fb..c4e1799afcf 100644 --- a/nano/templates/comm_console.tmpl +++ b/nano/templates/comm_console.tmpl @@ -68,6 +68,9 @@ Used In File(s): /code/game/machinery/computers/communications.dm
{{:helper.link('Message List','comment',{'operation':'messagelist'})}}
+
+ {{:helper.link('Restart Nano-Mob Hunter GO! Server','power-off',{'operation':'RestartNanoMob'})}} +
{{if data.dock_request}}
{{:helper.link('Accept Docking Request','signal',{'operation':'AcceptDocking'})}} diff --git a/nano/templates/pda_mob_hunt.tmpl b/nano/templates/pda_mob_hunt.tmpl new file mode 100644 index 00000000000..dde4ab82a5c --- /dev/null +++ b/nano/templates/pda_mob_hunt.tmpl @@ -0,0 +1,91 @@ +
+
+ + + + {{if data.connected}} + + + {{else}} + + + {{/if}} + +
+ Connection Status: + + Connected + + {{:helper.link('Disconnect', 'sign-out', {'choice': 'Disconnect'})}} + + No Connection + + {{:helper.link('Connect', 'sign-in', {'choice': 'Reconnect'})}} +
+
+
+
+
My Collection:
+{{if data.no_collection}} +
+
+ Your collection is empty! Go capture some Nano-Mobs! +
+
+
+ Wild mobs captured: {{:data.wild_captures}} +{{else}} +
+
+
+ {{if data.entry}} + {{if data.entry.nickname}} + Name: {{:data.entry.nickname}}
+ {{/if}} + Species: {{:data.entry.real_name}}
+ Level: {{:data.entry.level}}
+ Primary Type: {{:data.entry.type1}}
+ {{if data.entry.type2}} + Secondary Type: {{:data.entry.type2}}
+ {{/if}} + {{if data.entry.sprite}} + + {{else}} + Mob Image Missing! + {{/if}} + {{else}} + + Mob Data Missing!

+
+ {{/if}} +
+
+
+
+ Wild mobs captured: {{:data.wild_captures}} +
+ + + + + + + + +
+ {{:helper.link('Previous Mob', 'arrow-left', {'choice': 'Prev'})}} + + {{:helper.link('Transfer Mob', 'exchange', {'choice': 'Transfer'})}} + + {{:helper.link('Rename Mob', 'pencil', {'choice': 'Rename'})}} + + {{:helper.link('Release Mob', 'tree', {'choice': 'Release'})}} + + {{:helper.link('Next Mob', 'arrow-right', {'choice': 'Next'})}} +
+ {{if data.entry.is_hacked}} +
+ {{:helper.link('Set Trap', 'bolt', {'choice': 'Set_Trap'})}} + {{/if}} +
+{{/if}} \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index 148c3ae3a77..e18f8500930 100644 --- a/paradise.dme +++ b/paradise.dme @@ -176,6 +176,7 @@ #include "code\controllers\Processes\lighting.dm" #include "code\controllers\Processes\machinery.dm" #include "code\controllers\Processes\mob.dm" +#include "code\controllers\Processes\nano_mob_hunter.dm" #include "code\controllers\Processes\nanoui.dm" #include "code\controllers\Processes\npcai.dm" #include "code\controllers\Processes\npcpool.dm" @@ -1057,6 +1058,11 @@ #include "code\modules\arcade\claw_game.dm" #include "code\modules\arcade\prize_counter.dm" #include "code\modules\arcade\prize_datums.dm" +#include "code\modules\arcade\mob_hunt\battle_computer.dm" +#include "code\modules\arcade\mob_hunt\mob_avatar.dm" +#include "code\modules\arcade\mob_hunt\mob_cards.dm" +#include "code\modules\arcade\mob_hunt\mob_datums.dm" +#include "code\modules\arcade\mob_hunt\mob_type_datums.dm" #include "code\modules\assembly\assembly.dm" #include "code\modules\assembly\bomb.dm" #include "code\modules\assembly\health.dm" @@ -1737,6 +1743,7 @@ #include "code\modules\pda\core_apps.dm" #include "code\modules\pda\messenger.dm" #include "code\modules\pda\messenger_plugins.dm" +#include "code\modules\pda\mob_hunt_game_app.dm" #include "code\modules\pda\PDA.dm" #include "code\modules\pda\pdas.dm" #include "code\modules\pda\radio.dm"